Skip to content

Commit a35bbd2

Browse files
author
royb
committed
replaced internal ML-KEM usage in JcaTlsCrypto with JCE providers
1 parent 7626743 commit a35bbd2

File tree

10 files changed

+217
-39
lines changed

10 files changed

+217
-39
lines changed

tls/src/main/java/org/bouncycastle/tls/crypto/TlsKemConfig.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
11
package org.bouncycastle.tls.crypto;
22

3+
import org.bouncycastle.jcajce.spec.KEMParameterSpec;
4+
import org.bouncycastle.jcajce.spec.KTSParameterSpec;
5+
36
public class TlsKemConfig
47
{
8+
protected final KTSParameterSpec ktsParameterSpec;
59
protected final int namedGroup;
610
protected final boolean isServer;
711

812
public TlsKemConfig(int namedGroup, boolean isServer)
913
{
1014
this.namedGroup = namedGroup;
1115
this.isServer = isServer;
16+
this.ktsParameterSpec = new KTSParameterSpec.Builder("AES-KWP", 256).withNoKdf().build();
17+
}
18+
public TlsKemConfig(int namedGroup, boolean isServer, KTSParameterSpec ktsParameterSpec)
19+
{
20+
this.namedGroup = namedGroup;
21+
this.isServer = isServer;
22+
this.ktsParameterSpec = ktsParameterSpec;
1223
}
1324

1425
public int getNamedGroup()
@@ -20,4 +31,9 @@ public boolean isServer()
2031
{
2132
return isServer;
2233
}
34+
35+
public KTSParameterSpec getKtsParameterSpec()
36+
{
37+
return ktsParameterSpec;
38+
}
2339
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ else if (NamedGroup.refersToASpecificFiniteField(namedGroup))
464464
}
465465
else if (NamedGroup.refersToASpecificKem(namedGroup))
466466
{
467+
//Note: There is no AlgorithmParametersSpi for ML-KEM
467468
return KemUtil.getAlgorithmParameters(this, NamedGroup.getKemName(namedGroup));
468469
}
469470

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
package org.bouncycastle.tls.crypto.impl.jcajce;
22

33
import java.io.IOException;
4+
import java.security.KeyPair;
45

5-
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
6-
import org.bouncycastle.crypto.SecretWithEncapsulation;
7-
import org.bouncycastle.pqc.crypto.mlkem.MLKEMPrivateKeyParameters;
8-
import org.bouncycastle.pqc.crypto.mlkem.MLKEMPublicKeyParameters;
6+
import org.bouncycastle.jcajce.SecretKeyWithEncapsulation;
7+
import org.bouncycastle.jcajce.provider.asymmetric.mlkem.BCMLKEMPrivateKey;
8+
import org.bouncycastle.jcajce.provider.asymmetric.mlkem.BCMLKEMPublicKey;
99
import org.bouncycastle.tls.crypto.TlsAgreement;
1010
import org.bouncycastle.tls.crypto.TlsSecret;
1111

1212
public class JceTlsMLKem implements TlsAgreement
1313
{
1414
protected final JceTlsMLKemDomain domain;
1515

16-
protected MLKEMPrivateKeyParameters privateKey;
17-
protected MLKEMPublicKeyParameters publicKey;
16+
protected BCMLKEMPrivateKey privateKey;
17+
protected BCMLKEMPublicKey publicKey;
1818
protected TlsSecret secret;
1919

2020
public JceTlsMLKem(JceTlsMLKemDomain domain)
@@ -26,16 +26,16 @@ public byte[] generateEphemeral() throws IOException
2626
{
2727
if (domain.isServer())
2828
{
29-
SecretWithEncapsulation encap = domain.encapsulate(publicKey);
29+
SecretKeyWithEncapsulation encap = domain.encapsulate(publicKey);
3030
this.publicKey = null;
31-
this.secret = domain.adoptLocalSecret(encap.getSecret());
31+
this.secret = domain.adoptLocalSecret(encap.getEncoded());
3232
return encap.getEncapsulation();
3333
}
3434
else
3535
{
36-
AsymmetricCipherKeyPair kp = domain.generateKeyPair();
37-
this.privateKey = (MLKEMPrivateKeyParameters)kp.getPrivate();
38-
return domain.encodePublicKey((MLKEMPublicKeyParameters)kp.getPublic());
36+
KeyPair kp = domain.generateKeyPair();
37+
this.privateKey = (BCMLKEMPrivateKey)kp.getPrivate();
38+
return ((BCMLKEMPublicKey)kp.getPublic()).getPublicData();
3939
}
4040
}
4141

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

Lines changed: 109 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,41 @@
22

33
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
44
import org.bouncycastle.crypto.SecretWithEncapsulation;
5+
import org.bouncycastle.jcajce.SecretKeyWithEncapsulation;
6+
import org.bouncycastle.jcajce.provider.asymmetric.mlkem.BCMLKEMPrivateKey;
7+
import org.bouncycastle.jcajce.provider.asymmetric.mlkem.BCMLKEMPublicKey;
8+
import org.bouncycastle.jcajce.spec.KEMExtractSpec;
9+
import org.bouncycastle.jcajce.spec.KEMGenerateSpec;
10+
import org.bouncycastle.jcajce.spec.KEMParameterSpec;
11+
import org.bouncycastle.jcajce.spec.KTSParameterSpec;
12+
import org.bouncycastle.jcajce.spec.MLKEMParameterSpec;
513
import org.bouncycastle.pqc.crypto.mlkem.MLKEMExtractor;
614
import org.bouncycastle.pqc.crypto.mlkem.MLKEMGenerator;
715
import org.bouncycastle.pqc.crypto.mlkem.MLKEMKeyGenerationParameters;
8-
import org.bouncycastle.pqc.crypto.mlkem.MLKEMKeyPairGenerator;
916
import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters;
1017
import org.bouncycastle.pqc.crypto.mlkem.MLKEMPrivateKeyParameters;
1118
import org.bouncycastle.pqc.crypto.mlkem.MLKEMPublicKeyParameters;
1219
import org.bouncycastle.tls.NamedGroup;
1320
import org.bouncycastle.tls.crypto.TlsAgreement;
1421
import org.bouncycastle.tls.crypto.TlsKemConfig;
1522
import org.bouncycastle.tls.crypto.TlsKemDomain;
23+
import org.bouncycastle.util.encoders.Hex;
24+
25+
import javax.crypto.Cipher;
26+
import javax.crypto.KeyGenerator;
27+
import javax.crypto.spec.SecretKeySpec;
28+
import java.nio.charset.StandardCharsets;
29+
import java.security.AlgorithmParameters;
30+
import java.security.GeneralSecurityException;
31+
import java.security.InvalidAlgorithmParameterException;
32+
import java.security.Key;
33+
import java.security.KeyPair;
34+
import java.security.KeyPairGenerator;
35+
import java.security.NoSuchAlgorithmException;
36+
import java.security.NoSuchProviderException;
37+
import java.security.PrivateKey;
38+
import java.security.PublicKey;
39+
import java.security.SecureRandom;
1640

1741
public class JceTlsMLKemDomain implements TlsKemDomain
1842
{
@@ -38,13 +62,29 @@ public static MLKEMParameters getDomainParameters(TlsKemConfig kemConfig)
3862
protected final TlsKemConfig config;
3963
protected final MLKEMParameters domainParameters;
4064
protected final boolean isServer;
65+
protected KeyGenerator keyGen;
66+
// protected KeyPairGenerator kpg;
67+
// protected Cipher cipher;
68+
4169

4270
public JceTlsMLKemDomain(JcaTlsCrypto crypto, TlsKemConfig kemConfig)
4371
{
4472
this.crypto = crypto;
4573
this.config = kemConfig;
4674
this.domainParameters = getDomainParameters(kemConfig);
4775
this.isServer = kemConfig.isServer();
76+
try
77+
{
78+
this.keyGen = keyGen = crypto.getHelper().createKeyGenerator(domainParameters.getName());
79+
}
80+
catch (NoSuchAlgorithmException e)
81+
{
82+
throw new RuntimeException(e);
83+
}
84+
catch (NoSuchProviderException e)
85+
{
86+
throw new RuntimeException(e);
87+
}
4888
}
4989

5090
public JceTlsSecret adoptLocalSecret(byte[] secret)
@@ -57,34 +97,89 @@ public TlsAgreement createKem()
5797
return new JceTlsMLKem(this);
5898
}
5999

60-
public JceTlsSecret decapsulate(MLKEMPrivateKeyParameters privateKey, byte[] ciphertext)
100+
public JceTlsSecret decapsulate(PrivateKey privateKey, byte[] ciphertext)
61101
{
62-
MLKEMExtractor kemExtract = new MLKEMExtractor(privateKey);
63-
byte[] secret = kemExtract.extractSecret(ciphertext);
64-
return adoptLocalSecret(secret);
102+
try
103+
{
104+
keyGen.init(new KEMExtractSpec.Builder(privateKey, ciphertext, "DEF", 256).withNoKdf().build());
105+
SecretKeyWithEncapsulation secEnc = (SecretKeyWithEncapsulation)keyGen.generateKey();
106+
107+
return adoptLocalSecret(secEnc.getEncoded());
108+
}
109+
catch (Exception e)
110+
{
111+
throw Exceptions.illegalArgumentException("invalid key: " + e.getMessage(), e);
112+
}
113+
114+
115+
// MLKEMExtractor kemExtract = new MLKEMExtractor(privateKey);
116+
// byte[] secret = kemExtract.extractSecret(ciphertext);
117+
// return adoptLocalSecret(secret);
65118
}
66119

67-
public MLKEMPublicKeyParameters decodePublicKey(byte[] encoding)
120+
public BCMLKEMPublicKey decodePublicKey(byte[] encoding)
68121
{
69-
return new MLKEMPublicKeyParameters(domainParameters, encoding);
122+
return new BCMLKEMPublicKey(new MLKEMPublicKeyParameters(domainParameters, encoding));
70123
}
71124

72-
public SecretWithEncapsulation encapsulate(MLKEMPublicKeyParameters publicKey)
125+
public SecretKeyWithEncapsulation encapsulate(PublicKey publicKey)
73126
{
74-
MLKEMGenerator kemGen = new MLKEMGenerator(crypto.getSecureRandom());
75-
return kemGen.generateEncapsulated(publicKey);
127+
try
128+
{
129+
keyGen.init(new KEMGenerateSpec.Builder(publicKey, "DEF", 256).withNoKdf().build());
130+
return (SecretKeyWithEncapsulation)keyGen.generateKey();
131+
}
132+
catch (Exception e)
133+
{
134+
throw Exceptions.illegalArgumentException("invalid key: " + e.getMessage(), e);
135+
}
76136
}
77137

78138
public byte[] encodePublicKey(MLKEMPublicKeyParameters publicKey)
79139
{
80140
return publicKey.getEncoded();
81141
}
82142

83-
public AsymmetricCipherKeyPair generateKeyPair()
143+
private void init()
144+
{
145+
// try
146+
// {
147+
//// kpg = KeyPairGenerator.getInstance("MLKEM");
148+
//// kpg.initialize(MLKEMParameterSpec.fromName(domainParameters.getName()), crypto.getSecureRandom());
149+
//// keyGen = KeyGenerator.getInstance(domainParameters.getName(), "BC");
150+
//
151+
//// cipher = KemUtil.getCipher(crypto, domainParameters.getName());
152+
//
153+
//
154+
// }
155+
// catch (GeneralSecurityException e)
156+
// {
157+
// throw Exceptions.illegalStateException("unable to create key pair: " + e.getMessage(), e);
158+
// }
159+
160+
161+
}
162+
public KeyPair generateKeyPair()
84163
{
85-
MLKEMKeyPairGenerator keyPairGenerator = new MLKEMKeyPairGenerator();
86-
keyPairGenerator.init(new MLKEMKeyGenerationParameters(crypto.getSecureRandom(), domainParameters));
87-
return keyPairGenerator.generateKeyPair();
164+
// AlgorithmParameters params = KemUtil.getAlgorithmParameters(crypto, domainParameters.getName());
165+
// if (params == null)
166+
// {
167+
// throw new IllegalStateException("KEM parameters unavailable");
168+
// }
169+
KeyPairGenerator kpg = null;
170+
try
171+
{
172+
kpg = crypto.getHelper().createKeyPairGenerator(domainParameters.getName());
173+
}
174+
catch (NoSuchAlgorithmException e)
175+
{
176+
throw new RuntimeException(e);
177+
}
178+
catch (NoSuchProviderException e)
179+
{
180+
throw new RuntimeException(e);
181+
}
182+
return kpg.generateKeyPair();
88183
}
89184

90185
public boolean isServer()

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

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package org.bouncycastle.tls.crypto.impl.jcajce;
22

3+
import org.bouncycastle.jcajce.spec.MLKEMParameterSpec;
4+
35
import java.security.AlgorithmParameters;
6+
import java.security.spec.AlgorithmParameterSpec;
47

58
import javax.crypto.Cipher;
69

@@ -10,7 +13,11 @@ static AlgorithmParameters getAlgorithmParameters(JcaTlsCrypto crypto, String ke
1013
{
1114
try
1215
{
13-
// TODO[tls-kem] Return AlgorithmParameters to check against disabled algorithms?
16+
return null;
17+
// AlgorithmParameters algParams = AlgorithmParameters.getInstance(kemName, "BC");
18+
// MLKEMParameterSpec mlkemSpec = MLKEMParameterSpec.fromName(kemName);
19+
// algParams.init(mlkemSpec);
20+
// return algParams;
1421
}
1522
catch (AssertionError e)
1623
{
@@ -33,6 +40,7 @@ static Cipher getCipher(JcaTlsCrypto crypto, String kemName)
3340
}
3441
catch (Exception e)
3542
{
43+
throw new IllegalStateException("KEM cipher failed: " + kemName, e);
3644
}
3745

3846
return null;
@@ -41,7 +49,21 @@ static Cipher getCipher(JcaTlsCrypto crypto, String kemName)
4149
static boolean isKemSupported(JcaTlsCrypto crypto, String kemName)
4250
{
4351
// TODO[tls-kem] When implemented via provider, need to check for support dynamically
44-
// return kemName != null && getCipher(crypto, kemName) != null;
45-
return true;
52+
return kemName != null && getCipher(crypto, kemName) != null;
53+
}
54+
55+
static int getEncapsulationLength(String kemName)
56+
{
57+
switch (kemName)
58+
{
59+
case "ML-KEM-512":
60+
return 768;
61+
case "ML-KEM-768":
62+
return 1088;
63+
case "ML-KEM-1024":
64+
return 1568;
65+
default:
66+
throw new IllegalArgumentException("unknown kem name " + kemName);
67+
}
4668
}
4769
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.bouncycastle.tls.test;
2+
3+
import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto;
4+
5+
public class BcTlsProtocolKemTest
6+
extends TlsProtocolKemTest
7+
{
8+
public BcTlsProtocolKemTest()
9+
{
10+
super(new BcTlsCrypto());
11+
}
12+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.bouncycastle.tls.test;
2+
3+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
4+
import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider;
5+
6+
import java.security.SecureRandom;
7+
8+
public class JcaTlsProtocolKemTest
9+
extends TlsProtocolKemTest
10+
{
11+
public JcaTlsProtocolKemTest()
12+
{
13+
super(new JcaTlsCryptoProvider().setProvider(new BouncyCastleProvider()).create(new SecureRandom()));
14+
}
15+
}

tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemClient.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import java.io.IOException;
44
import java.io.PrintStream;
5+
import java.security.SecureRandom;
56
import java.util.Hashtable;
67
import java.util.Vector;
78

89
import org.bouncycastle.asn1.x509.Certificate;
10+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
911
import org.bouncycastle.tls.AlertDescription;
1012
import org.bouncycastle.tls.AlertLevel;
1113
import org.bouncycastle.tls.CertificateRequest;
@@ -28,6 +30,7 @@
2830
import org.bouncycastle.tls.crypto.TlsCertificate;
2931
import org.bouncycastle.tls.crypto.TlsCrypto;
3032
import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto;
33+
import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider;
3134
import org.bouncycastle.util.Arrays;
3235
import org.bouncycastle.util.Integers;
3336
import org.bouncycastle.util.encoders.Hex;
@@ -44,9 +47,9 @@ class MockTlsKemClient
4447
NamedGroup.MLKEM1024,
4548
};
4649

47-
MockTlsKemClient(TlsSession session)
50+
MockTlsKemClient(TlsCrypto crypto, TlsSession session)
4851
{
49-
super(new BcTlsCrypto());
52+
super(crypto);
5053

5154
this.session = session;
5255
}

0 commit comments

Comments
 (0)