F-5797: bound DER field lengths in WolfCryptDHPrivateKey PKCS#8 parsing

pull/248/head
Chris Conlon 2026-07-24 11:02:18 -06:00
parent e314b928b5
commit f1a011956e
2 changed files with 115 additions and 1 deletions

View File

@ -198,6 +198,10 @@ public class WolfCryptDHPrivateKey implements DHPrivateKey, Destroyable {
}
pLen = WolfCryptASN1Util.getDERLength(derData, idx);
idx += WolfCryptASN1Util.getDERLengthSize(derData, idx);
if (pLen < 0 || pLen > derData.length - idx) {
throw new IllegalArgumentException(
"Invalid PKCS#8: p length exceeds buffer");
}
pBytes = new byte[pLen];
System.arraycopy(derData, idx, pBytes, 0, pLen);
p = new BigInteger(1, pBytes);
@ -210,6 +214,10 @@ public class WolfCryptDHPrivateKey implements DHPrivateKey, Destroyable {
}
gLen = WolfCryptASN1Util.getDERLength(derData, idx);
idx += WolfCryptASN1Util.getDERLengthSize(derData, idx);
if (gLen < 0 || gLen > derData.length - idx) {
throw new IllegalArgumentException(
"Invalid PKCS#8: g length exceeds buffer");
}
gBytes = new byte[gLen];
System.arraycopy(derData, idx, gBytes, 0, gLen);
g = new BigInteger(1, gBytes);
@ -230,6 +238,10 @@ public class WolfCryptDHPrivateKey implements DHPrivateKey, Destroyable {
}
privLen = WolfCryptASN1Util.getDERLength(derData, idx);
idx += WolfCryptASN1Util.getDERLengthSize(derData, idx);
if (privLen < 0 || privLen > derData.length - idx) {
throw new IllegalArgumentException(
"Invalid PKCS#8: private value length exceeds buffer");
}
privBytes = new byte[privLen];
System.arraycopy(derData, idx, privBytes, 0, privLen);
privateVal = new BigInteger(1, privBytes);
@ -382,6 +394,10 @@ public class WolfCryptDHPrivateKey implements DHPrivateKey, Destroyable {
}
pLen = WolfCryptASN1Util.getDERLength(this.encoded, idx);
idx += WolfCryptASN1Util.getDERLengthSize(this.encoded, idx);
if (pLen < 0 || pLen > this.encoded.length - idx) {
throw new IllegalStateException(
"Invalid PKCS#8: p length exceeds buffer");
}
pBytes = new byte[pLen];
System.arraycopy(this.encoded, idx, pBytes, 0, pLen);
p = new BigInteger(1, pBytes);
@ -394,6 +410,10 @@ public class WolfCryptDHPrivateKey implements DHPrivateKey, Destroyable {
}
gLen = WolfCryptASN1Util.getDERLength(this.encoded, idx);
idx += WolfCryptASN1Util.getDERLengthSize(this.encoded, idx);
if (gLen < 0 || gLen > this.encoded.length - idx) {
throw new IllegalStateException(
"Invalid PKCS#8: g length exceeds buffer");
}
gBytes = new byte[gLen];
System.arraycopy(this.encoded, idx, gBytes, 0, gLen);
g = new BigInteger(1, gBytes);
@ -486,6 +506,10 @@ public class WolfCryptDHPrivateKey implements DHPrivateKey, Destroyable {
}
privLen = WolfCryptASN1Util.getDERLength(this.encoded, idx);
idx += WolfCryptASN1Util.getDERLengthSize(this.encoded, idx);
if (privLen < 0 || privLen > this.encoded.length - idx) {
throw new IllegalStateException(
"Invalid PKCS#8: private value length exceeds buffer");
}
privBytes = new byte[privLen];
System.arraycopy(this.encoded, idx, privBytes, 0, privLen);
privateVal = new BigInteger(1, privBytes);

View File

@ -50,6 +50,7 @@ import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Test;
import java.lang.reflect.Field;
import com.wolfssl.provider.jce.WolfCryptProvider;
import com.wolfssl.provider.jce.WolfCryptDHPublicKey;
import com.wolfssl.wolfcrypt.FeatureDetect;
@ -490,6 +491,44 @@ public class WolfCryptDHKeyFactoryTest {
}
}
@Test
public void testPKCS8RejectsOversizedDERFieldLength() throws Exception {
Assume.assumeTrue(FeatureDetect.DhEnabled());
KeyFactory kf = KeyFactory.getInstance("DH", "wolfJCE");
/* PKCS#8 that parses up to the p INTEGER, whose length is a 4-byte
* long-form 0x7FFFFFFF. Guard must reject it as a clean
* InvalidKeySpecException instead. */
byte[] malicious = new byte[] {
(byte)0x30, (byte)0x30,
(byte)0x02, (byte)0x01, (byte)0x00,
(byte)0x30, (byte)0x03,
(byte)0x06, (byte)0x01, (byte)0x2A,
(byte)0x30, (byte)0x09,
(byte)0x02,
(byte)0x84, (byte)0x7F, (byte)0xFF, (byte)0xFF, (byte)0xFF
};
try {
kf.generatePrivate(new PKCS8EncodedKeySpec(malicious));
fail("Should reject PKCS#8 with oversized DER field length");
} catch (InvalidKeySpecException e) {
/* Expected */
}
/* Positive test: a well-formed key still parses */
if (enabledKeySizes.contains(2048)) {
KeyPairGenerator kpg =
KeyPairGenerator.getInstance("DH", "wolfJCE");
kpg.initialize(2048);
byte[] valid = kpg.generateKeyPair().getPrivate().getEncoded();
assertNotNull(kf.generatePrivate(new PKCS8EncodedKeySpec(valid)));
}
}
@Test
public void testDHPublicKeySpecRangeValidation() throws Exception {
@ -994,5 +1033,56 @@ public class WolfCryptDHKeyFactoryTest {
assertEquals("Public key p should match",
originalPub.getParams().getP(), roundTripPubDH.getParams().getP());
}
}
/**
* Replace the cached DER of a key and clear its lazily-extracted fields,
* so the next getter re-parses the supplied bytes. Mirrors the state a
* key is left in after deserialization, where transient caches are null
* but the encoded form is populated.
*/
private static void resetEncoded(Object key, byte[] der,
String... cacheFields) throws Exception {
Field enc = key.getClass().getDeclaredField("encoded");
enc.setAccessible(true);
enc.set(key, der);
for (String name : cacheFields) {
Field f = key.getClass().getDeclaredField(name);
f.setAccessible(true);
f.set(key, null);
}
}
@Test
public void testPrivateKeyLazyExtractRejectsOversizedLength()
throws Exception {
Assume.assumeTrue(FeatureDetect.DhEnabled());
Assume.assumeTrue(enabledKeySizes.contains(2048));
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH", "wolfJCE");
kpg.initialize(2048);
DHPrivateKey priv = (DHPrivateKey)kpg.generateKeyPair().getPrivate();
/* PKCS#8 variant, adds the version INTEGER ahead of the params */
byte[] oversizedPkcs8 = new byte[] {
(byte)0x30, (byte)0x30,
(byte)0x02, (byte)0x01, (byte)0x00,
(byte)0x30, (byte)0x03,
(byte)0x06, (byte)0x01, (byte)0x2A,
(byte)0x30, (byte)0x09,
(byte)0x02,
(byte)0x84, (byte)0x7F, (byte)0xFF, (byte)0xFF, (byte)0xFF
};
resetEncoded(priv, oversizedPkcs8, "paramSpec", "privateValue");
try {
priv.getParams();
fail("getParams() should reject an oversized DER length");
} catch (IllegalStateException e) {
/* expected */
}
}
}