Skip to content

Commit f46b942

Browse files
committed
BCFKS: derive scrypt keys with the encoded parallelization parameter rather than the block size, retrying the previous convention on load so a store written by an earlier release still opens, and writing p equal to r by default under org.bouncycastle.bcfks.scrypt_p_eq_r so a store written here is read by both
1 parent 0a65fb6 commit f46b942

6 files changed

Lines changed: 370 additions & 62 deletions

File tree

‎core/src/main/java/org/bouncycastle/util/Properties.java‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,29 @@ public class Properties
319319
*/
320320
public static final String BCFKS_STORE_IT_COUNT = "org.bouncycastle.bcfks.store_it_count";
321321

322+
/**
323+
* Write BCFKS scrypt parameters with the parallelization parameter p set equal to the block
324+
* size r, whatever p the caller's {@code ScryptConfig} asked for. Default true.
325+
* <p>
326+
* Releases up to 1.86 derived a BCFKS scrypt key with r in place of p while encoding the
327+
* configured p, so a store whose p differed from its r encoded parameters that did not derive
328+
* its own keys: a conformant RFC 7914 reader failed it, and BC failed a conformant writer's.
329+
* Derivation now follows RFC 7914, and with this property set the written p is made to agree
330+
* with what those releases derive, so a store written here is both RFC 7914 correct and
331+
* readable by 1.86 and earlier. Clearing it honours the configured p instead, at the cost of
332+
* a store that those releases cannot read unless p already equals r.
333+
* <p>
334+
* Reading is unaffected either way - a store written by an earlier release is recognised and
335+
* read on its own terms, and the encoded parallelization parameter is what a store written
336+
* here is derived with whichever way this is set.
337+
* <p>
338+
* This is transitional: the default is intended to become false once enough of the installed
339+
* base is writing parameters that describe themselves, at which point a configured p is
340+
* honoured as written. Callers who need that now, and who do not need 1.86 and earlier to read
341+
* what they write, can clear it already. Read via {@link #isOverrideSet(String, boolean)}.
342+
*/
343+
public static final String BCFKS_SCRYPT_P_EQ_R = "org.bouncycastle.bcfks.scrypt_p_eq_r";
344+
322345
/**
323346
* Upper bound on the PBKDF2 iteration count honoured when BC takes that count from an
324347
* untrusted encoding: decrypting a PBES2-protected PKCS#8 / PEM private key or PKCS#12

‎core/src/main/jdk1.4/org/bouncycastle/util/Properties.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public class Properties
5252
public static final String BCFKS_MAX_IT_COUNT = "org.bouncycastle.bcfks.max_it_count";
5353
public static final String BCFKS_MAX_SCRYPT_MEMORY = "org.bouncycastle.bcfks.max_scrypt_memory";
5454
public static final String BCFKS_STORE_IT_COUNT = "org.bouncycastle.bcfks.store_it_count";
55+
public static final String BCFKS_SCRYPT_P_EQ_R = "org.bouncycastle.bcfks.scrypt_p_eq_r";
5556
public static final String PBE_MAX_ITERATION_COUNT = "org.bouncycastle.pbe.max_iteration_count";
5657
public static final String PBE_MAX_SCRYPT_MEMORY = "org.bouncycastle.pbe.max_scrypt_memory";
5758
public static final String PKMAC_MAX_ITERATION_COUNT = "org.bouncycastle.pkmac.max_iteration_count";

‎docs/releasenotes.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +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.
2627

2728
### 2.1.3 Additional Features and Functionality
2829

0 commit comments

Comments
 (0)