Skip to content

Commit 491d96e

Browse files
authored
ML-DSA + COSE improve tests (#116543)
* Improve COSE testing for ML-DSA * Fix Tests * Merge ML-DSA with existing tests
1 parent 5b8b862 commit 491d96e

23 files changed

+598
-825
lines changed

src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaTestsData.cs

Lines changed: 144 additions & 98 deletions
Large diffs are not rendered by default.

src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseKey.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ internal static CoseKey FromUntrustedAlgorithmAndKey(CoseAlgorithm untrustedAlgo
107107
_ => throw new CryptographicException(SR.Format(SR.Sign1UnknownCoseAlgorithm, untrustedAlgorithm))
108108
};
109109

110-
static CoseKey FromKeyWithExpectedAlgorithm(MLDsaAlgorithm expected, MLDsa key)
111-
=> key.Algorithm.Name == expected.Name ? FromKey(key) : CoseKey.FromKey(key);
110+
CoseKey FromKeyWithExpectedAlgorithm(MLDsaAlgorithm expected, MLDsa key)
111+
=> key.Algorithm.Name == expected.Name ? FromKey(key) : throw new CryptographicException(SR.Format(SR.Sign1UnknownCoseAlgorithm, untrustedAlgorithm));
112112
}
113113
#pragma warning restore SYSLIB5006
114114
else
@@ -133,18 +133,18 @@ internal static CoseAlgorithm CoseAlgorithmFromInt64(long alg)
133133
private static void ThrowIfCoseAlgorithmNotSupported(CoseAlgorithm alg)
134134
{
135135
#pragma warning disable SYSLIB5006
136-
if (alg != CoseAlgorithm.ES256 &&
137-
alg != CoseAlgorithm.ES384 &&
138-
alg != CoseAlgorithm.ES512 &&
139-
alg != CoseAlgorithm.PS256 &&
140-
alg != CoseAlgorithm.PS384 &&
141-
alg != CoseAlgorithm.PS512 &&
142-
alg != CoseAlgorithm.RS256 &&
143-
alg != CoseAlgorithm.RS384 &&
144-
alg != CoseAlgorithm.RS512 &&
145-
alg != CoseAlgorithm.MLDsa44 &&
146-
alg != CoseAlgorithm.MLDsa65 &&
147-
alg != CoseAlgorithm.MLDsa87)
136+
if (alg is not CoseAlgorithm.ES256 and
137+
not CoseAlgorithm.ES384 and
138+
not CoseAlgorithm.ES512 and
139+
not CoseAlgorithm.PS256 and
140+
not CoseAlgorithm.PS384 and
141+
not CoseAlgorithm.PS512 and
142+
not CoseAlgorithm.RS256 and
143+
not CoseAlgorithm.RS384 and
144+
not CoseAlgorithm.RS512 and
145+
not CoseAlgorithm.MLDsa44 and
146+
not CoseAlgorithm.MLDsa65 and
147+
not CoseAlgorithm.MLDsa87)
148148
{
149149
throw new CryptographicException(SR.Format(SR.Sign1UnknownCoseAlgorithm, alg));
150150
}

src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSigner.cs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public sealed class CoseSigner
1919
/// Gets the private key to use during signing.
2020
/// </summary>
2121
/// <value>The private key to use during signing.</value>
22-
public AsymmetricAlgorithm? Key { get; }
22+
public AsymmetricAlgorithm? Key => CoseKey.AsymmetricAlgorithm;
2323

2424
/// <summary>
2525
/// Gets the private key to use during signing.
@@ -78,9 +78,6 @@ public CoseSigner(AsymmetricAlgorithm key, HashAlgorithmName hashAlgorithm, Cose
7878
if (key is not ECDsa)
7979
throw new ArgumentException(SR.Format(SR.Sign1UnsupportedKey, key.GetType().Name), nameof(key));
8080

81-
#pragma warning disable CS0618 // Type or member is obsolete
82-
Key = key;
83-
#pragma warning restore CS0618 // Type or member is obsolete
8481
CoseKey = CoseKey.FromKey((ECDsa)key, hashAlgorithm);
8582

8683
_protectedHeaders = protectedHeaders;
@@ -115,9 +112,6 @@ public CoseSigner(RSA key, RSASignaturePadding signaturePadding, HashAlgorithmNa
115112
ArgumentNullException.ThrowIfNull(key);
116113
ArgumentNullException.ThrowIfNull(signaturePadding);
117114

118-
#pragma warning disable CS0618 // Type or member is obsolete
119-
Key = key;
120-
#pragma warning restore CS0618 // Type or member is obsolete
121115
CoseKey = CoseKey.FromKey(key, signaturePadding, hashAlgorithm);
122116

123117
_protectedHeaders = protectedHeaders;
@@ -137,9 +131,6 @@ public CoseSigner(CoseKey key, CoseHeaderMap? protectedHeaders = null, CoseHeade
137131
if (key is null)
138132
throw new ArgumentNullException(nameof(key));
139133

140-
#pragma warning disable CS0618 // Type or member is obsolete
141-
Key = null;
142-
#pragma warning restore CS0618 // Type or member is obsolete
143134
CoseKey = key;
144135

145136
_protectedHeaders = protectedHeaders;

src/libraries/System.Security.Cryptography.Cose/tests/CoseHeaderMapTests.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class CoseHeaderMapTests
1616
public void SetValue_GetValue_KnownCoseHeaderLabel(SetValueMethod setMethod, GetValueMethod getMethod)
1717
{
1818
var map = new CoseHeaderMap();
19-
SetValue(map, CoseHeaderLabel.Algorithm, (int)ECDsaAlgorithm.ES256, setMethod);
19+
SetValue(map, CoseHeaderLabel.Algorithm, (int)CoseAlgorithm.ES256, setMethod);
2020

2121
if (setMethod != SetValueMethod.AddShortcut)
2222
{
@@ -26,7 +26,7 @@ public void SetValue_GetValue_KnownCoseHeaderLabel(SetValueMethod setMethod, Get
2626
SetValue(map, CoseHeaderLabel.ContentType, ContentTypeDummyValue, setMethod);
2727
SetValue(map, CoseHeaderLabel.KeyIdentifier, s_sampleContent, setMethod);
2828

29-
Assert.Equal((int)ECDsaAlgorithm.ES256, GetValue<int>(map, CoseHeaderLabel.Algorithm, getMethod));
29+
Assert.Equal((int)CoseAlgorithm.ES256, GetValue<int>(map, CoseHeaderLabel.Algorithm, getMethod));
3030

3131
if (getMethod != GetValueMethod.GetValueShortcut)
3232
{
@@ -122,7 +122,7 @@ public void SetValue_InvalidCoseHeaderValue()
122122
public void Enumerate()
123123
{
124124
var map = new CoseHeaderMap();
125-
SetValue(map, CoseHeaderLabel.Algorithm, (int)ECDsaAlgorithm.ES256, default(SetValueMethod));
125+
SetValue(map, CoseHeaderLabel.Algorithm, (int)CoseAlgorithm.ES256, default(SetValueMethod));
126126
SetEncodedValue(map, CoseHeaderLabel.CriticalHeaders, GetDummyCritHeaderValue(), default(SetValueMethod));
127127
SetValue(map ,CoseHeaderLabel.ContentType, ContentTypeDummyValue, default(SetValueMethod));
128128
SetValue(map, CoseHeaderLabel.KeyIdentifier, s_sampleContent, default(SetValueMethod));
@@ -137,7 +137,7 @@ public void Enumerate()
137137
Assert.Equal(new CoseHeaderLabel(currentHeader), label);
138138
ReadOnlyMemory<byte> expectedValue = currentHeader switch
139139
{
140-
KnownHeaderAlg => EncodeInt32((int)ECDsaAlgorithm.ES256, writer),
140+
KnownHeaderAlg => EncodeInt32((int)CoseAlgorithm.ES256, writer),
141141
KnownHeaderCrit => GetDummyCritHeaderValue(),
142142
KnownHeaderContentType => EncodeString(ContentTypeDummyValue, writer),
143143
KnownHeaderKid => EncodeBytes(s_sampleContent, writer),
@@ -194,7 +194,7 @@ public void GetValueFromReadOnlyProtectedMap(GetValueMethod getMethod)
194194

195195
Assert.True(protectedHeaders.IsReadOnly, "message.ProtectedHeaders.IsReadOnly");
196196

197-
int expectedAlgorithm = (int)ECDsaAlgorithm.ES256;
197+
int expectedAlgorithm = (int)CoseAlgorithm.ES256;
198198
int algorithm = GetValue<int>(protectedHeaders, CoseHeaderLabel.Algorithm, getMethod);
199199
Assert.Equal(expectedAlgorithm, algorithm);
200200

@@ -221,7 +221,7 @@ public void SetValueAndRemoveAndClearThrowIfProtectedMapIsReadOnly()
221221
VerifyThrows(protectedHeaders, CoseHeaderLabel.Algorithm);
222222

223223
// Verify existing value was not overwritten even after throwing.
224-
Assert.Equal((int)ECDsaAlgorithm.ES256, GetValue<int>(protectedHeaders, CoseHeaderLabel.Algorithm, default(GetValueMethod)));
224+
Assert.Equal((int)CoseAlgorithm.ES256, GetValue<int>(protectedHeaders, CoseHeaderLabel.Algorithm, default(GetValueMethod)));
225225

226226
// Non-readonly header works correctly.
227227
CoseHeaderMap unprotectedHeaders = message.UnprotectedHeaders;
@@ -525,7 +525,7 @@ public static IEnumerable<object[]> KnownHeadersEncodedValues_TestData()
525525

526526
foreach ((SetValueMethod setMethod, GetValueMethod getMethod) in setGetValuePairs)
527527
{
528-
writer.WriteInt32((int)ECDsaAlgorithm.ES256);
528+
writer.WriteInt32((int)CoseAlgorithm.ES256);
529529
yield return ReturnDataAndReset(KnownHeaderAlg, writer, setMethod, getMethod);
530530

531531
WriteDummyCritHeaderValue(writer, useIndefiniteLength: false);

src/libraries/System.Security.Cryptography.Cose/tests/CoseHeaderValueTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public void GetValueAsInt32Overflows(long value)
3131
[InlineData(0)]
3232
[InlineData(int.MaxValue)]
3333
[InlineData(int.MinValue)]
34-
[InlineData((int)ECDsaAlgorithm.ES256)]
34+
[InlineData((int)CoseAlgorithm.ES256)]
3535
public void GetValueAsInt32Succeeds(int value)
3636
{
3737
var writer = new CborWriter();
@@ -193,7 +193,7 @@ public enum GetValueAs
193193
[InlineData(0)]
194194
[InlineData(int.MaxValue)]
195195
[InlineData(int.MinValue)]
196-
[InlineData((int)ECDsaAlgorithm.ES256)]
196+
[InlineData((int)CoseAlgorithm.ES256)]
197197
public void FromInt32Succeeds(int value)
198198
{
199199
var writer = new CborWriter();

src/libraries/System.Security.Cryptography.Cose/tests/CoseKeyTests.ArgumentValidation.cs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
#pragma warning disable SYSLIB5006
5-
64
using System;
75
using System.Collections.Generic;
86
using System.Formats.Cbor;
@@ -23,12 +21,10 @@ public void CreateCoseKeyWithNullArg_Throws()
2321
AssertExtensions.Throws<ArgumentNullException>("key", static () => CoseKey.FromKey((ECDsa)null!, HashAlgorithmName.SHA256));
2422
AssertExtensions.Throws<ArgumentNullException>("key", static () => CoseKey.FromKey((RSA)null!, RSASignaturePadding.Pkcs1, HashAlgorithmName.SHA256));
2523

26-
RSA rsaKey = (RSA)KeyManager.GetKey(CoseTestKeyManager.RSAPkcs1Identifier).Key;
27-
AssertExtensions.Throws<ArgumentNullException>("signaturePadding", () => CoseKey.FromKey(rsaKey, null!, HashAlgorithmName.SHA256));
28-
AssertExtensions.Throws<ArgumentNullException>("hashAlgorithm.Name", () => CoseKey.FromKey(rsaKey, RSASignaturePadding.Pkcs1, default));
24+
AssertExtensions.Throws<ArgumentNullException>("signaturePadding", () => CoseKey.FromKey(CoseTestHelpers.RSAKey, null!, HashAlgorithmName.SHA256));
25+
AssertExtensions.Throws<ArgumentNullException>("hashAlgorithm.Name", () => CoseKey.FromKey(CoseTestHelpers.RSAKey, RSASignaturePadding.Pkcs1, default));
2926

30-
ECDsa ecdsaKey = (ECDsa)KeyManager.GetKey(CoseTestKeyManager.ECDsaIdentifier).Key;
31-
AssertExtensions.Throws<ArgumentNullException>("hashAlgorithm.Name", () => CoseKey.FromKey(ecdsaKey, default));
27+
AssertExtensions.Throws<ArgumentNullException>("hashAlgorithm.Name", () => CoseKey.FromKey(CoseTestHelpers.ES256, default));
3228
}
3329

3430
[Fact]
@@ -40,11 +36,12 @@ public void CreateCoseSignerWithNullCoseKey_Throws()
4036
[Fact]
4137
public void VerifySingleSignerWithNullCoseKey_Throws()
4238
{
43-
CoseTestKey key = KeyManager.GetKey(CoseTestKeyManager.ECDsaIdentifier);
39+
CoseKey key = CoseKey.FromKey(CoseTestHelpers.ES256, HashAlgorithmName.SHA256);
40+
CoseSigner signer = new CoseSigner(key);
4441
byte[] payload = Encoding.UTF8.GetBytes(nameof(VerifySingleSignerWithNullCoseKey_Throws));
4542
MemoryStream payloadStream = new(payload);
46-
byte[] embeddedMessageBytes = CoseSign1Message.SignEmbedded(payload, key.Signer, Array.Empty<byte>());
47-
byte[] detatchedMessageBytes = CoseSign1Message.SignDetached(payload, key.Signer, Array.Empty<byte>());
43+
byte[] embeddedMessageBytes = CoseSign1Message.SignEmbedded(payload, signer, Array.Empty<byte>());
44+
byte[] detatchedMessageBytes = CoseSign1Message.SignDetached(payload, signer, Array.Empty<byte>());
4845

4946
CoseSign1Message embeddedMessage = CoseSign1Message.DecodeSign1(embeddedMessageBytes);
5047
CoseSign1Message detachedMessage = CoseSign1Message.DecodeSign1(detatchedMessageBytes);
@@ -58,11 +55,12 @@ public void VerifySingleSignerWithNullCoseKey_Throws()
5855
[Fact]
5956
public void VerifyMultiSignerWithNullCoseKey_Throws()
6057
{
61-
CoseTestKey key = KeyManager.GetKey(CoseTestKeyManager.ECDsaIdentifier);
58+
CoseKey key = CoseKey.FromKey(CoseTestHelpers.ES256, HashAlgorithmName.SHA256);
59+
CoseSigner signer = new CoseSigner(key);
6260
byte[] payload = Encoding.UTF8.GetBytes(nameof(VerifySingleSignerWithNullCoseKey_Throws));
6361
MemoryStream payloadStream = new(payload);
64-
byte[] embeddedMessageBytes = CoseMultiSignMessage.SignEmbedded(payload, key.Signer);
65-
byte[] detatchedMessageBytes = CoseMultiSignMessage.SignDetached(payload, key.Signer);
62+
byte[] embeddedMessageBytes = CoseMultiSignMessage.SignEmbedded(payload, signer);
63+
byte[] detatchedMessageBytes = CoseMultiSignMessage.SignDetached(payload, signer);
6664

6765
CoseMultiSignMessage embeddedMessage = CoseMultiSignMessage.DecodeMultiSign(embeddedMessageBytes);
6866
CoseMultiSignMessage detachedMessage = CoseMultiSignMessage.DecodeMultiSign(detatchedMessageBytes);

src/libraries/System.Security.Cryptography.Cose/tests/CoseKeyTests.SignVerify.cs

Lines changed: 1 addition & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
#pragma warning disable SYSLIB5006
5-
64
using System;
75
using System.Collections.Generic;
86
using System.Text;
@@ -14,17 +12,8 @@
1412

1513
namespace System.Security.Cryptography.Cose.Tests
1614
{
17-
public partial class CoseKeyTests : IClassFixture<CoseTestKeyManager.TestFixture>
15+
public partial class CoseKeyTests
1816
{
19-
private CoseTestKeyManager.TestFixture _keyManagerFixture;
20-
private CoseTestKeyManager KeyManager => _keyManagerFixture.KeyManager;
21-
private CoseTestKeyManager BadKeyManager => _keyManagerFixture.BadKeyManager;
22-
23-
public CoseKeyTests(CoseTestKeyManager.TestFixture keyManagerFixture)
24-
{
25-
_keyManagerFixture = keyManagerFixture;
26-
}
27-
2817
[ConditionalTheory(typeof(MLDsa), nameof(MLDsa.IsSupported))]
2918
[MemberData(nameof(AllMLDsaCoseDraftExamples))]
3019
public static void DecodeSign1MLDsaCoseDraftExamples(MLDsaAlgorithm algorithm, byte[] mldsaPk, byte[] sign1)
@@ -71,94 +60,6 @@ private static int GetExpectedMLDsaAlgorithm(MLDsaAlgorithm algorithm)
7160
}
7261
}
7362

74-
[Theory]
75-
[MemberData(nameof(AllKeysAndSign1Implementations))]
76-
public void TestSignVerifySingleSignerAllAlgorithms(string keyId, CoseTestSign1 signerImplementation)
77-
{
78-
byte[] payload = Encoding.UTF8.GetBytes("Hello World");
79-
byte[] signature = signerImplementation.Sign(KeyManager, keyId, payload);
80-
Assert.NotNull(signature);
81-
Assert.True(signature.Length > 0);
82-
Assert.True(signerImplementation.Verify(KeyManager, keyId, payload, signature));
83-
84-
// we try different key
85-
CoseTestKey differentKey = KeyManager.GetDifferentKey(keyId);
86-
Assert.False(signerImplementation.Verify(KeyManager, differentKey.Id, payload, signature));
87-
88-
// we try bad signature (or bad key with good signature, same thing)
89-
Assert.False(signerImplementation.Verify(BadKeyManager, keyId, payload, signature));
90-
91-
// we try fake payload
92-
if (!signerImplementation.IsEmbedded)
93-
{
94-
// embedded ignore payload arg
95-
byte[] fakePayload = Encoding.UTF8.GetBytes("Hello World 2");
96-
Assert.False(signerImplementation.Verify(KeyManager, keyId, fakePayload, signature));
97-
}
98-
}
99-
100-
[Theory]
101-
[MemberData(nameof(AllKeysAndMultiSignImplementations))]
102-
public void TestSignVerifyMultiSignerAllAlgorithms(string[] keyIds, CoseTestMultiSign signerImplementation)
103-
{
104-
byte[] payload = Encoding.UTF8.GetBytes("Hello World");
105-
byte[] signature = signerImplementation.Sign(KeyManager, keyIds, payload);
106-
Assert.NotNull(signature);
107-
Assert.True(signature.Length > 0);
108-
Assert.True(signerImplementation.Verify(KeyManager, keyIds, payload, signature));
109-
110-
// we try fake signature
111-
Assert.False(signerImplementation.Verify(BadKeyManager, keyIds, payload, signature));
112-
113-
// we try fake payload
114-
if (!signerImplementation.IsEmbedded)
115-
{
116-
byte[] fakePayload = Encoding.UTF8.GetBytes("Hello World 2");
117-
Assert.False(signerImplementation.Verify(KeyManager, keyIds, fakePayload, signature));
118-
}
119-
}
120-
121-
public static IEnumerable<object[]> AllKeysAndSign1Implementations()
122-
{
123-
foreach (string keyId in CoseTestKeyManager.GetAllKeyIds())
124-
{
125-
foreach (CoseTestSign1 sign1 in CoseTestSign1.GetImplementations())
126-
{
127-
yield return [keyId, sign1];
128-
}
129-
}
130-
}
131-
132-
public static IEnumerable<object[]> AllKeysAndMultiSignImplementations()
133-
{
134-
string[] keyIds = CoseTestKeyManager.GetAllKeyIds();
135-
int[] nrOfKeys = [1, 2, 3, 3, 5];
136-
137-
for (int i = 0; i < keyIds.Length; i++)
138-
{
139-
string[] keysToTest = PickNKeys(nrOfKeys[i % nrOfKeys.Length], i, keyIds);
140-
foreach (CoseTestMultiSign multiSign in CoseTestMultiSign.GetImplementations())
141-
{
142-
yield return [keysToTest, multiSign];
143-
}
144-
}
145-
146-
static string[] PickNKeys(int n, int atIndex, string[] keys)
147-
{
148-
Assert.True(keys.Length >= 1);
149-
150-
// If n is larger than the number of keys, we just wrap around and use same key multiple times.
151-
152-
string[] ret = new string[n];
153-
for (int i = 0; i < n; i++)
154-
{
155-
ret[i] = keys[(atIndex + i) % keys.Length];
156-
}
157-
158-
return ret;
159-
}
160-
}
161-
16263
public static IEnumerable<object[]> AllMLDsaCoseDraftExamples()
16364
{
16465
yield return new object[] { MLDsaAlgorithm.MLDsa44, MLDsa44PkHex.HexToByteArray(), MLDsa44Sign1Hex.HexToByteArray() };

0 commit comments

Comments
 (0)