refactor(noise/protocol): migrate AESGCMFallbackCipherState to Kotlin

- Override CipherState properties; initialize `val` fields inline.
- Rewrite the big-endian 4-byte CTR-counter increment at iv[12..15]
  as an explicit `(iv[i] + 1).toByte()` chain — Kotlin rejects the
  `++(iv[15])` form on Byte array elements.
- Replace `Arrays.fill(arr, 0.toByte())` with `arr.fill(0)` and fix
  the GHASH-tag XOR loop to cast back to Byte explicitly.
- Rijndael CTR keystream and GHASH paths preserved byte-for-byte.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
yet 2026-05-15 17:59:39 +04:00
parent e0a390996f
commit 6f8fcf8022

View File

@ -25,7 +25,6 @@ import com.bitchat.android.noise.southernstorm.crypto.GHASH
import com.bitchat.android.noise.southernstorm.crypto.RijndaelAES
import com.bitchat.android.noise.southernstorm.protocol.Noise.destroy
import com.bitchat.android.noise.southernstorm.protocol.Noise.throwBadTagException
import java.util.Arrays
import javax.crypto.BadPaddingException
import javax.crypto.ShortBufferException
@ -34,26 +33,13 @@ import javax.crypto.ShortBufferException
* the JCA/JCE does not have a suitable GCM or CTR provider.
*/
internal class AESGCMFallbackCipherState : CipherState {
private val aes: RijndaelAES
private var n: Long
private val iv: ByteArray
private val enciv: ByteArray
private val hashKey: ByteArray
private val ghash: GHASH
private var haskey: Boolean
/**
* Constructs a new cipher state for the "AESGCM" algorithm.
*/
init {
aes = RijndaelAES()
n = 0
iv = ByteArray(16)
enciv = ByteArray(16)
hashKey = ByteArray(16)
ghash = GHASH()
haskey = false
}
private val aes: RijndaelAES = RijndaelAES()
private var n: Long = 0
private val iv: ByteArray = ByteArray(16)
private val enciv: ByteArray = ByteArray(16)
private val hashKey: ByteArray = ByteArray(16)
private val ghash: GHASH = GHASH()
private var haskey: Boolean = false
override fun destroy() {
aes.destroy()
@ -63,14 +49,9 @@ internal class AESGCMFallbackCipherState : CipherState {
destroy(enciv)
}
val cipherName: String
get() = "AESGCM"
val keyLength: Int
get() = 32
val mACLength: Int
get() = if (haskey) 16 else 0
override val cipherName: String get() = "AESGCM"
override val keyLength: Int get() = 32
override val macLength: Int get() = if (haskey) 16 else 0
override fun initializeKey(key: ByteArray, offset: Int) {
// Set up the AES key.
@ -78,7 +59,7 @@ internal class AESGCMFallbackCipherState : CipherState {
haskey = true
// Generate the hashing key by encrypting a block of zeroes.
Arrays.fill(hashKey, 0.toByte())
hashKey.fill(0)
aes.encrypt(hashKey, 0, hashKey, 0)
ghash.reset(hashKey, 0)
@ -123,7 +104,7 @@ internal class AESGCMFallbackCipherState : CipherState {
// Encrypt a block of zeroes to generate the hash key to XOR
// the GHASH tag with at the end of the encrypt/decrypt operation.
Arrays.fill(hashKey, 0.toByte())
hashKey.fill(0)
aes.encrypt(iv, 0, hashKey, 0)
@ -157,8 +138,18 @@ internal class AESGCMFallbackCipherState : CipherState {
var ciphertextOffset = ciphertextOffset
var length = length
while (length > 0) {
// Increment the IV and encrypt it to get the next keystream block.
if ((++(iv[15])).toInt() == 0) if ((++(iv[14])).toInt() == 0) if ((++(iv[13])).toInt() == 0) ++(iv[12])
// Increment the IV (big-endian 4-byte counter at iv[12..15])
// and encrypt it to get the next keystream block.
iv[15] = (iv[15] + 1).toByte()
if (iv[15].toInt() == 0) {
iv[14] = (iv[14] + 1).toByte()
if (iv[14].toInt() == 0) {
iv[13] = (iv[13] + 1).toByte()
if (iv[13].toInt() == 0) {
iv[12] = (iv[12] + 1).toByte()
}
}
}
aes.encrypt(iv, 0, enciv, 0)
@ -203,8 +194,10 @@ internal class AESGCMFallbackCipherState : CipherState {
ghash.update(ciphertext, ciphertextOffset, length)
ghash.pad((if (ad != null) ad.size else 0).toLong(), length.toLong())
ghash.finish(ciphertext, ciphertextOffset + length, 16)
for (index in 0..15) ciphertext[ciphertextOffset + length + index] =
ciphertext[ciphertextOffset + length + index].toInt() xor hashKey[index].toInt()
for (index in 0..15) {
ciphertext[ciphertextOffset + length + index] =
(ciphertext[ciphertextOffset + length + index].toInt() xor hashKey[index].toInt()).toByte()
}
return length + 16
}