Skip to content

Commit c808b0a

Browse files
committed
TLS: Initial work on draft-tls-westerbaan-mldsa-00
1 parent 9edd166 commit c808b0a

File tree

12 files changed

+248
-9
lines changed

12 files changed

+248
-9
lines changed

tls/src/main/java/org/bouncycastle/jsse/provider/SignatureSchemeInfo.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ private enum All
7171

7272
sm2sig_sm3(SignatureScheme.sm2sig_sm3, "SM3withSM2", "EC"),
7373

74+
// TODO[tls] Need mechanism for restricting signature schemes to TLS 1.3+ before adding
75+
// DRAFT_mldsa44(SignatureScheme.DRAFT_mldsa44, "ML-DSA-44", "ML-DSA-44"),
76+
// DRAFT_mldsa65(SignatureScheme.DRAFT_mldsa65, "ML-DSA-65", "ML-DSA-65"),
77+
// DRAFT_mldsa87(SignatureScheme.DRAFT_mldsa87, "ML-DSA-87", "ML-DSA-87"),
78+
7479
/*
7580
* Legacy/Historical: mostly not supported in 1.3, except ecdsa_sha1 and rsa_pkcs1_sha1 are
7681
* still permitted as a last resort for certs.

tls/src/main/java/org/bouncycastle/tls/SignatureScheme.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ public class SignatureScheme
4545

4646
public static final int sm2sig_sm3 = 0x0708;
4747

48+
/*
49+
* draft-tls-westerbaan-mldsa-00
50+
*/
51+
public static final int DRAFT_mldsa44 = 0x0904;
52+
public static final int DRAFT_mldsa65 = 0x0905;
53+
public static final int DRAFT_mldsa87 = 0x0906;
54+
4855
/*
4956
* RFC 8446 reserved for private use (0xFE00..0xFFFF)
5057
*/
@@ -70,6 +77,9 @@ public static int getCryptoHashAlgorithm(int signatureScheme)
7077
{
7178
case ed25519:
7279
case ed448:
80+
case DRAFT_mldsa44:
81+
case DRAFT_mldsa65:
82+
case DRAFT_mldsa87:
7383
return -1;
7484
case ecdsa_brainpoolP256r1tls13_sha256:
7585
case rsa_pss_pss_sha256:
@@ -146,6 +156,12 @@ public static String getName(int signatureScheme)
146156
return "ecdsa_brainpoolP512r1tls13_sha512";
147157
case sm2sig_sm3:
148158
return "sm2sig_sm3";
159+
case DRAFT_mldsa44:
160+
return "DRAFT_mldsa44";
161+
case DRAFT_mldsa65:
162+
return "DRAFT_mldsa65";
163+
case DRAFT_mldsa87:
164+
return "DRAFT_mldsa87";
149165
default:
150166
return "UNKNOWN";
151167
}
@@ -238,6 +254,19 @@ public static boolean isECDSA(int signatureScheme)
238254
}
239255
}
240256

257+
public static boolean isMLDSA(int signatureScheme)
258+
{
259+
switch (signatureScheme)
260+
{
261+
case DRAFT_mldsa44:
262+
case DRAFT_mldsa65:
263+
case DRAFT_mldsa87:
264+
return true;
265+
default:
266+
return false;
267+
}
268+
}
269+
241270
public static boolean isRSAPSS(int signatureScheme)
242271
{
243272
switch (signatureScheme)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package org.bouncycastle.tls.crypto.impl;
2+
3+
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
4+
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
5+
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
6+
import org.bouncycastle.pqc.crypto.mldsa.MLDSAParameters;
7+
import org.bouncycastle.tls.SignatureScheme;
8+
9+
public class PQCUtil
10+
{
11+
public static ASN1ObjectIdentifier getMLDSAObjectidentifier(int signatureScheme)
12+
{
13+
switch (signatureScheme)
14+
{
15+
case SignatureScheme.DRAFT_mldsa44:
16+
return NISTObjectIdentifiers.id_ml_dsa_44;
17+
case SignatureScheme.DRAFT_mldsa65:
18+
return NISTObjectIdentifiers.id_ml_dsa_65;
19+
case SignatureScheme.DRAFT_mldsa87:
20+
return NISTObjectIdentifiers.id_ml_dsa_87;
21+
default:
22+
throw new IllegalArgumentException();
23+
}
24+
}
25+
26+
public static ASN1ObjectIdentifier getMLDSAObjectidentifier(MLDSAParameters parameters)
27+
{
28+
if (MLDSAParameters.ml_dsa_44 == parameters)
29+
{
30+
return NISTObjectIdentifiers.id_ml_dsa_44;
31+
}
32+
if (MLDSAParameters.ml_dsa_65 == parameters)
33+
{
34+
return NISTObjectIdentifiers.id_ml_dsa_65;
35+
}
36+
if (MLDSAParameters.ml_dsa_87 == parameters)
37+
{
38+
return NISTObjectIdentifiers.id_ml_dsa_87;
39+
}
40+
throw new IllegalArgumentException();
41+
}
42+
43+
public static int getMLDSASignatureScheme(MLDSAParameters parameters)
44+
{
45+
if (MLDSAParameters.ml_dsa_44 == parameters)
46+
{
47+
return SignatureScheme.DRAFT_mldsa44;
48+
}
49+
if (MLDSAParameters.ml_dsa_65 == parameters)
50+
{
51+
return SignatureScheme.DRAFT_mldsa65;
52+
}
53+
if (MLDSAParameters.ml_dsa_87 == parameters)
54+
{
55+
return SignatureScheme.DRAFT_mldsa87;
56+
}
57+
throw new IllegalArgumentException();
58+
}
59+
60+
public static boolean supportsMLDSA(AlgorithmIdentifier pubKeyAlgID, ASN1ObjectIdentifier algorithm)
61+
{
62+
return pubKeyAlgID.getAlgorithm().equals(algorithm)
63+
&& pubKeyAlgID.getParameters() == null;
64+
}
65+
}

tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcDefaultTlsCredentialedSigner.java

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
77
import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters;
88
import org.bouncycastle.crypto.params.RSAKeyParameters;
9+
import org.bouncycastle.pqc.crypto.mldsa.MLDSAPrivateKeyParameters;
910
import org.bouncycastle.tls.Certificate;
1011
import org.bouncycastle.tls.DefaultTlsCredentialedSigner;
1112
import org.bouncycastle.tls.SignatureAndHashAlgorithm;
@@ -22,7 +23,6 @@ public class BcDefaultTlsCredentialedSigner
2223
private static TlsSigner makeSigner(BcTlsCrypto crypto, AsymmetricKeyParameter privateKey, Certificate certificate,
2324
SignatureAndHashAlgorithm signatureAndHashAlgorithm)
2425
{
25-
TlsSigner signer;
2626
if (privateKey instanceof RSAKeyParameters)
2727
{
2828
RSAKeyParameters privKeyRSA = (RSAKeyParameters)privateKey;
@@ -36,11 +36,11 @@ private static TlsSigner makeSigner(BcTlsCrypto crypto, AsymmetricKeyParameter p
3636
}
3737
}
3838

39-
signer = new BcTlsRSASigner(crypto, privKeyRSA);
39+
return new BcTlsRSASigner(crypto, privKeyRSA);
4040
}
4141
else if (privateKey instanceof DSAPrivateKeyParameters)
4242
{
43-
signer = new BcTlsDSASigner(crypto, (DSAPrivateKeyParameters)privateKey);
43+
return new BcTlsDSASigner(crypto, (DSAPrivateKeyParameters)privateKey);
4444
}
4545
else if (privateKey instanceof ECPrivateKeyParameters)
4646
{
@@ -63,22 +63,34 @@ else if (privateKey instanceof ECPrivateKeyParameters)
6363
}
6464
}
6565

66-
signer = new BcTlsECDSASigner(crypto, privKeyEC);
66+
return new BcTlsECDSASigner(crypto, privKeyEC);
6767
}
6868
else if (privateKey instanceof Ed25519PrivateKeyParameters)
6969
{
70-
signer = new BcTlsEd25519Signer(crypto, (Ed25519PrivateKeyParameters)privateKey);
70+
return new BcTlsEd25519Signer(crypto, (Ed25519PrivateKeyParameters)privateKey);
7171
}
7272
else if (privateKey instanceof Ed448PrivateKeyParameters)
7373
{
74-
signer = new BcTlsEd448Signer(crypto, (Ed448PrivateKeyParameters)privateKey);
74+
return new BcTlsEd448Signer(crypto, (Ed448PrivateKeyParameters)privateKey);
75+
}
76+
else if (privateKey instanceof MLDSAPrivateKeyParameters)
77+
{
78+
if (signatureAndHashAlgorithm != null)
79+
{
80+
TlsSigner signer = BcTlsMLDSASigner.create(crypto, (MLDSAPrivateKeyParameters)privateKey,
81+
SignatureScheme.from(signatureAndHashAlgorithm));
82+
if (signer != null)
83+
{
84+
return signer;
85+
}
86+
}
87+
88+
throw new IllegalArgumentException("ML-DSA private key of wrong type for signature algorithm");
7589
}
7690
else
7791
{
7892
throw new IllegalArgumentException("'privateKey' type not supported: " + privateKey.getClass().getName());
7993
}
80-
81-
return signer;
8294
}
8395

8496
public BcDefaultTlsCredentialedSigner(TlsCryptoParameters cryptoParams, BcTlsCrypto crypto,

tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,10 @@ public boolean hasSignatureScheme(int signatureScheme)
455455
switch (signatureScheme)
456456
{
457457
case SignatureScheme.sm2sig_sm3:
458+
// TODO[tls] Test coverage before adding
459+
case SignatureScheme.DRAFT_mldsa44:
460+
case SignatureScheme.DRAFT_mldsa65:
461+
case SignatureScheme.DRAFT_mldsa87:
458462
return false;
459463
default:
460464
{
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package org.bouncycastle.tls.crypto.impl.bc;
2+
3+
import java.io.IOException;
4+
5+
import org.bouncycastle.crypto.params.ParametersWithRandom;
6+
import org.bouncycastle.pqc.crypto.mldsa.MLDSAPrivateKeyParameters;
7+
import org.bouncycastle.pqc.crypto.mldsa.MLDSASigner;
8+
import org.bouncycastle.tls.SignatureAndHashAlgorithm;
9+
import org.bouncycastle.tls.SignatureScheme;
10+
import org.bouncycastle.tls.crypto.TlsStreamSigner;
11+
import org.bouncycastle.tls.crypto.impl.PQCUtil;
12+
13+
public class BcTlsMLDSASigner
14+
extends BcTlsSigner
15+
{
16+
public static BcTlsMLDSASigner create(BcTlsCrypto crypto, MLDSAPrivateKeyParameters privateKey, int signatureScheme)
17+
{
18+
if (signatureScheme != PQCUtil.getMLDSASignatureScheme(privateKey.getParameters()))
19+
{
20+
return null;
21+
}
22+
23+
return new BcTlsMLDSASigner(crypto, privateKey, signatureScheme);
24+
}
25+
26+
private final int signatureScheme;
27+
28+
private BcTlsMLDSASigner(BcTlsCrypto crypto, MLDSAPrivateKeyParameters privateKey, int signatureScheme)
29+
{
30+
super(crypto, privateKey);
31+
32+
this.signatureScheme = signatureScheme;
33+
}
34+
35+
public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] hash) throws IOException
36+
{
37+
throw new UnsupportedOperationException();
38+
}
39+
40+
public TlsStreamSigner getStreamSigner(SignatureAndHashAlgorithm algorithm)
41+
{
42+
if (algorithm == null || SignatureScheme.from(algorithm) != signatureScheme)
43+
{
44+
throw new IllegalStateException("Invalid algorithm: " + algorithm);
45+
}
46+
47+
MLDSASigner signer = new MLDSASigner();
48+
signer.init(true, new ParametersWithRandom(privateKey, crypto.getSecureRandom()));
49+
50+
return new BcTlsStreamSigner(signer);
51+
}
52+
}

tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsRawKeyCertificate.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
import org.bouncycastle.crypto.signers.PSSSigner;
2727
import org.bouncycastle.crypto.signers.RSADigestSigner;
2828
import org.bouncycastle.crypto.util.PublicKeyFactory;
29+
import org.bouncycastle.pqc.crypto.mldsa.MLDSAParameters;
30+
import org.bouncycastle.pqc.crypto.mldsa.MLDSAPublicKeyParameters;
31+
import org.bouncycastle.pqc.crypto.mldsa.MLDSASigner;
2932
import org.bouncycastle.tls.AlertDescription;
3033
import org.bouncycastle.tls.HashAlgorithm;
3134
import org.bouncycastle.tls.SignatureAlgorithm;
@@ -39,6 +42,7 @@
3942
import org.bouncycastle.tls.crypto.TlsEncryptor;
4043
import org.bouncycastle.tls.crypto.TlsVerifier;
4144
import org.bouncycastle.tls.crypto.impl.LegacyTls13Verifier;
45+
import org.bouncycastle.tls.crypto.impl.PQCUtil;
4246
import org.bouncycastle.tls.crypto.impl.RSAUtil;
4347

4448
/**
@@ -247,6 +251,27 @@ public Tls13Verifier createVerifier(int signatureScheme) throws IOException
247251
// return new BcTls13Verifier(verifier);
248252
// }
249253

254+
case SignatureScheme.DRAFT_mldsa44:
255+
case SignatureScheme.DRAFT_mldsa65:
256+
case SignatureScheme.DRAFT_mldsa87:
257+
{
258+
ASN1ObjectIdentifier algorithm = PQCUtil.getMLDSAObjectidentifier(signatureScheme);
259+
validateMLDSA(algorithm);
260+
261+
MLDSAPublicKeyParameters publicKey = getPubKeyMLDSA();
262+
MLDSAParameters parameters = publicKey.getParameters();
263+
if (!PQCUtil.getMLDSAObjectidentifier(parameters).equals(algorithm))
264+
{
265+
throw new TlsFatalAlert(AlertDescription.certificate_unknown,
266+
"ML-DSA public key not for " + SignatureScheme.getText(signatureScheme));
267+
}
268+
269+
MLDSASigner verifier = new MLDSASigner();
270+
verifier.init(false, publicKey);
271+
272+
return new BcTls13Verifier(verifier);
273+
}
274+
250275
default:
251276
throw new TlsFatalAlert(AlertDescription.internal_error);
252277
}
@@ -387,6 +412,18 @@ public Ed448PublicKeyParameters getPubKeyEd448() throws IOException
387412
}
388413
}
389414

415+
public MLDSAPublicKeyParameters getPubKeyMLDSA() throws IOException
416+
{
417+
try
418+
{
419+
return (MLDSAPublicKeyParameters)getPublicKey();
420+
}
421+
catch (ClassCastException e)
422+
{
423+
throw new TlsFatalAlert(AlertDescription.certificate_unknown, "Public key not ML-DSA", e);
424+
}
425+
}
426+
390427
public RSAKeyParameters getPubKeyRSA() throws IOException
391428
{
392429
try
@@ -448,6 +485,12 @@ protected boolean supportsKeyUsage(int keyUsageBit)
448485
return true;
449486
}
450487

488+
protected boolean supportsMLDSA(ASN1ObjectIdentifier algorithm)
489+
{
490+
AlgorithmIdentifier pubKeyAlgID = keyInfo.getAlgorithm();
491+
return PQCUtil.supportsMLDSA(pubKeyAlgID, algorithm);
492+
}
493+
451494
protected boolean supportsRSA_PKCS1()
452495
{
453496
AlgorithmIdentifier pubKeyAlgID = keyInfo.getAlgorithm();
@@ -539,6 +582,15 @@ public void validateKeyUsage(int keyUsageBit)
539582
}
540583
}
541584

585+
protected void validateMLDSA(ASN1ObjectIdentifier algorithm)
586+
throws IOException
587+
{
588+
if (!supportsMLDSA(algorithm))
589+
{
590+
throw new TlsFatalAlert(AlertDescription.certificate_unknown, "No support for ML-DSA signature scheme");
591+
}
592+
}
593+
542594
protected void validateRSA_PKCS1()
543595
throws IOException
544596
{

tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCertificate.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,10 @@ public Tls13Verifier createVerifier(int signatureScheme) throws IOException
274274
// TODO[RFC 8998]
275275
// case SignatureScheme.sm2sig_sm3:
276276

277+
case SignatureScheme.DRAFT_mldsa44:
278+
case SignatureScheme.DRAFT_mldsa65:
279+
case SignatureScheme.DRAFT_mldsa87:
280+
277281
default:
278282
throw new TlsFatalAlert(AlertDescription.internal_error);
279283
}

tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,10 @@ public boolean hasSignatureScheme(int signatureScheme)
778778
switch (signatureScheme)
779779
{
780780
case SignatureScheme.sm2sig_sm3:
781+
// TODO[tls] Implement before adding
782+
case SignatureScheme.DRAFT_mldsa44:
783+
case SignatureScheme.DRAFT_mldsa65:
784+
case SignatureScheme.DRAFT_mldsa87:
781785
return false;
782786
default:
783787
{

tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCertificate.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,10 @@ public Tls13Verifier createVerifier(int signatureScheme) throws IOException
274274
// TODO[RFC 8998]
275275
// case SignatureScheme.sm2sig_sm3:
276276

277+
case SignatureScheme.DRAFT_mldsa44:
278+
case SignatureScheme.DRAFT_mldsa65:
279+
case SignatureScheme.DRAFT_mldsa87:
280+
277281
default:
278282
throw new TlsFatalAlert(AlertDescription.internal_error);
279283
}

0 commit comments

Comments
 (0)