diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptDHPrivateKey.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptDHPrivateKey.java index 0caff1f5..5b261a90 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptDHPrivateKey.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptDHPrivateKey.java @@ -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); diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptDHKeyFactoryTest.java b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptDHKeyFactoryTest.java index 5e7ad484..81d01459 100644 --- a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptDHKeyFactoryTest.java +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptDHKeyFactoryTest.java @@ -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 */ + } + } +}