Skip to content

Commit 0bd9d2b

Browse files
committed
BCFKS: accept an encoded scrypt parallelization parameter equal to either the configured one or the block size when a load store parameter's ScryptConfig is checked against an existing store, so a store round trips under the configuration that wrote it whichever way org.bouncycastle.bcfks.scrypt_p_eq_r was set
1 parent f46b942 commit 0bd9d2b

3 files changed

Lines changed: 32 additions & 3 deletions

File tree

‎docs/releasenotes.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Date: 2026, TBD
2323

2424
- The name-constraint host canonicalisation removed a single RFC 1034 root-label dot, the only empty label a name may legally carry, but nothing refused the ones that are not legal: a dNSName, rfc822Name host or uniformResourceIdentifier host such as "example.com.." kept a phantom empty label after the strip and so matched no constraint at all, escaping an excluded subtree naming the host it appears to carry. A tested name whose host carries an empty label - a second trailing dot, a doubled dot or a leading dot - is now refused outright wherever a constraint of that type is in force, rather than canonicalised into a name it is not: removing the extra dots would decide on the caller's behalf that "example.com.." names example.com, which is not how a consumer resolving or comparing the name reads it, and refusing fails closed in both directions where canonicalising would newly admit such a name under a permitted subtree. The single trailing dot is canonicalised as before, a bare "." remains the root label rather than an empty one, and the guard is scoped to the host, so the doubled dot a quoted local part may legally carry is unaffected. Constraints are untouched - one may still begin with a dot, which is how this implementation spells "subdomains only" (github PR #2436).
2525
- SSLContext.createSSLEngine() from the BCJSSE provider in the 1.86 bctls jar failed with NoSuchMethodError on every JDK from 9 up, leaving engine-based users of the provider (Netty, Vert.x and the like) unable to open a connection. The jdk1.5 and jdk1.9 copies of the package-private SSLEngineUtil had declared create(ContextData) with different return types since 2019 - SSLEngine and ProvSSLEngine - and the root ProvSSLContextSpi, compiled against the first, is paired at runtime with the versions/9 copy on any modern JDK. Until 1.86 the java9 compile had hidden this by implicitly recompiling the whole base tree into META-INF/versions/9 (447 classes, ProvSSLContextSpi among them); the -implicit:none added in 1.86 to stop that duplication exposed the mismatch. The jdk1.9 copy now declares the same return type as the root one, a JDK 25 test creates engines against the built jar, and a new multiReleaseCheck Gradle task on every distributed jar reads the constant pool of each class in the jar and in the sibling BC jars it depends on and fails the build when a member reference does not resolve against the copy of its target that a JDK would pair it with, so the class of defect cannot ship again; the same check runs on arbitrary jars, a published release included, as multiReleaseCheckJar (github #2448).
26-
- The BCFKS key store derived its scrypt keys with the block size r in place of the parallelization parameter p, while writing the p the caller configured out to the store: BcFKSKeyStoreSpi passed getBlockSize() to SCrypt.generate for both arguments and never read the encoded parallelization parameter at all, so every store whose ScryptConfig gave a p other than its r encoded parameters that do not derive its own keys. The store was self-consistent - BC read back what BC wrote - but a conformant RFC 7914 reader computed a different key and so failed the integrity check and the store decryption, and BC could not open such a store written by anyone else. Derivation now follows RFC 7914. A store written by 1.86 or earlier is still read: the integrity check is retried under the old convention, and where a signature check leaves no MAC to settle it the store decryption is retried instead, in both cases reporting the failure of the encoded parameters rather than of the fallback. The write side is governed by org.bouncycastle.bcfks.scrypt_p_eq_r, default true, which writes p equal to r whatever the ScryptConfig asked for: the two conventions then agree, so a store written here is both RFC 7914 correct and readable by 1.86 and earlier. Clearing the property honours the configured p, which those releases cannot read unless p already equals r; the default is intended to become false in a later release, once enough of the installed base is writing parameters that describe themselves. The parallelization parameter is now bounded alongside the cost parameter before the derivation, as the PKCS#8 and PKCS#12 scrypt paths already bound it.
26+
- The BCFKS key store derived its scrypt keys with the block size r in place of the parallelization parameter p, while writing the p the caller configured out to the store: BcFKSKeyStoreSpi passed getBlockSize() to SCrypt.generate for both arguments and never read the encoded parallelization parameter at all, so every store whose ScryptConfig gave a p other than its r encoded parameters that do not derive its own keys. The store was self-consistent - BC read back what BC wrote - but a conformant RFC 7914 reader computed a different key and so failed the integrity check and the store decryption, and BC could not open such a store written by anyone else. Derivation now follows RFC 7914. A store written by 1.86 or earlier is still read: the integrity check is retried under the old convention, and where a signature check leaves no MAC to settle it the store decryption is retried instead, in both cases reporting the failure of the encoded parameters rather than of the fallback. The write side is governed by org.bouncycastle.bcfks.scrypt_p_eq_r, default true, which writes p equal to r whatever the ScryptConfig asked for: the two conventions then agree, so a store written here is both RFC 7914 correct and readable by 1.86 and earlier. Clearing the property honours the configured p, which those releases cannot read unless p already equals r; the default is intended to become false in a later release, once enough of the installed base is writing parameters that describe themselves. Loading with a BCFKSLoadStoreParameter carrying a ScryptConfig accepts an encoded p equal to either the configured p or the block size, so a store round trips under the configuration that wrote it whichever way the property was set; every other parameter is compared as before. The parallelization parameter is now bounded alongside the cost parameter before the derivation, as the PKCS#8 and PKCS#12 scrypt paths already bound it.
2727

2828
### 2.1.3 Additional Features and Functionality
2929

‎prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/bcfks/BcFKSKeyStoreSpi.java‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1375,10 +1375,14 @@ private boolean isSimilarHmacPbkd(PBKDFConfig storePBKDFConfig, KeyDerivationFun
13751375
ScryptConfig scryptConfig = (ScryptConfig)storePBKDFConfig;
13761376
ScryptParams sParams = ScryptParams.getInstance(hmacPkbdAlgorithm.getParameters());
13771377

1378+
int parallelization = sParams.getParallelizationParameter().intValue();
1379+
13781380
if (scryptConfig.getSaltLength() != sParams.getSalt().length
13791381
|| scryptConfig.getBlockSize() != sParams.getBlockSize().intValue()
13801382
|| scryptConfig.getCostParameter() != sParams.getCostParameter().intValue()
1381-
|| scryptConfig.getParallelizationParameter() != sParams.getParallelizationParameter().intValue())
1383+
// a store written with p equal to r carries the block size whatever p was configured - see Properties.BCFKS_SCRYPT_P_EQ_R.
1384+
|| (parallelization != scryptConfig.getParallelizationParameter()
1385+
&& parallelization != sParams.getBlockSize().intValue()))
13821386
{
13831387
return false;
13841388
}

‎prov/src/test/java/org/bouncycastle/jce/provider/test/BCFKSStoreTest.java‎

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1493,6 +1493,8 @@ private void shouldWriteConformantScryptParallelization()
14931493
private void checkScryptParallelization(int expectedP)
14941494
throws Exception
14951495
{
1496+
ScryptConfig config = new ScryptConfig.Builder(1024, 8, 1).withSaltLength(20).build();
1497+
14961498
KeyStore store1 = KeyStore.getInstance("BCFKS", "BC");
14971499

14981500
store1.load(null, null);
@@ -1501,7 +1503,7 @@ private void checkScryptParallelization(int expectedP)
15011503
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
15021504

15031505
store1.store(new BCFKSLoadStoreParameter.Builder(bOut, testPassword)
1504-
.withStorePBKDFConfig(new ScryptConfig.Builder(1024, 8, 1).withSaltLength(20).build()).build());
1506+
.withStorePBKDFConfig(config).build());
15051507

15061508
byte[] enc = bOut.toByteArray();
15071509

@@ -1531,6 +1533,29 @@ private void checkScryptParallelization(int expectedP)
15311533
store2.load(new ByteArrayInputStream(enc), testPassword);
15321534

15331535
checkScryptStoreEntry(store2);
1536+
1537+
// the store has to come back under the configuration that wrote it, whatever p was encoded.
1538+
KeyStore store3 = KeyStore.getInstance("BCFKS", "BC");
1539+
1540+
store3.load(new BCFKSLoadStoreParameter.Builder(new ByteArrayInputStream(enc), testPassword)
1541+
.withStorePBKDFConfig(config).build());
1542+
1543+
checkScryptStoreEntry(store3);
1544+
1545+
// a configuration that differs other than in p is still refused.
1546+
try
1547+
{
1548+
KeyStore store4 = KeyStore.getInstance("BCFKS", "BC");
1549+
1550+
store4.load(new BCFKSLoadStoreParameter.Builder(new ByteArrayInputStream(enc), testPassword)
1551+
.withStorePBKDFConfig(new ScryptConfig.Builder(2048, 8, 1).withSaltLength(20).build()).build());
1552+
1553+
fail("mismatched scrypt configuration accepted");
1554+
}
1555+
catch (IOException e)
1556+
{
1557+
isEquals("configuration parameters do not match existing store", e.getMessage());
1558+
}
15341559
}
15351560

15361561
private byte[] recalculateMac(PbkdMacIntegrityCheck integrityCheck, byte[] content, ScryptParams params, int p)

0 commit comments

Comments
 (0)