diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMFallbackCipherState.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMFallbackCipherState.kt index fff41baa..e1357ea9 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMFallbackCipherState.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMFallbackCipherState.kt @@ -172,14 +172,13 @@ internal class AESGCMFallbackCipherState : CipherState { ad: ByteArray?, plaintext: ByteArray, plaintextOffset: Int, ciphertext: ByteArray, ciphertextOffset: Int, length: Int ): Int { - val space: Int require(!(ciphertextOffset < 0 || ciphertextOffset > ciphertext.size)) require(!(length < 0 || plaintextOffset < 0 || plaintextOffset > plaintext.size || length > plaintext.size || (plaintext.size - plaintextOffset) < length)) - space = ciphertext.size - ciphertextOffset + val space: Int = ciphertext.size - ciphertextOffset if (!haskey) { // The key is not set yet - return the plaintext as-is. if (length > space) throw ShortBufferException() - if (plaintext != ciphertext || plaintextOffset != ciphertextOffset) System.arraycopy( + if (plaintext !== ciphertext || plaintextOffset != ciphertextOffset) System.arraycopy( plaintext, plaintextOffset, ciphertext, @@ -219,7 +218,7 @@ internal class AESGCMFallbackCipherState : CipherState { if (!haskey) { // The key is not set yet - return the ciphertext as-is. if (length > space) throw ShortBufferException() - if (plaintext != ciphertext || plaintextOffset != ciphertextOffset) System.arraycopy( + if (plaintext !== ciphertext || plaintextOffset != ciphertextOffset) System.arraycopy( ciphertext, ciphertextOffset, plaintext, @@ -244,8 +243,7 @@ internal class AESGCMFallbackCipherState : CipherState { } override fun fork(key: ByteArray, offset: Int): CipherState { - val cipher: CipherState - cipher = AESGCMFallbackCipherState() + val cipher: CipherState = AESGCMFallbackCipherState() cipher.initializeKey(key, offset) return cipher } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMOnCtrCipherState.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMOnCtrCipherState.kt index d68b5d17..1a7175e3 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMOnCtrCipherState.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMOnCtrCipherState.kt @@ -45,7 +45,12 @@ import javax.crypto.spec.SecretKeySpec internal class AESGCMOnCtrCipherState @Throws(NoSuchAlgorithmException::class) constructor() : CipherState { - private val cipher: Cipher + private val cipher: Cipher = try { + Cipher.getInstance("AES/CTR/NoPadding") + } catch (e: NoSuchPaddingException) { + // AES/CTR is available, but not the unpadded version? Huh? + throw NoSuchAlgorithmException("AES/CTR/NoPadding not available", e) + } private var keySpec: SecretKeySpec? = null private var n: Long = 0 private val iv: ByteArray = ByteArray(16) @@ -53,13 +58,6 @@ constructor() : CipherState { private val ghash: GHASH = GHASH() init { - cipher = try { - Cipher.getInstance("AES/CTR/NoPadding") - } catch (e: NoSuchPaddingException) { - // AES/CTR is available, but not the unpadded version? Huh? - throw NoSuchAlgorithmException("AES/CTR/NoPadding not available", e) - } - // Try to set a 256-bit key on the cipher. Some JCE's are // configured to disallow 256-bit AES if an extra policy // file has not been installed. @@ -85,9 +83,9 @@ constructor() : CipherState { val params = IvParameterSpec(iv) try { cipher.init(Cipher.ENCRYPT_MODE, keySpec, params) - } catch (e: InvalidKeyException) { + } catch (_: InvalidKeyException) { // Shouldn't happen. - } catch (e: InvalidAlgorithmParameterException) { + } catch (_: InvalidAlgorithmParameterException) { // Shouldn't happen. } } @@ -196,14 +194,13 @@ constructor() : CipherState { ad: ByteArray?, plaintext: ByteArray, plaintextOffset: Int, ciphertext: ByteArray, ciphertextOffset: Int, length: Int ): Int { - val space: Int require(!(ciphertextOffset < 0 || ciphertextOffset > ciphertext.size)) require(!(length < 0 || plaintextOffset < 0 || plaintextOffset > plaintext.size || length > plaintext.size || (plaintext.size - plaintextOffset) < length)) - space = ciphertext.size - ciphertextOffset + val space: Int = ciphertext.size - ciphertextOffset if (keySpec == null) { // The key is not set yet - return the plaintext as-is. if (length > space) throw ShortBufferException() - if (plaintext != ciphertext || plaintextOffset != ciphertextOffset) System.arraycopy( + if (plaintext !== ciphertext || plaintextOffset != ciphertextOffset) System.arraycopy( plaintext, plaintextOffset, ciphertext, @@ -259,7 +256,7 @@ constructor() : CipherState { if (keySpec == null) { // The key is not set yet - return the ciphertext as-is. if (length > space) throw ShortBufferException() - if (plaintext != ciphertext || plaintextOffset != ciphertextOffset) System.arraycopy( + if (plaintext !== ciphertext || plaintextOffset != ciphertextOffset) System.arraycopy( ciphertext, ciphertextOffset, plaintext, @@ -281,7 +278,7 @@ constructor() : CipherState { throw IllegalStateException(e) } ghash.update(ciphertext, ciphertextOffset, dataLen) - ghash.pad((if (ad != null) ad.size else 0).toLong(), dataLen.toLong()) + ghash.pad((ad?.size ?: 0).toLong(), dataLen.toLong()) ghash.finish(iv, 0, 16) var temp = 0 for (index in 0..15) temp = diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/ChaChaPolyCipherState.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/ChaChaPolyCipherState.kt index 10e64d94..9962b5e6 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/ChaChaPolyCipherState.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/ChaChaPolyCipherState.kt @@ -89,7 +89,7 @@ internal class ChaChaPolyCipherState : CipherState { */ private fun finish(ad: ByteArray?, length: Int) { poly.pad() - putLittleEndian64(polyKey, 0, (if (ad != null) ad.size else 0).toLong()) + putLittleEndian64(polyKey, 0, (ad?.size ?: 0).toLong()) putLittleEndian64(polyKey, 8, length.toLong()) poly.update(polyKey, 0, 16) poly.finish(polyKey, 0) @@ -128,14 +128,13 @@ internal class ChaChaPolyCipherState : CipherState { ad: ByteArray?, plaintext: ByteArray, plaintextOffset: Int, ciphertext: ByteArray, ciphertextOffset: Int, length: Int ): Int { - val space: Int require(!(ciphertextOffset < 0 || ciphertextOffset > ciphertext.size)) require(!(length < 0 || plaintextOffset < 0 || plaintextOffset > plaintext.size || length > plaintext.size || (plaintext.size - plaintextOffset) < length)) - space = ciphertext.size - ciphertextOffset + val space: Int = ciphertext.size - ciphertextOffset if (!haskey) { // The key is not set yet - return the plaintext as-is. if (length > space) throw ShortBufferException() - if (plaintext != ciphertext || plaintextOffset != ciphertextOffset) System.arraycopy( + if (plaintext !== ciphertext || plaintextOffset != ciphertextOffset) System.arraycopy( plaintext, plaintextOffset, ciphertext, @@ -162,16 +161,15 @@ internal class ChaChaPolyCipherState : CipherState { plaintextOffset: Int, length: Int ): Int { - var space: Int require(!(ciphertextOffset < 0 || ciphertextOffset > ciphertext.size)) - space = ciphertext.size - ciphertextOffset + var space: Int = ciphertext.size - ciphertextOffset if (length > space) throw ShortBufferException() require(!(length < 0 || plaintextOffset < 0 || plaintextOffset > plaintext.size || length > ciphertext.size || (ciphertext.size - ciphertextOffset) < length)) space = plaintext.size - plaintextOffset if (!haskey) { // The key is not set yet - return the ciphertext as-is. if (length > space) throw ShortBufferException() - if (plaintext != ciphertext || plaintextOffset != ciphertextOffset) System.arraycopy( + if (plaintext !== ciphertext || plaintextOffset != ciphertextOffset) System.arraycopy( ciphertext, ciphertextOffset, plaintext, @@ -241,21 +239,25 @@ internal class ChaChaPolyCipherState : CipherState { outputOffset += 4 length -= 4 } - if (length == 3) { - value = block[posn] - output[outputOffset] = (input[inputOffset].toInt() xor value).toByte() - output[outputOffset + 1] = - (input[inputOffset + 1].toInt() xor (value shr 8)).toByte() - output[outputOffset + 2] = - (input[inputOffset + 2].toInt() xor (value shr 16)).toByte() - } else if (length == 2) { - value = block[posn] - output[outputOffset] = (input[inputOffset].toInt() xor value).toByte() - output[outputOffset + 1] = - (input[inputOffset + 1].toInt() xor (value shr 8)).toByte() - } else if (length == 1) { - value = block[posn] - output[outputOffset] = (input[inputOffset].toInt() xor value).toByte() + when (length) { + 3 -> { + value = block[posn] + output[outputOffset] = (input[inputOffset].toInt() xor value).toByte() + output[outputOffset + 1] = + (input[inputOffset + 1].toInt() xor (value shr 8)).toByte() + output[outputOffset + 2] = + (input[inputOffset + 2].toInt() xor (value shr 16)).toByte() + } + 2 -> { + value = block[posn] + output[outputOffset] = (input[inputOffset].toInt() xor value).toByte() + output[outputOffset + 1] = + (input[inputOffset + 1].toInt() xor (value shr 8)).toByte() + } + 1 -> { + value = block[posn] + output[outputOffset] = (input[inputOffset].toInt() xor value).toByte() + } } } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve25519DHState.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve25519DHState.kt index 573ff5f9..0f0582a5 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve25519DHState.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve25519DHState.kt @@ -49,21 +49,21 @@ internal class Curve25519DHState : DHState { } override fun getPublicKey(key: ByteArray, offset: Int) { - System.arraycopy(publicKey, 0, key, offset, 32) + publicKey.copyInto(key, offset, 0, 32) } override fun setPublicKey(key: ByteArray, offset: Int) { - System.arraycopy(key, offset, publicKey, 0, 32) + key.copyInto(publicKey, 0, offset, offset + 32) privateKey.fill(0) mode = 0x01 } override fun getPrivateKey(key: ByteArray, offset: Int) { - System.arraycopy(privateKey, 0, key, offset, 32) + privateKey.copyInto(key, offset, 0, 32) } override fun setPrivateKey(key: ByteArray, offset: Int) { - System.arraycopy(key, offset, privateKey, 0, 32) + key.copyInto(privateKey, 0, offset, offset + 32) Curve25519.eval(publicKey, 0, privateKey, null) mode = 0x03 } @@ -99,8 +99,8 @@ internal class Curve25519DHState : DHState { override fun copyFrom(other: DHState) { check(other is Curve25519DHState) { "Mismatched DH key objects" } if (other === this) return - System.arraycopy(other.privateKey, 0, privateKey, 0, 32) - System.arraycopy(other.publicKey, 0, publicKey, 0, 32) + other.privateKey.copyInto(privateKey, 0, 0, 32) + other.publicKey.copyInto(publicKey, 0, 0, 32) mode = other.mode } } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve448DHState.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve448DHState.kt index c322973f..b61d13a6 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve448DHState.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve448DHState.kt @@ -49,21 +49,21 @@ internal class Curve448DHState : DHState { } override fun getPublicKey(key: ByteArray, offset: Int) { - System.arraycopy(publicKey, 0, key, offset, 56) + publicKey.copyInto(key, offset, 0, 56) } override fun setPublicKey(key: ByteArray, offset: Int) { - System.arraycopy(key, offset, publicKey, 0, 56) + key.copyInto(publicKey, 0, offset, offset + 56) privateKey.fill(0) mode = 0x01 } override fun getPrivateKey(key: ByteArray, offset: Int) { - System.arraycopy(privateKey, 0, key, offset, 56) + privateKey.copyInto(key, offset, 0, 56) } override fun setPrivateKey(key: ByteArray, offset: Int) { - System.arraycopy(key, offset, privateKey, 0, 56) + key.copyInto(privateKey, 0, offset, offset + 56) Curve448.eval(publicKey, 0, privateKey, null) mode = 0x03 } @@ -99,8 +99,8 @@ internal class Curve448DHState : DHState { override fun copyFrom(other: DHState) { check(other is Curve448DHState) { "Mismatched DH key objects" } if (other === this) return - System.arraycopy(other.privateKey, 0, privateKey, 0, 56) - System.arraycopy(other.publicKey, 0, publicKey, 0, 56) + other.privateKey.copyInto(privateKey, 0, 0, 56) + other.publicKey.copyInto(publicKey, 0, 0, 56) mode = other.mode } } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/HandshakeState.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/HandshakeState.kt index a0dc7cfd..80317871 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/HandshakeState.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/HandshakeState.kt @@ -28,7 +28,6 @@ import com.bitchat.android.noise.southernstorm.protocol.Noise.destroy import com.bitchat.android.noise.southernstorm.protocol.Pattern.lookup import com.bitchat.android.noise.southernstorm.protocol.Pattern.reverseFlags import java.security.NoSuchAlgorithmException -import java.util.Arrays import javax.crypto.BadPaddingException import javax.crypto.ShortBufferException @@ -97,8 +96,8 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { * @return true if a pre-shared key is needed; false if not. */ fun needsPreSharedKey(): Boolean { - if (preSharedKey != null) return false - else return (requirements and PSK_REQUIRED) != 0 + return if (preSharedKey != null) false + else (requirements and PSK_REQUIRED) != 0 } /** @@ -168,8 +167,8 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { * will return false. */ fun needsLocalKeyPair(): Boolean { - if (localKeyPair != null) return !localKeyPair!!.hasPrivateKey() - else return false + return if (localKeyPair != null) !localKeyPair!!.hasPrivateKey() + else false } /** @@ -180,8 +179,8 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { * false if not. */ fun hasLocalKeyPair(): Boolean { - if (localKeyPair != null) return localKeyPair!!.hasPrivateKey() - else return false + return if (localKeyPair != null) localKeyPair!!.hasPrivateKey() + else false } /** @@ -193,8 +192,8 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { * will return false. */ fun needsRemotePublicKey(): Boolean { - if (remotePublicKey != null) return !remotePublicKey!!.hasPublicKey() - else return false + return if (remotePublicKey != null) !remotePublicKey!!.hasPublicKey() + else false } /** @@ -205,8 +204,8 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { * false if not. */ fun hasRemotePublicKey(): Boolean { - if (remotePublicKey != null) return remotePublicKey!!.hasPublicKey() - else return false + return if (remotePublicKey != null) remotePublicKey!!.hasPublicKey() + else false } val fixedEphemeralKey: DHState? @@ -227,7 +226,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { try { fixedEphemeral = createDH(localEphemeral!!.dhName) - } catch (e: NoSuchAlgorithmException) { + } catch (_: NoSuchAlgorithmException) { // This shouldn't happen - the local ephemeral key would // have already been created with the same name! fixedEphemeral = null @@ -253,7 +252,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { try { fixedHybrid = createDH(localHybrid!!.dhName) - } catch (e: NoSuchAlgorithmException) { + } catch (_: NoSuchAlgorithmException) { // This shouldn't happen - the local hybrid key would // have already been created with the same name! fixedHybrid = null @@ -320,21 +319,21 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { if ((flags.toInt() and Pattern.FLAG_LOCAL_EPHEMERAL.toInt()) != 0) localEphemeral = createDH(dh) if ((flags.toInt() and Pattern.FLAG_LOCAL_HYBRID.toInt()) != 0) localHybrid = - Noise.createDH(hybrid!!) + createDH(hybrid!!) if ((flags.toInt() and Pattern.FLAG_REMOTE_STATIC.toInt()) != 0) remotePublicKey = createDH(dh) if ((flags.toInt() and Pattern.FLAG_REMOTE_EPHEMERAL.toInt()) != 0) remoteEphemeral = createDH(dh) if ((flags.toInt() and Pattern.FLAG_REMOTE_HYBRID.toInt()) != 0) remoteHybrid = - Noise.createDH(hybrid!!) + createDH(hybrid!!) // We cannot use hybrid algorithms like New Hope for ephemeral or static keys, // as the unbalanced nature of the algorithm only works with "f" and "ff" tokens. - if (localKeyPair is DHStateHybrid) throw NoSuchAlgorithmException("Cannot use '" + localKeyPair!!.dhName + "' for static keys") - if (localEphemeral is DHStateHybrid) throw NoSuchAlgorithmException("Cannot use '" + localEphemeral!!.dhName + "' for ephemeral keys") - if (remotePublicKey is DHStateHybrid) throw NoSuchAlgorithmException("Cannot use '" + remotePublicKey!!.dhName + "' for static keys") - if (remoteEphemeral is DHStateHybrid) throw NoSuchAlgorithmException("Cannot use '" + remoteEphemeral!!.dhName + "' for ephemeral keys") + if (localKeyPair is DHStateHybrid) throw NoSuchAlgorithmException("Cannot use '${localKeyPair!!.dhName}' for static keys") + if (localEphemeral is DHStateHybrid) throw NoSuchAlgorithmException("Cannot use '${localEphemeral!!.dhName}' for ephemeral keys") + if (remotePublicKey is DHStateHybrid) throw NoSuchAlgorithmException("Cannot use '${remotePublicKey!!.dhName}' for static keys") + if (remoteEphemeral is DHStateHybrid) throw NoSuchAlgorithmException("Cannot use '${remoteEphemeral!!.dhName}' for ephemeral keys") } /** @@ -379,9 +378,9 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { // Log the symmetric state BEFORE any mixing operations (matching iOS) Log.d(TAG, "=== ANDROID HANDSHAKE START - INITIAL STATE ===") - Log.d(TAG, "Protocol: " + symmetric!!.protocolName) - Log.d(TAG, "Role: " + (if (isInitiator) "INITIATOR" else "RESPONDER")) - Log.d(TAG, "Initial symmetric hash: " + bytesToHex(symmetric!!.handshakeHash!!)) + Log.d(TAG, "Protocol: ${symmetric!!.protocolName}") + Log.d(TAG, "Role: ${if (isInitiator) "INITIATOR" else "RESPONDER"}") + Log.d(TAG, "Initial symmetric hash: ${bytesToHex(symmetric!!.handshakeHash!!)}") // Hash the prologue value. @@ -389,7 +388,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { val p = prologue if (p != null) symmetric!!.mixHash(p, 0, p.size) else symmetric!!.mixHash(emptyPrologue, 0, 0) - Log.d(TAG, "Hash after empty prologue: " + bytesToHex(symmetric!!.handshakeHash!!)) + Log.d(TAG, "Hash after empty prologue: ${bytesToHex(symmetric!!.handshakeHash!!)}") // Hash the pre-shared key into the chaining key and handshake hash. @@ -421,13 +420,12 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { Log.d(TAG, "=== ANDROID HANDSHAKE START - FINAL STATE ===") Log.d( TAG, - "Final symmetric hash after mixPreMessageKeys(): " + bytesToHex(symmetric!!.handshakeHash!!) + "Final symmetric hash after mixPreMessageKeys(): ${bytesToHex(symmetric!!.handshakeHash!!)}" ) Log.d(TAG, "===========================================") // The handshake has officially started - set the first action. - if (isInitiator) action = WRITE_MESSAGE - else action = READ_MESSAGE + action = if (isInitiator) WRITE_MESSAGE else READ_MESSAGE } /** @@ -579,7 +577,6 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { * @param remote Remote public key object. */ private fun mixDH(local: DHState, remote: DHState) { - check(!(local == null || remote == null)) { "Pattern definition error" } val len = local.sharedKeyLength val shared = ByteArray(len) try { @@ -750,7 +747,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { else -> { // Unknown token code. Abort. throw IllegalStateException( - "Unknown handshake token " + token.toInt().toString() + "Unknown handshake token ${token.toInt()}" ) } } @@ -777,7 +774,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { // If we failed, then clear any sensitive data that may have // already been written to the message buffer. if (!success) { - Arrays.fill(message, messageOffset, message.size - messageOffset, 0.toByte()) + message.fill(0, messageOffset, message.size - messageOffset) action = FAILED } } @@ -817,7 +814,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { payload: ByteArray, payloadOffset: Int ): Int { - var messageOffset = messageOffset + @Suppress("NAME_SHADOWING") var messageOffset = messageOffset var success = false val messageEnd = messageOffset + messageLength @@ -955,7 +952,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { else -> { // Unknown token code. Abort. throw IllegalStateException( - "Unknown handshake token " + token.toInt().toString() + "Unknown handshake token ${token.toInt()}" ) } } @@ -976,7 +973,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { // If we failed, then clear any sensitive data that may have // already been written to the payload buffer. if (!success) { - Arrays.fill(payload, payloadOffset, payload.size - payloadOffset, 0.toByte()) + payload.fill(0, payloadOffset, payload.size - payloadOffset) action = FAILED } } @@ -1143,7 +1140,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { private fun bytesToHex(bytes: ByteArray): String { val sb = StringBuilder() for (b in bytes) { - sb.append(String.format("%02x", b)) + sb.append("%02x".format(b)) } return sb.toString() } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/NewHopeDHState.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/NewHopeDHState.kt index 85e489ef..a5b9c586 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/NewHopeDHState.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/NewHopeDHState.kt @@ -48,7 +48,7 @@ internal class NewHopeDHState : DHStateHybrid { */ private inner class NewHopeWithPrivateKey(private val randomData: ByteArray) : NewHopeTor() { override fun randombytes(buffer: ByteArray) { - System.arraycopy(randomData, 0, buffer, 0, buffer.size) + randomData.copyInto(buffer, 0, 0, buffer.size) } } @@ -102,21 +102,19 @@ internal class NewHopeDHState : DHStateHybrid { override fun getPublicKey(key: ByteArray, offset: Int) { val pk = publicKey - if (pk != null) System.arraycopy(pk, 0, key, offset, publicKeyLength) + if (pk != null) pk.copyInto(key, offset, 0, publicKeyLength) else key.fill(0, offset, offset + publicKeyLength) } override fun setPublicKey(key: ByteArray, offset: Int) { publicKey?.let { Noise.destroy(it) } val len = publicKeyLength - val pk = ByteArray(len) - System.arraycopy(key, offset, pk, 0, len) - publicKey = pk + publicKey = key.copyOfRange(offset, offset + len) } override fun getPrivateKey(key: ByteArray, offset: Int) { val sk = privateKey - if (sk != null) System.arraycopy(sk, 0, key, offset, privateKeyLength) + if (sk != null) sk.copyInto(key, offset, 0, privateKeyLength) else key.fill(0, offset, offset + privateKeyLength) } @@ -125,9 +123,7 @@ internal class NewHopeDHState : DHStateHybrid { // Guess the key type from the length of the test data. keyType = if (offset == 0 && key.size == 64) KeyType.AlicePrivate else KeyType.BobPrivate val len = privateKeyLength - val sk = ByteArray(len) - System.arraycopy(key, offset, sk, 0, len) - privateKey = sk + privateKey = key.copyOfRange(offset, offset + len) } override fun setToNullPublicKey() { @@ -169,7 +165,7 @@ internal class NewHopeDHState : DHStateHybrid { } KeyType.BobCalculated -> { // The shared key for Bob was already computed when the key was generated. - System.arraycopy(privateKey!!, 0, sharedKey, 0, NewHope.SHAREDBYTES) + privateKey!!.copyInto(sharedKey, 0, 0, NewHope.SHAREDBYTES) } else -> throw IllegalStateException("Cannot calculate with this DH object") } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.kt index 4c2ac2de..4a8f8878 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.kt @@ -28,7 +28,6 @@ import com.bitchat.android.noise.southernstorm.protocol.Noise.destroy import java.io.UnsupportedEncodingException import java.security.DigestException import java.security.MessageDigest -import java.util.Arrays import javax.crypto.BadPaddingException import javax.crypto.ShortBufferException @@ -57,7 +56,7 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName */ var handshakeHash: ByteArray? private set - private var prev_h: ByteArray? + private var prevH: ByteArray? /** * Constructs a new symmetric state object. @@ -73,50 +72,38 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName this.protocolName = protocolName cipher = createCipher(cipherName) hash = createHash(hashName) - val hashLength = hash!!.getDigestLength() + val hashLength = hash!!.digestLength ck = ByteArray(hashLength) - this.handshakeHash = ByteArray(hashLength) - prev_h = ByteArray(hashLength) + val initialHash = ByteArray(hashLength) + this.handshakeHash = initialHash + prevH = ByteArray(hashLength) - val protocolNameBytes: ByteArray - try { - protocolNameBytes = protocolName.toByteArray(charset("UTF-8")) - } catch (e: UnsupportedEncodingException) { + val protocolNameBytes: ByteArray = try { + protocolName.toByteArray(charset("UTF-8")) + } catch (_: UnsupportedEncodingException) { // If UTF-8 is not supported, then we are definitely in trouble! throw UnsupportedOperationException("UTF-8 encoding is not supported") } if (protocolNameBytes.size <= hashLength) { - System.arraycopy( - protocolNameBytes, 0, - this.handshakeHash, 0, protocolNameBytes.size - ) - Arrays.fill( - this.handshakeHash, - protocolNameBytes.size, - handshakeHash!!.size, - 0.toByte() - ) + protocolNameBytes.copyInto(initialHash, 0, 0, protocolNameBytes.size) + initialHash.fill(0, protocolNameBytes.size, initialHash.size) } else { hashOne( protocolNameBytes, 0, protocolNameBytes.size, - this.handshakeHash!!, 0, handshakeHash!!.size + initialHash, 0, initialHash.size ) } - System.arraycopy(this.handshakeHash, 0, ck, 0, hashLength) + initialHash.copyInto(ck!!, 0, 0, hashLength) // LOGGING: Initial symmetric state after protocol name initialization (matching iOS) Log.d(TAG, "=== ANDROID SYMMETRIC STATE INITIALIZED ===") - Log.d(TAG, "Protocol: " + protocolName) - Log.d( - TAG, "Initial hash (h): " + Companion.bytesToHex( - this.handshakeHash!! - ) - ) - Log.d(TAG, "Initial chaining key (ck): " + Companion.bytesToHex(ck!!)) - Log.d(TAG, "Hash length: " + handshakeHash!!.size) + Log.d(TAG, "Protocol: $protocolName") + Log.d(TAG, "Initial hash (h): " + bytesToHex(initialHash)) + Log.d(TAG, "Initial chaining key (ck): " + bytesToHex(ck!!)) + Log.d(TAG, "Hash length: " + initialHash.size) Log.d(TAG, "=========================================") } @@ -137,15 +124,11 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName fun mixKey(data: ByteArray, offset: Int, length: Int) { // LOGGING: Before mixKey operation (matching iOS) val inputData = ByteArray(length) - System.arraycopy(data, offset, inputData, 0, length) + data.copyInto(inputData, 0, offset, offset + length) Log.d(TAG, "*** Android mixKey() BEFORE ***") - Log.d(TAG, "Input data (" + length + " bytes): " + bytesToHex(inputData)) - Log.d(TAG, "Current CK: " + Companion.bytesToHex(ck!!)) - Log.d( - TAG, "Current Hash: " + Companion.bytesToHex( - this.handshakeHash!! - ) - ) + Log.d(TAG, "Input data ($length bytes): " + bytesToHex(inputData)) + Log.d(TAG, "Current CK: " + bytesToHex(ck!!)) + Log.d(TAG, "Current Hash: " + bytesToHex(this.handshakeHash!!)) val keyLength = cipher!!.keyLength val tempKey = ByteArray(keyLength) @@ -172,12 +155,8 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName // LOGGING: After mixKey operation (matching iOS) Log.d(TAG, "*** Android mixKey() AFTER ***") - Log.d(TAG, "New CK: " + Companion.bytesToHex(ck!!)) - Log.d( - TAG, "Hash unchanged: " + Companion.bytesToHex( - this.handshakeHash!! - ) - ) + Log.d(TAG, "New CK: " + bytesToHex(ck!!)) + Log.d(TAG, "Hash unchanged: " + bytesToHex(this.handshakeHash!!)) Log.d(TAG, "Cipher now has key: " + (cipher!!.macLength > 0)) } @@ -191,28 +170,21 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName fun mixHash(data: ByteArray, offset: Int, length: Int) { // LOGGING: Before mixHash operation (matching iOS) val inputData = ByteArray(length) - System.arraycopy(data, offset, inputData, 0, length) + data.copyInto(inputData, 0, offset, offset + length) Log.d(TAG, "*** Android mixHash() BEFORE ***") - Log.d(TAG, "Input data (" + length + " bytes): " + bytesToHex(inputData)) - Log.d( - TAG, "Current Hash: " + Companion.bytesToHex( - this.handshakeHash!! - ) - ) + Log.d(TAG, "Input data ($length bytes): " + bytesToHex(inputData)) + Log.d(TAG, "Current Hash: " + bytesToHex(this.handshakeHash!!)) + val h = handshakeHash!! hashTwo( - this.handshakeHash!!, 0, handshakeHash!!.size, data, offset, length, - this.handshakeHash!!, 0, handshakeHash!!.size + h, 0, h.size, data, offset, length, + h, 0, h.size ) // LOGGING: After mixHash operation (matching iOS) Log.d(TAG, "*** Android mixHash() AFTER ***") - Log.d( - TAG, "New Hash: " + Companion.bytesToHex( - this.handshakeHash!! - ) - ) + Log.d(TAG, "New Hash: " + bytesToHex(this.handshakeHash!!)) } /** @@ -221,7 +193,7 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName * @param key The pre-shared key value. */ fun mixPreSharedKey(key: ByteArray) { - val temp = ByteArray(hash!!.getDigestLength()) + val temp = ByteArray(hash!!.digestLength) try { hkdf(ck!!, 0, ck!!.size, key, 0, key.size, ck!!, 0, ck!!.size, temp, 0, temp.size) mixHash(temp, 0, temp.size) @@ -333,10 +305,11 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName plaintextOffset: Int, length: Int ): Int { - System.arraycopy(this.handshakeHash, 0, prev_h, 0, handshakeHash!!.size) + val h = handshakeHash!! + h.copyInto(prevH!!, 0, 0, h.size) mixHash(ciphertext, ciphertextOffset, length) return cipher!!.decryptWithAd( - prev_h, + prevH, ciphertext, ciphertextOffset, plaintext, @@ -348,20 +321,15 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName /** * Splits the symmetric state into two ciphers for session encryption, * and optionally mixes in a secondary symmetric key. - * + * * @param secondaryKey The buffer containing the secondary key. * @param offset The offset of the first secondary key byte. * @param length The length of the secondary key in bytes, which * must be either 0 or 32. * @return The pair of ciphers for sending and receiving. - * + * * @throws IllegalArgumentException The length is not 0 or 32. */ - /** - * Splits the symmetric state into two ciphers for session encryption. - * - * @return The pair of ciphers for sending and receiving. - */ @JvmOverloads fun split( secondaryKey: ByteArray? = ByteArray(0), @@ -388,8 +356,8 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName if (c1 == null || c2 == null || pair == null) { // Could not create some of the objects. Clean up the others // to avoid accidental leakage of k1 or k2. - if (c1 != null) c1.destroy() - if (c2 != null) c2.destroy() + c1?.destroy() + c2?.destroy() pair = null } } @@ -420,9 +388,9 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName Noise.destroy(this.handshakeHash!!) this.handshakeHash = null } - if (prev_h != null) { - Noise.destroy(prev_h!!) - prev_h = null + if (prevH != null) { + Noise.destroy(prevH!!) + prevH = null } } @@ -450,8 +418,8 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName hash!!.update(data, offset, length) try { hash!!.digest(output, outputOffset, outputLength) - } catch (e: DigestException) { - Arrays.fill(output, outputOffset, outputLength, 0.toByte()) + } catch (_: DigestException) { + output.fill(0, outputOffset, outputLength) } } @@ -480,8 +448,8 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName hash!!.update(data2, offset2, length2) try { hash!!.digest(output, outputOffset, outputLength) - } catch (e: DigestException) { - Arrays.fill(output, outputOffset, outputLength, 0.toByte()) + } catch (_: DigestException) { + output.fill(0, outputOffset, outputLength) } } @@ -505,19 +473,18 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName ) { // In all of the algorithms of interest to us, the block length // is twice the size of the hash length. - val hashLength = hash!!.getDigestLength() + val hashLength = hash!!.digestLength val blockLength = hashLength * 2 val block = ByteArray(blockLength) - var index: Int try { if (keyLength <= blockLength) { System.arraycopy(key, keyOffset, block, 0, keyLength) - Arrays.fill(block, keyLength, blockLength, 0.toByte()) + block.fill(0, keyLength, blockLength) } else { hash!!.reset() hash!!.update(key, keyOffset, keyLength) hash!!.digest(block, 0, hashLength) - Arrays.fill(block, hashLength, blockLength, 0.toByte()) + block.fill(0, hashLength, blockLength) } for (i in 0 until blockLength) { block[i] = (block[i].toInt() xor 0x36).toByte() @@ -533,8 +500,8 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName hash!!.update(block, 0, blockLength) hash!!.update(output, outputOffset, hashLength) hash!!.digest(output, outputOffset, outputLength) - } catch (e: DigestException) { - Arrays.fill(output, outputOffset, outputLength, 0.toByte()) + } catch (_: DigestException) { + output.fill(0, outputOffset, outputLength) } finally { destroy(block) } @@ -564,7 +531,7 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName output1: ByteArray, output1Offset: Int, output1Length: Int, output2: ByteArray, output2Offset: Int, output2Length: Int ) { - val hashLength = hash!!.getDigestLength() + val hashLength = hash!!.digestLength val tempKey = ByteArray(hashLength) val tempHash = ByteArray(hashLength + 1) try { @@ -590,7 +557,7 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName private fun bytesToHex(bytes: ByteArray): String { val sb = StringBuilder() for (b in bytes) { - sb.append(String.format("%02x", b)) + sb.append("%02x".format(b)) } return sb.toString() }