diff --git a/CHANGELOG.md b/CHANGELOG.md index 40a7230b..b6620c72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [v1.3.0-pre4](https://github.com/microsoft/CoseSignTool/tree/v1.3.0-pre4) (2025-03-13) + +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.3.0-pre3...v1.3.0-pre4) + +**Merged pull requests:** + +- Add support for CoseHashEnvelope [\#125](https://github.com/microsoft/CoseSignTool/pull/125) ([JeromySt](https://github.com/JeromySt)) + ## [v1.3.0-pre3](https://github.com/microsoft/CoseSignTool/tree/v1.3.0-pre3) (2024-12-28) [Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.3.0-pre2...v1.3.0-pre3) @@ -70,31 +78,31 @@ ## [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.6-pre2...v1.2.8-pre1) **Merged pull requests:** - Make the advanced stream handling optional. Use simple retry instead. [\#112](https://github.com/microsoft/CoseSignTool/pull/112) ([lemccomb](https://github.com/lemccomb)) -## [v1.2.8](https://github.com/microsoft/CoseSignTool/tree/v1.2.8) (2024-09-23) +## [v1.2.6-pre2](https://github.com/microsoft/CoseSignTool/tree/v1.2.6-pre2) (2024-09-23) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.6-pre2...v1.2.8) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.8...v1.2.6-pre2) -## [v1.2.6-pre2](https://github.com/microsoft/CoseSignTool/tree/v1.2.6-pre2) (2024-09-23) +## [v1.2.8](https://github.com/microsoft/CoseSignTool/tree/v1.2.8) (2024-09-23) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.6-pre1...v1.2.6-pre2) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.2.6-pre1...v1.2.8) **Merged pull requests:** @@ -304,19 +312,19 @@ ## [v1.1.7-pre1](https://github.com/microsoft/CoseSignTool/tree/v1.1.7-pre1) (2024-02-14) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.6-pre1...v1.1.7-pre1) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.7...v1.1.7-pre1) **Merged pull requests:** - Command Line Validation of Indirect Signatures [\#78](https://github.com/microsoft/CoseSignTool/pull/78) ([elantiguamsft](https://github.com/elantiguamsft)) -## [v1.1.6-pre1](https://github.com/microsoft/CoseSignTool/tree/v1.1.6-pre1) (2024-02-07) +## [v1.1.7](https://github.com/microsoft/CoseSignTool/tree/v1.1.7) (2024-02-07) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.7...v1.1.6-pre1) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.6-pre1...v1.1.7) -## [v1.1.7](https://github.com/microsoft/CoseSignTool/tree/v1.1.7) (2024-02-07) +## [v1.1.6-pre1](https://github.com/microsoft/CoseSignTool/tree/v1.1.6-pre1) (2024-02-07) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.6...v1.1.7) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.6...v1.1.6-pre1) **Merged pull requests:** @@ -380,19 +388,19 @@ ## [v1.1.1-pre1](https://github.com/microsoft/CoseSignTool/tree/v1.1.1-pre1) (2024-01-17) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.1...v1.1.1-pre1) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.0-pre7...v1.1.1-pre1) **Merged pull requests:** - Move CreateChangelog to after build in PR build [\#70](https://github.com/microsoft/CoseSignTool/pull/70) ([lemccomb](https://github.com/lemccomb)) -## [v1.1.1](https://github.com/microsoft/CoseSignTool/tree/v1.1.1) (2024-01-12) +## [v1.1.0-pre7](https://github.com/microsoft/CoseSignTool/tree/v1.1.0-pre7) (2024-01-12) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.0-pre7...v1.1.1) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.1...v1.1.0-pre7) -## [v1.1.0-pre7](https://github.com/microsoft/CoseSignTool/tree/v1.1.0-pre7) (2024-01-12) +## [v1.1.1](https://github.com/microsoft/CoseSignTool/tree/v1.1.1) (2024-01-12) -[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.0-pre6...v1.1.0-pre7) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v1.1.0-pre6...v1.1.1) **Closed issues:** @@ -465,7 +473,7 @@ ## [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.2...v0.3.1-pre.10) +[Full Changelog](https://github.com/microsoft/CoseSignTool/compare/v0.3.1-pre.9...v0.3.1-pre.10) **Merged pull requests:** @@ -475,13 +483,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/CoseHashEnvelopeTests.cs b/CoseIndirectSignature.Tests/CoseHashEnvelopeTests.cs index 81caa034..11a92b6a 100644 --- a/CoseIndirectSignature.Tests/CoseHashEnvelopeTests.cs +++ b/CoseIndirectSignature.Tests/CoseHashEnvelopeTests.cs @@ -279,6 +279,78 @@ public void ValidCoseHashEnvelopePayloadNoPreImageContentShouldValidate() contentType.Should().BeNullOrEmpty(); } + [Test] + public void ValidCoseHashEnvelopePayloadNoPreImageContentCoaPShouldValidate() + { + ICoseSigningKeyProvider coseSigningKeyProvider = TestUtils.SetupMockSigningKeyProvider(); + CoseSign1MessageFactory factory = new(); + + byte[] randomBytes = new byte[50]; + new Random().NextBytes(randomBytes); + Mock mockHeaderExtender = new(MockBehavior.Strict); + CoseHeaderMap protectedHeader = new(); + CoseHeaderMap unProtectedHeader = new(); + + CoseHashEnvelopeHeaderExtender headerExtender = new CoseHashEnvelopeHeaderExtender(HashAlgorithmName.SHA256, "application/test"); + protectedHeader = headerExtender.ExtendProtectedHeaders(protectedHeader); + protectedHeader.Remove(CoseHashEnvelopeHeaderExtender.CoseHashEnvelopeHeaderLabels[CoseHashEnvelopeHeaderLabels.PreimageContentType]); + // https://www.rfc-editor.org/rfc/rfc9052.html#CoAP_content_type + protectedHeader.Add(CoseHashEnvelopeHeaderExtender.CoseHashEnvelopeHeaderLabels[CoseHashEnvelopeHeaderLabels.PreimageContentType], CoseHeaderValue.FromInt32(98)); + + mockHeaderExtender.Setup(x => x.ExtendProtectedHeaders(It.IsAny())).Returns(protectedHeader); + mockHeaderExtender.Setup(x => x.ExtendUnProtectedHeaders(It.IsAny())).Returns(unProtectedHeader); + + CoseSign1Message? message = factory.CreateCoseSign1Message( + randomBytes, + coseSigningKeyProvider, + embedPayload: true, + headerExtender: mockHeaderExtender.Object); + message.Should().NotBeNull(); + message!.TryGetIsCoseHashEnvelope().Should().BeTrue(); + message.TryGetPayloadHashAlgorithm(out CoseHashAlgorithm? algoName).Should().BeTrue(); + algoName.Should().Be(CoseHashAlgorithm.SHA256); + message.TryGetPreImageContentType(out string? contentType).Should().BeFalse(); + contentType.Should().BeNullOrEmpty(); + message.TryGetPreImageContentType(out int? coapPreImageContentType).Should().BeTrue(); + coapPreImageContentType.Should().Be(98); + } + + [Test] + public void ValidCoseHashEnvelopePayloadNoPreImageContentCoaPUnprotectedShouldValidate() + { + ICoseSigningKeyProvider coseSigningKeyProvider = TestUtils.SetupMockSigningKeyProvider(); + CoseSign1MessageFactory factory = new(); + + byte[] randomBytes = new byte[50]; + new Random().NextBytes(randomBytes); + Mock mockHeaderExtender = new(MockBehavior.Strict); + CoseHeaderMap protectedHeader = new(); + CoseHeaderMap unProtectedHeader = new(); + + CoseHashEnvelopeHeaderExtender headerExtender = new CoseHashEnvelopeHeaderExtender(HashAlgorithmName.SHA256, "application/test"); + protectedHeader = headerExtender.ExtendProtectedHeaders(protectedHeader); + protectedHeader.Remove(CoseHashEnvelopeHeaderExtender.CoseHashEnvelopeHeaderLabels[CoseHashEnvelopeHeaderLabels.PreimageContentType]); + // https://www.rfc-editor.org/rfc/rfc9052.html#CoAP_content_type + unProtectedHeader.Add(CoseHashEnvelopeHeaderExtender.CoseHashEnvelopeHeaderLabels[CoseHashEnvelopeHeaderLabels.PreimageContentType], CoseHeaderValue.FromInt32(98)); + + mockHeaderExtender.Setup(x => x.ExtendProtectedHeaders(It.IsAny())).Returns(protectedHeader); + mockHeaderExtender.Setup(x => x.ExtendUnProtectedHeaders(It.IsAny())).Returns(unProtectedHeader); + + CoseSign1Message? message = factory.CreateCoseSign1Message( + randomBytes, + coseSigningKeyProvider, + embedPayload: true, + headerExtender: mockHeaderExtender.Object); + message.Should().NotBeNull(); + message!.TryGetIsCoseHashEnvelope().Should().BeTrue(); + message.TryGetPayloadHashAlgorithm(out CoseHashAlgorithm? algoName).Should().BeTrue(); + algoName.Should().Be(CoseHashAlgorithm.SHA256); + message.TryGetPreImageContentType(out string? contentType).Should().BeFalse(); + contentType.Should().BeNullOrEmpty(); + message.TryGetPreImageContentType(out int? coapPreImageContentType).Should().BeTrue(); + coapPreImageContentType.Should().Be(98); + } + [Test] public void ValidCoseHashEnvelopePayloadLocationProtectedHeaderShouldValidate() { diff --git a/CoseIndirectSignature/CoseHashEnvelopeHeaderExtender.cs b/CoseIndirectSignature/CoseHashEnvelopeHeaderExtender.cs index 272ca5c6..6745b288 100644 --- a/CoseIndirectSignature/CoseHashEnvelopeHeaderExtender.cs +++ b/CoseIndirectSignature/CoseHashEnvelopeHeaderExtender.cs @@ -125,11 +125,10 @@ public CoseHeaderMap ExtendProtectedHeaders(CoseHeaderMap protectedHeaders) Label 3 (content_type) MUST NOT be present in the protected or unprotected headers. - Label 3 is easily confused with label TBD_2 - payload_preimage_content_type. The difference between content_type - (3) and payload_preimage_content_type (TBD2) is content_type is used - to identify the content format associated with payload, whereas - payload_preimage_content_type is used to identify the content format + Label 3 is easily confused with label 259 payload_preimage_content_type. + The difference between content_type (3) and payload_preimage_content_type (259) + is content_type is used to identify the content format associated with payload, + whereas payload_preimage_content_type is used to identify the content format of the bytes which are hashed to produce the payload. */ protectedHeaders.Remove(CoseHeaderLabel.ContentType); diff --git a/CoseIndirectSignature/Extensions/CoseSign1MessageCoseHashEnvelopeExtensions.cs b/CoseIndirectSignature/Extensions/CoseSign1MessageCoseHashEnvelopeExtensions.cs index 7e3c6c06..6d7e046a 100644 --- a/CoseIndirectSignature/Extensions/CoseSign1MessageCoseHashEnvelopeExtensions.cs +++ b/CoseIndirectSignature/Extensions/CoseSign1MessageCoseHashEnvelopeExtensions.cs @@ -12,7 +12,7 @@ public static class CoseSign1MessageCoseHashEnvelopeExtensions { /// /// Checks to see if the COSE Sign1 Message is a CoseHashEnvelope. - /// https://datatracker.ietf.org/doc/draft-ietf-cose-hash-envelope/03/ + /// https://www.ietf.org/archive/id/draft-ietf-cose-hash-envelope-04.html /// /// The to check. /// True if is a CoseHashEnvelope, False otherwise. @@ -65,7 +65,7 @@ public static bool TryGetPayloadHashAlgorithm(this CoseSign1Message? @this, out return false; } - // Label TBD_1(payload hash algorithm) MUST be present in the protected header + // Label 258 (payload hash algorithm) MUST be present in the protected header if (@this.ProtectedHeaders.TryGetValue(CoseHashEnvelopeHeaderExtender.CoseHashEnvelopeHeaderLabels[CoseHashEnvelopeHeaderLabels.PayloadHashAlg], out payloadHashAlgorithmValue)) { extractedValue = GetCoseHashAlgorithmFromHeaderValue(payloadHashAlgorithmValue); @@ -99,40 +99,74 @@ public static bool TryGetPayloadHashAlgorithm(this CoseSign1Message? @this, out } } + /// + /// Tries to get the preimage content type as string from the headers of the CoseSign1Message. + /// + /// The to evaluate. + /// OUT param which will have the value of the PreImageContentType Cose Header as a string. + /// True if the PreImageContentType header is found and will be non-null, False otherwise. + public static bool TryGetPreImageContentType(this CoseSign1Message? @this, out string? preImageContentType) => + TryGetPreImageContentType(@this, out preImageContentType, out _) && !string.IsNullOrWhiteSpace(preImageContentType); + + /// + /// Tries to get the preimage content type as CoaP from the headers of the CoseSign1Message. + /// + /// The to evaluate. + /// OUT param which will have the value of the PreImageContentType Cose Header as a CoAP content type. + /// True if the PreImageContentType header is found and will be non-null, False otherwise. + public static bool TryGetPreImageContentType(this CoseSign1Message? @this, out int? coapPreImageContentType) => + TryGetPreImageContentType(@this, out _, out coapPreImageContentType) && coapPreImageContentType != null; + /// /// Tries to get the preimage content type from the headers of the CoseSign1Message. /// /// The to evaluate. /// OUT param which will have the value of the PreImageContentType Cose Header. + /// OUT param which will have the value of the PreImageContentType Cose Header as a CoAP content type. /// True if the PreImageContentType header is found and will be non-null, False otherwise. - public static bool TryGetPreImageContentType(this CoseSign1Message? @this, out string? preImageContentType) + public static bool TryGetPreImageContentType(this CoseSign1Message? @this, out string? preImageContentType, out int? coapPreImageContentType) { + coapPreImageContentType = null; + preImageContentType = null; + if (@this == null) { Trace.TraceError($"{nameof(TryGetPayloadHashAlgorithm)} was called on a null CoseSign1Message object"); - preImageContentType = null; return false; } - // Label TBD_2(content type of the preimage of the payload) MAY be + // Label 259 (content type of the preimage of the payload) MAY be // present in the protected header or unprotected header. // first check protected headers as its preferred to be present there if (@this.ProtectedHeaders.TryGetValue(CoseHashEnvelopeHeaderExtender.CoseHashEnvelopeHeaderLabels[CoseHashEnvelopeHeaderLabels.PreimageContentType], out CoseHeaderValue preImageContentTypeValue)) { - preImageContentType = preImageContentTypeValue.GetValueAsString(); + try + { + preImageContentType = preImageContentTypeValue.GetValueAsString(); + } + catch(InvalidOperationException) + { + coapPreImageContentType = preImageContentTypeValue.GetValueAsInt32(); + } return true; } // second check unprotected headers if (@this.UnprotectedHeaders.TryGetValue(CoseHashEnvelopeHeaderExtender.CoseHashEnvelopeHeaderLabels[CoseHashEnvelopeHeaderLabels.PreimageContentType], out preImageContentTypeValue)) { - preImageContentType = preImageContentTypeValue.GetValueAsString(); + try + { + preImageContentType = preImageContentTypeValue.GetValueAsString(); + } + catch (InvalidOperationException) + { + coapPreImageContentType = preImageContentTypeValue.GetValueAsInt32(); + } return true; } Trace.TraceWarning($"{nameof(TryGetPreImageContentType)} was called on a CoseSign1Message object({@this.GetHashCode()}) without the PreimageContentType header present."); - preImageContentType = null; return false; } @@ -152,7 +186,7 @@ public static bool TryGetPayloadLocation(this CoseSign1Message? @this, out strin return false; } - // Label TBD_3(payload_location) MAY be added to the protected + // Label 260 (payload_location) MAY be added to the protected // header and MUST NOT be presented in the unprotected header. if(@this.UnprotectedHeaders?.TryGetValue(CoseHashEnvelopeHeaderExtender.CoseHashEnvelopeHeaderLabels[CoseHashEnvelopeHeaderLabels.PayloadLocation], out CoseHeaderValue payloadLocationValue) ?? false)