F-5224 / F-5796: enforce 512-bit minimum DH prime in KeyPairGenerator spec init

pull/248/head
Chris Conlon 2026-07-24 10:37:41 -06:00
parent a02707230d
commit e314b928b5
5 changed files with 162 additions and 6 deletions

View File

@ -19,6 +19,14 @@ extern "C" {
#define com_wolfssl_wolfcrypt_Dh_WC_FFDHE_6144 259L
#undef com_wolfssl_wolfcrypt_Dh_WC_FFDHE_8192
#define com_wolfssl_wolfcrypt_Dh_WC_FFDHE_8192 260L
/*
* Class: com_wolfssl_wolfcrypt_Dh
* Method: dhMinSize
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Dh_dhMinSize
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_Dh
* Method: mallocNativeStruct_internal

View File

@ -41,6 +41,18 @@
#define RNG WC_RNG
#endif
/* Some FIPS versions don't have DH_MIN_SIZE defined. Values match those
* used by wolfSSL settings.h. */
#ifndef DH_MIN_SIZE
#ifdef HAVE_FIPS
#define DH_MIN_SIZE 2048
#elif defined(WOLFSSL_MIN_DHKEY_BITS)
#define DH_MIN_SIZE WOLFSSL_MIN_DHKEY_BITS
#else
#define DH_MIN_SIZE 1024
#endif
#endif
/* Some FIPS versions don't have DH_MAX_SIZE defined */
#ifndef DH_MAX_SIZE
#ifdef USE_FAST_MATH
@ -60,6 +72,19 @@
#endif
#endif
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Dh_dhMinSize
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#if !defined(NO_DH)
return (jint)DH_MIN_SIZE;
#else
return 0;
#endif
}
JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_Dh_mallocNativeStruct_1internal(
JNIEnv* env, jobject this)
{

View File

@ -476,14 +476,31 @@ public class WolfCryptKeyPairGenerator extends KeyPairGeneratorSpi {
}
DHParameterSpec dhSpec = (DHParameterSpec)params;
this.dhP = dhSpec.getP().toByteArray();
this.dhG = dhSpec.getG().toByteArray();
BigInteger dhSpecP = dhSpec.getP();
BigInteger dhSpecG = dhSpec.getG();
if (dhP == null || dhG == null) {
if (dhSpecP == null || dhSpecG == null) {
throw new InvalidAlgorithmParameterException(
"Invalid parameters, either p or g is null");
}
if (dhSpecP.signum() <= 0 || dhSpecG.signum() <= 0) {
throw new InvalidAlgorithmParameterException(
"DH parameters p and g must be positive");
}
/* Reject primes the native library would refuse, so the
* failure throws here instead of from generateKeyPair() */
int dhPrimeBits = dhSpecP.bitLength();
if (dhPrimeBits < Dh.DH_MIN_SIZE) {
throw new InvalidAlgorithmParameterException(
"DH prime size must be at least " + Dh.DH_MIN_SIZE +
" bits, got " + dhPrimeBits);
}
this.dhP = dhSpecP.toByteArray();
this.dhG = dhSpecG.toByteArray();
log("init with spec, prime len: " + this.dhP.length);
break;

View File

@ -38,6 +38,13 @@ public class Dh extends NativeStruct {
/** Lock around object state */
protected final Object stateLock = new Object();
/**
* Minimum DH prime size in bits accepted by the native wolfSSL build.
* Tracks native DH_MIN_SIZE, which is build configurable through
* WOLFSSL_MIN_DHKEY_BITS.
*/
public static final int DH_MIN_SIZE = Dh.dhMinSize();
/* Named DH group constants (FFDHE from RFC 7919) */
/** FFDHE 2048-bit group */
public static final int WC_FFDHE_2048 = 256;
@ -103,6 +110,7 @@ public class Dh extends NativeStruct {
}
}
private static native int dhMinSize();
private native long mallocNativeStruct_internal() throws OutOfMemoryError;
private native void wc_InitDhKey();
private native void wc_FreeDhKey();

View File

@ -67,6 +67,7 @@ import java.security.interfaces.ECPrivateKey;
import java.security.spec.ECParameterSpec;
import com.wolfssl.wolfcrypt.Rsa;
import com.wolfssl.wolfcrypt.Dh;
import com.wolfssl.wolfcrypt.Ecc;
import com.wolfssl.wolfcrypt.Fips;
import com.wolfssl.wolfcrypt.FeatureDetect;
@ -598,7 +599,7 @@ public class WolfCryptKeyPairGeneratorTest {
KeyPairGenerator.getInstance("DH", "wolfJCE");
DHParameterSpec spec = new DHParameterSpec(
new BigInteger(prime),
new BigInteger(1, prime),
new BigInteger(base),
testDHKeySizes[i]);
@ -609,6 +610,103 @@ public class WolfCryptKeyPairGeneratorTest {
}
}
@Test
public void testKeyPairGeneratorDhRejectsSmallPrimeSpec()
throws NoSuchProviderException, NoSuchAlgorithmException,
InvalidAlgorithmParameterException {
/* skip test if DH is not compiled in native wolfSSL */
if (!FeatureDetect.DhEnabled()) {
return;
}
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH", "wolfJCE");
/* 255-bit prime (Curve25519 field prime), below the 512-bit min,
* must be rejected before key generation */
DHParameterSpec weakSpec = new DHParameterSpec(
new BigInteger("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
"FFFFFFFFFFFFFFFFFFED", 16),
new BigInteger(base));
try {
kpg.initialize(weakSpec);
fail("initialize(DHParameterSpec) should reject a prime " +
"below the native minimum");
} catch (InvalidAlgorithmParameterException e) {
/* expected */
}
/* Positive check: 2048-bit prime spec should be accepted */
DHParameterSpec goodSpec = new DHParameterSpec(
new BigInteger(1, prime), new BigInteger(base));
kpg.initialize(goodSpec);
assertNotNull(kpg.generateKeyPair());
}
@Test
public void testKeyPairGeneratorDhRejectsNonPositiveParams()
throws NoSuchProviderException, NoSuchAlgorithmException,
InvalidAlgorithmParameterException {
/* skip test if DH is not compiled in native wolfSSL */
if (!FeatureDetect.DhEnabled()) {
return;
}
BigInteger goodP = new BigInteger(1, prime);
BigInteger goodG = new BigInteger(base);
/* A negative p can still clear the minimum bit length check */
BigInteger[][] bad = new BigInteger[][] {
{ goodP.negate(), goodG },
{ goodP, goodG.negate() },
{ BigInteger.ZERO, goodG },
{ goodP, BigInteger.ZERO }
};
for (BigInteger[] pg : bad) {
KeyPairGenerator kpg =
KeyPairGenerator.getInstance("DH", "wolfJCE");
try {
kpg.initialize(new DHParameterSpec(pg[0], pg[1]));
fail("initialize() should reject non-positive DH params");
} catch (InvalidAlgorithmParameterException e) {
/* expected */
}
}
}
@Test
public void testKeyPairGeneratorDhRejectedSpecLeavesStateIntact()
throws NoSuchProviderException, NoSuchAlgorithmException,
InvalidAlgorithmParameterException {
/* skip test if DH is not compiled in native wolfSSL */
if (!FeatureDetect.DhEnabled()) {
return;
}
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH", "wolfJCE");
kpg.initialize(2048);
DHParameterSpec weakSpec = new DHParameterSpec(
BigInteger.ONE.shiftLeft(255).setBit(0), new BigInteger(base));
try {
kpg.initialize(weakSpec);
fail("initialize(DHParameterSpec) should reject a prime " +
"below the native minimum");
} catch (InvalidAlgorithmParameterException e) {
/* expected */
}
/* The rejected spec must not have replaced the 2048-bit params */
DHPrivateKey priv = (DHPrivateKey)kpg.generateKeyPair().getPrivate();
assertTrue("Rejected spec must not weaken generator state",
priv.getParams().getP().bitLength() >= Dh.DH_MIN_SIZE);
}
@Test
public void testKeyPairGeneratorDhInitWithKeySize()
throws NoSuchProviderException, NoSuchAlgorithmException,
@ -684,7 +782,7 @@ public class WolfCryptKeyPairGeneratorTest {
KeyPairGenerator.getInstance("DH", "wolfJCE");
DHParameterSpec spec = new DHParameterSpec(
new BigInteger(prime), new BigInteger(base), 512);
new BigInteger(1, prime), new BigInteger(base), 512);
kpg.initialize(spec);
kpg.initialize(spec);
@ -704,7 +802,7 @@ public class WolfCryptKeyPairGeneratorTest {
KeyPairGenerator.getInstance("DH", "wolfJCE");
DHParameterSpec spec = new DHParameterSpec(
new BigInteger(prime), new BigInteger(base), 512);
new BigInteger(1, prime), new BigInteger(base), 512);
kpg.initialize(spec);