Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix missing Microsoft.Bcl.Hash reference #68

Merged
merged 4 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

## [Unreleased](https://github.com/microsoft/CoseSignTool/tree/HEAD)

[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.0-pre5...HEAD)

**Closed issues:**

- CoseSignTool.exe `validate` incorrectly passes validation [\#66](https://github.com/microsoft/CoseSignTool/issues/66)

## [v1.1.0-pre5](https://github.com/microsoft/CoseSignTool/tree/v1.1.0-pre5) (2024-01-12)

[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.0-pre4...v1.1.0-pre5)

**Merged pull requests:**

- Fixing Command Line False Positives [\#67](https://github.com/microsoft/CoseSignTool/pull/67) ([elantiguamsft](https://github.com/elantiguamsft))

## [v1.1.0-pre4](https://github.com/microsoft/CoseSignTool/tree/v1.1.0-pre4) (2023-11-15)

[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.0-pre3...v1.1.0-pre4)
Expand Down
19 changes: 5 additions & 14 deletions CoseHandler/CoseValidationError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,19 @@ namespace CoseX509;
/// <summary>
/// Holds information about an error in COSE validation.
/// </summary>
public readonly struct CoseValidationError
{
/// <summary>
/// Creates a new CoseValidationError instance.
/// </summary>
/// <param name="errorCode">A ValidationFailureCode value that represents the error type.</param>
/// <param name="message">A text description of the error.</param>
public CoseValidationError(ValidationFailureCode errorCode)
{
ErrorCode = errorCode;
Message = ErrorMessages[errorCode];
}
/// <param name="errorCode">A ValidationFailureCode value that represents the error type.</param>

public readonly struct CoseValidationError(ValidationFailureCode errorCode)
{
/// <summary>
/// Gets or sets a ValidationFailureCode value that represents the error type.
/// </summary>
public ValidationFailureCode ErrorCode { get; }
public ValidationFailureCode ErrorCode { get; } = errorCode;

/// <summary>
/// Gets or sets a text description of the error.
/// </summary>
public string Message { get; }
public string Message { get; } = ErrorMessages[errorCode];

/// <summary>
/// A dictionary that maps error messages to error codes.
Expand Down
1 change: 1 addition & 0 deletions CoseSign1.Abstractions/CoseSign1.Abstractions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1" />
<PackageReference Include="System.Formats.Cbor" Version="7.0.0" />
<PackageReference Include="System.Security.Cryptography.Cose" Version="7.0.0" />
</ItemGroup>
Expand Down
15 changes: 15 additions & 0 deletions CoseSignTool.tests/MainTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace CoseSignUnitTests;

using System;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using CST = CoseSignTool.CoseSignTool;
Expand Down Expand Up @@ -130,4 +131,18 @@ public void FromMainInvalid()
string[] args5 = { "validate", @"/rt", payload, @"/sf", sigFile, @"/rt", "cert.wacky" };
CST.Main(args5).Should().Be((int)ExitCode.CertificateLoadFailure);
}

[TestMethod]
public void ReturnsHelpRequestedWhenVerbMissing()
{
string[] args = Array.Empty<string>();
CST.Main(args).Should().Be((int)ExitCode.HelpRequested);
}

[TestMethod]
public void ReturnsHelpRequestedWhenNoOptionsAfterVerb()
{
string[] args = Array.Empty<string>();
CST.Main(args).Should().Be((int)ExitCode.HelpRequested);
}
}
26 changes: 18 additions & 8 deletions CoseSignTool/CoseCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

namespace CoseSignTool;

using System;
using System.Text.RegularExpressions;

/// <summary>
/// A base class for console commands that handle COSE signatures.
/// </summary>
Expand Down Expand Up @@ -256,11 +259,18 @@ private static bool HasInvalidArgument(string[] args, StringDictionary options,
#endregion

#region Usage

/// <summary>
/// Gets the help text for the command and merges it with the general help text for CoseSignTool.
/// </summary>
/// <returns>The merged help text.</returns>
public static string Usage => $"{BaseUsageString}{UsageString}";

/// <summary>
/// The first section of the command line usage. Content is generic to CoseSignTool.
/// Each line should have no more than 120 characters to avoid wrapping. Break is here: *V*
/// </summary>
// The usage text to display. Each line should have no more than 120 characters to avoid wrapping. Break is here: *V*
protected internal const string BaseUsageString = @$"
protected const string BaseUsageString = @$"
*** CoseSignTool ***
A tool for signing, validating, and getting payload from Cose signatures.

Expand All @@ -269,18 +279,18 @@ CoseSignTool.exe [sign | validate | get] [options]
-- OR --
[source program] | CoseSignTool.exe [sign | validate | get] [options]
where [source program] pipes the first required option to CoseSignTool.
";

/// <summary>
/// The end of the usage string for when no command was specified.
/// </summary>
protected const string UsageString = @"
Sign: Signs the specified file or piped content with a detached or embedded signature.
Validate: Validates that the specified COSE signature file or piped signature content matches the original payload and
is signed with a valid certificate chain.
Get: Retrieves and decodes the original payload from a COSE embed signed file or piped signature, writes it to a file or
to the console, and writes any validation errors to Standard Error.
";

/// <summary>
/// The end of the usage string for when no command was specified.
/// </summary>
public static readonly string UsageString = $"{BaseUsageString}{Environment.NewLine}" +
$"To see the options for a specific command, type 'CoseSignTool [sign | validate | get] /?'";
To see the options for a specific command, type 'CoseSignTool [sign | validate | get] /?'";
#endregion
}
32 changes: 25 additions & 7 deletions CoseSignTool/CoseSignTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

namespace CoseSignTool;

using System.Net.NetworkInformation;

/// <summary>
/// Command line interface for COSE signing and validation operations.
/// </summary>
Expand All @@ -24,13 +26,27 @@ private enum Verbs
/// <returns>An exit code indicating success or failure.</returns>
public static int Main(string[] args)
{
var firstArg = args[0] ?? "help";
// Make sure we have a verb and at least one argument, and that neither of the first two arguments are help requests.
if (args.Length == 0 || IsNullOrHelp(args[0]) || !Enum.TryParse(args[0], ignoreCase: true, out Verb))
{
return (int)Usage(CoseCommand.Usage);
}
else if (args.Length == 1 || IsNullOrHelp(args[1]))
{
string usageString = Verb switch
{
Verbs.Sign => SignCommand.Usage,
Verbs.Validate => ValidateCommand.Usage,
Verbs.Get => GetCommand.Usage,
_ => CoseCommand.Usage,
};

return (int)Usage(usageString);
}

try
{
return Enum.TryParse(firstArg, ignoreCase: true, out Verb)
? (int)RunCommand(Verb, args.Skip(1).ToArray())
: (int)Usage(CoseCommand.UsageString);
return (int)RunCommand(Verb, args.Skip(1).ToArray());
}
catch (Exception ex)
{
Expand All @@ -45,6 +61,8 @@ public static int Main(string[] args)
}
}

private static bool IsNullOrHelp(string arg) => arg is null || arg.EndsWith('?') || arg.EndsWith("help", StringComparison.OrdinalIgnoreCase);

/// <summary>
/// Creates a SignCommand, ValidateCommand, or GetCommand instance based on raw command line input and then runs the command.
/// </summary>
Expand All @@ -62,13 +80,13 @@ private static ExitCode RunCommand(Verbs verb, string[] args)
{
case Verbs.Sign:
provider = CoseCommand.LoadCommandLineArgs(args, SignCommand.Options, out badArg);
return (provider is null) ? Usage(SignCommand.UsageString, badArg) : new SignCommand(provider).Run();
return (provider is null) ? Usage(SignCommand.Usage, badArg) : new SignCommand(provider).Run();
case Verbs.Validate:
provider = CoseCommand.LoadCommandLineArgs(args, ValidateCommand.Options, out badArg);
return (provider is null) ? Usage(ValidateCommand.UsageString, badArg) : new ValidateCommand(provider).Run();
return (provider is null) ? Usage(ValidateCommand.Usage, badArg) : new ValidateCommand(provider).Run();
case Verbs.Get:
provider = CoseCommand.LoadCommandLineArgs(args, GetCommand.Options, out badArg);
return (provider is null) ? Usage(GetCommand.UsageString, badArg) : new GetCommand(provider).Run();
return (provider is null) ? Usage(GetCommand.Usage, badArg) : new GetCommand(provider).Run();
default:
return ExitCode.InvalidArgumentValue;
}
Expand Down
9 changes: 6 additions & 3 deletions CoseSignTool/GetCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,12 @@ protected internal override ValidationResult RunCoseHandlerCommand(Stream signat
return result;
}

/// <inheritdoc/>
public static new string Usage => $"{BaseUsageString}{UsageString}{SharedOptionsText}";

// The usage text to display. Each line should have no more than 120 characters to avoid wrapping. Break is here: *V*
internal static new string UsageString = $@"
// Shared options are inherited from ValidateCommand.
protected new const string UsageString = @"
Get command: Retrieves and decodes the original payload from a COSE embed signed file or piped signature, writes it to a
file or to the console, and writes any validation errors to Standard Error.

Expand All @@ -77,6 +81,5 @@ protected internal override ValidationResult RunCoseHandlerCommand(Stream signat

SaveTo /sa: Specifies a file path to write the decoded payload content to.
If no path is specified, output will be written to console.

{OptionalsBlock}";
";
}
13 changes: 8 additions & 5 deletions CoseSignTool/SignCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace CoseSignTool;
/// <summary>
/// Signs a file with a COSE signature based on passed in command line arguments.
/// </summary>
public sealed class SignCommand : CoseCommand
public class SignCommand : CoseCommand
{
/// <summary>
/// A map of command line options to their abbreviated aliases.
Expand Down Expand Up @@ -219,11 +219,14 @@ internal X509Certificate2 LoadCert()
return cert;
}

/// <inheritdoc/>
public static new string Usage => $"{BaseUsageString}{UsageString}";

/// <summary>
/// Command line usage specific to the SignInternal command.
/// Each line should have no more than 120 characters to avoid wrapping. Break is here: *V*
/// </summary>
// The usage text to display. Each line should have no more than 120 characters to avoid wrapping. Break is here: *V*
public static new readonly string UsageString = @"
protected new const string UsageString = @"
Sign command: Signs the specified file or piped content with a detached or embedded signature.
A detached signature resides in a separate file and validates against the original content by hash match.
An embedded signature contains an encoded copy of the original payload. Not supported for payload of >2gb in size.
Expand All @@ -239,9 +242,9 @@ A detached signature resides in a separate file and validates against the origin

PfxCertificate / pfx: A path to a private key certificate file (.pfx) to sign with.

Password / pw: Optional. The password for the .pfx file if it has one.
Password / pw: Optional. The password for the .pfx file if it has one. (Strongly recommended!)

--OR--
--OR--

Thumbprint / th: The SHA1 thumbprint of a certificate in the local certificate store to sign the file with.
Use the optional StoreName and StoreLocation parameters to tell CoseSignTool where to find the matching
Expand Down
14 changes: 8 additions & 6 deletions CoseSignTool/ValidateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,11 @@ protected internal override void ApplyOptions(CommandLineConfigurationProvider p
return rootCerts;
}

/// <inheritdoc/>
public static new string Usage => $"{BaseUsageString}{UsageString}{SharedOptionsText}";

// The usage text to display. Each line should have no more than 120 characters to avoid wrapping. Break is here: *V*
internal static new string UsageString = $@"
protected new const string UsageString = @"
Validate command: Validates that the specified COSE signature file or piped signature content matches the original
payload and is signed with a valid certificate chain.

Expand All @@ -225,12 +228,11 @@ payload and is signed with a valid certificate chain.

PayloadFile / payload / p: Required for detached signatures. The original source file that was signed.
Do not use for embedded signatures.
";

{OptionalsBlock}";

protected static readonly string OptionalsBlock = $@"
Roots / rt: Optional. A comma-separated list of public key certificate files (.cer or .p7b), enclosed in quote marks,
to try to chain the signing certificate to.
protected const string SharedOptionsText = $@"
Roots / rt: Optional. A comma-separated list of public key certificate files (.cer or .p7b), enclosed in quote
marks, to try to chain the signing certificate to.
CoseSignTool will try to chain to installed roots first, then user-supplied roots.
If the COSE signature is signed with a self-signed certificate, that certificate must either be installed on and
trusted by the machine or supplied as a root to pass validation.
Expand Down
Loading