From e8882d6ab11cf961afda76e1f546584b41c8679f Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:36:07 +0400 Subject: [PATCH] Convert CipherState to Kotlin --- .../southernstorm/protocol/CipherState.kt | 267 ++++++++++-------- .../protocol/SymmetricState.java | 8 +- 2 files changed, 146 insertions(+), 129 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherState.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherState.kt index 95796740..67efd831 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherState.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherState.kt @@ -19,144 +19,161 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ +package com.bitchat.android.noise.southernstorm.protocol -package com.bitchat.android.noise.southernstorm.protocol; - -import javax.crypto.BadPaddingException; -import javax.crypto.ShortBufferException; +import javax.crypto.BadPaddingException +import javax.crypto.ShortBufferException /** * Interface to an authenticated cipher for use in the Noise protocol. - * + * * CipherState objects are used to encrypt or decrypt data during a * session. Once the handshake has completed, HandshakeState.split() * will create two CipherState objects for encrypting packets sent to * the other party, and decrypting packets received from the other party. */ -public interface CipherState extends Destroyable { +interface CipherState : Destroyable { + /** + * Gets the Noise protocol name for this cipher. + * + * @return The cipher name. + */ + val cipherName: String - /** - * Gets the Noise protocol name for this cipher. - * - * @return The cipher name. - */ - String getCipherName(); + /** + * Gets the length of the key values for this cipher. + * + * @return The length of the key in bytes; usually 32. + */ + val keyLength: Int - /** - * Gets the length of the key values for this cipher. - * - * @return The length of the key in bytes; usually 32. - */ - int getKeyLength(); - - /** - * Gets the length of the MAC values for this cipher. - * - * @return The length of MAC values in bytes, or zero if the - * key has not yet been initialized. - */ - int getMACLength(); + /** + * Gets the length of the MAC values for this cipher. + * + * @return The length of MAC values in bytes, or zero if the + * key has not yet been initialized. + */ + // JvmName preserved so existing Java impls/callers (getMACLength) keep + // resolving — Kotlin would otherwise expose this as getMacLength. + @get:JvmName("getMACLength") + val macLength: Int - /** - * Initializes the key on this cipher object. - * - * @param key Points to a buffer that contains the key. - * @param offset The offset of the key in the key buffer. - * - * The key buffer must contain at least getKeyLength() bytes - * starting at offset. - * - * @see #hasKey() - */ - void initializeKey(byte[] key, int offset); + /** + * Initializes the key on this cipher object. + * + * @param key Points to a buffer that contains the key. + * @param offset The offset of the key in the key buffer. + * + * The key buffer must contain at least getKeyLength() bytes + * starting at offset. + * + * @see .hasKey + */ + fun initializeKey(key: ByteArray, offset: Int) - /** - * Determine if this cipher object has been configured with a key. - * - * @return true if this cipher object has a key; false if the - * key has not yet been set with initializeKey(). - * - * @see #initializeKey(byte[], int) - */ - boolean hasKey(); - - /** - * Encrypts a plaintext buffer using the cipher and a block of associated data. - * - * @param ad The associated data, or null if there is none. - * @param plaintext The buffer containing the plaintext to encrypt. - * @param plaintextOffset The offset within the plaintext buffer of the - * first byte or plaintext data. - * @param ciphertext The buffer to place the ciphertext in. This can - * be the same as the plaintext buffer. - * @param ciphertextOffset The first offset within the ciphertext buffer - * to place the ciphertext and the MAC tag. - * @param length The length of the plaintext. - * @return The length of the ciphertext plus the MAC tag, or -1 if the - * ciphertext buffer is not large enough to hold the result. - * - * @throws ShortBufferException The ciphertext buffer does not have - * enough space to hold the ciphertext plus MAC. - * - * @throws IllegalStateException The nonce has wrapped around. - * - * @throws IllegalArgumentException One of the parameters is out of range. - * - * The plaintext and ciphertext buffers can be the same for in-place - * encryption. In that case, plaintextOffset must be identical to - * ciphertextOffset. - * - * There must be enough space in the ciphertext buffer to accomodate - * length + getMACLength() bytes of data starting at ciphertextOffset. - */ - int encryptWithAd(byte[] ad, byte[] plaintext, int plaintextOffset, byte[] ciphertext, int ciphertextOffset, int length) throws ShortBufferException; + /** + * Determine if this cipher object has been configured with a key. + * + * @return true if this cipher object has a key; false if the + * key has not yet been set with initializeKey(). + * + * @see .initializeKey + */ + fun hasKey(): Boolean - /** - * Decrypts a ciphertext buffer using the cipher and a block of associated data. - * - * @param ad The associated data, or null if there is none. - * @param ciphertext The buffer containing the ciphertext to decrypt. - * @param ciphertextOffset The offset within the ciphertext buffer of - * the first byte of ciphertext data. - * @param plaintext The buffer to place the plaintext in. This can be - * the same as the ciphertext buffer. - * @param plaintextOffset The first offset within the plaintext buffer - * to place the plaintext. - * @param length The length of the incoming ciphertext plus the MAC tag. - * @return The length of the plaintext with the MAC tag stripped off. - * - * @throws ShortBufferException The plaintext buffer does not have - * enough space to store the decrypted data. - * - * @throws BadPaddingException The MAC value failed to verify. - * - * @throws IllegalStateException The nonce has wrapped around. - * - * @throws IllegalArgumentException One of the parameters is out of range. - * - * The plaintext and ciphertext buffers can be the same for in-place - * decryption. In that case, ciphertextOffset must be identical to - * plaintextOffset. - */ - int decryptWithAd(byte[] ad, byte[] ciphertext, int ciphertextOffset, byte[] plaintext, int plaintextOffset, int length) throws ShortBufferException, BadPaddingException; + /** + * Encrypts a plaintext buffer using the cipher and a block of associated data. + * + * @param ad The associated data, or null if there is none. + * @param plaintext The buffer containing the plaintext to encrypt. + * @param plaintextOffset The offset within the plaintext buffer of the + * first byte or plaintext data. + * @param ciphertext The buffer to place the ciphertext in. This can + * be the same as the plaintext buffer. + * @param ciphertextOffset The first offset within the ciphertext buffer + * to place the ciphertext and the MAC tag. + * @param length The length of the plaintext. + * @return The length of the ciphertext plus the MAC tag, or -1 if the + * ciphertext buffer is not large enough to hold the result. + * + * @throws ShortBufferException The ciphertext buffer does not have + * enough space to hold the ciphertext plus MAC. + * + * @throws IllegalStateException The nonce has wrapped around. + * + * @throws IllegalArgumentException One of the parameters is out of range. + * + * The plaintext and ciphertext buffers can be the same for in-place + * encryption. In that case, plaintextOffset must be identical to + * ciphertextOffset. + * + * There must be enough space in the ciphertext buffer to accomodate + * length + getMACLength() bytes of data starting at ciphertextOffset. + */ + @Throws(ShortBufferException::class) + fun encryptWithAd( + ad: ByteArray?, + plaintext: ByteArray, + plaintextOffset: Int, + ciphertext: ByteArray, + ciphertextOffset: Int, + length: Int + ): Int - /** - * Creates a new instance of this cipher and initializes it with a key. - * - * @param key The buffer containing the key. - * @param offset The offset into the key buffer of the first key byte. - * @return A new CipherState of the same class as this one. - */ - CipherState fork(byte[] key, int offset); - - /** - * Sets the nonce value. - * - * @param nonce The new nonce value, which must be greater than or equal - * to the current value. - * - * This function is intended for testing purposes only. If the nonce - * value goes backwards then security may be compromised. - */ - void setNonce(long nonce); + /** + * Decrypts a ciphertext buffer using the cipher and a block of associated data. + * + * @param ad The associated data, or null if there is none. + * @param ciphertext The buffer containing the ciphertext to decrypt. + * @param ciphertextOffset The offset within the ciphertext buffer of + * the first byte of ciphertext data. + * @param plaintext The buffer to place the plaintext in. This can be + * the same as the ciphertext buffer. + * @param plaintextOffset The first offset within the plaintext buffer + * to place the plaintext. + * @param length The length of the incoming ciphertext plus the MAC tag. + * @return The length of the plaintext with the MAC tag stripped off. + * + * @throws ShortBufferException The plaintext buffer does not have + * enough space to store the decrypted data. + * + * @throws BadPaddingException The MAC value failed to verify. + * + * @throws IllegalStateException The nonce has wrapped around. + * + * @throws IllegalArgumentException One of the parameters is out of range. + * + * The plaintext and ciphertext buffers can be the same for in-place + * decryption. In that case, ciphertextOffset must be identical to + * plaintextOffset. + */ + @Throws(ShortBufferException::class, BadPaddingException::class) + fun decryptWithAd( + ad: ByteArray?, + ciphertext: ByteArray, + ciphertextOffset: Int, + plaintext: ByteArray, + plaintextOffset: Int, + length: Int + ): Int + + /** + * Creates a new instance of this cipher and initializes it with a key. + * + * @param key The buffer containing the key. + * @param offset The offset into the key buffer of the first key byte. + * @return A new CipherState of the same class as this one. + */ + fun fork(key: ByteArray, offset: Int): CipherState + + /** + * Sets the nonce value. + * + * @param nonce The new nonce value, which must be greater than or equal + * to the current value. + * + * This function is intended for testing purposes only. If the nonce + * value goes backwards then security may be compromised. + */ + fun setNonce(nonce: Long) } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.java index 0e70f9f3..d669baa3 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.java +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.java @@ -122,7 +122,7 @@ class SymmetricState implements Destroyable { */ public int getMACLength() { - return cipher.getMACLength(); + return cipher.mACLength; } /** @@ -142,7 +142,7 @@ class SymmetricState implements Destroyable { Log.d(TAG, "Current CK: " + bytesToHex(ck)); Log.d(TAG, "Current Hash: " + bytesToHex(h)); - int keyLength = cipher.getKeyLength(); + int keyLength = cipher.keyLength; byte[] tempKey = new byte [keyLength]; try { hkdf(ck, 0, ck.length, data, offset, length, ck, 0, ck.length, tempKey, 0, keyLength); @@ -155,7 +155,7 @@ class SymmetricState implements Destroyable { Log.d(TAG, "*** Android mixKey() AFTER ***"); Log.d(TAG, "New CK: " + bytesToHex(ck)); Log.d(TAG, "Hash unchanged: " + bytesToHex(h)); - Log.d(TAG, "Cipher now has key: " + (cipher.getMACLength() > 0)); + Log.d(TAG, "Cipher now has key: " + (cipher.mACLength > 0)); } /** @@ -314,7 +314,7 @@ class SymmetricState implements Destroyable { { if (length != 0 && length != 32) throw new IllegalArgumentException("Secondary keys must be 0 or 32 bytes in length"); - int keyLength = cipher.getKeyLength(); + int keyLength = cipher.keyLength; byte[] k1 = new byte [keyLength]; byte[] k2 = new byte [keyLength]; try {