Skip to content

Commit 91a6ee0

Browse files
[release/10.0] Update Composite ML-DSA to draft 8 (#120117)
* Update Composite ML-DSA to draft 8 * PR feedback --------- Co-authored-by: Pranav Senthilnathan <pranav.senthilnathan@live.com>
1 parent 9743146 commit 91a6ee0

File tree

15 files changed

+414
-555
lines changed

15 files changed

+414
-555
lines changed

src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaAlgorithm.cs

Lines changed: 44 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ namespace System.Security.Cryptography
1212
[Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
1313
public sealed class CompositeMLDsaAlgorithm : IEquatable<CompositeMLDsaAlgorithm>
1414
{
15-
internal const int RandomizerSizeInBytes = 32;
16-
1715
/// <summary>
1816
/// Gets the name of the algorithm.
1917
/// </summary>
@@ -454,8 +452,8 @@ private static CompositeMLDsaAlgorithm CreateRsa(
454452
mldsaAlgorithm.PrivateSeedSizeInBytes + maxRsaPrivateKeySizeInBytes,
455453
mldsaAlgorithm.PublicKeySizeInBytes + keySizeInBytes, // Private key contains at least n
456454
mldsaAlgorithm.PublicKeySizeInBytes + maxRsaPublicKeySizeInBytes,
457-
RandomizerSizeInBytes + mldsaAlgorithm.SignatureSizeInBytes + keySizeInBytes,
458-
RandomizerSizeInBytes + mldsaAlgorithm.SignatureSizeInBytes + keySizeInBytes,
455+
mldsaAlgorithm.SignatureSizeInBytes + keySizeInBytes,
456+
mldsaAlgorithm.SignatureSizeInBytes + keySizeInBytes,
459457
oid);
460458
}
461459

@@ -465,29 +463,8 @@ private static CompositeMLDsaAlgorithm CreateECDsa(
465463
int keySizeInBits,
466464
string oid)
467465
{
468-
// The key size calculation depends on the size of the curve algorithm's OID, and only includes the curves
469-
// supported at the time of writing this code. If more curves are added, ensure the OID length is accounted for in the calculation.
470-
Debug.Assert(oid is
471-
Oids.MLDsa44WithECDsaP256PreHashSha256 or
472-
Oids.MLDsa65WithECDsaP256PreHashSha512 or
473-
Oids.MLDsa65WithECDsaP384PreHashSha512 or
474-
Oids.MLDsa87WithECDsaP384PreHashSha512 or
475-
Oids.MLDsa87WithECDsaP521PreHashSha512 or
476-
Oids.MLDsa65WithECDsaBrainpoolP256r1PreHashSha512 or
477-
Oids.MLDsa87WithECDsaBrainpoolP384r1PreHashSha512);
478-
479466
int keySizeInBytes = (keySizeInBits + 7) / 8;
480467

481-
const int MaxUniversalTagLength = 1;
482-
483-
// long form prefix and 4 bytes for length. CLR arrays and spans only support length up to int.MaxValue.
484-
// Padding with leading zero bytes is allowed, but we still limit the length to 4 bytes since the only
485-
// plausible scenario would be encoding a 4-byte numeric data type without trimming.
486-
// Note this bound also covers indefinite length encodings which require only 1 + 2 bytes of overhead.
487-
const int MaxLengthLength = 1 + 4;
488-
489-
const int MaxPrefixLength = MaxUniversalTagLength + MaxLengthLength;
490-
491468
// RFC 5915, Section 3
492469
// ECPrivateKey ::= SEQUENCE {
493470
// version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
@@ -496,51 +473,32 @@ Oids.MLDsa65WithECDsaBrainpoolP256r1PreHashSha512 or
496473
// publicKey [1] BIT STRING OPTIONAL
497474
// }
498475

499-
int maxPrivateKeySizeInBytes =
500-
MaxPrefixLength +
501-
(
502-
// version
503-
MaxPrefixLength + 1 + // Version should always be 1
504-
505-
// privateKey
506-
MaxPrefixLength + keySizeInBytes +
507-
508-
// parameters
509-
1 + MaxLengthLength + // Explicit tag
510-
(
511-
// RFC5480, Section 2.1.1
512-
// ECParameters ::= CHOICE {
513-
// namedCurve OBJECT IDENTIFIER
514-
// -- implicitCurve NULL
515-
// -- specifiedCurve SpecifiedECDomain
516-
// }
517-
// -- implicitCurve and specifiedCurve MUST NOT be used in PKIX.
518-
//
519-
// So this is a CHOICE with with the only option being a namedCurve OBJECT IDENTIFIER.
520-
//
521-
// Curve | OID | DER Encoding with prefix | Length without prefix
522-
// ----------------|-----------------------|----------------------------------|----------------------
523-
// secp256r1 | 1.2.840.10045.3.1.7 | 06 08 2A 86 48 CE 3D 03 01 07 | 8
524-
// secp384r1 | 1.3.132.0.34 | 06 05 2B 81 04 00 22 | 5
525-
// secp521r1 | 1.3.132.0.35 | 06 05 2B 81 04 00 23 | 5
526-
// brainpoolP256r1 | 1.3.36.3.3.2.8.1.1.7 | 06 09 2B 24 03 03 02 08 01 01 07 | 9
527-
// brainpoolP384r1 | 1.3.36.3.3.2.8.1.1.11 | 06 09 2B 24 03 03 02 08 01 01 0B | 9
528-
MaxPrefixLength + 9 // This doesn't need to be exact, but it does need to consider all supported curves.
529-
) +
530-
531-
// publicKey
532-
1 + MaxLengthLength + // Explicit tag
533-
1 + 2 * keySizeInBytes
534-
);
476+
int versionSizeInBytes =
477+
1 + // Tag for INTEGER
478+
1 + // Length field
479+
1; // Value (always 1)
480+
481+
int privateKeySizeInBytes =
482+
1 + // Tag for OCTET STRING
483+
GetDerLengthLength(keySizeInBytes) + // Length field
484+
keySizeInBytes; // Value
485+
486+
// parameters and publicKey must be omitted for Composite ML-DSA
487+
488+
int ecPrivateKeySizeInBytes =
489+
1 + // Tag for SEQUENCE
490+
GetDerLengthLength(versionSizeInBytes + privateKeySizeInBytes) + // Length field
491+
versionSizeInBytes + // Version
492+
privateKeySizeInBytes;
535493

536494
return new CompositeMLDsaAlgorithm(
537495
name,
538-
mldsaAlgorithm.PrivateSeedSizeInBytes + keySizeInBytes, // ECPrivateKey has at least the private key
539-
mldsaAlgorithm.PrivateSeedSizeInBytes + maxPrivateKeySizeInBytes,
496+
mldsaAlgorithm.PrivateSeedSizeInBytes + ecPrivateKeySizeInBytes,
497+
mldsaAlgorithm.PrivateSeedSizeInBytes + ecPrivateKeySizeInBytes,
540498
mldsaAlgorithm.PublicKeySizeInBytes + 1 + 2 * keySizeInBytes,
541499
mldsaAlgorithm.PublicKeySizeInBytes + 1 + 2 * keySizeInBytes,
542-
RandomizerSizeInBytes + mldsaAlgorithm.SignatureSizeInBytes + 2 + 3 * 2, // 2 non-zero INTEGERS and overhead for 3 ASN.1 values
543-
RandomizerSizeInBytes + mldsaAlgorithm.SignatureSizeInBytes + AsymmetricAlgorithmHelpers.GetMaxDerSignatureSize(keySizeInBits),
500+
mldsaAlgorithm.SignatureSizeInBytes + 2 + 3 * 2, // 2 non-zero INTEGERS and overhead for 3 ASN.1 values
501+
mldsaAlgorithm.SignatureSizeInBytes + AsymmetricAlgorithmHelpers.GetMaxDerSignatureSize(keySizeInBits),
544502
oid);
545503
}
546504

@@ -559,9 +517,28 @@ private static CompositeMLDsaAlgorithm CreateEdDsa(
559517
mldsaAlgorithm.PrivateSeedSizeInBytes + keySizeInBytes,
560518
mldsaAlgorithm.PublicKeySizeInBytes + keySizeInBytes,
561519
mldsaAlgorithm.PublicKeySizeInBytes + keySizeInBytes,
562-
RandomizerSizeInBytes + mldsaAlgorithm.SignatureSizeInBytes + 2 * keySizeInBytes,
563-
RandomizerSizeInBytes + mldsaAlgorithm.SignatureSizeInBytes + 2 * keySizeInBytes,
520+
mldsaAlgorithm.SignatureSizeInBytes + 2 * keySizeInBytes,
521+
mldsaAlgorithm.SignatureSizeInBytes + 2 * keySizeInBytes,
564522
oid);
565523
}
524+
525+
private static int GetDerLengthLength(int payloadLength)
526+
{
527+
Debug.Assert(payloadLength >= 0);
528+
529+
if (payloadLength <= 0x7F)
530+
return 1;
531+
532+
if (payloadLength <= 0xFF)
533+
return 2;
534+
535+
if (payloadLength <= 0xFFFF)
536+
return 3;
537+
538+
if (payloadLength <= 0xFFFFFF)
539+
return 4;
540+
541+
return 5;
542+
}
566543
}
567544
}

src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaManaged.ECDsa.cs

Lines changed: 11 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -71,30 +71,13 @@ public static unsafe ECDsaComponent ImportPrivateKey(ECDsaAlgorithm algorithm, R
7171
{
7272
ECPrivateKey ecPrivateKey = ECPrivateKey.Decode(manager.Memory, AsnEncodingRules.BER);
7373

74-
if (ecPrivateKey.Version != 1)
74+
if (ecPrivateKey.Version != 1 ||
75+
ecPrivateKey.Parameters is not null ||
76+
ecPrivateKey.PublicKey is not null)
7577
{
7678
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
7779
}
7880

79-
// If domain parameters are present, validate that they match the composite ML-DSA algorithm.
80-
if (ecPrivateKey.Parameters is ECDomainParameters domainParameters)
81-
{
82-
if (domainParameters.Named is not string curveOid || curveOid != algorithm.CurveOidValue)
83-
{
84-
// The curve specified must be named and match the required curve for the composite ML-DSA algorithm.
85-
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
86-
}
87-
}
88-
89-
byte[]? x = null;
90-
byte[]? y = null;
91-
92-
// If public key is present, add it to the parameters.
93-
if (ecPrivateKey.PublicKey is ReadOnlyMemory<byte> publicKey)
94-
{
95-
EccKeyFormatHelper.GetECPointFromUncompressedPublicKey(publicKey.Span, algorithm.KeySizeInBytes, out x, out y);
96-
}
97-
9881
byte[] d = new byte[ecPrivateKey.PrivateKey.Length];
9982

10083
using (PinAndClear.Track(d))
@@ -107,8 +90,8 @@ public static unsafe ECDsaComponent ImportPrivateKey(ECDsaAlgorithm algorithm, R
10790
Curve = algorithm.Curve,
10891
Q = new ECPoint
10992
{
110-
X = x,
111-
Y = y,
93+
X = null,
94+
Y = null,
11295
},
11396
D = d
11497
};
@@ -121,14 +104,10 @@ public static unsafe ECDsaComponent ImportPrivateKey(ECDsaAlgorithm algorithm, R
121104
#error ECDsa.Create(ECParameters) is avaliable in .NET Framework 4.7.2 and later, so this workaround is not needed anymore.
122105
#endif
123106
Debug.Assert(!string.IsNullOrEmpty(algorithm.CurveOid.FriendlyName));
124-
Debug.Assert(x is null == y is null);
125107

126-
if (x is null)
127-
{
128-
byte[] zero = new byte[d.Length];
129-
x = zero;
130-
y = zero;
131-
}
108+
byte[] zero = new byte[d.Length];
109+
byte[] x = zero;
110+
byte[] y = zero;
132111

133112
if (!TryValidateNamedCurve(x, y, d))
134113
{
@@ -227,7 +206,7 @@ internal override bool TryExportPrivateKey(Span<byte> destination, out int bytes
227206

228207
try
229208
{
230-
WriteKey(ecParameters.D, ecParameters.Q.X, ecParameters.Q.Y, _algorithm.CurveOidValue, writer);
209+
WriteKey(ecParameters.D, writer);
231210
return writer.TryEncode(destination, out bytesWritten);
232211
}
233212
finally
@@ -260,7 +239,7 @@ internal override bool TryExportPrivateKey(Span<byte> destination, out int bytes
260239
throw new CryptographicException();
261240
}
262241

263-
WriteKey(d, x, y, _algorithm.CurveOidValue, writer);
242+
WriteKey(d, writer);
264243
return true;
265244
});
266245
});
@@ -273,7 +252,7 @@ internal override bool TryExportPrivateKey(Span<byte> destination, out int bytes
273252
}
274253
#endif
275254

276-
static void WriteKey(byte[] d, byte[]? x, byte[]? y, string curveOid, AsnWriter writer)
255+
static void WriteKey(byte[] d, AsnWriter writer)
277256
{
278257
// ECPrivateKey
279258
using (writer.PushSequence())
@@ -283,23 +262,6 @@ static void WriteKey(byte[] d, byte[]? x, byte[]? y, string curveOid, AsnWriter
283262

284263
// privateKey
285264
writer.WriteOctetString(d);
286-
287-
// domainParameters
288-
using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true)))
289-
{
290-
writer.WriteObjectIdentifier(curveOid);
291-
}
292-
293-
// publicKey
294-
if (x != null)
295-
{
296-
Debug.Assert(y != null);
297-
298-
using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1, isConstructed: true)))
299-
{
300-
EccKeyFormatHelper.WriteUncompressedPublicKey(x, y, writer);
301-
}
302-
}
303265
}
304266
}
305267
}

0 commit comments

Comments
 (0)