Skip to content

Commit d141bf4

Browse files
committed
The KTS key-wrapping Ciphers now validate a KTSParameterSpec's key derivation function when they take it, so a KDF the provider cannot service is reported as the InvalidAlgorithmParameterException engineInit declares rather than an unchecked exception out of wrap or unwrap.
1 parent 38c7e7b commit d141bf4

7 files changed

Lines changed: 39 additions & 3 deletions

File tree

‎docs/releasenotes.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Date: 2026, TBD
1717

1818
- A KeyAgreement asked for its shared secret before doPhase returned data rather than refusing. javax.crypto.KeyAgreement specifies IllegalStateException for that state, but nothing in the provider tracked it, so each SPI handed back whatever its result field held: for Diffie-Hellman that was the private value itself - engineInit seeded result with x, so generateSecret() returned the private exponent padded to the prime's length and generateSecret("AES") an all-zero key taken from that padding - while ECDH returned null and its named-algorithm overload raised NullPointerException. BaseAgreementSpi now records whether a doPhase has completed the agreement since the last init and refuses the request with an IllegalStateException naming the algorithm, so every family in the provider - DH, ECDH and ECMQV, the SM2 exchange, both ECGOST families, XDH, SM9 and NewHope - answers the same way, and the DH SPI no longer holds the private value in that field at all.
1919
- Mac.getInstance and KeyGenerator.getInstance by the HMAC SHA-512/224 and SHA-512/256 object identifiers (1.2.840.113549.2.12 and .13) failed, although the same algorithms resolved by name and the matching SecretKeyFactory aliases were registered: the SHA512 mappings called addHMACAlgorithm for the two truncated variants without the addHMACAlias that registers their OIDs against Mac and KeyGenerator. Both are now aliased, as every other HMAC in that class already was.
20+
- A KTSParameterSpec naming an HKDF key-derivation function with a parameters field - a form the provider does not service - was accepted at Cipher init and then failed out of wrap or unwrap with an unchecked IllegalStateException neither method declares. The KTS key-wrapping Ciphers (ML-KEM, Classic McEliece, FrodoKEM, the composite KEM and RSA-KEM) now validate the spec's KDF when they take it, reporting an unserviceable one as the InvalidAlgorithmParameterException engineInit declares, which is what the javax.crypto.KEM services already did through KdfUtil.resolveKemSpec.
2021

2122
### 2.1.3 Additional Features and Functionality
2223

‎prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/cmce/CMCECipherSpi.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.bouncycastle.jcajce.provider.asymmetric.util.WrapUtil;
2929
import org.bouncycastle.jcajce.provider.util.SecurityExceptions;
3030
import org.bouncycastle.jcajce.spec.CMCEParameterSpec;
31+
import org.bouncycastle.jcajce.provider.asymmetric.util.KdfUtil;
3132
import org.bouncycastle.jcajce.spec.KTSParameterSpec;
3233
import org.bouncycastle.util.Arrays;
3334
import org.bouncycastle.util.Exceptions;
@@ -136,6 +137,8 @@ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec paramSpec,
136137
}
137138

138139
kemParameterSpec = (KTSParameterSpec)paramSpec;
140+
141+
KdfUtil.checkKdfSupported(kemParameterSpec);
139142
}
140143

141144
if (opmode == Cipher.WRAP_MODE)

‎prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/compositekem/CompositeKEMCipherSpi.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.bouncycastle.jcajce.CompositePublicKey;
2828
import org.bouncycastle.jcajce.provider.asymmetric.util.WrapUtil;
2929
import org.bouncycastle.jcajce.provider.util.SecurityExceptions;
30+
import org.bouncycastle.jcajce.provider.asymmetric.util.KdfUtil;
3031
import org.bouncycastle.jcajce.spec.KTSParameterSpec;
3132
import org.bouncycastle.util.Arrays;
3233
import org.bouncycastle.util.Exceptions;
@@ -116,6 +117,8 @@ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec paramSpec,
116117
}
117118

118119
kemParameterSpec = (KTSParameterSpec)paramSpec;
120+
121+
KdfUtil.checkKdfSupported(kemParameterSpec);
119122
}
120123

121124
if (opmode == Cipher.WRAP_MODE)

‎prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/frodokem/FrodoKEMCipherSpi.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.bouncycastle.jcajce.provider.asymmetric.util.WrapUtil;
2929
import org.bouncycastle.jcajce.provider.util.SecurityExceptions;
3030
import org.bouncycastle.jcajce.spec.FrodoKEMParameterSpec;
31+
import org.bouncycastle.jcajce.provider.asymmetric.util.KdfUtil;
3132
import org.bouncycastle.jcajce.spec.KTSParameterSpec;
3233
import org.bouncycastle.util.Arrays;
3334
import org.bouncycastle.util.Exceptions;
@@ -136,6 +137,8 @@ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec paramSpec,
136137
}
137138

138139
kemParameterSpec = (KTSParameterSpec)paramSpec;
140+
141+
KdfUtil.checkKdfSupported(kemParameterSpec);
139142
}
140143

141144
if (opmode == Cipher.WRAP_MODE)

‎prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMCipherSpi.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.bouncycastle.crypto.params.MLKEMParameters;
2828
import org.bouncycastle.jcajce.provider.asymmetric.util.WrapUtil;
2929
import org.bouncycastle.jcajce.provider.util.SecurityExceptions;
30+
import org.bouncycastle.jcajce.provider.asymmetric.util.KdfUtil;
3031
import org.bouncycastle.jcajce.spec.KTSParameterSpec;
3132
import org.bouncycastle.jcajce.spec.MLKEMParameterSpec;
3233
import org.bouncycastle.util.Arrays;
@@ -146,6 +147,8 @@ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec paramSpec,
146147
}
147148

148149
kemParameterSpec = (KTSParameterSpec)paramSpec;
150+
151+
KdfUtil.checkKdfSupported(kemParameterSpec);
149152
}
150153

151154
if (opmode == Cipher.WRAP_MODE)

‎prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/RSAKEMCipherSpi.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.bouncycastle.crypto.params.ParametersWithRandom;
3030
import org.bouncycastle.jcajce.provider.asymmetric.util.WrapUtil;
3131
import org.bouncycastle.jcajce.provider.util.SecurityExceptions;
32+
import org.bouncycastle.jcajce.provider.asymmetric.util.KdfUtil;
3233
import org.bouncycastle.jcajce.spec.KTSParameterSpec;
3334
import org.bouncycastle.util.Arrays;
3435
import org.bouncycastle.util.BigIntegers;
@@ -139,6 +140,8 @@ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec paramSpec,
139140

140141
this.ktsSpec = (KTSParameterSpec)paramSpec;
141142

143+
KdfUtil.checkKdfSupported(this.ktsSpec);
144+
142145
if (opmode == Cipher.WRAP_MODE)
143146
{
144147
if (!(key instanceof RSAPublicKey))

‎prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/KdfUtil.java‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,26 @@ public static String resolveAlgorithm(KTSParameterSpec parameterSpec, String alg
223223
/**
224224
* Return true if makeKeyBytes can service the passed in KDF algorithm identifier.
225225
*/
226+
/**
227+
* Refuse a KDF this provider cannot service, at the point the spec is accepted rather than at
228+
* wrap or unwrap, where it would surface as an unchecked exception those methods do not declare.
229+
* The KEM services reach the same check through {@link #resolveKemSpec}; this is for the KTS
230+
* key-wrapping Ciphers, which take their spec at init.
231+
*
232+
* @param ktsSpec the caller-supplied spec; a null KDF algorithm means no KDF and is accepted.
233+
* @throws InvalidAlgorithmParameterException if the spec names a KDF that cannot be serviced.
234+
*/
235+
public static void checkKdfSupported(KTSParameterSpec ktsSpec)
236+
throws InvalidAlgorithmParameterException
237+
{
238+
AlgorithmIdentifier kdfAlgorithm = ktsSpec.getKdfAlgorithm();
239+
240+
if (kdfAlgorithm != null && !isSupportedKdf(kdfAlgorithm))
241+
{
242+
throw new InvalidAlgorithmParameterException("unsupported KDF: " + kdfAlgorithm.getAlgorithm());
243+
}
244+
}
245+
226246
private static boolean isSupportedKdf(AlgorithmIdentifier kdfAlgorithm)
227247
{
228248
ASN1ObjectIdentifier kdfOid = kdfAlgorithm.getAlgorithm();
@@ -360,7 +380,7 @@ else if (PKCSObjectIdentifiers.id_alg_hkdf_with_sha256.equals(kdfAlgorithm.getAl
360380
}
361381
else
362382
{
363-
throw new IllegalStateException("HDKF parameter support not added");
383+
throw new IllegalStateException("HKDF parameter support not added");
364384
}
365385
}
366386
else if (PKCSObjectIdentifiers.id_alg_hkdf_with_sha384.equals(kdfAlgorithm.getAlgorithm()))
@@ -375,7 +395,7 @@ else if (PKCSObjectIdentifiers.id_alg_hkdf_with_sha384.equals(kdfAlgorithm.getAl
375395
}
376396
else
377397
{
378-
throw new IllegalStateException("HDKF parameter support not added");
398+
throw new IllegalStateException("HKDF parameter support not added");
379399
}
380400
}
381401
else if (PKCSObjectIdentifiers.id_alg_hkdf_with_sha512.equals(kdfAlgorithm.getAlgorithm()))
@@ -390,7 +410,7 @@ else if (PKCSObjectIdentifiers.id_alg_hkdf_with_sha512.equals(kdfAlgorithm.getAl
390410
}
391411
else
392412
{
393-
throw new IllegalStateException("HDKF parameter support not added");
413+
throw new IllegalStateException("HKDF parameter support not added");
394414
}
395415
}
396416
else if (NISTObjectIdentifiers.id_Kmac128.equals(kdfAlgorithm.getAlgorithm()))

0 commit comments

Comments
 (0)