From 62458dd4d1162fce1e27da617d2291edfc70ab1b Mon Sep 17 00:00:00 2001 From: Winfried Gerlach Date: Thu, 1 Oct 2026 15:12:08 +0200 Subject: [PATCH] #2476 Salsa20Engine.processBytes now XORs whole 64-byte keystream blocks in a plain loop instead of stepping a keystream index per byte, so Salsa20, XSalsa20, ChaCha and XChaCha20 run 1.3 to 1.75 times as fast Co-Authored-By: Claude Opus 5.5 --- .../crypto/engines/Salsa20Engine.java | 35 ++++++++++++++++++- docs/releasenotes.md | 2 ++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java index 913c4edca2..8cab2f2181 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java @@ -278,7 +278,40 @@ public int processBytes( throw new MaxBytesExceededException("2^70 byte limit per IV would be exceeded; Change IV"); } - for (int i = 0; i < len; i++) + int i = 0; + + /* The rest of a partly used block */ + for (; i < len && index != 0; i++) + { + out[i + outOff] = (byte)(keyStream[index] ^ in[i + inOff]); + index = (index + 1) & 63; + + if (index == 0) + { + advanceCounter(); + generateKeyStream(keyStream); + } + } + + /* + * Whole blocks, from the block boundary that leaves - or from i == len, where this does nothing - with no + * per-byte index arithmetic. Byte by byte and in order, as the other loops, so in and out may still overlap; + * and like them, the next block's keystream is generated as soon as one block is used up. + */ + for (; len - i >= 64; i += 64) + { + final int inPos = inOff + i, outPos = outOff + i; + for (int j = 0; j < 64; ++j) + { + out[outPos + j] = (byte)(keyStream[j] ^ in[inPos + j]); + } + + advanceCounter(); + generateKeyStream(keyStream); + } + + /* The start of the next block */ + for (; i < len; i++) { out[i + outOff] = (byte)(keyStream[index] ^ in[i + inOff]); index = (index + 1) & 63; diff --git a/docs/releasenotes.md b/docs/releasenotes.md index 3983bd59b6..8c550bbd1b 100644 --- a/docs/releasenotes.md +++ b/docs/releasenotes.md @@ -81,6 +81,8 @@ Date: 2026, TBD - The BCJSSE provider adds an org.bouncycastle.jsse.BCSSLContext interface exposing extended functionality of its SSLContext, obtained with org.bouncycastle.jsse.util.ContextUtil.getBCSSLContext() by way of the new BCSSLSessionContext interface the context's session contexts implement. Its getDefaultParameters(boolean) and getSupportedParameters(boolean) return the context's default and supported parameters as a BCSSLParameters for either client or server mode, including the BC-specific properties, where SSLContext.getDefaultSSLParameters() and getSupportedSSLParameters() report client mode only and cannot carry those properties. A BCSSLContext describes the initialization of the SSLContext it was obtained from, and is not updated if the SSLContext is re-initialized. +- Salsa20Engine - and with it ChaChaEngine, ChaCha7539Engine, XSalsa20Engine, XChaCha20Engine, ChaCha20-Poly1305 and the provider's Salsa20 and ChaCha ciphers - now XORs whole 64-byte blocks of keystream into the data in a plain loop, where every byte used to go through the keystream index, a mask and a block-boundary test. In a JMH comparison on an x86-64 machine ChaCha20 and Salsa20 ran 1.3 to 1.75 times as fast for 64 bytes to 16 KB on JDK 17, 21 and 25, and ChaCha20-Poly1305 encryption 1.3 to 1.4 times. The output is unchanged. + ### 2.1.4 Additional Notes - The sources and javadoc jars of the Ant-built distributions (jdk14, jdk15to18 and jdk13) no longer carry test material. Each module's javadoc target copies the package documentation it needs - org/bouncycastle//**/*.html - back into the module source directory that has already been compiled from, and zip-src zips that directory afterwards, so every test package's package.html arrived in the sources jar by that route; javadoc-util additionally copied org/bouncycastle/asn1/isismtt/**/*.java, which put test classes into the bcutil javadoc as generated pages, and javadoc-pg deliberately copied the gpg and bcpg test sources in order to document them. Separately the source copies excluded test material only one directory deep and only for *.java, because Ant reads ** as an any-depth wildcard just where it is a whole path segment, so anything nested further or with another extension - the PEM certificate fixtures under org/bouncycastle/est/test/san corrected in 1.86, and an ICAO master list under org/bouncycastle/asn1/icao/test - went through. The source and javadoc copies of every module now exclude test directories at any depth, and javadoc-pg no longer documents the test packages. org.bouncycastle.util.test is unaffected and still ships in the bcprov binary, sources and javadoc jars, as it does from the Gradle build: it is the SimpleTest framework the light-weight API's own test classes are written against, not test material of the distribution. No binary changes - the classes and resources of every Ant-built jar are identical to those of the 1.86 release - and the Gradle-built jdk18on artifacts never carried any of this.