F-4354 / F-4355: bound encoded entry and HMAC sizes in WolfSSLKeyStore.engineLoad

pull/238/head
Chris Conlon 2026-07-08 11:08:13 -06:00
parent 43c0058a8e
commit 865c769427
5 changed files with 99 additions and 2 deletions

View File

@ -36,6 +36,7 @@ file for JCE provider customization:
| --- | --- | --- | --- |
| wolfjce.wks.iterationCount | 210,000 | Numeric | PBKDF2 iteration count (10,000 minimum) |
| wolfjce.wks.maxCertChainLength | 100 | Integer | Max cert chain length |
| wolfjce.wks.maxEntrySize | 10485760 | Integer | Max encoded entry size in bytes when loading WKS (10 MB default) |
| wolfjce.keystore.kekCacheEnabled | false | true | Enable KEK caching in WKS KeyStore for performance |
| wolfjce.keystore.kekCacheTtlSec | 300 | Integer | KEK cache TTL in seconds (1 second minimum) |
| wolfjce.mapJKStoWKS | UNSET | true | Register fake JKS KeyStore service mapped to WKS |

View File

@ -13,6 +13,7 @@ please reference the appropriate Security Policy or contact fips@wolfssl.com.
| --- | --- | --- | --- |
| `wolfjce.wks.iterationCount` | 210,000 | 10,000 | PBKDF2 iteration count |
| `wolfjce.wks.maxCertChainLength` | 100 | N/A | Max cert chain length |
| `wolfjce.wks.maxEntrySize` | 10485760 | N/A | Max encoded entry size in bytes |
| `wolfjce.keystore.kekCacheEnabled` | false | N/A | Enable KEK caching |
| `wolfjce.keystore.kekCacheTtlSec` | 300 | 1 | Cache TTL in seconds |

View File

@ -21,6 +21,8 @@ extern "C" {
#define com_wolfssl_provider_jce_WolfSSLKeyStore_WKS_HMAC_KEY_LENGTH 64L
#undef com_wolfssl_provider_jce_WolfSSLKeyStore_WKS_DEFAULT_MAX_CHAIN_COUNT
#define com_wolfssl_provider_jce_WolfSSLKeyStore_WKS_DEFAULT_MAX_CHAIN_COUNT 100L
#undef com_wolfssl_provider_jce_WolfSSLKeyStore_WKS_DEFAULT_MAX_ENTRY_SIZE
#define com_wolfssl_provider_jce_WolfSSLKeyStore_WKS_DEFAULT_MAX_ENTRY_SIZE 10485760L
#undef com_wolfssl_provider_jce_WolfSSLKeyStore_WKS_MAGIC_NUMBER
#define com_wolfssl_provider_jce_WolfSSLKeyStore_WKS_MAGIC_NUMBER 7L
#undef com_wolfssl_provider_jce_WolfSSLKeyStore_WKS_STORE_VERSION

View File

@ -224,6 +224,11 @@ public class WolfSSLKeyStore extends KeyStoreSpi {
private static final int WKS_DEFAULT_MAX_CHAIN_COUNT = 100;
private static final int WKS_MAX_CHAIN_COUNT;
/* Max encoded entry size in bytes, configurable via
* 'wolfjce.wks.maxEntrySize' Security property */
private static final int WKS_DEFAULT_MAX_ENTRY_SIZE = 10 * 1024 * 1024;
private static final int WKS_MAX_ENTRY_SIZE;
/* WKS magic number, used when storing KeyStore to OutputStream */
private static final int WKS_MAGIC_NUMBER = 7;
@ -341,8 +346,10 @@ public class WolfSSLKeyStore extends KeyStoreSpi {
static {
int iCount = WKS_PBKDF2_DEFAULT_ITERATIONS;
int cLength = WKS_DEFAULT_MAX_CHAIN_COUNT;
int eLength = WKS_DEFAULT_MAX_ENTRY_SIZE;
String iterations = null;
String chainCount = null;
String entrySize = null;
/* Set PBKDF2 iteration count, using default or one set by
* user in 'wolfjce.wks.iterationCount' Security property in
@ -389,6 +396,27 @@ public class WolfSSLKeyStore extends KeyStoreSpi {
log("setting max cert chain length: " + cLength);
WKS_MAX_CHAIN_COUNT = cLength;
/* Set max encoded entry size limit, using default or one set with
* `wolfjce.wks.maxEntrySize` Security property */
entrySize = Security.getProperty("wolfjce.wks.maxEntrySize");
if (entrySize != null && !entrySize.isEmpty()) {
try {
eLength = Integer.parseInt(entrySize);
if (eLength <= 0) {
log("wolfjce.wks.maxEntrySize (" + eLength +
") lower than 0, using default");
eLength = WKS_DEFAULT_MAX_ENTRY_SIZE;
}
} catch (NumberFormatException e) {
/* Error parsing property, fall back to default */
log("error parsing wolfjce.wks.maxEntrySize property, " +
"using default instead");
}
}
log("setting max entry size: " + eLength);
WKS_MAX_ENTRY_SIZE = eLength;
}
/**
@ -2385,6 +2413,12 @@ public class WolfSSLKeyStore extends KeyStoreSpi {
throw new IOException("Invalid encoded length, negative");
}
if (encodedLen > WKS_MAX_ENTRY_SIZE) {
throw new IOException("Encoded entry length (" +
encodedLen + ") is larger than max allowed: " +
WKS_MAX_ENTRY_SIZE);
}
/* encoded entry */
encodedEntry = new byte[encodedLen];
bytesRead = dis.read(encodedEntry);
@ -2444,8 +2478,10 @@ public class WolfSSLKeyStore extends KeyStoreSpi {
/* HMAC len and HMAC */
hmacLen = dis.readInt();
if (hmacLen < 0) {
throw new IOException("Invalid HMAC length, negative");
if (hmacLen != WKS_HMAC_KEY_LENGTH) {
throw new IOException(
"HMAC length (" + hmacLen + ") is different than " +
"expected (" + WKS_HMAC_KEY_LENGTH + ")");
}
hmac = new byte[hmacLen];
hmacLen = dis.read(hmac);

View File

@ -45,6 +45,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.nio.charset.StandardCharsets;
@ -401,6 +402,62 @@ public class WolfSSLKeyStoreTest {
}
}
/**
* A crafted WKS stream with an oversized encoded entry length must be
* rejected with an IOException, not trigger an unbounded allocation.
* Uses a null password so the HMAC integrity check is skipped.
*/
@Test
public void testEngineLoadRejectsOversizedEntry() throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeInt(7); /* WKS magic number */
dos.writeInt(1); /* WKS store version */
dos.writeInt(1); /* entry count */
dos.writeInt(2); /* entry type: certificate */
dos.writeUTF("evil"); /* alias */
dos.writeInt(Integer.MAX_VALUE); /* encoded entry length */
dos.flush();
KeyStore store = KeyStore.getInstance("WKS", "wolfJCE");
try {
store.load(new ByteArrayInputStream(bos.toByteArray()), null);
fail("oversized encoded entry length should throw IOException");
} catch (IOException e) {
/* expected, allocation must be bounded before it happens */
}
}
/**
* A crafted WKS stream with an HMAC length that is not the expected
* HMAC-SHA512 size must be rejected with an IOException, not trigger an
* unbounded allocation. Uses a null password so the HMAC integrity
* check is skipped.
*/
@Test
public void testEngineLoadRejectsInvalidHmacLength() throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeInt(7); /* WKS magic number */
dos.writeInt(1); /* WKS store version */
dos.writeInt(0); /* entry count */
dos.writeInt(16); /* salt length */
dos.write(new byte[16]); /* salt */
dos.writeInt(10000); /* PBKDF2 iterations */
dos.writeInt(Integer.MAX_VALUE); /* HMAC length */
dos.flush();
KeyStore store = KeyStore.getInstance("WKS", "wolfJCE");
try {
store.load(new ByteArrayInputStream(bos.toByteArray()), null);
fail("invalid HMAC length should throw IOException");
} catch (IOException e) {
/* expected, allocation must be bounded before it happens */
}
}
/**
* Create PrivateKey and Certificate objects based on files.