Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -473,23 +473,67 @@ private static CompositeMLDsaAlgorithm CreateECDsa(
// publicKey [1] BIT STRING OPTIONAL
// }

// version

int versionSizeInBytes =
1 + // Tag for INTEGER
1 + // Length field
1; // Value (always 1)

// privateKey

int privateKeySizeInBytes =
1 + // Tag for OCTET STRING
GetDerLengthLength(keySizeInBytes) + // Length field
keySizeInBytes; // Value

// parameters and publicKey must be omitted for Composite ML-DSA
// parameters

int namedCurveSizeInBytes =
oid switch
{
Oids.MLDsa44WithECDsaP256PreHashSha256 or
Oids.MLDsa65WithECDsaP256PreHashSha512 =>
// 1.2.840.10045.3.1.7
// 06 08 2A 86 48 CE 3D 03 01 07
10,
Oids.MLDsa65WithECDsaP384PreHashSha512 or
Oids.MLDsa87WithECDsaP384PreHashSha512 =>
// 1.3.132.0.34
// 06 05 2B 81 04 00 22
7,
Oids.MLDsa87WithECDsaP521PreHashSha512 =>
// 1.3.132.0.35
// 06 05 2B 81 04 00 23
7,
Oids.MLDsa65WithECDsaBrainpoolP256r1PreHashSha512 =>
// 1.3.36.3.3.2.8.1.1.7
// 06 09 2B 24 03 03 02 08 01 01 07
11,
Oids.MLDsa87WithECDsaBrainpoolP384r1PreHashSha512 =>
// 1.3.36.3.3.2.8.1.1.11
// 06 09 2B 24 03 03 02 08 01 01 0B
11,
_ => AssertAndThrow(oid),
};

static int AssertAndThrow(string oid)
{
Debug.Fail($"Unsupported OID: {oid}.");
throw new CryptographicException();
}

int parametersSizeInBytes =
1 + // Context-specific tag for [0]
GetDerLengthLength(namedCurveSizeInBytes) + // Length field
namedCurveSizeInBytes; // Value

// publicKey must be omitted for Composite ML-DSA

int ecPrivateKeySizeInBytes =
1 + // Tag for SEQUENCE
GetDerLengthLength(versionSizeInBytes + privateKeySizeInBytes) + // Length field
versionSizeInBytes + // Version
privateKeySizeInBytes;
1 + // Tag for SEQUENCE
GetDerLengthLength(versionSizeInBytes + privateKeySizeInBytes + parametersSizeInBytes) + // Length field
versionSizeInBytes + privateKeySizeInBytes + parametersSizeInBytes; // Value

return new CompositeMLDsaAlgorithm(
name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,17 @@ public static unsafe ECDsaComponent ImportPrivateKey(ECDsaAlgorithm algorithm, R
ECPrivateKey ecPrivateKey = ECPrivateKey.Decode(manager.Memory, AsnEncodingRules.BER);

if (ecPrivateKey.Version != 1 ||
ecPrivateKey.Parameters is not null ||
ecPrivateKey.PublicKey is not null)
{
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}

if (ecPrivateKey.Parameters?.Named != algorithm.CurveOidValue)
{
// The curve specified must be named and match the required curve for the Composite ML-DSA algorithm.
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}

byte[] d = new byte[ecPrivateKey.PrivateKey.Length];

using (PinAndClear.Track(d))
Expand Down Expand Up @@ -206,7 +211,7 @@ internal override bool TryExportPrivateKey(Span<byte> destination, out int bytes

try
{
WriteKey(ecParameters.D, writer);
WriteKey(ecParameters.D, _algorithm.CurveOidValue, writer);
return writer.TryEncode(destination, out bytesWritten);
}
finally
Expand Down Expand Up @@ -239,7 +244,7 @@ internal override bool TryExportPrivateKey(Span<byte> destination, out int bytes
throw new CryptographicException();
}

WriteKey(d, writer);
WriteKey(d, _algorithm.CurveOidValue, writer);
return true;
});
});
Expand All @@ -252,7 +257,7 @@ internal override bool TryExportPrivateKey(Span<byte> destination, out int bytes
}
#endif

static void WriteKey(byte[] d, AsnWriter writer)
static void WriteKey(byte[] d, string curveOid, AsnWriter writer)
{
// ECPrivateKey
using (writer.PushSequence())
Expand All @@ -262,6 +267,12 @@ static void WriteKey(byte[] d, AsnWriter writer)

// privateKey
writer.WriteOctetString(d);

// parameters
using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true)))
{
writer.WriteObjectIdentifier(curveOid);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ internal static CompositeMLDsa GenerateKeyImpl(CompositeMLDsaAlgorithm algorithm

AlgorithmMetadata metadata = s_algorithmMetadata[algorithm];

// draft-ietf-lamps-pq-composite-sigs-08, 4.1
// draft-ietf-lamps-pq-composite-sigs-12, 4.1
// 1. Generate component keys
//
// mldsaSeed = Random(32)
Expand Down Expand Up @@ -115,7 +115,7 @@ internal static CompositeMLDsa ImportCompositeMLDsaPublicKeyImpl(CompositeMLDsaA

AlgorithmMetadata metadata = s_algorithmMetadata[algorithm];

// draft-ietf-lamps-pq-composite-sigs-08, 5.1
// draft-ietf-lamps-pq-composite-sigs-12, 5.1
// 1. Parse each constituent encoded public key.
// The length of the mldsaKey is known based on the
// size of the ML-DSA component key length specified
Expand Down Expand Up @@ -167,7 +167,7 @@ internal static CompositeMLDsa ImportCompositeMLDsaPrivateKeyImpl(CompositeMLDsa

AlgorithmMetadata metadata = s_algorithmMetadata[algorithm];

// draft-ietf-lamps-pq-composite-sigs-08, 5.2
// draft-ietf-lamps-pq-composite-sigs-12, 5.2
// 1. Parse each constituent encoded key.
//
// mldsaSeed = bytes[:32]
Expand Down Expand Up @@ -203,7 +203,7 @@ static CryptographicException FailAndGetException()

protected override int SignDataCore(ReadOnlySpan<byte> data, ReadOnlySpan<byte> context, Span<byte> destination)
{
// draft-ietf-lamps-pq-composite-sigs-08, 4.2
// draft-ietf-lamps-pq-composite-sigs-12, 4.2
// 1. If len(ctx) > 255:
// return error

Expand Down Expand Up @@ -287,7 +287,7 @@ protected override int SignDataCore(ReadOnlySpan<byte> data, ReadOnlySpan<byte>

protected override bool VerifyDataCore(ReadOnlySpan<byte> data, ReadOnlySpan<byte> context, ReadOnlySpan<byte> signature)
{
// draft-ietf-lamps-pq-composite-sigs-08, 4.3
// draft-ietf-lamps-pq-composite-sigs-12, 4.3
// 1. If len(ctx) > 255
// return error

Expand Down Expand Up @@ -375,7 +375,7 @@ protected override bool TryExportPkcs8PrivateKeyCore(Span<byte> destination, out

protected override int ExportCompositeMLDsaPublicKeyCore(Span<byte> destination)
{
// draft-ietf-lamps-pq-composite-sigs-08, 5.1
// draft-ietf-lamps-pq-composite-sigs-12, 5.1
// 1. Combine and output the encoded public key
//
// output mldsaPK || tradPK
Expand All @@ -397,7 +397,7 @@ protected override int ExportCompositeMLDsaPublicKeyCore(Span<byte> destination)

protected override int ExportCompositeMLDsaPrivateKeyCore(Span<byte> destination)
{
// draft-ietf-lamps-pq-composite-sigs-08, 5.2
// draft-ietf-lamps-pq-composite-sigs-12, 5.2
// 1. Combine and output the encoded private key.
//
// output mldsaSeed || tradSK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,57 +158,118 @@ static byte[] CreateKeyWithVersion(int? version)

return ComposeKeys(
MLDsaTestsData.IetfMLDsa65.PrivateSeed,
WriteECPrivateKey(version, ecdsaKey.D, oid: null, point: null));
WriteECPrivateKey(version, ecdsaKey.D, oid: ecdsaKey.Curve.Oid.Value, point: null));
}
}

[Fact]
public static void ImportBadPrivateKey_ECDsa_NoPrivateKey()
{
ECParameters ecdsaKey = EccTestData.GetNistP256ReferenceKey();

byte[] compositeKey = ComposeKeys(
MLDsaTestsData.IetfMLDsa65.PrivateSeed,
WriteECPrivateKey(version: 1, d: null, oid: null, point: null));
WriteECPrivateKey(version: 1, d: null, oid: ecdsaKey.Curve.Oid.Value, point: null));

AssertImportBadPrivateKey(CompositeMLDsaAlgorithm.MLDsa65WithECDsaP256, compositeKey);
}

[Fact]
public static void ImportBadPrivateKey_ECDsa_HasCurve()
public static void ImportBadPrivateKey_ECDsa_WrongCurve()
{
ECParameters ecdsaKey = EccTestData.GetNistP256ReferenceKey();
CompositeMLDsaAlgorithm algorithm = CompositeMLDsaAlgorithm.MLDsa65WithECDsaP256;

// Domain parameters are not allowed
// Wrong curve OID
AssertImportBadPrivateKey(
CompositeMLDsaAlgorithm.MLDsa65WithECDsaP256,
ComposeKeys(
algorithm,
CreateKeyWithCurveOid(ECCurve.NamedCurves.nistP521.Oid.Value));

AssertImportBadPrivateKey(
algorithm,
CreateKeyWithCurveOid("1.3.36.3.3.2.8.1.1.7")); // brainpoolP256r1

// P256 is 1.2.840.10045.3.1.7, so try an OID with same length but invalid
AssertImportBadPrivateKey(
algorithm,
CreateKeyWithCurveOid("1.2.840.10045.3.1.6"));

// No parameters
AssertImportBadPrivateKey(
algorithm,
CreateKeyWithCurveOid(null));

static byte[] CreateKeyWithCurveOid(string? oid)
{
ECParameters ecdsaKey = EccTestData.GetNistP256ReferenceKey();

return ComposeKeys(
MLDsaTestsData.IetfMLDsa65.PrivateSeed,
WriteECPrivateKey(version: 1, ecdsaKey.D, ecdsaKey.Curve.Oid.Value, point: null)));
WriteECPrivateKey(version: 1, ecdsaKey.D, oid, null));
}
}

[Fact]
public static void ImportPrivateKey_ECDsa_HasPublicKey()
public static void ImportBadPrivateKey_ECDsa_ImplicitCurve()
{
ECParameters ecdsaKey = EccTestData.GetNistP256ReferenceKey();
ECParameters ecKey = EccTestData.GetNistP256ReferenceKey();
AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);

// Public key is not allowed
AssertImportBadPrivateKey(
CompositeMLDsaAlgorithm.MLDsa65WithECDsaP256,
ComposeKeys(
MLDsaTestsData.IetfMLDsa65.PrivateSeed,
WriteECPrivateKey(version: 1, ecdsaKey.D, oid: null, ecdsaKey.Q)));
// ECPrivateKey
using (writer.PushSequence())
{
// version
writer.WriteInteger(1);

// privateKey
writer.WriteOctetString(ecKey.D);

// domainParameters
using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true)))
{
// Implicit curve is encoded as ASN.1 NULL
writer.WriteNull();
}
}

byte[] compositeKey = ComposeKeys(MLDsaTestsData.IetfMLDsa65.PrivateSeed, writer);
AssertImportBadPrivateKey(CompositeMLDsaAlgorithm.MLDsa65WithECDsaP256, compositeKey);
}

[Fact]
public static void ImportBadPrivateKey_ECDsa_ExplicitCurve()
{
// Key with explicit curve parameters generated with:
// > openssl ecparam -name prime256v1 -genkey -param_enc explicit -noout | openssl ec -no_public
string keyBase64 =
"""
MIIBIgIBAQQgWal2XjEwfJhLkEsSJfu3MbTRuzcsr320CEuAH03ojJqggfowgfcC
AQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAAAAAAAAAAAAAA////////////////
MFsEIP////8AAAABAAAAAAAAAAAAAAAA///////////////8BCBaxjXYqjqT57Pr
vVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSdNgiG5wSTamZ44ROdJreBn36QBEEE
axfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5RdiYwpZP40Li/hp/m47n60p8D54W
K84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA//////////+85vqtpxeehPO5ysL8
YyVRAgEB
""";
byte[] key = Convert.FromBase64String(keyBase64);

byte[] compositeKey = new byte[MLDsaTestsData.IetfMLDsa65.PrivateSeed.Length + key.Length];
MLDsaTestsData.IetfMLDsa65.PrivateSeed.CopyTo(compositeKey, 0);
key.CopyTo(compositeKey, MLDsaTestsData.IetfMLDsa65.PrivateSeed.Length);

AssertImportBadPrivateKey(CompositeMLDsaAlgorithm.MLDsa65WithECDsaP256, compositeKey);
}

[Fact]
public static void ImportPrivateKey_ECDsa_HasCurveAndPublicKey()
public static void ImportPrivateKey_ECDsa_HasPublicKey()
{
ECParameters ecdsaKey = EccTestData.GetNistP256ReferenceKey();

// Domain parameters and public key are not allowed
// Public key is not allowed
AssertImportBadPrivateKey(
CompositeMLDsaAlgorithm.MLDsa65WithECDsaP256,
ComposeKeys(
MLDsaTestsData.IetfMLDsa65.PrivateSeed,
WriteECPrivateKey(version: 1, ecdsaKey.D, ecdsaKey.Curve.Oid.Value, ecdsaKey.Q)));
WriteECPrivateKey(version: 1, ecdsaKey.D, oid: ecdsaKey.Curve.Oid.Value, ecdsaKey.Q)));
}

static byte[] ComposeKeys(byte[] mldsaKey, AsnWriter tradKey)
Expand Down
Loading
Loading