diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d4ac924..8a942b06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,36 @@ # Changelog -## [v1.3.0-pre5](https://github.com/microsoft/CoseSignTool/tree/v1.3.0-pre5) (2025-03-18) +## [v1.5.0-pre1](https://github.com/microsoft/CoseSignTool/tree/v1.5.0-pre1) (2025-05-07) + +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.5.1...v1.5.0-pre1) + +## [v1.5.1](https://github.com/microsoft/CoseSignTool/tree/v1.5.1) (2025-05-07) + +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.4.0-pre1...v1.5.1) + +**Merged pull requests:** + +- Allow beta version of Azure.Security.CodeTransparency [\#129](https://github.com/microsoft/CoseSignTool/pull/129) ([lemccomb](https://github.com/lemccomb)) + +## [v1.4.0-pre1](https://github.com/microsoft/CoseSignTool/tree/v1.4.0-pre1) (2025-04-28) + +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.5.0...v1.4.0-pre1) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.4.0...v1.3.0-pre5) +## [v1.5.0](https://github.com/microsoft/CoseSignTool/tree/v1.5.0) (2025-04-28) + +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.4.0...v1.5.0) + +**Merged pull requests:** + +- Added support for Transparency to CoseSign1 libraries to leverage services such as Azure Code Transparency Service [\#127](https://github.com/microsoft/CoseSignTool/pull/127) ([JeromySt](https://github.com/JeromySt)) ## [v1.4.0](https://github.com/microsoft/CoseSignTool/tree/v1.4.0) (2025-03-18) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.3.0-pre4...v1.4.0) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.3.0-pre5...v1.4.0) + +## [v1.3.0-pre5](https://github.com/microsoft/CoseSignTool/tree/v1.3.0-pre5) (2025-03-18) + +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.3.0-pre4...v1.3.0-pre5) **Merged pull requests:** @@ -86,19 +110,19 @@ ## [v1.2.8-pre3](https://github.com/microsoft/CoseSignTool/tree/v1.2.8-pre3) (2024-10-15) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.8-pre1...v1.2.8-pre3) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.8-pre2...v1.2.8-pre3) **Merged pull requests:** - Increase timeout for checking for empty streams [\#113](https://github.com/microsoft/CoseSignTool/pull/113) ([lemccomb](https://github.com/lemccomb)) -## [v1.2.8-pre1](https://github.com/microsoft/CoseSignTool/tree/v1.2.8-pre1) (2024-09-25) +## [v1.2.8-pre2](https://github.com/microsoft/CoseSignTool/tree/v1.2.8-pre2) (2024-09-25) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.8-pre2...v1.2.8-pre1) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.8-pre1...v1.2.8-pre2) -## [v1.2.8-pre2](https://github.com/microsoft/CoseSignTool/tree/v1.2.8-pre2) (2024-09-25) +## [v1.2.8-pre1](https://github.com/microsoft/CoseSignTool/tree/v1.2.8-pre1) (2024-09-25) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.8...v1.2.8-pre2) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.8...v1.2.8-pre1) **Merged pull requests:** @@ -248,19 +272,19 @@ ## [v1.2.1-pre2](https://github.com/microsoft/CoseSignTool/tree/v1.2.1-pre2) (2024-03-15) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.2...v1.2.1-pre2) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.1-pre1...v1.2.1-pre2) **Merged pull requests:** - more granular error codes [\#86](https://github.com/microsoft/CoseSignTool/pull/86) ([lemccomb](https://github.com/lemccomb)) -## [v1.2.2](https://github.com/microsoft/CoseSignTool/tree/v1.2.2) (2024-03-12) +## [v1.2.1-pre1](https://github.com/microsoft/CoseSignTool/tree/v1.2.1-pre1) (2024-03-12) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.1-pre1...v1.2.2) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.2...v1.2.1-pre1) -## [v1.2.1-pre1](https://github.com/microsoft/CoseSignTool/tree/v1.2.1-pre1) (2024-03-12) +## [v1.2.2](https://github.com/microsoft/CoseSignTool/tree/v1.2.2) (2024-03-12) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.0-pre1...v1.2.1-pre1) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.0-pre1...v1.2.2) **Merged pull requests:** @@ -360,19 +384,19 @@ ## [v1.1.3-pre1](https://github.com/microsoft/CoseSignTool/tree/v1.1.3-pre1) (2024-01-26) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.3...v1.1.3-pre1) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.2-pre1...v1.1.3-pre1) **Merged pull requests:** - Adding Validation Option to Output Certificate Chain [\#73](https://github.com/microsoft/CoseSignTool/pull/73) ([elantiguamsft](https://github.com/elantiguamsft)) -## [v1.1.3](https://github.com/microsoft/CoseSignTool/tree/v1.1.3) (2024-01-24) +## [v1.1.2-pre1](https://github.com/microsoft/CoseSignTool/tree/v1.1.2-pre1) (2024-01-24) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.2-pre1...v1.1.3) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.3...v1.1.2-pre1) -## [v1.1.2-pre1](https://github.com/microsoft/CoseSignTool/tree/v1.1.2-pre1) (2024-01-24) +## [v1.1.3](https://github.com/microsoft/CoseSignTool/tree/v1.1.3) (2024-01-24) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.1-pre2...v1.1.2-pre1) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.1-pre2...v1.1.3) **Merged pull requests:** @@ -453,7 +477,7 @@ ## [v1.1.0-pre1](https://github.com/microsoft/CoseSignTool/tree/v1.1.0-pre1) (2023-11-03) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.0...v1.1.0-pre1) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v0.3.1-pre.10...v1.1.0-pre1) **Merged pull requests:** @@ -463,13 +487,13 @@ - DetachedSignatureFactory accepts pre-hashed content as payload [\#53](https://github.com/microsoft/CoseSignTool/pull/53) ([elantiguamsft](https://github.com/elantiguamsft)) - Add password support for certificate files [\#52](https://github.com/microsoft/CoseSignTool/pull/52) ([lemccomb](https://github.com/lemccomb)) -## [v1.1.0](https://github.com/microsoft/CoseSignTool/tree/v1.1.0) (2023-10-10) +## [v0.3.1-pre.10](https://github.com/microsoft/CoseSignTool/tree/v0.3.1-pre.10) (2023-10-10) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v0.3.1-pre.10...v1.1.0) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.0...v0.3.1-pre.10) -## [v0.3.1-pre.10](https://github.com/microsoft/CoseSignTool/tree/v0.3.1-pre.10) (2023-10-10) +## [v1.1.0](https://github.com/microsoft/CoseSignTool/tree/v1.1.0) (2023-10-10) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v0.3.2...v0.3.1-pre.10) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v0.3.1-pre.9...v1.1.0) **Merged pull requests:** @@ -479,13 +503,13 @@ - Port changes from ADO repo to GitHub repo [\#46](https://github.com/microsoft/CoseSignTool/pull/46) ([lemccomb](https://github.com/lemccomb)) - Re-enable CodeQL [\#45](https://github.com/microsoft/CoseSignTool/pull/45) ([lemccomb](https://github.com/lemccomb)) -## [v0.3.2](https://github.com/microsoft/CoseSignTool/tree/v0.3.2) (2023-09-28) +## [v0.3.1-pre.9](https://github.com/microsoft/CoseSignTool/tree/v0.3.1-pre.9) (2023-09-28) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v0.3.1-pre.9...v0.3.2) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v0.3.2...v0.3.1-pre.9) -## [v0.3.1-pre.9](https://github.com/microsoft/CoseSignTool/tree/v0.3.1-pre.9) (2023-09-28) +## [v0.3.2](https://github.com/microsoft/CoseSignTool/tree/v0.3.2) (2023-09-28) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v0.3.1-pre.8...v0.3.1-pre.9) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v0.3.1-pre.8...v0.3.2) **Merged pull requests:** diff --git a/CoseIndirectSignature.Tests/IndirectSignatureFactoryTests.cs b/CoseIndirectSignature.Tests/IndirectSignatureFactoryTests.cs index b76f07c0..339d8d69 100644 --- a/CoseIndirectSignature.Tests/IndirectSignatureFactoryTests.cs +++ b/CoseIndirectSignature.Tests/IndirectSignatureFactoryTests.cs @@ -1,8 +1,24 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace CoseIndirectSignature.Tests; - +namespace CoseIndirectSignature.Tests; + +public class TestCoseHeaderExtender : ICoseHeaderExtender +{ + public Func? ExtendProtectedHeadersFunc { get; set; } + public Func? ExtendUnProtectedHeadersFunc { get; set; } + + public CoseHeaderMap ExtendProtectedHeaders(CoseHeaderMap protectedHeaders) + { + return ExtendProtectedHeadersFunc == null ? protectedHeaders : ExtendProtectedHeadersFunc(protectedHeaders); + } + + public CoseHeaderMap ExtendUnProtectedHeaders(CoseHeaderMap? unProtectedHeaders) + { + return ExtendUnProtectedHeadersFunc == null ? new CoseHeaderMap() : ExtendUnProtectedHeadersFunc(unProtectedHeaders); + } +} + /// /// Class for Testing Methods of /// @@ -43,31 +59,31 @@ public async Task TestCreateIndirectSignatureAsync() new Random().NextBytes(randomBytes); using MemoryStream memStream = new(randomBytes); - // test the sync method - Assert.Throws(() => factory.CreateIndirectSignature(randomBytes, coseSigningKeyProvider, string.Empty)); - - CoseSign1Message IndirectSignatureCurrent = factory.CreateIndirectSignature(randomBytes, coseSigningKeyProvider, "application/test.payload"); + // test the sync method + Assert.Throws(() => factory.CreateIndirectSignature(randomBytes, coseSigningKeyProvider, string.Empty)); + + CoseSign1Message IndirectSignatureCurrent = factory.CreateIndirectSignature(randomBytes, coseSigningKeyProvider, "application/test.payload"); IndirectSignatureCurrent.IsIndirectSignature().Should().BeTrue(); - IndirectSignatureCurrent.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignatureCurrent.TryGetPreImageContentType(out string? payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignatureCurrent.TryGetPayloadHashAlgorithm(out CoseHashAlgorithm? algo).Should().BeTrue(); - algo!.Should().Be(CoseHashAlgorithm.SHA256); + IndirectSignatureCurrent.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignatureCurrent.TryGetPreImageContentType(out string? payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignatureCurrent.TryGetPayloadHashAlgorithm(out CoseHashAlgorithm? algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); #pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature = factory.CreateIndirectSignature(randomBytes, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); + CoseSign1Message IndirectSignature = factory.CreateIndirectSignature(randomBytes, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); IndirectSignature.SignatureMatches(randomBytes).Should().BeTrue(); - memStream.Seek(0, SeekOrigin.Begin); - - - Assert.Throws(() => factory.CreateIndirectSignature(memStream, coseSigningKeyProvider, string.Empty)); - memStream.Seek(0, SeekOrigin.Begin); - + memStream.Seek(0, SeekOrigin.Begin); + + + Assert.Throws(() => factory.CreateIndirectSignature(memStream, coseSigningKeyProvider, string.Empty)); + memStream.Seek(0, SeekOrigin.Begin); + #pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature2 = factory.CreateIndirectSignature(memStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); + CoseSign1Message IndirectSignature2 = factory.CreateIndirectSignature(memStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature2.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature2.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); @@ -75,42 +91,60 @@ public async Task TestCreateIndirectSignatureAsync() memStream.Seek(0, SeekOrigin.Begin); // test the async methods - Assert.ThrowsAsync(() => factory.CreateIndirectSignatureAsync(randomBytes, coseSigningKeyProvider, string.Empty)); - - CoseSign1Message IndirectSignatureCurrentAsync = await factory.CreateIndirectSignatureAsync(randomBytes, coseSigningKeyProvider, "application/test.payload"); + Assert.ThrowsAsync(() => factory.CreateIndirectSignatureAsync(randomBytes, coseSigningKeyProvider, string.Empty)); + + CoseSign1Message IndirectSignatureCurrentAsync = await factory.CreateIndirectSignatureAsync(randomBytes, coseSigningKeyProvider, "application/test.payload"); IndirectSignatureCurrentAsync.IsIndirectSignature().Should().BeTrue(); - IndirectSignatureCurrentAsync.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignatureCurrentAsync.TryGetPreImageContentType(out payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignatureCurrentAsync.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); - algo!.Should().Be(CoseHashAlgorithm.SHA256); - + IndirectSignatureCurrentAsync.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignatureCurrentAsync.TryGetPreImageContentType(out payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignatureCurrentAsync.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); + #pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature3 = await factory.CreateIndirectSignatureAsync(randomBytes, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); + CoseSign1Message IndirectSignature3 = await factory.CreateIndirectSignatureAsync(randomBytes, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature3.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature3.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); IndirectSignature3.SignatureMatches(randomBytes).Should().BeTrue(); Assert.ThrowsAsync(() => factory.CreateIndirectSignatureAsync(memStream, coseSigningKeyProvider, string.Empty)); - memStream.Seek(0, SeekOrigin.Begin); - - CoseSign1Message IndirectSignatureCurrentStreamAsync = await factory.CreateIndirectSignatureAsync(memStream, coseSigningKeyProvider, "application/test.payload"); + memStream.Seek(0, SeekOrigin.Begin); + + CoseSign1Message IndirectSignatureCurrentStreamAsync = await factory.CreateIndirectSignatureAsync(memStream, coseSigningKeyProvider, "application/test.payload"); IndirectSignatureCurrentStreamAsync.IsIndirectSignature().Should().BeTrue(); - IndirectSignatureCurrentStreamAsync.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignatureCurrentStreamAsync.TryGetPreImageContentType(out payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignatureCurrentStreamAsync.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); - algo!.Should().Be(CoseHashAlgorithm.SHA256); - memStream.Seek(0, SeekOrigin.Begin); - + IndirectSignatureCurrentStreamAsync.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignatureCurrentStreamAsync.TryGetPreImageContentType(out payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignatureCurrentStreamAsync.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); + memStream.Seek(0, SeekOrigin.Begin); + #pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature4 = await factory.CreateIndirectSignatureAsync(memStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); + CoseSign1Message IndirectSignature4 = await factory.CreateIndirectSignatureAsync(memStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature4.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature4.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); IndirectSignature4.SignatureMatches(randomBytes).Should().BeTrue(); memStream.Seek(0, SeekOrigin.Begin); + + TestCoseHeaderExtender testExtender = new TestCoseHeaderExtender(); + CoseHeaderLabel coseHeaderLabel = new("test-header"); + testExtender.ExtendProtectedHeadersFunc = (CoseHeaderMap protectedHeaders) => + { + protectedHeaders[coseHeaderLabel] = CoseHeaderValue.FromString("test-value"); + return protectedHeaders; + }; + + CoseSign1Message IndirectSignature5 = factory.CreateIndirectSignature(randomBytes, coseSigningKeyProvider, "application/test.payload", coseHeaderExtender: testExtender); + IndirectSignature5.IsIndirectSignature().Should().BeTrue(); + IndirectSignature5.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignature5.TryGetPreImageContentType(out payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignature5.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); + IndirectSignature5.ProtectedHeaders.ContainsKey(coseHeaderLabel).Should().BeTrue(); + IndirectSignature5.ProtectedHeaders[coseHeaderLabel].GetValueAsString().Should().Be("test-value"); } [Test] @@ -126,37 +160,37 @@ public async Task TestCreateIndirectSignatureHashProvidedAsync() using MemoryStream hashStream = new(hash); // test the sync method - Assert.Throws(() => factory.CreateIndirectSignatureFromHash(hash, coseSigningKeyProvider, string.Empty)); - - CoseSign1Message IndirectSignatureCurrent = factory.CreateIndirectSignatureFromHash(hash, coseSigningKeyProvider, "application/test.payload"); - IndirectSignatureCurrent.IsIndirectSignature().Should().BeTrue(); - IndirectSignatureCurrent.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignatureCurrent.TryGetPreImageContentType(out string? payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignatureCurrent.TryGetPayloadHashAlgorithm(out CoseHashAlgorithm? algo).Should().BeTrue(); - algo!.Should().Be(CoseHashAlgorithm.SHA256); - + Assert.Throws(() => factory.CreateIndirectSignatureFromHash(hash, coseSigningKeyProvider, string.Empty)); + + CoseSign1Message IndirectSignatureCurrent = factory.CreateIndirectSignatureFromHash(hash, coseSigningKeyProvider, "application/test.payload"); + IndirectSignatureCurrent.IsIndirectSignature().Should().BeTrue(); + IndirectSignatureCurrent.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignatureCurrent.TryGetPreImageContentType(out string? payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignatureCurrent.TryGetPayloadHashAlgorithm(out CoseHashAlgorithm? algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); + #pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature = factory.CreateIndirectSignatureFromHash(hash, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); + CoseSign1Message IndirectSignature = factory.CreateIndirectSignatureFromHash(hash, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); - IndirectSignature.SignatureMatches(randomBytes).Should().BeTrue(); - - Assert.Throws(() => factory.CreateIndirectSignatureFromHash(hashStream, coseSigningKeyProvider, string.Empty)); - hashStream.Seek(0, SeekOrigin.Begin); - - CoseSign1Message IndirectSignatureStreamCurrent = factory.CreateIndirectSignatureFromHash(hashStream, coseSigningKeyProvider, "application/test.payload"); - IndirectSignatureStreamCurrent.IsIndirectSignature().Should().BeTrue(); - IndirectSignatureStreamCurrent.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignatureStreamCurrent.TryGetPreImageContentType(out payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignatureStreamCurrent.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); - algo!.Should().Be(CoseHashAlgorithm.SHA256); - hashStream.Seek(0, SeekOrigin.Begin); - -#pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature2 = factory.CreateIndirectSignatureFromHash(hashStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); + IndirectSignature.SignatureMatches(randomBytes).Should().BeTrue(); + + Assert.Throws(() => factory.CreateIndirectSignatureFromHash(hashStream, coseSigningKeyProvider, string.Empty)); + hashStream.Seek(0, SeekOrigin.Begin); + + CoseSign1Message IndirectSignatureStreamCurrent = factory.CreateIndirectSignatureFromHash(hashStream, coseSigningKeyProvider, "application/test.payload"); + IndirectSignatureStreamCurrent.IsIndirectSignature().Should().BeTrue(); + IndirectSignatureStreamCurrent.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignatureStreamCurrent.TryGetPreImageContentType(out payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignatureStreamCurrent.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); + hashStream.Seek(0, SeekOrigin.Begin); + +#pragma warning disable CS0618 // Type or member is obsolete + CoseSign1Message IndirectSignature2 = factory.CreateIndirectSignatureFromHash(hashStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature2.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature2.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); @@ -164,38 +198,38 @@ public async Task TestCreateIndirectSignatureHashProvidedAsync() hashStream.Seek(0, SeekOrigin.Begin); // test the async methods - Assert.ThrowsAsync(() => factory.CreateIndirectSignatureFromHashAsync(hash, coseSigningKeyProvider, string.Empty)); - - CoseSign1Message IndirectSignatureCurrentAsync = factory.CreateIndirectSignatureFromHash(hash, coseSigningKeyProvider, "application/test.payload"); - IndirectSignatureCurrentAsync.IsIndirectSignature().Should().BeTrue(); - IndirectSignatureCurrentAsync.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignatureCurrentAsync.TryGetPreImageContentType(out payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignatureCurrentAsync.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); - algo!.Should().Be(CoseHashAlgorithm.SHA256); - hashStream.Seek(0, SeekOrigin.Begin); - + Assert.ThrowsAsync(() => factory.CreateIndirectSignatureFromHashAsync(hash, coseSigningKeyProvider, string.Empty)); + + CoseSign1Message IndirectSignatureCurrentAsync = factory.CreateIndirectSignatureFromHash(hash, coseSigningKeyProvider, "application/test.payload"); + IndirectSignatureCurrentAsync.IsIndirectSignature().Should().BeTrue(); + IndirectSignatureCurrentAsync.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignatureCurrentAsync.TryGetPreImageContentType(out payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignatureCurrentAsync.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); + hashStream.Seek(0, SeekOrigin.Begin); + #pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature3 = await factory.CreateIndirectSignatureFromHashAsync(hash, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); + CoseSign1Message IndirectSignature3 = await factory.CreateIndirectSignatureFromHashAsync(hash, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature3.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature3.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); IndirectSignature3.SignatureMatches(randomBytes).Should().BeTrue(); Assert.ThrowsAsync(() => factory.CreateIndirectSignatureFromHashAsync(hashStream, coseSigningKeyProvider, string.Empty)); - hashStream.Seek(0, SeekOrigin.Begin); - - CoseSign1Message IndirectSignatureCurrentStreamAsync = factory.CreateIndirectSignatureFromHash(hash, coseSigningKeyProvider, "application/test.payload"); - IndirectSignatureCurrentStreamAsync.IsIndirectSignature().Should().BeTrue(); - IndirectSignatureCurrentStreamAsync.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignatureCurrentStreamAsync.TryGetPreImageContentType(out payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignatureCurrentStreamAsync.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); - algo!.Should().Be(CoseHashAlgorithm.SHA256); - hashStream.Seek(0, SeekOrigin.Begin); - + hashStream.Seek(0, SeekOrigin.Begin); + + CoseSign1Message IndirectSignatureCurrentStreamAsync = factory.CreateIndirectSignatureFromHash(hash, coseSigningKeyProvider, "application/test.payload"); + IndirectSignatureCurrentStreamAsync.IsIndirectSignature().Should().BeTrue(); + IndirectSignatureCurrentStreamAsync.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignatureCurrentStreamAsync.TryGetPreImageContentType(out payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignatureCurrentStreamAsync.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); + hashStream.Seek(0, SeekOrigin.Begin); + #pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature4 = await factory.CreateIndirectSignatureFromHashAsync(hashStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); + CoseSign1Message IndirectSignature4 = await factory.CreateIndirectSignatureFromHashAsync(hashStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature4.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature4.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); @@ -213,37 +247,37 @@ public async Task TestCreateIndirectSignatureBytesAsync() using MemoryStream memStream = new(randomBytes); // test the sync method - Assert.Throws(() => factory.CreateIndirectSignatureBytes(randomBytes, coseSigningKeyProvider, string.Empty)); - + Assert.Throws(() => factory.CreateIndirectSignatureBytes(randomBytes, coseSigningKeyProvider, string.Empty)); + CoseSign1Message IndirectSignatureCurrent = CoseMessage.DecodeSign1(factory.CreateIndirectSignatureBytes(randomBytes, coseSigningKeyProvider, "application/test.payload").ToArray()); - IndirectSignatureCurrent.IsIndirectSignature().Should().BeTrue(); - IndirectSignatureCurrent.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignatureCurrent.TryGetPreImageContentType(out string? payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignatureCurrent.TryGetPayloadHashAlgorithm(out CoseHashAlgorithm? algo).Should().BeTrue(); - algo!.Should().Be(CoseHashAlgorithm.SHA256); - + IndirectSignatureCurrent.IsIndirectSignature().Should().BeTrue(); + IndirectSignatureCurrent.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignatureCurrent.TryGetPreImageContentType(out string? payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignatureCurrent.TryGetPayloadHashAlgorithm(out CoseHashAlgorithm? algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); + #pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature = CoseMessage.DecodeSign1(factory.CreateIndirectSignatureBytes(randomBytes, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV).ToArray()); + CoseSign1Message IndirectSignature = CoseMessage.DecodeSign1(factory.CreateIndirectSignatureBytes(randomBytes, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV).ToArray()); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); IndirectSignature.SignatureMatches(randomBytes).Should().BeTrue(); - - Assert.Throws(() => factory.CreateIndirectSignatureBytes(memStream, coseSigningKeyProvider, string.Empty)); - memStream.Seek(0, SeekOrigin.Begin); - + + Assert.Throws(() => factory.CreateIndirectSignatureBytes(memStream, coseSigningKeyProvider, string.Empty)); + memStream.Seek(0, SeekOrigin.Begin); + CoseSign1Message IndirectSignatureStreamCurrent = CoseMessage.DecodeSign1(factory.CreateIndirectSignatureBytes(memStream, coseSigningKeyProvider, "application/test.payload").ToArray()); - IndirectSignatureStreamCurrent.IsIndirectSignature().Should().BeTrue(); - IndirectSignatureStreamCurrent.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignatureStreamCurrent.TryGetPreImageContentType(out payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignatureStreamCurrent.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); - algo!.Should().Be(CoseHashAlgorithm.SHA256); - memStream.Seek(0, SeekOrigin.Begin); - + IndirectSignatureStreamCurrent.IsIndirectSignature().Should().BeTrue(); + IndirectSignatureStreamCurrent.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignatureStreamCurrent.TryGetPreImageContentType(out payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignatureStreamCurrent.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); + memStream.Seek(0, SeekOrigin.Begin); + #pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature2 = CoseMessage.DecodeSign1(factory.CreateIndirectSignatureBytes(memStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV).ToArray()); + CoseSign1Message IndirectSignature2 = CoseMessage.DecodeSign1(factory.CreateIndirectSignatureBytes(memStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV).ToArray()); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature2.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature2.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); @@ -251,37 +285,37 @@ public async Task TestCreateIndirectSignatureBytesAsync() memStream.Seek(0, SeekOrigin.Begin); // test the async methods - Assert.ThrowsAsync(() => factory.CreateIndirectSignatureBytesAsync(randomBytes, coseSigningKeyProvider, string.Empty)); - + Assert.ThrowsAsync(() => factory.CreateIndirectSignatureBytesAsync(randomBytes, coseSigningKeyProvider, string.Empty)); + CoseSign1Message IndirectSignatureCurrentAsync = CoseMessage.DecodeSign1(factory.CreateIndirectSignatureBytes(randomBytes, coseSigningKeyProvider, "application/test.payload").ToArray()); - IndirectSignatureCurrentAsync.IsIndirectSignature().Should().BeTrue(); - IndirectSignatureCurrentAsync.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignatureCurrentAsync.TryGetPreImageContentType(out payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignatureCurrentAsync.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); - algo!.Should().Be(CoseHashAlgorithm.SHA256); - + IndirectSignatureCurrentAsync.IsIndirectSignature().Should().BeTrue(); + IndirectSignatureCurrentAsync.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignatureCurrentAsync.TryGetPreImageContentType(out payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignatureCurrentAsync.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); + #pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature3 = CoseMessage.DecodeSign1((await factory.CreateIndirectSignatureBytesAsync(randomBytes, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV)).ToArray()); + CoseSign1Message IndirectSignature3 = CoseMessage.DecodeSign1((await factory.CreateIndirectSignatureBytesAsync(randomBytes, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV)).ToArray()); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature3.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature3.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); IndirectSignature3.SignatureMatches(randomBytes).Should().BeTrue(); Assert.ThrowsAsync(() => factory.CreateIndirectSignatureBytesAsync(memStream, coseSigningKeyProvider, string.Empty)); - memStream.Seek(0, SeekOrigin.Begin); - + memStream.Seek(0, SeekOrigin.Begin); + CoseSign1Message IndirectSignatureCurrentStreamAsync = CoseMessage.DecodeSign1((await factory.CreateIndirectSignatureBytesAsync(memStream, coseSigningKeyProvider, "application/test.payload")).ToArray()); - IndirectSignatureCurrentStreamAsync.IsIndirectSignature().Should().BeTrue(); - IndirectSignatureCurrentStreamAsync.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignatureCurrentStreamAsync.TryGetPreImageContentType(out payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignatureCurrentStreamAsync.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); - algo!.Should().Be(CoseHashAlgorithm.SHA256); - memStream.Seek(0, SeekOrigin.Begin); - + IndirectSignatureCurrentStreamAsync.IsIndirectSignature().Should().BeTrue(); + IndirectSignatureCurrentStreamAsync.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignatureCurrentStreamAsync.TryGetPreImageContentType(out payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignatureCurrentStreamAsync.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); + memStream.Seek(0, SeekOrigin.Begin); + #pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature4 = CoseMessage.DecodeSign1((await factory.CreateIndirectSignatureBytesAsync(memStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV)).ToArray()); + CoseSign1Message IndirectSignature4 = CoseMessage.DecodeSign1((await factory.CreateIndirectSignatureBytesAsync(memStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV)).ToArray()); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature4.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature4.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); @@ -302,37 +336,37 @@ public async Task TestCreateIndirectSignatureBytesHashProvidedAsync() using MemoryStream hashStream = new(hash); // test the sync method - Assert.Throws(() => factory.CreateIndirectSignatureBytesFromHash(hash, coseSigningKeyProvider, string.Empty)); - + Assert.Throws(() => factory.CreateIndirectSignatureBytesFromHash(hash, coseSigningKeyProvider, string.Empty)); + CoseSign1Message IndirectSignatureCurrent = CoseMessage.DecodeSign1(factory.CreateIndirectSignatureBytesFromHash(hash, coseSigningKeyProvider, "application/test.payload").ToArray()); - IndirectSignatureCurrent.IsIndirectSignature().Should().BeTrue(); - IndirectSignatureCurrent.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignatureCurrent.TryGetPreImageContentType(out string? payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignatureCurrent.TryGetPayloadHashAlgorithm(out CoseHashAlgorithm? algo).Should().BeTrue(); - algo!.Should().Be(CoseHashAlgorithm.SHA256); - + IndirectSignatureCurrent.IsIndirectSignature().Should().BeTrue(); + IndirectSignatureCurrent.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignatureCurrent.TryGetPreImageContentType(out string? payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignatureCurrent.TryGetPayloadHashAlgorithm(out CoseHashAlgorithm? algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); + #pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature = CoseMessage.DecodeSign1(factory.CreateIndirectSignatureBytesFromHash(hash, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV).ToArray()); + CoseSign1Message IndirectSignature = CoseMessage.DecodeSign1(factory.CreateIndirectSignatureBytesFromHash(hash, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV).ToArray()); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); IndirectSignature.SignatureMatches(randomBytes).Should().BeTrue(); - Assert.Throws(() => factory.CreateIndirectSignatureBytesFromHash(hashStream, coseSigningKeyProvider, string.Empty)); - hashStream.Seek(0, SeekOrigin.Begin); - + Assert.Throws(() => factory.CreateIndirectSignatureBytesFromHash(hashStream, coseSigningKeyProvider, string.Empty)); + hashStream.Seek(0, SeekOrigin.Begin); + CoseSign1Message IndirectSignatureStreamCurrent = CoseMessage.DecodeSign1(factory.CreateIndirectSignatureBytesFromHash(hashStream, coseSigningKeyProvider, "application/test.payload").ToArray()); - IndirectSignatureStreamCurrent.IsIndirectSignature().Should().BeTrue(); - IndirectSignatureStreamCurrent.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignatureStreamCurrent.TryGetPreImageContentType(out payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignatureStreamCurrent.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); - algo!.Should().Be(CoseHashAlgorithm.SHA256); - hashStream.Seek(0, SeekOrigin.Begin); - + IndirectSignatureStreamCurrent.IsIndirectSignature().Should().BeTrue(); + IndirectSignatureStreamCurrent.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignatureStreamCurrent.TryGetPreImageContentType(out payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignatureStreamCurrent.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); + hashStream.Seek(0, SeekOrigin.Begin); + #pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature2 = CoseMessage.DecodeSign1(factory.CreateIndirectSignatureBytesFromHash(hashStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV).ToArray()); + CoseSign1Message IndirectSignature2 = CoseMessage.DecodeSign1(factory.CreateIndirectSignatureBytesFromHash(hashStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV).ToArray()); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature2.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature2.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); @@ -340,37 +374,37 @@ public async Task TestCreateIndirectSignatureBytesHashProvidedAsync() hashStream.Seek(0, SeekOrigin.Begin); // test the async methods - Assert.ThrowsAsync(() => factory.CreateIndirectSignatureBytesFromHashAsync(hash, coseSigningKeyProvider, string.Empty)); - + Assert.ThrowsAsync(() => factory.CreateIndirectSignatureBytesFromHashAsync(hash, coseSigningKeyProvider, string.Empty)); + CoseSign1Message IndirectSignatureHashCurrent = CoseMessage.DecodeSign1(factory.CreateIndirectSignatureBytesFromHash(hash, coseSigningKeyProvider, "application/test.payload").ToArray()); - IndirectSignatureHashCurrent.IsIndirectSignature().Should().BeTrue(); - IndirectSignatureHashCurrent.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignatureHashCurrent.TryGetPreImageContentType(out payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignatureHashCurrent.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); - algo!.Should().Be(CoseHashAlgorithm.SHA256); - + IndirectSignatureHashCurrent.IsIndirectSignature().Should().BeTrue(); + IndirectSignatureHashCurrent.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignatureHashCurrent.TryGetPreImageContentType(out payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignatureHashCurrent.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); + #pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature3 = CoseMessage.DecodeSign1((await factory.CreateIndirectSignatureBytesFromHashAsync(hash, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV)).ToArray()); + CoseSign1Message IndirectSignature3 = CoseMessage.DecodeSign1((await factory.CreateIndirectSignatureBytesFromHashAsync(hash, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV)).ToArray()); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature3.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature3.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); IndirectSignature3.SignatureMatches(randomBytes).Should().BeTrue(); Assert.ThrowsAsync(() => factory.CreateIndirectSignatureBytesFromHashAsync(hashStream, coseSigningKeyProvider, string.Empty)); - hashStream.Seek(0, SeekOrigin.Begin); - + hashStream.Seek(0, SeekOrigin.Begin); + CoseSign1Message IndirectSignatureHashCurrentAsync = CoseMessage.DecodeSign1((await factory.CreateIndirectSignatureBytesFromHashAsync(hashStream, coseSigningKeyProvider, "application/test.payload")).ToArray()); - IndirectSignatureHashCurrentAsync.IsIndirectSignature().Should().BeTrue(); - IndirectSignatureHashCurrentAsync.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignatureHashCurrentAsync.TryGetPreImageContentType(out payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignatureHashCurrentAsync.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); - algo!.Should().Be(CoseHashAlgorithm.SHA256); - hashStream.Seek(0, SeekOrigin.Begin); - + IndirectSignatureHashCurrentAsync.IsIndirectSignature().Should().BeTrue(); + IndirectSignatureHashCurrentAsync.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignatureHashCurrentAsync.TryGetPreImageContentType(out payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignatureHashCurrentAsync.TryGetPayloadHashAlgorithm(out algo).Should().BeTrue(); + algo!.Should().Be(CoseHashAlgorithm.SHA256); + hashStream.Seek(0, SeekOrigin.Begin); + #pragma warning disable CS0618 // Type or member is obsolete - CoseSign1Message IndirectSignature4 = CoseMessage.DecodeSign1((await factory.CreateIndirectSignatureBytesFromHashAsync(hashStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV)).ToArray()); + CoseSign1Message IndirectSignature4 = CoseMessage.DecodeSign1((await factory.CreateIndirectSignatureBytesFromHashAsync(hashStream, coseSigningKeyProvider, "application/test.payload", IndirectSignatureFactory.IndirectSignatureVersion.CoseHashV)).ToArray()); #pragma warning restore CS0618 // Type or member is obsolete IndirectSignature4.ProtectedHeaders.ContainsKey(CoseHeaderLabel.ContentType).Should().BeTrue(); IndirectSignature4.ProtectedHeaders[CoseHeaderLabel.ContentType].GetValueAsString().Should().Be("application/test.payload+cose-hash-v"); @@ -400,10 +434,10 @@ public void TestCreateIndirectSignatureAlreadyProvided() Assert.Throws(() => factory.CreateIndirectSignature(hash, coseSigningKeyProvider, string.Empty)); CoseSign1Message IndirectSignature = CoseMessage.DecodeSign1(factory.CreateIndirectSignatureBytes(randomBytes, coseSigningKeyProvider, "application/test.payload").ToArray()); IndirectSignature.IsIndirectSignature().Should().BeTrue(); - IndirectSignature.SignatureMatches(randomBytes).Should().BeTrue(); - IndirectSignature.TryGetPreImageContentType(out string? payloadType).Should().Be(true); - payloadType!.Should().Be("application/test.payload"); - IndirectSignature.TryGetPayloadHashAlgorithm(out CoseHashAlgorithm? algo).Should().BeTrue(); + IndirectSignature.SignatureMatches(randomBytes).Should().BeTrue(); + IndirectSignature.TryGetPreImageContentType(out string? payloadType).Should().Be(true); + payloadType!.Should().Be("application/test.payload"); + IndirectSignature.TryGetPayloadHashAlgorithm(out CoseHashAlgorithm? algo).Should().BeTrue(); algo!.Should().Be(CoseHashAlgorithm.SHA256); } } diff --git a/CoseIndirectSignature/IndirectSignatureFactory.Bytes.cs b/CoseIndirectSignature/IndirectSignatureFactory.Bytes.cs index bbf8b66f..b2ba9d8f 100644 --- a/CoseIndirectSignature/IndirectSignatureFactory.Bytes.cs +++ b/CoseIndirectSignature/IndirectSignatureFactory.Bytes.cs @@ -17,13 +17,15 @@ public sealed partial class IndirectSignatureFactory /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// True to use the older format - CoseHashV, False to use CoseHashEnvelope format (default). + /// Optional header extender to add custom headers to the COSE message. /// A CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null public CoseSign1Message CreateIndirectSignature( ReadOnlyMemory payload, ICoseSigningKeyProvider signingKeyProvider, string contentType, - bool useOldFormat = false) => + bool useOldFormat = false, + ICoseHeaderExtender coseHeaderExtender = null) => CreateIndirectSignature( payload: payload, signingKeyProvider: signingKeyProvider, @@ -33,7 +35,8 @@ public CoseSign1Message CreateIndirectSignature( #pragma warning disable CS0618 // Type or member is obsolete ? IndirectSignatureVersion.CoseHashV #pragma warning restore CS0618 // Type or member is obsolete - : IndirectSignatureVersion.CoseHashEnvelope); + : IndirectSignatureVersion.CoseHashEnvelope, + coseHeaderExtender: coseHeaderExtender); /// /// Creates a Indirect signature of the payload given a hash of the payload returned as a following the rules in this class description. @@ -42,6 +45,7 @@ public CoseSign1Message CreateIndirectSignature( /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// True to use the older format - CoseHashV, False to use CoseHashEnvelope format (default). + /// Optional header extender to add custom headers to the COSE message. /// A CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null /// Hash size does not correspond to any known hash algorithms @@ -49,7 +53,8 @@ public CoseSign1Message CreateIndirectSignatureFromHash( ReadOnlyMemory rawHash, ICoseSigningKeyProvider signingKeyProvider, string contentType, - bool useOldFormat = false) => + bool useOldFormat = false, + ICoseHeaderExtender? coseHeaderExtender = null) => CreateIndirectSignatureFromHash( rawHash: rawHash, signingKeyProvider: signingKeyProvider, @@ -59,7 +64,8 @@ public CoseSign1Message CreateIndirectSignatureFromHash( #pragma warning disable CS0618 // Type or member is obsolete ? IndirectSignatureVersion.CoseHashV #pragma warning restore CS0618 // Type or member is obsolete - : IndirectSignatureVersion.CoseHashEnvelope); + : IndirectSignatureVersion.CoseHashEnvelope, + coseHeaderExtender: coseHeaderExtender); #endregion #region async old signature - backwards compatibility /// @@ -69,13 +75,15 @@ public CoseSign1Message CreateIndirectSignatureFromHash( /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// True to use the older format - CoseHashV, False to use CoseHashEnvelope format (default). + /// Optional header extender to add custom headers to the COSE message. /// A Task which can be awaited which will return a CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null public Task CreateIndirectSignatureAsync( ReadOnlyMemory payload, ICoseSigningKeyProvider signingKeyProvider, string contentType, - bool useOldFormat = false) => + bool useOldFormat = false, + ICoseHeaderExtender? coseHeaderExtender = null) => CreateIndirectSignatureAsync( payload: payload, signingKeyProvider: signingKeyProvider, @@ -85,7 +93,8 @@ public Task CreateIndirectSignatureAsync( #pragma warning disable CS0618 // Type or member is obsolete ? IndirectSignatureVersion.CoseHashV #pragma warning restore CS0618 // Type or member is obsolete - : IndirectSignatureVersion.CoseHashEnvelope); + : IndirectSignatureVersion.CoseHashEnvelope, + coseHeaderExtender: coseHeaderExtender); /// /// Creates a Indirect signature of the payload given a hash of the payload returned as a following the rules in this class description. @@ -94,6 +103,7 @@ public Task CreateIndirectSignatureAsync( /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// True to use the older format - CoseHashV, False to use CoseHashEnvelope format (default). + /// Optional header extender to add custom headers to the COSE message. /// A CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null /// Hash size does not correspond to any known hash algorithms @@ -101,7 +111,8 @@ public Task CreateIndirectSignatureFromHashAsync( ReadOnlyMemory rawHash, ICoseSigningKeyProvider signingKeyProvider, string contentType, - bool useOldFormat = false) => + bool useOldFormat = false, + ICoseHeaderExtender? coseHeaderExtender = null) => CreateIndirectSignatureFromHashAsync( rawHash: rawHash, signingKeyProvider: signingKeyProvider, @@ -111,7 +122,8 @@ public Task CreateIndirectSignatureFromHashAsync( #pragma warning disable CS0618 // Type or member is obsolete ? IndirectSignatureVersion.CoseHashV #pragma warning restore CS0618 // Type or member is obsolete - : IndirectSignatureVersion.CoseHashEnvelope); + : IndirectSignatureVersion.CoseHashEnvelope, + coseHeaderExtender: coseHeaderExtender); #endregion #region sync new signature @@ -122,19 +134,22 @@ public Task CreateIndirectSignatureFromHashAsync( /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// The this factory should create. + /// Optional header extender to add custom headers to the COSE message. /// A CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null public CoseSign1Message CreateIndirectSignature( ReadOnlyMemory payload, ICoseSigningKeyProvider signingKeyProvider, string contentType, - IndirectSignatureVersion signatureVersion) => + IndirectSignatureVersion signatureVersion, + ICoseHeaderExtender? coseHeaderExtender = null) => (CoseSign1Message)CreateIndirectSignatureWithChecksInternal( returnBytes: false, signingKeyProvider: signingKeyProvider, contentType: contentType, bytePayload: payload, - signatureVersion: signatureVersion); + signatureVersion: signatureVersion, + headerExtender: coseHeaderExtender); /// /// Creates a Indirect signature of the payload given a hash of the payload returned as a following the rules in this class description. @@ -143,6 +158,7 @@ public CoseSign1Message CreateIndirectSignature( /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// The this factory should create. + /// Optional header extender to add custom headers to the COSE message. /// A CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null /// Hash size does not correspond to any known hash algorithms @@ -150,14 +166,16 @@ public CoseSign1Message CreateIndirectSignatureFromHash( ReadOnlyMemory rawHash, ICoseSigningKeyProvider signingKeyProvider, string contentType, - IndirectSignatureVersion signatureVersion) => + IndirectSignatureVersion signatureVersion, + ICoseHeaderExtender? coseHeaderExtender = null) => (CoseSign1Message)CreateIndirectSignatureWithChecksInternal( returnBytes: false, signingKeyProvider: signingKeyProvider, contentType: contentType, bytePayload: rawHash, payloadHashed: true, - signatureVersion: signatureVersion); + signatureVersion: signatureVersion, + headerExtender: coseHeaderExtender); #endregion #region async new signature /// @@ -167,20 +185,23 @@ public CoseSign1Message CreateIndirectSignatureFromHash( /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// The this factory should create.. + /// Optional header extender to add custom headers to the COSE message. /// A Task which can be awaited which will return a CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null public Task CreateIndirectSignatureAsync( ReadOnlyMemory payload, ICoseSigningKeyProvider signingKeyProvider, string contentType, - IndirectSignatureVersion signatureVersion) => + IndirectSignatureVersion signatureVersion, + ICoseHeaderExtender? coseHeaderExtender = null) => Task.FromResult( (CoseSign1Message)CreateIndirectSignatureWithChecksInternal( returnBytes: false, signingKeyProvider: signingKeyProvider, contentType: contentType, bytePayload: payload, - signatureVersion: signatureVersion)); + signatureVersion: signatureVersion, + headerExtender: coseHeaderExtender)); /// /// Creates a Indirect signature of the payload given a hash of the payload returned as a following the rules in this class description. @@ -189,6 +210,7 @@ public Task CreateIndirectSignatureAsync( /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// The this factory should create. + /// Optional header extender to add custom headers to the COSE message. /// A CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null /// Hash size does not correspond to any known hash algorithms @@ -196,7 +218,8 @@ public Task CreateIndirectSignatureFromHashAsync( ReadOnlyMemory rawHash, ICoseSigningKeyProvider signingKeyProvider, string contentType, - IndirectSignatureVersion signatureVersion) => + IndirectSignatureVersion signatureVersion, + ICoseHeaderExtender? coseHeaderExtender = null) => Task.FromResult( (CoseSign1Message)CreateIndirectSignatureWithChecksInternal( returnBytes: false, @@ -204,7 +227,8 @@ public Task CreateIndirectSignatureFromHashAsync( contentType: contentType, bytePayload: rawHash, payloadHashed: true, - signatureVersion: signatureVersion)); + signatureVersion: signatureVersion, + headerExtender: coseHeaderExtender)); #endregion #endregion @@ -217,13 +241,15 @@ public Task CreateIndirectSignatureFromHashAsync( /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// True to use the older format - CoseHashV, False to use CoseHashEnvelope format (default). + /// Optional header extender to add custom headers to the COSE message. /// A byte[] representation of a CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null public ReadOnlyMemory CreateIndirectSignatureBytes( ReadOnlyMemory payload, ICoseSigningKeyProvider signingKeyProvider, string contentType, - bool useOldFormat = false) => + bool useOldFormat = false, + ICoseHeaderExtender? coseHeaderExtender = null) => CreateIndirectSignatureBytes( payload: payload, signingKeyProvider: signingKeyProvider, @@ -233,7 +259,8 @@ public ReadOnlyMemory CreateIndirectSignatureBytes( #pragma warning disable CS0618 // Type or member is obsolete ? IndirectSignatureVersion.CoseHashV #pragma warning restore CS0618 // Type or member is obsolete - : IndirectSignatureVersion.CoseHashEnvelope); + : IndirectSignatureVersion.CoseHashEnvelope, + coseHeaderExtender: coseHeaderExtender); /// /// Creates a Indirect signature of the payload given a hash of the payload returned as a following the rules in this class description. @@ -242,6 +269,7 @@ public ReadOnlyMemory CreateIndirectSignatureBytes( /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// True to use the older format - CoseHashV, False to use CoseHashEnvelope format (default). + /// Optional header extender to add custom headers to the COSE message. /// A byte[] representation of a CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null /// Hash size does not correspond to any known hash algorithms @@ -249,7 +277,8 @@ public ReadOnlyMemory CreateIndirectSignatureBytesFromHash( ReadOnlyMemory rawHash, ICoseSigningKeyProvider signingKeyProvider, string contentType, - bool useOldFormat = false) => + bool useOldFormat = false, + ICoseHeaderExtender? coseHeaderExtender = null) => CreateIndirectSignatureBytesFromHash( rawHash: rawHash, signingKeyProvider: signingKeyProvider, @@ -259,7 +288,8 @@ public ReadOnlyMemory CreateIndirectSignatureBytesFromHash( #pragma warning disable CS0618 // Type or member is obsolete ? IndirectSignatureVersion.CoseHashV #pragma warning restore CS0618 // Type or member is obsolete - : IndirectSignatureVersion.CoseHashEnvelope); + : IndirectSignatureVersion.CoseHashEnvelope, + coseHeaderExtender: coseHeaderExtender); #endregion #region async old signature - backwards compatibility /// @@ -269,13 +299,15 @@ public ReadOnlyMemory CreateIndirectSignatureBytesFromHash( /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// True to use the older format - CoseHashV, False to use CoseHashEnvelope format (default). + /// Optional header extender to add custom headers to the COSE message. /// A Task which when completed returns a byte[] representation of a CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null public Task> CreateIndirectSignatureBytesAsync( ReadOnlyMemory payload, ICoseSigningKeyProvider signingKeyProvider, string contentType, - bool useOldFormat = false) => + bool useOldFormat = false, + ICoseHeaderExtender? coseHeaderExtender = null) => CreateIndirectSignatureBytesAsync( payload: payload, signingKeyProvider: signingKeyProvider, @@ -285,7 +317,8 @@ public Task> CreateIndirectSignatureBytesAsync( #pragma warning disable CS0618 // Type or member is obsolete ? IndirectSignatureVersion.CoseHashV #pragma warning restore CS0618 // Type or member is obsolete - : IndirectSignatureVersion.CoseHashEnvelope); + : IndirectSignatureVersion.CoseHashEnvelope, + coseHeaderExtender: coseHeaderExtender); /// /// Creates a Indirect signature of the payload given a hash of the payload returned as a following the rules in this class description. @@ -294,6 +327,7 @@ public Task> CreateIndirectSignatureBytesAsync( /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// True to use the older format - CoseHashV, False to use CoseHashEnvelope format (default). + /// Optional header extender to add custom headers to the COSE message. /// A Task which when completed returns a byte[] representation of a CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null /// Hash size does not correspond to any known hash algorithms @@ -301,7 +335,8 @@ public Task> CreateIndirectSignatureBytesFromHashAsync( ReadOnlyMemory rawHash, ICoseSigningKeyProvider signingKeyProvider, string contentType, - bool useOldFormat = false) => + bool useOldFormat = false, + ICoseHeaderExtender? coseHeaderExtender = null) => CreateIndirectSignatureBytesFromHashAsync( rawHash: rawHash, signingKeyProvider: signingKeyProvider, @@ -311,7 +346,8 @@ public Task> CreateIndirectSignatureBytesFromHashAsync( #pragma warning disable CS0618 // Type or member is obsolete ? IndirectSignatureVersion.CoseHashV #pragma warning restore CS0618 // Type or member is obsolete - : IndirectSignatureVersion.CoseHashEnvelope); + : IndirectSignatureVersion.CoseHashEnvelope, + coseHeaderExtender: coseHeaderExtender); #endregion #region sync new signature /// @@ -321,19 +357,22 @@ public Task> CreateIndirectSignatureBytesFromHashAsync( /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// The this factory should create. + /// Optional header extender to add custom headers to the COSE message. /// A byte[] representation of a CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null public ReadOnlyMemory CreateIndirectSignatureBytes( ReadOnlyMemory payload, ICoseSigningKeyProvider signingKeyProvider, string contentType, - IndirectSignatureVersion signatureVersion) => + IndirectSignatureVersion signatureVersion, + ICoseHeaderExtender? coseHeaderExtender = null) => (ReadOnlyMemory)CreateIndirectSignatureWithChecksInternal( returnBytes: true, signingKeyProvider: signingKeyProvider, contentType: contentType, bytePayload: payload, - signatureVersion: signatureVersion); + signatureVersion: signatureVersion, + headerExtender: coseHeaderExtender); /// /// Creates a Indirect signature of the payload given a hash of the payload returned as a following the rules in this class description. @@ -342,6 +381,7 @@ public ReadOnlyMemory CreateIndirectSignatureBytes( /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// The this factory should create. + /// Optional header extender to add custom headers to the COSE message. /// A byte[] representation of a CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null /// Hash size does not correspond to any known hash algorithms @@ -349,14 +389,16 @@ public ReadOnlyMemory CreateIndirectSignatureBytesFromHash( ReadOnlyMemory rawHash, ICoseSigningKeyProvider signingKeyProvider, string contentType, - IndirectSignatureVersion signatureVersion) => + IndirectSignatureVersion signatureVersion, + ICoseHeaderExtender? coseHeaderExtender = null) => (ReadOnlyMemory)CreateIndirectSignatureWithChecksInternal( returnBytes: true, signingKeyProvider: signingKeyProvider, contentType: contentType, bytePayload: rawHash, payloadHashed: true, - signatureVersion: signatureVersion); + signatureVersion: signatureVersion, + headerExtender: coseHeaderExtender); #endregion #region async old signature - backwards compatibility @@ -367,20 +409,23 @@ public ReadOnlyMemory CreateIndirectSignatureBytesFromHash( /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// The this factory should create. + /// Optional header extender to add custom headers to the COSE message. /// A Task which when completed returns a byte[] representation of a CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null public Task> CreateIndirectSignatureBytesAsync( ReadOnlyMemory payload, ICoseSigningKeyProvider signingKeyProvider, string contentType, - IndirectSignatureVersion signatureVersion) => + IndirectSignatureVersion signatureVersion, + ICoseHeaderExtender? coseHeaderExtender = null) => Task.FromResult( (ReadOnlyMemory)CreateIndirectSignatureWithChecksInternal( returnBytes: true, signingKeyProvider: signingKeyProvider, contentType: contentType, bytePayload: payload, - signatureVersion: signatureVersion)); + signatureVersion: signatureVersion, + headerExtender: coseHeaderExtender)); /// /// Creates a Indirect signature of the payload given a hash of the payload returned as a following the rules in this class description. @@ -389,6 +434,7 @@ public Task> CreateIndirectSignatureBytesAsync( /// The COSE signing key provider to be used for the signing operation within the . /// A media type string following https://datatracker.ietf.org/doc/html/rfc6838. /// The this factory should create. + /// Optional header extender to add custom headers to the COSE message. /// A Task which when completed returns a byte[] representation of a CoseSign1Message which can be used as a Indirect signature validation of the payload. /// The contentType parameter was empty or null /// Hash size does not correspond to any known hash algorithms @@ -396,7 +442,8 @@ public Task> CreateIndirectSignatureBytesFromHashAsync( ReadOnlyMemory rawHash, ICoseSigningKeyProvider signingKeyProvider, string contentType, - IndirectSignatureVersion signatureVersion) => + IndirectSignatureVersion signatureVersion, + ICoseHeaderExtender? coseHeaderExtender = null) => Task.FromResult( (ReadOnlyMemory)CreateIndirectSignatureWithChecksInternal( returnBytes: true, @@ -404,7 +451,8 @@ public Task> CreateIndirectSignatureBytesFromHashAsync( contentType: contentType, bytePayload: rawHash, payloadHashed: true, - signatureVersion: signatureVersion)); + signatureVersion: signatureVersion, + headerExtender: coseHeaderExtender)); #endregion #endregion } diff --git a/CoseIndirectSignature/IndirectSignatureFactory.CoseHashEnvelope.cs b/CoseIndirectSignature/IndirectSignatureFactory.CoseHashEnvelope.cs index 894e58d7..88277c36 100644 --- a/CoseIndirectSignature/IndirectSignatureFactory.CoseHashEnvelope.cs +++ b/CoseIndirectSignature/IndirectSignatureFactory.CoseHashEnvelope.cs @@ -18,6 +18,7 @@ public sealed partial class IndirectSignatureFactory /// If not null, then Stream API's on the CoseSign1MessageFactory are used. /// If streamPayload is null then this must be specified and must not be null and will use the Byte API's on the CoseSign1MesssageFactory /// True if the payload represents the raw hash + /// Optional header extender to add custom headers to the COSE message. /// Either a CoseSign1Message or a ReadOnlyMemory{byte} representing the CoseSign1Message object. /// The contentType parameter was empty or null /// Either streamPayload or bytePayload must be specified, but not both at the same time, or both cannot be null @@ -28,7 +29,8 @@ private object CreateIndirectSignatureWithChecksInternalCoseHashEnvelopeFormat( string contentType, Stream? streamPayload = null, ReadOnlyMemory? bytePayload = null, - bool payloadHashed = false) + bool payloadHashed = false, + ICoseHeaderExtender? headerExtender = null) { ReadOnlyMemory hash; HashAlgorithmName algoName = InternalHashAlgorithmName; @@ -54,16 +56,20 @@ private object CreateIndirectSignatureWithChecksInternalCoseHashEnvelopeFormat( } } + ICoseHeaderExtender effectiveHeaderExtender = headerExtender == null ? + new CoseHashEnvelopeHeaderExtender(algoName, contentType, null) : + new ChainedCoseHeaderExtender(new[] { headerExtender, new CoseHashEnvelopeHeaderExtender(algoName, contentType, null) }); + return returnBytes ? InternalMessageFactory.CreateCoseSign1MessageBytes( hash, signingKeyProvider, embedPayload: true, - headerExtender: new CoseHashEnvelopeHeaderExtender(algoName, contentType, null)) + headerExtender: effectiveHeaderExtender) : InternalMessageFactory.CreateCoseSign1Message( hash, signingKeyProvider, embedPayload: true, - headerExtender: new CoseHashEnvelopeHeaderExtender(algoName, contentType, null)); + headerExtender: effectiveHeaderExtender); } } diff --git a/CoseIndirectSignature/IndirectSignatureFactory.CoseHashV.cs b/CoseIndirectSignature/IndirectSignatureFactory.CoseHashV.cs index eacc44bd..a546eea1 100644 --- a/CoseIndirectSignature/IndirectSignatureFactory.CoseHashV.cs +++ b/CoseIndirectSignature/IndirectSignatureFactory.CoseHashV.cs @@ -17,6 +17,7 @@ public sealed partial class IndirectSignatureFactory /// If not null, then Stream API's on the CoseSign1MessageFactory are used. /// If streamPayload is null then this must be specified and must not be null and will use the Byte API's on the CoseSign1MesssageFactory /// True if the payload represents the raw hash + /// Optional header extender to add custom headers to the COSE message. /// Either a CoseSign1Message or a ReadOnlyMemory{byte} representing the CoseSign1Message object. /// The contentType parameter was empty or null /// Either streamPayload or bytePayload must be specified, but not both at the same time, or both cannot be null @@ -27,7 +28,8 @@ private object CreateIndirectSignatureWithChecksInternalCoseHashVFormat( string contentType, Stream? streamPayload = null, ReadOnlyMemory? bytePayload = null, - bool payloadHashed = false) + bool payloadHashed = false, + ICoseHeaderExtender? headerExtender = null) { CoseHashV hash; string extendedContentType = ExtendContentTypeCoseHashV(contentType); @@ -61,13 +63,15 @@ private object CreateIndirectSignatureWithChecksInternalCoseHashVFormat( hash.Serialize(), signingKeyProvider, embedPayload: true, - contentType: extendedContentType) + contentType: extendedContentType, + headerExtender: headerExtender) // return the CoseSign1Message object : InternalMessageFactory.CreateCoseSign1Message( hash.Serialize(), signingKeyProvider, embedPayload: true, - contentType: extendedContentType); + contentType: extendedContentType, + headerExtender: headerExtender); } /// diff --git a/CoseIndirectSignature/IndirectSignatureFactory.Direct.cs b/CoseIndirectSignature/IndirectSignatureFactory.Direct.cs index 47496ad7..43cd428a 100644 --- a/CoseIndirectSignature/IndirectSignatureFactory.Direct.cs +++ b/CoseIndirectSignature/IndirectSignatureFactory.Direct.cs @@ -17,6 +17,7 @@ public sealed partial class IndirectSignatureFactory /// If not null, then Stream API's on the CoseSign1MessageFactory are used. /// If streamPayload is null then this must be specified and must not be null and will use the Byte API's on the CoseSign1MesssageFactory /// True if the payload represents the raw hash + /// Optional header extender to add custom headers to the COSE message. /// Either a CoseSign1Message or a ReadOnlyMemory{byte} representing the CoseSign1Message object. /// The contentType parameter was empty or null /// Either streamPayload or bytePayload must be specified, but not both at the same time, or both cannot be null @@ -27,7 +28,8 @@ private object CreateIndirectSignatureWithChecksInternalDirectFormat( string contentType, Stream? streamPayload = null, ReadOnlyMemory? bytePayload = null, - bool payloadHashed = false) + bool payloadHashed = false, + ICoseHeaderExtender? headerExtender = null) { ReadOnlyMemory hash; string extendedContentType; @@ -59,12 +61,14 @@ private object CreateIndirectSignatureWithChecksInternalDirectFormat( hash, signingKeyProvider, embedPayload: true, - contentType: extendedContentType) + contentType: extendedContentType, + headerExtender: headerExtender) : InternalMessageFactory.CreateCoseSign1Message( hash, signingKeyProvider, embedPayload: true, - contentType: extendedContentType); + contentType: extendedContentType, + headerExtender: headerExtender); } /// diff --git a/CoseIndirectSignature/IndirectSignatureFactory.cs b/CoseIndirectSignature/IndirectSignatureFactory.cs index 7086a5bc..958a2bbb 100644 --- a/CoseIndirectSignature/IndirectSignatureFactory.cs +++ b/CoseIndirectSignature/IndirectSignatureFactory.cs @@ -14,27 +14,27 @@ namespace CoseIndirectSignature; /// The default hash algorithm used is . /// public sealed partial class IndirectSignatureFactory : IDisposable -{ - /// - /// The version of the indirect signature to be used. - /// - public enum IndirectSignatureVersion - { - /// - /// The older format, which is not recommended for new applications and is included for backwards compatibility. - /// - [Obsolete("Use CoseHashEnvelope instead")] - Direct, - /// - /// The CoseHashV format, which is not recommended for new applications and is included for backwards compatibility. - /// - [Obsolete("Use CoseHashEnvelope instead")] - CoseHashV, - /// - /// The CoseHashEnvelope format, which is the recommended format for new applications. - /// - CoseHashEnvelope - } +{ + /// + /// The version of the indirect signature to be used. + /// + public enum IndirectSignatureVersion + { + /// + /// The older format, which is not recommended for new applications and is included for backwards compatibility. + /// + [Obsolete("Use CoseHashEnvelope instead")] + Direct, + /// + /// The CoseHashV format, which is not recommended for new applications and is included for backwards compatibility. + /// + [Obsolete("Use CoseHashEnvelope instead")] + CoseHashV, + /// + /// The CoseHashEnvelope format, which is the recommended format for new applications. + /// + CoseHashEnvelope + } private readonly HashAlgorithm InternalHashAlgorithm; private readonly uint HashLength; @@ -96,8 +96,8 @@ private CoseHashAlgorithm GetCoseHashAlgorithmFromHashAlgorithm(HashAlgorithm al SHA512 => CoseHashAlgorithm.SHA512, _ => throw new ArgumentException($@"No mapping for hash algorithm {algorithm.GetType().FullName} to any {nameof(CoseHashAlgorithm)}") }; - } - + } + /// /// Does the heavy lifting for this class in computing the hash and creating the correct representation of the CoseSign1Message base on input. /// @@ -108,6 +108,7 @@ private CoseHashAlgorithm GetCoseHashAlgorithmFromHashAlgorithm(HashAlgorithm al /// If streamPayload is null then this must be specified and must not be null and will use the Byte API's on the CoseSign1MesssageFactory /// True if the payload represents the raw hash /// The this factory should create. + /// An optional to extend the protected headers of the CoseSign1Message. /// Either a CoseSign1Message or a ReadOnlyMemory{byte} representing the CoseSign1Message object. /// The contentType parameter was empty or null /// Either streamPayload or bytePayload must be specified, but not both at the same time, or both cannot be null @@ -115,11 +116,12 @@ private CoseHashAlgorithm GetCoseHashAlgorithmFromHashAlgorithm(HashAlgorithm al private object CreateIndirectSignatureWithChecksInternal( bool returnBytes, ICoseSigningKeyProvider signingKeyProvider, - string contentType, + string contentType, IndirectSignatureVersion signatureVersion, Stream? streamPayload = null, ReadOnlyMemory? bytePayload = null, - bool payloadHashed = false) + bool payloadHashed = false, + ICoseHeaderExtender? headerExtender = null) { if (string.IsNullOrWhiteSpace(contentType)) { @@ -131,42 +133,45 @@ private object CreateIndirectSignatureWithChecksInternal( { throw new ArgumentNullException("payload", "Either streamPayload or bytePayload must be specified, but not both at the same time, or both cannot be null"); } - - switch(signatureVersion) - { -#pragma warning disable CS0618 // Type or member is obsolete - case IndirectSignatureVersion.Direct: -#pragma warning restore CS0618 // Type or member is obsolete - return CreateIndirectSignatureWithChecksInternalDirectFormat( - returnBytes, - signingKeyProvider, - contentType, - streamPayload, - bytePayload, - payloadHashed); -#pragma warning disable CS0618 // Type or member is obsolete - case IndirectSignatureVersion.CoseHashV: -#pragma warning restore CS0618 // Type or member is obsolete + + switch(signatureVersion) + { +#pragma warning disable CS0618 // Type or member is obsolete + case IndirectSignatureVersion.Direct: +#pragma warning restore CS0618 // Type or member is obsolete + return CreateIndirectSignatureWithChecksInternalDirectFormat( + returnBytes, + signingKeyProvider, + contentType, + streamPayload, + bytePayload, + payloadHashed, + headerExtender); +#pragma warning disable CS0618 // Type or member is obsolete + case IndirectSignatureVersion.CoseHashV: +#pragma warning restore CS0618 // Type or member is obsolete return CreateIndirectSignatureWithChecksInternalCoseHashVFormat( returnBytes, signingKeyProvider, contentType, streamPayload, bytePayload, - payloadHashed); - case IndirectSignatureVersion.CoseHashEnvelope: - return CreateIndirectSignatureWithChecksInternalCoseHashEnvelopeFormat( + payloadHashed, + headerExtender); + case IndirectSignatureVersion.CoseHashEnvelope: + return CreateIndirectSignatureWithChecksInternalCoseHashEnvelopeFormat( returnBytes, signingKeyProvider, contentType, streamPayload, bytePayload, - payloadHashed); - default: - throw new ArgumentOutOfRangeException(nameof(signatureVersion), "Unknown signature version"); + payloadHashed, + headerExtender); + default: + throw new ArgumentOutOfRangeException(nameof(signatureVersion), "Unknown signature version"); } } - + /// /// Get the hash algorithm from the specified CoseHashAlgorithm. /// @@ -182,8 +187,8 @@ public static HashAlgorithm GetHashAlgorithmFromCoseHashAlgorithm(CoseHashAlgori CoseHashAlgorithm.SHA384 => new SHA384Managed(), _ => throw new NotSupportedException($"The algorithm {algorithm} is not supported by {nameof(CoseHashV)}.") }; - } - + } + /// /// Method for handling byte[] and stream for the same logic. /// @@ -222,8 +227,8 @@ internal static bool HashMatches(CoseHashAlgorithm hashAlgorithm, ReadOnlyMemory { 32, HashAlgorithmName.SHA256 }, { 48, HashAlgorithmName.SHA384 }, { 64, HashAlgorithmName.SHA512 } - }); - + }); + private bool DisposedValue; /// /// Dispose pattern implementation diff --git a/CoseSign1.Tests/ChainedCoseHeaderExtenderTests.cs b/CoseSign1.Tests/ChainedCoseHeaderExtenderTests.cs new file mode 100644 index 00000000..4babf10e --- /dev/null +++ b/CoseSign1.Tests/ChainedCoseHeaderExtenderTests.cs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace CoseSign1.Tests; + +/// +/// Unit tests for . +/// +public class ChainedCoseHeaderExtenderTests +{ + /// + /// Dummy implementation of for testing chaining behavior. + /// + private class DummyExtender : ICoseHeaderExtender + { + private readonly Func Protected; + private readonly Func Unprotected; + /// + /// Initializes a new instance of the class. + /// + /// Delegate to handle . + /// Delegate to handle . + public DummyExtender(Func protectedFunc, Func unprotectedFunc) + { + Protected = protectedFunc; + Unprotected = unprotectedFunc; + } + /// + public CoseHeaderMap ExtendProtectedHeaders(CoseHeaderMap protectedHeaders) => Protected(protectedHeaders); + /// + public CoseHeaderMap ExtendUnProtectedHeaders(CoseHeaderMap? unProtectedHeaders) => Unprotected(unProtectedHeaders); + } + + [Test] + public void Constructor_ThrowsOnNullEnumerable() + { + Assert.Throws(() => new ChainedCoseHeaderExtender(null!)); + } + + [Test] + public void Constructor_ThrowsOnNullElement() + { + var extenders = new ICoseHeaderExtender[] { new DummyExtender(h => h, h => h!), null! }; + Assert.Throws(() => new ChainedCoseHeaderExtender(extenders)); + } + + [Test] + public void ExtendProtectedHeaders_ThrowsOnNullInput() + { + var chain = new ChainedCoseHeaderExtender(new[] { new DummyExtender(h => h, h => h) }); + Assert.Throws(() => chain.ExtendProtectedHeaders(null)); + } + + [Test] + public void ExtendProtectedHeaders_ThrowsIfAnyExtenderReturnsNull() + { + var extenders = new ICoseHeaderExtender[] { + new DummyExtender(h => h, h => h), + new DummyExtender(h => null, h => h) + }; + var chain = new ChainedCoseHeaderExtender(extenders); + Assert.Throws(() => chain.ExtendProtectedHeaders(new CoseHeaderMap())); + } + + [Test] + public void ExtendUnProtectedHeaders_ThrowsIfAnyExtenderReturnsNull() + { + var extenders = new ICoseHeaderExtender[] { + new DummyExtender(h => h, h => h), + new DummyExtender(h => h, h => null) + }; + var chain = new ChainedCoseHeaderExtender(extenders); + Assert.Throws(() => chain.ExtendUnProtectedHeaders(new CoseHeaderMap())); + } + + [Test] + public void ExtendProtectedHeaders_ChainsCorrectly() + { + var chain = new ChainedCoseHeaderExtender(new ICoseHeaderExtender[] { + new DummyExtender(h => { h[new CoseHeaderLabel("a")] = CoseHeaderValue.FromInt32(1); return h; }, h => h!), + new DummyExtender(h => { h[new CoseHeaderLabel("b")] = CoseHeaderValue.FromInt32(2); return h; }, h => h!) + }); + var map = new CoseHeaderMap(); + var result = chain.ExtendProtectedHeaders(map); + result[new CoseHeaderLabel("a")].GetValueAsInt32().Should().Be(1); + result[new CoseHeaderLabel("b")].GetValueAsInt32().Should().Be(2); + } + + [Test] + public void ExtendUnProtectedHeaders_ChainsCorrectly() + { + var chain = new ChainedCoseHeaderExtender(new ICoseHeaderExtender[] { + new DummyExtender(h => h, h => { h![new CoseHeaderLabel("x")] = CoseHeaderValue.FromInt32(10); return h; }), + new DummyExtender(h => h, h => { h![new CoseHeaderLabel("y")] = CoseHeaderValue.FromInt32(20); return h; }) + }); + var map = new CoseHeaderMap(); + var result = chain.ExtendUnProtectedHeaders(map); + result[new CoseHeaderLabel("x")].GetValueAsInt32().Should().Be(10); + result[new CoseHeaderLabel("y")].GetValueAsInt32().Should().Be(20); + } +} + diff --git a/CoseSign1/ChainedCoseHeaderExtender.cs b/CoseSign1/ChainedCoseHeaderExtender.cs new file mode 100644 index 00000000..ccb92163 --- /dev/null +++ b/CoseSign1/ChainedCoseHeaderExtender.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.Linq; + +namespace CoseSign1; + +/// +/// Chains multiple instances and applies them in order to protected and unprotected COSE header maps. +/// +/// +/// This class allows composition of multiple header extenders, executing each in sequence. Each extender's output is passed as input to the next. +/// If any extender returns null, an is thrown. The input list and all elements must be non-null. +/// +public sealed class ChainedCoseHeaderExtender : ICoseHeaderExtender +{ + private readonly IReadOnlyList Extenders; + + /// + /// Initializes a new instance of the class. + /// + /// The sequence of instances to chain. Must not be null and must not contain null elements. + /// Thrown if is null. + /// Thrown if any element in is null. + public ChainedCoseHeaderExtender(IEnumerable extenders) + { + if (extenders == null) + { + throw new ArgumentNullException(nameof(extenders)); + } + + if (extenders.Any(e => e == null)) + { + throw new ArgumentException("One or more extenders are null.", nameof(extenders)); + } + + Extenders = extenders.ToList().AsReadOnly(); + } + + /// + /// Applies all chained extenders to the provided protected headers in order. + /// + /// The initial to extend. Must not be null. + /// The extended after all extenders have been applied. + /// Thrown if is null. + /// Thrown if any extender returns null. + public CoseHeaderMap ExtendProtectedHeaders(CoseHeaderMap protectedHeaders) + { + if (protectedHeaders == null) + { + throw new ArgumentNullException(nameof(protectedHeaders)); + } + + CoseHeaderMap result = protectedHeaders; + foreach (var extender in Extenders) + { + result = extender.ExtendProtectedHeaders(result); + if (result == null) + { + throw new InvalidOperationException($"Extender {extender.GetType().Name} returned null from ExtendProtectedHeaders."); + } + } + return result; + } + + /// + /// Applies all chained extenders to the provided unprotected headers in order. + /// + /// The initial to extend. May be null. + /// The extended after all extenders have been applied. + /// Thrown if any extender returns null. + public CoseHeaderMap ExtendUnProtectedHeaders(CoseHeaderMap? unProtectedHeaders) + { + CoseHeaderMap? result = unProtectedHeaders; + foreach (var extender in Extenders) + { + result = extender.ExtendUnProtectedHeaders(result); + if (result == null) + { + throw new InvalidOperationException($"Extender {extender.GetType().Name} returned null from ExtendUnProtectedHeaders."); + } + } + return result!; + } +}