diff --git a/src/libraries/System.Security.Cryptography.Cose/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.Cose/src/Resources/Strings.resx index 0691df3b1e48b5..c175c410cb8505 100644 --- a/src/libraries/System.Security.Cryptography.Cose/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography.Cose/src/Resources/Strings.resx @@ -231,6 +231,9 @@ Unsupported key '{0}'. + + COSE algorithm '{0}' does not match the algorithm '{1}' of the specified key. + Algorithm header CBOR type was incorrect, expected int or tstr. diff --git a/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSign1Message.cs b/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSign1Message.cs index da3b1e0c4df954..3cc43dfd2e3af8 100644 --- a/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSign1Message.cs +++ b/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSign1Message.cs @@ -771,18 +771,7 @@ public bool VerifyDetached(CoseKey key, Stream detachedContent, ReadOnlySpan contentBytes, Stream? contentStream, ReadOnlySpan associatedData) { Debug.Assert(contentStream == null || contentBytes.Length == 0); - ReadOnlyMemory encodedAlg = CoseHelpers.GetCoseAlgorithmFromProtectedHeaders(ProtectedHeaders); - - CoseAlgorithm? nullableAlg = CoseHelpers.DecodeCoseAlgorithmHeader(encodedAlg); - if (nullableAlg == null) - { - throw new CryptographicException(SR.Sign1VerifyAlgHeaderWasIncorrect); - } - - if (nullableAlg.Value != key.Algorithm) - { - throw new CryptographicException(SR.Format(SR.Sign1UnknownCoseAlgorithm, nullableAlg)); - } + ValidateAlgorithm(key); using (ToBeSignedBuilder toBeSignedBuilder = key.CreateToBeSignedBuilder()) { @@ -932,6 +921,7 @@ public Task VerifyDetachedAsync(CoseKey key, Stream detachedContent, ReadO throw new InvalidOperationException(SR.ContentWasEmbedded); } + ValidateAlgorithm(key); return VerifyAsyncCore(key, detachedContent, associatedData, cancellationToken); } @@ -1048,5 +1038,15 @@ private CoseAlgorithm GetCoseAlgorithmFromProtectedHeaders() return nullableAlg.Value; } + + private void ValidateAlgorithm(CoseKey key) + { + CoseAlgorithm algorithm = GetCoseAlgorithmFromProtectedHeaders(); + + if (algorithm != key.Algorithm) + { + throw new CryptographicException(SR.Format(SR.Sign1VerifyAlgDoesNotMatchKeyAlgorithm, algorithm, key.Algorithm)); + } + } } } diff --git a/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSignature.cs b/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSignature.cs index a6c9d0e88268d8..2b65f4b7d1a1d0 100644 --- a/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSignature.cs +++ b/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSignature.cs @@ -608,6 +608,7 @@ public Task VerifyDetachedAsync(CoseKey key, Stream detachedContent, ReadO throw new InvalidOperationException(SR.ContentWasEmbedded); } + ValidateAlgorithm(key); return VerifyAsyncCore(key, detachedContent, associatedData, cancellationToken); } @@ -644,6 +645,8 @@ private async Task VerifyAsyncCore(CoseKey key, Stream content, ReadOnlyMe private bool VerifyCore(CoseKey key, ReadOnlySpan contentBytes, Stream? contentStream, ReadOnlySpan associatedData) { + ValidateAlgorithm(key); + using (ToBeSignedBuilder toBeSignedBuilder = key.CreateToBeSignedBuilder()) { int bufferLength = CoseMessage.ComputeToBeSignedEncodedSize( @@ -685,5 +688,15 @@ private CoseAlgorithm GetCoseAlgorithmFromProtectedHeaders() return nullableAlg.Value; } + + private void ValidateAlgorithm(CoseKey key) + { + CoseAlgorithm algorithm = GetCoseAlgorithmFromProtectedHeaders(); + + if (algorithm != key.Algorithm) + { + throw new CryptographicException(SR.Format(SR.Sign1VerifyAlgDoesNotMatchKeyAlgorithm, algorithm, key.Algorithm)); + } + } } } diff --git a/src/libraries/System.Security.Cryptography.Cose/tests/CoseMultiSignMessageTests.Verify.Stream.cs b/src/libraries/System.Security.Cryptography.Cose/tests/CoseMultiSignMessageTests.Verify.Stream.cs index 35a008b409bf31..50d41a0568217d 100644 --- a/src/libraries/System.Security.Cryptography.Cose/tests/CoseMultiSignMessageTests.Verify.Stream.cs +++ b/src/libraries/System.Security.Cryptography.Cose/tests/CoseMultiSignMessageTests.Verify.Stream.cs @@ -4,6 +4,7 @@ using System.Collections.ObjectModel; using System.IO; using System.Threading.Tasks; +using Test.Cryptography; using Xunit; using static System.Security.Cryptography.Cose.Tests.CoseTestHelpers; @@ -63,6 +64,49 @@ public async Task VerifyAsyncWithUnreadableStream() using Stream unseekableStream = GetTestStream(s_sampleContent, StreamKind.Unreadable); await Assert.ThrowsAsync("detachedContent", () => msg.Signatures[0].VerifyDetachedAsync(DefaultKey, unseekableStream)); } + + [Fact] + public async Task VerifyWithCoseKeyThrowsForMismatchedAlgorithmHeader() + { + CoseSigner signer = GetCoseSigner(DefaultKey, DefaultHash); + signer.ProtectedHeaders.Add(new CoseHeaderLabel(42), 42); + string encodedMessage = CoseMultiSignMessage.SignDetached(s_sampleContent, signer).ByteArrayToHex(); + encodedMessage = ReplaceFirst(encodedMessage, "47A20126182A182A", "44A1013822"); + + CoseMultiSignMessage message = CoseMessage.DecodeMultiSign(ByteUtils.HexToByteArray(encodedMessage)); + CoseSignature signature = Assert.Single(message.Signatures); + CoseKey key = new CoseKey(DefaultKey, DefaultHash); + using Stream syncStream = GetTestStream(s_sampleContent); + using Stream asyncStream = GetTestStream(s_sampleContent); + + CryptographicException syncException = Assert.Throws( + () => signature.VerifyDetached(key, syncStream)); + CryptographicException asyncException = await Assert.ThrowsAsync( + () => signature.VerifyDetachedAsync(key, asyncStream)); + + Assert.Contains(nameof(CoseAlgorithm.ES384), syncException.Message); + Assert.Contains(nameof(CoseAlgorithm.ES256), syncException.Message); + Assert.Contains(nameof(CoseAlgorithm.ES384), asyncException.Message); + Assert.Contains(nameof(CoseAlgorithm.ES256), asyncException.Message); + } + + [Fact] + public async Task VerifyWithCoseKeyThrowsForMissingAlgorithmHeader() + { + CoseSigner signer = GetCoseSigner(DefaultKey, DefaultHash); + signer.ProtectedHeaders.Add(new CoseHeaderLabel(42), 42); + string encodedMessage = CoseMultiSignMessage.SignDetached(s_sampleContent, signer).ByteArrayToHex(); + encodedMessage = ReplaceFirst(encodedMessage, "47A20126182A182A", "45A1182A182A"); + + CoseMultiSignMessage message = CoseMessage.DecodeMultiSign(ByteUtils.HexToByteArray(encodedMessage)); + CoseSignature signature = Assert.Single(message.Signatures); + CoseKey key = new CoseKey(DefaultKey, DefaultHash); + using Stream syncStream = GetTestStream(s_sampleContent); + using Stream asyncStream = GetTestStream(s_sampleContent); + + Assert.Throws(() => signature.VerifyDetached(key, syncStream)); + await Assert.ThrowsAsync(() => signature.VerifyDetachedAsync(key, asyncStream)); + } } public class CoseMultiSignMessageTests_VerifyStream_Sync : CoseMultiSignMessageTests_VerifyStream diff --git a/src/libraries/System.Security.Cryptography.Cose/tests/CoseSign1MessageTests.Verify.Stream.cs b/src/libraries/System.Security.Cryptography.Cose/tests/CoseSign1MessageTests.Verify.Stream.cs index 09a10710db416e..68649b5ce208ea 100644 --- a/src/libraries/System.Security.Cryptography.Cose/tests/CoseSign1MessageTests.Verify.Stream.cs +++ b/src/libraries/System.Security.Cryptography.Cose/tests/CoseSign1MessageTests.Verify.Stream.cs @@ -3,6 +3,7 @@ using System.IO; using System.Threading.Tasks; +using Test.Cryptography; using Xunit; using static System.Security.Cryptography.Cose.Tests.CoseTestHelpers; @@ -59,6 +60,47 @@ public async Task VerifyAsyncWithUnreadableStream() using Stream unseekableStream = GetTestStream(s_sampleContent, StreamKind.Unreadable); await Assert.ThrowsAsync("detachedContent", () => msg.VerifyDetachedAsync(DefaultKey, unseekableStream)); } + + [Fact] + public async Task VerifyWithCoseKeyThrowsForMismatchedAlgorithmHeader() + { + CoseSigner signer = GetCoseSigner(DefaultKey, DefaultHash); + signer.ProtectedHeaders.Add(new CoseHeaderLabel(42), 42); + string encodedMessage = CoseSign1Message.SignDetached(s_sampleContent, signer).ByteArrayToHex(); + encodedMessage = ReplaceFirst(encodedMessage, "47A20126182A182A", "44A1013822"); + + CoseSign1Message message = CoseMessage.DecodeSign1(ByteUtils.HexToByteArray(encodedMessage)); + CoseKey key = new CoseKey(DefaultKey, DefaultHash); + using Stream syncStream = GetTestStream(s_sampleContent); + using Stream asyncStream = GetTestStream(s_sampleContent); + + CryptographicException syncException = Assert.Throws( + () => message.VerifyDetached(key, syncStream)); + CryptographicException asyncException = await Assert.ThrowsAsync( + () => message.VerifyDetachedAsync(key, asyncStream)); + + Assert.Contains(nameof(CoseAlgorithm.ES384), syncException.Message); + Assert.Contains(nameof(CoseAlgorithm.ES256), syncException.Message); + Assert.Contains(nameof(CoseAlgorithm.ES384), asyncException.Message); + Assert.Contains(nameof(CoseAlgorithm.ES256), asyncException.Message); + } + + [Fact] + public async Task VerifyWithCoseKeyThrowsForMissingAlgorithmHeader() + { + CoseSigner signer = GetCoseSigner(DefaultKey, DefaultHash); + signer.ProtectedHeaders.Add(new CoseHeaderLabel(42), 42); + string encodedMessage = CoseSign1Message.SignDetached(s_sampleContent, signer).ByteArrayToHex(); + encodedMessage = ReplaceFirst(encodedMessage, "47A20126182A182A", "45A1182A182A"); + + CoseSign1Message message = CoseMessage.DecodeSign1(ByteUtils.HexToByteArray(encodedMessage)); + CoseKey key = new CoseKey(DefaultKey, DefaultHash); + using Stream syncStream = GetTestStream(s_sampleContent); + using Stream asyncStream = GetTestStream(s_sampleContent); + + Assert.Throws(() => message.VerifyDetached(key, syncStream)); + await Assert.ThrowsAsync(() => message.VerifyDetachedAsync(key, asyncStream)); + } } public class CoseSign1MessageTests_VerifyStream_Sync : CoseSign1MessageTests_VerifyStream