Skip to content

Commit 762db64

Browse files
committed
ML-KEM: Add missing mappings, fix OID mappings
- unify MLKEM mappings using SpiUtil - restrict keys by parameter set where appropriate
1 parent 2326ee5 commit 762db64

File tree

5 files changed

+105
-96
lines changed

5 files changed

+105
-96
lines changed

prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/MLKEM.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
66
import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
77
import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
8+
import org.bouncycastle.jcajce.util.SpiUtil;
89

910
public class MLKEM
1011
{
@@ -51,6 +52,22 @@ public void configure(ConfigurableProvider provider)
5152
provider.addKeyInfoConverter(NISTObjectIdentifiers.id_alg_ml_kem_512, keyFact);
5253
provider.addKeyInfoConverter(NISTObjectIdentifiers.id_alg_ml_kem_768, keyFact);
5354
provider.addKeyInfoConverter(NISTObjectIdentifiers.id_alg_ml_kem_1024, keyFact);
55+
56+
if (SpiUtil.hasKEM())
57+
{
58+
// "This algorithm supports keys with ML-KEM-512, ML-KEM-768, and ML-KEM-1024 parameter sets."
59+
provider.addAlgorithm("KEM.ML-KEM", PREFIX + "MLKEMSpi$MLKEM");
60+
// "[..] (ML-KEM) using the ML-KEM-512 parameter set [..]."
61+
provider.addAlgorithm("KEM.ML-KEM-512", PREFIX + "MLKEMSpi$MLKEM512");
62+
// "[..] (ML-KEM) using the ML-KEM-768 parameter set [..]."
63+
provider.addAlgorithm("KEM.ML-KEM-768", PREFIX + "MLKEMSpi$MLKEM768");
64+
// "[..] (ML-KEM) using the ML-KEM-1024 parameter set [..]."
65+
provider.addAlgorithm("KEM.ML-KEM-1024", PREFIX + "MLKEMSpi$MLKEM1024");
66+
67+
provider.addAlgorithm("Alg.Alias.KEM." + NISTObjectIdentifiers.id_alg_ml_kem_512, "ML-KEM-512");
68+
provider.addAlgorithm("Alg.Alias.KEM." + NISTObjectIdentifiers.id_alg_ml_kem_768, "ML-KEM-768");
69+
provider.addAlgorithm("Alg.Alias.KEM." + NISTObjectIdentifiers.id_alg_ml_kem_1024, "ML-KEM-1024");
70+
}
5471
}
5572
}
5673
}

prov/src/main/jdk17/org/bouncycastle/jcajce/provider/asymmetric/MLKEM.java

Lines changed: 0 additions & 62 deletions
This file was deleted.

prov/src/main/jdk17/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMDecapsulatorSpi.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,22 @@
1212
import org.bouncycastle.pqc.jcajce.provider.util.KdfUtil;
1313
import org.bouncycastle.util.Arrays;
1414

15-
public class MLKEMDecapsulatorSpi
15+
/*
16+
* NOTE: Per javadoc for javax.crypto.KEM, "Encapsulator and Decapsulator objects are also immutable. It is safe to
17+
* invoke multiple encapsulate and decapsulate methods on the same Encapsulator or Decapsulator object at the same
18+
* time. Each invocation of encapsulate will generate a new shared secret and key encapsulation message."
19+
*/
20+
class MLKEMDecapsulatorSpi
1621
implements KEMSpi.DecapsulatorSpi
1722
{
18-
BCMLKEMPrivateKey privateKey;
19-
KTSParameterSpec parameterSpec;
20-
MLKEMExtractor kemExt;
23+
// private final BCMLKEMPrivateKey privateKey;
24+
private final KTSParameterSpec parameterSpec;
25+
private final MLKEMExtractor kemExt;
2126

22-
public MLKEMDecapsulatorSpi(BCMLKEMPrivateKey privateKey, KTSParameterSpec parameterSpec)
27+
MLKEMDecapsulatorSpi(BCMLKEMPrivateKey privateKey, KTSParameterSpec parameterSpec)
2328
{
24-
this.privateKey = privateKey;
29+
// this.privateKey = privateKey;
2530
this.parameterSpec = parameterSpec;
26-
2731
this.kemExt = new MLKEMExtractor(privateKey.getKeyParams());
2832
}
2933

prov/src/main/jdk17/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMEncapsulatorSpi.java

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,19 @@
1414
import org.bouncycastle.pqc.jcajce.provider.util.KdfUtil;
1515
import org.bouncycastle.util.Arrays;
1616

17-
public class MLKEMEncapsulatorSpi
17+
/*
18+
* NOTE: Per javadoc for javax.crypto.KEM, "Encapsulator and Decapsulator objects are also immutable. It is safe to
19+
* invoke multiple encapsulate and decapsulate methods on the same Encapsulator or Decapsulator object at the same
20+
* time. Each invocation of encapsulate will generate a new shared secret and key encapsulation message."
21+
*/
22+
class MLKEMEncapsulatorSpi
1823
implements KEMSpi.EncapsulatorSpi
1924
{
2025
private final BCMLKEMPublicKey publicKey;
2126
private final KTSParameterSpec parameterSpec;
2227
private final MLKEMGenerator kemGen;
2328

24-
public MLKEMEncapsulatorSpi(BCMLKEMPublicKey publicKey, KTSParameterSpec parameterSpec, SecureRandom random)
29+
MLKEMEncapsulatorSpi(BCMLKEMPublicKey publicKey, KTSParameterSpec parameterSpec, SecureRandom random)
2530
{
2631
this.publicKey = publicKey;
2732
this.parameterSpec = parameterSpec;
@@ -59,7 +64,7 @@ else if (!algorithm.equals(keyAlgName))
5964
try
6065
{
6166
SecretKey secretKey = new SecretKeySpec(kdfSecret, from, to - from, algorithm);
62-
return new KEM.Encapsulated(secretKey, encapsulation, null); //TODO: DER encoding for params
67+
return new KEM.Encapsulated(secretKey, encapsulation, null);
6368
}
6469
finally
6570
{
@@ -76,17 +81,6 @@ public int engineSecretSize()
7681
@Override
7782
public int engineEncapsulationSize()
7883
{
79-
//TODO: Maybe make parameterSet public or add getEncapsulationSize() in KEMGenerator.java
80-
switch (publicKey.getKeyParams().getParameters().getName())
81-
{
82-
case "ML-KEM-512":
83-
return 768;
84-
case "ML-KEM-768":
85-
return 1088;
86-
case "ML-KEM-1024":
87-
return 1568;
88-
default:
89-
return -1;
90-
}
84+
return publicKey.getKeyParams().getParameters().getEncapsulationLength();
9185
}
9286
}

prov/src/main/jdk17/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMSpi.java

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,51 +10,107 @@
1010
import javax.crypto.KEMSpi;
1111

1212
import org.bouncycastle.jcajce.spec.KTSParameterSpec;
13+
import org.bouncycastle.pqc.crypto.mlkem.MLKEMKeyParameters;
14+
import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters;
1315

1416
public class MLKEMSpi
1517
implements KEMSpi
1618
{
19+
private final MLKEMParameters mlkemParameters;
20+
21+
MLKEMSpi(MLKEMParameters mlkemParameters)
22+
{
23+
this.mlkemParameters = mlkemParameters;
24+
}
25+
1726
@Override
18-
public EncapsulatorSpi engineNewEncapsulator(PublicKey publicKey, AlgorithmParameterSpec spec, SecureRandom secureRandom)
19-
throws InvalidAlgorithmParameterException, InvalidKeyException
27+
public EncapsulatorSpi engineNewEncapsulator(PublicKey publicKey, AlgorithmParameterSpec spec,
28+
SecureRandom secureRandom) throws InvalidAlgorithmParameterException, InvalidKeyException
2029
{
2130
if (!(publicKey instanceof BCMLKEMPublicKey))
2231
{
23-
throw new InvalidKeyException("unsupported key");
32+
throw new InvalidKeyException("unsupported key type");
2433
}
34+
35+
BCMLKEMPublicKey bcPublicKey = (BCMLKEMPublicKey)publicKey;
36+
checkKeyParameters(bcPublicKey.getKeyParams());
37+
2538
if (spec == null)
2639
{
2740
// Do not wrap key, no KDF
2841
spec = new KTSParameterSpec.Builder("Generic", 256).withNoKdf().build();
2942
}
30-
if (!(spec instanceof KTSParameterSpec))
43+
else if (!(spec instanceof KTSParameterSpec))
3144
{
3245
throw new InvalidAlgorithmParameterException("MLKEM can only accept KTSParameterSpec");
3346
}
34-
if (secureRandom == null)
35-
{
36-
secureRandom = new SecureRandom();
37-
}
38-
return new MLKEMEncapsulatorSpi((BCMLKEMPublicKey) publicKey, (KTSParameterSpec) spec, secureRandom);
47+
48+
return new MLKEMEncapsulatorSpi(bcPublicKey, (KTSParameterSpec)spec, secureRandom);
3949
}
4050

4151
@Override
4252
public DecapsulatorSpi engineNewDecapsulator(PrivateKey privateKey, AlgorithmParameterSpec spec)
43-
throws InvalidAlgorithmParameterException, InvalidKeyException
53+
throws InvalidAlgorithmParameterException, InvalidKeyException
4454
{
4555
if (!(privateKey instanceof BCMLKEMPrivateKey))
4656
{
4757
throw new InvalidKeyException("unsupported key");
4858
}
59+
60+
BCMLKEMPrivateKey bcPrivateKey = (BCMLKEMPrivateKey)privateKey;
61+
checkKeyParameters(bcPrivateKey.getKeyParams());
62+
4963
if (spec == null)
5064
{
5165
// Do not unwrap key, no KDF
5266
spec = new KTSParameterSpec.Builder("Generic", 256).withNoKdf().build();
5367
}
54-
if (!(spec instanceof KTSParameterSpec))
68+
else if (!(spec instanceof KTSParameterSpec))
5569
{
5670
throw new InvalidAlgorithmParameterException("MLKEM can only accept KTSParameterSpec");
5771
}
58-
return new MLKEMDecapsulatorSpi((BCMLKEMPrivateKey) privateKey, (KTSParameterSpec) spec);
72+
73+
return new MLKEMDecapsulatorSpi(bcPrivateKey, (KTSParameterSpec)spec);
74+
}
75+
76+
private void checkKeyParameters(MLKEMKeyParameters key) throws InvalidKeyException
77+
{
78+
if (mlkemParameters != null && mlkemParameters != key.getParameters())
79+
{
80+
throw new InvalidKeyException("ML-KEM key mismatch");
81+
}
82+
}
83+
84+
public static class MLKEM extends MLKEMSpi
85+
{
86+
public MLKEM()
87+
{
88+
// NOTE: Unrestricted parameters/keys
89+
super(null);
90+
}
91+
}
92+
93+
public static class MLKEM512 extends MLKEMSpi
94+
{
95+
public MLKEM512()
96+
{
97+
super(MLKEMParameters.ml_kem_512);
98+
}
99+
}
100+
101+
public static class MLKEM768 extends MLKEMSpi
102+
{
103+
public MLKEM768()
104+
{
105+
super(MLKEMParameters.ml_kem_768);
106+
}
107+
}
108+
109+
public static class MLKEM1024 extends MLKEMSpi
110+
{
111+
public MLKEM1024()
112+
{
113+
super(MLKEMParameters.ml_kem_1024);
114+
}
59115
}
60116
}

0 commit comments

Comments
 (0)