This is an automated email from the ASF dual-hosted git repository. twolf pushed a commit to branch dev_3.0 in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit 1693f478f3463510000a403cc2e1e272bb502b84 Author: Thomas Wolf <[email protected]> AuthorDate: Wed Oct 15 19:10:04 2025 +0200 ChaCha20: simplify initializing the Poly1305 Mac Just get a full block from ChaCha20 to increment the block counter instead of re-initializing the ChaCha20 cipher. This also has the advantage that getting the second 32 bytes overwrites the first 32 bytes and thus clears the Mac key. --- .../sshd/common/cipher/AbstractChaCha20Cipher.java | 2 ++ .../apache/sshd/common/cipher/ChaCha20Cipher.java | 1 - .../sshd/common/cipher/ChaCha20CipherFactory.java | 23 ++++++++++------------ 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/AbstractChaCha20Cipher.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/AbstractChaCha20Cipher.java index b7db12141..95409d1a3 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/cipher/AbstractChaCha20Cipher.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/AbstractChaCha20Cipher.java @@ -24,6 +24,8 @@ package org.apache.sshd.common.cipher; */ public abstract class AbstractChaCha20Cipher implements Cipher { + protected static final int BLOCK_BYTES = 64; + protected AbstractChaCha20Cipher() { super(); } diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/ChaCha20Cipher.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/ChaCha20Cipher.java index e4ebde8b6..9e170c2c9 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/cipher/ChaCha20Cipher.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/ChaCha20Cipher.java @@ -99,7 +99,6 @@ public class ChaCha20Cipher extends AbstractChaCha20Cipher { } protected static class ChaChaEngine { - private static final int BLOCK_BYTES = 64; private static final int BLOCK_INTS = BLOCK_BYTES / Integer.BYTES; private static final int KEY_OFFSET = 4; private static final int KEY_BYTES = 32; diff --git a/sshd-common/src/main/java11/org/apache/sshd/common/cipher/ChaCha20CipherFactory.java b/sshd-common/src/main/java11/org/apache/sshd/common/cipher/ChaCha20CipherFactory.java index d7a3eb6c5..dfffc2b33 100644 --- a/sshd-common/src/main/java11/org/apache/sshd/common/cipher/ChaCha20CipherFactory.java +++ b/sshd-common/src/main/java11/org/apache/sshd/common/cipher/ChaCha20CipherFactory.java @@ -131,7 +131,7 @@ public final class ChaCha20CipherFactory implements Supplier<Cipher> { AlgorithmParameterSpec algorithmParameterSpec = new ChaCha20ParameterSpec(nonce, 0); k1 = new SecretKeySpec(Arrays.copyOfRange(key, 0, 32), "ChaCha20"); bodyEngine.init(mode == Mode.Encrypt ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, k1, algorithmParameterSpec); - mac.init(computePolyMacKey()); + init(mac, bodyEngine); k2 = new SecretKeySpec(Arrays.copyOfRange(key, 32, 64), "ChaCha20"); headerEngine.init(mode == Mode.Encrypt ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, k2, algorithmParameterSpec); @@ -179,23 +179,20 @@ public final class ChaCha20CipherFactory implements Supplier<Cipher> { BufferUtils.putUInt(counter, nonce, 8, 4); AlgorithmParameterSpec algorithmParameterSpec = new ChaCha20ParameterSpec(nonce, 0); bodyEngine.init(mode == Mode.Encrypt ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, k1, algorithmParameterSpec); - mac.init(computePolyMacKey()); + init(mac, bodyEngine); headerEngine.init(mode == Mode.Encrypt ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, k2, algorithmParameterSpec); } - private byte[] computePolyMacKey() throws GeneralSecurityException { + private void init(Mac mac, javax.crypto.Cipher engine) throws Exception { + // Getting a full block from ChaCha20 increments its block counter (from 0 to 1), so the cipher + // is set up correctly as a side-effect. The extra bytes gotten are simply discarded. + // + // Note that AbstractChaCha20Cipher.BLOCK_BYTES == 2 * Poly1305Mac.KEY_BYTES. byte[] block = new byte[Poly1305Mac.KEY_BYTES]; - bodyEngine.update(block, 0, block.length, block); - // JDK does not like re-initialization with same key and nonce, even if the counter is different. - // But it only checks the latest nonce. So trick it: - nonce[0] ^= 1; - AlgorithmParameterSpec next = new ChaCha20ParameterSpec(nonce, 1); - bodyEngine.init(mode == Mode.Encrypt ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, k1, next); - nonce[0] ^= 1; - next = new ChaCha20ParameterSpec(nonce, 1); - bodyEngine.init(mode == Mode.Encrypt ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, k1, next); - return block; + engine.update(block, 0, block.length, block); + mac.init(block); + engine.update(block, 0, block.length, block); } }
