From 6ac2769f632f72373dd02c488b66395b8df738c2 Mon Sep 17 00:00:00 2001 From: yet300 Date: Fri, 8 May 2026 16:30:20 +0400 Subject: [PATCH 01/37] Migrate ChaChaCore, Poly1305, and Destroyable to Kotlin - Translated `ChaChaCore.java` and `Poly1305.java` to Kotlin. - Replaced JVM-specific dependencies (`java.util.Arrays`, `System.arraycopy`) with Kotlin Standard Library equivalents (`.fill()`, `.copyInto()`) - Refactored `while` loops into idiomatic Kotlin `for` loops. --- .../southernstorm/crypto/ChaChaCore.java | 306 +++++----- .../noise/southernstorm/crypto/Poly1305.java | 564 +++++++++--------- .../{Destroyable.java => Destroyable1.kt} | 13 +- 3 files changed, 427 insertions(+), 456 deletions(-) rename app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/{Destroyable.java => Destroyable1.kt} (89%) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/ChaChaCore.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/ChaChaCore.java index cadcb89f..afb87f5e 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/ChaChaCore.java +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/ChaChaCore.java @@ -19,182 +19,164 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ - -package com.bitchat.android.noise.southernstorm.crypto; +package com.bitchat.android.noise.southernstorm.crypto /** * Implementation of the ChaCha20 core hash transformation. */ -public final class ChaChaCore { +object ChaChaCore { + /** + * Hashes an input block with ChaCha20. + * + * @param output The output block, which must contain at least 16 + * elements and must not overlap with the input. + * @param input The input block, which must contain at least 16 + * elements. + */ + fun hash(output: IntArray, input: IntArray) { + // Copy the input to the output to start with. + input.copyInto(output, 0, 0, 16) - private ChaChaCore() {} - - /** - * Hashes an input block with ChaCha20. - * - * @param output The output block, which must contain at least 16 - * elements and must not overlap with the input. - * @param input The input block, which must contain at least 16 - * elements. - */ - public static void hash(int[] output, int[] input) - { - int index; - - // Copy the input to the output to start with. - for (index = 0; index < 16; ++index) - output[index] = input[index]; - - // Perform the 20 ChaCha rounds in groups of two. - for (index = 0; index < 20; index += 2) { - // Column round. - quarterRound(output, 0, 4, 8, 12); - quarterRound(output, 1, 5, 9, 13); - quarterRound(output, 2, 6, 10, 14); - quarterRound(output, 3, 7, 11, 15); + // Perform the 20 ChaCha rounds in groups of two. + for (index in 0 until 20 step 2) { + // Column round. + quarterRound(output, 0, 4, 8, 12) + quarterRound(output, 1, 5, 9, 13) + quarterRound(output, 2, 6, 10, 14) + quarterRound(output, 3, 7, 11, 15) - // Diagonal round. - quarterRound(output, 0, 5, 10, 15); - quarterRound(output, 1, 6, 11, 12); - quarterRound(output, 2, 7, 8, 13); - quarterRound(output, 3, 4, 9, 14); - } + // Diagonal round. + quarterRound(output, 0, 5, 10, 15) + quarterRound(output, 1, 6, 11, 12) + quarterRound(output, 2, 7, 8, 13) + quarterRound(output, 3, 4, 9, 14) + } - // Add the input block to the output. - for (index = 0; index < 16; ++index) - output[index] += input[index]; - } + // Add the input block to the output. + for (index in 0 until 16) { + output[index] += input[index] + } + } - private static int char4(char c1, char c2, char c3, char c4) - { - return (((int)c1) & 0xFF) | ((((int)c2) & 0xFF) << 8) | ((((int)c3) & 0xFF) << 16) | ((((int)c4) & 0xFF) << 24); - } + private fun char4(c1: Char, c2: Char, c3: Char, c4: Char): Int = + (c1.code and 0xFF) or + ((c2.code and 0xFF) shl 8) or + ((c3.code and 0xFF) shl 16) or + ((c4.code and 0xFF) shl 24) - private static int fromLittleEndian(byte[] key, int offset) - { - return (key[offset] & 0xFF) | ((key[offset + 1] & 0xFF) << 8) | ((key[offset + 2] & 0xFF) << 16) | ((key[offset + 3] & 0xFF) << 24); - } + private fun fromLittleEndian(key: ByteArray, offset: Int): Int = + (key[offset].toInt() and 0xFF) or + ((key[offset + 1].toInt() and 0xFF) shl 8) or + ((key[offset + 2].toInt() and 0xFF) shl 16) or + ((key[offset + 3].toInt() and 0xFF) shl 24) - /** - * Initializes a ChaCha20 block with a 128-bit key. - * - * @param output The output block, which must consist of at - * least 16 words. - * @param key The buffer containing the key. - * @param offset Offset of the key in the buffer. - */ - public static void initKey128(int[] output, byte[] key, int offset) - { - output[0] = char4('e', 'x', 'p', 'a'); - output[1] = char4('n', 'd', ' ', '1'); - output[2] = char4('6', '-', 'b', 'y'); - output[3] = char4('t', 'e', ' ', 'k'); - output[4] = fromLittleEndian(key, offset); - output[5] = fromLittleEndian(key, offset + 4); - output[6] = fromLittleEndian(key, offset + 8); - output[7] = fromLittleEndian(key, offset + 12); - output[8] = output[4]; - output[9] = output[5]; - output[10] = output[6]; - output[11] = output[7]; - output[12] = 0; - output[13] = 0; - output[14] = 0; - output[15] = 0; - } - /** - * Initializes a ChaCha20 block with a 256-bit key. - * - * @param output The output block, which must consist of at - * least 16 words. - * @param key The buffer containing the key. - * @param offset Offset of the key in the buffer. - */ - public static void initKey256(int[] output, byte[] key, int offset) - { - output[0] = char4('e', 'x', 'p', 'a'); - output[1] = char4('n', 'd', ' ', '3'); - output[2] = char4('2', '-', 'b', 'y'); - output[3] = char4('t', 'e', ' ', 'k'); - output[4] = fromLittleEndian(key, offset); - output[5] = fromLittleEndian(key, offset + 4); - output[6] = fromLittleEndian(key, offset + 8); - output[7] = fromLittleEndian(key, offset + 12); - output[8] = fromLittleEndian(key, offset + 16); - output[9] = fromLittleEndian(key, offset + 20); - output[10] = fromLittleEndian(key, offset + 24); - output[11] = fromLittleEndian(key, offset + 28); - output[12] = 0; - output[13] = 0; - output[14] = 0; - output[15] = 0; - } + /** + * Initializes a ChaCha20 block with a 128-bit key. + * + * @param output The output block, which must consist of at + * least 16 words. + * @param key The buffer containing the key. + * @param offset Offset of the key in the buffer. + */ + fun initKey128(output: IntArray, key: ByteArray, offset: Int) { + output[0] = char4('e', 'x', 'p', 'a') + output[1] = char4('n', 'd', ' ', '1') + output[2] = char4('6', '-', 'b', 'y') + output[3] = char4('t', 'e', ' ', 'k') + output[4] = fromLittleEndian(key, offset) + output[5] = fromLittleEndian(key, offset + 4) + output[6] = fromLittleEndian(key, offset + 8) + output[7] = fromLittleEndian(key, offset + 12) + output[8] = output[4] + output[9] = output[5] + output[10] = output[6] + output[11] = output[7] + output[12] = 0 + output[13] = 0 + output[14] = 0 + output[15] = 0 + } - /** - * Initializes the 64-bit initialization vector in a ChaCha20 block. - * - * @param output The output block, which must consist of at - * least 16 words and must have been initialized by initKey256() - * or initKey128(). - * @param iv The 64-bit initialization vector value. - * - * The counter portion of the output block is set to zero. - */ - public static void initIV(int[] output, long iv) - { - output[12] = 0; - output[13] = 0; - output[14] = (int)iv; - output[15] = (int)(iv >> 32); - } - - /** - * Initializes the 64-bit initialization vector and counter in a ChaCha20 block. - * - * @param output The output block, which must consist of at - * least 16 words and must have been initialized by initKey256() - * or initKey128(). - * @param iv The 64-bit initialization vector value. - * @param counter The 64-bit counter value. - */ - public static void initIV(int[] output, long iv, long counter) - { - output[12] = (int)counter; - output[13] = (int)(counter >> 32); - output[14] = (int)iv; - output[15] = (int)(iv >> 32); - } - - private static int leftRotate16(int v) - { - return v << 16 | (v >>> 16); - } + /** + * Initializes a ChaCha20 block with a 256-bit key. + * + * @param output The output block, which must consist of at + * least 16 words. + * @param key The buffer containing the key. + * @param offset Offset of the key in the buffer. + */ + fun initKey256(output: IntArray, key: ByteArray, offset: Int) { + output[0] = char4('e', 'x', 'p', 'a') + output[1] = char4('n', 'd', ' ', '3') + output[2] = char4('2', '-', 'b', 'y') + output[3] = char4('t', 'e', ' ', 'k') + output[4] = fromLittleEndian(key, offset) + output[5] = fromLittleEndian(key, offset + 4) + output[6] = fromLittleEndian(key, offset + 8) + output[7] = fromLittleEndian(key, offset + 12) + output[8] = fromLittleEndian(key, offset + 16) + output[9] = fromLittleEndian(key, offset + 20) + output[10] = fromLittleEndian(key, offset + 24) + output[11] = fromLittleEndian(key, offset + 28) + output[12] = 0 + output[13] = 0 + output[14] = 0 + output[15] = 0 + } - private static int leftRotate12(int v) - { - return v << 12 | (v >>> 20); - } + /** + * Initializes the 64-bit initialization vector in a ChaCha20 block. + * + * @param output The output block, which must consist of at + * least 16 words and must have been initialized by initKey256() + * or initKey128(). + * @param iv The 64-bit initialization vector value. + * + * The counter portion of the output block is set to zero. + */ + fun initIV(output: IntArray, iv: Long) { + output[12] = 0 + output[13] = 0 + output[14] = iv.toInt() + output[15] = (iv shr 32).toInt() + } - private static int leftRotate8(int v) - { - return v << 8 | (v >>> 24); - } + /** + * Initializes the 64-bit initialization vector and counter in a ChaCha20 block. + * + * @param output The output block, which must consist of at + * least 16 words and must have been initialized by initKey256() + * or initKey128(). + * @param iv The 64-bit initialization vector value. + * @param counter The 64-bit counter value. + */ + fun initIV(output: IntArray, iv: Long, counter: Long) { + output[12] = counter.toInt() + output[13] = (counter shr 32).toInt() + output[14] = iv.toInt() + output[15] = (iv shr 32).toInt() + } - private static int leftRotate7(int v) - { - return v << 7 | (v >>> 25); - } + private fun leftRotate16(v: Int): Int = (v shl 16) or (v ushr 16) - private static void quarterRound(int[] v, int a, int b, int c, int d) - { - v[a] += v[b]; - v[d] = leftRotate16(v[d] ^ v[a]); - v[c] += v[d]; - v[b] = leftRotate12(v[b] ^ v[c]); - v[a] += v[b]; - v[d] = leftRotate8(v[d] ^ v[a]); - v[c] += v[d]; - v[b] = leftRotate7(v[b] ^ v[c]); - } + private fun leftRotate12(v: Int): Int = (v shl 12) or (v ushr 20) + + + private fun leftRotate8(v: Int): Int = (v shl 8) or (v ushr 24) + + + private fun leftRotate7(v: Int): Int = (v shl 7) or (v ushr 25) + + + private fun quarterRound(v: IntArray, a: Int, b: Int, c: Int, d: Int) { + v[a] += v[b] + v[d] = leftRotate16(v[d] xor v[a]) + v[c] += v[d] + v[b] = leftRotate12(v[b] xor v[c]) + v[a] += v[b] + v[d] = leftRotate8(v[d] xor v[a]) + v[c] += v[d] + v[b] = leftRotate7(v[b] xor v[c]) + } } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Poly1305.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Poly1305.java index 820539f8..6f3d7f30 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Poly1305.java +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Poly1305.java @@ -19,309 +19,299 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ +package com.bitchat.android.noise.southernstorm.crypto -package com.bitchat.android.noise.southernstorm.crypto; +import com.bitchat.android.noise.southernstorm.protocol.Destroyable -import java.util.Arrays; - -import com.bitchat.android.noise.southernstorm.protocol.Destroyable; /** * Simple implementation of the Poly1305 message authenticator. */ -public final class Poly1305 implements Destroyable { +class Poly1305 : Destroyable { + // The 130-bit intermediate values are broken up into five 26-bit words. + private val nonce = ByteArray(16) + private val block = ByteArray(16) + private val h = IntArray(5) + private val r = IntArray(5) + private val c = IntArray(5) + private val t = LongArray(10) + private var posn = 0 - // The 130-bit intermediate values are broken up into five 26-bit words. - private byte[] nonce; - private byte[] block; - private int[] h; - private int[] r; - private int[] c; - private long[] t; - private int posn; - /** - * Constructs a new Poly1305 message authenticator. - */ - public Poly1305() - { - nonce = new byte [16]; - block = new byte [16]; - h = new int [5]; - r = new int [5]; - c = new int [5]; - t = new long [10]; - posn = 0; - } + /** + * Resets the message authenticator with a new key. + * + * @param key The buffer containing the 32 byte key. + * @param offset The offset into the buffer of the first key byte. + */ + fun reset(key: ByteArray, offset: Int) { - /** - * Resets the message authenticator with a new key. - * - * @param key The buffer containing the 32 byte key. - * @param offset The offset into the buffer of the first key byte. - */ - public void reset(byte[] key, int offset) - { - System.arraycopy(key, offset + 16, nonce, 0, 16); - Arrays.fill(h, 0); - posn = 0; - - // Convert the first 16 bytes of the key into a 130-bit - // "r" value while masking off the bits that we don't need. - r[0] = ((key[offset] & 0xFF)) | - ((key[offset + 1] & 0xFF) << 8) | - ((key[offset + 2] & 0xFF) << 16) | - ((key[offset + 3] & 0x03) << 24); - r[1] = ((key[offset + 3] & 0x0C) >> 2) | - ((key[offset + 4] & 0xFC) << 6) | - ((key[offset + 5] & 0xFF) << 14) | - ((key[offset + 6] & 0x0F) << 22); - r[2] = ((key[offset + 6] & 0xF0) >> 4) | - ((key[offset + 7] & 0x0F) << 4) | - ((key[offset + 8] & 0xFC) << 12) | - ((key[offset + 9] & 0x3F) << 20); - r[3] = ((key[offset + 9] & 0xC0) >> 6) | - ((key[offset + 10] & 0xFF) << 2) | - ((key[offset + 11] & 0x0F) << 10) | - ((key[offset + 12] & 0xFC) << 18); - r[4] = ((key[offset + 13] & 0xFF)) | - ((key[offset + 14] & 0xFF) << 8) | - ((key[offset + 15] & 0x0F) << 16); - } + // Kotlin KMP Array Copy + key.copyInto(destination = nonce, destinationOffset = 0, startIndex = offset + 16, endIndex = offset + 32) - /** - * Updates the message authenticator with more input data. - * - * @param data The buffer containing the input data. - * @param offset The offset of the first byte of input. - * @param length The number of bytes of input. - */ - public void update(byte[] data, int offset, int length) - { - while (length > 0) { - if (posn == 0 && length >= 16) { - // We can process the chunk directly out of the input buffer. - processChunk(data, offset, false); - offset += 16; - length -= 16; - } else { - // Collect up partial bytes in the block buffer. - int temp = 16 - posn; - if (temp > length) - temp = length; - System.arraycopy(data, offset, block, posn, temp); - offset += temp; - length -= temp; - posn += temp; - if (posn >= 16) { - processChunk(block, 0, false); - posn = 0; - } - } - } - } + h.fill(0) + posn = 0 - /** - * Pads the input with zeroes to a multiple of 16 bytes. - */ - public void pad() - { - if (posn != 0) { - Arrays.fill(block, posn, 16, (byte)0); - processChunk(block, 0, false); - posn = 0; - } - } - /** - * Finishes the message authenticator and returns the 16-byte token. - * - * @param token The buffer to receive the token. - * @param offset The offset of the token in the buffer. - */ - public void finish(byte[] token, int offset) - { - // Pad and flush the final chunk. - if (posn != 0) { - block[posn] = (byte)1; - Arrays.fill(block, posn + 1, 16, (byte)0); - processChunk(block, 0, true); - } - - // At this point, processChunk() has left h as a partially reduced - // result that is less than (2^130 - 5) * 6. Perform one more - // reduction and a trial subtraction to produce the final result. + // Convert the first 16 bytes of the key into a 130-bit + // "r" value while masking off the bits that we don't need. + r[0] = ((key[offset].toInt() and 0xFF)) or + ((key[offset + 1].toInt() and 0xFF) shl 8) or + ((key[offset + 2].toInt() and 0xFF) shl 16) or + ((key[offset + 3].toInt() and 0x03) shl 24) + r[1] = ((key[offset + 3].toInt() and 0x0C) shr 2) or + ((key[offset + 4].toInt() and 0xFC) shl 6) or + ((key[offset + 5].toInt() and 0xFF) shl 14) or + ((key[offset + 6].toInt() and 0x0F) shl 22) + r[2] = ((key[offset + 6].toInt() and 0xF0) shr 4) or + ((key[offset + 7].toInt() and 0x0F) shl 4) or + ((key[offset + 8].toInt() and 0xFC) shl 12) or + ((key[offset + 9].toInt() and 0x3F) shl 20) + r[3] = ((key[offset + 9].toInt() and 0xC0) shr 6) or + ((key[offset + 10].toInt() and 0xFF) shl 2) or + ((key[offset + 11].toInt() and 0x0F) shl 10) or + ((key[offset + 12].toInt() and 0xFC) shl 18) + r[4] = ((key[offset + 13].toInt() and 0xFF)) or + ((key[offset + 14].toInt() and 0xFF) shl 8) or + ((key[offset + 15].toInt() and 0x0F) shl 16) + } - // Multiply the high bits of h by 5 and add them to the 130 low bits. - int carry = (h[4] >> 26) * 5 + h[0]; - h[0] = carry & 0x03FFFFFF; - carry = (carry >> 26) + h[1]; - h[1] = carry & 0x03FFFFFF; - carry = (carry >> 26) + h[2]; - h[2] = carry & 0x03FFFFFF; - carry = (carry >> 26) + h[3]; - h[3] = carry & 0x03FFFFFF; - h[4] = (carry >> 26) + (h[4] & 0x03FFFFFF); + /** + * Updates the message authenticator with more input data. + * + * @param data The buffer containing the input data. + * @param offset The offset of the first byte of input. + * @param length The number of bytes of input. + */ + fun update(data: ByteArray, offset: Int, length: Int) { + var offset = offset + var length = length + while (length > 0) { + if (posn == 0 && length >= 16) { + // We can process the chunk directly out of the input buffer. + processChunk(data, offset, false) + offset += 16 + length -= 16 + } else { + // Collect up partial bytes in the block buffer. + var temp = 16 - posn + if (temp > length) temp = length + System.arraycopy(data, offset, block, posn, temp) + offset += temp + length -= temp + posn += temp + if (posn >= 16) { + processChunk(block, 0, false) + posn = 0 + } + } + } + } - // Subtract (2^130 - 5) from h by computing c = h + 5 - 2^130. - // The "minus 2^130" step is implicit. - carry = 5 + h[0]; - c[0] = carry & 0x03FFFFFF; - carry = (carry >> 26) + h[1]; - c[1] = carry & 0x03FFFFFF; - carry = (carry >> 26) + h[2]; - c[2] = carry & 0x03FFFFFF; - carry = (carry >> 26) + h[3]; - c[3] = carry & 0x03FFFFFF; - c[4] = (carry >> 26) + h[4]; + /** + * Pads the input with zeroes to a multiple of 16 bytes. + */ + fun pad() { + if (posn != 0) { + // Kotlin Array Fill + block.fill(0.toByte(), fromIndex = posn, toIndex = 16) + processChunk(block, 0, false) + posn = 0 + } + } - // Borrow occurs if bit 2^130 of the previous c result is zero. - // Carefully turn this into a selection mask so we can select either - // h or c as the final result. - int mask = -((c[4] >> 26) & 0x01); - int nmask = ~mask; - h[0] = (h[0] & nmask) | (c[0] & mask); - h[1] = (h[1] & nmask) | (c[1] & mask); - h[2] = (h[2] & nmask) | (c[2] & mask); - h[3] = (h[3] & nmask) | (c[3] & mask); - h[4] = (h[4] & nmask) | (c[4] & mask); - - // Convert h into little-endian in the block buffer. - block[0] = (byte)(h[0]); - block[1] = (byte)(h[0] >> 8); - block[2] = (byte)(h[0] >> 16); - block[3] = (byte)((h[0] >> 24) | (h[1] << 2)); - block[4] = (byte)(h[1] >> 6); - block[5] = (byte)(h[1] >> 14); - block[6] = (byte)((h[1] >> 22) | (h[2] << 4)); - block[7] = (byte)(h[2] >> 4); - block[8] = (byte)(h[2] >> 12); - block[9] = (byte)((h[2] >> 20) | (h[3] << 6)); - block[10] = (byte)(h[3] >> 2); - block[11] = (byte)(h[3] >> 10); - block[12] = (byte)(h[3] >> 18); - block[13] = (byte)(h[4]); - block[14] = (byte)(h[4] >> 8); - block[15] = (byte)(h[4] >> 16); - - // Add the nonce and write the final result to the token. - carry = (nonce[0] & 0xFF) + (block[0] & 0xFF); - token[offset] = (byte)carry; - for (int x = 1; x < 16; ++x) { - carry = (carry >> 8) + (nonce[x] & 0xFF) + (block[x] & 0xFF); - token[offset + x] = (byte)carry; - } - } + /** + * Finishes the message authenticator and returns the 16-byte token. + * + * @param token The buffer to receive the token. + * @param offset The offset of the token in the buffer. + */ + fun finish(token: ByteArray, offset: Int) { + // Pad and flush the final chunk. + if (posn != 0) { + block[posn] = 1.toByte() + block.fill(0.toByte(), fromIndex = posn + 1, toIndex = 16) + processChunk(block, 0, true) + } - /** - * Processes the next chunk of input data. - * - * @param chunk Buffer containing the input data chunk. - * @param offset Offset of the first byte of the 16-byte chunk. - * @param finalChunk Set to true if this is the final chunk. - */ - private void processChunk(byte[] chunk, int offset, boolean finalChunk) - { - int x; - - // Unpack the 128-bit chunk into a 130-bit value in "c". - c[0] = ((chunk[offset] & 0xFF)) | - ((chunk[offset + 1] & 0xFF) << 8) | - ((chunk[offset + 2] & 0xFF) << 16) | - ((chunk[offset + 3] & 0x03) << 24); - c[1] = ((chunk[offset + 3] & 0xFC) >> 2) | - ((chunk[offset + 4] & 0xFF) << 6) | - ((chunk[offset + 5] & 0xFF) << 14) | - ((chunk[offset + 6] & 0x0F) << 22); - c[2] = ((chunk[offset + 6] & 0xF0) >> 4) | - ((chunk[offset + 7] & 0xFF) << 4) | - ((chunk[offset + 8] & 0xFF) << 12) | - ((chunk[offset + 9] & 0x3F) << 20); - c[3] = ((chunk[offset + 9] & 0xC0) >> 6) | - ((chunk[offset + 10] & 0xFF) << 2) | - ((chunk[offset + 11] & 0xFF) << 10) | - ((chunk[offset + 12] & 0xFF) << 18); - c[4] = ((chunk[offset + 13] & 0xFF)) | - ((chunk[offset + 14] & 0xFF) << 8) | - ((chunk[offset + 15] & 0xFF) << 16); - if (!finalChunk) - c[4] |= (1 << 24); - - // Compute h = ((h + c) * r) mod (2^130 - 5) - - // Start with h += c. We assume that h is less than (2^130 - 5) * 6 - // and that c is less than 2^129, so the result will be less than 2^133. - h[0] += c[0]; - h[1] += c[1]; - h[2] += c[2]; - h[3] += c[3]; - h[4] += c[4]; - // Multiply h by r. We know that r is less than 2^124 because the - // top 4 bits were AND-ed off by reset(). That makes h * r less - // than 2^257. Which is less than the (2^130 - 6)^2 we want for - // the modulo reduction step that follows. The intermediate limbs - // are 52 bits in size, which allows us to collect up carries in the - // extra bits of the 64 bit longs and propagate them later. - long hv = h[0]; - t[0] = hv * r[0]; - t[1] = hv * r[1]; - t[2] = hv * r[2]; - t[3] = hv * r[3]; - t[4] = hv * r[4]; - for (x = 1; x < 5; ++x) { - hv = h[x]; - t[x] += hv * r[0]; - t[x + 1] += hv * r[1]; - t[x + 2] += hv * r[2]; - t[x + 3] += hv * r[3]; - t[x + 4] = hv * r[4]; - } - - // Propagate carries to convert the t limbs from 52-bit back to 26-bit. - // The low bits are placed into h and the high bits are placed into c. - h[0] = ((int)t[0]) & 0x03FFFFFF; - hv = t[1] + (t[0] >> 26); - h[1] = ((int)hv) & 0x03FFFFFF; - hv = t[2] + (hv >> 26); - h[2] = ((int)hv) & 0x03FFFFFF; - hv = t[3] + (hv >> 26); - h[3] = ((int)hv) & 0x03FFFFFF; - hv = t[4] + (hv >> 26); - h[4] = ((int)hv) & 0x03FFFFFF; - hv = t[5] + (hv >> 26); - c[0] = ((int)hv) & 0x03FFFFFF; - hv = t[6] + (hv >> 26); - c[1] = ((int)hv) & 0x03FFFFFF; - hv = t[7] + (hv >> 26); - c[2] = ((int)hv) & 0x03FFFFFF; - hv = t[8] + (hv >> 26); - c[3] = ((int)hv) & 0x03FFFFFF; - hv = t[9] + (hv >> 26); - c[4] = ((int)hv); - - // Reduce h * r modulo (2^130 - 5) by multiplying the high 130 bits by 5 - // and adding them to the low 130 bits. This will leave the result at - // most 5 subtractions away from the answer we want. - int carry = h[0] + c[0] * 5; - h[0] = carry & 0x03FFFFFF; - carry = (carry >> 26) + h[1] + c[1] * 5; - h[1] = carry & 0x03FFFFFF; - carry = (carry >> 26) + h[2] + c[2] * 5; - h[2] = carry & 0x03FFFFFF; - carry = (carry >> 26) + h[3] + c[3] * 5; - h[3] = carry & 0x03FFFFFF; - carry = (carry >> 26) + h[4] + c[4] * 5; - h[4] = carry; - } + // At this point, processChunk() has left h as a partially reduced + // result that is less than (2^130 - 5) * 6. Perform one more + // reduction and a trial subtraction to produce the final result. - @Override - public void destroy() { - Arrays.fill(nonce, (byte)0); - Arrays.fill(block, (byte)0); - Arrays.fill(h, (int)0); - Arrays.fill(r, (int)0); - Arrays.fill(c, (int)0); - Arrays.fill(t, (long)0); - } + // Multiply the high bits of h by 5 and add them to the 130 low bits. + var carry = (h[4] shr 26) * 5 + h[0] + h[0] = carry and 0x03FFFFFF + carry = (carry shr 26) + h[1] + h[1] = carry and 0x03FFFFFF + carry = (carry shr 26) + h[2] + h[2] = carry and 0x03FFFFFF + carry = (carry shr 26) + h[3] + h[3] = carry and 0x03FFFFFF + h[4] = (carry shr 26) + (h[4] and 0x03FFFFFF) + + // Subtract (2^130 - 5) from h by computing c = h + 5 - 2^130. + // The "minus 2^130" step is implicit. + carry = 5 + h[0] + c[0] = carry and 0x03FFFFFF + carry = (carry shr 26) + h[1] + c[1] = carry and 0x03FFFFFF + carry = (carry shr 26) + h[2] + c[2] = carry and 0x03FFFFFF + carry = (carry shr 26) + h[3] + c[3] = carry and 0x03FFFFFF + c[4] = (carry shr 26) + h[4] + + // Borrow occurs if bit 2^130 of the previous c result is zero. + // Carefully turn this into a selection mask so we can select either + // h or c as the final result. + val mask = -((c[4] shr 26) and 0x01) + val nmask = mask.inv() + h[0] = (h[0] and nmask) or (c[0] and mask) + h[1] = (h[1] and nmask) or (c[1] and mask) + h[2] = (h[2] and nmask) or (c[2] and mask) + h[3] = (h[3] and nmask) or (c[3] and mask) + h[4] = (h[4] and nmask) or (c[4] and mask) + + + // Convert h into little-endian in the block buffer. + block[0] = (h[0]).toByte() + block[1] = (h[0] shr 8).toByte() + block[2] = (h[0] shr 16).toByte() + block[3] = ((h[0] shr 24) or (h[1] shl 2)).toByte() + block[4] = (h[1] shr 6).toByte() + block[5] = (h[1] shr 14).toByte() + block[6] = ((h[1] shr 22) or (h[2] shl 4)).toByte() + block[7] = (h[2] shr 4).toByte() + block[8] = (h[2] shr 12).toByte() + block[9] = ((h[2] shr 20) or (h[3] shl 6)).toByte() + block[10] = (h[3] shr 2).toByte() + block[11] = (h[3] shr 10).toByte() + block[12] = (h[3] shr 18).toByte() + block[13] = (h[4]).toByte() + block[14] = (h[4] shr 8).toByte() + block[15] = (h[4] shr 16).toByte() + + + // Add the nonce and write the final result to the token. + carry = (nonce[0].toInt() and 0xFF) + (block[0].toInt() and 0xFF) + token[offset] = carry.toByte() + for (x in 1..15) { + carry = (carry shr 8) + (nonce[x].toInt() and 0xFF) + (block[x].toInt() and 0xFF) + token[offset + x] = carry.toByte() + } + } + + /** + * Processes the next chunk of input data. + * + * @param chunk Buffer containing the input data chunk. + * @param offset Offset of the first byte of the 16-byte chunk. + * @param finalChunk Set to true if this is the final chunk. + */ + private fun processChunk(chunk: ByteArray, offset: Int, finalChunk: Boolean) { + + // Unpack the 128-bit chunk into a 130-bit value in "c". + c[0] = ((chunk[offset].toInt() and 0xFF)) or + ((chunk[offset + 1].toInt() and 0xFF) shl 8) or + ((chunk[offset + 2].toInt() and 0xFF) shl 16) or + ((chunk[offset + 3].toInt() and 0x03) shl 24) + c[1] = ((chunk[offset + 3].toInt() and 0xFC) shr 2) or + ((chunk[offset + 4].toInt() and 0xFF) shl 6) or + ((chunk[offset + 5].toInt() and 0xFF) shl 14) or + ((chunk[offset + 6].toInt() and 0x0F) shl 22) + c[2] = ((chunk[offset + 6].toInt() and 0xF0) shr 4) or + ((chunk[offset + 7].toInt() and 0xFF) shl 4) or + ((chunk[offset + 8].toInt() and 0xFF) shl 12) or + ((chunk[offset + 9].toInt() and 0x3F) shl 20) + c[3] = ((chunk[offset + 9].toInt() and 0xC0) shr 6) or + ((chunk[offset + 10].toInt() and 0xFF) shl 2) or + ((chunk[offset + 11].toInt() and 0xFF) shl 10) or + ((chunk[offset + 12].toInt() and 0xFF) shl 18) + c[4] = ((chunk[offset + 13].toInt() and 0xFF)) or + ((chunk[offset + 14].toInt() and 0xFF) shl 8) or + ((chunk[offset + 15].toInt() and 0xFF) shl 16) + if (!finalChunk) c[4] = c[4] or (1 shl 24) + + + // Compute h = ((h + c) * r) mod (2^130 - 5) + + // Start with h += c. We assume that h is less than (2^130 - 5) * 6 + // and that c is less than 2^129, so the result will be less than 2^133. + h[0] += c[0] + h[1] += c[1] + h[2] += c[2] + h[3] += c[3] + h[4] += c[4] + + // Multiply h by r. We know that r is less than 2^124 because the + // top 4 bits were AND-ed off by reset(). That makes h * r less + // than 2^257. Which is less than the (2^130 - 6)^2 we want for + // the modulo reduction step that follows. The intermediate limbs + // are 52 bits in size, which allows us to collect up carries in the + // extra bits of the 64 bit longs and propagate them later. + var hv = h[0].toLong() + t[0] = hv * r[0] + t[1] = hv * r[1] + t[2] = hv * r[2] + t[3] = hv * r[3] + t[4] = hv * r[4] + + for (x in 1 until 5) { + hv = h[x].toLong() + t[x] += hv * r[0] + t[x + 1] += hv * r[1] + t[x + 2] += hv * r[2] + t[x + 3] += hv * r[3] + t[x + 4] = hv * r[4] + } + + + // Propagate carries to convert the t limbs from 52-bit back to 26-bit. + // The low bits are placed into h and the high bits are placed into c. + h[0] = (t[0].toInt()) and 0x03FFFFFF + hv = t[1] + (t[0] shr 26) + h[1] = (hv.toInt()) and 0x03FFFFFF + hv = t[2] + (hv shr 26) + h[2] = (hv.toInt()) and 0x03FFFFFF + hv = t[3] + (hv shr 26) + h[3] = (hv.toInt()) and 0x03FFFFFF + hv = t[4] + (hv shr 26) + h[4] = (hv.toInt()) and 0x03FFFFFF + hv = t[5] + (hv shr 26) + c[0] = (hv.toInt()) and 0x03FFFFFF + hv = t[6] + (hv shr 26) + c[1] = (hv.toInt()) and 0x03FFFFFF + hv = t[7] + (hv shr 26) + c[2] = (hv.toInt()) and 0x03FFFFFF + hv = t[8] + (hv shr 26) + c[3] = (hv.toInt()) and 0x03FFFFFF + hv = t[9] + (hv shr 26) + c[4] = (hv.toInt()) + + + // Reduce h * r modulo (2^130 - 5) by multiplying the high 130 bits by 5 + // and adding them to the low 130 bits. This will leave the result at + // most 5 subtractions away from the answer we want. + var carry = h[0] + c[0] * 5 + h[0] = carry and 0x03FFFFFF + carry = (carry shr 26) + h[1] + c[1] * 5 + h[1] = carry and 0x03FFFFFF + carry = (carry shr 26) + h[2] + c[2] * 5 + h[2] = carry and 0x03FFFFFF + carry = (carry shr 26) + h[3] + c[3] * 5 + h[3] = carry and 0x03FFFFFF + carry = (carry shr 26) + h[4] + c[4] * 5 + h[4] = carry + } + + override fun destroy() { + // Kotlin Array Fill + nonce.fill(0.toByte()) + block.fill(0.toByte()) + h.fill(0) + r.fill(0) + c.fill(0) + t.fill(0L) + } } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Destroyable.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Destroyable1.kt similarity index 89% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Destroyable.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Destroyable1.kt index 1ab0061c..a92f5546 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Destroyable.java +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Destroyable1.kt @@ -20,7 +20,7 @@ * DEALINGS IN THE SOFTWARE. */ -package com.bitchat.android.noise.southernstorm.protocol; +package com.bitchat.android.noise.southernstorm.protocol /** * Interface for objects that implement destroying. @@ -34,10 +34,9 @@ package com.bitchat.android.noise.southernstorm.protocol; * The Noise.destroy() function can help with destroying byte arrays * that hold sensitive values. */ -public interface Destroyable { - - /** - * Destroys all sensitive state in the current object. - */ - void destroy(); +interface Destroyable { + /** + * Destroys all sensitive state in the current object. + */ + fun destroy() } From 240635ccfe2e2860d24db4980daae334c9caa4bf Mon Sep 17 00:00:00 2001 From: yet300 Date: Fri, 8 May 2026 16:40:20 +0400 Subject: [PATCH 02/37] Rename .java to .kt --- .../noise/southernstorm/crypto/{ChaChaCore.java => ChaChaCore.kt} | 0 .../noise/southernstorm/crypto/{Poly1305.java => Poly1305.kt} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/{ChaChaCore.java => ChaChaCore.kt} (100%) rename app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/{Poly1305.java => Poly1305.kt} (100%) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/ChaChaCore.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/ChaChaCore.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/ChaChaCore.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/ChaChaCore.kt diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Poly1305.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Poly1305.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Poly1305.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Poly1305.kt From cf0a7678090bbb40106af1d2b07192431f4afc93 Mon Sep 17 00:00:00 2001 From: yet300 Date: Fri, 8 May 2026 16:40:20 +0400 Subject: [PATCH 03/37] Rename Destroyable1 to Destroyable and convert ChaChaCore and Poly1305 to Kotlin --- .../southernstorm/protocol/{Destroyable1.kt => Destroyable.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/{Destroyable1.kt => Destroyable.kt} (100%) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Destroyable1.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Destroyable.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Destroyable1.kt rename to app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Destroyable.kt From a68efc89216bebc19bdd11cd359e293eb8459fc6 Mon Sep 17 00:00:00 2001 From: yet300 Date: Fri, 8 May 2026 17:01:50 +0400 Subject: [PATCH 04/37] Refactor: Convert RijndaelAES to pure Kotlin for KMP - Translated `RijndaelAES.java` to Kotlin. - Removed JVM-specific `java.util.Arrays` dependency, utilizing Kotlin's `.fill()`. - Refactored C-style variable declarations, moving them closer to their usage scope for better Kotlin idiomatics. - Retained the original lookup tables (S-boxes) and bitwise logic to ensure the fallback software AES implementation remains cryptographically accurate. --- .../noise/southernstorm/crypto/RijndaelAES.kt | 1095 +++++++++++++++++ 1 file changed, 1095 insertions(+) create mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/RijndaelAES.kt diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/RijndaelAES.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/RijndaelAES.kt new file mode 100644 index 00000000..ea5876f4 --- /dev/null +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/RijndaelAES.kt @@ -0,0 +1,1095 @@ +// This implementation is a straight C-to-Java port of the +// public domain code from the original Rijndael authors: +// http://web.cs.ucdavis.edu/~rogaway/ocb/ocb-ref/ +// The original license declaration follows (all modifications +// are released under the same terms): +/* + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.bitchat.android.noise.southernstorm.crypto + + +/** + * Public domain fallback implementation of AES in ECB mode. + */ +class RijndaelAES { + private val rk = IntArray(60) + private var Nr = 14 + + /** + * Destroys the sensitive state in this key schedule. + */ + fun destroy() { + rk.fill(0) + } + + /** + * Expand the cipher key into the encryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ + fun setupEnc(cipherKey: ByteArray, offset: Int, keyBits: Int): Int { + var i = 0 + var temp: Int + + rk[0] = GETU32(cipherKey, offset) + rk[1] = GETU32(cipherKey, offset + 4) + rk[2] = GETU32(cipherKey, offset + 8) + rk[3] = GETU32(cipherKey, offset + 12) + var rkoffset = 0 + + if (keyBits == 128) { + while (true) { + temp = rk[rkoffset + 3] + rk[rkoffset + 4] = rk[rkoffset] xor + (Te4[(temp shr 16) and 0xff] and -0x1000000) xor + (Te4[(temp shr 8) and 0xff] and 0x00ff0000) xor + (Te4[temp and 0xff] and 0x0000ff00) xor + (Te4[(temp shr 24) and 0xff] and 0x000000ff) xor + rcon[i] + rk[rkoffset + 5] = rk[rkoffset + 1] xor rk[rkoffset + 4] + rk[rkoffset + 6] = rk[rkoffset + 2] xor rk[rkoffset + 5] + rk[rkoffset + 7] = rk[rkoffset + 3] xor rk[rkoffset + 6] + if (++i == 10) { + Nr = 10 + return Nr + } + rkoffset += 4 + } + } + + rk[rkoffset + 4] = GETU32(cipherKey, offset + 16) + rk[rkoffset + 5] = GETU32(cipherKey, offset + 20) + + if (keyBits == 192) { + while (true) { + temp = rk[rkoffset + 5] + rk[rkoffset + 6] = rk[rkoffset] xor + (Te4[(temp shr 16) and 0xff] and -0x1000000) xor + (Te4[(temp shr 8) and 0xff] and 0x00ff0000) xor + (Te4[temp and 0xff] and 0x0000ff00) xor + (Te4[(temp shr 24) and 0xff] and 0x000000ff) xor + rcon[i] + rk[rkoffset + 7] = rk[rkoffset + 1] xor rk[rkoffset + 6] + rk[rkoffset + 8] = rk[rkoffset + 2] xor rk[rkoffset + 7] + rk[rkoffset + 9] = rk[rkoffset + 3] xor rk[rkoffset + 8] + if (++i == 8) { + Nr = 12 + return Nr + } + rk[rkoffset + 10] = rk[rkoffset + 4] xor rk[rkoffset + 9] + rk[rkoffset + 11] = rk[rkoffset + 5] xor rk[rkoffset + 10] + rkoffset += 6 + } + } + + rk[rkoffset + 6] = GETU32(cipherKey, offset + 24) + rk[rkoffset + 7] = GETU32(cipherKey, offset + 28) + + if (keyBits == 256) { + while (true) { + temp = rk[rkoffset + 7] + rk[rkoffset + 8] = rk[rkoffset + 0] xor + (Te4[(temp shr 16) and 0xff] and -0x1000000) xor + (Te4[(temp shr 8) and 0xff] and 0x00ff0000) xor + (Te4[(temp) and 0xff] and 0x0000ff00) xor + (Te4[(temp shr 24) and 0xff] and 0x000000ff) xor + rcon[i] + rk[rkoffset + 9] = rk[rkoffset + 1] xor rk[rkoffset + 8] + rk[rkoffset + 10] = rk[rkoffset + 2] xor rk[rkoffset + 9] + rk[rkoffset + 11] = rk[rkoffset + 3] xor rk[rkoffset + 10] + if (++i == 7) { + Nr = 14 + return Nr + } + temp = rk[rkoffset + 11] + rk[rkoffset + 12] = rk[rkoffset + 4] xor + (Te4[(temp shr 24) and 0xff] and -0x1000000) xor + (Te4[(temp shr 16) and 0xff] and 0x00ff0000) xor + (Te4[(temp shr 8) and 0xff] and 0x0000ff00) xor + (Te4[(temp) and 0xff] and 0x000000ff) + rk[rkoffset + 13] = rk[rkoffset + 5] xor rk[rkoffset + 12] + rk[rkoffset + 14] = rk[rkoffset + 6] xor rk[rkoffset + 13] + rk[rkoffset + 15] = rk[rkoffset + 7] xor rk[rkoffset + 14] + + rkoffset += 8 + } + } + return 0 + } + + /** + * Expand the cipher key into the decryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ + fun setupDec(cipherKey: ByteArray, offset: Int, keyBits: Int): Int { + val Nr = setupEnc(cipherKey, offset, keyBits) + var i = 0 + var j = 4 * Nr + + while (i < j) { + var temp = rk[i] + rk[i] = rk[j] + rk[j] = temp + + temp = rk[i + 1] + rk[i + 1] = rk[j + 1] + rk[j + 1] = temp + + temp = rk[i + 2] + rk[i + 2] = rk[j + 2] + rk[j + 2] = temp + + temp = rk[i + 3] + rk[i + 3] = rk[j + 3] + rk[j + 3] = temp + + i += 4 + j -= 4 + } + + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + var rkoffset = 0 + + for (round in 1 until Nr) { + rkoffset += 4 + rk[rkoffset + 0] = + Td0[Te4[(rk[rkoffset] shr 24) and 0xff] and 0xff] xor + Td1[Te4[(rk[rkoffset] shr 16) and 0xff] and 0xff] xor + Td2[Te4[(rk[rkoffset] shr 8) and 0xff] and 0xff] xor + Td3[Te4[rk[rkoffset] and 0xff] and 0xff] + rk[rkoffset + 1] = + Td0[Te4[(rk[rkoffset + 1] shr 24) and 0xff] and 0xff] xor + Td1[Te4[(rk[rkoffset + 1] shr 16) and 0xff] and 0xff] xor + Td2[Te4[(rk[rkoffset + 1] shr 8) and 0xff] and 0xff] xor + Td3[Te4[rk[rkoffset + 1] and 0xff] and 0xff] + rk[rkoffset + 2] = + Td0[Te4[(rk[rkoffset + 2] shr 24) and 0xff] and 0xff] xor + Td1[Te4[(rk[rkoffset + 2] shr 16) and 0xff] and 0xff] xor + Td2[Te4[(rk[rkoffset + 2] shr 8) and 0xff] and 0xff] xor + Td3[Te4[rk[rkoffset + 2] and 0xff] and 0xff] + rk[rkoffset + 3] = + Td0[Te4[(rk[rkoffset + 3] shr 24) and 0xff] and 0xff] xor + Td1[Te4[(rk[rkoffset + 3] shr 16) and 0xff] and 0xff] xor + Td2[Te4[(rk[rkoffset + 3] shr 8) and 0xff] and 0xff] xor + Td3[Te4[rk[rkoffset + 3] and 0xff] and 0xff] + } + this.Nr = Nr + return Nr + } + + fun encrypt(pt: ByteArray, ptoffset: Int, ct: ByteArray, ctoffset: Int) { + var s0 = GETU32(pt, ptoffset) xor rk[0] + var s1 = GETU32(pt, ptoffset + 4) xor rk[1] + var s2 = GETU32(pt, ptoffset + 8) xor rk[2] + var s3 = GETU32(pt, ptoffset + 12) xor rk[3] + + var t0: Int; var t1: Int; var t2: Int; var t3: Int + var r = Nr shr 1 + var rkoffset = 0 + + while (true) { + t0 = + Te0[(s0 shr 24) and 0xff] xor + Te1[(s1 shr 16) and 0xff] xor + Te2[(s2 shr 8) and 0xff] xor + Te3[(s3) and 0xff] xor + rk[rkoffset + 4] + t1 = + Te0[(s1 shr 24) and 0xff] xor + Te1[(s2 shr 16) and 0xff] xor + Te2[(s3 shr 8) and 0xff] xor + Te3[(s0) and 0xff] xor + rk[rkoffset + 5] + t2 = + Te0[(s2 shr 24) and 0xff] xor + Te1[(s3 shr 16) and 0xff] xor + Te2[(s0 shr 8) and 0xff] xor + Te3[(s1) and 0xff] xor + rk[rkoffset + 6] + t3 = + Te0[(s3 shr 24) and 0xff] xor + Te1[(s0 shr 16) and 0xff] xor + Te2[(s1 shr 8) and 0xff] xor + Te3[(s2) and 0xff] xor + rk[rkoffset + 7] + + rkoffset += 8 + if (--r == 0) { + break + } + + s0 = + Te0[(t0 shr 24) and 0xff] xor + Te1[(t1 shr 16) and 0xff] xor + Te2[(t2 shr 8) and 0xff] xor + Te3[(t3) and 0xff] xor + rk[rkoffset] + s1 = + Te0[(t1 shr 24) and 0xff] xor + Te1[(t2 shr 16) and 0xff] xor + Te2[(t3 shr 8) and 0xff] xor + Te3[(t0) and 0xff] xor + rk[rkoffset + 1] + s2 = + Te0[(t2 shr 24) and 0xff] xor + Te1[(t3 shr 16) and 0xff] xor + Te2[(t0 shr 8) and 0xff] xor + Te3[(t1) and 0xff] xor + rk[rkoffset + 2] + s3 = + Te0[(t3 shr 24) and 0xff] xor + Te1[(t0 shr 16) and 0xff] xor + Te2[(t1 shr 8) and 0xff] xor + Te3[(t2) and 0xff] xor + rk[rkoffset + 3] + } + + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4[(t0 shr 24) and 0xff] and -0x1000000) xor + (Te4[(t1 shr 16) and 0xff] and 0x00ff0000) xor + (Te4[(t2 shr 8) and 0xff] and 0x0000ff00) xor + (Te4[(t3) and 0xff] and 0x000000ff) xor + rk[rkoffset] + PUTU32(ct, ctoffset, s0) + s1 = + (Te4[(t1 shr 24) and 0xff] and -0x1000000) xor + (Te4[(t2 shr 16) and 0xff] and 0x00ff0000) xor + (Te4[(t3 shr 8) and 0xff] and 0x0000ff00) xor + (Te4[(t0) and 0xff] and 0x000000ff) xor + rk[rkoffset + 1] + PUTU32(ct, ctoffset + 4, s1) + s2 = + (Te4[(t2 shr 24) and 0xff] and -0x1000000) xor + (Te4[(t3 shr 16) and 0xff] and 0x00ff0000) xor + (Te4[(t0 shr 8) and 0xff] and 0x0000ff00) xor + (Te4[(t1) and 0xff] and 0x000000ff) xor + rk[rkoffset + 2] + PUTU32(ct, ctoffset + 8, s2) + s3 = + (Te4[(t3 shr 24) and 0xff] and -0x1000000) xor + (Te4[(t0 shr 16) and 0xff] and 0x00ff0000) xor + (Te4[(t1 shr 8) and 0xff] and 0x0000ff00) xor + (Te4[(t2) and 0xff] and 0x000000ff) xor + rk[rkoffset + 3] + PUTU32(ct, ctoffset + 12, s3) + } + + fun decrypt(ct: ByteArray, ctoffset: Int, pt: ByteArray, ptoffset: Int) { + var s0 = GETU32(ct, ctoffset) xor rk[0] + var s1 = GETU32(ct, ctoffset + 4) xor rk[1] + var s2 = GETU32(ct, ctoffset + 8) xor rk[2] + var s3 = GETU32(ct, ctoffset + 12) xor rk[3] + + var t0: Int; var t1: Int; var t2: Int; var t3: Int + var r = Nr shr 1 + var rkoffset = 0 + + while (true) { + t0 = + Td0[(s0 shr 24) and 0xff] xor + Td1[(s3 shr 16) and 0xff] xor + Td2[(s2 shr 8) and 0xff] xor + Td3[(s1) and 0xff] xor + rk[rkoffset + 4] + t1 = + Td0[(s1 shr 24) and 0xff] xor + Td1[(s0 shr 16) and 0xff] xor + Td2[(s3 shr 8) and 0xff] xor + Td3[(s2) and 0xff] xor + rk[rkoffset + 5] + t2 = + Td0[(s2 shr 24) and 0xff] xor + Td1[(s1 shr 16) and 0xff] xor + Td2[(s0 shr 8) and 0xff] xor + Td3[(s3) and 0xff] xor + rk[rkoffset + 6] + t3 = + Td0[(s3 shr 24) and 0xff] xor + Td1[(s2 shr 16) and 0xff] xor + Td2[(s1 shr 8) and 0xff] xor + Td3[(s0) and 0xff] xor + rk[rkoffset + 7] + + rkoffset += 8 + if (--r == 0) { + break + } + + s0 = + Td0[(t0 shr 24) and 0xff] xor + Td1[(t3 shr 16) and 0xff] xor + Td2[(t2 shr 8) and 0xff] xor + Td3[(t1) and 0xff] xor + rk[rkoffset] + s1 = + Td0[(t1 shr 24) and 0xff] xor + Td1[(t0 shr 16) and 0xff] xor + Td2[(t3 shr 8) and 0xff] xor + Td3[(t2) and 0xff] xor + rk[rkoffset + 1] + s2 = + Td0[(t2 shr 24) and 0xff] xor + Td1[(t1 shr 16) and 0xff] xor + Td2[(t0 shr 8) and 0xff] xor + Td3[(t3) and 0xff] xor + rk[rkoffset + 2] + s3 = + Td0[(t3 shr 24) and 0xff] xor + Td1[(t2 shr 16) and 0xff] xor + Td2[(t1 shr 8) and 0xff] xor + Td3[(t0) and 0xff] xor + rk[rkoffset + 3] + } + + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 shr 24) and 0xff] and -0x1000000) xor + (Td4[(t3 shr 16) and 0xff] and 0x00ff0000) xor + (Td4[(t2 shr 8) and 0xff] and 0x0000ff00) xor + (Td4[(t1) and 0xff] and 0x000000ff) xor + rk[rkoffset] + PUTU32(pt, ptoffset, s0) + s1 = + (Td4[(t1 shr 24) and 0xff] and -0x1000000) xor + (Td4[(t0 shr 16) and 0xff] and 0x00ff0000) xor + (Td4[(t3 shr 8) and 0xff] and 0x0000ff00) xor + (Td4[(t2) and 0xff] and 0x000000ff) xor + rk[rkoffset + 1] + PUTU32(pt, ptoffset + 4, s1) + s2 = + (Td4[(t2 shr 24) and 0xff] and -0x1000000) xor + (Td4[(t1 shr 16) and 0xff] and 0x00ff0000) xor + (Td4[(t0 shr 8) and 0xff] and 0x0000ff00) xor + (Td4[(t3) and 0xff] and 0x000000ff) xor + rk[rkoffset + 2] + PUTU32(pt, ptoffset + 8, s2) + s3 = + (Td4[(t3 shr 24) and 0xff] and -0x1000000) xor + (Td4[(t2 shr 16) and 0xff] and 0x00ff0000) xor + (Td4[(t1 shr 8) and 0xff] and 0x0000ff00) xor + (Td4[(t0) and 0xff] and 0x000000ff) xor + rk[rkoffset + 3] + PUTU32(pt, ptoffset + 12, s3) + } + + companion object { + private fun GETU32(buf: ByteArray, offset: Int): Int { + return ((buf[offset].toInt() and 0xFF) shl 24) or + ((buf[offset + 1].toInt() and 0xFF) shl 16) or + ((buf[offset + 2].toInt() and 0xFF) shl 8) or + (buf[offset + 3].toInt() and 0xFF) + } + + private fun PUTU32(buf: ByteArray, offset: Int, value: Int) { + buf[offset] = (value shr 24).toByte() + buf[offset + 1] = (value shr 16).toByte() + buf[offset + 2] = (value shr 8).toByte() + buf[offset + 3] = value.toByte() + } + + private val Te0 = intArrayOf( + -0x399c9c5b, -0x783837c, -0x11888867, -0x9848473, + -0xd0df3, -0x29949443, -0x2190904f, -0x6e3a3aac, + 0x60303050, 0x02010103, -0x31989857, 0x562b2b7d, + -0x180101e7, -0x4a28289e, 0x4dababe6, -0x13898966, + -0x703535bb, 0x1f82829d, -0x763636c0, -0x5828279, + -0x100505eb, -0x4da6a615, -0x71b8b837, -0x40f0ff5, + 0x41adadec, -0x4c2b2b99, 0x5fa2a2fd, 0x45afafea, + 0x239c9cbf, 0x53a4a4f7, -0x1b8d8d6a, -0x643f3fa5, + 0x75b7b7c2, -0x1e0202e4, 0x3d9393ae, 0x4c26266a, + 0x6c36365a, 0x7e3f3f41, -0xa0808fe, -0x7c3333b1, + 0x6834345c, 0x51a5a5f4, -0x2e1a1acc, -0x60e0ef8, + -0x1d8e8e6d, -0x5427278d, 0x62313153, 0x2a15153f, + 0x0804040c, -0x6a3838ae, 0x46232365, -0x623c3ca2, + 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, + 0x0e070709, 0x24121236, 0x1b80809b, -0x201d1dc3, + -0x321414da, 0x4e272769, 0x7fb2b2cd, -0x158a8a61, + 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, + 0x361b1b2d, -0x2391914e, -0x4ba5a512, 0x5ba0a0fb, + -0x5badad0a, 0x763b3b4d, -0x4829299f, 0x7db3b3ce, + 0x5229297b, -0x221c1cc2, 0x5e2f2f71, 0x13848497, + -0x59acac0b, -0x462e2e98, 0x00000000, -0x3e1212d4, + 0x40202060, -0x1c0303e1, 0x79b1b1c8, -0x49a4a413, + -0x2b959542, -0x723434ba, 0x67bebed9, 0x7239394b, + -0x6bb5b522, -0x67b3b32c, -0x4fa7a718, -0x7a3030b6, + -0x442f2f95, -0x3a1010d6, 0x4faaaae5, -0x120404ea, + -0x79bcbc3b, -0x65b2b229, 0x66333355, 0x11858594, + -0x75baba31, -0x160606f0, 0x04020206, -0x180807f, + -0x5fafaf10, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, + -0x5daeae0d, 0x5da3a3fe, -0x7fbfbf40, 0x058f8f8a, + 0x3f9292ad, 0x219d9dbc, 0x70383848, -0xe0a0afc, + 0x63bcbcdf, 0x77b6b6c1, -0x5025258b, 0x42212163, + 0x20101030, -0x1a0000e6, -0x20c0cf2, -0x402d2d93, + -0x7e3232b4, 0x180c0c14, 0x26131335, -0x3c1313d1, + -0x41a0a01f, 0x359797a2, -0x77bbbb34, 0x2e171739, + -0x6c3b3ba9, 0x55a7a7f2, -0x381817e, 0x7a3d3d47, + -0x379b9b54, -0x45a2a219, 0x3219192b, -0x198c8c6b, + -0x3f9f9f60, 0x19818198, -0x61b0b02f, -0x5c232381, + 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, + -0x73b9b936, -0x381111d7, 0x6bb8b8d3, 0x2814143c, + -0x58212187, -0x43a1a11e, 0x160b0b1d, -0x5224248a, + -0x241f1fc5, 0x64323256, 0x743a3a4e, 0x140a0a1e, + -0x6db6b625, 0x0c06060a, 0x4824246c, -0x47a3a31c, + -0x603d3da3, -0x422c2c92, 0x43acacef, -0x3b9d9d5a, + 0x399191a8, 0x319595a4, -0x2c1b1bc9, -0xd868675, + -0x2a1818ce, -0x743737bd, 0x6e373759, -0x25929249, + 0x018d8d8c, -0x4e2a2a9c, -0x63b1b12e, 0x49a9a9e0, + -0x2793934c, -0x53a9a906, -0xc0b0bf9, -0x301515db, + -0x359a9a51, -0xb858572, 0x47aeaee9, 0x10080818, + 0x6fbabad5, -0xf878778, 0x4a25256f, 0x5c2e2e72, + 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, -0x683939af, + -0x341717dd, -0x5e222284, -0x178b8b64, 0x3e1f1f21, + -0x69b4b423, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, + -0x1f8f8f70, 0x7c3e3e42, 0x71b5b5c4, -0x33999956, + -0x6fb7b728, 0x06030305, -0x80909ff, 0x1c0e0e12, + -0x3d9e9e5d, 0x6a35355f, -0x51a8a807, 0x69b9b9d0, + 0x17868691, -0x663e3ea8, 0x3a1d1d27, 0x279e9eb9, + -0x261e1ec8, -0x140707ed, 0x2b9898b3, 0x22111133, + -0x2d969645, -0x56262690, 0x078e8e89, 0x339494a7, + 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, -0x361616e0, + -0x783131b7, -0x55aaaa01, 0x50282878, -0x5a202086, + 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, + 0x65bfbfda, -0x281919cf, -0x7bbdbd3a, -0x2f979748, + -0x7dbebe3d, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, + 0x7bb0b0cb, -0x57abab04, 0x6dbbbbd6, 0x2c16163a, + ) + private val Te1 = intArrayOf( + -0x5a399c9d, -0x7b078384, -0x66118889, -0x72098485, + 0x0dfff2f2, -0x42299495, -0x4e219091, 0x5491c5c5, + 0x50603030, 0x03020101, -0x56319899, 0x7d562b2b, + 0x19e7fefe, 0x62b5d7d7, -0x19b25455, -0x6513898a, + 0x458fcaca, -0x62e07d7e, 0x4089c9c9, -0x78058283, + 0x15effafa, -0x144da6a7, -0x3671b8b9, 0x0bfbf0f0, + -0x13be5253, 0x67b3d4d4, -0x2a05d5e, -0x15ba5051, + -0x40dc6364, -0x8ac5b5c, -0x691b8d8e, 0x5b9bc0c0, + -0x3d8a4849, 0x1ce1fdfd, -0x51c26c6d, 0x6a4c2626, + 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, + 0x5c683434, -0xbae5a5b, 0x34d1e5e5, 0x08f9f1f1, + -0x6c1d8e8f, 0x73abd8d8, 0x53623131, 0x3f2a1515, + 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, + 0x28301818, -0x5ec8696a, 0x0f0a0505, -0x4ad06566, + 0x090e0707, 0x36241212, -0x64e47f80, 0x3ddfe2e2, + 0x26cdebeb, 0x694e2727, -0x32804d4e, -0x60158a8b, + 0x1b120909, -0x61e27c7d, 0x74582c2c, 0x2e341a1a, + 0x2d361b1b, -0x4d239192, -0x114ba5a6, -0x4a45f60, + -0x95badae, 0x4d763b3b, 0x61b7d6d6, -0x31824c4d, + 0x7b522929, 0x3edde3e3, 0x715e2f2f, -0x68ec7b7c, + -0xa59acad, 0x68b9d1d1, 0x00000000, 0x2cc1eded, + 0x60402020, 0x1fe3fcfc, -0x37864e4f, -0x1249a4a5, + -0x412b9596, 0x468dcbcb, -0x26984142, 0x4b723939, + -0x216bb5b6, -0x2b67b3b4, -0x174fa7a8, 0x4a85cfcf, + 0x6bbbd0d0, 0x2ac5efef, -0x1ab05556, 0x16edfbfb, + -0x3a79bcbd, -0x2865b2b3, 0x55663333, -0x6bee7a7b, + -0x3075babb, 0x10e9f9f9, 0x06040202, -0x7e018081, + -0xf5fafb0, 0x44783c3c, -0x45da6061, -0x1cb45758, + -0xc5daeaf, -0x1a25c5d, -0x3f7fbfc0, -0x75fa7071, + -0x52c06d6e, -0x43de6263, 0x48703838, 0x04f1f5f5, + -0x209c4344, -0x3e88494a, 0x75afdada, 0x63422121, + 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, + 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, + -0x1e41a0a1, -0x5dca6869, -0x3377bbbc, 0x392e1717, + 0x5793c4c4, -0xdaa5859, -0x7d038182, 0x477a3d3d, + -0x53379b9c, -0x1845a2a3, 0x2b321919, -0x6a198c8d, + -0x5f3f9fa0, -0x67e67e7f, -0x2e61b0b1, 0x7fa3dcdc, + 0x66442222, 0x7e542a2a, -0x54c46f70, -0x7cf47778, + -0x3573b9ba, 0x29c7eeee, -0x2c944748, 0x3c281414, + 0x79a7dede, -0x1d43a1a2, 0x1d160b0b, 0x76addbdb, + 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, + -0x246db6b7, 0x0a0c0606, 0x6c482424, -0x1b47a3a4, + 0x5d9fc2c2, 0x6ebdd3d3, -0x10bc5354, -0x593b9d9e, + -0x57c66e6f, -0x5bce6a6b, 0x37d3e4e4, -0x740d8687, + 0x32d5e7e7, 0x438bc8c8, 0x596e3737, -0x48259293, + -0x73fe7273, 0x64b1d5d5, -0x2d63b1b2, -0x1fb65657, + -0x4b279394, -0x553a9aa, 0x07f3f4f4, 0x25cfeaea, + -0x50359a9b, -0x710b8586, -0x16b85152, 0x18100808, + -0x2a904546, -0x770f8788, 0x6f4a2525, 0x725c2e2e, + 0x24381c1c, -0xea8595a, -0x388c4b4c, 0x5197c6c6, + 0x23cbe8e8, 0x7ca1dddd, -0x63178b8c, 0x213e1f1f, + -0x2269b4b5, -0x239e4243, -0x79f27475, -0x7af07576, + -0x6f1f8f90, 0x427c3e3e, -0x3b8e4a4b, -0x5533999a, + -0x276fb7b8, 0x05060303, 0x01f7f6f6, 0x121c0e0e, + -0x5c3d9e9f, 0x5f6a3535, -0x651a8a9, -0x2f964647, + -0x6ee8797a, 0x5899c1c1, 0x273a1d1d, -0x46d86162, + 0x38d9e1e1, 0x13ebf8f8, -0x4cd46768, 0x33221111, + -0x442d9697, 0x70a9d9d9, -0x76f87172, -0x58cc6b6c, + -0x49d26465, 0x223c1e1e, -0x6dea7879, 0x20c9e9e9, + 0x4987cece, -0x55aaab, 0x78502828, 0x7aa5dfdf, + -0x70fc7374, -0x7a65e5f, -0x7ff67677, 0x171a0d0d, + -0x259a4041, 0x31d7e6e6, -0x397bbdbe, -0x472f9798, + -0x3c7dbebf, -0x4fd66667, 0x775a2d2d, 0x111e0f0f, + -0x34844f50, -0x357abac, -0x29924445, 0x3a2c1616, + ) + private val Te2 = intArrayOf( + 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, + -0xdf2000e, 0x6bbdd66b, 0x6fb1de6f, -0x3aab6e3b, + 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, + -0x1e61802, -0x289d4a29, -0x5419b255, 0x769aec76, + -0x35ba7036, -0x7d62e07e, -0x36bf7637, 0x7d87fa7d, + -0x5ea1006, 0x59ebb259, 0x47c98e47, -0xff40410, + -0x5213be53, -0x2b984c2c, -0x5d02a05e, -0x5015ba51, + -0x6340dc64, -0x5b08ac5c, 0x7296e472, -0x3fa46440, + -0x483d8a49, -0x2e31e03, -0x6c51c26d, 0x266a4c26, + 0x365a6c36, 0x3f417e3f, -0x8fd0a09, -0x33b07c34, + 0x345c6834, -0x5a0bae5b, -0x1acb2e1b, -0xef7060f, + 0x7193e271, -0x278c5428, 0x31536231, 0x153f2a15, + 0x040c0804, -0x38ad6a39, 0x23654623, -0x3ca1623d, + 0x18283018, -0x695ec86a, 0x050f0a05, -0x654ad066, + 0x07090e07, 0x12362412, -0x7f64e480, -0x1dc2201e, + -0x14d93215, 0x27694e27, -0x4d32804e, 0x759fea75, + 0x091b1209, -0x7c61e27d, 0x2c74582c, 0x1a2e341a, + 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, -0x5f04a460, + 0x52f6a452, 0x3b4d763b, -0x299e482a, -0x4c31824d, + 0x297b5229, -0x1cc1221d, 0x2f715e2f, -0x7b68ec7c, + 0x53f5a653, -0x2e97462f, 0x00000000, -0x12d33e13, + 0x20604020, -0x3e01c04, -0x4e37864f, 0x5bedb65b, + 0x6abed46a, -0x34b97235, -0x41269842, 0x394b7239, + 0x4ade944a, 0x4cd4984c, 0x58e8b058, -0x30b57a31, + -0x2f944430, -0x10d53a11, -0x551ab056, -0x4e91205, + 0x43c58643, 0x4dd79a4d, 0x33556633, -0x7a6bee7b, + 0x45cf8a45, -0x6ef1607, 0x02060402, 0x7f81fe7f, + 0x50f0a050, 0x3c44783c, -0x6045da61, -0x571cb458, + 0x51f3a251, -0x5c01a25d, 0x40c08040, -0x7075fa71, + -0x6d52c06e, -0x6243de63, 0x38487038, -0xafb0e0b, + -0x43209c44, -0x493e884a, -0x258a5026, 0x21634221, + 0x10302010, -0xe51a01, -0xcf1020d, -0x2d92402e, + -0x32b37e33, 0x0c14180c, 0x13352613, -0x13d03c14, + 0x5fe1be5f, -0x685dca69, 0x44cc8844, 0x17392e17, + -0x3ba86c3c, -0x580daa59, 0x7e82fc7e, 0x3d477a3d, + 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, + 0x60a0c060, -0x7e67e67f, 0x4fd19e4f, -0x23805c24, + 0x22664422, 0x2a7e542a, -0x6f54c470, -0x777cf478, + 0x46ca8c46, -0x11d63812, -0x472c9448, 0x143c2814, + -0x21865822, 0x5ee2bc5e, 0x0b1d160b, -0x24895225, + -0x1fc42420, 0x32566432, 0x3a4e743a, 0x0a1e140a, + 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, + -0x3da2603e, -0x2c91422d, -0x5310bc54, 0x62a6c462, + -0x6e57c66f, -0x6a5bce6b, -0x1bc82c1c, 0x798bf279, + -0x18cd2a19, -0x37bc7438, 0x37596e37, 0x6db7da6d, + -0x7273fe73, -0x2a9b4e2b, 0x4ed29c4e, -0x561fb657, + 0x6cb4d86c, 0x56faac56, -0xbf80c0c, -0x15da3016, + 0x65afca65, 0x7a8ef47a, -0x5116b852, 0x08181008, + -0x452a9046, 0x7888f078, 0x256f4a25, 0x2e725c2e, + 0x1c24381c, -0x590ea85a, -0x4b388c4c, -0x39ae683a, + -0x17dc3418, -0x22835e23, 0x749ce874, 0x1f213e1f, + 0x4bdd964b, -0x42239e43, -0x7479f275, -0x757af076, + 0x7090e070, 0x3e427c3e, -0x4a3b8e4b, 0x66aacc66, + 0x48d89048, 0x03050603, -0x9fe080a, 0x0e121c0e, + 0x61a3c261, 0x355f6a35, 0x57f9ae57, -0x462f9647, + -0x796ee87a, -0x3ea7663f, 0x1d273a1d, -0x6146d862, + -0x1ec7261f, -0x7ec1408, -0x674cd468, 0x11332211, + 0x69bbd269, -0x268f5627, -0x7176f872, -0x6b58cc6c, + -0x6449d265, 0x1e223c1e, -0x786dea79, -0x16df3617, + -0x31b67832, 0x55ffaa55, 0x28785028, -0x20855a21, + -0x7370fc74, -0x5e07a65f, -0x767ff677, 0x0d171a0d, + -0x40259a41, -0x19ce281a, 0x42c68442, 0x68b8d068, + 0x41c38241, -0x664fd667, 0x2d775a2d, 0x0f111e0f, + -0x4f348450, 0x54fca854, -0x44299245, 0x163a2c16, + ) + private val Te3 = intArrayOf( + 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, + -0xd0df201, 0x6b6bbdd6, 0x6f6fb1de, -0x3a3aab6f, + 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, + -0x101e619, -0x28289d4b, -0x545419b3, 0x76769aec, + -0x3535ba71, -0x7d7d62e1, -0x3636bf77, 0x7d7d87fa, + -0x505ea11, 0x5959ebb2, 0x4747c98e, -0xf0ff405, + -0x525213bf, -0x2b2b984d, -0x5d5d02a1, -0x505015bb, + -0x636340dd, -0x5b5b08ad, 0x727296e4, -0x3f3fa465, + -0x48483d8b, -0x202e31f, -0x6c6c51c3, 0x26266a4c, + 0x36365a6c, 0x3f3f417e, -0x808fd0b, -0x3333b07d, + 0x34345c68, -0x5a5a0baf, -0x1a1acb2f, -0xe0ef707, + 0x717193e2, -0x27278c55, 0x31315362, 0x15153f2a, + 0x04040c08, -0x3838ad6b, 0x23236546, -0x3c3ca163, + 0x18182830, -0x69695ec9, 0x05050f0a, -0x65654ad1, + 0x0707090e, 0x12123624, -0x7f7f64e5, -0x1d1dc221, + -0x1414d933, 0x2727694e, -0x4d4d3281, 0x75759fea, + 0x09091b12, -0x7c7c61e3, 0x2c2c7458, 0x1a1a2e34, + 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, -0x5f5f04a5, + 0x5252f6a4, 0x3b3b4d76, -0x29299e49, -0x4c4c3183, + 0x29297b52, -0x1c1cc123, 0x2f2f715e, -0x7b7b68ed, + 0x5353f5a6, -0x2e2e9747, 0x00000000, -0x1212d33f, + 0x20206040, -0x303e01d, -0x4e4e3787, 0x5b5bedb6, + 0x6a6abed4, -0x3434b973, -0x41412699, 0x39394b72, + 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, -0x3030b57b, + -0x2f2f9445, -0x1010d53b, -0x55551ab1, -0x404e913, + 0x4343c586, 0x4d4dd79a, 0x33335566, -0x7a7a6bef, + 0x4545cf8a, -0x606ef17, 0x02020604, 0x7f7f81fe, + 0x5050f0a0, 0x3c3c4478, -0x606045db, -0x57571cb5, + 0x5151f3a2, -0x5c5c01a3, 0x4040c080, -0x707075fb, + -0x6d6d52c1, -0x626243df, 0x38384870, -0xa0afb0f, + -0x4343209d, -0x49493e89, -0x25258a51, 0x21216342, + 0x10103020, -0xe51b, -0xc0cf103, -0x2d2d9241, + -0x3232b37f, 0x0c0c1418, 0x13133526, -0x1313d03d, + 0x5f5fe1be, -0x68685dcb, 0x4444cc88, 0x1717392e, + -0x3b3ba86d, -0x58580dab, 0x7e7e82fc, 0x3d3d477a, + 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, + 0x6060a0c0, -0x7e7e67e7, 0x4f4fd19e, -0x2323805d, + 0x22226644, 0x2a2a7e54, -0x6f6f54c5, -0x77777cf5, + 0x4646ca8c, -0x1111d639, -0x47472c95, 0x14143c28, + -0x21218659, 0x5e5ee2bc, 0x0b0b1d16, -0x24248953, + -0x1f1fc425, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, + 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, + -0x3d3da261, -0x2c2c9143, -0x535310bd, 0x6262a6c4, + -0x6e6e57c7, -0x6a6a5bcf, -0x1b1bc82d, 0x79798bf2, + -0x1818cd2b, -0x3737bc75, 0x3737596e, 0x6d6db7da, + -0x727273ff, -0x2a2a9b4f, 0x4e4ed29c, -0x56561fb7, + 0x6c6cb4d8, 0x5656faac, -0xb0bf80d, -0x1515da31, + 0x6565afca, 0x7a7a8ef4, -0x515116b9, 0x08081810, + -0x45452a91, 0x787888f0, 0x25256f4a, 0x2e2e725c, + 0x1c1c2438, -0x59590ea9, -0x4b4b388d, -0x3939ae69, + -0x1717dc35, -0x2222835f, 0x74749ce8, 0x1f1f213e, + 0x4b4bdd96, -0x4242239f, -0x747479f3, -0x75757af1, + 0x707090e0, 0x3e3e427c, -0x4a4a3b8f, 0x6666aacc, + 0x4848d890, 0x03030506, -0x909fe09, 0x0e0e121c, + 0x6161a3c2, 0x35355f6a, 0x5757f9ae, -0x46462f97, + -0x79796ee9, -0x3e3ea767, 0x1d1d273a, -0x616146d9, + -0x1e1ec727, -0x707ec15, -0x67674cd5, 0x11113322, + 0x6969bbd2, -0x26268f57, -0x717176f9, -0x6b6b58cd, + -0x646449d3, 0x1e1e223c, -0x78786deb, -0x1616df37, + -0x3131b679, 0x5555ffaa, 0x28287850, -0x2020855b, + -0x737370fd, -0x5e5e07a7, -0x76767ff7, 0x0d0d171a, + -0x4040259b, -0x1919ce29, 0x4242c684, 0x6868b8d0, + 0x4141c382, -0x66664fd7, 0x2d2d775a, 0x0f0f111e, + -0x4f4f3485, 0x5454fca8, -0x44442993, 0x16163a2c, + ) + private val Te4 = intArrayOf( + 0x63636363, 0x7c7c7c7c, 0x77777777, 0x7b7b7b7b, + -0xd0d0d0e, 0x6b6b6b6b, 0x6f6f6f6f, -0x3a3a3a3b, + 0x30303030, 0x01010101, 0x67676767, 0x2b2b2b2b, + -0x1010102, -0x28282829, -0x54545455, 0x76767676, + -0x35353536, -0x7d7d7d7e, -0x36363637, 0x7d7d7d7d, + -0x5050506, 0x59595959, 0x47474747, -0xf0f0f10, + -0x52525253, -0x2b2b2b2c, -0x5d5d5d5e, -0x50505051, + -0x63636364, -0x5b5b5b5c, 0x72727272, -0x3f3f3f40, + -0x48484849, -0x2020203, -0x6c6c6c6d, 0x26262626, + 0x36363636, 0x3f3f3f3f, -0x8080809, -0x33333334, + 0x34343434, -0x5a5a5a5b, -0x1a1a1a1b, -0xe0e0e0f, + 0x71717171, -0x27272728, 0x31313131, 0x15151515, + 0x04040404, -0x38383839, 0x23232323, -0x3c3c3c3d, + 0x18181818, -0x6969696a, 0x05050505, -0x65656566, + 0x07070707, 0x12121212, -0x7f7f7f80, -0x1d1d1d1e, + -0x14141415, 0x27272727, -0x4d4d4d4e, 0x75757575, + 0x09090909, -0x7c7c7c7d, 0x2c2c2c2c, 0x1a1a1a1a, + 0x1b1b1b1b, 0x6e6e6e6e, 0x5a5a5a5a, -0x5f5f5f60, + 0x52525252, 0x3b3b3b3b, -0x2929292a, -0x4c4c4c4d, + 0x29292929, -0x1c1c1c1d, 0x2f2f2f2f, -0x7b7b7b7c, + 0x53535353, -0x2e2e2e2f, 0x00000000, -0x12121213, + 0x20202020, -0x3030304, -0x4e4e4e4f, 0x5b5b5b5b, + 0x6a6a6a6a, -0x34343435, -0x41414142, 0x39393939, + 0x4a4a4a4a, 0x4c4c4c4c, 0x58585858, -0x30303031, + -0x2f2f2f30, -0x10101011, -0x55555556, -0x4040405, + 0x43434343, 0x4d4d4d4d, 0x33333333, -0x7a7a7a7b, + 0x45454545, -0x6060607, 0x02020202, 0x7f7f7f7f, + 0x50505050, 0x3c3c3c3c, -0x60606061, -0x57575758, + 0x51515151, -0x5c5c5c5d, 0x40404040, -0x70707071, + -0x6d6d6d6e, -0x62626263, 0x38383838, -0xa0a0a0b, + -0x43434344, -0x4949494a, -0x25252526, 0x21212121, + 0x10101010, -0x1, -0xc0c0c0d, -0x2d2d2d2e, + -0x32323233, 0x0c0c0c0c, 0x13131313, -0x13131314, + 0x5f5f5f5f, -0x68686869, 0x44444444, 0x17171717, + -0x3b3b3b3c, -0x58585859, 0x7e7e7e7e, 0x3d3d3d3d, + 0x64646464, 0x5d5d5d5d, 0x19191919, 0x73737373, + 0x60606060, -0x7e7e7e7f, 0x4f4f4f4f, -0x23232324, + 0x22222222, 0x2a2a2a2a, -0x6f6f6f70, -0x77777778, + 0x46464646, -0x11111112, -0x47474748, 0x14141414, + -0x21212122, 0x5e5e5e5e, 0x0b0b0b0b, -0x24242425, + -0x1f1f1f20, 0x32323232, 0x3a3a3a3a, 0x0a0a0a0a, + 0x49494949, 0x06060606, 0x24242424, 0x5c5c5c5c, + -0x3d3d3d3e, -0x2c2c2c2d, -0x53535354, 0x62626262, + -0x6e6e6e6f, -0x6a6a6a6b, -0x1b1b1b1c, 0x79797979, + -0x18181819, -0x37373738, 0x37373737, 0x6d6d6d6d, + -0x72727273, -0x2a2a2a2b, 0x4e4e4e4e, -0x56565657, + 0x6c6c6c6c, 0x56565656, -0xb0b0b0c, -0x15151516, + 0x65656565, 0x7a7a7a7a, -0x51515152, 0x08080808, + -0x45454546, 0x78787878, 0x25252525, 0x2e2e2e2e, + 0x1c1c1c1c, -0x5959595a, -0x4b4b4b4c, -0x3939393a, + -0x17171718, -0x22222223, 0x74747474, 0x1f1f1f1f, + 0x4b4b4b4b, -0x42424243, -0x74747475, -0x75757576, + 0x70707070, 0x3e3e3e3e, -0x4a4a4a4b, 0x66666666, + 0x48484848, 0x03030303, -0x909090a, 0x0e0e0e0e, + 0x61616161, 0x35353535, 0x57575757, -0x46464647, + -0x7979797a, -0x3e3e3e3f, 0x1d1d1d1d, -0x61616162, + -0x1e1e1e1f, -0x7070708, -0x67676768, 0x11111111, + 0x69696969, -0x26262627, -0x71717172, -0x6b6b6b6c, + -0x64646465, 0x1e1e1e1e, -0x78787879, -0x16161617, + -0x31313132, 0x55555555, 0x28282828, -0x20202021, + -0x73737374, -0x5e5e5e5f, -0x76767677, 0x0d0d0d0d, + -0x40404041, -0x1919191a, 0x42424242, 0x68686868, + 0x41414141, -0x66666667, 0x2d2d2d2d, 0x0f0f0f0f, + -0x4f4f4f50, 0x54545454, -0x44444445, 0x16161616, + ) + private val Td0 = intArrayOf( + 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, + 0x3bab6bcb, 0x1f9d45f1, -0x5305a755, 0x4be30393, + 0x2030fa55, -0x5289920a, -0x7733896f, -0xafdb3db, + 0x4fe5d7fc, -0x3ad53429, 0x26354480, -0x4a9d5c71, + -0x214ea5b7, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, + -0x3cd08afe, -0x7eb30fee, -0x72b9685d, 0x6bd3f9c6, + 0x038f5fe7, 0x15929c95, -0x40928515, -0x6aada626, + -0x2b417cd3, 0x587421d3, 0x49e06929, -0x713637bc, + 0x75c2896a, -0xb718688, -0x66a7c195, 0x27b971dd, + -0x411eb04a, -0xf7752e9, -0x36df539a, 0x7dce3ab4, + 0x63df4a18, -0x1ae5ce7e, -0x68aecca0, 0x62537f45, + -0x4e9b8820, -0x4494517c, -0x17e5fe4, -0x6f7d46c, + 0x70486858, -0x70ba02e7, -0x6b219379, 0x527bf8b7, + -0x548c2cdd, 0x724b02e2, -0x1ce070a9, 0x6655ab2a, + -0x4d14d7f9, 0x2fb5c203, -0x793a8466, -0x2cc8f75b, + 0x302887f2, 0x23bfa5b2, 0x02036aba, -0x12e97da4, + -0x7530e3d5, -0x58864b6e, -0xcf80d10, 0x4e69e2a1, + 0x65daf4cd, 0x0605bed5, -0x2ecb9de1, -0x3b590176, + 0x342e539d, -0x5d0caa60, 0x058ae132, -0x5b09148b, + 0x0b83ec39, 0x4060efaa, 0x5e719f06, -0x4291efaf, + 0x3e218af9, -0x6922f9c3, -0x22c1fa52, 0x4de6bd46, + -0x6eab724b, 0x71c45d05, 0x0406d46f, 0x605015ff, + 0x1998fb24, -0x29421669, -0x76bfbc34, 0x67d99e77, + -0x4f17bd43, 0x07898b88, -0x18e6a4c8, 0x79c8eedb, + -0x5e83f5b9, 0x7c420fe9, -0x77be137, 0x00000000, + 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, + -0x2f10005, 0x0f853856, 0x3daed51e, 0x362d3927, + 0x0a0fd964, 0x685ca621, -0x64a4ab2f, 0x24362e3a, + 0x0c0a67b1, -0x6ca818f1, -0x4b11692e, 0x1b9b919e, + -0x7f3f3ab1, 0x61dc20a2, 0x5a774b69, 0x1c121a16, + -0x1d6c45f6, -0x3f5fd51b, 0x3c22e043, 0x121b171d, + 0x0e090d0b, -0xd743853, 0x2db6a8b9, 0x141ea9c8, + 0x57f11985, -0x508af8b4, -0x11662245, -0x5c809f03, + -0x8fed961, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, + -0x74bcd68a, -0x34dc3924, -0x49120398, -0x471b0e9d, + -0x28ce2336, 0x42638510, 0x13972240, -0x7b39eee0, + -0x7ab5db83, -0x2d44c208, -0x5106cdef, -0x38d65e93, + 0x1d9e2f4b, -0x234dcf0d, 0x0d8652ec, 0x77c1e3d0, + 0x2bb3166c, -0x568f4667, 0x119448fa, 0x47e96422, + -0x5703733c, -0x5f0fc0e6, 0x567d2cd8, 0x223390ef, + -0x78b6b139, -0x26c72e3f, -0x73355d02, -0x672bf4ca, + -0x590a7e31, -0x5a8521d8, -0x254871da, 0x3fadbfa4, + 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, + -0x972ec3e, -0x6f274718, 0x2e39f75e, -0x7d3c500b, + -0x60a27f42, 0x69d0937c, 0x6fd52da9, -0x30daed4d, + -0x375366c5, 0x10187da7, -0x17639c92, -0x24c44485, + -0x32d987f7, 0x6e5918f4, -0x136548ff, -0x7cb06558, + -0x196a919b, -0x55001982, 0x21bccf08, -0x10ea171a, + -0x45186427, 0x4a6f36ce, -0x1560f62c, 0x29b07cd6, + 0x31a4b2af, 0x2a3f2331, -0x395a6bd0, 0x35a266c0, + 0x744ebc37, -0x37d355a, -0x1f6f2f50, 0x33a7d815, + -0xefb67b6, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, + 0x764dd68d, 0x43efb04d, -0x3355b2ac, -0x1b69fb21, + -0x612e4a1d, 0x4c6a881b, -0x3ed3e048, 0x4665517f, + -0x62a115fc, 0x018c355d, -0x5788b8d, -0x4f4bed2, + -0x4c98e2a6, -0x6d242dae, -0x16efa9cd, 0x6dd64713, + -0x65289e74, 0x37a10c7a, 0x59f8148e, -0x14ecc377, + -0x3156d812, -0x489e36cb, -0x1ee31a13, 0x7a47b13c, + -0x632d20a7, 0x55f2733f, 0x1814ce79, 0x73c737bf, + 0x53f7cdea, 0x5ffdaa5b, -0x20c290ec, 0x7844db86, + -0x35500c7f, -0x46973bc2, 0x3824342c, -0x3d5cbfa1, + 0x161dc372, -0x431ddaf4, 0x283c498b, -0xf26abf, + 0x39a80171, 0x080cb3de, -0x274b1b64, 0x6456c190, + 0x7bcb8461, -0x2acd4990, 0x486c5c74, -0x2f47a8be, + ) + private val Td1 = intArrayOf( + 0x5051f4a7, 0x537e4165, -0x3ce5e85c, -0x69c5d8a2, + -0x34c45495, -0xee062bb, -0x545305a8, -0x6cb41cfd, + 0x552030fa, -0x9528993, -0x6e77338a, 0x25f5024c, + -0x3b01a29, -0x283ad535, -0x7fd9cabc, -0x704a9d5d, + 0x49deb15a, 0x6725ba1b, -0x67ba15f2, -0x1ea20140, + 0x02c32f75, 0x12814cf0, -0x5c72b969, -0x39942c07, + -0x18fc70a1, -0x6aea6d64, -0x14409286, -0x256aada7, + 0x2dd4be83, -0x2ca78bdf, 0x2949e069, 0x448ec9c8, + 0x6a75c289, 0x78f48e79, 0x6b99583e, -0x22d8468f, + -0x49411eb1, 0x17f088ad, 0x66c920ac, -0x4b8231c6, + 0x1863df4a, -0x7d1ae5cf, 0x60975133, 0x4562537f, + -0x1f4e9b89, -0x7b449452, 0x1cfe81a0, -0x6b06f7d5, + 0x58704868, 0x198f45fd, -0x786b2194, -0x48ad8408, + 0x23ab73d3, -0x1d8db4fe, 0x57e31f8f, 0x2a6655ab, + 0x07b2eb28, 0x032fb5c2, -0x65793a85, -0x5a2cc8f8, + -0xdcfd779, -0x4ddc405b, -0x45fdfc96, 0x5ced1682, + 0x2b8acf1c, -0x6d58864c, -0xf0cf80e, -0x5eb1961e, + -0x329a250c, -0x2af9fa42, 0x1fd13462, -0x753b5902, + -0x62cbd1ad, -0x5f5d0cab, 0x32058ae1, 0x75a4f6eb, + 0x390b83ec, -0x55bf9f11, 0x065e719f, 0x51bd6e10, + -0x6c1de76, 0x3d96dd06, -0x5122c1fb, 0x464de6bd, + -0x4a6eab73, 0x0571c45d, 0x6f0406d4, -0x9fafeb, + 0x241998fb, -0x68294217, -0x3376bfbd, 0x7767d99e, + -0x424f17be, -0x77f87675, 0x38e7195b, -0x24863712, + 0x47a17c0a, -0x1683bdf1, -0x36077be2, 0x00000000, + -0x7cf67f7a, 0x48322bed, -0x53e1ee90, 0x4e6c5a72, + -0x402f101, 0x560f8538, 0x1e3daed5, 0x27362d39, + 0x640a0fd9, 0x21685ca6, -0x2e64a4ac, 0x3a24362e, + -0x4ef3f599, 0x0f9357e7, -0x2d4b116a, -0x61e4646f, + 0x4f80c0c5, -0x5d9e23e0, 0x695a774b, 0x161c121a, + 0x0ae293ba, -0x1a3f5fd6, 0x433c22e0, 0x1d121b17, + 0x0b0e090d, -0x520d7439, -0x46d24958, -0x37ebe157, + -0x7aa80ee7, 0x4caf7507, -0x44116623, -0x25c80a0, + -0x6008feda, -0x43a38d0b, -0x3abb99c5, 0x345bfb7e, + 0x768b4329, -0x2334dc3a, 0x68b6edfc, 0x63b8e4f1, + -0x3528ce24, 0x10426385, 0x40139722, 0x2084c611, + 0x7d854a24, -0x72d44c3, 0x11aef932, 0x6dc729a1, + 0x4b1d9e2f, -0xc234dd0, -0x13f279ae, -0x2f883e1d, + 0x6c2bb316, -0x66568f47, -0x5ee6bb8, 0x2247e964, + -0x3b570374, 0x1aa0f03f, -0x27a982d4, -0x10ddcc70, + -0x3878b6b2, -0x3e26c72f, -0x173355e, 0x3698d40b, + -0x30590a7f, 0x28a57ade, 0x26dab78e, -0x5bc05241, + -0x1bd3c563, 0x0d507892, -0x6495a034, 0x62547e46, + -0x3d0972ed, -0x176f2748, 0x5e2e39f7, -0xa7d3c51, + -0x4160a280, 0x7c69d093, -0x56902ad3, -0x4c30daee, + 0x3bc8ac99, -0x58efe783, 0x6ee89c63, 0x7bdb3bbb, + 0x09cd2678, -0xb91a6e8, 0x01ec9ab7, -0x577cb066, + 0x65e6956e, 0x7eaaffe6, 0x0821bccf, -0x1910ea18, + -0x26451865, -0x31b590ca, -0x2b1560f7, -0x29d64f84, + -0x50ce5b4e, 0x312a3f23, 0x30c6a594, -0x3fca5d9a, + 0x37744ebc, -0x59037d36, -0x4f1f6f30, 0x1533a7d8, + 0x4af10498, -0x8be1326, 0x0e7fcd50, 0x2f1791f6, + -0x7289b22a, 0x4d43efb0, 0x54ccaa4d, -0x201b69fc, + -0x1c612e4b, 0x1b4c6a88, -0x473ed3e1, 0x7f466551, + 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, + 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, + -0x7365289f, 0x7a37a10c, -0x71a607ec, -0x7614ecc4, + -0x113156d9, 0x35b761c9, -0x121ee31b, 0x3c7a47b1, + 0x599cd2df, 0x3f55f273, 0x791814ce, -0x408c38c9, + -0x15ac0833, 0x5b5ffdaa, 0x14df3d6f, -0x7987bb25, + -0x7e35500d, 0x3eb968c4, 0x2c382434, 0x5fc2a340, + 0x72161dc3, 0x0cbce225, -0x74d7c3b7, 0x41ff0d95, + 0x7139a801, -0x21f7f34d, -0x63274b1c, -0x6f9ba93f, + 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857, + ) + private val Td2 = intArrayOf( + -0x58afae0c, 0x65537e41, -0x5b3ce5e9, 0x5e963a27, + 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, + -0x5aadfd0, 0x6df6ad76, 0x769188cc, 0x4c25f502, + -0x2803b01b, -0x34283ad6, 0x44802635, -0x5c704a9e, + 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, -0x3f1ea202, + 0x7502c32f, -0xfed7eb4, -0x685c72ba, -0x639942d, + 0x5fe7038f, -0x636aea6e, 0x7aebbf6d, 0x59da9552, + -0x7cd22b42, 0x21d35874, 0x692949e0, -0x37bb7137, + -0x76958a3e, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, + 0x4fb6bee1, -0x52e80f78, -0x539936e0, 0x3ab47dce, + 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, + 0x77e0b164, -0x517b4495, -0x5fe3017f, 0x2b94f908, + 0x68587048, -0x2e670bb, 0x6c8794de, -0x748ad85, + -0x2cdc548d, 0x02e2724b, -0x70a81ce1, -0x54d599ab, + 0x2807b2eb, -0x3dfcd04b, 0x7b9a86c5, 0x08a5d337, + -0x780dcfd8, -0x5a4ddc41, 0x6aba0203, -0x7da312ea, + 0x1c2b8acf, -0x4b6d5887, -0xd0f0cf9, -0x1d5eb197, + -0xb329a26, -0x412af9fb, 0x621fd134, -0x1753b5a, + 0x539d342e, 0x55a0a2f3, -0x1ecdfa76, -0x148a5b0a, + -0x13c6f47d, -0x1055bfa0, -0x60f9a18f, 0x1051bd6e, + + -0x7506c1df, 0x063d96dd, 0x05aedd3e, -0x42b9b21a, + -0x724a6eac, 0x5d0571c4, -0x2b90fbfa, 0x15ff6050, + -0x4dbe668, -0x16682943, 0x43cc8940, -0x61889827, + 0x42bdb0e8, -0x7477f877, 0x5b38e719, -0x11248638, + 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, + -0x797cf680, -0x12b7cdd5, 0x70ac1e11, 0x724e6c5a, + -0x402f2, 0x38560f85, -0x2ae1c252, 0x3927362d, + -0x269bf5f1, -0x59de97a4, 0x54d19b5b, 0x2e3a2436, + 0x67b10c0a, -0x18f06ca9, -0x692d4b12, -0x6e61e465, + -0x3ab07f40, 0x20a261dc, 0x4b695a77, 0x1a161c12, + -0x45f51d6d, 0x2ae5c0a0, -0x1fbcc3de, 0x171d121b, + 0x0d0b0e09, -0x38520d75, -0x5746d24a, -0x5637ebe2, + 0x198557f1, 0x074caf75, -0x22441167, 0x60fda37f, + 0x269ff701, -0xa43a38e, 0x3bc54466, 0x7e345bfb, + 0x29768b43, -0x392334dd, -0x3974913, -0xe9c471c, + -0x233528cf, -0x7aefbd9d, 0x22401397, 0x112084c6, + 0x247d854a, 0x3df8d2bb, 0x3211aef9, -0x5e9238d7, + 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, -0x1c2f883f, + 0x166c2bb3, -0x46665690, 0x48fa1194, 0x642247e9, + -0x733b5704, 0x3f1aa0f0, 0x2cd8567d, -0x6f10ddcd, + 0x4ec78749, -0x2e3e26c8, -0x5d017336, 0x0b3698d4, + -0x7e30590b, -0x21d75a86, -0x71d92549, -0x405bc053, + -0x621bd3c6, -0x6df2af88, -0x336495a1, 0x4662547e, + 0x13c2f68d, -0x47176f28, -0x8a1d1c7, -0x500a7d3d, + -0x7f4160a3, -0x6c839630, 0x2da96fd5, 0x12b3cf25, + -0x66c43754, 0x7da71018, 0x636ee89c, -0x448424c5, + 0x7809cd26, 0x18f46e59, -0x48fe1366, -0x65577cb1, + 0x6e65e695, -0x19815501, -0x30f7de44, -0x171910eb, + -0x64264519, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, + -0x4d50ce5c, 0x23312a3f, -0x6bcf395b, 0x66c035a2, + -0x43c88bb2, -0x3559037e, -0x2f4f1f70, -0x27eacc59, + -0x67b50efc, -0x2508be14, 0x500e7fcd, -0x9d0e86f, + -0x297289b3, -0x4fb2bc11, 0x4d54ccaa, 0x04dfe496, + -0x4a1c612f, -0x77e4b396, 0x1fb8c12c, 0x517f4665, + -0x15fb62a2, 0x355d018c, 0x7473fa87, 0x412efb0b, + 0x1d5ab367, -0x2dad6d25, 0x5633e910, 0x47136dd6, + 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, + 0x27eecea9, -0x36ca489f, -0x1a121ee4, -0x4ec385b9, + -0x20a6632e, 0x733f55f2, -0x3186e7ec, 0x37bf73c7, + -0x3215ac09, -0x55a4a003, 0x6f14df3d, -0x247987bc, + -0xc7e3551, -0x3bc14698, 0x342c3824, 0x405fc2a3, + -0x3c8de9e3, 0x250cbce2, 0x498b283c, -0x6abe00f3, + 0x017139a8, -0x4c21f7f4, -0x1b63274c, -0x3e6f9baa, + -0x7b9e8435, -0x498f2ace, 0x5c74486c, 0x5742d0b8, + ) + private val Td3 = intArrayOf( + -0xb58afaf, 0x4165537e, 0x17a4c31a, 0x275e963a, + -0x549434c5, -0x62ba0ee1, -0x5a75454, -0x1cfc6cb5, + 0x30fa5520, 0x766df6ad, -0x33896e78, 0x024c25f5, + -0x1a2803b1, 0x2acbd7c5, 0x35448026, 0x62a38fb5, + -0x4ea5b622, -0x45e498db, -0x15f167bb, -0x13f1ea3, + 0x2f7502c3, 0x4cf01281, 0x4697a38d, -0x2c063995, + -0x70a018fd, -0x6d636aeb, 0x6d7aebbf, 0x5259da95, + -0x417cd22c, 0x7421d358, -0x1f96d6b7, -0x3637bb72, + -0x3d76958b, -0x7186870c, 0x583e6b99, -0x468e22d9, + -0x1eb04942, -0x7752e810, 0x20ac66c9, -0x31c54b83, + -0x20b5e79d, 0x1a3182e5, 0x51336097, 0x537f4562, + 0x6477e0b1, 0x6bae84bb, -0x7e5fe302, 0x082b94f9, + 0x48685870, 0x45fd198f, -0x2193786c, 0x7bf8b752, + 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, + -0x14d7f84e, -0x4a3dfcd1, -0x3a84657a, 0x3708a5d3, + 0x2887f230, -0x405a4ddd, 0x036aba02, 0x16825ced, + -0x30e3d476, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, + -0x250b329b, 0x05bed506, 0x34621fd1, -0x5901753c, + 0x2e539d34, -0xcaa5f5e, -0x751ecdfb, -0x9148a5c, + -0x7c13c6f5, 0x60efaa40, 0x719f065e, 0x6e1051bd, + 0x218af93e, -0x22f9c26a, 0x3e05aedd, -0x1942b9b3, + 0x548db591, -0x3ba2fa8f, 0x06d46f04, 0x5015ff60, + -0x6704dbe7, -0x4216682a, 0x4043cc89, -0x26618899, + -0x17bd4250, -0x767477f9, 0x195b38e7, -0x37112487, + 0x7c0a47a1, 0x420fe97c, -0x7be13608, 0x00000000, + -0x7f797cf7, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, + 0x0efffbfd, -0x7ac7a9f1, -0x512ae1c3, 0x2d392736, + 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, + 0x0a67b10c, 0x57e70f93, -0x11692d4c, -0x646e61e5, + -0x3f3ab080, -0x23df5d9f, 0x774b695a, 0x121a161c, + -0x6c45f51e, -0x5fd51a40, 0x22e0433c, 0x1b171d12, + 0x090d0b0e, -0x7438520e, -0x495746d3, 0x1ea9c814, + -0xee67aa9, 0x75074caf, -0x66224412, 0x7f60fda3, + 0x01269ff7, 0x72f5bc5c, 0x663bc544, -0x481cba5, + 0x4329768b, 0x23c6dccb, -0x1203974a, -0x1b0e9c48, + 0x31dccad7, 0x63851042, -0x68ddbfed, -0x39eedf7c, + 0x4a247d85, -0x44c2072e, -0x6cdee52, 0x29a16dc7, + -0x61d0b4e3, -0x4dcf0c24, -0x79ad13f3, -0x3e1c2f89, + -0x4ce993d5, 0x70b999a9, -0x6bb705ef, -0x169bddb9, + -0x3733b58, -0xfc0e560, 0x7d2cd856, 0x3390ef22, + 0x494ec787, 0x38d1c1d9, -0x355d0174, -0x2bf4c968, + -0xa7e305a, 0x7ade28a5, -0x4871d926, -0x52405bc1, + 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, + -0x72ec3d0a, -0x27471770, 0x39f75e2e, -0x3c500a7e, + 0x5d80be9f, -0x2f6c8397, -0x2ad25691, 0x2512b3cf, + -0x5366c438, 0x187da710, -0x639c9118, 0x3bbb7bdb, + 0x267809cd, 0x5918f46e, -0x6548fe14, 0x4f9aa883, + -0x6a919a1a, -0x198156, -0x4330f7df, 0x15e8e6ef, + -0x18642646, 0x6f36ce4a, -0x60f62b16, -0x4f8329d7, + -0x5b4d50cf, 0x3f23312a, -0x5a6bcf3a, -0x5d993fcb, + 0x4ebc3774, -0x7d355904, -0x6f2f4f20, -0x5827eacd, + 0x04984af1, -0x132508bf, -0x32aff181, -0x6e09d0e9, + 0x4dd68d76, -0x104fb2bd, -0x55b2ab34, -0x69fb201c, + -0x2e4a1c62, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, + 0x5eea049d, -0x73caa2ff, -0x788b8c06, 0x0b412efb, + 0x671d5ab3, -0x242dad6e, 0x105633e9, -0x29b8ec93, + -0x289e7366, -0x5ef385c9, -0x7eb71a7, 0x133c89eb, + -0x56d81132, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, + -0x2d20a664, -0xd8cc0ab, 0x14ce7918, -0x38c8408d, + -0x83215ad, -0x255a4a1, 0x3d6f14df, 0x44db8678, + -0x500c7e36, 0x68c43eb9, 0x24342c38, -0x5cbfa03e, + 0x1dc37216, -0x1ddaf344, 0x3c498b28, 0x0d9541ff, + -0x57fe8ec7, 0x0cb3de08, -0x4b1b6328, 0x56c19064, + -0x347b9e85, 0x32b670d5, 0x6c5c7448, -0x47a8bd30, + ) + private val Td4 = intArrayOf( + 0x52525252, 0x09090909, 0x6a6a6a6a, -0x2a2a2a2b, + 0x30303030, 0x36363636, -0x5a5a5a5b, 0x38383838, + -0x40404041, 0x40404040, -0x5c5c5c5d, -0x61616162, + -0x7e7e7e7f, -0xc0c0c0d, -0x28282829, -0x4040405, + 0x7c7c7c7c, -0x1c1c1c1d, 0x39393939, -0x7d7d7d7e, + -0x64646465, 0x2f2f2f2f, -0x1, -0x78787879, + 0x34343434, -0x71717172, 0x43434343, 0x44444444, + -0x3b3b3b3c, -0x21212122, -0x16161617, -0x34343435, + 0x54545454, 0x7b7b7b7b, -0x6b6b6b6c, 0x32323232, + -0x5959595a, -0x3d3d3d3e, 0x23232323, 0x3d3d3d3d, + -0x11111112, 0x4c4c4c4c, -0x6a6a6a6b, 0x0b0b0b0b, + 0x42424242, -0x5050506, -0x3c3c3c3d, 0x4e4e4e4e, + 0x08080808, 0x2e2e2e2e, -0x5e5e5e5f, 0x66666666, + 0x28282828, -0x26262627, 0x24242424, -0x4d4d4d4e, + 0x76767676, 0x5b5b5b5b, -0x5d5d5d5e, 0x49494949, + 0x6d6d6d6d, -0x74747475, -0x2e2e2e2f, 0x25252525, + 0x72727272, -0x7070708, -0x909090a, 0x64646464, + -0x7979797a, 0x68686868, -0x67676768, 0x16161616, + -0x2b2b2b2c, -0x5b5b5b5c, 0x5c5c5c5c, -0x33333334, + 0x5d5d5d5d, 0x65656565, -0x4949494a, -0x6d6d6d6e, + 0x6c6c6c6c, 0x70707070, 0x48484848, 0x50505050, + -0x2020203, -0x12121213, -0x46464647, -0x25252526, + 0x5e5e5e5e, 0x15151515, 0x46464646, 0x57575757, + -0x58585859, -0x72727273, -0x62626263, -0x7b7b7b7c, + -0x6f6f6f70, -0x27272728, -0x54545455, 0x00000000, + -0x73737374, -0x43434344, -0x2c2c2c2d, 0x0a0a0a0a, + -0x8080809, -0x1b1b1b1c, 0x58585858, 0x05050505, + -0x47474748, -0x4c4c4c4d, 0x45454545, 0x06060606, + -0x2f2f2f30, 0x2c2c2c2c, 0x1e1e1e1e, -0x70707071, + -0x35353536, 0x3f3f3f3f, 0x0f0f0f0f, 0x02020202, + -0x3e3e3e3f, -0x50505051, -0x42424243, 0x03030303, + 0x01010101, 0x13131313, -0x75757576, 0x6b6b6b6b, + 0x3a3a3a3a, -0x6e6e6e6f, 0x11111111, 0x41414141, + 0x4f4f4f4f, 0x67676767, -0x23232324, -0x15151516, + -0x68686869, -0xd0d0d0e, -0x30303031, -0x31313132, + -0xf0f0f10, -0x4b4b4b4c, -0x1919191a, 0x73737373, + -0x6969696a, -0x53535354, 0x74747474, 0x22222222, + -0x18181819, -0x52525253, 0x35353535, -0x7a7a7a7b, + -0x1d1d1d1e, -0x6060607, 0x37373737, -0x17171718, + 0x1c1c1c1c, 0x75757575, -0x20202021, 0x6e6e6e6e, + 0x47474747, -0xe0e0e0f, 0x1a1a1a1a, 0x71717171, + 0x1d1d1d1d, 0x29292929, -0x3a3a3a3b, -0x76767677, + 0x6f6f6f6f, -0x48484849, 0x62626262, 0x0e0e0e0e, + -0x55555556, 0x18181818, -0x41414142, 0x1b1b1b1b, + -0x3030304, 0x56565656, 0x3e3e3e3e, 0x4b4b4b4b, + -0x3939393a, -0x2d2d2d2e, 0x79797979, 0x20202020, + -0x65656566, -0x24242425, -0x3f3f3f40, -0x1010102, + 0x78787878, -0x32323233, 0x5a5a5a5a, -0xb0b0b0c, + 0x1f1f1f1f, -0x22222223, -0x57575758, 0x33333333, + -0x77777778, 0x07070707, -0x38383839, 0x31313131, + -0x4e4e4e4f, 0x12121212, 0x10101010, 0x59595959, + 0x27272727, -0x7f7f7f80, -0x13131314, 0x5f5f5f5f, + 0x60606060, 0x51515151, 0x7f7f7f7f, -0x56565657, + 0x19191919, -0x4a4a4a4b, 0x4a4a4a4a, 0x0d0d0d0d, + 0x2d2d2d2d, -0x1a1a1a1b, 0x7a7a7a7a, -0x60606061, + -0x6c6c6c6d, -0x36363637, -0x63636364, -0x10101011, + -0x5f5f5f60, -0x1f1f1f20, 0x3b3b3b3b, 0x4d4d4d4d, + -0x51515152, 0x2a2a2a2a, -0xa0a0a0b, -0x4f4f4f50, + -0x37373738, -0x14141415, -0x44444445, 0x3c3c3c3c, + -0x7c7c7c7d, 0x53535353, -0x66666667, 0x61616161, + 0x17171717, 0x2b2b2b2b, 0x04040404, 0x7e7e7e7e, + -0x45454546, 0x77777777, -0x2929292a, 0x26262626, + -0x1e1e1e1f, 0x69696969, 0x14141414, 0x63636363, + 0x55555555, 0x21212121, 0x0c0c0c0c, 0x7d7d7d7d, + ) + private val rcon = intArrayOf( + 0x01000000, + 0x02000000, + 0x04000000, + 0x08000000, + 0x10000000, + 0x20000000, + 0x40000000, + -0x80000000, + 0x1B000000, + 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ + ) + } +} From c11f5258e3a121cebddf9206893fdf7ac752a945 Mon Sep 17 00:00:00 2001 From: yet300 Date: Fri, 8 May 2026 17:19:07 +0400 Subject: [PATCH 05/37] Rename .java to .kt --- .../crypto/{SHA256MessageDigest.java => SHA256MessageDigest.kt} | 0 .../crypto/{SHA512MessageDigest.java => SHA512MessageDigest.kt} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/{SHA256MessageDigest.java => SHA256MessageDigest.kt} (100%) rename app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/{SHA512MessageDigest.java => SHA512MessageDigest.kt} (100%) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA256MessageDigest.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA256MessageDigest.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA256MessageDigest.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA256MessageDigest.kt diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA512MessageDigest.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA512MessageDigest.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA512MessageDigest.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA512MessageDigest.kt From 544e51ff6ba0973336f89e4b2b914679d1547458 Mon Sep 17 00:00:00 2001 From: yet300 Date: Fri, 8 May 2026 17:19:07 +0400 Subject: [PATCH 06/37] Refactor: Convert SHA-256 and SHA-512 to pure Kotlin for KMP - Translated `SHA256MessageDigest` and `SHA512MessageDigest` to Kotlin. - Removed `java.util.Arrays` and `System.arraycopy` dependencies, using Kotlin's native `.fill()` and `.copyInto()`. - Refactored legacy `while` loops into idiomatic Kotlin `for` loops. - Improved variable scoping in the `transform` function by declaring local variables at the point of use. --- .../southernstorm/crypto/RijndaelAES.java | 1099 ----------------- .../crypto/SHA256MessageDigest.kt | 403 +++--- .../crypto/SHA512MessageDigest.kt | 438 ++++--- .../southernstorm/crypto/package-info.java | 12 - 4 files changed, 400 insertions(+), 1552 deletions(-) delete mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/RijndaelAES.java delete mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/package-info.java diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/RijndaelAES.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/RijndaelAES.java deleted file mode 100644 index a34d07c2..00000000 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/RijndaelAES.java +++ /dev/null @@ -1,1099 +0,0 @@ -// This implementation is a straight C-to-Java port of the -// public domain code from the original Rijndael authors: -// http://web.cs.ucdavis.edu/~rogaway/ocb/ocb-ref/ -// The original license declaration follows (all modifications -// are released under the same terms): - -/* - * rijndael-alg-fst.c - * - * @version 3.0 (December 2000) - * - * Optimised ANSI C code for the Rijndael cipher (now AES) - * - * @author Vincent Rijmen - * @author Antoon Bosselaers - * @author Paulo Barreto - * - * This code is hereby placed in the public domain. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.bitchat.android.noise.southernstorm.crypto; - -import java.util.Arrays; - -/** - * Public domain fallback implementation of AES in ECB mode. - */ -public class RijndaelAES { - - private int[] rk; - private int Nr; - - /** - * Constructs a new key schedule object for AES encryption/decryption. - */ - public RijndaelAES() - { - rk = new int [60]; - Nr = 14; - } - - /** - * Destroys the sensitive state in this key schedule. - */ - public void destroy() { - Arrays.fill(rk, 0); - } - - private static int GETU32(byte[] buf, int offset) - { - return ((buf[offset ] & 0xFF) << 24) | - ((buf[offset + 1] & 0xFF) << 16) | - ((buf[offset + 2] & 0xFF) << 8) | - (buf[offset + 3] & 0xFF); - } - - private static void PUTU32(byte[] buf, int offset, int value) - { - buf[offset] = (byte)(value >> 24); - buf[offset + 1] = (byte)(value >> 16); - buf[offset + 2] = (byte)(value >> 8); - buf[offset + 3] = (byte)value; - } - - /** - * Expand the cipher key into the encryption key schedule. - * - * @return the number of rounds for the given cipher key size. - */ - public int setupEnc(byte[] cipherKey, int offset, int keyBits) { - int i = 0; - int temp; - - rk[0] = GETU32(cipherKey, offset ); - rk[1] = GETU32(cipherKey, offset + 4); - rk[2] = GETU32(cipherKey, offset + 8); - rk[3] = GETU32(cipherKey, offset + 12); - int rkoffset = 0; - if (keyBits == 128) { - for (;;) { - temp = rk[rkoffset + 3]; - rk[rkoffset + 4] = rk[rkoffset] ^ - (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ - (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ - (Te4[(temp ) & 0xff] & 0x0000ff00) ^ - (Te4[(temp >> 24) & 0xff] & 0x000000ff) ^ - rcon[i]; - rk[rkoffset + 5] = rk[rkoffset + 1] ^ rk[rkoffset + 4]; - rk[rkoffset + 6] = rk[rkoffset + 2] ^ rk[rkoffset + 5]; - rk[rkoffset + 7] = rk[rkoffset + 3] ^ rk[rkoffset + 6]; - if (++i == 10) { - Nr = 10; - return Nr; - } - rkoffset += 4; - } - } - rk[rkoffset + 4] = GETU32(cipherKey, offset + 16); - rk[rkoffset + 5] = GETU32(cipherKey, offset + 20); - if (keyBits == 192) { - for (;;) { - temp = rk[rkoffset + 5]; - rk[rkoffset + 6] = rk[rkoffset] ^ - (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ - (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ - (Te4[(temp ) & 0xff] & 0x0000ff00) ^ - (Te4[(temp >> 24) & 0xff] & 0x000000ff) ^ - rcon[i]; - rk[rkoffset + 7] = rk[rkoffset + 1] ^ rk[rkoffset + 6]; - rk[rkoffset + 8] = rk[rkoffset + 2] ^ rk[rkoffset + 7]; - rk[rkoffset + 9] = rk[rkoffset + 3] ^ rk[rkoffset + 8]; - if (++i == 8) { - Nr = 12; - return Nr; - } - rk[rkoffset + 10] = rk[rkoffset + 4] ^ rk[rkoffset + 9]; - rk[rkoffset + 11] = rk[rkoffset + 5] ^ rk[rkoffset + 10]; - rkoffset += 6; - } - } - rk[rkoffset + 6] = GETU32(cipherKey, offset + 24); - rk[rkoffset + 7] = GETU32(cipherKey, offset + 28); - if (keyBits == 256) { - for (;;) { - temp = rk[rkoffset + 7]; - rk[rkoffset + 8] = rk[rkoffset + 0] ^ - (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ - (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ - (Te4[(temp ) & 0xff] & 0x0000ff00) ^ - (Te4[(temp >> 24) & 0xff] & 0x000000ff) ^ - rcon[i]; - rk[rkoffset + 9] = rk[rkoffset + 1] ^ rk[rkoffset + 8]; - rk[rkoffset + 10] = rk[rkoffset + 2] ^ rk[rkoffset + 9]; - rk[rkoffset + 11] = rk[rkoffset + 3] ^ rk[rkoffset + 10]; - if (++i == 7) { - Nr = 14; - return Nr; - } - temp = rk[rkoffset + 11]; - rk[rkoffset + 12] = rk[rkoffset + 4] ^ - (Te4[(temp >> 24) & 0xff] & 0xff000000) ^ - (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(temp ) & 0xff] & 0x000000ff); - rk[rkoffset + 13] = rk[rkoffset + 5] ^ rk[rkoffset + 12]; - rk[rkoffset + 14] = rk[rkoffset + 6] ^ rk[rkoffset + 13]; - rk[rkoffset + 15] = rk[rkoffset + 7] ^ rk[rkoffset + 14]; - - rkoffset += 8; - } - } - return 0; - } - - /** - * Expand the cipher key into the decryption key schedule. - * - * @return the number of rounds for the given cipher key size. - */ - public int setupDec(byte[] cipherKey, int offset, int keyBits) { - int Nr, i, j; - int temp; - - /* expand the cipher key: */ - Nr = setupEnc(cipherKey, offset, keyBits); - /* invert the order of the round keys: */ - for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { - temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; - temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; - temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; - temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; - } - /* apply the inverse MixColumn transform to all round keys but the first and the last: */ - int rkoffset = 0; - for (i = 1; i < Nr; i++) { - rkoffset += 4; - rk[rkoffset + 0] = - Td0[Te4[(rk[rkoffset ] >> 24) & 0xff] & 0xff] ^ - Td1[Te4[(rk[rkoffset ] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[rkoffset ] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[rkoffset ] ) & 0xff] & 0xff]; - rk[rkoffset + 1] = - Td0[Te4[(rk[rkoffset + 1] >> 24) & 0xff] & 0xff] ^ - Td1[Te4[(rk[rkoffset + 1] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[rkoffset + 1] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[rkoffset + 1] ) & 0xff] & 0xff]; - rk[rkoffset + 2] = - Td0[Te4[(rk[rkoffset + 2] >> 24) & 0xff] & 0xff] ^ - Td1[Te4[(rk[rkoffset + 2] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[rkoffset + 2] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[rkoffset + 2] ) & 0xff] & 0xff]; - rk[rkoffset + 3] = - Td0[Te4[(rk[rkoffset + 3] >> 24) & 0xff] & 0xff] ^ - Td1[Te4[(rk[rkoffset + 3] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[rkoffset + 3] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[rkoffset + 3] ) & 0xff] & 0xff]; - } - this.Nr = Nr; - return Nr; - } - - public void encrypt(byte[] pt, int ptoffset, byte[] ct, int ctoffset) { - int s0, s1, s2, s3, t0, t1, t2, t3; - int r, rkoffset; - - /* - * map byte array block to cipher state - * and add initial round key: - */ - s0 = GETU32(pt, ptoffset ) ^ rk[0]; - s1 = GETU32(pt, ptoffset + 4) ^ rk[1]; - s2 = GETU32(pt, ptoffset + 8) ^ rk[2]; - s3 = GETU32(pt, ptoffset + 12) ^ rk[3]; - - /* - * Nr - 1 full rounds: - */ - r = Nr >> 1; - rkoffset = 0; - for (;;) { - t0 = - Te0[(s0 >> 24) & 0xff] ^ - Te1[(s1 >> 16) & 0xff] ^ - Te2[(s2 >> 8) & 0xff] ^ - Te3[(s3 ) & 0xff] ^ - rk[rkoffset + 4]; - t1 = - Te0[(s1 >> 24) & 0xff] ^ - Te1[(s2 >> 16) & 0xff] ^ - Te2[(s3 >> 8) & 0xff] ^ - Te3[(s0 ) & 0xff] ^ - rk[rkoffset + 5]; - t2 = - Te0[(s2 >> 24) & 0xff] ^ - Te1[(s3 >> 16) & 0xff] ^ - Te2[(s0 >> 8) & 0xff] ^ - Te3[(s1 ) & 0xff] ^ - rk[rkoffset + 6]; - t3 = - Te0[(s3 >> 24) & 0xff] ^ - Te1[(s0 >> 16) & 0xff] ^ - Te2[(s1 >> 8) & 0xff] ^ - Te3[(s2 ) & 0xff] ^ - rk[rkoffset + 7]; - - rkoffset += 8; - if (--r == 0) { - break; - } - - s0 = - Te0[(t0 >> 24) & 0xff] ^ - Te1[(t1 >> 16) & 0xff] ^ - Te2[(t2 >> 8) & 0xff] ^ - Te3[(t3 ) & 0xff] ^ - rk[rkoffset]; - s1 = - Te0[(t1 >> 24) & 0xff] ^ - Te1[(t2 >> 16) & 0xff] ^ - Te2[(t3 >> 8) & 0xff] ^ - Te3[(t0 ) & 0xff] ^ - rk[rkoffset + 1]; - s2 = - Te0[(t2 >> 24) & 0xff] ^ - Te1[(t3 >> 16) & 0xff] ^ - Te2[(t0 >> 8) & 0xff] ^ - Te3[(t1 ) & 0xff] ^ - rk[rkoffset + 2]; - s3 = - Te0[(t3 >> 24) & 0xff] ^ - Te1[(t0 >> 16) & 0xff] ^ - Te2[(t1 >> 8) & 0xff] ^ - Te3[(t2 ) & 0xff] ^ - rk[rkoffset + 3]; - } - - /* - * apply last round and - * map cipher state to byte array block: - */ - s0 = - (Te4[(t0 >> 24) & 0xff] & 0xff000000) ^ - (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t3 ) & 0xff] & 0x000000ff) ^ - rk[rkoffset]; - PUTU32(ct, ctoffset , s0); - s1 = - (Te4[(t1 >> 24) & 0xff] & 0xff000000) ^ - (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t0 ) & 0xff] & 0x000000ff) ^ - rk[rkoffset + 1]; - PUTU32(ct, ctoffset + 4, s1); - s2 = - (Te4[(t2 >> 24) & 0xff] & 0xff000000) ^ - (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t1 ) & 0xff] & 0x000000ff) ^ - rk[rkoffset + 2]; - PUTU32(ct, ctoffset + 8, s2); - s3 = - (Te4[(t3 >> 24) & 0xff] & 0xff000000) ^ - (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t2 ) & 0xff] & 0x000000ff) ^ - rk[rkoffset + 3]; - PUTU32(ct, ctoffset + 12, s3); - } - - public void decrypt(byte[] ct, int ctoffset, byte[] pt, int ptoffset) { - int s0, s1, s2, s3, t0, t1, t2, t3; - int r, rkoffset; - - /* - * map byte array block to cipher state - * and add initial round key: - */ - s0 = GETU32(ct, ctoffset ) ^ rk[0]; - s1 = GETU32(ct, ctoffset + 4) ^ rk[1]; - s2 = GETU32(ct, ctoffset + 8) ^ rk[2]; - s3 = GETU32(ct, ctoffset + 12) ^ rk[3]; - - /* - * Nr - 1 full rounds: - */ - r = Nr >> 1; - rkoffset = 0; - for (;;) { - t0 = - Td0[(s0 >> 24) & 0xff] ^ - Td1[(s3 >> 16) & 0xff] ^ - Td2[(s2 >> 8) & 0xff] ^ - Td3[(s1 ) & 0xff] ^ - rk[rkoffset + 4]; - t1 = - Td0[(s1 >> 24) & 0xff] ^ - Td1[(s0 >> 16) & 0xff] ^ - Td2[(s3 >> 8) & 0xff] ^ - Td3[(s2 ) & 0xff] ^ - rk[rkoffset + 5]; - t2 = - Td0[(s2 >> 24) & 0xff] ^ - Td1[(s1 >> 16) & 0xff] ^ - Td2[(s0 >> 8) & 0xff] ^ - Td3[(s3 ) & 0xff] ^ - rk[rkoffset + 6]; - t3 = - Td0[(s3 >> 24) & 0xff] ^ - Td1[(s2 >> 16) & 0xff] ^ - Td2[(s1 >> 8) & 0xff] ^ - Td3[(s0 ) & 0xff] ^ - rk[rkoffset + 7]; - - rkoffset += 8; - if (--r == 0) { - break; - } - - s0 = - Td0[(t0 >> 24) & 0xff] ^ - Td1[(t3 >> 16) & 0xff] ^ - Td2[(t2 >> 8) & 0xff] ^ - Td3[(t1 ) & 0xff] ^ - rk[rkoffset]; - s1 = - Td0[(t1 >> 24) & 0xff] ^ - Td1[(t0 >> 16) & 0xff] ^ - Td2[(t3 >> 8) & 0xff] ^ - Td3[(t2 ) & 0xff] ^ - rk[rkoffset + 1]; - s2 = - Td0[(t2 >> 24) & 0xff] ^ - Td1[(t1 >> 16) & 0xff] ^ - Td2[(t0 >> 8) & 0xff] ^ - Td3[(t3 ) & 0xff] ^ - rk[rkoffset + 2]; - s3 = - Td0[(t3 >> 24) & 0xff] ^ - Td1[(t2 >> 16) & 0xff] ^ - Td2[(t1 >> 8) & 0xff] ^ - Td3[(t0 ) & 0xff] ^ - rk[rkoffset + 3]; - } - - /* - * apply last round and - * map cipher state to byte array block: - */ - s0 = - (Td4[(t0 >> 24) & 0xff] & 0xff000000) ^ - (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t1 ) & 0xff] & 0x000000ff) ^ - rk[rkoffset]; - PUTU32(pt, ptoffset , s0); - s1 = - (Td4[(t1 >> 24) & 0xff] & 0xff000000) ^ - (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t2 ) & 0xff] & 0x000000ff) ^ - rk[rkoffset + 1]; - PUTU32(pt, ptoffset + 4, s1); - s2 = - (Td4[(t2 >> 24) & 0xff] & 0xff000000) ^ - (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t3 ) & 0xff] & 0x000000ff) ^ - rk[rkoffset + 2]; - PUTU32(pt, ptoffset + 8, s2); - s3 = - (Td4[(t3 >> 24) & 0xff] & 0xff000000) ^ - (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t0 ) & 0xff] & 0x000000ff) ^ - rk[rkoffset + 3]; - PUTU32(pt, ptoffset + 12, s3); - } - - private static final int[] Te0 = { - 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, - 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, - 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, - 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, - 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, - 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, - 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, - 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, - 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, - 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, - 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, - 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, - 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, - 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, - 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, - 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, - 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, - 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, - 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, - 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, - 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, - 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, - 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, - 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, - 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, - 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, - 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, - 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, - 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, - 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, - 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, - 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, - 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, - 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, - 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, - 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, - 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, - 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, - 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, - 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, - 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, - 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, - 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, - 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, - 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, - 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, - 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, - 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, - 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, - 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, - 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, - 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, - 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, - 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, - 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, - 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, - 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, - 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, - 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, - 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, - 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, - 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, - 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, - 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, - }; - private static final int[] Te1 = { - 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, - 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, - 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, - 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, - 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, - 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, - 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, - 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, - 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, - 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, - 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, - 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, - 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, - 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, - 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, - 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, - 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, - 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, - 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, - 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, - 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, - 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, - 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, - 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, - 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, - 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, - 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, - 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, - 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, - 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, - 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, - 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, - 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, - 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, - 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, - 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, - 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, - 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, - 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, - 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, - 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, - 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, - 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, - 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, - 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, - 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, - 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, - 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, - 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, - 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, - 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, - 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, - 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, - 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, - 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, - 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, - 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, - 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, - 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, - 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, - 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, - 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, - 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, - 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, - }; - private static final int[] Te2 = { - 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, - 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, - 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, - 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, - 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, - 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, - 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, - 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, - 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, - 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, - 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, - 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, - 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, - 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, - 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, - 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, - 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, - 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, - 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, - 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, - 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, - 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, - 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, - 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, - 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, - 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, - 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, - 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, - 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, - 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, - 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, - 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, - 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, - 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, - 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, - 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, - 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, - 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, - 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, - 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, - 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, - 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, - 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, - 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, - 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, - 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, - 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, - 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, - 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, - 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, - 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, - 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, - 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, - 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, - 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, - 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, - 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, - 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, - 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, - 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, - 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, - 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, - 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, - 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, - }; - private static final int[] Te3 = { - - 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, - 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, - 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, - 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, - 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, - 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, - 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, - 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, - 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, - 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, - 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, - 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, - 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, - 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, - 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, - 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, - 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, - 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, - 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, - 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, - 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, - 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, - 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, - 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, - 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, - 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, - 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, - 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, - 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, - 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, - 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, - 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, - 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, - 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, - 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, - 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, - 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, - 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, - 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, - 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, - 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, - 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, - 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, - 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, - 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, - 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, - 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, - 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, - 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, - 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, - 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, - 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, - 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, - 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, - 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, - 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, - 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, - 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, - 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, - 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, - 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, - 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, - 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, - 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, - }; - private static final int[] Te4 = { - 0x63636363, 0x7c7c7c7c, 0x77777777, 0x7b7b7b7b, - 0xf2f2f2f2, 0x6b6b6b6b, 0x6f6f6f6f, 0xc5c5c5c5, - 0x30303030, 0x01010101, 0x67676767, 0x2b2b2b2b, - 0xfefefefe, 0xd7d7d7d7, 0xabababab, 0x76767676, - 0xcacacaca, 0x82828282, 0xc9c9c9c9, 0x7d7d7d7d, - 0xfafafafa, 0x59595959, 0x47474747, 0xf0f0f0f0, - 0xadadadad, 0xd4d4d4d4, 0xa2a2a2a2, 0xafafafaf, - 0x9c9c9c9c, 0xa4a4a4a4, 0x72727272, 0xc0c0c0c0, - 0xb7b7b7b7, 0xfdfdfdfd, 0x93939393, 0x26262626, - 0x36363636, 0x3f3f3f3f, 0xf7f7f7f7, 0xcccccccc, - 0x34343434, 0xa5a5a5a5, 0xe5e5e5e5, 0xf1f1f1f1, - 0x71717171, 0xd8d8d8d8, 0x31313131, 0x15151515, - 0x04040404, 0xc7c7c7c7, 0x23232323, 0xc3c3c3c3, - 0x18181818, 0x96969696, 0x05050505, 0x9a9a9a9a, - 0x07070707, 0x12121212, 0x80808080, 0xe2e2e2e2, - 0xebebebeb, 0x27272727, 0xb2b2b2b2, 0x75757575, - 0x09090909, 0x83838383, 0x2c2c2c2c, 0x1a1a1a1a, - 0x1b1b1b1b, 0x6e6e6e6e, 0x5a5a5a5a, 0xa0a0a0a0, - 0x52525252, 0x3b3b3b3b, 0xd6d6d6d6, 0xb3b3b3b3, - 0x29292929, 0xe3e3e3e3, 0x2f2f2f2f, 0x84848484, - 0x53535353, 0xd1d1d1d1, 0x00000000, 0xedededed, - 0x20202020, 0xfcfcfcfc, 0xb1b1b1b1, 0x5b5b5b5b, - 0x6a6a6a6a, 0xcbcbcbcb, 0xbebebebe, 0x39393939, - 0x4a4a4a4a, 0x4c4c4c4c, 0x58585858, 0xcfcfcfcf, - 0xd0d0d0d0, 0xefefefef, 0xaaaaaaaa, 0xfbfbfbfb, - 0x43434343, 0x4d4d4d4d, 0x33333333, 0x85858585, - 0x45454545, 0xf9f9f9f9, 0x02020202, 0x7f7f7f7f, - 0x50505050, 0x3c3c3c3c, 0x9f9f9f9f, 0xa8a8a8a8, - 0x51515151, 0xa3a3a3a3, 0x40404040, 0x8f8f8f8f, - 0x92929292, 0x9d9d9d9d, 0x38383838, 0xf5f5f5f5, - 0xbcbcbcbc, 0xb6b6b6b6, 0xdadadada, 0x21212121, - 0x10101010, 0xffffffff, 0xf3f3f3f3, 0xd2d2d2d2, - 0xcdcdcdcd, 0x0c0c0c0c, 0x13131313, 0xecececec, - 0x5f5f5f5f, 0x97979797, 0x44444444, 0x17171717, - 0xc4c4c4c4, 0xa7a7a7a7, 0x7e7e7e7e, 0x3d3d3d3d, - 0x64646464, 0x5d5d5d5d, 0x19191919, 0x73737373, - 0x60606060, 0x81818181, 0x4f4f4f4f, 0xdcdcdcdc, - 0x22222222, 0x2a2a2a2a, 0x90909090, 0x88888888, - 0x46464646, 0xeeeeeeee, 0xb8b8b8b8, 0x14141414, - 0xdededede, 0x5e5e5e5e, 0x0b0b0b0b, 0xdbdbdbdb, - 0xe0e0e0e0, 0x32323232, 0x3a3a3a3a, 0x0a0a0a0a, - 0x49494949, 0x06060606, 0x24242424, 0x5c5c5c5c, - 0xc2c2c2c2, 0xd3d3d3d3, 0xacacacac, 0x62626262, - 0x91919191, 0x95959595, 0xe4e4e4e4, 0x79797979, - 0xe7e7e7e7, 0xc8c8c8c8, 0x37373737, 0x6d6d6d6d, - 0x8d8d8d8d, 0xd5d5d5d5, 0x4e4e4e4e, 0xa9a9a9a9, - 0x6c6c6c6c, 0x56565656, 0xf4f4f4f4, 0xeaeaeaea, - 0x65656565, 0x7a7a7a7a, 0xaeaeaeae, 0x08080808, - 0xbabababa, 0x78787878, 0x25252525, 0x2e2e2e2e, - 0x1c1c1c1c, 0xa6a6a6a6, 0xb4b4b4b4, 0xc6c6c6c6, - 0xe8e8e8e8, 0xdddddddd, 0x74747474, 0x1f1f1f1f, - 0x4b4b4b4b, 0xbdbdbdbd, 0x8b8b8b8b, 0x8a8a8a8a, - 0x70707070, 0x3e3e3e3e, 0xb5b5b5b5, 0x66666666, - 0x48484848, 0x03030303, 0xf6f6f6f6, 0x0e0e0e0e, - 0x61616161, 0x35353535, 0x57575757, 0xb9b9b9b9, - 0x86868686, 0xc1c1c1c1, 0x1d1d1d1d, 0x9e9e9e9e, - 0xe1e1e1e1, 0xf8f8f8f8, 0x98989898, 0x11111111, - 0x69696969, 0xd9d9d9d9, 0x8e8e8e8e, 0x94949494, - 0x9b9b9b9b, 0x1e1e1e1e, 0x87878787, 0xe9e9e9e9, - 0xcececece, 0x55555555, 0x28282828, 0xdfdfdfdf, - 0x8c8c8c8c, 0xa1a1a1a1, 0x89898989, 0x0d0d0d0d, - 0xbfbfbfbf, 0xe6e6e6e6, 0x42424242, 0x68686868, - 0x41414141, 0x99999999, 0x2d2d2d2d, 0x0f0f0f0f, - 0xb0b0b0b0, 0x54545454, 0xbbbbbbbb, 0x16161616, - }; - private static final int[] Td0 = { - 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, - 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, - 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, - 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, - 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, - 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, - 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, - 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, - 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, - 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, - 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, - 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, - 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, - 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, - 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, - 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, - 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, - 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, - 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, - 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, - 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, - 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, - 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, - 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, - 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, - 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, - 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, - 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, - 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, - 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, - 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, - 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, - 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, - 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, - 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, - 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, - 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, - 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, - 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, - 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, - 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, - 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, - 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, - 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, - 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, - 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, - 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, - 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, - 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, - 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, - 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, - 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, - 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, - 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, - 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, - 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, - 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, - 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, - 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, - 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, - 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, - 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, - 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, - 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742, - }; - private static final int[] Td1 = { - 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, - 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, - 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, - 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, - 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, - 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, - 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, - 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, - 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, - 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, - 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, - 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, - 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, - 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, - 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, - 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, - 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, - 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, - 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, - 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, - 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, - 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, - 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, - 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, - 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, - 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, - 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, - 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, - 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, - 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, - 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, - 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, - 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, - 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, - 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, - 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, - 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, - 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, - 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, - 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, - 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, - 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, - 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, - 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, - 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, - 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, - 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, - 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, - 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, - 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, - 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, - 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, - 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, - 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, - 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, - 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, - 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, - 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, - 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, - 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, - 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, - 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, - 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, - 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857, - }; - private static final int[] Td2 = { - 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, - 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, - 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, - 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, - 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, - 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, - 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, - 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, - 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, - 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, - 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, - 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, - 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, - 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, - 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, - 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, - 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, - 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, - 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, - 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, - - 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, - 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, - 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, - 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, - 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, - 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, - 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, - 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, - 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, - 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, - 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, - 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, - 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, - 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, - 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, - 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, - 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, - 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, - 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, - 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, - 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, - 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, - 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, - 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, - 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, - 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, - 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, - 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, - 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, - 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, - 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, - 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, - 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, - 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, - 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, - 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, - 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, - 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, - 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, - 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, - 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, - 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, - 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, - 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8, - }; - private static final int[] Td3 = { - 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, - 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, - 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, - 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, - 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, - 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, - 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, - 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, - 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, - 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, - 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, - 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, - 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, - 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, - 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, - 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, - 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, - 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, - 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, - 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, - 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, - 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, - 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, - 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, - 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, - 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, - 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, - 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, - 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, - 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, - 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, - 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, - 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, - 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, - 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, - 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, - 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, - 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, - 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, - 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, - 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, - 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, - 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, - 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, - 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, - 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, - 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, - 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, - 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, - 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, - 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, - 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, - 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, - 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, - 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, - 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, - 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, - 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, - 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, - 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, - 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, - 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, - 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, - 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0, - }; - private static final int[] Td4 = { - 0x52525252, 0x09090909, 0x6a6a6a6a, 0xd5d5d5d5, - 0x30303030, 0x36363636, 0xa5a5a5a5, 0x38383838, - 0xbfbfbfbf, 0x40404040, 0xa3a3a3a3, 0x9e9e9e9e, - 0x81818181, 0xf3f3f3f3, 0xd7d7d7d7, 0xfbfbfbfb, - 0x7c7c7c7c, 0xe3e3e3e3, 0x39393939, 0x82828282, - 0x9b9b9b9b, 0x2f2f2f2f, 0xffffffff, 0x87878787, - 0x34343434, 0x8e8e8e8e, 0x43434343, 0x44444444, - 0xc4c4c4c4, 0xdededede, 0xe9e9e9e9, 0xcbcbcbcb, - 0x54545454, 0x7b7b7b7b, 0x94949494, 0x32323232, - 0xa6a6a6a6, 0xc2c2c2c2, 0x23232323, 0x3d3d3d3d, - 0xeeeeeeee, 0x4c4c4c4c, 0x95959595, 0x0b0b0b0b, - 0x42424242, 0xfafafafa, 0xc3c3c3c3, 0x4e4e4e4e, - 0x08080808, 0x2e2e2e2e, 0xa1a1a1a1, 0x66666666, - 0x28282828, 0xd9d9d9d9, 0x24242424, 0xb2b2b2b2, - 0x76767676, 0x5b5b5b5b, 0xa2a2a2a2, 0x49494949, - 0x6d6d6d6d, 0x8b8b8b8b, 0xd1d1d1d1, 0x25252525, - 0x72727272, 0xf8f8f8f8, 0xf6f6f6f6, 0x64646464, - 0x86868686, 0x68686868, 0x98989898, 0x16161616, - 0xd4d4d4d4, 0xa4a4a4a4, 0x5c5c5c5c, 0xcccccccc, - 0x5d5d5d5d, 0x65656565, 0xb6b6b6b6, 0x92929292, - 0x6c6c6c6c, 0x70707070, 0x48484848, 0x50505050, - 0xfdfdfdfd, 0xedededed, 0xb9b9b9b9, 0xdadadada, - 0x5e5e5e5e, 0x15151515, 0x46464646, 0x57575757, - 0xa7a7a7a7, 0x8d8d8d8d, 0x9d9d9d9d, 0x84848484, - 0x90909090, 0xd8d8d8d8, 0xabababab, 0x00000000, - 0x8c8c8c8c, 0xbcbcbcbc, 0xd3d3d3d3, 0x0a0a0a0a, - 0xf7f7f7f7, 0xe4e4e4e4, 0x58585858, 0x05050505, - 0xb8b8b8b8, 0xb3b3b3b3, 0x45454545, 0x06060606, - 0xd0d0d0d0, 0x2c2c2c2c, 0x1e1e1e1e, 0x8f8f8f8f, - 0xcacacaca, 0x3f3f3f3f, 0x0f0f0f0f, 0x02020202, - 0xc1c1c1c1, 0xafafafaf, 0xbdbdbdbd, 0x03030303, - 0x01010101, 0x13131313, 0x8a8a8a8a, 0x6b6b6b6b, - 0x3a3a3a3a, 0x91919191, 0x11111111, 0x41414141, - 0x4f4f4f4f, 0x67676767, 0xdcdcdcdc, 0xeaeaeaea, - 0x97979797, 0xf2f2f2f2, 0xcfcfcfcf, 0xcececece, - 0xf0f0f0f0, 0xb4b4b4b4, 0xe6e6e6e6, 0x73737373, - 0x96969696, 0xacacacac, 0x74747474, 0x22222222, - 0xe7e7e7e7, 0xadadadad, 0x35353535, 0x85858585, - 0xe2e2e2e2, 0xf9f9f9f9, 0x37373737, 0xe8e8e8e8, - 0x1c1c1c1c, 0x75757575, 0xdfdfdfdf, 0x6e6e6e6e, - 0x47474747, 0xf1f1f1f1, 0x1a1a1a1a, 0x71717171, - 0x1d1d1d1d, 0x29292929, 0xc5c5c5c5, 0x89898989, - 0x6f6f6f6f, 0xb7b7b7b7, 0x62626262, 0x0e0e0e0e, - 0xaaaaaaaa, 0x18181818, 0xbebebebe, 0x1b1b1b1b, - 0xfcfcfcfc, 0x56565656, 0x3e3e3e3e, 0x4b4b4b4b, - 0xc6c6c6c6, 0xd2d2d2d2, 0x79797979, 0x20202020, - 0x9a9a9a9a, 0xdbdbdbdb, 0xc0c0c0c0, 0xfefefefe, - 0x78787878, 0xcdcdcdcd, 0x5a5a5a5a, 0xf4f4f4f4, - 0x1f1f1f1f, 0xdddddddd, 0xa8a8a8a8, 0x33333333, - 0x88888888, 0x07070707, 0xc7c7c7c7, 0x31313131, - 0xb1b1b1b1, 0x12121212, 0x10101010, 0x59595959, - 0x27272727, 0x80808080, 0xecececec, 0x5f5f5f5f, - 0x60606060, 0x51515151, 0x7f7f7f7f, 0xa9a9a9a9, - 0x19191919, 0xb5b5b5b5, 0x4a4a4a4a, 0x0d0d0d0d, - 0x2d2d2d2d, 0xe5e5e5e5, 0x7a7a7a7a, 0x9f9f9f9f, - 0x93939393, 0xc9c9c9c9, 0x9c9c9c9c, 0xefefefef, - 0xa0a0a0a0, 0xe0e0e0e0, 0x3b3b3b3b, 0x4d4d4d4d, - 0xaeaeaeae, 0x2a2a2a2a, 0xf5f5f5f5, 0xb0b0b0b0, - 0xc8c8c8c8, 0xebebebeb, 0xbbbbbbbb, 0x3c3c3c3c, - 0x83838383, 0x53535353, 0x99999999, 0x61616161, - 0x17171717, 0x2b2b2b2b, 0x04040404, 0x7e7e7e7e, - 0xbabababa, 0x77777777, 0xd6d6d6d6, 0x26262626, - 0xe1e1e1e1, 0x69696969, 0x14141414, 0x63636363, - 0x55555555, 0x21212121, 0x0c0c0c0c, 0x7d7d7d7d, - }; - private static final int[] rcon = { - 0x01000000, 0x02000000, 0x04000000, 0x08000000, - 0x10000000, 0x20000000, 0x40000000, 0x80000000, - 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ - }; -} diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA256MessageDigest.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA256MessageDigest.kt index 0ef581df..7ce36212 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA256MessageDigest.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA256MessageDigest.kt @@ -19,226 +19,207 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ +package com.bitchat.android.noise.southernstorm.crypto -package com.bitchat.android.noise.southernstorm.crypto; +import com.bitchat.android.noise.southernstorm.protocol.Destroyable +import java.security.DigestException +import java.security.MessageDigest -import java.security.DigestException; -import java.security.MessageDigest; -import java.util.Arrays; - -import com.bitchat.android.noise.southernstorm.protocol.Destroyable; /** * Fallback implementation of SHA256. */ -public class SHA256MessageDigest extends MessageDigest implements Destroyable { +class SHA256MessageDigest : MessageDigest("SHA-256"), Destroyable { + private val h = IntArray(8) + private val block = ByteArray(64) + private val w = IntArray(64) + private var length: Long = 0 + private var posn = 0 - private int[] h; - private byte[] block; - private int[] w; - private long length; - private int posn; - - /** - * Constructs a new SHA256 message digest object. - */ - public SHA256MessageDigest() { - super("SHA-256"); - h = new int [8]; - block = new byte [64]; - w = new int [64]; - engineReset(); - } - - @Override - public void destroy() { - Arrays.fill(h, (int)0); - Arrays.fill(block, (byte)0); - Arrays.fill(w, (int)0); - } - - private static void writeBE32(byte[] buf, int offset, int value) - { - buf[offset] = (byte)(value >> 24); - buf[offset + 1] = (byte)(value >> 16); - buf[offset + 2] = (byte)(value >> 8); - buf[offset + 3] = (byte)value; - } - - @Override - protected byte[] engineDigest() { - byte[] digest = new byte [32]; - try { - engineDigest(digest, 0, 32); - } catch (DigestException e) { - // Shouldn't happen, but just in case. - Arrays.fill(digest, (byte)0); - } - return digest; - } - - @Override - protected int engineDigest(byte[] buf, int offset, int len) throws DigestException - { - if (len < 32) - throw new DigestException("Invalid digest length for SHA256"); - if (posn <= (64 - 9)) { - block[posn] = (byte)0x80; - Arrays.fill(block, posn + 1, 64 - 8, (byte)0); - } else { - block[posn] = (byte)0x80; - Arrays.fill(block, posn + 1, 64, (byte)0); - transform(block, 0); - Arrays.fill(block, 0, 64 - 8, (byte)0); - } - writeBE32(block, 64 - 8, (int)(length >> 32)); - writeBE32(block, 64 - 4, (int)length); - transform(block, 0); - posn = 0; - for (int index = 0; index < 8; ++index) - writeBE32(buf, offset + index * 4, h[index]); - return 32; - } - - @Override - protected int engineGetDigestLength() { - return 32; - } - - @Override - protected void engineReset() { - h[0] = 0x6A09E667; - h[1] = 0xBB67AE85; - h[2] = 0x3C6EF372; - h[3] = 0xA54FF53A; - h[4] = 0x510E527F; - h[5] = 0x9B05688C; - h[6] = 0x1F83D9AB; - h[7] = 0x5BE0CD19; - length = 0; - posn = 0; - } - - @Override - protected void engineUpdate(byte input) { - block[posn++] = input; - length += 8; - if (posn >= 64) { - transform(block, 0); - posn = 0; - } - } - - @Override - protected void engineUpdate(byte[] input, int offset, int len) { - while (len > 0) { - if (posn == 0 && len >= 64) { - transform(input, offset); - offset += 64; - len -= 64; - length += 64 * 8; - } else { - int temp = 64 - posn; - if (temp > len) - temp = len; - System.arraycopy(input, offset, block, posn, temp); - posn += temp; - length += temp * 8; - if (posn >= 64) { - transform(block, 0); - posn = 0; - } - offset += temp; - len -= temp; - } - } - } - - private static final int[] k = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 - }; - - private static int rightRotate(int value, int n) - { - return (value >>> n) | (value << (32 - n)); + init { + engineReset() } - private void transform(byte[] m, int offset) - { - int a, b, c, d, e, f, g, h; - int temp1, temp2; - int index; + override fun destroy() { + h.fill(0) + block.fill(0.toByte()) + w.fill(0) + } - // Initialize working variables to the current hash value. - a = this.h[0]; - b = this.h[1]; - c = this.h[2]; - d = this.h[3]; - e = this.h[4]; - f = this.h[5]; - g = this.h[6]; - h = this.h[7]; - - // Convert the 16 input message words from big endian to host byte order. - for (index = 0; index < 16; ++index) { - w[index] = ((m[offset] & 0xFF) << 24) | - ((m[offset + 1] & 0xFF) << 16) | - ((m[offset + 2] & 0xFF) << 8) | - (m[offset + 3] & 0xFF); - offset += 4; - } - - // Extend the first 16 words to 64. - for (index = 16; index < 64; ++index) { - w[index] = w[index - 16] + w[index - 7] + - (rightRotate(w[index - 15], 7) ^ - rightRotate(w[index - 15], 18) ^ - (w[index - 15] >>> 3)) + - (rightRotate(w[index - 2], 17) ^ - rightRotate(w[index - 2], 19) ^ - (w[index - 2] >>> 10)); - } - - // Compression function main loop. - for (index = 0; index < 64; ++index) { - temp1 = (h) + k[index] + w[index] + - (rightRotate((e), 6) ^ rightRotate((e), 11) ^ rightRotate((e), 25)) + - (((e) & (f)) ^ ((~(e)) & (g))); - temp2 = (rightRotate((a), 2) ^ rightRotate((a), 13) ^ rightRotate((a), 22)) + - (((a) & (b)) ^ ((a) & (c)) ^ ((b) & (c))); - h = g; - g = f; - f = e; - e = d + temp1; - d = c; - c = b; - b = a; - a = temp1 + temp2; - } + override fun engineDigest(): ByteArray { + val digest = ByteArray(32) + try { + engineDigest(digest, 0, 32) + } catch (e: DigestException) { + // Shouldn't happen, but just in case. + digest.fill(0.toByte()) + } + return digest + } - // Add the compressed chunk to the current hash value. - this.h[0] += a; - this.h[1] += b; - this.h[2] += c; - this.h[3] += d; - this.h[4] += e; - this.h[5] += f; - this.h[6] += g; - this.h[7] += h; - } + @Throws(DigestException::class) + override fun engineDigest(buf: ByteArray, offset: Int, len: Int): Int { + if (len < 32) throw DigestException("Invalid digest length for SHA256") + if (posn <= (64 - 9)) { + block[posn] = 0x80.toByte() + block.fill(0.toByte(), posn + 1, 64 - 8) + } else { + block[posn] = 0x80.toByte() + block.fill(0.toByte(), posn + 1, 64) + transform(block, 0) + block.fill(0.toByte(), 0, 64 - 8) + } + writeBE32(block, 64 - 8, (length shr 32).toInt()) + writeBE32(block, 64 - 4, length.toInt()) + transform(block, 0) + posn = 0 + for (index in 0..7) writeBE32(buf, offset + index * 4, h[index]) + return 32 + } + + override fun engineGetDigestLength(): Int { + return 32 + } + + override fun engineReset() { + h[0] = 0x6A09E667 + h[1] = -0x4498517b + h[2] = 0x3C6EF372 + h[3] = -0x5ab00ac6 + h[4] = 0x510E527F + h[5] = -0x64fa9774 + h[6] = 0x1F83D9AB + h[7] = 0x5BE0CD19 + length = 0 + posn = 0 + } + + override fun engineUpdate(input: Byte) { + block[posn++] = input + length += 8 + if (posn >= 64) { + transform(block, 0) + posn = 0 + } + } + + override fun engineUpdate(input: ByteArray, offset: Int, len: Int) { + var offset = offset + var len = len + while (len > 0) { + if (posn == 0 && len >= 64) { + transform(input, offset) + offset += 64 + len -= 64 + length += (64 * 8).toLong() + } else { + var temp = 64 - posn + if (temp > len) temp = len + System.arraycopy(input, offset, block, posn, temp) + posn += temp + length += (temp * 8).toLong() + if (posn >= 64) { + transform(block, 0) + posn = 0 + } + offset += temp + len -= temp + } + } + } + + private fun transform(m: ByteArray, offset: Int) { + var currentOffset = offset + + // Initialize working variables to the current hash value. + var a = this.h[0] + var b = this.h[1] + var c = this.h[2] + var d = this.h[3] + var e = this.h[4] + var f = this.h[5] + var g = this.h[6] + var h = this.h[7] + + // Convert the 16 input message words from big endian to host byte order. + for (index in 0 until 16) { + w[index] = ((m[currentOffset].toInt() and 0xFF) shl 24) or + ((m[currentOffset + 1].toInt() and 0xFF) shl 16) or + ((m[currentOffset + 2].toInt() and 0xFF) shl 8) or + (m[currentOffset + 3].toInt() and 0xFF) + currentOffset += 4 + } + + + // Extend the first 16 words to 64. + for (index in 16 until 64) { + w[index] = w[index - 16] + w[index - 7] + + (rightRotate(w[index - 15], 7) xor + rightRotate(w[index - 15], 18) xor + (w[index - 15] ushr 3)) + + (rightRotate(w[index - 2], 17) xor + rightRotate(w[index - 2], 19) xor + (w[index - 2] ushr 10)) + } + + + // Compression function main loop. + for (index in 0 until 64) { + val temp1 = h + k[index] + w[index] + + (rightRotate(e, 6) xor rightRotate(e, 11) xor rightRotate(e, 25)) + + ((e and f) xor (e.inv() and g)) + val temp2 = (rightRotate(a, 2) xor rightRotate(a, 13) xor rightRotate(a, 22)) + + ((a and b) xor (a and c) xor (b and c)) + h = g + g = f + f = e + e = d + temp1 + d = c + c = b + b = a + a = temp1 + temp2 + } + + // Add the compressed chunk to the current hash value. + this.h[0] += a + this.h[1] += b + this.h[2] += c + this.h[3] += d + this.h[4] += e + this.h[5] += f + this.h[6] += g + this.h[7] += h + } + + companion object { + private fun writeBE32(buf: ByteArray, offset: Int, value: Int) { + buf[offset] = (value shr 24).toByte() + buf[offset + 1] = (value shr 16).toByte() + buf[offset + 2] = (value shr 8).toByte() + buf[offset + 3] = value.toByte() + } + + private val k = intArrayOf( + 0x428a2f98, 0x71374491, -0x4a3f0431, -0x164a245b, + 0x3956c25b, 0x59f111f1, -0x6dc07d5c, -0x54e3a12b, + -0x27f85568, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, -0x7f214e02, -0x6423f959, -0x3e640e8c, + -0x1b64963f, -0x1041b87a, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + -0x67c1aeae, -0x57ce3993, -0x4ffcd838, -0x40a68039, + -0x391ff40d, -0x2a586eb9, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, -0x7e3d36d2, -0x6d8dd37b, + -0x5d40175f, -0x57e599b5, -0x3db47490, -0x3893ae5d, + -0x2e6d17e7, -0x2966f9dc, -0xbf1ca7b, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, -0x7b3787ec, -0x7338fdf8, + -0x6f410006, -0x5baf9315, -0x41065c09, -0x398e870e + ) + + private fun rightRotate(value: Int, n: Int): Int { + return (value ushr n) or (value shl (32 - n)) + } + } } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA512MessageDigest.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA512MessageDigest.kt index e1f0fcdf..1347a033 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA512MessageDigest.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/SHA512MessageDigest.kt @@ -19,14 +19,11 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ +package com.bitchat.android.noise.southernstorm.crypto -package com.bitchat.android.noise.southernstorm.crypto; - -import java.security.DigestException; -import java.security.MessageDigest; -import java.util.Arrays; - -import com.bitchat.android.noise.southernstorm.protocol.Destroyable; +import com.bitchat.android.noise.southernstorm.protocol.Destroyable +import java.security.DigestException +import java.security.MessageDigest /** * Fallback implementation of SHA512. @@ -34,232 +31,213 @@ import com.bitchat.android.noise.southernstorm.protocol.Destroyable; * Note: This implementation is limited to a maximum 2^56 - 1 bytes of input. * That is, we don't bother trying to implement 128-bit length values. */ -public class SHA512MessageDigest extends MessageDigest implements Destroyable { +class SHA512MessageDigest : MessageDigest("SHA-512"), Destroyable { + private val h = LongArray(8) + private val block = ByteArray(128) + private val w = LongArray(80) + private var length: Long = 0 + private var posn = 0 - private long[] h; - private byte[] block; - private long[] w; - private long length; - private int posn; - - /** - * Constructs a new SHA512 message digest object. - */ - public SHA512MessageDigest() { - super("SHA-512"); - h = new long [8]; - block = new byte [128]; - w = new long [80]; - engineReset(); - } - - @Override - public void destroy() { - Arrays.fill(h, (long)0); - Arrays.fill(block, (byte)0); - Arrays.fill(w, (long)0); - } - - private static void writeBE64(byte[] buf, int offset, long value) - { - buf[offset] = (byte)(value >> 56); - buf[offset + 1] = (byte)(value >> 48); - buf[offset + 2] = (byte)(value >> 40); - buf[offset + 3] = (byte)(value >> 32); - buf[offset + 4] = (byte)(value >> 24); - buf[offset + 5] = (byte)(value >> 16); - buf[offset + 6] = (byte)(value >> 8); - buf[offset + 7] = (byte)value; - } - - @Override - protected byte[] engineDigest() { - byte[] digest = new byte [64]; - try { - engineDigest(digest, 0, 64); - } catch (DigestException e) { - // Shouldn't happen, but just in case. - Arrays.fill(digest, (byte)0); - } - return digest; - } - - @Override - protected int engineDigest(byte[] buf, int offset, int len) throws DigestException - { - if (len < 64) - throw new DigestException("Invalid digest length for SHA512"); - if (posn <= (128 - 17)) { - block[posn] = (byte)0x80; - Arrays.fill(block, posn + 1, 128 - 8, (byte)0); - } else { - block[posn] = (byte)0x80; - Arrays.fill(block, posn + 1, 128, (byte)0); - transform(block, 0); - Arrays.fill(block, 0, 128 - 8, (byte)0); - } - writeBE64(block, 128 - 8, length); - transform(block, 0); - posn = 0; - for (int index = 0; index < 8; ++index) - writeBE64(buf, offset + index * 8, h[index]); - return 64; - } - - @Override - protected int engineGetDigestLength() { - return 64; - } - - @Override - protected void engineReset() { - h[0] = 0x6a09e667f3bcc908L; - h[1] = 0xbb67ae8584caa73bL; - h[2] = 0x3c6ef372fe94f82bL; - h[3] = 0xa54ff53a5f1d36f1L; - h[4] = 0x510e527fade682d1L; - h[5] = 0x9b05688c2b3e6c1fL; - h[6] = 0x1f83d9abfb41bd6bL; - h[7] = 0x5be0cd19137e2179L; - length = 0; - posn = 0; - } - - @Override - protected void engineUpdate(byte input) { - block[posn++] = input; - length += 8; - if (posn >= 128) { - transform(block, 0); - posn = 0; - } - } - - @Override - protected void engineUpdate(byte[] input, int offset, int len) { - while (len > 0) { - if (posn == 0 && len >= 128) { - transform(input, offset); - offset += 128; - len -= 128; - length += 128 * 8; - } else { - int temp = 128 - posn; - if (temp > len) - temp = len; - System.arraycopy(input, offset, block, posn, temp); - posn += temp; - length += temp * 8; - if (posn >= 128) { - transform(block, 0); - posn = 0; - } - offset += temp; - len -= temp; - } - } - } - - private static final long[] k = { - 0x428A2F98D728AE22L, 0x7137449123EF65CDL, 0xB5C0FBCFEC4D3B2FL, - 0xE9B5DBA58189DBBCL, 0x3956C25BF348B538L, 0x59F111F1B605D019L, - 0x923F82A4AF194F9BL, 0xAB1C5ED5DA6D8118L, 0xD807AA98A3030242L, - 0x12835B0145706FBEL, 0x243185BE4EE4B28CL, 0x550C7DC3D5FFB4E2L, - 0x72BE5D74F27B896FL, 0x80DEB1FE3B1696B1L, 0x9BDC06A725C71235L, - 0xC19BF174CF692694L, 0xE49B69C19EF14AD2L, 0xEFBE4786384F25E3L, - 0x0FC19DC68B8CD5B5L, 0x240CA1CC77AC9C65L, 0x2DE92C6F592B0275L, - 0x4A7484AA6EA6E483L, 0x5CB0A9DCBD41FBD4L, 0x76F988DA831153B5L, - 0x983E5152EE66DFABL, 0xA831C66D2DB43210L, 0xB00327C898FB213FL, - 0xBF597FC7BEEF0EE4L, 0xC6E00BF33DA88FC2L, 0xD5A79147930AA725L, - 0x06CA6351E003826FL, 0x142929670A0E6E70L, 0x27B70A8546D22FFCL, - 0x2E1B21385C26C926L, 0x4D2C6DFC5AC42AEDL, 0x53380D139D95B3DFL, - 0x650A73548BAF63DEL, 0x766A0ABB3C77B2A8L, 0x81C2C92E47EDAEE6L, - 0x92722C851482353BL, 0xA2BFE8A14CF10364L, 0xA81A664BBC423001L, - 0xC24B8B70D0F89791L, 0xC76C51A30654BE30L, 0xD192E819D6EF5218L, - 0xD69906245565A910L, 0xF40E35855771202AL, 0x106AA07032BBD1B8L, - 0x19A4C116B8D2D0C8L, 0x1E376C085141AB53L, 0x2748774CDF8EEB99L, - 0x34B0BCB5E19B48A8L, 0x391C0CB3C5C95A63L, 0x4ED8AA4AE3418ACBL, - 0x5B9CCA4F7763E373L, 0x682E6FF3D6B2B8A3L, 0x748F82EE5DEFB2FCL, - 0x78A5636F43172F60L, 0x84C87814A1F0AB72L, 0x8CC702081A6439ECL, - 0x90BEFFFA23631E28L, 0xA4506CEBDE82BDE9L, 0xBEF9A3F7B2C67915L, - 0xC67178F2E372532BL, 0xCA273ECEEA26619CL, 0xD186B8C721C0C207L, - 0xEADA7DD6CDE0EB1EL, 0xF57D4F7FEE6ED178L, 0x06F067AA72176FBAL, - 0x0A637DC5A2C898A6L, 0x113F9804BEF90DAEL, 0x1B710B35131C471BL, - 0x28DB77F523047D84L, 0x32CAAB7B40C72493L, 0x3C9EBE0A15C9BEBCL, - 0x431D67C49C100D4CL, 0x4CC5D4BECB3E42B6L, 0x597F299CFC657E2AL, - 0x5FCB6FAB3AD6FAECL, 0x6C44198C4A475817L - }; - - private static long rightRotate(long value, int n) - { - return (value >>> n) | (value << (64 - n)); + init { + engineReset() } - private void transform(byte[] m, int offset) - { - long a, b, c, d, e, f, g, h; - long temp1, temp2; - int index; + override fun destroy() { + h.fill(0L) + block.fill(0.toByte()) + w.fill(0L) + } - // Initialize working variables to the current hash value. - a = this.h[0]; - b = this.h[1]; - c = this.h[2]; - d = this.h[3]; - e = this.h[4]; - f = this.h[5]; - g = this.h[6]; - h = this.h[7]; - - // Convert the 16 input message words from big endian to host byte order. - for (index = 0; index < 16; ++index) { - w[index] = ((m[offset] & 0xFFL) << 56) | - ((m[offset + 1] & 0xFFL) << 48) | - ((m[offset + 2] & 0xFFL) << 40) | - ((m[offset + 3] & 0xFFL) << 32) | - ((m[offset + 4] & 0xFFL) << 24) | - ((m[offset + 5] & 0xFFL) << 16) | - ((m[offset + 6] & 0xFFL) << 8) | - (m[offset + 7] & 0xFFL); - offset += 8; - } - - // Extend the first 16 words to 80. - for (index = 16; index < 80; ++index) { - w[index] = w[index - 16] + w[index - 7] + - (rightRotate(w[index - 15], 1) ^ - rightRotate(w[index - 15], 8) ^ - (w[index - 15] >>> 7)) + - (rightRotate(w[index - 2], 19) ^ - rightRotate(w[index - 2], 61) ^ - (w[index - 2] >>> 6)); - } - - // Compression function main loop. - for (index = 0; index < 80; ++index) { - temp1 = (h) + k[index] + w[index] + - (rightRotate((e), 14) ^ rightRotate((e), 18) ^ rightRotate((e), 41)) + - (((e) & (f)) ^ ((~(e)) & (g))); - temp2 = (rightRotate((a), 28) ^ rightRotate((a), 34) ^ rightRotate((a), 39)) + - (((a) & (b)) ^ ((a) & (c)) ^ ((b) & (c))); - h = g; - g = f; - f = e; - e = d + temp1; - d = c; - c = b; - b = a; - a = temp1 + temp2; - } - // Add the compressed chunk to the current hash value. - this.h[0] += a; - this.h[1] += b; - this.h[2] += c; - this.h[3] += d; - this.h[4] += e; - this.h[5] += f; - this.h[6] += g; - this.h[7] += h; - } + override fun engineDigest(): ByteArray { + val digest = ByteArray(64) + try { + engineDigest(digest, 0, 64) + } catch (e: DigestException) { + // Shouldn't happen, but just in case. + digest.fill(0.toByte()) + } + return digest + } + + @Throws(DigestException::class) + override fun engineDigest(buf: ByteArray, offset: Int, len: Int): Int { + if (len < 64) throw DigestException("Invalid digest length for SHA512") + if (posn <= (128 - 17)) { + block[posn] = 0x80.toByte() + block.fill(0.toByte(), posn + 1, 128 - 8) + } else { + block[posn] = 0x80.toByte() + block.fill(0.toByte(), posn + 1, 128) + transform(block, 0) + block.fill(0.toByte(), 0, 128 - 8) + } + writeBE64(block, 128 - 8, length) + transform(block, 0) + posn = 0 + for (index in 0..7) writeBE64(buf, offset + index * 8, h[index]) + return 64 + } + + override fun engineGetDigestLength(): Int = 64 + + + override fun engineReset() { + h[0] = 0x6a09e667f3bcc908L + h[1] = -0x4498517a7b3558c5L + h[2] = 0x3c6ef372fe94f82bL + h[3] = -0x5ab00ac5a0e2c90fL + h[4] = 0x510e527fade682d1L + h[5] = -0x64fa9773d4c193e1L + h[6] = 0x1f83d9abfb41bd6bL + h[7] = 0x5be0cd19137e2179L + length = 0 + posn = 0 + } + + override fun engineUpdate(input: Byte) { + block[posn++] = input + length += 8 + if (posn >= 128) { + transform(block, 0) + posn = 0 + } + } + + override fun engineUpdate(input: ByteArray, offset: Int, len: Int) { + var offset = offset + var len = len + while (len > 0) { + if (posn == 0 && len >= 128) { + transform(input, offset) + offset += 128 + len -= 128 + length += (128 * 8).toLong() + } else { + var temp = 128 - posn + if (temp > len) temp = len + System.arraycopy(input, offset, block, posn, temp) + posn += temp + length += (temp * 8).toLong() + if (posn >= 128) { + transform(block, 0) + posn = 0 + } + offset += temp + len -= temp + } + } + } + + private fun transform(m: ByteArray, offset: Int) { + var currentOffset = offset + + // Initialize working variables to the current hash value. + var a = this.h[0] + var b = this.h[1] + var c = this.h[2] + var d = this.h[3] + var e = this.h[4] + var f = this.h[5] + var g = this.h[6] + var h = this.h[7] + + for (index in 0 until 16) { + w[index] = ((m[currentOffset].toLong() and 0xFFL) shl 56) or + ((m[currentOffset + 1].toLong() and 0xFFL) shl 48) or + ((m[currentOffset + 2].toLong() and 0xFFL) shl 40) or + ((m[currentOffset + 3].toLong() and 0xFFL) shl 32) or + ((m[currentOffset + 4].toLong() and 0xFFL) shl 24) or + ((m[currentOffset + 5].toLong() and 0xFFL) shl 16) or + ((m[currentOffset + 6].toLong() and 0xFFL) shl 8) or + (m[currentOffset + 7].toLong() and 0xFFL) + currentOffset += 8 + } + + + // Extend the first 16 words to 80. + for (index in 16 until 80) { + w[index] = w[index - 16] + w[index - 7] + + (rightRotate(w[index - 15], 1) xor + rightRotate(w[index - 15], 8) xor + (w[index - 15] ushr 7)) + + (rightRotate(w[index - 2], 19) xor + rightRotate(w[index - 2], 61) xor + (w[index - 2] ushr 6)) + } + + // Compression function main loop. + for (index in 0 until 80) { + val temp1 = h + k[index] + w[index] + + (rightRotate(e, 14) xor rightRotate(e, 18) xor rightRotate(e, 41)) + + ((e and f) xor (e.inv() and g)) + val temp2 = (rightRotate(a, 28) xor rightRotate(a, 34) xor rightRotate(a, 39)) + + ((a and b) xor (a and c) xor (b and c)) + h = g + g = f + f = e + e = d + temp1 + d = c + c = b + b = a + a = temp1 + temp2 + } + + // Add the compressed chunk to the current hash value. + this.h[0] += a + this.h[1] += b + this.h[2] += c + this.h[3] += d + this.h[4] += e + this.h[5] += f + this.h[6] += g + this.h[7] += h + } + + companion object { + private fun writeBE64(buf: ByteArray, offset: Int, value: Long) { + buf[offset] = (value shr 56).toByte() + buf[offset + 1] = (value shr 48).toByte() + buf[offset + 2] = (value shr 40).toByte() + buf[offset + 3] = (value shr 32).toByte() + buf[offset + 4] = (value shr 24).toByte() + buf[offset + 5] = (value shr 16).toByte() + buf[offset + 6] = (value shr 8).toByte() + buf[offset + 7] = value.toByte() + } + + private val k = longArrayOf( + 0x428A2F98D728AE22L, 0x7137449123EF65CDL, -0x4a3f043013b2c4d1L, + -0x164a245a7e762444L, 0x3956C25BF348B538L, 0x59F111F1B605D019L, + -0x6dc07d5b50e6b065L, -0x54e3a12a25927ee8L, -0x27f855675cfcfdbeL, + 0x12835B0145706FBEL, 0x243185BE4EE4B28CL, 0x550C7DC3D5FFB4E2L, + 0x72BE5D74F27B896FL, -0x7f214e01c4e9694fL, -0x6423f958da38edcbL, + -0x3e640e8b3096d96cL, -0x1b64963e610eb52eL, -0x1041b879c7b0da1dL, + 0x0FC19DC68B8CD5B5L, 0x240CA1CC77AC9C65L, 0x2DE92C6F592B0275L, + 0x4A7484AA6EA6E483L, 0x5CB0A9DCBD41FBD4L, 0x76F988DA831153B5L, + -0x67c1aead11992055L, -0x57ce3992d24bcdf0L, -0x4ffcd8376704dec1L, + -0x40a680384110f11cL, -0x391ff40cc257703eL, -0x2a586eb86cf558dbL, + 0x06CA6351E003826FL, 0x142929670A0E6E70L, 0x27B70A8546D22FFCL, + 0x2E1B21385C26C926L, 0x4D2C6DFC5AC42AEDL, 0x53380D139D95B3DFL, + 0x650A73548BAF63DEL, 0x766A0ABB3C77B2A8L, -0x7e3d36d1b812511aL, + -0x6d8dd37aeb7dcac5L, -0x5d40175eb30efc9cL, -0x57e599b443bdcfffL, + -0x3db4748f2f07686fL, -0x3893ae5cf9ab41d0L, -0x2e6d17e62910ade8L, + -0x2966f9dbaa9a56f0L, -0xbf1ca7aa88edfd6L, 0x106AA07032BBD1B8L, + 0x19A4C116B8D2D0C8L, 0x1E376C085141AB53L, 0x2748774CDF8EEB99L, + 0x34B0BCB5E19B48A8L, 0x391C0CB3C5C95A63L, 0x4ED8AA4AE3418ACBL, + 0x5B9CCA4F7763E373L, 0x682E6FF3D6B2B8A3L, 0x748F82EE5DEFB2FCL, + 0x78A5636F43172F60L, -0x7b3787eb5e0f548eL, -0x7338fdf7e59bc614L, + -0x6f410005dc9ce1d8L, -0x5baf9314217d4217L, -0x41065c084d3986ebL, + -0x398e870d1c8dacd5L, -0x35d8c13115d99e64L, -0x2e794738de3f3df9L, + -0x15258229321f14e2L, -0xa82b08011912e88L, 0x06F067AA72176FBAL, + 0x0A637DC5A2C898A6L, 0x113F9804BEF90DAEL, 0x1B710B35131C471BL, + 0x28DB77F523047D84L, 0x32CAAB7B40C72493L, 0x3C9EBE0A15C9BEBCL, + 0x431D67C49C100D4CL, 0x4CC5D4BECB3E42B6L, 0x597F299CFC657E2AL, + 0x5FCB6FAB3AD6FAECL, 0x6C44198C4A475817L + ) + + private fun rightRotate(value: Long, n: Int): Long { + return (value ushr n) or (value shl (64 - n)) + } + } } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/package-info.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/package-info.java deleted file mode 100644 index 2f12fb80..00000000 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/package-info.java +++ /dev/null @@ -1,12 +0,0 @@ - -/** - * Fallback implementations of cryptographic primitives. - * - * This package provides plain Java implementations of the - * cryptographic primitives that Noise requires which do not - * normally come with standard JDK's. - * - * Applications that use Noise won't normally use these classes - * directly. - */ -package com.bitchat.android.noise.southernstorm.crypto; From 7cc35d7f2160cdba64d2e013ed0a39195096e320 Mon Sep 17 00:00:00 2001 From: yet300 Date: Fri, 8 May 2026 17:33:49 +0400 Subject: [PATCH 07/37] Rename .java to .kt --- .../crypto/{Blake2bMessageDigest.java => Blake2bMessageDigest.kt} | 0 .../crypto/{Blake2sMessageDigest.java => Blake2sMessageDigest.kt} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/{Blake2bMessageDigest.java => Blake2bMessageDigest.kt} (100%) rename app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/{Blake2sMessageDigest.java => Blake2sMessageDigest.kt} (100%) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2bMessageDigest.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2bMessageDigest.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2bMessageDigest.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2bMessageDigest.kt diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2sMessageDigest.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2sMessageDigest.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2sMessageDigest.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2sMessageDigest.kt From 820618e7c965278040f0358651a62c78d8e0c066 Mon Sep 17 00:00:00 2001 From: yet300 Date: Fri, 8 May 2026 17:33:50 +0400 Subject: [PATCH 08/37] Refactor: Convert BLAKE2b and BLAKE2s hash functions to pure Kotlin - Translated `Blake2bMessageDigest` and `Blake2sMessageDigest` to Kotlin. - Replaced JVM-bound `java.util.Arrays` and `System.arraycopy` with Kotlin's native `.fill()` and `.copyInto()`. - Fixed nullable array type `Array` to `Array` for the `sigma` permutation matrix, removing the need for `!!` assertions. - Refactored `while` loops into idiomatic Kotlin `for` loops in the `transform` functions. - Eliminated mutable parameter shadowing warnings by introducing `currentOffset` and `remainingLen` local variables. --- .../crypto/Blake2bMessageDigest.kt | 366 ++++++++---------- .../crypto/Blake2sMessageDigest.kt | 353 ++++++++--------- 2 files changed, 333 insertions(+), 386 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2bMessageDigest.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2bMessageDigest.kt index 1d754d47..93fc01b6 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2bMessageDigest.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2bMessageDigest.kt @@ -19,14 +19,12 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ +package com.bitchat.android.noise.southernstorm.crypto -package com.bitchat.android.noise.southernstorm.crypto; +import com.bitchat.android.noise.southernstorm.protocol.Destroyable +import java.security.DigestException +import java.security.MessageDigest -import java.security.DigestException; -import java.security.MessageDigest; -import java.util.Arrays; - -import com.bitchat.android.noise.southernstorm.protocol.Destroyable; /** * Fallback implementation of BLAKE2b for the Noise library. @@ -35,209 +33,185 @@ import com.bitchat.android.noise.southernstorm.protocol.Destroyable; * length of 64 bytes and a limit of 2^64 - 1 bytes of input. * Keyed hashing and variable-length digests are not supported. */ -public class Blake2bMessageDigest extends MessageDigest implements Destroyable { +class Blake2bMessageDigest : MessageDigest("BLAKE2B-512"), Destroyable { + private val h = LongArray(8) + private val block = ByteArray(128) + private val m = LongArray(16) + private val v = LongArray(16) + private var length: Long = 0 + private var posn = 0 - private long[] h; - private byte[] block; - private long[] m; - private long[] v; - private long length; - private int posn; + init { + engineReset() + } - /** - * Constructs a new BLAKE2b message digest object. - */ - public Blake2bMessageDigest() { - super("BLAKE2B-512"); - h = new long [8]; - block = new byte [128]; - m = new long [16]; - v = new long [16]; - engineReset(); - } + override fun engineDigest(): ByteArray { + val digest = ByteArray(64) + try { + engineDigest(digest, 0, 64) + } catch (e: DigestException) { + // Shouldn't happen, but just in case. + digest.fill(0) + } + return digest + } - @Override - protected byte[] engineDigest() { - byte[] digest = new byte [64]; - try { - engineDigest(digest, 0, 64); - } catch (DigestException e) { - // Shouldn't happen, but just in case. - Arrays.fill(digest, (byte)0); - } - return digest; - } - - @Override - protected int engineDigest(byte[] buf, int offset, int len) throws DigestException - { - if (len < 64) - throw new DigestException("Invalid digest length for BLAKE2b"); - Arrays.fill(block, posn, 128, (byte)0); - transform(-1); - for (int index = 0; index < 8; ++index) { - long value = h[index]; - buf[offset++] = (byte)value; - buf[offset++] = (byte)(value >> 8); - buf[offset++] = (byte)(value >> 16); - buf[offset++] = (byte)(value >> 24); - buf[offset++] = (byte)(value >> 32); - buf[offset++] = (byte)(value >> 40); - buf[offset++] = (byte)(value >> 48); - buf[offset++] = (byte)(value >> 56); - } - return 32; - } + @Throws(DigestException::class) + override fun engineDigest(buf: ByteArray, offset: Int, len: Int): Int { + var offset = offset + if (len < 64) throw DigestException("Invalid digest length for BLAKE2b") + block.fill(0, posn, 128) + transform(-1) + for (index in 0..7) { + val value = h[index] + buf[offset++] = value.toByte() + buf[offset++] = (value shr 8).toByte() + buf[offset++] = (value shr 16).toByte() + buf[offset++] = (value shr 24).toByte() + buf[offset++] = (value shr 32).toByte() + buf[offset++] = (value shr 40).toByte() + buf[offset++] = (value shr 48).toByte() + buf[offset++] = (value shr 56).toByte() + } + return 32 + } - @Override - protected int engineGetDigestLength() { - return 64; - } + override fun engineGetDigestLength(): Int = 64 - @Override - protected void engineReset() { - h[0] = 0x6a09e667f3bcc908L ^ 0x01010040; - h[1] = 0xbb67ae8584caa73bL; - h[2] = 0x3c6ef372fe94f82bL; - h[3] = 0xa54ff53a5f1d36f1L; - h[4] = 0x510e527fade682d1L; - h[5] = 0x9b05688c2b3e6c1fL; - h[6] = 0x1f83d9abfb41bd6bL; - h[7] = 0x5be0cd19137e2179L; - length = 0; - posn = 0; - } + override fun engineReset() { + h[0] = 0x6a09e667f3bcc908L xor 0x01010040L + h[1] = -0x4498517a7b3558c5L + h[2] = 0x3c6ef372fe94f82bL + h[3] = -0x5ab00ac5a0e2c90fL + h[4] = 0x510e527fade682d1L + h[5] = -0x64fa9773d4c193e1L + h[6] = 0x1f83d9abfb41bd6bL + h[7] = 0x5be0cd19137e2179L + length = 0 + posn = 0 + } - @Override - protected void engineUpdate(byte input) { - if (posn >= 128) { - transform(0); - posn = 0; - } - block[posn++] = input; - ++length; - } + override fun engineUpdate(input: Byte) { + if (posn >= 128) { + transform(0) + posn = 0 + } + block[posn++] = input + ++length + } - @Override - protected void engineUpdate(byte[] input, int offset, int len) { - while (len > 0) { - if (posn >= 128) { - transform(0); - posn = 0; - } - int temp = (128 - posn); - if (temp > len) - temp = len; - System.arraycopy(input, offset, block, posn, temp); - posn += temp; - length += temp; - offset += temp; - len -= temp; - } - } + override fun engineUpdate(input: ByteArray, offset: Int, len: Int) { + var currentOffset = offset + var remainingLen = len + while (remainingLen > 0) { + if (posn >= 128) { + transform(0) + posn = 0 + } + var temp = 128 - posn + if (temp > remainingLen) temp = remainingLen - // Permutation on the message input state for BLAKE2b. - static final byte[][] sigma = { - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, - {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, - {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, - {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, - {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0}, - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, - }; - - private void transform(long f0) - { - int index; - int offset; - - // Unpack the input block from little-endian into host-endian. - for (index = 0, offset = 0; index < 16; ++index, offset += 8) { - m[index] = (block[offset] & 0xFFL) | - ((block[offset + 1] & 0xFFL) << 8) | - ((block[offset + 2] & 0xFFL) << 16) | - ((block[offset + 3] & 0xFFL) << 24) | - ((block[offset + 4] & 0xFFL) << 32) | - ((block[offset + 5] & 0xFFL) << 40) | - ((block[offset + 6] & 0xFFL) << 48) | - ((block[offset + 7] & 0xFFL) << 56); - } - - // Format the block to be hashed. - for (index = 0; index < 8; ++index) - v[index] = h[index]; - v[8] = 0x6a09e667f3bcc908L; - v[9] = 0xbb67ae8584caa73bL; - v[10] = 0x3c6ef372fe94f82bL; - v[11] = 0xa54ff53a5f1d36f1L; - v[12] = 0x510e527fade682d1L ^ length; - v[13] = 0x9b05688c2b3e6c1fL; - v[14] = 0x1f83d9abfb41bd6bL ^ f0; - v[15] = 0x5be0cd19137e2179L; - - // Perform the 12 BLAKE2b rounds. - for (index = 0; index < 12; ++index) { - // Column round. - quarterRound(0, 4, 8, 12, 0, index); - quarterRound(1, 5, 9, 13, 1, index); - quarterRound(2, 6, 10, 14, 2, index); - quarterRound(3, 7, 11, 15, 3, index); + input.copyInto(block, posn, currentOffset, currentOffset + temp) + posn += temp + length += temp.toLong() + currentOffset += temp + remainingLen -= temp + } + } - // Diagonal round. - quarterRound(0, 5, 10, 15, 4, index); - quarterRound(1, 6, 11, 12, 5, index); - quarterRound(2, 7, 8, 13, 6, index); - quarterRound(3, 4, 9, 14, 7, index); - } - - // Combine the new and old hash values. - for (index = 0; index < 8; ++index) - h[index] ^= (v[index] ^ v[index + 8]); - } + private fun transform(f0: Long) { + var currentOffset = 0 - private static long rightRotate32(long v) - { - return v << 32 | (v >>> 32); - } + // Unpack the input block from little-endian into host-endian. + for (index in 0 until 16) { + m[index] = (block[currentOffset].toLong() and 0xFFL) or + ((block[currentOffset + 1].toLong() and 0xFFL) shl 8) or + ((block[currentOffset + 2].toLong() and 0xFFL) shl 16) or + ((block[currentOffset + 3].toLong() and 0xFFL) shl 24) or + ((block[currentOffset + 4].toLong() and 0xFFL) shl 32) or + ((block[currentOffset + 5].toLong() and 0xFFL) shl 40) or + ((block[currentOffset + 6].toLong() and 0xFFL) shl 48) or + ((block[currentOffset + 7].toLong() and 0xFFL) shl 56) + currentOffset += 8 + } - private static long rightRotate24(long v) - { - return v << 40 | (v >>> 24); - } - private static long rightRotate16(long v) - { - return v << 48 | (v >>> 16); - } + // Format the block to be hashed. + for (index in 0 until 8) { + v[index] = h[index] + } - private static long rightRotate63(long v) - { - return v << 1 | (v >>> 63); - } + v[8] = 0x6a09e667f3bcc908L + v[9] = -0x4498517a7b3558c5L + v[10] = 0x3c6ef372fe94f82bL + v[11] = -0x5ab00ac5a0e2c90fL + v[12] = 0x510e527fade682d1L xor length + v[13] = -0x64fa9773d4c193e1L + v[14] = 0x1f83d9abfb41bd6bL xor f0 + v[15] = 0x5be0cd19137e2179L - private void quarterRound(int a, int b, int c, int d, int i, int row) - { - v[a] += v[b] + m[sigma[row][2 * i]]; - v[d] = rightRotate32(v[d] ^ v[a]); - v[c] += v[d]; - v[b] = rightRotate24(v[b] ^ v[c]); - v[a] += v[b] + m[sigma[row][2 * i + 1]]; - v[d] = rightRotate16(v[d] ^ v[a]); - v[c] += v[d]; - v[b] = rightRotate63(v[b] ^ v[c]); - } - @Override - public void destroy() { - Arrays.fill(h, (long)0); - Arrays.fill(block, (byte)0); - Arrays.fill(m, (long)0); - Arrays.fill(v, (long)0); - } + // Perform the 12 BLAKE2b rounds. + for (index in 0 until 12) { + // Column round. + quarterRound(0, 4, 8, 12, 0, index) + quarterRound(1, 5, 9, 13, 1, index) + quarterRound(2, 6, 10, 14, 2, index) + quarterRound(3, 7, 11, 15, 3, index) + + // Diagonal round. + quarterRound(0, 5, 10, 15, 4, index) + quarterRound(1, 6, 11, 12, 5, index) + quarterRound(2, 7, 8, 13, 6, index) + quarterRound(3, 4, 9, 14, 7, index) + } + + + // Combine the new and old hash values. + for (index in 0 until 8) { + h[index] = h[index] xor (v[index] xor v[index + 8]) + } + } + + private fun quarterRound(a: Int, b: Int, c: Int, d: Int, i: Int, row: Int) { + v[a] += v[b] + m[sigma[row][2 * i].toInt()] + v[d] = rightRotate32(v[d] xor v[a]) + v[c] += v[d] + v[b] = rightRotate24(v[b] xor v[c]) + v[a] += v[b] + m[sigma[row][2 * i + 1].toInt()] + v[d] = rightRotate16(v[d] xor v[a]) + v[c] += v[d] + v[b] = rightRotate63(v[b] xor v[c]) + } + + override fun destroy() { + h.fill(0L) + block.fill(0) + m.fill(0L) + v.fill(0L) + + } + + companion object { + // Permutation on the message input state for BLAKE2b. + val sigma: Array = arrayOf( + byteArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), + byteArrayOf(14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3), + byteArrayOf(11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4), + byteArrayOf(7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8), + byteArrayOf(9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13), + byteArrayOf(2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9), + byteArrayOf(12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11), + byteArrayOf(13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10), + byteArrayOf(6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5), + byteArrayOf(10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0), + byteArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), + byteArrayOf(14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3), + ) + + private fun rightRotate32(v: Long): Long = (v shl 32) or (v ushr 32) + private fun rightRotate24(v: Long): Long = (v shl 40) or (v ushr 24) + private fun rightRotate16(v: Long): Long = (v shl 48) or (v ushr 16) + private fun rightRotate63(v: Long): Long = (v shl 1) or (v ushr 63) + } } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2sMessageDigest.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2sMessageDigest.kt index 4f3f98c6..08dea64a 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2sMessageDigest.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Blake2sMessageDigest.kt @@ -19,14 +19,11 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ +package com.bitchat.android.noise.southernstorm.crypto -package com.bitchat.android.noise.southernstorm.crypto; - -import java.security.DigestException; -import java.security.MessageDigest; -import java.util.Arrays; - -import com.bitchat.android.noise.southernstorm.protocol.Destroyable; +import com.bitchat.android.noise.southernstorm.protocol.Destroyable +import java.security.DigestException +import java.security.MessageDigest /** * Fallback implementation of BLAKE2s for the Noise library. @@ -35,199 +32,175 @@ import com.bitchat.android.noise.southernstorm.protocol.Destroyable; * length of 32 bytes. Keyed hashing and variable-length digests are * not supported. */ -public class Blake2sMessageDigest extends MessageDigest implements Destroyable { - - private int[] h; - private byte[] block; - private int[] m; - private int[] v; - private long length; - private int posn; +class Blake2sMessageDigest : MessageDigest("BLAKE2S-256"), Destroyable { + private val h = IntArray(8) + private val block = ByteArray(64) + private val m = IntArray(16) + private val v = IntArray(16) + private var length: Long = 0 + private var posn = 0 - /** - * Constructs a new BLAKE2s message digest object. - */ - public Blake2sMessageDigest() { - super("BLAKE2S-256"); - h = new int [8]; - block = new byte [64]; - m = new int [16]; - v = new int [16]; - engineReset(); - } - - @Override - protected byte[] engineDigest() { - byte[] digest = new byte [32]; - try { - engineDigest(digest, 0, 32); - } catch (DigestException e) { - // Shouldn't happen, but just in case. - Arrays.fill(digest, (byte)0); - } - return digest; - } - - @Override - protected int engineDigest(byte[] buf, int offset, int len) throws DigestException - { - if (len < 32) - throw new DigestException("Invalid digest length for BLAKE2s"); - Arrays.fill(block, posn, 64, (byte)0); - transform(-1); - for (int index = 0; index < 8; ++index) { - int value = h[index]; - buf[offset++] = (byte)value; - buf[offset++] = (byte)(value >> 8); - buf[offset++] = (byte)(value >> 16); - buf[offset++] = (byte)(value >> 24); - } - return 32; - } + init { + engineReset() + } - @Override - protected int engineGetDigestLength() { - return 32; - } - @Override - protected void engineReset() { - h[0] = 0x6A09E667 ^ 0x01010020; - h[1] = 0xBB67AE85; - h[2] = 0x3C6EF372; - h[3] = 0xA54FF53A; - h[4] = 0x510E527F; - h[5] = 0x9B05688C; - h[6] = 0x1F83D9AB; - h[7] = 0x5BE0CD19; - length = 0; - posn = 0; - } + override fun engineDigest(): ByteArray { + val digest = ByteArray(32) + try { + engineDigest(digest, 0, 32) + } catch (e: DigestException) { + // Shouldn't happen, but just in case. + digest.fill(0) + } + return digest + } - @Override - protected void engineUpdate(byte input) { - if (posn >= 64) { - transform(0); - posn = 0; - } - block[posn++] = input; - ++length; - } + @Throws(DigestException::class) + override fun engineDigest(buf: ByteArray, offset: Int, len: Int): Int { + var offset = offset + if (len < 32) throw DigestException("Invalid digest length for BLAKE2s") + block.fill(0, posn, 64) + transform(-1) + for (index in 0..7) { + val value = h[index] + buf[offset++] = value.toByte() + buf[offset++] = (value shr 8).toByte() + buf[offset++] = (value shr 16).toByte() + buf[offset++] = (value shr 24).toByte() + } + return 32 + } - @Override - protected void engineUpdate(byte[] input, int offset, int len) { - while (len > 0) { - if (posn >= 64) { - transform(0); - posn = 0; - } - int temp = (64 - posn); - if (temp > len) - temp = len; - System.arraycopy(input, offset, block, posn, temp); - posn += temp; - length += temp; - offset += temp; - len -= temp; - } - } + override fun engineGetDigestLength(): Int = 32 - // Permutation on the message input state for BLAKE2s. - static final byte[][] sigma = { - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, - {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, - {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, - {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, - {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0} - }; - - private void transform(int f0) - { - int index; - int offset; - - // Unpack the input block from little-endian into host-endian. - for (index = 0, offset = 0; index < 16; ++index, offset += 4) { - m[index] = (block[offset] & 0xFF) | - ((block[offset + 1] & 0xFF) << 8) | - ((block[offset + 2] & 0xFF) << 16) | - ((block[offset + 3] & 0xFF) << 24); - } - - // Format the block to be hashed. - for (index = 0; index < 8; ++index) - v[index] = h[index]; - v[8] = 0x6A09E667; - v[9] = 0xBB67AE85; - v[10] = 0x3C6EF372; - v[11] = 0xA54FF53A; - v[12] = 0x510E527F ^ (int)length; - v[13] = 0x9B05688C ^ (int)(length >> 32); - v[14] = 0x1F83D9AB ^ f0; - v[15] = 0x5BE0CD19; - - // Perform the 10 BLAKE2s rounds. - for (index = 0; index < 10; ++index) { - // Column round. - quarterRound(0, 4, 8, 12, 0, index); - quarterRound(1, 5, 9, 13, 1, index); - quarterRound(2, 6, 10, 14, 2, index); - quarterRound(3, 7, 11, 15, 3, index); - // Diagonal round. - quarterRound(0, 5, 10, 15, 4, index); - quarterRound(1, 6, 11, 12, 5, index); - quarterRound(2, 7, 8, 13, 6, index); - quarterRound(3, 4, 9, 14, 7, index); - } - - // Combine the new and old hash values. - for (index = 0; index < 8; ++index) - h[index] ^= (v[index] ^ v[index + 8]); - } - - private static int rightRotate16(int v) - { - return v << 16 | (v >>> 16); - } + override fun engineReset() { + h[0] = 0x6A09E667 xor 0x01010020 + h[1] = -0x4498517b + h[2] = 0x3C6EF372 + h[3] = -0x5ab00ac6 + h[4] = 0x510E527F + h[5] = -0x64fa9774 + h[6] = 0x1F83D9AB + h[7] = 0x5BE0CD19 + length = 0 + posn = 0 + } - private static int rightRotate12(int v) - { - return v << 20 | (v >>> 12); - } + override fun engineUpdate(input: Byte) { + if (posn >= 64) { + transform(0) + posn = 0 + } + block[posn++] = input + ++length + } - private static int rightRotate8(int v) - { - return v << 24 | (v >>> 8); - } + override fun engineUpdate(input: ByteArray, offset: Int, len: Int) { + var currentOffset = offset + var remainingLen = len + while (remainingLen > 0) { + if (posn >= 64) { + transform(0) + posn = 0 + } + var temp = 64 - posn + if (temp > remainingLen) temp = remainingLen - private static int rightRotate7(int v) - { - return v << 25 | (v >>> 7); - } + input.copyInto(block, posn, currentOffset, currentOffset + temp) + posn += temp + length += temp.toLong() + currentOffset += temp + remainingLen -= temp + } + } - private void quarterRound(int a, int b, int c, int d, int i, int row) - { - v[a] += v[b] + m[sigma[row][2 * i]]; - v[d] = rightRotate16(v[d] ^ v[a]); - v[c] += v[d]; - v[b] = rightRotate12(v[b] ^ v[c]); - v[a] += v[b] + m[sigma[row][2 * i + 1]]; - v[d] = rightRotate8(v[d] ^ v[a]); - v[c] += v[d]; - v[b] = rightRotate7(v[b] ^ v[c]); - } + private fun transform(f0: Int) { + var currentOffset = 0 - @Override - public void destroy() { - Arrays.fill(h, (int)0); - Arrays.fill(block, (byte)0); - Arrays.fill(m, (int)0); - Arrays.fill(v, (int)0); - } + + // Unpack the input block from little-endian into host-endian. + for (index in 0 until 16) { + m[index] = (block[currentOffset].toInt() and 0xFF) or + ((block[currentOffset + 1].toInt() and 0xFF) shl 8) or + ((block[currentOffset + 2].toInt() and 0xFF) shl 16) or + ((block[currentOffset + 3].toInt() and 0xFF) shl 24) + currentOffset += 4 + } + + // Format the block to be hashed. + for (index in 0 until 8) { + v[index] = h[index] + } + v[8] = 0x6A09E667 + v[9] = -0x4498517b + v[10] = 0x3C6EF372 + v[11] = -0x5ab00ac6 + v[12] = 0x510E527F xor length.toInt() + v[13] = -0x64fa9774 xor (length shr 32).toInt() + v[14] = 0x1F83D9AB xor f0 + v[15] = 0x5BE0CD19 + + + // Perform the 10 BLAKE2s rounds. + for (index in 0 until 10) { + // Column round. + quarterRound(0, 4, 8, 12, 0, index) + quarterRound(1, 5, 9, 13, 1, index) + quarterRound(2, 6, 10, 14, 2, index) + quarterRound(3, 7, 11, 15, 3, index) + + // Diagonal round. + quarterRound(0, 5, 10, 15, 4, index) + quarterRound(1, 6, 11, 12, 5, index) + quarterRound(2, 7, 8, 13, 6, index) + quarterRound(3, 4, 9, 14, 7, index) + } + + + // Combine the new and old hash values. + for (index in 0 until 8) { + h[index] = h[index] xor (v[index] xor v[index + 8]) + } + } + + private fun quarterRound(a: Int, b: Int, c: Int, d: Int, i: Int, row: Int) { + v[a] += v[b] + m[sigma[row][2 * i].toInt()] + v[d] = rightRotate16(v[d] xor v[a]) + v[c] += v[d] + v[b] = rightRotate12(v[b] xor v[c]) + v[a] += v[b] + m[sigma[row][2 * i + 1].toInt()] + v[d] = rightRotate8(v[d] xor v[a]) + v[c] += v[d] + v[b] = rightRotate7(v[b] xor v[c]) + } + + override fun destroy() { + h.fill(0) + block.fill(0) + m.fill(0) + v.fill(0) + } + + companion object { + // Permutation on the message input state for BLAKE2s. + val sigma: Array = arrayOf( + byteArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), + byteArrayOf(14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3), + byteArrayOf(11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4), + byteArrayOf(7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8), + byteArrayOf(9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13), + byteArrayOf(2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9), + byteArrayOf(12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11), + byteArrayOf(13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10), + byteArrayOf(6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5), + byteArrayOf(10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0) + ) + + private fun rightRotate16(v: Int): Int = (v shl 16) or (v ushr 16) + private fun rightRotate12(v: Int): Int = (v shl 20) or (v ushr 12) + private fun rightRotate8(v: Int): Int = (v shl 24) or (v ushr 8) + private fun rightRotate7(v: Int): Int = (v shl 25) or (v ushr 7) + } } From 5c9a229df4367443078f50df2df9f8ee2416e733 Mon Sep 17 00:00:00 2001 From: yet300 Date: Sat, 9 May 2026 15:07:32 +0400 Subject: [PATCH 09/37] Rename .java to .kt --- .../noise/southernstorm/crypto/{Curve25519.java => Curve25519.kt} | 0 .../noise/southernstorm/crypto/{Curve448.java => Curve448.kt} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/{Curve25519.java => Curve25519.kt} (100%) rename app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/{Curve448.java => Curve448.kt} (100%) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve25519.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve25519.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve25519.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve25519.kt diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve448.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve448.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve448.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve448.kt From afea44b33abc96ba7ed10b443a76db6976170db4 Mon Sep 17 00:00:00 2001 From: yet300 Date: Sat, 9 May 2026 15:07:32 +0400 Subject: [PATCH 10/37] Convert Curve25519 and Curve448 to Kotlin --- .../noise/southernstorm/crypto/Curve25519.kt | 891 ++++++++-------- .../noise/southernstorm/crypto/Curve448.kt | 993 +++++++++--------- 2 files changed, 890 insertions(+), 994 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve25519.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve25519.kt index 4b06562a..fd6c1693 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve25519.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve25519.kt @@ -19,513 +19,464 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ - -package com.bitchat.android.noise.southernstorm.crypto; - -import java.util.Arrays; +package com.bitchat.android.noise.southernstorm.crypto /** * Implementation of the Curve25519 elliptic curve algorithm. * * This implementation is based on that from arduinolibs: * https://github.com/rweather/arduinolibs - * + * * Differences in this version are due to using 26-bit limbs for the * representation instead of the 8/16/32-bit limbs in the original. * * References: http://cr.yp.to/ecdh.html, RFC 7748 */ -public final class Curve25519 { +class Curve25519 { + private val x_1 = IntArray(NUM_LIMBS_255BIT) + private val x_2 = IntArray(NUM_LIMBS_255BIT) + private val x_3 = IntArray(NUM_LIMBS_255BIT) + private val z_2 = IntArray(NUM_LIMBS_255BIT) + private val z_3 = IntArray(NUM_LIMBS_255BIT) + private val A = IntArray(NUM_LIMBS_255BIT) + private val B = IntArray(NUM_LIMBS_255BIT) + private val C = IntArray(NUM_LIMBS_255BIT) + private val D = IntArray(NUM_LIMBS_255BIT) + private val E = IntArray(NUM_LIMBS_255BIT) + private val AA = IntArray(NUM_LIMBS_255BIT) + private val BB = IntArray(NUM_LIMBS_255BIT) + private val DA = IntArray(NUM_LIMBS_255BIT) + private val CB = IntArray(NUM_LIMBS_255BIT) + private val t1 = LongArray(NUM_LIMBS_510BIT) + private val t2 = IntArray(NUM_LIMBS_510BIT) - // Numbers modulo 2^255 - 19 are broken up into ten 26-bit words. - private static final int NUM_LIMBS_255BIT = 10; - private static final int NUM_LIMBS_510BIT = 20; - private int[] x_1; - private int[] x_2; - private int[] x_3; - private int[] z_2; - private int[] z_3; - private int[] A; - private int[] B; - private int[] C; - private int[] D; - private int[] E; - private int[] AA; - private int[] BB; - private int[] DA; - private int[] CB; - private long[] t1; - private int[] t2; + /** + * Destroy all sensitive data in this object. + */ + private fun destroy() { + // Destroy all temporary variables. + x_1.fill(0) + x_2.fill(0) + x_3.fill(0) + z_2.fill(0) + z_3.fill(0) + A.fill(0) + B.fill(0) + C.fill(0) + D.fill(0) + E.fill(0) + AA.fill(0) + BB.fill(0) + DA.fill(0) + CB.fill(0) + t1.fill(0L) + t2.fill(0) + } - /** - * Constructs the temporary state holder for Curve25519 evaluation. - */ - private Curve25519() - { - // Allocate memory for all of the temporary variables we will need. - x_1 = new int [NUM_LIMBS_255BIT]; - x_2 = new int [NUM_LIMBS_255BIT]; - x_3 = new int [NUM_LIMBS_255BIT]; - z_2 = new int [NUM_LIMBS_255BIT]; - z_3 = new int [NUM_LIMBS_255BIT]; - A = new int [NUM_LIMBS_255BIT]; - B = new int [NUM_LIMBS_255BIT]; - C = new int [NUM_LIMBS_255BIT]; - D = new int [NUM_LIMBS_255BIT]; - E = new int [NUM_LIMBS_255BIT]; - AA = new int [NUM_LIMBS_255BIT]; - BB = new int [NUM_LIMBS_255BIT]; - DA = new int [NUM_LIMBS_255BIT]; - CB = new int [NUM_LIMBS_255BIT]; - t1 = new long [NUM_LIMBS_510BIT]; - t2 = new int [NUM_LIMBS_510BIT]; - } - + /** + * Reduces a number modulo 2^255 - 19 where it is known that the + * number can be reduced with only 1 trial subtraction. + * + * @param x The number to reduce, and the result. + */ + private fun reduceQuick(x: IntArray) { + // Perform a trial subtraction of (2^255 - 19) from "x" which is + // equivalent to adding 19 and subtracting 2^255. We add 19 here; + // the subtraction of 2^255 occurs in the next step. + var carry = 19 + for (index in 0 until NUM_LIMBS_255BIT) { + carry += x[index] + t2[index] = carry and 0x03FFFFFF + carry = carry shr 26 + } - /** - * Destroy all sensitive data in this object. - */ - private void destroy() { - // Destroy all temporary variables. - Arrays.fill(x_1, 0); - Arrays.fill(x_2, 0); - Arrays.fill(x_3, 0); - Arrays.fill(z_2, 0); - Arrays.fill(z_3, 0); - Arrays.fill(A, 0); - Arrays.fill(B, 0); - Arrays.fill(C, 0); - Arrays.fill(D, 0); - Arrays.fill(E, 0); - Arrays.fill(AA, 0); - Arrays.fill(BB, 0); - Arrays.fill(DA, 0); - Arrays.fill(CB, 0); - Arrays.fill(t1, 0L); - Arrays.fill(t2, 0); - } + // If there was a borrow, then the original "x" is the correct answer. + // If there was no borrow, then "t2" is the correct answer. Select the + // correct answer but do it in a way that instruction timing will not + // reveal which value was selected. Borrow will occur if bit 21 of + // "t2" is zero. Turn the bit into a selection mask. + val mask = -((t2[NUM_LIMBS_255BIT - 1] shr 21) and 0x01) + val nmask = mask.inv() + t2[NUM_LIMBS_255BIT - 1] = t2[NUM_LIMBS_255BIT - 1] and 0x001FFFFF + for (index in 0 until NUM_LIMBS_255BIT) { + x[index] = (x[index] and nmask) or (t2[index] and mask) + } + } - /** - * Reduces a number modulo 2^255 - 19 where it is known that the - * number can be reduced with only 1 trial subtraction. - * - * @param x The number to reduce, and the result. - */ - private void reduceQuick(int[] x) - { - int index, carry; - - // Perform a trial subtraction of (2^255 - 19) from "x" which is - // equivalent to adding 19 and subtracting 2^255. We add 19 here; - // the subtraction of 2^255 occurs in the next step. - carry = 19; - for (index = 0; index < NUM_LIMBS_255BIT; ++index) { - carry += x[index]; - t2[index] = carry & 0x03FFFFFF; - carry >>= 26; - } + /** + * Reduce a number modulo 2^255 - 19. + * + * @param result The result. + * @param x The value to be reduced. This array will be + * modified during the reduction. + * @param size The number of limbs in the high order half of x. + */ + private fun reduce(result: IntArray, x: IntArray, size: Int) { + // Calculate (x mod 2^255) + ((x / 2^255) * 19) which will + // either produce the answer we want or it will produce a + // value of the form "answer + j * (2^255 - 19)". There are + // 5 left-over bits in the top-most limb of the bottom half. + var carry = 0 + var limb = x[NUM_LIMBS_255BIT - 1] shr 21 + x[NUM_LIMBS_255BIT - 1] = x[NUM_LIMBS_255BIT - 1] and 0x001FFFFF + for (index in 0 until size) { + limb += x[NUM_LIMBS_255BIT + index] shl 5 + carry += (limb and 0x03FFFFFF) * 19 + x[index] + x[index] = carry and 0x03FFFFFF + limb = limb shr 26 + carry = carry shr 26 + } + if (size < NUM_LIMBS_255BIT) { + // The high order half of the number is short; e.g. for mulA24(). + // Propagate the carry through the rest of the low order part. + for (index in size until NUM_LIMBS_255BIT) { + carry += x[index] + x[index] = carry and 0x03FFFFFF + carry = carry shr 26 + } + } - // If there was a borrow, then the original "x" is the correct answer. - // If there was no borrow, then "t2" is the correct answer. Select the - // correct answer but do it in a way that instruction timing will not - // reveal which value was selected. Borrow will occur if bit 21 of - // "t2" is zero. Turn the bit into a selection mask. - int mask = -((t2[NUM_LIMBS_255BIT - 1] >> 21) & 0x01); - int nmask = ~mask; - t2[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF; - for (index = 0; index < NUM_LIMBS_255BIT; ++index) - x[index] = (x[index] & nmask) | (t2[index] & mask); - } + // The "j" value may still be too large due to the final carry-out. + // We must repeat the reduction. If we already have the answer, + // then this won't do any harm but we must still do the calculation + // to preserve the overall timing. The "j" value will be between + // 0 and 19, which means that the carry we care about is in the + // top 5 bits of the highest limb of the bottom half. + carry = (x[NUM_LIMBS_255BIT - 1] shr 21) * 19 + x[NUM_LIMBS_255BIT - 1] = x[NUM_LIMBS_255BIT - 1] and 0x001FFFFF + for (index in 0 until NUM_LIMBS_255BIT) { + carry += x[index] + result[index] = carry and 0x03FFFFFF + carry = carry shr 26 + } - /** - * Reduce a number modulo 2^255 - 19. - * - * @param result The result. - * @param x The value to be reduced. This array will be - * modified during the reduction. - * @param size The number of limbs in the high order half of x. - */ - private void reduce(int[] result, int[] x, int size) - { - int index, limb, carry; - - // Calculate (x mod 2^255) + ((x / 2^255) * 19) which will - // either produce the answer we want or it will produce a - // value of the form "answer + j * (2^255 - 19)". There are - // 5 left-over bits in the top-most limb of the bottom half. - carry = 0; - limb = x[NUM_LIMBS_255BIT - 1] >> 21; - x[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF; - for (index = 0; index < size; ++index) { - limb += x[NUM_LIMBS_255BIT + index] << 5; - carry += (limb & 0x03FFFFFF) * 19 + x[index]; - x[index] = carry & 0x03FFFFFF; - limb >>= 26; - carry >>= 26; - } - if (size < NUM_LIMBS_255BIT) { - // The high order half of the number is short; e.g. for mulA24(). - // Propagate the carry through the rest of the low order part. - for (index = size; index < NUM_LIMBS_255BIT; ++index) { - carry += x[index]; - x[index] = carry & 0x03FFFFFF; - carry >>= 26; - } - } + // At this point "x" will either be the answer or it will be the + // answer plus (2^255 - 19). Perform a trial subtraction to + // complete the reduction process. + reduceQuick(result) + } - // The "j" value may still be too large due to the final carry-out. - // We must repeat the reduction. If we already have the answer, - // then this won't do any harm but we must still do the calculation - // to preserve the overall timing. The "j" value will be between - // 0 and 19, which means that the carry we care about is in the - // top 5 bits of the highest limb of the bottom half. - carry = (x[NUM_LIMBS_255BIT - 1] >> 21) * 19; - x[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF; - for (index = 0; index < NUM_LIMBS_255BIT; ++index) { - carry += x[index]; - result[index] = carry & 0x03FFFFFF; - carry >>= 26; - } + /** + * Multiplies two numbers modulo 2^255 - 19. + * + * @param result The result. + * @param x The first number to multiply. + * @param y The second number to multiply. + */ + private fun mul(result: IntArray, x: IntArray, y: IntArray) { + // Multiply the two numbers to create the intermediate result. + var v = x[0].toLong() + for (i in 0 until NUM_LIMBS_255BIT) { + t1[i] = v * y[i] + } + for (i in 1 until NUM_LIMBS_255BIT) { + v = x[i].toLong() + for (j in 0 until (NUM_LIMBS_255BIT - 1)) { + t1[i + j] += v * y[j] + } + t1[i + NUM_LIMBS_255BIT - 1] = v * y[NUM_LIMBS_255BIT - 1] + } - // At this point "x" will either be the answer or it will be the - // answer plus (2^255 - 19). Perform a trial subtraction to - // complete the reduction process. - reduceQuick(result); - } + // Propagate carries and convert back into 26-bit words. + v = t1[0] + t2[0] = v.toInt() and 0x03FFFFFF + for (i in 1 until NUM_LIMBS_510BIT) { + v = (v shr 26) + t1[i] + t2[i] = v.toInt() and 0x03FFFFFF + } - /** - * Multiplies two numbers modulo 2^255 - 19. - * - * @param result The result. - * @param x The first number to multiply. - * @param y The second number to multiply. - */ - private void mul(int[] result, int[] x, int[] y) - { - int i, j; - - // Multiply the two numbers to create the intermediate result. - long v = x[0]; - for (i = 0; i < NUM_LIMBS_255BIT; ++i) { - t1[i] = v * y[i]; - } - for (i = 1; i < NUM_LIMBS_255BIT; ++i) { - v = x[i]; - for (j = 0; j < (NUM_LIMBS_255BIT - 1); ++j) { - t1[i + j] += v * y[j]; - } - t1[i + NUM_LIMBS_255BIT - 1] = v * y[NUM_LIMBS_255BIT - 1]; - } - - // Propagate carries and convert back into 26-bit words. - v = t1[0]; - t2[0] = ((int)v) & 0x03FFFFFF; - for (i = 1; i < NUM_LIMBS_510BIT; ++i) { - v = (v >> 26) + t1[i]; - t2[i] = ((int)v) & 0x03FFFFFF; - } - - // Reduce the result modulo 2^255 - 19. - reduce(result, t2, NUM_LIMBS_255BIT); - } - - /** - * Squares a number modulo 2^255 - 19. - * - * @param result The result. - * @param x The number to square. - */ - private void square(int[] result, int[] x) - { - mul(result, x, x); - } + // Reduce the result modulo 2^255 - 19. + reduce(result, t2, NUM_LIMBS_255BIT) + } - /** - * Multiplies a number by the a24 constant, modulo 2^255 - 19. - * - * @param result The result. - * @param x The number to multiply by a24. - */ - private void mulA24(int[] result, int[] x) - { - long a24 = 121665; - long carry = 0; - int index; - for (index = 0; index < NUM_LIMBS_255BIT; ++index) { - carry += a24 * x[index]; - t2[index] = ((int)carry) & 0x03FFFFFF; - carry >>= 26; - } - t2[NUM_LIMBS_255BIT] = ((int)carry) & 0x03FFFFFF; - reduce(result, t2, 1); - } + /** + * Squares a number modulo 2^255 - 19. + * + * @param result The result. + * @param x The number to square. + */ + private fun square(result: IntArray, x: IntArray) { + mul(result, x, x) + } - /** - * Adds two numbers modulo 2^255 - 19. - * - * @param result The result. - * @param x The first number to add. - * @param y The second number to add. - */ - private void add(int[] result, int[] x, int[] y) - { - int index, carry; - carry = x[0] + y[0]; - result[0] = carry & 0x03FFFFFF; - for (index = 1; index < NUM_LIMBS_255BIT; ++index) { - carry = (carry >> 26) + x[index] + y[index]; - result[index] = carry & 0x03FFFFFF; - } - reduceQuick(result); - } + /** + * Multiplies a number by the a24 constant, modulo 2^255 - 19. + * + * @param result The result. + * @param x The number to multiply by a24. + */ + private fun mulA24(result: IntArray, x: IntArray) { + val a24: Long = 121665 + var carry: Long = 0 + for (index in 0 until NUM_LIMBS_255BIT) { + carry += a24 * x[index] + t2[index] = carry.toInt() and 0x03FFFFFF + carry = carry shr 26 + } + t2[NUM_LIMBS_255BIT] = carry.toInt() and 0x03FFFFFF + reduce(result, t2, 1) + } - /** - * Subtracts two numbers modulo 2^255 - 19. - * - * @param result The result. - * @param x The first number to subtract. - * @param y The second number to subtract. - */ - private void sub(int[] result, int[] x, int[] y) - { - int index, borrow; - - // Subtract y from x to generate the intermediate result. - borrow = 0; - for (index = 0; index < NUM_LIMBS_255BIT; ++index) { - borrow = x[index] - y[index] - ((borrow >> 26) & 0x01); - result[index] = borrow & 0x03FFFFFF; - } + /** + * Adds two numbers modulo 2^255 - 19. + * + * @param result The result. + * @param x The first number to add. + * @param y The second number to add. + */ + private fun add(result: IntArray, x: IntArray, y: IntArray) { + var carry = x[0] + y[0] + result[0] = carry and 0x03FFFFFF + for (index in 1 until NUM_LIMBS_255BIT) { + carry = (carry shr 26) + x[index] + y[index] + result[index] = carry and 0x03FFFFFF + } + reduceQuick(result) + } - // If we had a borrow, then the result has gone negative and we - // have to add 2^255 - 19 to the result to make it positive again. - // The top bits of "borrow" will be all 1's if there is a borrow - // or it will be all 0's if there was no borrow. Easiest is to - // conditionally subtract 19 and then mask off the high bits. - borrow = result[0] - ((-((borrow >> 26) & 0x01)) & 19); - result[0] = borrow & 0x03FFFFFF; - for (index = 1; index < NUM_LIMBS_255BIT; ++index) { - borrow = result[index] - ((borrow >> 26) & 0x01); - result[index] = borrow & 0x03FFFFFF; - } - result[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF; - } - - /** - * Conditional swap of two values. - * - * @param select Set to 1 to swap, 0 to leave as-is. - * @param x The first value. - * @param y The second value. - */ - private static void cswap(int select, int[] x, int[] y) - { - int dummy; - select = -select; - for (int index = 0; index < NUM_LIMBS_255BIT; ++index) { - dummy = select & (x[index] ^ y[index]); - x[index] ^= dummy; - y[index] ^= dummy; - } - } + /** + * Subtracts two numbers modulo 2^255 - 19. + * + * @param result The result. + * @param x The first number to subtract. + * @param y The second number to subtract. + */ + private fun sub(result: IntArray, x: IntArray, y: IntArray) { + // Subtract y from x to generate the intermediate result. + var borrow = 0 + for (index in 0 until NUM_LIMBS_255BIT) { + borrow = x[index] - y[index] - ((borrow shr 26) and 0x01) + result[index] = borrow and 0x03FFFFFF + } - /** - * Raise x to the power of (2^250 - 1). - * - * @param result The result. Must not overlap with x. - * @param x The argument. - */ - private void pow250(int[] result, int[] x) - { - int i, j; - - // The big-endian hexadecimal expansion of (2^250 - 1) is: - // 03FFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF - // - // The naive implementation needs to do 2 multiplications per 1 bit and - // 1 multiplication per 0 bit. We can improve upon this by creating a - // pattern 0000000001 ... 0000000001. If we square and multiply the - // pattern by itself we can turn the pattern into the partial results - // 0000000011 ... 0000000011, 0000000111 ... 0000000111, etc. - // This averages out to about 1.1 multiplications per 1 bit instead of 2. + // If we had a borrow, then the result has gone negative and we + // have to add 2^255 - 19 to the result to make it positive again. + // The top bits of "borrow" will be all 1's if there is a borrow + // or it will be all 0's if there was no borrow. Easiest is to + // conditionally subtract 19 and then mask off the high bits. + borrow = result[0] - ((-((borrow shr 26) and 0x01)) and 19) + result[0] = borrow and 0x03FFFFFF + for (index in 1 until NUM_LIMBS_255BIT) { + borrow = result[index] - ((borrow shr 26) and 0x01) + result[index] = borrow and 0x03FFFFFF + } + result[NUM_LIMBS_255BIT - 1] = result[NUM_LIMBS_255BIT - 1] and 0x001FFFFF + } - // Build a pattern of 250 bits in length of repeated copies of 0000000001. - square(A, x); - for (j = 0; j < 9; ++j) - square(A, A); - mul(result, A, x); - for (i = 0; i < 23; ++i) { - for (j = 0; j < 10; ++j) - square(A, A); - mul(result, result, A); - } + /** + * Raise x to the power of (2^250 - 1). + * + * @param result The result. Must not overlap with x. + * @param x The argument. + */ + private fun pow250(result: IntArray, x: IntArray) { + // The big-endian hexadecimal expansion of (2^250 - 1) is: + // 03FFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF + // + // The naive implementation needs to do 2 multiplications per 1 bit and + // 1 multiplication per 0 bit. We can improve upon this by creating a + // pattern 0000000001 ... 0000000001. If we square and multiply the + // pattern by itself we can turn the pattern into the partial results + // 0000000011 ... 0000000011, 0000000111 ... 0000000111, etc. + // This averages out to about 1.1 multiplications per 1 bit instead of 2. - // Multiply bit-shifted versions of the 0000000001 pattern into - // the result to "fill in" the gaps in the pattern. - square(A, result); - mul(result, result, A); - for (j = 0; j < 8; ++j) { - square(A, A); - mul(result, result, A); - } - } + // Build a pattern of 250 bits in length of repeated copies of 0000000001. + square(A, x) + repeat(9) { + square(A, A) + } + mul(result, A, x) + repeat(23) { + repeat(10) { + square(A, A) + } + mul(result, result, A) + } - /** - * Computes the reciprocal of a number modulo 2^255 - 19. - * - * @param result The result. Must not overlap with x. - * @param x The argument. - */ - private void recip(int[] result, int[] x) - { - // The reciprocal is the same as x ^ (p - 2) where p = 2^255 - 19. - // The big-endian hexadecimal expansion of (p - 2) is: - // 7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFEB - // Start with the 250 upper bits of the expansion of (p - 2). - pow250(result, x); + // Multiply bit-shifted versions of the 0000000001 pattern into + // the result to "fill in" the gaps in the pattern. + square(A, result) + mul(result, result, A) + repeat(8) { + square(A, A) + mul(result, result, A) + } + } - // Deal with the 5 lowest bits of (p - 2), 01011, from highest to lowest. - square(result, result); - square(result, result); - mul(result, result, x); - square(result, result); - square(result, result); - mul(result, result, x); - square(result, result); - mul(result, result, x); - } + /** + * Computes the reciprocal of a number modulo 2^255 - 19. + * + * @param result The result. Must not overlap with x. + * @param x The argument. + */ + private fun recip(result: IntArray, x: IntArray) { + // The reciprocal is the same as x ^ (p - 2) where p = 2^255 - 19. + // Start with the 250 upper bits of the expansion of (p - 2). + pow250(result, x) - /** - * Evaluates the curve for every bit in a secret key. - * - * @param s The 32-byte secret key. - */ - private void evalCurve(byte[] s) - { - int sposn = 31; - int sbit = 6; - int svalue = s[sposn] | 0x40; - int swap = 0; - int select; + // Deal with the 5 lowest bits of (p - 2), 01011, from highest to lowest. + repeat(2) { square(result, result) } + mul(result, result, x) + repeat(2) { square(result, result) } + mul(result, result, x) + square(result, result) + mul(result, result, x) + } - // Iterate over all 255 bits of "s" from the highest to the lowest. - // We ignore the high bit of the 256-bit representation of "s". - for (;;) { - // Conditional swaps on entry to this bit but only if we - // didn't swap on the previous bit. - select = (svalue >> sbit) & 0x01; - swap ^= select; - cswap(swap, x_2, x_3); - cswap(swap, z_2, z_3); - swap = select; + /** + * Evaluates the curve for every bit in a secret key. + * + * @param s The 32-byte secret key. + */ + private fun evalCurve(s: ByteArray) { + var sposn = 31 + var sbit = 6 + var svalue = s[sposn].toInt() or 0x40 + var swap = 0 - // Evaluate the curve. - add(A, x_2, z_2); // A = x_2 + z_2 - square(AA, A); // AA = A^2 - sub(B, x_2, z_2); // B = x_2 - z_2 - square(BB, B); // BB = B^2 - sub(E, AA, BB); // E = AA - BB - add(C, x_3, z_3); // C = x_3 + z_3 - sub(D, x_3, z_3); // D = x_3 - z_3 - mul(DA, D, A); // DA = D * A - mul(CB, C, B); // CB = C * B - add(x_3, DA, CB); // x_3 = (DA + CB)^2 - square(x_3, x_3); - sub(z_3, DA, CB); // z_3 = x_1 * (DA - CB)^2 - square(z_3, z_3); - mul(z_3, z_3, x_1); - mul(x_2, AA, BB); // x_2 = AA * BB - mulA24(z_2, E); // z_2 = E * (AA + a24 * E) - add(z_2, z_2, AA); - mul(z_2, z_2, E); + // Iterate over all 255 bits of "s" from the highest to the lowest. + // We ignore the high bit of the 256-bit representation of "s". + while (true) { + // Conditional swaps on entry to this bit but only if we + // didn't swap on the previous bit. + val select = (svalue shr sbit) and 0x01 + swap = swap xor select + cswap(swap, x_2, x_3) + cswap(swap, z_2, z_3) + swap = select - // Move onto the next lower bit of "s". - if (sbit > 0) { - --sbit; - } else if (sposn == 0) { - break; - } else if (sposn == 1) { - --sposn; - svalue = s[sposn] & 0xF8; - sbit = 7; - } else { - --sposn; - svalue = s[sposn]; - sbit = 7; - } - } + // Evaluate the curve. + add(A, x_2, z_2) // A = x_2 + z_2 + square(AA, A) // AA = A^2 + sub(B, x_2, z_2) // B = x_2 - z_2 + square(BB, B) // BB = B^2 + sub(E, AA, BB) // E = AA - BB + add(C, x_3, z_3) // C = x_3 + z_3 + sub(D, x_3, z_3) // D = x_3 - z_3 + mul(DA, D, A) // DA = D * A + mul(CB, C, B) // CB = C * B + add(x_3, DA, CB) // x_3 = (DA + CB)^2 + square(x_3, x_3) + sub(z_3, DA, CB) // z_3 = x_1 * (DA - CB)^2 + square(z_3, z_3) + mul(z_3, z_3, x_1) + mul(x_2, AA, BB) // x_2 = AA * BB + mulA24(z_2, E) // z_2 = E * (AA + a24 * E) + add(z_2, z_2, AA) + mul(z_2, z_2, E) - // Final conditional swaps. - cswap(swap, x_2, x_3); - cswap(swap, z_2, z_3); - } + // Move onto the next lower bit of "s". + if (sbit > 0) { + --sbit + } else if (sposn == 0) { + break + } else if (sposn == 1) { + --sposn + svalue = s[sposn].toInt() and 0xF8 + sbit = 7 + } else { + --sposn + svalue = s[sposn].toInt() + sbit = 7 + } + } - /** - * Evaluates the Curve25519 curve. - * - * @param result Buffer to place the result of the evaluation into. - * @param offset Offset into the result buffer. - * @param privateKey The private key to use in the evaluation. - * @param publicKey The public key to use in the evaluation, or null - * if the base point of the curve should be used. - */ - public static void eval(byte[] result, int offset, byte[] privateKey, byte[] publicKey) - { - Curve25519 state = new Curve25519(); - try { - // Unpack the public key value. If null, use 9 as the base point. - Arrays.fill(state.x_1, 0); - if (publicKey != null) { - // Convert the input value from little-endian into 26-bit limbs. - for (int index = 0; index < 32; ++index) { - int bit = (index * 8) % 26; - int word = (index * 8) / 26; - int value = publicKey[index] & 0xFF; - if (bit <= (26 - 8)) { - state.x_1[word] |= value << bit; - } else { - state.x_1[word] |= value << bit; - state.x_1[word] &= 0x03FFFFFF; - state.x_1[word + 1] |= value >> (26 - bit); - } - } + // Final conditional swaps. + cswap(swap, x_2, x_3) + cswap(swap, z_2, z_3) + } - // Just in case, we reduce the number modulo 2^255 - 19 to - // make sure that it is in range of the field before we start. - // This eliminates values between 2^255 - 19 and 2^256 - 1. - state.reduceQuick(state.x_1); - state.reduceQuick(state.x_1); - } else { - state.x_1[0] = 9; - } + companion object { + // Numbers modulo 2^255 - 19 are broken up into ten 26-bit words. + private const val NUM_LIMBS_255BIT = 10 + private const val NUM_LIMBS_510BIT = 20 - // Initialize the other temporary variables. - Arrays.fill(state.x_2, 0); // x_2 = 1 - state.x_2[0] = 1; - Arrays.fill(state.z_2, 0); // z_2 = 0 - System.arraycopy(state.x_1, 0, state.x_3, 0, state.x_1.length); // x_3 = x_1 - Arrays.fill(state.z_3, 0); // z_3 = 1 - state.z_3[0] = 1; - - // Evaluate the curve for every bit of the private key. - state.evalCurve(privateKey); + /** + * Conditional swap of two values. + * + * @param select Set to 1 to swap, 0 to leave as-is. + * @param x The first value. + * @param y The second value. + */ + private fun cswap(select: Int, x: IntArray, y: IntArray) { + val s = -select + for (index in 0 until NUM_LIMBS_255BIT) { + val dummy = s and (x[index] xor y[index]) + x[index] = x[index] xor dummy + y[index] = y[index] xor dummy + } + } - // Compute x_2 * (z_2 ^ (p - 2)) where p = 2^255 - 19. - state.recip(state.z_3, state.z_2); - state.mul(state.x_2, state.x_2, state.z_3); + /** + * Evaluates the Curve25519 curve. + * + * @param result Buffer to place the result of the evaluation into. + * @param offset Offset into the result buffer. + * @param privateKey The private key to use in the evaluation. + * @param publicKey The public key to use in the evaluation, or null + * if the base point of the curve should be used. + */ + @JvmStatic + fun eval(result: ByteArray, offset: Int, privateKey: ByteArray, publicKey: ByteArray?) { + val state = Curve25519() + try { + // Unpack the public key value. If null, use 9 as the base point. + state.x_1.fill(0) + if (publicKey != null) { + // Convert the input value from little-endian into 26-bit limbs. + for (index in 0 until 32) { + val bit = (index * 8) % 26 + val word = (index * 8) / 26 + val value = publicKey[index].toInt() and 0xFF + if (bit <= (26 - 8)) { + state.x_1[word] = state.x_1[word] or (value shl bit) + } else { + state.x_1[word] = state.x_1[word] or (value shl bit) + state.x_1[word] = state.x_1[word] and 0x03FFFFFF + state.x_1[word + 1] = state.x_1[word + 1] or (value shr (26 - bit)) + } + } - // Convert x_2 into little-endian in the result buffer. - for (int index = 0; index < 32; ++index) { - int bit = (index * 8) % 26; - int word = (index * 8) / 26; - if (bit <= (26 - 8)) - result[offset + index] = (byte)(state.x_2[word] >> bit); - else - result[offset + index] = (byte)((state.x_2[word] >> bit) | (state.x_2[word + 1] << (26 - bit))); - } - } finally { - // Clean up all temporary state before we exit. - state.destroy(); - } - } + // Just in case, we reduce the number modulo 2^255 - 19 to + // make sure that it is in range of the field before we start. + // This eliminates values between 2^255 - 19 and 2^256 - 1. + state.reduceQuick(state.x_1) + state.reduceQuick(state.x_1) + } else { + state.x_1[0] = 9 + } + + // Initialize the other temporary variables. + state.x_2.fill(0) // x_2 = 1 + state.x_2[0] = 1 + state.z_2.fill(0) // z_2 = 0 + state.x_1.copyInto(state.x_3) // x_3 = x_1 + state.z_3.fill(0) // z_3 = 1 + state.z_3[0] = 1 + + + // Evaluate the curve for every bit of the private key. + state.evalCurve(privateKey) + + // Compute x_2 * (z_2 ^ (p - 2)) where p = 2^255 - 19. + state.recip(state.z_3, state.z_2) + state.mul(state.x_2, state.x_2, state.z_3) + + // Convert x_2 into little-endian in the result buffer. + for (index in 0 until 32) { + val bit = (index * 8) % 26 + val word = (index * 8) / 26 + if (bit <= (26 - 8)) { + result[offset + index] = (state.x_2[word] shr bit).toByte() + } else { + result[offset + index] = + ((state.x_2[word] shr bit) or (state.x_2[word + 1] shl (26 - bit))).toByte() + } + } + } finally { + // Clean up all temporary state before we exit. + state.destroy() + } + } + } } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve448.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve448.kt index 715b8549..4dfbe3ea 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve448.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve448.kt @@ -19,7 +19,6 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ - /* Portions of this code were extracted from the p448/arch_32 field @@ -52,360 +51,128 @@ THE SOFTWARE. ---- */ +package com.bitchat.android.noise.southernstorm.crypto -package com.bitchat.android.noise.southernstorm.crypto; - -import java.util.Arrays; /** * Implementation of the Curve448 elliptic curve algorithm. * * Reference: RFC 7748 */ -public final class Curve448 { +class Curve448 private constructor() { + // Numbers modulo 2^448 - 2^224 - 1 are broken up into sixteen 28-bit words. + private val x_1 = IntArray(16) + private val x_2 = IntArray(16) + private val x_3 = IntArray(16) + private val z_2 = IntArray(16) + private val z_3 = IntArray(16) + private val A = IntArray(16) + private val B = IntArray(16) + private val C = IntArray(16) + private val D = IntArray(16) + private val E = IntArray(16) + private val AA = IntArray(16) + private val BB = IntArray(16) + private val DA = IntArray(16) + private val CB = IntArray(16) + private val aa = IntArray(8) + private val bb = IntArray(8) - // Numbers modulo 2^448 - 2^224 - 1 are broken up into sixteen 28-bit words. - private int[] x_1; - private int[] x_2; - private int[] x_3; - private int[] z_2; - private int[] z_3; - private int[] A; - private int[] B; - private int[] C; - private int[] D; - private int[] E; - private int[] AA; - private int[] BB; - private int[] DA; - private int[] CB; - private int[] aa; - private int[] bb; - /** - * Constructs the temporary state holder for Curve448 evaluation. - */ - private Curve448() - { - // Allocate memory for all of the temporary variables we will need. - x_1 = new int [16]; - x_2 = new int [16]; - x_3 = new int [16]; - z_2 = new int [16]; - z_3 = new int [16]; - A = new int [16]; - B = new int [16]; - C = new int [16]; - D = new int [16]; - E = new int [16]; - AA = new int [16]; - BB = new int [16]; - DA = new int [16]; - CB = new int [16]; - aa = new int [8]; - bb = new int [8]; - } + /** + * Destroy all sensitive data in this object. + */ + private fun destroy() { + // Destroy all temporary variables. + x_1.fill(0) + x_2.fill(0) + x_3.fill(0) + z_2.fill(0) + z_3.fill(0) + A.fill(0) + B.fill(0) + C.fill(0) + D.fill(0) + E.fill(0) + AA.fill(0) + BB.fill(0) + DA.fill(0) + CB.fill(0) + aa.fill(0) + bb.fill(0) + } - /** - * Destroy all sensitive data in this object. - */ - private void destroy() { - // Destroy all temporary variables. - Arrays.fill(x_1, 0); - Arrays.fill(x_2, 0); - Arrays.fill(x_3, 0); - Arrays.fill(z_2, 0); - Arrays.fill(z_3, 0); - Arrays.fill(A, 0); - Arrays.fill(B, 0); - Arrays.fill(C, 0); - Arrays.fill(D, 0); - Arrays.fill(E, 0); - Arrays.fill(AA, 0); - Arrays.fill(BB, 0); - Arrays.fill(DA, 0); - Arrays.fill(CB, 0); - Arrays.fill(aa, 0); - Arrays.fill(bb, 0); - } + // p448_mul() + private fun mul(c: IntArray, a: IntArray, b: IntArray) { + var accum0 = 0L + var accum1 = 0L + val mask = (1 shl 28) - 1 - /* Beginning of code imported from Ed448-Goldilocks */ - - private static long widemul_32(int a, int b) - { - return ((long)a) * b; - } + for (i in 0 until 8) { + aa[i] = a[i] + a[i + 8] + bb[i] = b[i] + b[i + 8] + } - // p448_mul() - private void mul(int[] c, int[] a, int[] b) - { - long accum0 = 0, accum1 = 0, accum2 = 0; - int mask = (1<<28) - 1; + for (j in 0 until 8) { + var accum2 = 0L - int i,j; - for (i=0; i<8; i++) { - aa[i] = a[i] + a[i+8]; - bb[i] = b[i] + b[i+8]; - } + for (i in 0..j) { + accum2 += widemul_32(a[j - i], b[i]) + accum1 += widemul_32(aa[j - i], bb[i]) + accum0 += widemul_32(a[8 + j - i], b[8 + i]) + } - for (j=0; j<8; j++) { - accum2 = 0; + accum1 -= accum2 + accum0 += accum2 + accum2 = 0 - for (i=0; i<=j; i++) { - accum2 += widemul_32(a[j-i],b[i]); - accum1 += widemul_32(aa[j-i],bb[i]); - accum0 += widemul_32(a[8+j-i], b[8+i]); - } + for (i in (j + 1) until 8) { + accum0 -= widemul_32(a[8 + j - i], b[i]) + accum2 += widemul_32(aa[8 + j - i], bb[i]) + accum1 += widemul_32(a[16 + j - i], b[8 + i]) + } - accum1 -= accum2; - accum0 += accum2; - accum2 = 0; + accum1 += accum2 + accum0 += accum2 - for (; i<8; i++) { - accum0 -= widemul_32(a[8+j-i], b[i]); - accum2 += widemul_32(aa[8+j-i], bb[i]); - accum1 += widemul_32(a[16+j-i], b[8+i]); - } + c[j] = accum0.toInt() and mask + c[j + 8] = accum1.toInt() and mask - accum1 += accum2; - accum0 += accum2; + accum0 = accum0 ushr 28 + accum1 = accum1 ushr 28 + } - c[j] = ((int)(accum0)) & mask; - c[j+8] = ((int)(accum1)) & mask; + accum0 += accum1 + accum0 += c[8].toLong() + accum1 += c[0].toLong() + c[8] = accum0.toInt() and mask + c[0] = accum1.toInt() and mask - accum0 >>>= 28; - accum1 >>>= 28; - } + accum0 = accum0 ushr 28 + accum1 = accum1 ushr 28 + c[9] += accum0.toInt() + c[1] += accum1.toInt() + } - accum0 += accum1; - accum0 += c[8]; - accum1 += c[0]; - c[8] = ((int)(accum0)) & mask; - c[0] = ((int)(accum1)) & mask; + /* End of code imported from Ed448-Goldilocks */ + /** + * Squares a number modulo 2^448 - 2^224 - 1. + * + * @param result The result. + * @param x The number to square. + */ + private fun square(result: IntArray, x: IntArray) { + mul(result, x, x) + } - accum0 >>>= 28; - accum1 >>>= 28; - c[9] += ((int)(accum0)); - c[1] += ((int)(accum1)); - } - - // p448_mulw() - private static void mulw(int[] c, int[] a, long b) - { - int bhi = (int)(b>>28), blo = ((int)b) & ((1<<28)-1); - - long accum0, accum8; - int mask = (1<<28) - 1; - - int i; - - accum0 = widemul_32(blo, a[0]); - accum8 = widemul_32(blo, a[8]); - accum0 += widemul_32(bhi, a[15]); - accum8 += widemul_32(bhi, a[15] + a[7]); - - c[0] = ((int)accum0) & mask; accum0 >>>= 28; - c[8] = ((int)accum8) & mask; accum8 >>>= 28; - - for (i=1; i<8; i++) { - accum0 += widemul_32(blo, a[i]); - accum8 += widemul_32(blo, a[i+8]); - - accum0 += widemul_32(bhi, a[i-1]); - accum8 += widemul_32(bhi, a[i+7]); - - c[i] = ((int)accum0) & mask; accum0 >>>= 28; - c[i+8] = ((int)accum8) & mask; accum8 >>>= 28; - } - - accum0 += accum8 + c[8]; - c[8] = ((int)accum0) & mask; - c[9] += accum0 >>> 28; - - accum8 += c[0]; - c[0] = ((int)accum8) & mask; - c[1] += accum8 >>> 28; - } - - // p448_weak_reduce - private static void weak_reduce(int[] a) - { - int mask = (1<<28) - 1; - int tmp = a[15] >>> 28; - int i; - a[8] += tmp; - for (i=15; i>0; i--) { - a[i] = (a[i] & mask) + (a[i-1]>>>28); - } - a[0] = (a[0] & mask) + tmp; - } - - // p448_strong_reduce - private static void strong_reduce(int[] a) - { - int mask = (1<<28) - 1; - - /* first, clear high */ - a[8] += a[15]>>>28; - a[0] += a[15]>>>28; - a[15] &= mask; - - /* now the total is less than 2^448 - 2^(448-56) + 2^(448-56+8) < 2p */ - - /* compute total_value - p. No need to reduce mod p. */ - - long scarry = 0; - int i; - for (i=0; i<16; i++) { - scarry = scarry + (a[i] & 0xFFFFFFFFL) - ((i==8)?mask-1:mask); - a[i] = (int)(scarry & mask); - scarry >>= 28; - } - - /* uncommon case: it was >= p, so now scarry = 0 and this = x - * common case: it was < p, so now scarry = -1 and this = x - p + 2^448 - * so let's add back in p. will carry back off the top for 2^448. - */ - - int scarry_mask = (int)(scarry & mask); - long carry = 0; - - /* add it back */ - for (i=0; i<16; i++) { - carry = carry + (a[i] & 0xFFFFFFFFL) + ((i==8)?(scarry_mask&~1):scarry_mask); - a[i] = (int)(carry & mask); - carry >>>= 28; - } - } - - // field_add() - private static void add(int[] out, int[] a, int[] b) - { - for (int i = 0; i < 16; ++i) - out[i] = a[i] + b[i]; - weak_reduce(out); - } - - // field_sub() - private static void sub(int[] out, int[] a, int[] b) - { - int i; - - // p448_sub_RAW(out, a, b) - for (i = 0; i < 16; ++i) - out[i] = a[i] - b[i]; - - // p448_bias(out, 2) - int co1 = ((1 << 28) - 1) * 2; - int co2 = co1 - 2; - for (i = 0; i < 16; ++i) { - if (i != 8) - out[i] += co1; - else - out[i] += co2; - } - - weak_reduce(out); - } - - // p448_serialize() - private static void serialize(byte[] serial, int offset, int[] x) - { - int i,j; - for (i=0; i<8; i++) { - long limb = x[2*i] + (((long)x[2*i+1])<<28); - for (j=0; j<7; j++) { - serial[offset+7*i+j] = (byte)limb; - limb >>= 8; - } - } - } - - private static int is_zero(int x) - { - long xx = x & 0xFFFFFFFFL; - xx--; - return (int)(xx >> 32); - } - - // p448_deserialize() - private static int deserialize(int[] x, byte[] serial, int offset) - { - int i,j; - for (i=0; i<8; i++) { - long out = 0; - for (j=0; j<7; j++) { - out |= (serial[offset+7*i+j] & 0xFFL)<<(8*j); - } - x[2*i] = ((int)out) & ((1<<28)-1); - x[2*i+1] = (int)(out >>> 28); - } - - /* Check for reduction. - * - * The idea is to create a variable ge which is all ones (rather, 56 ones) - * if and only if the low $i$ words of $x$ are >= those of p. - * - * Remember p = little_endian(1111,1111,1111,1111,1110,1111,1111,1111) - */ - int ge = -1, mask = (1<<28)-1; - for (i=0; i<8; i++) { - ge &= x[i]; - } - - /* At this point, ge = 1111 iff bottom are all 1111. Now propagate if 1110, or set if 1111 */ - ge = (ge & (x[8] + 1)) | is_zero(x[8] ^ mask); - - /* Propagate the rest */ - for (i=9; i<16; i++) { - ge &= x[i]; - } - - return ~is_zero(ge ^ mask); - } - - /* End of code imported from Ed448-Goldilocks */ - - /** - * Squares a number modulo 2^448 - 2^224 - 1. - * - * @param result The result. - * @param x The number to square. - */ - private void square(int[] result, int[] x) - { - mul(result, x, x); - } - - /** - * Conditional swap of two values. - * - * @param select Set to 1 to swap, 0 to leave as-is. - * @param x The first value. - * @param y The second value. - */ - private static void cswap(int select, int[] x, int[] y) - { - int dummy; - select = -select; - for (int index = 0; index < 16; ++index) { - dummy = select & (x[index] ^ y[index]); - x[index] ^= dummy; - y[index] ^= dummy; - } - } - - /** - * Computes the reciprocal of a number modulo 2^448 - 2^224 - 1. - * - * @param result The result. Must not overlap with z_2. - * @param z_2 The argument. - */ - private void recip(int[] result, int[] z_2) - { - int posn; - - /* Compute z_2 ^ (p - 2) + /** + * Computes the reciprocal of a number modulo 2^448 - 2^224 - 1. + * + * @param result The result. Must not overlap with z_2. + * @param z_2 The argument. + */ + private fun recip(result: IntArray, z_2: IntArray) { + /* Compute z_2 ^ (p - 2) The value p - 2 is: FF...FEFF...FD, which from highest to lowest is 223 one bits, followed by a zero bit, followed by 222 one bits, @@ -419,204 +186,382 @@ public final class Curve448 { 11110000 and then multiply by the 1111 pattern to get 11111111. We then repeat that to turn 11111111 into 1111111111111111, etc. */ - square(B, z_2); /* Set A to a 4 bit pattern */ - mul(A, B, z_2); - square(B, A); - mul(A, B, z_2); - square(B, A); - mul(A, B, z_2); - square(B, A); /* Set C to a 6 bit pattern */ - mul(C, B, z_2); - square(B, C); - mul(C, B, z_2); - square(B, C); /* Set A to a 8 bit pattern */ - mul(A, B, z_2); - square(B, A); - mul(A, B, z_2); - square(E, A); /* Set E to a 16 bit pattern */ - square(B, E); - for (posn = 1; posn < 4; ++posn) { - square(E, B); - square(B, E); - } - mul(E, B, A); - square(AA, E); /* Set AA to a 32 bit pattern */ - square(B, AA); - for (posn = 1; posn < 8; ++posn) { - square(AA, B); - square(B, AA); - } - mul(AA, B, E); - square(BB, AA); /* Set BB to a 64 bit pattern */ - square(B, BB); - for (posn = 1; posn < 16; ++posn) { - square(BB, B); - square(B, BB); - } - mul(BB, B, AA); - square(DA, BB); /* Set DA to a 128 bit pattern */ - square(B, DA); - for (posn = 1; posn < 32; ++posn) { - square(DA, B); - square(B, DA); - } - mul(DA, B, BB); - square(CB, DA); /* Set CB to a 192 bit pattern */ - square(B, CB); /* 192 = 128 + 64 */ - for (posn = 1; posn < 32; ++posn) { - square(CB, B); - square(B, CB); - } - mul(CB, B, BB); - square(DA, CB); /* Set DA to a 208 bit pattern */ - square(B, DA); /* 208 = 128 + 64 + 16 */ - for (posn = 1; posn < 8; ++posn) { - square(DA, B); - square(B, DA); - } - mul(DA, B, E); - square(CB, DA); /* Set CB to a 216 bit pattern */ - square(B, CB); /* 216 = 128 + 64 + 16 + 8 */ - for (posn = 1; posn < 4; ++posn) { - square(CB, B); - square(B, CB); - } - mul(CB, B, A); - square(DA, CB); /* Set DA to a 222 bit pattern */ - square(B, DA); /* 222 = 128 + 64 + 16 + 8 + 6 */ - for (posn = 1; posn < 3; ++posn) { - square(DA, B); - square(B, DA); - } - mul(DA, B, C); - square(CB, DA); /* Set CB to a 224 bit pattern */ - mul(B, CB, z_2); /* CB = DA|1|0 */ - square(CB, B); - square(BB, CB); /* Set BB to a 446 bit pattern */ - square(B, BB); /* BB = DA|1|0|DA */ - for (posn = 1; posn < 111; ++posn) { - square(BB, B); - square(B, BB); - } - mul(BB, B, DA); - square(B, BB); /* Set result to a 448 bit pattern */ - square(BB, B); /* result = DA|1|0|DA|01 */ - mul(result, BB, z_2); - } + square(B, z_2) /* Set A to a 4 bit pattern */ + mul(A, B, z_2) + square(B, A) + mul(A, B, z_2) + square(B, A) + mul(A, B, z_2) + square(B, A) /* Set C to a 6 bit pattern */ + mul(C, B, z_2) + square(B, C) + mul(C, B, z_2) + square(B, C) /* Set A to a 8 bit pattern */ + mul(A, B, z_2) + square(B, A) + mul(A, B, z_2) + square(E, A) /* Set E to a 16 bit pattern */ + square(B, E) + repeat(3) { + square(E, B) + square(B, E) + } + mul(E, B, A) + square(AA, E) /* Set AA to a 32 bit pattern */ + square(B, AA) + repeat(7) { + square(AA, B) + square(B, AA) + } + mul(AA, B, E) + square(BB, AA) /* Set BB to a 64 bit pattern */ + square(B, BB) + repeat(15) { + square(BB, B) + square(B, BB) + } + mul(BB, B, AA) + square(DA, BB) /* Set DA to a 128 bit pattern */ + square(B, DA) + repeat(31) { + square(DA, B) + square(B, DA) + } + mul(DA, B, BB) + square(CB, DA) /* Set CB to a 192 bit pattern */ + square(B, CB) /* 192 = 128 + 64 */ + repeat(31) { + square(CB, B) + square(B, CB) + } + mul(CB, B, BB) + square(DA, CB) /* Set DA to a 208 bit pattern */ + square(B, DA) /* 208 = 128 + 64 + 16 */ + repeat(7) { + square(DA, B) + square(B, DA) + } + mul(DA, B, E) + square(CB, DA) /* Set CB to a 216 bit pattern */ + square(B, CB) /* 216 = 128 + 64 + 16 + 8 */ + repeat(3) { + square(CB, B) + square(B, CB) + } + mul(CB, B, A) + square(DA, CB) /* Set DA to a 222 bit pattern */ + square(B, DA) /* 222 = 128 + 64 + 16 + 8 + 6 */ + repeat(2) { + square(DA, B) + square(B, DA) + } + mul(DA, B, C) + square(CB, DA) /* Set CB to a 224 bit pattern */ + mul(B, CB, z_2) /* CB = DA|1|0 */ + square(CB, B) + square(BB, CB) /* Set BB to a 446 bit pattern */ + square(B, BB) /* BB = DA|1|0|DA */ + repeat(110) { + square(BB, B) + square(B, BB) + } + mul(BB, B, DA) + square(B, BB) /* Set result to a 448 bit pattern */ + square(BB, B) /* result = DA|1|0|DA|01 */ + mul(result, BB, z_2) + } - /** - * Evaluates the curve for every bit in a secret key. - * - * @param s The 56-byte secret key. - */ - private void evalCurve(byte[] s) - { - int sposn = 55; - int sbit = 7; - int svalue = s[sposn] | 0x80; - int swap = 0; - int select; + /** + * Evaluates the curve for every bit in a secret key. + * + * @param s The 56-byte secret key. + */ + private fun evalCurve(s: ByteArray) { + var sposn = 55 + var sbit = 7 + var svalue = s[sposn].toInt() or 0x80 + var swap = 0 - // Iterate over all 448 bits of "s" from the highest to the lowest. - for (;;) { - // Conditional swaps on entry to this bit but only if we - // didn't swap on the previous bit. - select = (svalue >> sbit) & 0x01; - swap ^= select; - cswap(swap, x_2, x_3); - cswap(swap, z_2, z_3); - swap = select; + // Iterate over all 448 bits of "s" from the highest to the lowest. + while (true) { + // Conditional swaps on entry to this bit but only if we + // didn't swap on the previous bit. + val select = (svalue shr sbit) and 0x01 + swap = swap xor select + cswap(swap, x_2, x_3) + cswap(swap, z_2, z_3) + swap = select - // Evaluate the curve. - add(A, x_2, z_2); // A = x_2 + z_2 - square(AA, A); // AA = A^2 - sub(B, x_2, z_2); // B = x_2 - z_2 - square(BB, B); // BB = B^2 - sub(E, AA, BB); // E = AA - BB - add(C, x_3, z_3); // C = x_3 + z_3 - sub(D, x_3, z_3); // D = x_3 - z_3 - mul(DA, D, A); // DA = D * A - mul(CB, C, B); // CB = C * B - add(z_2, DA, CB); // x_3 = (DA + CB)^2 - square(x_3, z_2); - sub(z_2, DA, CB); // z_3 = x_1 * (DA - CB)^2 - square(x_2, z_2); - mul(z_3, x_1, x_2); - mul(x_2, AA, BB); // x_2 = AA * BB - mulw(z_2, E, 39081); // z_2 = E * (AA + a24 * E) - add(A, AA, z_2); - mul(z_2, E, A); + // Evaluate the curve. + add(A, x_2, z_2) // A = x_2 + z_2 + square(AA, A) // AA = A^2 + sub(B, x_2, z_2) // B = x_2 - z_2 + square(BB, B) // BB = B^2 + sub(E, AA, BB) // E = AA - BB + add(C, x_3, z_3) // C = x_3 + z_3 + sub(D, x_3, z_3) // D = x_3 - z_3 + mul(DA, D, A) // DA = D * A + mul(CB, C, B) // CB = C * B + add(z_2, DA, CB) // x_3 = (DA + CB)^2 + square(x_3, z_2) + sub(z_2, DA, CB) // z_3 = x_1 * (DA - CB)^2 + square(x_2, z_2) + mul(z_3, x_1, x_2) + mul(x_2, AA, BB) // x_2 = AA * BB + mulw(z_2, E, 39081) // z_2 = E * (AA + a24 * E) + add(A, AA, z_2) + mul(z_2, E, A) - // Move onto the next lower bit of "s". - if (sbit > 0) { - --sbit; - } else if (sposn == 0) { - break; - } else if (sposn == 1) { - --sposn; - svalue = s[sposn] & 0xFC; - sbit = 7; - } else { - --sposn; - svalue = s[sposn]; - sbit = 7; - } - } + // Move onto the next lower bit of "s". + if (sbit > 0) { + --sbit + } else if (sposn == 0) { + break + } else if (sposn == 1) { + --sposn + svalue = s[sposn].toInt() and 0xFC + sbit = 7 + } else { + --sposn + svalue = s[sposn].toInt() + sbit = 7 + } + } - // Final conditional swaps. - cswap(swap, x_2, x_3); - cswap(swap, z_2, z_3); - } + // Final conditional swaps. + cswap(swap, x_2, x_3) + cswap(swap, z_2, z_3) + } - /** - * Evaluates the Curve448 curve. - * - * @param result Buffer to place the result of the evaluation into. - * @param offset Offset into the result buffer. - * @param privateKey The private key to use in the evaluation. - * @param publicKey The public key to use in the evaluation, or null - * if the base point of the curve should be used. - * @return Returns true if the curve evaluation was successful, - * false if the publicKey value is out of range. - */ - public static boolean eval(byte[] result, int offset, byte[] privateKey, byte[] publicKey) - { - Curve448 state = new Curve448(); - int success = -1; - try { - // Unpack the public key value. If null, use 5 as the base point. - Arrays.fill(state.x_1, 0); - if (publicKey != null) { - // Convert the input value from little-endian into 28-bit limbs. - // It is possible that the public key is out of range. If so, - // delay reporting that state until the function completes. - success = deserialize(state.x_1, publicKey, 0); - } else { - state.x_1[0] = 5; - } + companion object { + /* Beginning of code imported from Ed448-Goldilocks */ + private fun widemul_32(a: Int, b: Int): Long = (a.toLong()) * b - // Initialize the other temporary variables. - Arrays.fill(state.x_2, 0); // x_2 = 1 - state.x_2[0] = 1; - Arrays.fill(state.z_2, 0); // z_2 = 0 - System.arraycopy(state.x_1, 0, state.x_3, 0, state.x_1.length); // x_3 = x_1 - Arrays.fill(state.z_3, 0); // z_3 = 1 - state.z_3[0] = 1; - - // Evaluate the curve for every bit of the private key. - state.evalCurve(privateKey); + // p448_mulw() + private fun mulw(c: IntArray, a: IntArray, b: Long) { + val bhi = (b shr 28).toInt() + val blo = b.toInt() and ((1 shl 28) - 1) - // Compute x_2 * (z_2 ^ (p - 2)) where p = 2^448 - 2^224 - 1. - state.recip(state.z_3, state.z_2); - state.mul(state.x_1, state.x_2, state.z_3); + var accum0 = widemul_32(blo, a[0]) + var accum8 = widemul_32(blo, a[8]) + accum0 += widemul_32(bhi, a[15]) + accum8 += widemul_32(bhi, a[15] + a[7]) - // Convert x_2 into little-endian in the result buffer. - strong_reduce(state.x_1); - serialize(result, offset, state.x_1); - } finally { - // Clean up all temporary state before we exit. - state.destroy(); - } - return (success & 0x01) != 0; - } + val mask = (1 shl 28) - 1 + + c[0] = accum0.toInt() and mask + accum0 = accum0 ushr 28 + c[8] = accum8.toInt() and mask + accum8 = accum8 ushr 28 + + for (i in 1 until 8) { + accum0 += widemul_32(blo, a[i]) + accum8 += widemul_32(blo, a[i + 8]) + + accum0 += widemul_32(bhi, a[i - 1]) + accum8 += widemul_32(bhi, a[i + 7]) + + c[i] = accum0.toInt() and mask + accum0 = accum0 ushr 28 + c[i + 8] = accum8.toInt() and mask + accum8 = accum8 ushr 28 + } + + accum0 += accum8 + c[8] + c[8] = accum0.toInt() and mask + c[9] += (accum0 ushr 28).toInt() + + accum8 += c[0].toLong() + c[0] = accum8.toInt() and mask + c[1] += (accum8 ushr 28).toInt() + } + + + // p448_weak_reduce + private fun weak_reduce(a: IntArray) { + val mask = (1 shl 28) - 1 + val tmp = a[15] ushr 28 + a[8] += tmp + for (i in 15 downTo 1) { + a[i] = (a[i] and mask) + (a[i - 1] ushr 28) + } + a[0] = (a[0] and mask) + tmp + } + + // p448_strong_reduce + private fun strong_reduce(a: IntArray) { + val mask = (1 shl 28) - 1 + + /* first, clear high */ + a[8] += a[15] ushr 28 + a[0] += a[15] ushr 28 + a[15] = a[15] and mask + + var scarry = 0L + for (i in 0 until 16) { + scarry = scarry + (a[i].toLong() and 0xFFFFFFFFL) - (if (i == 8) mask - 1 else mask) + a[i] = (scarry and mask.toLong()).toInt() + scarry = scarry shr 28 + } + + val scarryMask = (scarry and mask.toLong()).toInt() + var carry = 0L + + for (i in 0 until 16) { + carry += (a[i].toLong() and 0xFFFFFFFFL) + (if (i == 8) scarryMask and 1.inv() else scarryMask) + a[i] = (carry and mask.toLong()).toInt() + carry = carry ushr 28 + } + } + + + // field_add() + private fun add(out: IntArray, a: IntArray, b: IntArray) { + for (i in 0..15) out[i] = a[i] + b[i] + weak_reduce(out) + } + + // field_sub() + private fun sub(out: IntArray, a: IntArray, b: IntArray) { + for (i in 0 until 16) { + out[i] = a[i] - b[i] + } + + val co1 = ((1 shl 28) - 1) * 2 + val co2 = co1 - 2 + for (i in 0 until 16) { + if (i != 8) out[i] += co1 else out[i] += co2 + } + + weak_reduce(out) + } + + // p448_serialize() + private fun serialize(serial: ByteArray, offset: Int, x: IntArray) { + for (i in 0 until 8) { + var limb = x[2 * i].toLong() + (x[2 * i + 1].toLong() shl 28) + for (j in 0 until 7) { + serial[offset + 7 * i + j] = limb.toByte() + limb = limb shr 8 + } + } + } + + + private fun is_zero(x: Int): Int { + var xx = x.toLong() and 0xFFFFFFFFL + xx-- + return (xx shr 32).toInt() + } + + // p448_deserialize() + private fun deserialize(x: IntArray, serial: ByteArray, offset: Int): Int { + for (i in 0 until 8) { + var out = 0L + for (j in 0 until 7) { + out = out or ((serial[offset + 7 * i + j].toLong() and 0xFFL) shl (8 * j)) + } + x[2 * i] = out.toInt() and ((1 shl 28) - 1) + x[2 * i + 1] = (out ushr 28).toInt() + } + + /* Check for reduction. + * + * The idea is to create a variable ge which is all ones (rather, 56 ones) + * if and only if the low $i$ words of $x$ are >= those of p. + * + * Remember p = little_endian(1111,1111,1111,1111,1110,1111,1111,1111) + */ + var ge = -1 + val mask = (1 shl 28) - 1 + for (i in 0 until 8) { + ge = ge and x[i] + } + + /* At this point, ge = 1111 iff bottom are all 1111. Now propagate if 1110, or set if 1111 */ + ge = (ge and (x[8] + 1)) or is_zero(x[8] xor mask) + + /* Propagate the rest */ + for (i in 9 until 16) { + ge = ge and x[i] + } + + + return is_zero(ge xor mask).inv() + } + + /** + * Conditional swap of two values. + * + * @param select Set to 1 to swap, 0 to leave as-is. + * @param x The first value. + * @param y The second value. + */ + private fun cswap(select: Int, x: IntArray, y: IntArray) { + val s = -select + for (index in 0..15) { + val dummy = s and (x[index] xor y[index]) + x[index] = x[index] xor dummy + y[index] = y[index] xor dummy + } + } + + /** + * Evaluates the Curve448 curve. + * + * @param result Buffer to place the result of the evaluation into. + * @param offset Offset into the result buffer. + * @param privateKey The private key to use in the evaluation. + * @param publicKey The public key to use in the evaluation, or null + * if the base point of the curve should be used. + * @return Returns true if the curve evaluation was successful, + * false if the publicKey value is out of range. + */ + @JvmStatic + fun eval( + result: ByteArray, + offset: Int, + privateKey: ByteArray, + publicKey: ByteArray? + ): Boolean { + val state = Curve448() + var success = -1 + try { + // Unpack the public key value. If null, use 5 as the base point. + state.x_1.fill(0) + if (publicKey != null) { + // Convert the input value from little-endian into 28-bit limbs. + // It is possible that the public key is out of range. If so, + // delay reporting that state until the function completes. + success = deserialize(state.x_1, publicKey, 0) + } else { + state.x_1[0] = 5 + } + + // Initialize the other temporary variables. + state.x_2.fill(0) + state.x_2[0] = 1 + state.z_2.fill(0) + state.x_1.copyInto(state.x_3) + state.z_3.fill(0) + state.z_3[0] = 1 + + + // Evaluate the curve for every bit of the private key. + state.evalCurve(privateKey) + + // Compute x_2 * (z_2 ^ (p - 2)) where p = 2^448 - 2^224 - 1. + state.recip(state.z_3, state.z_2) + state.mul(state.x_1, state.x_2, state.z_3) + + // Convert x_2 into little-endian in the result buffer. + strong_reduce(state.x_1) + serialize(result, offset, state.x_1) + } finally { + // Clean up all temporary state before we exit. + state.destroy() + } + return (success and 0x01) != 0 + } + } } From 0963c2a3cc867bceb4ffeaa365a94426e20a3790 Mon Sep 17 00:00:00 2001 From: yet300 Date: Sat, 9 May 2026 15:10:00 +0400 Subject: [PATCH 11/37] Rename .java to .kt --- .../android/noise/southernstorm/crypto/{GHASH.java => GHASH.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/{GHASH.java => GHASH.kt} (100%) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/GHASH.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/GHASH.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/GHASH.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/GHASH.kt From 2c7fad6abc1b2b192c8e36158e5581558cc525a3 Mon Sep 17 00:00:00 2001 From: yet300 Date: Sat, 9 May 2026 15:10:00 +0400 Subject: [PATCH 12/37] Convert GHASH to Kotlin --- .../noise/southernstorm/crypto/GHASH.kt | 308 ++++++++---------- 1 file changed, 144 insertions(+), 164 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/GHASH.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/GHASH.kt index 386438ae..860374e8 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/GHASH.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/GHASH.kt @@ -19,186 +19,166 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ +package com.bitchat.android.noise.southernstorm.crypto -package com.bitchat.android.noise.southernstorm.crypto; - -import java.util.Arrays; - -import com.bitchat.android.noise.southernstorm.protocol.Destroyable; +import com.bitchat.android.noise.southernstorm.protocol.Destroyable /** * Implementation of the GHASH primitive for GCM. */ -public final class GHASH implements Destroyable { +class GHASH : Destroyable { + private val h = LongArray(2) + private val y = ByteArray(16) + private var posn: Int = 0 - private long[] H; - private byte[] Y; - int posn; - - /** - * Constructs a new GHASH object. - */ - public GHASH() - { - H = new long [2]; - Y = new byte [16]; - posn = 0; - } + /** + * Resets this GHASH object with a new key. + * + * @param key The key, which must contain at least 16 bytes. + * @param offset The offset of the first key byte. + */ + fun reset(key: ByteArray, offset: Int) { + h[0] = readBigEndian(key, offset) + h[1] = readBigEndian(key, offset + 8) + y.fill(0) + posn = 0 + } - /** - * Resets this GHASH object with a new key. - * - * @param key The key, which must contain at least 16 bytes. - * @param offset The offset of the first key byte. - */ - public void reset(byte[] key, int offset) - { - H[0] = readBigEndian(key, offset); - H[1] = readBigEndian(key, offset + 8); - Arrays.fill(Y, (byte)0); - posn = 0; - } + /** + * Resets the GHASH object but retains the previous key. + */ + fun reset() { + y.fill(0) + posn = 0 + } - /** - * Resets the GHASH object but retains the previous key. - */ - public void reset() - { - Arrays.fill(Y, (byte)0); - posn = 0; - } + /** + * Updates this GHASH object with more data. + * + * @param data Buffer containing the data. + * @param offset Offset of the first data byte in the buffer. + * @param length The number of bytes from the buffer to hash. + */ + fun update(data: ByteArray, offset: Int, length: Int) { + var currentOffset = offset + var remainingLength = length + while (remainingLength > 0) { + var size = 16 - posn + if (size > remainingLength) size = remainingLength + for (index in 0 until size) { + y[posn + index] = (y[posn + index].toInt() xor data[currentOffset + index].toInt()).toByte() + } + posn += size + remainingLength -= size + currentOffset += size + if (posn == 16) { + gf128Mul(y, h) + posn = 0 + } + } + } - /** - * Updates this GHASH object with more data. - * - * @param data Buffer containing the data. - * @param offset Offset of the first data byte in the buffer. - * @param length The number of bytes from the buffer to hash. - */ - public void update(byte[] data, int offset, int length) - { - while (length > 0) { - int size = 16 - posn; - if (size > length) - size = length; - for (int index = 0; index < size; ++index) - Y[posn + index] ^= data[offset + index]; - posn += size; - length -= size; - offset += size; - if (posn == 16) { - GF128_mul(Y, H); - posn = 0; - } - } - } - - /** - * Finishes the GHASH process and returns the tag. - * - * @param tag Buffer to receive the tag. - * @param offset Offset of the first byte of the tag. - * @param length The length of the tag, which must be less - * than or equal to 16. - */ - public void finish(byte[] tag, int offset, int length) - { - pad(); - System.arraycopy(Y, 0, tag, offset, length); - } - - /** - * Pads the input to a 16-byte boundary. - */ - public void pad() - { - if (posn != 0) { - // Padding involves XOR'ing the rest of state->Y with zeroes, - // which does nothing. Immediately process the next chunk. - GF128_mul(Y, H); - posn = 0; - } - } + /** + * Finishes the GHASH process and returns the tag. + * + * @param tag Buffer to receive the tag. + * @param offset Offset of the first byte of the tag. + * @param length The length of the tag, which must be less + * than or equal to 16. + */ + fun finish(tag: ByteArray, offset: Int, length: Int) { + pad() + y.copyInto(tag, destinationOffset = offset, startIndex = 0, endIndex = length) + } - /** - * Pads the input to a 16-byte boundary and then adds a block - * containing the AD and data lengths. - * - * @param adLen Length of the associated data in bytes. - * @param dataLen Length of the data in bytes. - */ - public void pad(long adLen, long dataLen) - { - byte[] temp = new byte [16]; - try { - pad(); - writeBigEndian(temp, 0, adLen * 8); - writeBigEndian(temp, 8, dataLen * 8); - update(temp, 0, 16); - } finally { - Arrays.fill(temp, (byte)0); - } - } + /** + * Pads the input to a 16-byte boundary. + */ + fun pad() { + if (posn != 0) { + // Padding involves XOR'ing the rest of state->Y with zeroes, + // which does nothing. Immediately process the next chunk. + gf128Mul(y, h) + posn = 0 + } + } - @Override - public void destroy() { - Arrays.fill(H, 0L); - Arrays.fill(Y, (byte)0); - } + /** + * Pads the input to a 16-byte boundary and then adds a block + * containing the AD and data lengths. + * + * @param adLen Length of the associated data in bytes. + * @param dataLen Length of the data in bytes. + */ + fun pad(adLen: Long, dataLen: Long) { + val temp = ByteArray(16) + try { + pad() + writeBigEndian(temp, 0, adLen * 8) + writeBigEndian(temp, 8, dataLen * 8) + update(temp, 0, 16) + } finally { + temp.fill(0) + } + } - private static long readBigEndian(byte[] buf, int offset) - { - return ((buf[offset] & 0xFFL) << 56) | - ((buf[offset + 1] & 0xFFL) << 48) | - ((buf[offset + 2] & 0xFFL) << 40) | - ((buf[offset + 3] & 0xFFL) << 32) | - ((buf[offset + 4] & 0xFFL) << 24) | - ((buf[offset + 5] & 0xFFL) << 16) | - ((buf[offset + 6] & 0xFFL) << 8) | - (buf[offset + 7] & 0xFFL); - } + override fun destroy() { + h.fill(0L) + y.fill(0) + } - private static void writeBigEndian(byte[] buf, int offset, long value) - { - buf[offset] = (byte)(value >> 56); - buf[offset + 1] = (byte)(value >> 48); - buf[offset + 2] = (byte)(value >> 40); - buf[offset + 3] = (byte)(value >> 32); - buf[offset + 4] = (byte)(value >> 24); - buf[offset + 5] = (byte)(value >> 16); - buf[offset + 6] = (byte)(value >> 8); - buf[offset + 7] = (byte)value; - } + companion object { + private fun readBigEndian(buf: ByteArray, offset: Int): Long { + return ((buf[offset].toLong() and 0xFFL) shl 56) or + ((buf[offset + 1].toLong() and 0xFFL) shl 48) or + ((buf[offset + 2].toLong() and 0xFFL) shl 40) or + ((buf[offset + 3].toLong() and 0xFFL) shl 32) or + ((buf[offset + 4].toLong() and 0xFFL) shl 24) or + ((buf[offset + 5].toLong() and 0xFFL) shl 16) or + ((buf[offset + 6].toLong() and 0xFFL) shl 8) or + (buf[offset + 7].toLong() and 0xFFL) + } - private static void GF128_mul(byte[] Y, long[] H) - { - long Z0 = 0; // Z = 0 - long Z1 = 0; - long V0 = H[0]; // V = H - long V1 = H[1]; + private fun writeBigEndian(buf: ByteArray, offset: Int, value: Long) { + buf[offset] = (value shr 56).toByte() + buf[offset + 1] = (value shr 48).toByte() + buf[offset + 2] = (value shr 40).toByte() + buf[offset + 3] = (value shr 32).toByte() + buf[offset + 4] = (value shr 24).toByte() + buf[offset + 5] = (value shr 16).toByte() + buf[offset + 6] = (value shr 8).toByte() + buf[offset + 7] = value.toByte() + } - // Multiply Z by V for the set bits in Y, starting at the top. - // This is a very simple bit by bit version that may not be very - // fast but it should be resistant to cache timing attacks. - for (int posn = 0; posn < 16; ++posn) { - int value = Y[posn] & 0xFF; - for (int bit = 7; bit >= 0; --bit) { - // Extract the high bit of "value" and turn it into a mask. - long mask = -((long)((value >> bit) & 0x01)); + private fun gf128Mul(y: ByteArray, h: LongArray) { + var z0: Long = 0 // Z = 0 + var z1: Long = 0 + var v0 = h[0] // V = H + var v1 = h[1] - // XOR V with Z if the bit is 1. - Z0 ^= (V0 & mask); - Z1 ^= (V1 & mask); + // Multiply Z by V for the set bits in Y, starting at the top. + // This is a very simple bit by bit version that may not be very + // fast but it should be resistant to cache timing attacks. + for (pos in 0..15) { + val value = y[pos].toInt() and 0xFF + for (bit in 7 downTo 0) { + // Extract the high bit of "value" and turn it into a mask. + var mask = -(((value shr bit) and 0x01).toLong()) - // Rotate V right by 1 bit. - mask = ((~(V1 & 0x01)) + 1) & 0xE100000000000000L; - V1 = (V1 >>> 1) | (V0 << 63); - V0 = (V0 >>> 1) ^ mask; - } - } + // XOR V with Z if the bit is 1. + z0 = z0 xor (v0 and mask) + z1 = z1 xor (v1 and mask) - // We have finished the block so copy Z into Y and byte-swap. - writeBigEndian(Y, 0, Z0); - writeBigEndian(Y, 8, Z1); - } + // Rotate V right by 1 bit. + mask = (((v1 and 0x01L).inv()) + 1) and -0x1f00000000000000L + v1 = (v1 ushr 1) or (v0 shl 63) + v0 = (v0 ushr 1) xor mask + } + } + + // We have finished the block so copy Z into Y and byte-swap. + writeBigEndian(y, 0, z0) + writeBigEndian(y, 8, z1) + } + } } From 10a84ca56c8ed013c170b7cb4a5f8462c988f0ee Mon Sep 17 00:00:00 2001 From: yet300 Date: Sat, 9 May 2026 16:28:23 +0400 Subject: [PATCH 13/37] Rename .java to .kt --- .../noise/southernstorm/crypto/{NewHope.java => NewHope.kt} | 0 .../noise/southernstorm/crypto/{NewHopeTor.java => NewHopeTor.kt} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/{NewHope.java => NewHope.kt} (100%) rename app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/{NewHopeTor.java => NewHopeTor.kt} (100%) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHope.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHope.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHope.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHope.kt diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHopeTor.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHopeTor.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHopeTor.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHopeTor.kt From 5693bbfcd8e24d053302ae27c89645be568ad413 Mon Sep 17 00:00:00 2001 From: yet300 Date: Sat, 9 May 2026 16:28:23 +0400 Subject: [PATCH 14/37] refactor(crypto): migrate NewHope and NewHopeTor implementations to Kotlin - Ported the NewHope and NewHopeTor key exchange algorithms from Java to idiomatic Kotlin. - Maintained strict functional parity with the original verified Java implementation. - Preserved all necessary cryptographic lookup tables (NTT Montgomery, bit-reversal) to ensure mathematical correctness. - Converted Keccak-f[1600] and ChaCha20 core algorithms into more compact Kotlin structures while retaining constant-time execution safety. - Ensured seamless Java interoperability by using `const val` for protocol constants like `SENDABYTES` and `SENDBBYTES`. - Improved overall code readability and removed legacy/redundant constructs. --- .../noise/southernstorm/crypto/NewHope.kt | 2340 ++++++----------- .../noise/southernstorm/crypto/NewHopeTor.kt | 1084 +------- 2 files changed, 907 insertions(+), 2517 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHope.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHope.kt index df1d8360..2266f7fc 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHope.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHope.kt @@ -5,11 +5,10 @@ * Original authors: Erdem Alkim, Léo Ducas, Thomas Pöppelmann, Peter Schwabe * Java port: Rhys Weatherley */ +package com.bitchat.android.noise.southernstorm.crypto -package com.bitchat.android.noise.southernstorm.crypto; +import java.security.SecureRandom -import java.security.SecureRandom; -import java.util.Arrays; /** * NewHope key exchange algorithm. @@ -19,1587 +18,814 @@ import java.util.Arrays; * * @see NewHopeTor */ -public class NewHope { - - // -------------- params.h -------------- - - static final int PARAM_N = 1024; - static final int PARAM_Q = 12289; - static final int POLY_BYTES = 1792; - static final int SEEDBYTES = 32; - static final int RECBYTES = 256; - - /** - * Number of bytes in the public key value sent by Alice. - */ - public static final int SENDABYTES = POLY_BYTES + SEEDBYTES; - - /** - * Number of bytes in the public key value sent by Bob. - */ - public static final int SENDBBYTES = POLY_BYTES + RECBYTES; - - /** - * Number of bytes in shared secret values computed by shareda() and sharedb(). - */ - public static final int SHAREDBYTES = 32; - - // -------------- newhope.c -------------- - - private Poly sk; - - /** - * Constructs a NewHope object. - */ - public NewHope() - { - sk = null; - } - - @Override - protected void finalize() - { - destroy(); - } - - /** - * Destroys sensitive material in this object. - * - * This function should be called once the application has finished - * with the private key contained in this object. This function - * will also be called when the object is finalized, but the point - * of finalization is unpredictable. This function provides a more - * predictable place where the sensitive data is destroyed. - */ - public void destroy() - { - if (sk != null) { - sk.destroy(); - sk = null; - } - } - - /** - * Generates the keypair for Alice. - * - * @param send Buffer to place the public key for Alice in, to be sent to Bob. - * @param sendOffset Offset of the first byte in the send buffer to populate. - * - * The send buffer must have space for at least NewHope.SENDABYTES bytes - * starting at sendOffset. - * - * @see #sharedb(byte[], int, byte[], int, byte[], int) - * @see #shareda(byte[], int, byte[], int) - */ - public void keygen(byte[] send, int sendOffset) - { - Poly a = new Poly(); - Poly e = new Poly(); - Poly r = new Poly(); - Poly pk = new Poly(); - byte[] seed = new byte [SEEDBYTES + 32]; - byte[] noiseseed = new byte [32]; - - try { - randombytes(seed); - sha3256(seed, 0, seed, 0, SEEDBYTES); /* Don't send output of system RNG */ - System.arraycopy(seed, SEEDBYTES, noiseseed, 0, 32); - - uniform(a.coeffs, seed); - - if (sk == null) - sk = new Poly(); - sk.getnoise(noiseseed,(byte)0); - sk.ntt(); - - e.getnoise(noiseseed,(byte)1); - e.ntt(); - - r.pointwise(sk,a); - pk.add(e,r); - - encode_a(send, sendOffset, pk, seed); - } finally { - a.destroy(); - e.destroy(); - r.destroy(); - pk.destroy(); - Arrays.fill(seed, (byte)0); - Arrays.fill(noiseseed, (byte)0); - } - } - - /** - * Generates the public key and shared secret for Bob. - * - * @param sharedkey Buffer to place the shared secret for Bob in. - * @param sharedkeyOffset Offset of the first byte in the sharedkey buffer to populate. - * @param send Buffer to place the public key for Bob in to be sent to Alice. - * @param sendOffset Offset of the first byte in the send buffer to populate. - * @param received Buffer containing the public key value received from Alice. - * @param receivedOffset Offset of the first byte of the value received from Alice. - * - * The sharedkey buffer must have space for at least NewHope.SHAREDBYTES - * bytes starting at sharedkeyOffset. - * - * The send buffer must have space for at least NewHope.SENDBBYTES bytes - * starting at sendOffset. - * - * The received buffer must have space for at least NewHope.SENDABYTES - * bytes starting at receivedOffset. - * - * @see #shareda(byte[], int, byte[], int) - * @see #keygen(byte[], int) - */ - public void sharedb(byte[] sharedkey, int sharedkeyOffset, - byte[] send, int sendOffset, - byte[] received, int receivedOffset) - { - Poly sp = new Poly(); - Poly ep = new Poly(); - Poly v = new Poly(); - Poly a = new Poly(); - Poly pka = new Poly(); - Poly c = new Poly(); - Poly epp = new Poly(); - Poly bp = new Poly(); - byte[] seed = new byte [SEEDBYTES]; - byte[] noiseseed = new byte [32]; - byte[] skey = new byte [32]; - - try { - randombytes(noiseseed); - - decode_a(pka, seed, received, receivedOffset); - uniform(a.coeffs, seed); - - sp.getnoise(noiseseed,(byte)0); - sp.ntt(); - ep.getnoise(noiseseed,(byte)1); - ep.ntt(); - - bp.pointwise(a, sp); - bp.add(bp, ep); - - v.pointwise(pka, sp); - v.invntt(); - - epp.getnoise(noiseseed,(byte)2); - v.add(v, epp); - - helprec(c, v, noiseseed, (byte)3); - - encode_b(send, sendOffset, bp, c); - - rec(skey, v, c); - - sha3256(sharedkey, sharedkeyOffset, skey, 0, 32); - } finally { - sp.destroy(); - ep.destroy(); - v.destroy(); - a.destroy(); - pka.destroy(); - c.destroy(); - epp.destroy(); - bp.destroy(); - Arrays.fill(seed, (byte)0); - Arrays.fill(noiseseed, (byte)0); - Arrays.fill(skey, (byte)0); - } - } - - /** - * Generates the shared secret for Alice. - * - * @param sharedkey Buffer to place the shared secret for Alice in. - * @param sharedkeyOffset Offset of the first byte in the sharedkey buffer to populate. - * @param received Buffer containing the public key value received from Bob. - * @param receivedOffset Offset of the first byte of the value received from Bob. - * - * The sharedkey buffer must have space for at least NewHope.SHAREDBYTES - * bytes starting at sharedkeyOffset. - * - * The received buffer must have space for at least NewHope.SENDBBYTES bytes - * starting at receivedOffset. - * - * @see #shareda(byte[], int, byte[], int) - * @see #keygen(byte[], int) - */ - public void shareda(byte[] sharedkey, int sharedkeyOffset, - byte[] received, int receivedOffset) - { - Poly v = new Poly(); - Poly bp = new Poly(); - Poly c = new Poly(); - byte[] skey = new byte [32]; - - try { - decode_b(bp, c, received, receivedOffset); - - v.pointwise(sk,bp); - v.invntt(); - - rec(skey, v, c); - - sha3256(sharedkey, sharedkeyOffset, skey, 0, 32); - } finally { - v.destroy(); - bp.destroy(); - c.destroy(); - Arrays.fill(skey, (byte)0); - } - } - - /** - * Generates random bytes for use in the NewHope implementation. - * - * @param buffer The buffer to fill with random bytes. - * - * This function may be overridden in subclasses to provide a better - * random number generator or to provide static data for test vectors. - */ - protected void randombytes(byte[] buffer) - { - SecureRandom random = new SecureRandom(); - random.nextBytes(buffer); - } - - private static void encode_a(byte[] r, int roffset, Poly pk, byte[] seed) - { - int i; - pk.tobytes(r, roffset); - for(i=0;i> 2) & 0x03); - c.coeffs[4*i+2] = (char)((r[POLY_BYTES+roffset+i] >> 4) & 0x03); - c.coeffs[4*i+3] = (char)(((r[POLY_BYTES+roffset+i] & 0xff) >> 6)); - } - } - - // -------------- poly.c -------------- - - private class Poly - { - public char[] coeffs; - - public Poly() - { - coeffs = new char [PARAM_N]; - } - - protected void finalize() - { - destroy(); - } - - public void destroy() - { - Arrays.fill(coeffs, (char)0); - } - - public void frombytes(byte[] a, int offset) - { - int i; - for (i = 0; i < PARAM_N/4; i++) - { - coeffs[4*i+0] = (char)( (a[offset+7*i+0] & 0xff) | ((a[offset+7*i+1] & 0x3f) << 8)); - coeffs[4*i+1] = (char)(((a[offset+7*i+1] & 0xc0) >> 6) | ((a[offset+7*i+2] & 0xff) << 2) | ((a[offset+7*i+3] & 0x0f) << 10)); - coeffs[4*i+2] = (char)(((a[offset+7*i+3] & 0xf0) >> 4) | ((a[offset+7*i+4] & 0xff) << 4) | ((a[offset+7*i+5] & 0x03) << 12)); - coeffs[4*i+3] = (char)(((a[offset+7*i+5] & 0xfc) >> 2) | ((a[offset+7*i+6] & 0xff) << 6)); - } - } - - public void tobytes(byte[] r, int offset) - { - int i; - int t0,t1,t2,t3,m; - int c; - for (i = 0; i < PARAM_N/4; i++) - { - t0 = barrett_reduce(coeffs[4*i+0]); //Make sure that coefficients have only 14 bits - t1 = barrett_reduce(coeffs[4*i+1]); - t2 = barrett_reduce(coeffs[4*i+2]); - t3 = barrett_reduce(coeffs[4*i+3]); - - m = t0 - PARAM_Q; - c = m; - c >>= 15; - t0 = m ^ ((t0^m)&c); // >= 15; - t1 = m ^ ((t1^m)&c); // >= 15; - t2 = m ^ ((t2^m)&c); // >= 15; - t3 = m ^ ((t3^m)&c); // > 8) | (t1 << 6)); - r[offset+7*i+2] = (byte)(t1 >> 2); - r[offset+7*i+3] = (byte)((t1 >> 10) | (t2 << 4)); - r[offset+7*i+4] = (byte)(t2 >> 4); - r[offset+7*i+5] = (byte)((t2 >> 12) | (t3 << 2)); - r[offset+7*i+6] = (byte)(t3 >> 6); - } - } - - public void getnoise(byte[] seed, byte nonce) - { - byte[] buf = new byte [4*PARAM_N]; - int /*t, d,*/ a, b; - int i/*,j*/; - - try { - crypto_stream_chacha20(buf,0,4*PARAM_N,nonce,seed); - - for(i=0;i>> j) & 0x01010101; - a = ((d >>> 8) & 0xff) + (d & 0xff); - b = (d >>> 24) + ((d >>> 16) & 0xff); - - What the above is doing is reading 32-bit words from buf and then - setting a and b to the number of 1 bits in the low and high 16 bits. - We instead use the following technique from "Bit Twiddling Hacks", - modified for 16-bit quantities: - - https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel - */ - a = (buf[4*i] & 0xff) | (((buf[4*i+1]) & 0xff) << 8); - a = a - ((a >> 1) & 0x5555); - a = (a & 0x3333) + ((a >> 2) & 0x3333); - a = ((a >> 4) + a) & 0x0F0F; - a = ((a >> 8) + a) & 0x00FF; - - b = (buf[4*i+2] & 0xff) | (((buf[4*i+3]) & 0xff) << 8); - b = b - ((b >> 1) & 0x5555); - b = (b & 0x3333) + ((b >> 2) & 0x3333); - b = ((b >> 4) + b) & 0x0F0F; - b = ((b >> 8) + b) & 0x00FF; - - coeffs[i] = (char)(a + PARAM_Q - b); - } - } finally { - Arrays.fill(buf, (byte)0); - } - } - - public void pointwise(Poly a, Poly b) - { - int i; - int t; - for(i=0;i SHAKE128_RATE*nblocks-2) - { - nblocks=1; - shake128_squeezeblocks(buf,0,nblocks,state); - pos = 0; - } - } - } finally { - Arrays.fill(state, 0); - Arrays.fill(buf, (byte)0); - } - } - - // -------------- reduce.c -------------- - - private static final int qinv = 12287; // -inverse_mod(p,2^18) - private static final int rlog = 18; - - private static int montgomery_reduce(int a) - { - int u; - - u = (a * qinv); - u &= ((1<>> 18; - } - - private static int barrett_reduce(int a) - { - int u; - a &= 0xffff; - u = (a * 5) >> 16; - u *= PARAM_Q; - a -= u; - return a & 0xffff; - } - - // -------------- error_correction.c -------------- - - private static int abs(int v) - { - int mask = v >> 31; - return (v ^ mask) - mask; - } - - private static int f(int[] v0, int v0offset, int[] v1, int v1offset, int x) - { - int xit, t, r, b; - - // Next 6 lines compute t = x/PARAM_Q; - b = x*2730; - t = b >> 25; - b = x - t*12289; - b = 12288 - b; - b >>= 31; - t -= b; - - r = t & 1; - xit = (t>>1); - v0[v0offset] = xit+r; // v0 = round(x/(2*PARAM_Q)) - - t -= 1; - r = t & 1; - v1[v1offset] = (t>>1)+r; - - return abs(x-((v0[v0offset])*2*PARAM_Q)); - } - - private static int g(int x) - { - int t,c,b; - - // Next 6 lines compute t = x/(4*PARAM_Q); - b = x*2730; - t = b >> 27; - b = x - t*49156; - b = 49155 - b; - b >>= 31; - t -= b; - - c = t & 1; - t = (t >> 1) + c; // t = round(x/(8*PARAM_Q)) - - t *= 8*PARAM_Q; - - return abs(t - x); - } - - private static int LDDecode(int xi0, int xi1, int xi2, int xi3) - { - int t; - - t = g(xi0); - t += g(xi1); - t += g(xi2); - t += g(xi3); - - t -= 8*PARAM_Q; - t >>= 31; - return t&1; - } - - private static void helprec(Poly c, Poly v, byte[] seed, byte nonce) - { - int[] v0 = new int [8]; - int v_tmp0,v_tmp1,v_tmp2,v_tmp3; - int k; - int rbit; - byte[] rand = new byte [32]; - int i; - - try { - crypto_stream_chacha20(rand,0,32,((long)nonce) << 56,seed); - - for(i=0; i<256; i++) - { - rbit = (rand[i>>3] >> (i&7)) & 1; - - k = f(v0,0, v0,4, 8*v.coeffs[ 0+i] + 4*rbit); - k += f(v0,1, v0,5, 8*v.coeffs[256+i] + 4*rbit); - k += f(v0,2, v0,6, 8*v.coeffs[512+i] + 4*rbit); - k += f(v0,3, v0,7, 8*v.coeffs[768+i] + 4*rbit); - - k = (2*PARAM_Q-1-k) >> 31; - - v_tmp0 = ((~k) & v0[0]) ^ (k & v0[4]); - v_tmp1 = ((~k) & v0[1]) ^ (k & v0[5]); - v_tmp2 = ((~k) & v0[2]) ^ (k & v0[6]); - v_tmp3 = ((~k) & v0[3]) ^ (k & v0[7]); - - c.coeffs[ 0+i] = (char)((v_tmp0 - v_tmp3) & 3); - c.coeffs[256+i] = (char)((v_tmp1 - v_tmp3) & 3); - c.coeffs[512+i] = (char)((v_tmp2 - v_tmp3) & 3); - c.coeffs[768+i] = (char)(( -k + 2*v_tmp3) & 3); - } - } finally { - Arrays.fill(v0, 0); - Arrays.fill(rand, (byte)0); - } - } - - private static void rec(byte[] key, Poly v, Poly c) - { - int i; - int tmp0,tmp1,tmp2,tmp3; - - for(i=0;i<32;i++) - key[i] = 0; - - for(i=0; i<256; i++) - { - char c768 = c.coeffs[768+i]; - tmp0 = 16*PARAM_Q + 8*(int)v.coeffs[ 0+i] - PARAM_Q * (2*c.coeffs[ 0+i]+c768); - tmp1 = 16*PARAM_Q + 8*(int)v.coeffs[256+i] - PARAM_Q * (2*c.coeffs[256+i]+c768); - tmp2 = 16*PARAM_Q + 8*(int)v.coeffs[512+i] - PARAM_Q * (2*c.coeffs[512+i]+c768); - tmp3 = 16*PARAM_Q + 8*(int)v.coeffs[768+i] - PARAM_Q * ( c768); - - key[i>>3] |= LDDecode(tmp0, tmp1, tmp2, tmp3) << (i & 7); - } - } - - // -------------- ntt.c -------------- - - private static final int bitrev_table_combined[/*496*/] = { - 524289,262146,786435,131076,655365,393222,917511,65544, - 589833,327690,851979,196620,720909,458766,983055,32784, - 557073,294930,819219,163860,688149,426006,950295,98328, - 622617,360474,884763,229404,753693,491550,1015839,540705, - 278562,802851,147492,671781,409638,933927,81960,606249, - 344106,868395,213036,737325,475182,999471,573489,311346, - 835635,180276,704565,442422,966711,114744,639033,376890, - 901179,245820,770109,507966,1032255,532545,270402,794691, - 139332,663621,401478,925767,598089,335946,860235,204876, - 729165,467022,991311,565329,303186,827475,172116,696405, - 434262,958551,106584,630873,368730,893019,237660,761949, - 499806,1024095,548961,286818,811107,155748,680037,417894, - 942183,614505,352362,876651,221292,745581,483438,1007727, - 581745,319602,843891,188532,712821,450678,974967,647289, - 385146,909435,254076,778365,516222,1040511,528513,266370, - 790659,659589,397446,921735,594057,331914,856203,200844, - 725133,462990,987279,561297,299154,823443,168084,692373, - 430230,954519,626841,364698,888987,233628,757917,495774, - 1020063,544929,282786,807075,676005,413862,938151,610473, - 348330,872619,217260,741549,479406,1003695,577713,315570, - 839859,708789,446646,970935,643257,381114,905403,250044, - 774333,512190,1036479,536769,274626,798915,667845,405702, - 929991,602313,340170,864459,733389,471246,995535,569553, - 307410,831699,700629,438486,962775,635097,372954,897243, - 241884,766173,504030,1028319,553185,291042,815331,684261, - 422118,946407,618729,356586,880875,749805,487662,1011951, - 585969,323826,848115,717045,454902,979191,651513,389370, - 913659,782589,520446,1044735,526593,788739,657669,395526, - 919815,592137,329994,854283,723213,461070,985359,559377, - 297234,821523,690453,428310,952599,624921,362778,887067, - 755997,493854,1018143,543009,805155,674085,411942,936231, - 608553,346410,870699,739629,477486,1001775,575793,837939, - 706869,444726,969015,641337,379194,903483,772413,510270, - 1034559,534849,796995,665925,403782,928071,600393,862539, - 731469,469326,993615,567633,829779,698709,436566,960855, - 633177,371034,895323,764253,502110,1026399,551265,813411, - 682341,420198,944487,616809,878955,747885,485742,1010031, - 584049,846195,715125,452982,977271,649593,911739,780669, - 518526,1042815,530817,792963,661893,924039,596361,858507, - 727437,465294,989583,563601,825747,694677,432534,956823, - 629145,891291,760221,498078,1022367,547233,809379,678309, - 940455,612777,874923,743853,481710,1005999,580017,842163, - 711093,973239,645561,907707,776637,514494,1038783,539073, - 801219,670149,932295,604617,866763,735693,997839,571857, - 834003,702933,965079,637401,899547,768477,506334,1030623, - 555489,817635,686565,948711,621033,883179,752109,1014255, - 588273,850419,719349,981495,653817,915963,784893,1047039, - 787971,656901,919047,591369,853515,722445,984591,558609, - 820755,689685,951831,624153,886299,755229,1017375,804387, - 673317,935463,607785,869931,738861,1001007,837171,706101, - 968247,640569,902715,771645,1033791,796227,665157,927303, - 861771,730701,992847,829011,697941,960087,632409,894555, - 763485,1025631,812643,681573,943719,878187,747117,1009263, - 845427,714357,976503,910971,779901,1042047,792195,923271, - 857739,726669,988815,824979,693909,956055,890523,759453, - 1021599,808611,939687,874155,743085,1005231,841395,972471, - 906939,775869,1038015,800451,931527,865995,997071,833235, - 964311,898779,767709,1029855,816867,947943,882411,1013487, - 849651,980727,915195,1046271,921351,855819,986895,823059, - 954135,888603,1019679,937767,872235,1003311,970551,905019, - 1036095,929607,995151,962391,896859,1027935,946023,1011567, - 978807,1044351,991119,958359,1023903,1007535,1040319,1032159 - }; - - // Modified version of bitrev_vector() from the C reference code - // that reduces the number of array bounds checks on the bitrev_table - // from 1024 to 496. The values in the combined table are encoded - // as (i + (r * PARAM_N)) where i and r are the indices to swap. - // The pseudo-code to generate this combined table is: - // p = 0; - // for (i = 0; i < PARAM_N; i++) { - // r = bitrev_table[i]; - // if (i < r) - // bitrev_table_combined[p++] = i + (r * PARAM_N); - // } - private static void bitrev_vector(char[] poly) - { - int i,r,p; - char tmp; - - for(p = 0; p < 496; ++p) - { - int indices = bitrev_table_combined[p]; - i = indices & 0x03FF; - r = indices >> 10; - tmp = poly[i]; - poly[i] = poly[r]; - poly[r] = tmp; - } - } - - private static void mul_coefficients(char[] poly, char[] factors) - { - int i; - - for(i = 0; i < PARAM_N; i++) - poly[i] = (char)montgomery_reduce((poly[i] * factors[i])); - } - - /* GS_bo_to_no; omegas need to be in Montgomery domain */ - private static void ntt_global(char[] a, char[] omega) - { - int i, start, j, jTwiddle, distance; - char temp, W; - - - for(i=0;i<10;i+=2) - { - // Even level - distance = (1<>> (64 - offset)); - } - - private static long load64(byte[] x, int offset) - { - long r = 0; - - for (int i = 0; i < 8; ++i) { - r |= ((long)(x[offset+i] & 0xff)) << (8 * i); - } - return r; - } - - private static void store64(byte[] x, int offset, long u) - { - int i; - - for(i=0; i<8; ++i) { - x[offset+i] = (byte)u; - u >>= 8; - } - } - - private static final long[] KeccakF_RoundConstants = - { - 0x0000000000000001L, - 0x0000000000008082L, - 0x800000000000808aL, - 0x8000000080008000L, - 0x000000000000808bL, - 0x0000000080000001L, - 0x8000000080008081L, - 0x8000000000008009L, - 0x000000000000008aL, - 0x0000000000000088L, - 0x0000000080008009L, - 0x000000008000000aL, - 0x000000008000808bL, - 0x800000000000008bL, - 0x8000000000008089L, - 0x8000000000008003L, - 0x8000000000008002L, - 0x8000000000000080L, - 0x000000000000800aL, - 0x800000008000000aL, - 0x8000000080008081L, - 0x8000000000008080L, - 0x0000000080000001L, - 0x8000000080008008L - }; - - - private static void KeccakF1600_StatePermute(long[] state) - { - int round; - - long Aba, Abe, Abi, Abo, Abu; - long Aga, Age, Agi, Ago, Agu; - long Aka, Ake, Aki, Ako, Aku; - long Ama, Ame, Ami, Amo, Amu; - long Asa, Ase, Asi, Aso, Asu; - long BCa, BCe, BCi, BCo, BCu; - long Da, De, Di, Do, Du; - long Eba, Ebe, Ebi, Ebo, Ebu; - long Ega, Ege, Egi, Ego, Egu; - long Eka, Eke, Eki, Eko, Eku; - long Ema, Eme, Emi, Emo, Emu; - long Esa, Ese, Esi, Eso, Esu; - - //copyFromState(A, state) - Aba = state[ 0]; - Abe = state[ 1]; - Abi = state[ 2]; - Abo = state[ 3]; - Abu = state[ 4]; - Aga = state[ 5]; - Age = state[ 6]; - Agi = state[ 7]; - Ago = state[ 8]; - Agu = state[ 9]; - Aka = state[10]; - Ake = state[11]; - Aki = state[12]; - Ako = state[13]; - Aku = state[14]; - Ama = state[15]; - Ame = state[16]; - Ami = state[17]; - Amo = state[18]; - Amu = state[19]; - Asa = state[20]; - Ase = state[21]; - Asi = state[22]; - Aso = state[23]; - Asu = state[24]; - - for( round = 0; round < 24; round += 2 ) - { - // prepareTheta - BCa = Aba^Aga^Aka^Ama^Asa; - BCe = Abe^Age^Ake^Ame^Ase; - BCi = Abi^Agi^Aki^Ami^Asi; - BCo = Abo^Ago^Ako^Amo^Aso; - BCu = Abu^Agu^Aku^Amu^Asu; - - //thetaRhoPiChiIotaPrepareTheta(round , A, E) - Da = BCu^ROL(BCe, 1); - De = BCa^ROL(BCi, 1); - Di = BCe^ROL(BCo, 1); - Do = BCi^ROL(BCu, 1); - Du = BCo^ROL(BCa, 1); - - Aba ^= Da; - BCa = Aba; - Age ^= De; - BCe = ROL(Age, 44); - Aki ^= Di; - BCi = ROL(Aki, 43); - Amo ^= Do; - BCo = ROL(Amo, 21); - Asu ^= Du; - BCu = ROL(Asu, 14); - Eba = BCa ^((~BCe)& BCi ); - Eba ^= KeccakF_RoundConstants[round]; - Ebe = BCe ^((~BCi)& BCo ); - Ebi = BCi ^((~BCo)& BCu ); - Ebo = BCo ^((~BCu)& BCa ); - Ebu = BCu ^((~BCa)& BCe ); - - Abo ^= Do; - BCa = ROL(Abo, 28); - Agu ^= Du; - BCe = ROL(Agu, 20); - Aka ^= Da; - BCi = ROL(Aka, 3); - Ame ^= De; - BCo = ROL(Ame, 45); - Asi ^= Di; - BCu = ROL(Asi, 61); - Ega = BCa ^((~BCe)& BCi ); - Ege = BCe ^((~BCi)& BCo ); - Egi = BCi ^((~BCo)& BCu ); - Ego = BCo ^((~BCu)& BCa ); - Egu = BCu ^((~BCa)& BCe ); - - Abe ^= De; - BCa = ROL(Abe, 1); - Agi ^= Di; - BCe = ROL(Agi, 6); - Ako ^= Do; - BCi = ROL(Ako, 25); - Amu ^= Du; - BCo = ROL(Amu, 8); - Asa ^= Da; - BCu = ROL(Asa, 18); - Eka = BCa ^((~BCe)& BCi ); - Eke = BCe ^((~BCi)& BCo ); - Eki = BCi ^((~BCo)& BCu ); - Eko = BCo ^((~BCu)& BCa ); - Eku = BCu ^((~BCa)& BCe ); - - Abu ^= Du; - BCa = ROL(Abu, 27); - Aga ^= Da; - BCe = ROL(Aga, 36); - Ake ^= De; - BCi = ROL(Ake, 10); - Ami ^= Di; - BCo = ROL(Ami, 15); - Aso ^= Do; - BCu = ROL(Aso, 56); - Ema = BCa ^((~BCe)& BCi ); - Eme = BCe ^((~BCi)& BCo ); - Emi = BCi ^((~BCo)& BCu ); - Emo = BCo ^((~BCu)& BCa ); - Emu = BCu ^((~BCa)& BCe ); - - Abi ^= Di; - BCa = ROL(Abi, 62); - Ago ^= Do; - BCe = ROL(Ago, 55); - Aku ^= Du; - BCi = ROL(Aku, 39); - Ama ^= Da; - BCo = ROL(Ama, 41); - Ase ^= De; - BCu = ROL(Ase, 2); - Esa = BCa ^((~BCe)& BCi ); - Ese = BCe ^((~BCi)& BCo ); - Esi = BCi ^((~BCo)& BCu ); - Eso = BCo ^((~BCu)& BCa ); - Esu = BCu ^((~BCa)& BCe ); - - // prepareTheta - BCa = Eba^Ega^Eka^Ema^Esa; - BCe = Ebe^Ege^Eke^Eme^Ese; - BCi = Ebi^Egi^Eki^Emi^Esi; - BCo = Ebo^Ego^Eko^Emo^Eso; - BCu = Ebu^Egu^Eku^Emu^Esu; - - //thetaRhoPiChiIotaPrepareTheta(round+1, E, A) - Da = BCu^ROL(BCe, 1); - De = BCa^ROL(BCi, 1); - Di = BCe^ROL(BCo, 1); - Do = BCi^ROL(BCu, 1); - Du = BCo^ROL(BCa, 1); - - Eba ^= Da; - BCa = Eba; - Ege ^= De; - BCe = ROL(Ege, 44); - Eki ^= Di; - BCi = ROL(Eki, 43); - Emo ^= Do; - BCo = ROL(Emo, 21); - Esu ^= Du; - BCu = ROL(Esu, 14); - Aba = BCa ^((~BCe)& BCi ); - Aba ^= KeccakF_RoundConstants[round+1]; - Abe = BCe ^((~BCi)& BCo ); - Abi = BCi ^((~BCo)& BCu ); - Abo = BCo ^((~BCu)& BCa ); - Abu = BCu ^((~BCa)& BCe ); - - Ebo ^= Do; - BCa = ROL(Ebo, 28); - Egu ^= Du; - BCe = ROL(Egu, 20); - Eka ^= Da; - BCi = ROL(Eka, 3); - Eme ^= De; - BCo = ROL(Eme, 45); - Esi ^= Di; - BCu = ROL(Esi, 61); - Aga = BCa ^((~BCe)& BCi ); - Age = BCe ^((~BCi)& BCo ); - Agi = BCi ^((~BCo)& BCu ); - Ago = BCo ^((~BCu)& BCa ); - Agu = BCu ^((~BCa)& BCe ); - - Ebe ^= De; - BCa = ROL(Ebe, 1); - Egi ^= Di; - BCe = ROL(Egi, 6); - Eko ^= Do; - BCi = ROL(Eko, 25); - Emu ^= Du; - BCo = ROL(Emu, 8); - Esa ^= Da; - BCu = ROL(Esa, 18); - Aka = BCa ^((~BCe)& BCi ); - Ake = BCe ^((~BCi)& BCo ); - Aki = BCi ^((~BCo)& BCu ); - Ako = BCo ^((~BCu)& BCa ); - Aku = BCu ^((~BCa)& BCe ); - - Ebu ^= Du; - BCa = ROL(Ebu, 27); - Ega ^= Da; - BCe = ROL(Ega, 36); - Eke ^= De; - BCi = ROL(Eke, 10); - Emi ^= Di; - BCo = ROL(Emi, 15); - Eso ^= Do; - BCu = ROL(Eso, 56); - Ama = BCa ^((~BCe)& BCi ); - Ame = BCe ^((~BCi)& BCo ); - Ami = BCi ^((~BCo)& BCu ); - Amo = BCo ^((~BCu)& BCa ); - Amu = BCu ^((~BCa)& BCe ); - - Ebi ^= Di; - BCa = ROL(Ebi, 62); - Ego ^= Do; - BCe = ROL(Ego, 55); - Eku ^= Du; - BCi = ROL(Eku, 39); - Ema ^= Da; - BCo = ROL(Ema, 41); - Ese ^= De; - BCu = ROL(Ese, 2); - Asa = BCa ^((~BCe)& BCi ); - Ase = BCe ^((~BCi)& BCo ); - Asi = BCi ^((~BCo)& BCu ); - Aso = BCo ^((~BCu)& BCa ); - Asu = BCu ^((~BCa)& BCe ); +open class NewHope { + // -------------- newhope.c -------------- + private var sk: Poly? = null + + /** + * Destroys sensitive material in this object. + */ + fun destroy() { + sk?.destroy() + sk = null + } + + /** + * Generates the keypair for Alice. + */ + fun keygen(send: ByteArray, sendOffset: Int) { + val a = Poly() + val e = Poly() + val r = Poly() + val pk = Poly() + val seed = ByteArray(SEEDBYTES + 32) + val noiseseed = ByteArray(32) + + try { + randombytes(seed) + sha3256(seed, 0, seed, 0, SEEDBYTES) /* Don't send output of system RNG */ + seed.copyInto(noiseseed, 0, SEEDBYTES, SEEDBYTES + 32) + + uniform(a.coeffs, seed) + + val currentSk = sk ?: Poly().also { sk = it } + currentSk.getnoise(noiseseed, 0.toByte()) + currentSk.ntt() + + e.getnoise(noiseseed, 1.toByte()) + e.ntt() + + r.pointwise(currentSk, a) + pk.add(e, r) + + encode_a(send, sendOffset, pk, seed) + } finally { + a.destroy() + e.destroy() + r.destroy() + pk.destroy() + seed.fill(0) + noiseseed.fill(0) + } + } + + /** + * Generates the public key and shared secret for Bob. + * + * @param sharedkey Buffer to place the shared secret for Bob in. + * @param sharedkeyOffset Offset of the first byte in the sharedkey buffer to populate. + * @param send Buffer to place the public key for Bob in to be sent to Alice. + * @param sendOffset Offset of the first byte in the send buffer to populate. + * @param received Buffer containing the public key value received from Alice. + * @param receivedOffset Offset of the first byte of the value received from Alice. + */ + fun sharedb( + sharedkey: ByteArray, sharedkeyOffset: Int, + send: ByteArray, sendOffset: Int, + received: ByteArray, receivedOffset: Int + ) { + val sp = Poly() + val ep = Poly() + val v = Poly() + val a = Poly() + val pka = Poly() + val c = Poly() + val epp = Poly() + val bp = Poly() + val seed = ByteArray(SEEDBYTES) + val noiseseed = ByteArray(32) + val skey = ByteArray(32) + + try { + randombytes(noiseseed) + + decode_a(pka, seed, received, receivedOffset) + uniform(a.coeffs, seed) + + sp.getnoise(noiseseed, 0.toByte()) + sp.ntt() + ep.getnoise(noiseseed, 1.toByte()) + ep.ntt() + + bp.pointwise(a, sp) + bp.add(bp, ep) + + v.pointwise(pka, sp) + v.invntt() + + epp.getnoise(noiseseed, 2.toByte()) + v.add(v, epp) + + helprec(c, v, noiseseed, 3.toByte()) + + encode_b(send, sendOffset, bp, c) + + rec(skey, v, c) + + sha3256(sharedkey, sharedkeyOffset, skey, 0, 32) + } finally { + sp.destroy() + ep.destroy() + v.destroy() + a.destroy() + pka.destroy() + c.destroy() + epp.destroy() + bp.destroy() + seed.fill(0) + noiseseed.fill(0) + skey.fill(0) + } + } + + /** + * Generates the shared secret for Alice. + */ + fun shareda( + sharedkey: ByteArray, sharedkeyOffset: Int, + received: ByteArray, receivedOffset: Int + ) { + val v = Poly() + val bp = Poly() + val c = Poly() + val skey = ByteArray(32) + + try { + decode_b(bp, c, received, receivedOffset) + + val currentSk = sk ?: throw IllegalStateException("Private key not initialized") + v.pointwise(currentSk, bp) + v.invntt() + + rec(skey, v, c) + + sha3256(sharedkey, sharedkeyOffset, skey, 0, 32) + } finally { + v.destroy() + bp.destroy() + c.destroy() + skey.fill(0) + } + } + + protected open fun randombytes(buffer: ByteArray) { + SecureRandom().nextBytes(buffer) + } + + // -------------- poly.c -------------- + inner class Poly { + val coeffs = CharArray(PARAM_N) + + fun destroy() { + coeffs.fill(0.toChar()) } - //copyToState(state, A) - state[ 0] = Aba; - state[ 1] = Abe; - state[ 2] = Abi; - state[ 3] = Abo; - state[ 4] = Abu; - state[ 5] = Aga; - state[ 6] = Age; - state[ 7] = Agi; - state[ 8] = Ago; - state[ 9] = Agu; - state[10] = Aka; - state[11] = Ake; - state[12] = Aki; - state[13] = Ako; - state[14] = Aku; - state[15] = Ama; - state[16] = Ame; - state[17] = Ami; - state[18] = Amo; - state[19] = Amu; - state[20] = Asa; - state[21] = Ase; - state[22] = Asi; - state[23] = Aso; - state[24] = Asu; - } + fun frombytes(a: ByteArray, offset: Int) { + for (i in 0 until PARAM_N / 4) { + coeffs[4 * i + 0] = ((a[offset + 7 * i + 0].toInt() and 0xff) or ((a[offset + 7 * i + 1].toInt() and 0x3f) shl 8)).toChar() + coeffs[4 * i + 1] = (((a[offset + 7 * i + 1].toInt() and 0xc0) shr 6) or ((a[offset + 7 * i + 2].toInt() and 0xff) shl 2) or ((a[offset + 7 * i + 3].toInt() and 0x0f) shl 10)).toChar() + coeffs[4 * i + 2] = (((a[offset + 7 * i + 3].toInt() and 0xf0) shr 4) or ((a[offset + 7 * i + 4].toInt() and 0xff) shl 4) or ((a[offset + 7 * i + 5].toInt() and 0x03) shl 12)).toChar() + coeffs[4 * i + 3] = (((a[offset + 7 * i + 5].toInt() and 0xfc) shr 2) or ((a[offset + 7 * i + 6].toInt() and 0xff) shl 6)).toChar() + } + } - private static void keccak_absorb(long[] s, int r, byte[] m, int offset, int mlen, byte p) - { - int i; - byte[] t = new byte [200]; + fun tobytes(r: ByteArray, offset: Int) { + for (i in 0 until PARAM_N / 4) { + var t0 = barrett_reduce(coeffs[4 * i + 0].code) + var t1 = barrett_reduce(coeffs[4 * i + 1].code) + var t2 = barrett_reduce(coeffs[4 * i + 2].code) + var t3 = barrett_reduce(coeffs[4 * i + 3].code) - try { - for (i = 0; i < 25; ++i) - s[i] = 0; + var m = t0 - PARAM_Q + var mask = m shr 15 + t0 = m xor ((t0 xor m) and mask) - while (mlen >= r) - { - for (i = 0; i < r / 8; ++i) - s[i] ^= load64(m, offset + 8 * i); + m = t1 - PARAM_Q + mask = m shr 15 + t1 = m xor ((t1 xor m) and mask) - KeccakF1600_StatePermute(s); - mlen -= r; - offset += r; - } + m = t2 - PARAM_Q + mask = m shr 15 + t2 = m xor ((t2 xor m) and mask) - for (i = 0; i < r; ++i) - t[i] = 0; - for (i = 0; i < mlen; ++i) - t[i] = m[offset + i]; - t[i] = p; - t[r - 1] |= 128; - for (i = 0; i < r / 8; ++i) - s[i] ^= load64(t, 8 * i); - } finally { - Arrays.fill(t, (byte)0); - } - } + m = t3 - PARAM_Q + mask = m shr 15 + t3 = m xor ((t3 xor m) and mask) - private static void keccak_squeezeblocks(byte[] h, int offset, int nblocks, long [] s, int r) - { - int i; - while(nblocks > 0) - { - KeccakF1600_StatePermute(s); - for(i=0;i<(r>>3);i++) - { - store64(h, offset+8*i, s[i]); - } - offset += r; - nblocks--; - } - } + r[offset + 7 * i + 0] = (t0 and 0xff).toByte() + r[offset + 7 * i + 1] = ((t0 shr 8) or (t1 shl 6)).toByte() + r[offset + 7 * i + 2] = (t1 shr 2).toByte() + r[offset + 7 * i + 3] = ((t1 shr 10) or (t2 shl 4)).toByte() + r[offset + 7 * i + 4] = (t2 shr 4).toByte() + r[offset + 7 * i + 5] = ((t2 shr 12) or (t3 shl 2)).toByte() + r[offset + 7 * i + 6] = (t3 shr 6).toByte() + } + } - static final int SHAKE128_RATE = 168; - - static void shake128_absorb(long[] s, byte[] input, int inputOffset, int inputByteLen) - { - keccak_absorb(s, SHAKE128_RATE, input, inputOffset, inputByteLen, (byte)0x1F); - } + fun getnoise(seed: ByteArray, nonce: Byte) { + val buf = ByteArray(4 * PARAM_N) + try { + crypto_stream_chacha20(buf, 0, 4 * PARAM_N, nonce.toLong(), seed) + for (i in 0 until PARAM_N) { + var a = (buf[4 * i].toInt() and 0xff) or ((buf[4 * i + 1].toInt() and 0xff) shl 8) + a = a - ((a shr 1) and 0x5555) + a = (a and 0x3333) + ((a shr 2) and 0x3333) + a = ((a shr 4) + a) and 0x0F0F + a = ((a shr 8) + a) and 0x00FF - static void shake128_squeezeblocks(byte[] output, int outputOffset, int nblocks, long[] s) - { - keccak_squeezeblocks(output, outputOffset, nblocks, s, SHAKE128_RATE); - } + var b = (buf[4 * i + 2].toInt() and 0xff) or ((buf[4 * i + 3].toInt() and 0xff) shl 8) + b = b - ((b shr 1) and 0x5555) + b = (b and 0x3333) + ((b shr 2) and 0x3333) + b = ((b shr 4) + b) and 0x0F0F + b = ((b shr 8) + b) and 0x00FF - private static final int SHA3_256_RATE = 136; - - private static void sha3256(byte[] output, int outputOffset, byte[] input, int inputOffset, int inputByteLen) - { - long[] s = new long [25]; - byte[] t = new byte [SHA3_256_RATE]; - int i; + coeffs[i] = (a + PARAM_Q - b).toChar() + } + } finally { + buf.fill(0) + } + } - try { - keccak_absorb(s, SHA3_256_RATE, input, inputOffset, inputByteLen, (byte)0x06); - keccak_squeezeblocks(t, 0, 1, s, SHA3_256_RATE); - for(i=0;i<32;i++) - output[i] = t[i]; - } finally { - Arrays.fill(s, 0); - Arrays.fill(t, (byte)0); - } - } + fun pointwise(a: Poly, b: Poly) { + for (i in 0 until PARAM_N) { + val t = montgomery_reduce(3186 * b.coeffs[i].code) + coeffs[i] = montgomery_reduce(a.coeffs[i].code * t).toChar() + } + } - // -------------- crypto_stream_chacha20.c -------------- + fun add(a: Poly, b: Poly) { + for (i in 0 until PARAM_N) { + coeffs[i] = barrett_reduce(a.coeffs[i].code + b.coeffs[i].code).toChar() + } + } - /* Based on the public domain implemntation in - * crypto_stream/chacha20/e/ref from http://bench.cr.yp.to/supercop.html - * by Daniel J. Bernstein */ + fun ntt() { + mul_coefficients(coeffs, psis_bitrev_montgomery) + ntt_global(coeffs, omegas_montgomery) + } - private static int load_littleendian(byte[] x, int offset) - { - return - (int) (x[offset + 0] & 0xff) - | (((int) (x[offset + 1] & 0xff)) << 8) - | (((int) (x[offset + 2] & 0xff)) << 16) - | (((int) (x[offset + 3] & 0xff)) << 24); - } + fun invntt() { + bitrev_vector(coeffs) + ntt_global(coeffs, omegas_inv_montgomery) + mul_coefficients(coeffs, psis_inv_montgomery) + } + } - private static void store_littleendian(byte[] x, int offset, int u) - { - x[offset + 0] = (byte)u; u >>= 8; - x[offset + 1] = (byte)u; u >>= 8; - x[offset + 2] = (byte)u; u >>= 8; - x[offset + 3] = (byte)u; - } - - // Note: This version is limited to a maximum of 2^32 blocks or 2^38 bytes - // because the block number counter is 32-bit instead of 64-bit. This isn't - // a problem for New Hope because the maximum required output is 4096 bytes. - private static void crypto_core_chacha20(byte[] out, int outOffset, long nonce, int blknum, byte[] k) - { - int x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; - int j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; - int i; + /** + * Derives the public "a" value from a 32-byte seed. + */ + protected open fun uniform(coeffs: CharArray, seed: ByteArray) { + var pos = 0 + var ctr = 0 + val state = LongArray(25) + var nblocks = 14 + val buf = ByteArray(SHAKE128_RATE * nblocks) - j0 = x0 = 0x61707865; // "expa" - j1 = x1 = 0x3320646e; // "nd 3" - j2 = x2 = 0x79622d32; // "2-by" - j3 = x3 = 0x6b206574; // "te k" - j4 = x4 = load_littleendian(k, 0); - j5 = x5 = load_littleendian(k, 4); - j6 = x6 = load_littleendian(k, 8); - j7 = x7 = load_littleendian(k, 12); - j8 = x8 = load_littleendian(k, 16); - j9 = x9 = load_littleendian(k, 20); - j10 = x10 = load_littleendian(k, 24); - j11 = x11 = load_littleendian(k, 28); - j12 = x12 = blknum; - j13 = x13 = 0; - j14 = x14 = (int)nonce; - j15 = x15 = (int)(nonce >>> 32); + try { + shake128_absorb(state, seed, 0, SEEDBYTES) + shake128_squeezeblocks(buf, 0, nblocks, state) - for (i = 20;i > 0;i -= 2) { - x0 += x4 ; x12 ^= x0 ; x12 = (x12 << 16) | (x12 >>> 16); - x8 += x12; x4 ^= x8 ; x4 = (x4 << 12) | (x4 >>> 20); - x0 += x4 ; x12 ^= x0 ; x12 = (x12 << 8) | (x12 >>> 24); - x8 += x12; x4 ^= x8 ; x4 = (x4 << 7) | (x4 >>> 25); - x1 += x5 ; x13 ^= x1 ; x13 = (x13 << 16) | (x13 >>> 16); - x9 += x13; x5 ^= x9 ; x5 = (x5 << 12) | (x5 >>> 20); - x1 += x5 ; x13 ^= x1 ; x13 = (x13 << 8) | (x13 >>> 24); - x9 += x13; x5 ^= x9 ; x5 = (x5 << 7) | (x5 >>> 25); - x2 += x6 ; x14 ^= x2 ; x14 = (x14 << 16) | (x14 >>> 16); - x10 += x14; x6 ^= x10; x6 = (x6 << 12) | (x6 >>> 20); - x2 += x6 ; x14 ^= x2 ; x14 = (x14 << 8) | (x14 >>> 24); - x10 += x14; x6 ^= x10; x6 = (x6 << 7) | (x6 >>> 25); - x3 += x7 ; x15 ^= x3 ; x15 = (x15 << 16) | (x15 >>> 16); - x11 += x15; x7 ^= x11; x7 = (x7 << 12) | (x7 >>> 20); - x3 += x7 ; x15 ^= x3 ; x15 = (x15 << 8) | (x15 >>> 24); - x11 += x15; x7 ^= x11; x7 = (x7 << 7) | (x7 >>> 25); - x0 += x5 ; x15 ^= x0 ; x15 = (x15 << 16) | (x15 >>> 16); - x10 += x15; x5 ^= x10; x5 = (x5 << 12) | (x5 >>> 20); - x0 += x5 ; x15 ^= x0 ; x15 = (x15 << 8) | (x15 >>> 24); - x10 += x15; x5 ^= x10; x5 = (x5 << 7) | (x5 >>> 25); - x1 += x6 ; x12 ^= x1 ; x12 = (x12 << 16) | (x12 >>> 16); - x11 += x12; x6 ^= x11; x6 = (x6 << 12) | (x6 >>> 20); - x1 += x6 ; x12 ^= x1 ; x12 = (x12 << 8) | (x12 >>> 24); - x11 += x12; x6 ^= x11; x6 = (x6 << 7) | (x6 >>> 25); - x2 += x7 ; x13 ^= x2 ; x13 = (x13 << 16) | (x13 >>> 16); - x8 += x13; x7 ^= x8 ; x7 = (x7 << 12) | (x7 >>> 20); - x2 += x7 ; x13 ^= x2 ; x13 = (x13 << 8) | (x13 >>> 24); - x8 += x13; x7 ^= x8 ; x7 = (x7 << 7) | (x7 >>> 25); - x3 += x4 ; x14 ^= x3 ; x14 = (x14 << 16) | (x14 >>> 16); - x9 += x14; x4 ^= x9 ; x4 = (x4 << 12) | (x4 >>> 20); - x3 += x4 ; x14 ^= x3 ; x14 = (x14 << 8) | (x14 >>> 24); - x9 += x14; x4 ^= x9 ; x4 = (x4 << 7) | (x4 >>> 25); - } + while (ctr < PARAM_N) { + val value = (buf[pos].toInt() and 0xff) or ((buf[pos + 1].toInt() and 0xff) shl 8) + if (value < 5 * PARAM_Q) coeffs[ctr++] = value.toChar() + pos += 2 + if (pos > SHAKE128_RATE * nblocks - 2) { + nblocks = 1 + shake128_squeezeblocks(buf, 0, nblocks, state) + pos = 0 + } + } + } finally { + state.fill(0) + buf.fill(0) + } + } - x0 += j0; - x1 += j1; - x2 += j2; - x3 += j3; - x4 += j4; - x5 += j5; - x6 += j6; - x7 += j7; - x8 += j8; - x9 += j9; - x10 += j10; - x11 += j11; - x12 += j12; - x13 += j13; - x14 += j14; - x15 += j15; + companion object { + // -------------- params.h -------------- + const val PARAM_N = 1024 + const val PARAM_Q = 12289 + const val POLY_BYTES = 1792 + const val SEEDBYTES = 32 + const val RECBYTES = 256 + const val SHAKE128_RATE = 168 + const val SHAREDBYTES = 32 + const val SENDABYTES = POLY_BYTES + SEEDBYTES + const val SENDBBYTES = POLY_BYTES + RECBYTES - store_littleendian(out, outOffset + 0,x0); - store_littleendian(out, outOffset + 4,x1); - store_littleendian(out, outOffset + 8,x2); - store_littleendian(out, outOffset + 12,x3); - store_littleendian(out, outOffset + 16,x4); - store_littleendian(out, outOffset + 20,x5); - store_littleendian(out, outOffset + 24,x6); - store_littleendian(out, outOffset + 28,x7); - store_littleendian(out, outOffset + 32,x8); - store_littleendian(out, outOffset + 36,x9); - store_littleendian(out, outOffset + 40,x10); - store_littleendian(out, outOffset + 44,x11); - store_littleendian(out, outOffset + 48,x12); - store_littleendian(out, outOffset + 52,x13); - store_littleendian(out, outOffset + 56,x14); - store_littleendian(out, outOffset + 60,x15); - } + private fun encode_a(r: ByteArray, roffset: Int, pk: Poly, seed: ByteArray) { + pk.tobytes(r, roffset) + seed.copyInto(r, POLY_BYTES + roffset, 0, SEEDBYTES) + } - private static void crypto_stream_chacha20(byte[] c, int coffset, int clen, long n, byte[] k) - { - int blknum = 0; + private fun decode_a(pk: Poly, seed: ByteArray, r: ByteArray, roffset: Int) { + pk.frombytes(r, roffset) + r.copyInto(seed, 0, POLY_BYTES + roffset, POLY_BYTES + roffset + SEEDBYTES) + } - if (clen <= 0) return; + private fun encode_b(r: ByteArray, roffset: Int, b: Poly, c: Poly) { + b.tobytes(r, roffset) + for (i in 0 until PARAM_N / 4) { + r[POLY_BYTES + roffset + i] = (c.coeffs[4 * i].code or (c.coeffs[4 * i + 1].code shl 2) or (c.coeffs[4 * i + 2].code shl 4) or (c.coeffs[4 * i + 3].code shl 6)).toByte() + } + } - while (clen >= 64) { - crypto_core_chacha20(c,coffset,n,blknum,k); - ++blknum; - clen -= 64; - coffset += 64; - } + private fun decode_b(b: Poly, c: Poly, r: ByteArray, roffset: Int) { + b.frombytes(r, roffset) + for (i in 0 until PARAM_N / 4) { + val v = r[POLY_BYTES + roffset + i].toInt() + c.coeffs[4 * i + 0] = (v and 0x03).toChar() + c.coeffs[4 * i + 1] = ((v shr 2) and 0x03).toChar() + c.coeffs[4 * i + 2] = ((v shr 4) and 0x03).toChar() + c.coeffs[4 * i + 3] = ((v shr 6) and 0x03).toChar() + } + } - if (clen != 0) { - byte[] block = new byte [64]; - try { - crypto_core_chacha20(block,0,n,blknum,k); - for (int i = 0;i < clen;++i) c[coffset+i] = block[i]; - } finally { - Arrays.fill(block, (byte)0); - } - } - } + private fun montgomery_reduce(a: Int): Int { + val u = (a * 12287) and ((1 shl 18) - 1) + return (a + u * PARAM_Q) ushr 18 + } - // -------------- precomp.c -------------- + private fun barrett_reduce(a: Int): Int { + val a16 = a and 0xffff + val u = (a16 * 5) shr 16 + return (a16 - u * PARAM_Q) and 0xffff + } - private static final char[/*PARAM_N/2*/] omegas_montgomery = { - 4075,6974,7373,7965,3262,5079,522,2169,6364,1018,1041,8775,2344, - 11011,5574,1973,4536,1050,6844,3860,3818,6118,2683,1190,4789,7822, - 7540,6752,5456,4449,3789,12142,11973,382,3988,468,6843,5339,6196, - 3710,11316,1254,5435,10930,3998,10256,10367,3879,11889,1728,6137, - 4948,5862,6136,3643,6874,8724,654,10302,1702,7083,6760,56,3199,9987, - 605,11785,8076,5594,9260,6403,4782,6212,4624,9026,8689,4080,11868, - 6221,3602,975,8077,8851,9445,5681,3477,1105,142,241,12231,1003, - 3532,5009,1956,6008,11404,7377,2049,10968,12097,7591,5057,3445, - 4780,2920,7048,3127,8120,11279,6821,11502,8807,12138,2127,2839, - 3957,431,1579,6383,9784,5874,677,3336,6234,2766,1323,9115,12237, - 2031,6956,6413,2281,3969,3991,12133,9522,4737,10996,4774,5429,11871, - 3772,453,5908,2882,1805,2051,1954,11713,3963,2447,6142,8174,3030, - 1843,2361,12071,2908,3529,3434,3202,7796,2057,5369,11939,1512,6906, - 10474,11026,49,10806,5915,1489,9789,5942,10706,10431,7535,426,8974, - 3757,10314,9364,347,5868,9551,9634,6554,10596,9280,11566,174,2948, - 2503,6507,10723,11606,2459,64,3656,8455,5257,5919,7856,1747,9166, - 5486,9235,6065,835,3570,4240,11580,4046,10970,9139,1058,8210,11848, - 922,7967,1958,10211,1112,3728,4049,11130,5990,1404,325,948,11143, - 6190,295,11637,5766,8212,8273,2919,8527,6119,6992,8333,1360,2555, - 6167,1200,7105,7991,3329,9597,12121,5106,5961,10695,10327,3051,9923, - 4896,9326,81,3091,1000,7969,4611,726,1853,12149,4255,11112,2768, - 10654,1062,2294,3553,4805,2747,4846,8577,9154,1170,2319,790,11334, - 9275,9088,1326,5086,9094,6429,11077,10643,3504,3542,8668,9744,1479, - 1,8246,7143,11567,10984,4134,5736,4978,10938,5777,8961,4591,5728, - 6461,5023,9650,7468,949,9664,2975,11726,2744,9283,10092,5067,12171, - 2476,3748,11336,6522,827,9452,5374,12159,7935,3296,3949,9893,4452, - 10908,2525,3584,8112,8011,10616,4989,6958,11809,9447,12280,1022, - 11950,9821,11745,5791,5092,2089,9005,2881,3289,2013,9048,729,7901, - 1260,5755,4632,11955,2426,10593,1428,4890,5911,3932,9558,8830,3637, - 5542,145,5179,8595,3707,10530,355,3382,4231,9741,1207,9041,7012,1168, - 10146,11224,4645,11885,10911,10377,435,7952,4096,493,9908,6845,6039, - 2422,2187,9723,8643,9852,9302,6022,7278,1002,4284,5088,1607,7313, - 875,8509,9430,1045,2481,5012,7428,354,6591,9377,11847,2401,1067, - 7188,11516,390,8511,8456,7270,545,8585,9611,12047,1537,4143,4714, - 4885,1017,5084,1632,3066,27,1440,8526,9273,12046,11618,9289,3400, - 9890,3136,7098,8758,11813,7384,3985,11869,6730,10745,10111,2249, - 4048,2884,11136,2126,1630,9103,5407,2686,9042,2969,8311,9424, - 9919,8779,5332,10626,1777,4654,10863,7351,3636,9585,5291,8374, - 2166,4919,12176,9140,12129,7852,12286,4895,10805,2780,5195,2305, - 7247,9644,4053,10600,3364,3271,4057,4414,9442,7917,2174 - }; + // -------------- error_correction.c -------------- + private fun abs(v: Int): Int { + val mask = v shr 31 + return (v xor mask) - mask + } - private static final char[/*PARAM_N/2*/] omegas_inv_montgomery = { - 4075,5315,4324,4916,10120,11767,7210,9027,10316,6715,1278,9945, - 3514,11248,11271,5925,147,8500,7840,6833,5537,4749,4467,7500,11099, - 9606,6171,8471,8429,5445,11239,7753,9090,12233,5529,5206,10587, - 1987,11635,3565,5415,8646,6153,6427,7341,6152,10561,400,8410,1922, - 2033,8291,1359,6854,11035,973,8579,6093,6950,5446,11821,8301,11907, - 316,52,3174,10966,9523,6055,8953,11612,6415,2505,5906,10710,11858, - 8332,9450,10162,151,3482,787,5468,1010,4169,9162,5241,9369,7509, - 8844,7232,4698,192,1321,10240,4912,885,6281,10333,7280,8757,11286, - 58,12048,12147,11184,8812,6608,2844,3438,4212,11314,8687,6068,421, - 8209,3600,3263,7665,6077,7507,5886,3029,6695,4213,504,11684,2302, - 1962,1594,6328,7183,168,2692,8960,4298,5184,11089,6122,9734,10929, - 3956,5297,6170,3762,9370,4016,4077,6523,652,11994,6099,1146,11341, - 11964,10885,6299,1159,8240,8561,11177,2078,10331,4322,11367,441, - 4079,11231,3150,1319,8243,709,8049,8719,11454,6224,3054,6803,3123, - 10542,4433,6370,7032,3834,8633,12225,9830,683,1566,5782,9786,9341, - 12115,723,3009,1693,5735,2655,2738,6421,11942,2925,1975,8532,3315, - 11863,4754,1858,1583,6347,2500,10800,6374,1483,12240,1263,1815, - 5383,10777,350,6920,10232,4493,9087,8855,8760,9381,218,9928,10446, - 9259,4115,6147,9842,8326,576,10335,10238,10484,9407,6381,11836,8517, - 418,6860,7515,1293,7552,2767,156,8298,8320,10008,5876,5333,10258, - 10115,4372,2847,7875,8232,9018,8925,1689,8236,2645,5042,9984,7094, - 9509,1484,7394,3,4437,160,3149,113,7370,10123,3915,6998,2704,8653, - 4938,1426,7635,10512,1663,6957,3510,2370,2865,3978,9320,3247,9603, - 6882,3186,10659,10163,1153,9405,8241,10040,2178,1544,5559,420,8304, - 4905,476,3531,5191,9153,2399,8889,3000,671,243,3016,3763,10849,12262, - 9223,10657,7205,11272,7404,7575,8146,10752,242,2678,3704,11744, - 5019,3833,3778,11899,773,5101,11222,9888,442,2912,5698,11935,4861, - 7277,9808,11244,2859,3780,11414,4976,10682,7201,8005,11287,5011, - 6267,2987,2437,3646,2566,10102,9867,6250,5444,2381,11796,8193,4337, - 11854,1912,1378,404,7644,1065,2143,11121,5277,3248,11082,2548,8058, - 8907,11934,1759,8582,3694,7110,12144,6747,8652,3459,2731,8357,6378, - 7399,10861,1696,9863,334,7657,6534,11029,4388,11560,3241,10276,9000, - 9408,3284,10200,7197,6498,544,2468,339,11267,9,2842,480,5331,7300, - 1673,4278,4177,8705,9764,1381,7837,2396,8340,8993,4354,130,6915, - 2837,11462,5767,953,8541,9813,118,7222,2197,3006,9545,563,9314, - 2625,11340,4821,2639,7266,5828,6561,7698,3328,6512,1351,7311,6553, - 8155,1305,722,5146,4043,12288,10810,2545,3621,8747,8785,1646,1212, - 5860,3195,7203,10963,3201,3014,955,11499,9970,11119,3135,3712,7443, - 9542,7484,8736,9995,11227,1635,9521,1177,8034,140,10436,11563,7678, - 4320,11289,9198,12208,2963,7393,2366,9238 - }; + private fun f(v0: IntArray, v0offset: Int, v1: IntArray, v1offset: Int, x: Int): Int { + var b = x * 2730 + var t = b shr 25 + b = x - t * 12289 + b = 12288 - b + t -= (b shr 31) + v0[v0offset] = (t shr 1) + (t and 1) + t -= 1 + v1[v1offset] = (t shr 1) + (t and 1) + return abs(x - (v0[v0offset] * 2 * PARAM_Q)) + } - private static final char[/*PARAM_N*/] psis_bitrev_montgomery = { - 4075,6974,7373,7965,3262,5079,522,2169,6364,1018,1041,8775,2344, - 11011,5574,1973,4536,1050,6844,3860,3818,6118,2683,1190,4789,7822, - 7540,6752,5456,4449,3789,12142,11973,382,3988,468,6843,5339,6196,3710, - 11316,1254,5435,10930,3998,10256,10367,3879,11889,1728,6137,4948, - 5862,6136,3643,6874,8724,654,10302,1702,7083,6760,56,3199,9987,605, - 11785,8076,5594,9260,6403,4782,6212,4624,9026,8689,4080,11868,6221, - 3602,975,8077,8851,9445,5681,3477,1105,142,241,12231,1003,3532,5009, - 1956,6008,11404,7377,2049,10968,12097,7591,5057,3445,4780,2920, - 7048,3127,8120,11279,6821,11502,8807,12138,2127,2839,3957,431,1579, - 6383,9784,5874,677,3336,6234,2766,1323,9115,12237,2031,6956,6413, - 2281,3969,3991,12133,9522,4737,10996,4774,5429,11871,3772,453, - 5908,2882,1805,2051,1954,11713,3963,2447,6142,8174,3030,1843,2361, - 12071,2908,3529,3434,3202,7796,2057,5369,11939,1512,6906,10474, - 11026,49,10806,5915,1489,9789,5942,10706,10431,7535,426,8974,3757, - 10314,9364,347,5868,9551,9634,6554,10596,9280,11566,174,2948,2503, - 6507,10723,11606,2459,64,3656,8455,5257,5919,7856,1747,9166,5486, - 9235,6065,835,3570,4240,11580,4046,10970,9139,1058,8210,11848,922, - 7967,1958,10211,1112,3728,4049,11130,5990,1404,325,948,11143,6190, - 295,11637,5766,8212,8273,2919,8527,6119,6992,8333,1360,2555,6167, - 1200,7105,7991,3329,9597,12121,5106,5961,10695,10327,3051,9923, - 4896,9326,81,3091,1000,7969,4611,726,1853,12149,4255,11112,2768, - 10654,1062,2294,3553,4805,2747,4846,8577,9154,1170,2319,790,11334, - 9275,9088,1326,5086,9094,6429,11077,10643,3504,3542,8668,9744,1479, - 1,8246,7143,11567,10984,4134,5736,4978,10938,5777,8961,4591,5728, - 6461,5023,9650,7468,949,9664,2975,11726,2744,9283,10092,5067,12171, - 2476,3748,11336,6522,827,9452,5374,12159,7935,3296,3949,9893,4452, - 10908,2525,3584,8112,8011,10616,4989,6958,11809,9447,12280,1022, - 11950,9821,11745,5791,5092,2089,9005,2881,3289,2013,9048,729,7901, - 1260,5755,4632,11955,2426,10593,1428,4890,5911,3932,9558,8830,3637, - 5542,145,5179,8595,3707,10530,355,3382,4231,9741,1207,9041,7012, - 1168,10146,11224,4645,11885,10911,10377,435,7952,4096,493,9908,6845, - 6039,2422,2187,9723,8643,9852,9302,6022,7278,1002,4284,5088,1607, - 7313,875,8509,9430,1045,2481,5012,7428,354,6591,9377,11847,2401, - 1067,7188,11516,390,8511,8456,7270,545,8585,9611,12047,1537,4143, - 4714,4885,1017,5084,1632,3066,27,1440,8526,9273,12046,11618,9289, - 3400,9890,3136,7098,8758,11813,7384,3985,11869,6730,10745,10111, - 2249,4048,2884,11136,2126,1630,9103,5407,2686,9042,2969,8311,9424, - 9919,8779,5332,10626,1777,4654,10863,7351,3636,9585,5291,8374, - 2166,4919,12176,9140,12129,7852,12286,4895,10805,2780,5195,2305, - 7247,9644,4053,10600,3364,3271,4057,4414,9442,7917,2174,3947, - 11951,2455,6599,10545,10975,3654,2894,7681,7126,7287,12269,4119, - 3343,2151,1522,7174,7350,11041,2442,2148,5959,6492,8330,8945,5598, - 3624,10397,1325,6565,1945,11260,10077,2674,3338,3276,11034,506, - 6505,1392,5478,8778,1178,2776,3408,10347,11124,2575,9489,12096, - 6092,10058,4167,6085,923,11251,11912,4578,10669,11914,425,10453, - 392,10104,8464,4235,8761,7376,2291,3375,7954,8896,6617,7790,1737, - 11667,3982,9342,6680,636,6825,7383,512,4670,2900,12050,7735,994, - 1687,11883,7021,146,10485,1403,5189,6094,2483,2054,3042,10945, - 3981,10821,11826,8882,8151,180,9600,7684,5219,10880,6780,204, - 11232,2600,7584,3121,3017,11053,7814,7043,4251,4739,11063,6771, - 7073,9261,2360,11925,1928,11825,8024,3678,3205,3359,11197,5209, - 8581,3238,8840,1136,9363,1826,3171,4489,7885,346,2068,1389,8257, - 3163,4840,6127,8062,8921,612,4238,10763,8067,125,11749,10125,5416, - 2110,716,9839,10584,11475,11873,3448,343,1908,4538,10423,7078, - 4727,1208,11572,3589,2982,1373,1721,10753,4103,2429,4209,5412, - 5993,9011,438,3515,7228,1218,8347,5232,8682,1327,7508,4924,448, - 1014,10029,12221,4566,5836,12229,2717,1535,3200,5588,5845,412, - 5102,7326,3744,3056,2528,7406,8314,9202,6454,6613,1417,10032,7784, - 1518,3765,4176,5063,9828,2275,6636,4267,6463,2065,7725,3495,8328, - 8755,8144,10533,5966,12077,9175,9520,5596,6302,8400,579,6781,11014, - 5734,11113,11164,4860,1131,10844,9068,8016,9694,3837,567,9348,7000, - 6627,7699,5082,682,11309,5207,4050,7087,844,7434,3769,293,9057, - 6940,9344,10883,2633,8190,3944,5530,5604,3480,2171,9282,11024,2213, - 8136,3805,767,12239,216,11520,6763,10353,7,8566,845,7235,3154,4360, - 3285,10268,2832,3572,1282,7559,3229,8360,10583,6105,3120,6643,6203, - 8536,8348,6919,3536,9199,10891,11463,5043,1658,5618,8787,5789,4719, - 751,11379,6389,10783,3065,7806,6586,2622,5386,510,7628,6921,578, - 10345,11839,8929,4684,12226,7154,9916,7302,8481,3670,11066,2334, - 1590,7878,10734,1802,1891,5103,6151,8820,3418,7846,9951,4693,417, - 9996,9652,4510,2946,5461,365,881,1927,1015,11675,11009,1371,12265, - 2485,11385,5039,6742,8449,1842,12217,8176,9577,4834,7937,9461,2643, - 11194,3045,6508,4094,3451,7911,11048,5406,4665,3020,6616,11345, - 7519,3669,5287,1790,7014,5410,11038,11249,2035,6125,10407,4565, - 7315,5078,10506,2840,2478,9270,4194,9195,4518,7469,1160,6878,2730, - 10421,10036,1734,3815,10939,5832,10595,10759,4423,8420,9617,7119, - 11010,11424,9173,189,10080,10526,3466,10588,7592,3578,11511,7785, - 9663,530,12150,8957,2532,3317,9349,10243,1481,9332,3454,3758,7899, - 4218,2593,11410,2276,982,6513,1849,8494,9021,4523,7988,8,457,648, - 150,8000,2307,2301,874,5650,170,9462,2873,9855,11498,2535,11169, - 5808,12268,9687,1901,7171,11787,3846,1573,6063,3793,466,11259, - 10608,3821,6320,4649,6263,2929 - }; + private fun g(x: Int): Int { + var b = x * 2730 + var t = b shr 27 + b = x - t * 49156 + b = 49155 - b + t -= (b shr 31) + t = (t shr 1) + (t and 1) + return abs(t * 8 * PARAM_Q - x) + } - private static final char[/*PARAM_N*/] psis_inv_montgomery = { - 256,10570,1510,7238,1034,7170,6291,7921,11665,3422,4000,2327, - 2088,5565,795,10647,1521,5484,2539,7385,1055,7173,8047,11683, - 1669,1994,3796,5809,4341,9398,11876,12230,10525,12037,12253, - 3506,4012,9351,4847,2448,7372,9831,3160,2207,5582,2553,7387,6322, - 9681,1383,10731,1533,219,5298,4268,7632,6357,9686,8406,4712,9451, - 10128,4958,5975,11387,8649,11769,6948,11526,12180,1740,10782, - 6807,2728,7412,4570,4164,4106,11120,12122,8754,11784,3439,5758, - 11356,6889,9762,11928,1704,1999,10819,12079,12259,7018,11536, - 1648,1991,2040,2047,2048,10826,12080,8748,8272,8204,1172,1923, - 7297,2798,7422,6327,4415,7653,6360,11442,12168,7005,8023,9924, - 8440,8228,2931,7441,1063,3663,5790,9605,10150,1450,8985,11817, - 10466,10273,12001,3470,7518,1074,1909,7295,9820,4914,702,5367, - 7789,8135,9940,1420,3714,11064,12114,12264,1752,5517,9566,11900, - 1700,3754,5803,829,1874,7290,2797,10933,5073,7747,8129,6428, - 6185,11417,1631,233,5300,9535,10140,11982,8734,8270,2937,10953, - 8587,8249,2934,9197,4825,5956,4362,9401,1343,3703,529,10609, - 12049,6988,6265,895,3639,4031,4087,4095,585,10617,8539,4731, - 4187,9376,3095,9220,10095,10220,1460,10742,12068,1724,5513, - 11321,6884,2739,5658,6075,4379,11159,10372,8504,4726,9453,3106, - 7466,11600,10435,8513,9994,8450,9985,3182,10988,8592,2983,9204, - 4826,2445,5616,6069,867,3635,5786,11360,5134,2489,10889,12089, - 1727,7269,2794,9177,1311,5454,9557,6632,2703,9164,10087,1441, - 3717,531,3587,2268,324,5313,759,1864,5533,2546,7386,9833,8427, - 4715,11207,1601,7251,4547,11183,12131,1733,10781,10318,1474, - 10744,5046,4232,11138,10369,6748,964,7160,4534,7670,8118,8182, - 4680,11202,6867,981,8918,1274,182,26,7026,8026,11680,12202, - 10521,1503,7237,4545,5916,9623,8397,11733,10454,3249,9242,6587, - 941,1890,270,10572,6777,9746,6659,6218,6155,6146,878,1881,7291, - 11575,12187,1741,7271,8061,11685,6936,4502,9421,4857,4205,7623, - 1089,10689,1527,8996,10063,11971,10488,6765,2722,3900,9335,11867, - 6962,11528,5158,4248,4118,5855,2592,5637,6072,2623,7397,8079, - 9932,4930,5971,853,3633,519,8852,11798,3441,11025,1575,225,8810, - 11792,12218,3501,9278,3081,9218,4828,7712,8124,11694,12204,3499, - 4011,573,3593,5780,7848,9899,10192,1456,208,7052,2763,7417,11593, - 10434,12024,8740,11782,10461,3250,5731,7841,9898,1414,202,3540, - 7528,2831,2160,10842,5060,4234,4116,588,84,12,7024,2759,9172,6577, - 11473,1639,9012,3043,7457,6332,11438,1634,1989,9062,11828,8712, - 11778,12216,10523,6770,9745,10170,4964,9487,6622,946,8913,6540, - 6201,4397,9406,8366,9973,8447,8229,11709,8695,10020,3187,5722, - 2573,10901,6824,4486,4152,9371,8361,2950,2177,311,1800,9035, - 8313,11721,3430,490,70,10,1757,251,3547,7529,11609,3414,7510, - 4584,4166,9373,1339,5458,7802,11648,1664,7260,9815,10180,6721, - 9738,10169,8475,8233,9954,1422,8981,1283,5450,11312,1616,3742, - 11068,10359,4991,713,3613,9294,8350,4704,672,96,7036,9783,11931, - 3460,5761,823,10651,12055,10500,1500,5481,783,3623,11051,8601, - 8251,8201,11705,10450,5004,4226,7626,2845,2162,3820,7568,9859, - 3164,452,10598,1514,5483,6050,6131,4387,7649,8115,6426,918,8909, - 8295,1185,5436,11310,8638,1234,5443,11311,5127,2488,2111,10835, - 5059,7745,2862,3920,560,80,1767,2008,3798,11076,6849,2734,10924, - 12094,8750,1250,10712,6797,971,7161,1023,8924,4786,7706,4612,4170, - 7618,6355,4419,5898,11376,10403,10264,6733,4473,639,5358,2521, - 9138,3061,5704,4326,618,5355,765,5376,768,7132,4530,9425,3102, - 9221,6584,11474,10417,10266,12000,6981,6264,4406,2385,7363,4563, - 4163,7617,9866,3165,9230,11852,10471,5007,5982,11388,5138,734, - 3616,11050,12112,6997,11533,12181,10518,12036,3475,2252,7344, - 9827,4915,9480,6621,4457,7659,9872,6677,4465,4149,7615,4599,657, - 3605,515,10607,6782,4480,640,1847,3775,5806,2585,5636,9583,1369, - 10729,8555,10000,11962,5220,7768,8132,8184,9947,1421,203,29,8782, - 11788,1684,10774,10317,4985,9490,8378,4708,11206,5112,5997,7879, - 11659,12199,8765,10030,4944,5973,6120,6141,6144,7900,11662,1666, - 238,34,3516,5769,9602,8394,9977,6692,956,10670,6791,9748,11926, - 8726,11780,5194,742,106,8793,10034,3189,10989,5081,4237,5872,4350, - 2377,10873,6820,6241,11425,10410,10265,3222,5727,9596,4882,2453, - 2106,3812,11078,12116,5242,4260,11142,8614,11764,12214,5256,4262, - 4120,11122,5100,11262,5120,2487,5622,9581,8391,8221,2930,10952, - 12098,6995,6266,9673,4893,699,3611,4027,5842,11368,1624,232,8811, - 8281,1183,169,8802,3013,2186,5579,797,3625,4029,11109,1587,7249, - 11569,8675,6506,2685,10917,12093,12261,12285,1755,7273,1039,1904, - 272,3550,9285,3082,5707,6082,4380,7648,11626,5172,4250,9385,8363, - 8217,4685,5936,848,8899,6538,934,1889,3781,9318,10109,10222,6727, - 961,5404,772,5377,9546,8386,1198,8949,3034,2189,7335,4559,5918,2601, - 10905,5069,9502,3113,7467,8089,11689,5181,9518,8382,2953,3933,4073, - 4093,7607,8109,2914,5683,4323,11151,1593,10761,6804,972,3650,2277, - 5592,4310,7638,9869,4921,703,1856,9043,4803,9464,1352,8971,11815, - 5199,7765,6376,4422,7654,2849,407,8836,6529,7955,2892,9191,1313, - 10721,12065,12257,1751,9028,8312,2943,2176,3822,546,78,8789,11789, - 10462,12028,6985,4509,9422,1346,5459,4291,613,10621,6784,9747,3148, - 7472,2823,5670,810,7138,8042,4660,7688,6365,6176,6149,2634,5643, - 9584,10147,11983,5223,9524,11894,10477,8519,1217,3685,2282,326, - 10580,3267,7489,4581,2410,5611,11335,6886,8006,8166,11700,3427, - 11023,8597,10006,3185,455,65,5276,7776,4622,5927,7869,9902,11948, - 5218,2501,5624,2559,10899,1557,1978,10816,10323,8497,4725,675,1852, - 10798,12076,10503,3256,9243,3076,2195,10847,12083,10504,12034,10497 - }; + private fun helprec(c: Poly, v: Poly, seed: ByteArray, nonce: Byte) { + val v0 = IntArray(8) + val rand = ByteArray(32) + try { + crypto_stream_chacha20(rand, 0, 32, nonce.toLong() shl 56, seed) + for (i in 0 until 256) { + val rbit = (rand[i shr 3].toInt() shr (i and 7)) and 1 + var k = f(v0, 0, v0, 4, 8 * v.coeffs[i].code + 4 * rbit) + k += f(v0, 1, v0, 5, 8 * v.coeffs[256 + i].code + 4 * rbit) + k += f(v0, 2, v0, 6, 8 * v.coeffs[512 + i].code + 4 * rbit) + k += f(v0, 3, v0, 7, 8 * v.coeffs[768 + i].code + 4 * rbit) + k = (2 * PARAM_Q - 1 - k) shr 31 + val vt0 = (k.inv() and v0[0]) xor (k and v0[4]) + val vt1 = (k.inv() and v0[1]) xor (k and v0[5]) + val vt2 = (k.inv() and v0[2]) xor (k and v0[6]) + val vt3 = (k.inv() and v0[3]) xor (k and v0[7]) + c.coeffs[i] = ((vt0 - vt3) and 3).toChar() + c.coeffs[256 + i] = ((vt1 - vt3) and 3).toChar() + c.coeffs[512 + i] = ((vt2 - vt3) and 3).toChar() + c.coeffs[768 + i] = ((-k + 2 * vt3) and 3).toChar() + } + } finally { + v0.fill(0); rand.fill(0) + } + } + + private fun rec(key: ByteArray, v: Poly, c: Poly) { + key.fill(0) + for (i in 0 until 256) { + val c768 = c.coeffs[768 + i].code + val t0 = 16 * PARAM_Q + 8 * v.coeffs[i].code - PARAM_Q * (2 * c.coeffs[i].code + c768) + val t1 = 16 * PARAM_Q + 8 * v.coeffs[256 + i].code - PARAM_Q * (2 * c.coeffs[256 + i].code + c768) + val t2 = 16 * PARAM_Q + 8 * v.coeffs[512 + i].code - PARAM_Q * (2 * c.coeffs[512 + i].code + c768) + val t3 = 16 * PARAM_Q + 8 * v.coeffs[768 + i].code - PARAM_Q * c768 + key[i shr 3] = (key[i shr 3].toInt() or (LDDecode(t0, t1, t2, t3) shl (i and 7))).toByte() + } + } + + private fun LDDecode(xi0: Int, xi1: Int, xi2: Int, xi3: Int): Int { + var t = g(xi0) + g(xi1) + g(xi2) + g(xi3) + return ((t - 8 * PARAM_Q) shr 31) and 1 + } + + private fun bitrev_vector(poly: CharArray) { + for (p in 0 until 496) { + val indices = bitrev_table_combined[p] + val i = indices and 0x03FF + val r = indices shr 10 + val tmp = poly[i] + poly[i] = poly[r] + poly[r] = tmp + } + } + + private fun mul_coefficients(poly: CharArray, factors: CharArray) { + for (i in 0 until PARAM_N) { + poly[i] = montgomery_reduce(poly[i].code * factors[i].code).toChar() + } + } + + private fun ntt_global(a: CharArray, omega: CharArray) { + for (i in 0 until 10 step 2) { + var dist = 1 shl i + repeat(dist) { start -> + var tw = 0 + var j = start + while (j < PARAM_N - 1) { + val w = omega[tw++]; val tmp = a[j] + a[j] = (tmp.code + a[j + dist].code).toChar() + a[j + dist] = montgomery_reduce(w.code * (tmp.code + 3 * PARAM_Q - a[dist + j].code)).toChar() + j += 2 * dist + } + } + dist = dist shl 1 + repeat(dist) { start -> + var tw = 0 + var j = start + while (j < PARAM_N - 1) { + val w = omega[tw++]; val tmp = a[j] + a[j] = barrett_reduce(tmp.code + a[j + dist].code).toChar() + a[j + dist] = montgomery_reduce(w.code * (tmp.code + 3 * PARAM_Q - a[dist + j].code)).toChar() + j += 2 * dist + } + } + } + } + + private fun crypto_stream_chacha20(c: ByteArray, coffset: Int, clen: Int, n: Long, k: ByteArray) { + if (clen <= 0) return + var off = coffset; var len = clen; var blk = 0 + while (len >= 64) { + crypto_core_chacha20(c, off, n, blk++, k) + len -= 64; off += 64 + } + if (len > 0) { + val block = ByteArray(64) + try { + crypto_core_chacha20(block, 0, n, blk, k) + block.copyInto(c, off, 0, len) + } finally { block.fill(0) } + } + } + + private fun crypto_core_chacha20(out: ByteArray, outOffset: Int, nonce: Long, blknum: Int, k: ByteArray) { + val state = IntArray(16) + state[0] = 0x61707865; state[1] = 0x3320646e; state[2] = 0x79622d32; state[3] = 0x6b206574 + for (i in 0 until 8) state[4 + i] = (k[4 * i].toInt() and 0xff) or ((k[4 * i + 1].toInt() and 0xff) shl 8) or ((k[4 * i + 2].toInt() and 0xff) shl 16) or ((k[4 * i + 3].toInt() and 0xff) shl 24) + state[12] = blknum; state[13] = 0; state[14] = nonce.toInt(); state[15] = (nonce ushr 32).toInt() + val working = state.copyOf() + for (i in 0 until 20 step 2) { + qr(working, 0, 4, 8, 12); qr(working, 1, 5, 9, 13); qr(working, 2, 6, 10, 14); qr(working, 3, 7, 11, 15) + qr(working, 0, 5, 10, 15); qr(working, 1, 6, 11, 12); qr(working, 2, 7, 8, 13); qr(working, 3, 4, 9, 14) + } + for (i in 0 until 16) { + val v = working[i] + state[i] + out[outOffset + 4 * i] = v.toByte() + out[outOffset + 4 * i + 1] = (v shr 8).toByte() + out[outOffset + 4 * i + 2] = (v shr 16).toByte() + out[outOffset + 4 * i + 3] = (v shr 24).toByte() + } + } + private fun qr(v: IntArray, a: Int, b: Int, c: Int, d: Int) { + v[a] += v[b]; v[d] = rotl(v[d] xor v[a], 16) + v[c] += v[d]; v[b] = rotl(v[b] xor v[c], 12) + v[a] += v[b]; v[d] = rotl(v[d] xor v[a], 8) + v[c] += v[d]; v[b] = rotl(v[b] xor v[c], 7) + } + private fun rotl(v: Int, n: Int) = (v shl n) or (v ushr (32 - n)) + + fun shake128_absorb(s: LongArray, input: ByteArray, offset: Int, len: Int) { + keccak_absorb(s, SHAKE128_RATE, input, offset, len, 0x1F.toByte()) + } + + fun shake128_squeezeblocks(output: ByteArray, offset: Int, nblocks: Int, s: LongArray) { + keccak_squeezeblocks(output, offset, nblocks, s, SHAKE128_RATE) + } + + private fun keccak_absorb(s: LongArray, r: Int, m: ByteArray, offset: Int, mlen: Int, p: Byte) { + var off = offset; var len = mlen + s.fill(0L) + while (len >= r) { + for (i in 0 until r / 8) s[i] = s[i] xor load64(m, off + 8 * i) + KeccakF1600_StatePermute(s) + len -= r; off += r + } + val t = ByteArray(200) + try { + for (i in 0 until len) t[i] = m[off + i] + t[len] = p; t[r - 1] = (t[r - 1].toInt() or 128).toByte() + for (i in 0 until r / 8) s[i] = s[i] xor load64(t, 8 * i) + } finally { t.fill(0) } + } + + private fun keccak_squeezeblocks(h: ByteArray, offset: Int, nblocks: Int, s: LongArray, r: Int) { + var off = offset + repeat(nblocks) { + KeccakF1600_StatePermute(s) + for (i in 0 until (r shr 3)) store64(h, off + 8 * i, s[i]) + off += r + } + } + + private fun load64(x: ByteArray, off: Int): Long { + var r = 0L + for (i in 0..7) r = r or ((x[off + i].toLong() and 0xffL) shl (8 * i)) + return r + } + + private fun store64(x: ByteArray, off: Int, u: Long) { + var v = u + for (i in 0..7) { x[off + i] = v.toByte(); v = v shr 8 } + } + + private fun KeccakF1600_StatePermute(s: LongArray) { + // Minimal Keccak-f[1600] implementation + repeat(24) { r -> + val bc = LongArray(5) + for (i in 0..4) bc[i] = s[i] xor s[i + 5] xor s[i + 10] xor s[i + 15] xor s[i + 20] + for (i in 0..4) { + val t = bc[(i + 4) % 5] xor rol64(bc[(i + 1) % 5], 1) + for (j in 0..20 step 5) s[i + j] = s[i + j] xor t + } + var cur = s[1]; var last = 0L + for (i in 0..23) { + val next = s[keccak_pi[i]]; s[keccak_pi[i]] = rol64(cur, keccak_rho[i]) + cur = next + } + for (j in 0..20 step 5) { + for (i in 0..4) bc[i] = s[i + j] + for (i in 0..4) s[i + j] = bc[i] xor (bc[(i + 1) % 5].inv() and bc[(i + 2) % 5]) + } + s[0] = s[0] xor KeccakF_RoundConstants[r] + } + } + private fun rol64(a: Long, n: Int) = if (n == 0) a else (a shl n) or (a ushr (64 - n)) + private val keccak_rho = intArrayOf(1, 62, 28, 27, 36, 44, 6, 55, 20, 3, 10, 43, 25, 39, 41, 45, 15, 21, 8, 18, 2, 61, 56, 14) + private val keccak_pi = intArrayOf(10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1) + private val KeccakF_RoundConstants = longArrayOf( + 0x0000000000000001L, 0x0000000000008082L, -0x7FFFFFFFFFFF7F76L, -0x7FFFFFFF7FFF8000L, + 0x000000000000808BL, 0x0000000080000001L, -0x7FFFFFFF7FFF7F7FL, -0x7FFFFFFFFFFF7FF7L, + 0x000000000000008AL, 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000AL, + 0x000000008000808BL, -0x7FFFFFFFFFFFFF75L, -0x7FFFFFFFFFFF7F77L, -0x7FFFFFFFFFFF7FFDL, + -0x7FFFFFFFFFFF7FFEL, -0x7FFFFFFFFFFFFF80L, 0x000000000000800AL, -0x7FFFFFFF7FFFFFF6L, + -0x7FFFFFFF7FFF7F7FL, -0x7FFFFFFFFFFF7F80L, 0x0000000080000001L, -0x7FFFFFFF7FFF7FF8L + ) + + private fun sha3256(output: ByteArray, outOffset: Int, input: ByteArray, inOffset: Int, len: Int) { + val s = LongArray(25) + keccak_absorb(s, 136, input, inOffset, len, 0x06.toByte()) + val t = ByteArray(136) + keccak_squeezeblocks(t, 0, 1, s, 136) + t.copyInto(output, outOffset, 0, 32) + } + + private val omegas_montgomery = charArrayOf( + 4075.toChar(), 6974.toChar(), 7373.toChar(), 7965.toChar(), 3262.toChar(), 5079.toChar(), 522.toChar(), 2169.toChar(), 6364.toChar(), 1018.toChar(), 1041.toChar(), 8775.toChar(), 2344.toChar(), 11011.toChar(), 5574.toChar(), 1973.toChar(), + 4536.toChar(), 1050.toChar(), 6844.toChar(), 3860.toChar(), 3818.toChar(), 6118.toChar(), 2683.toChar(), 1190.toChar(), 4789.toChar(), 7822.toChar(), 7540.toChar(), 6752.toChar(), 5456.toChar(), 4449.toChar(), 3789.toChar(), 12142.toChar(), + 11973.toChar(), 382.toChar(), 3988.toChar(), 468.toChar(), 6843.toChar(), 5339.toChar(), 6196.toChar(), 3710.toChar(), 11316.toChar(), 1254.toChar(), 5435.toChar(), 10930.toChar(), 3998.toChar(), 10256.toChar(), 10367.toChar(), 3879.toChar(), + 11889.toChar(), 1728.toChar(), 6137.toChar(), 4948.toChar(), 5862.toChar(), 6136.toChar(), 3643.toChar(), 6874.toChar(), 8724.toChar(), 654.toChar(), 10302.toChar(), 1702.toChar(), 7083.toChar(), 6760.toChar(), 56.toChar(), 3199.toChar(), + 9987.toChar(), 605.toChar(), 11785.toChar(), 8076.toChar(), 5594.toChar(), 9260.toChar(), 6403.toChar(), 4782.toChar(), 6212.toChar(), 4624.toChar(), 9026.toChar(), 8689.toChar(), 4080.toChar(), 11868.toChar(), 6221.toChar(), 3602.toChar(), + 975.toChar(), 8077.toChar(), 8851.toChar(), 9445.toChar(), 5681.toChar(), 3477.toChar(), 1105.toChar(), 142.toChar(), 241.toChar(), 12231.toChar(), 1003.toChar(), 3532.toChar(), 5009.toChar(), 1956.toChar(), 6008.toChar(), 11404.toChar(), + 7377.toChar(), 2049.toChar(), 10968.toChar(), 12097.toChar(), 7591.toChar(), 5057.toChar(), 3445.toChar(), 4780.toChar(), 2920.toChar(), 7048.toChar(), 3127.toChar(), 8120.toChar(), 11279.toChar(), 6821.toChar(), 11502.toChar(), 8807.toChar(), + 12138.toChar(), 2127.toChar(), 2839.toChar(), 3957.toChar(), 431.toChar(), 1579.toChar(), 6383.toChar(), 9784.toChar(), 5874.toChar(), 677.toChar(), 3336.toChar(), 6234.toChar(), 2766.toChar(), 1323.toChar(), 9115.toChar(), 12237.toChar(), + 2031.toChar(), 6956.toChar(), 6413.toChar(), 2281.toChar(), 3969.toChar(), 3991.toChar(), 12133.toChar(), 9522.toChar(), 4737.toChar(), 10996.toChar(), 4774.toChar(), 5429.toChar(), 11871.toChar(), 3772.toChar(), 453.toChar(), 5908.toChar(), + 2882.toChar(), 1805.toChar(), 2051.toChar(), 1954.toChar(), 11713.toChar(), 3963.toChar(), 2447.toChar(), 6142.toChar(), 8174.toChar(), 3030.toChar(), 1843.toChar(), 2361.toChar(), 12071.toChar(), 2908.toChar(), 3529.toChar(), 3434.toChar(), + 3202.toChar(), 7796.toChar(), 2057.toChar(), 5369.toChar(), 11939.toChar(), 1512.toChar(), 6906.toChar(), 10474.toChar(), 11026.toChar(), 49.toChar(), 10806.toChar(), 5915.toChar(), 1489.toChar(), 9789.toChar(), 5942.toChar(), 10706.toChar(), + 10431.toChar(), 7535.toChar(), 426.toChar(), 8974.toChar(), 3757.toChar(), 10314.toChar(), 9364.toChar(), 347.toChar(), 5868.toChar(), 9551.toChar(), 9634.toChar(), 6554.toChar(), 10596.toChar(), 9280.toChar(), 11566.toChar(), 174.toChar(), + 2948.toChar(), 2503.toChar(), 6507.toChar(), 10723.toChar(), 11606.toChar(), 2459.toChar(), 64.toChar(), 3656.toChar(), 8455.toChar(), 5257.toChar(), 5919.toChar(), 7856.toChar(), 1747.toChar(), 9166.toChar(), 5486.toChar(), 9235.toChar(), + 6065.toChar(), 835.toChar(), 3570.toChar(), 4240.toChar(), 11580.toChar(), 4046.toChar(), 10970.toChar(), 9139.toChar(), 1058.toChar(), 8210.toChar(), 11848.toChar(), 922.toChar(), 7967.toChar(), 1958.toChar(), 10211.toChar(), 1112.toChar(), + 3728.toChar(), 4049.toChar(), 11130.toChar(), 5990.toChar(), 1404.toChar(), 325.toChar(), 948.toChar(), 11143.toChar(), 6190.toChar(), 295.toChar(), 11637.toChar(), 5766.toChar(), 8212.toChar(), 8273.toChar(), 2919.toChar(), 8527.toChar(), + 6119.toChar(), 6992.toChar(), 8333.toChar(), 1360.toChar(), 2555.toChar(), 6167.toChar(), 1200.toChar(), 7105.toChar(), 7991.toChar(), 3329.toChar(), 9597.toChar(), 12121.toChar(), 5106.toChar(), 5961.toChar(), 10695.toChar(), 10327.toChar(), + 3051.toChar(), 9923.toChar(), 4896.toChar(), 9326.toChar(), 81.toChar(), 3091.toChar(), 1000.toChar(), 7969.toChar(), 4611.toChar(), 726.toChar(), 1853.toChar(), 12149.toChar(), 4255.toChar(), 11112.toChar(), 2768.toChar(), 10654.toChar(), + 1062.toChar(), 2294.toChar(), 3553.toChar(), 4805.toChar(), 2747.toChar(), 4846.toChar(), 8577.toChar(), 9154.toChar(), 1170.toChar(), 2319.toChar(), 790.toChar(), 11334.toChar(), 9275.toChar(), 9088.toChar(), 1326.toChar(), 5086.toChar(), + 9094.toChar(), 6429.toChar(), 11077.toChar(), 10643.toChar(), 3504.toChar(), 3542.toChar(), 8668.toChar(), 9744.toChar(), 1479.toChar(), 1.toChar(), 8246.toChar(), 7143.toChar(), 11567.toChar(), 10984.toChar(), 4134.toChar(), 5736.toChar(), + 4978.toChar(), 10938.toChar(), 5777.toChar(), 8961.toChar(), 4591.toChar(), 5728.toChar(), 6461.toChar(), 5023.toChar(), 9650.toChar(), 7468.toChar(), 949.toChar(), 9664.toChar(), 2975.toChar(), 11726.toChar(), 2744.toChar(), 9283.toChar(), + 10092.toChar(), 5067.toChar(), 12171.toChar(), 2476.toChar(), 3748.toChar(), 11336.toChar(), 6522.toChar(), 827.toChar(), 9452.toChar(), 5374.toChar(), 12159.toChar(), 7935.toChar(), 3296.toChar(), 3949.toChar(), 9893.toChar(), 4452.toChar(), + 10908.toChar(), 2525.toChar(), 3584.toChar(), 8112.toChar(), 8011.toChar(), 10616.toChar(), 4989.toChar(), 6958.toChar(), 11809.toChar(), 9447.toChar(), 12280.toChar(), 1022.toChar(), 11950.toChar(), 9821.toChar(), 11745.toChar(), 5791.toChar(), + 5092.toChar(), 2089.toChar(), 9005.toChar(), 2881.toChar(), 3289.toChar(), 2013.toChar(), 9048.toChar(), 729.toChar(), 7901.toChar(), 1260.toChar(), 5755.toChar(), 4632.toChar(), 11955.toChar(), 2426.toChar(), 10593.toChar(), 1428.toChar(), + 4890.toChar(), 5911.toChar(), 3932.toChar(), 9558.toChar(), 8830.toChar(), 3637.toChar(), 5542.toChar(), 145.toChar(), 5179.toChar(), 8595.toChar(), 3707.toChar(), 10530.toChar(), 355.toChar(), 3382.toChar(), 4231.toChar(), 9741.toChar(), + 1207.toChar(), 9041.toChar(), 7012.toChar(), 1168.toChar(), 10146.toChar(), 11224.toChar(), 4645.toChar(), 11885.toChar(), 10911.toChar(), 10377.toChar(), 435.toChar(), 7952.toChar(), 4096.toChar(), 493.toChar(), 9908.toChar(), 6845.toChar(), + 6039.toChar(), 2422.toChar(), 2187.toChar(), 9723.toChar(), 8643.toChar(), 9852.toChar(), 9302.toChar(), 6022.toChar(), 7278.toChar(), 1002.toChar(), 4284.toChar(), 5088.toChar(), 1607.toChar(), 7313.toChar(), 875.toChar(), 8509.toChar(), + 9430.toChar(), 1045.toChar(), 2481.toChar(), 5012.toChar(), 7428.toChar(), 354.toChar(), 6591.toChar(), 9377.toChar(), 11847.toChar(), 2401.toChar(), 1067.toChar(), 7188.toChar(), 11516.toChar(), 390.toChar(), 8511.toChar(), 8456.toChar(), + 7270.toChar(), 545.toChar(), 8585.toChar(), 9611.toChar(), 12047.toChar(), 1537.toChar(), 4143.toChar(), 4714.toChar(), 4885.toChar(), 1017.toChar(), 5084.toChar(), 1632.toChar(), 3066.toChar(), 27.toChar(), 1440.toChar(), 8526.toChar(), + 9273.toChar(), 12046.toChar(), 11618.toChar(), 9289.toChar(), 3400.toChar(), 9890.toChar(), 3136.toChar(), 7098.toChar(), 8758.toChar(), 11813.toChar(), 7384.toChar(), 3985.toChar(), 11869.toChar(), 6730.toChar(), 10745.toChar(), 10111.toChar(), + 2249.toChar(), 4048.toChar(), 2884.toChar(), 11136.toChar(), 2126.toChar(), 1630.toChar(), 9103.toChar(), 5407.toChar(), 2686.toChar(), 9042.toChar(), 2969.toChar(), 8311.toChar(), 9424.toChar(), 9919.toChar(), 8779.toChar(), 5332.toChar(), + 10626.toChar(), 1777.toChar(), 4654.toChar(), 10863.toChar(), 7351.toChar(), 3636.toChar(), 9585.toChar(), 5291.toChar(), 8374.toChar(), 2166.toChar(), 4919.toChar(), 12176.toChar(), 9140.toChar(), 12129.toChar(), 7852.toChar(), 12286.toChar(), + 4895.toChar(), 10805.toChar(), 2780.toChar(), 5195.toChar(), 2305.toChar(), 7247.toChar(), 9644.toChar(), 4053.toChar(), 10600.toChar(), 3364.toChar(), 3271.toChar(), 4057.toChar(), 4414.toChar(), 9442.toChar(), 7917.toChar(), 2174.toChar() + ) + private val omegas_inv_montgomery = charArrayOf( + 4075.toChar(), 5315.toChar(), 4324.toChar(), 4916.toChar(), 10120.toChar(), 11767.toChar(), 7210.toChar(), 9027.toChar(), 10316.toChar(), 6715.toChar(), 1278.toChar(), 9945.toChar(), 3514.toChar(), 11248.toChar(), 11271.toChar(), 5925.toChar(), + 147.toChar(), 8500.toChar(), 7840.toChar(), 6833.toChar(), 5537.toChar(), 4749.toChar(), 4467.toChar(), 7500.toChar(), 11099.toChar(), 9606.toChar(), 6171.toChar(), 8471.toChar(), 8429.toChar(), 5445.toChar(), 11239.toChar(), 7753.toChar(), + 9090.toChar(), 12233.toChar(), 5529.toChar(), 5206.toChar(), 10587.toChar(), 1987.toChar(), 11635.toChar(), 3565.toChar(), 5415.toChar(), 8646.toChar(), 6153.toChar(), 6427.toChar(), 7341.toChar(), 6152.toChar(), 10561.toChar(), 400.toChar(), + 8410.toChar(), 1922.toChar(), 2033.toChar(), 8291.toChar(), 1359.toChar(), 6854.toChar(), 11035.toChar(), 973.toChar(), 8579.toChar(), 6093.toChar(), 6950.toChar(), 5446.toChar(), 11821.toChar(), 8301.toChar(), 11907.toChar(), 316.toChar(), + 52.toChar(), 3174.toChar(), 10966.toChar(), 9523.toChar(), 6055.toChar(), 8953.toChar(), 11612.toChar(), 6415.toChar(), 2505.toChar(), 5906.toChar(), 10710.toChar(), 11858.toChar(), 8332.toChar(), 9450.toChar(), 10162.toChar(), 151.toChar(), + 3482.toChar(), 787.toChar(), 5468.toChar(), 1010.toChar(), 4169.toChar(), 9162.toChar(), 5241.toChar(), 9369.toChar(), 7509.toChar(), 8844.toChar(), 7232.toChar(), 4698.toChar(), 192.toChar(), 1321.toChar(), 10240.toChar(), 4912.toChar(), + 885.toChar(), 6281.toChar(), 10333.toChar(), 7280.toChar(), 8757.toChar(), 11286.toChar(), 58.toChar(), 12048.toChar(), 12147.toChar(), 11184.toChar(), 8812.toChar(), 6608.toChar(), 2844.toChar(), 3438.toChar(), 4212.toChar(), 11314.toChar(), + 8687.toChar(), 6068.toChar(), 421.toChar(), 8209.toChar(), 3600.toChar(), 3263.toChar(), 7665.toChar(), 6077.toChar(), 7507.toChar(), 5886.toChar(), 3029.toChar(), 6695.toChar(), 4213.toChar(), 504.toChar(), 11684.toChar(), 2302.toChar(), + 1962.toChar(), 1594.toChar(), 6328.toChar(), 7183.toChar(), 168.toChar(), 2692.toChar(), 8960.toChar(), 4298.toChar(), 5184.toChar(), 11089.toChar(), 6122.toChar(), 9734.toChar(), 10929.toChar(), 3956.toChar(), 5297.toChar(), 6170.toChar(), + 3762.toChar(), 9370.toChar(), 4016.toChar(), 4077.toChar(), 6523.toChar(), 652.toChar(), 11994.toChar(), 6099.toChar(), 1146.toChar(), 11341.toChar(), 11964.toChar(), 10885.toChar(), 6299.toChar(), 1159.toChar(), 8240.toChar(), 8561.toChar(), + 11177.toChar(), 2078.toChar(), 10331.toChar(), 4322.toChar(), 11367.toChar(), 441.toChar(), 4079.toChar(), 11231.toChar(), 3150.toChar(), 1319.toChar(), 8243.toChar(), 709.toChar(), 8049.toChar(), 8719.toChar(), 11454.toChar(), 6224.toChar(), + 3054.toChar(), 6803.toChar(), 3123.toChar(), 10542.toChar(), 4433.toChar(), 6370.toChar(), 7032.toChar(), 3834.toChar(), 8633.toChar(), 12225.toChar(), 9830.toChar(), 683.toChar(), 1566.toChar(), 5782.toChar(), 9786.toChar(), 9341.toChar(), + 12115.toChar(), 723.toChar(), 3009.toChar(), 1693.toChar(), 5735.toChar(), 2655.toChar(), 2738.toChar(), 6421.toChar(), 11942.toChar(), 2925.toChar(), 1975.toChar(), 8532.toChar(), 3315.toChar(), 11863.toChar(), 4754.toChar(), 1858.toChar(), + 1583.toChar(), 6347.toChar(), 2500.toChar(), 10800.toChar(), 6374.toChar(), 1483.toChar(), 12240.toChar(), 1263.toChar(), 1815.toChar(), 5383.toChar(), 10777.toChar(), 350.toChar(), 6920.toChar(), 10232.toChar(), 4493.toChar(), 9087.toChar(), + 8855.toChar(), 8760.toChar(), 9381.toChar(), 218.toChar(), 9928.toChar(), 10446.toChar(), 9259.toChar(), 4115.toChar(), 6147.toChar(), 9842.toChar(), 8326.toChar(), 576.toChar(), 10335.toChar(), 10238.toChar(), 10484.toChar(), 9407.toChar(), + 6381.toChar(), 11836.toChar(), 8517.toChar(), 418.toChar(), 6860.toChar(), 7515.toChar(), 1293.toChar(), 7552.toChar(), 2767.toChar(), 156.toChar(), 8298.toChar(), 8320.toChar(), 10008.toChar(), 5876.toChar(), 5333.toChar(), 10258.toChar(), + 10115.toChar(), 4372.toChar(), 2847.toChar(), 7875.toChar(), 8232.toChar(), 9018.toChar(), 8925.toChar(), 1689.toChar(), 8236.toChar(), 2645.toChar(), 5042.toChar(), 9984.toChar(), 7094.toChar(), 9509.toChar(), 1484.toChar(), 7394.toChar(), + 3.toChar(), 4437.toChar(), 160.toChar(), 3149.toChar(), 113.toChar(), 7370.toChar(), 10123.toChar(), 3915.toChar(), 6998.toChar(), 2704.toChar(), 8653.toChar(), 4938.toChar(), 1426.toChar(), 7635.toChar(), 10512.toChar(), 1663.toChar(), + 6957.toChar(), 3510.toChar(), 2370.toChar(), 2865.toChar(), 3978.toChar(), 9320.toChar(), 3247.toChar(), 9603.toChar(), 6882.toChar(), 3186.toChar(), 10659.toChar(), 10163.toChar(), 1153.toChar(), 9405.toChar(), 8241.toChar(), 10040.toChar(), + 2178.toChar(), 1544.toChar(), 5559.toChar(), 420.toChar(), 8304.toChar(), 4905.toChar(), 476.toChar(), 3531.toChar(), 5191.toChar(), 9153.toChar(), 2399.toChar(), 8889.toChar(), 3000.toChar(), 671.toChar(), 243.toChar(), 3016.toChar(), + 3763.toChar(), 10849.toChar(), 12262.toChar(), 9223.toChar(), 10657.toChar(), 7205.toChar(), 11272.toChar(), 7404.toChar(), 7575.toChar(), 8146.toChar(), 10752.toChar(), 242.toChar(), 2678.toChar(), 3704.toChar(), 11744.toChar(), 5019.toChar(), + 3833.toChar(), 3778.toChar(), 11899.toChar(), 773.toChar(), 5101.toChar(), 11222.toChar(), 9888.toChar(), 442.toChar(), 2912.toChar(), 5698.toChar(), 11935.toChar(), 4861.toChar(), 7277.toChar(), 9808.toChar(), 11244.toChar(), 2859.toChar(), + 3780.toChar(), 11414.toChar(), 4976.toChar(), 10682.toChar(), 7201.toChar(), 8005.toChar(), 11287.toChar(), 5011.toChar(), 6267.toChar(), 2987.toChar(), 2437.toChar(), 3646.toChar(), 2566.toChar(), 10102.toChar(), 9867.toChar(), 6250.toChar(), + 5444.toChar(), 2381.toChar(), 11796.toChar(), 8193.toChar(), 4337.toChar(), 11854.toChar(), 1912.toChar(), 1378.toChar(), 404.toChar(), 7644.toChar(), 1065.toChar(), 2143.toChar(), 11121.toChar(), 5277.toChar(), 3248.toChar(), 11082.toChar(), + 2548.toChar(), 8058.toChar(), 8907.toChar(), 11934.toChar(), 1759.toChar(), 8582.toChar(), 3694.toChar(), 7110.toChar(), 12144.toChar(), 6747.toChar(), 8652.toChar(), 3459.toChar(), 2731.toChar(), 8357.toChar(), 6378.toChar(), 7399.toChar(), + 10861.toChar(), 1696.toChar(), 9863.toChar(), 334.toChar(), 7657.toChar(), 6534.toChar(), 11029.toChar(), 4388.toChar(), 11560.toChar(), 3241.toChar(), 10276.toChar(), 9000.toChar(), 9408.toChar(), 3284.toChar(), 10200.toChar(), 7197.toChar(), + 6498.toChar(), 544.toChar(), 2468.toChar(), 339.toChar(), 11267.toChar(), 9.toChar(), 2842.toChar(), 480.toChar(), 5331.toChar(), 7300.toChar(), 1673.toChar(), 4278.toChar(), 4177.toChar(), 8705.toChar(), 9764.toChar(), 1381.toChar(), + 7837.toChar(), 2396.toChar(), 8340.toChar(), 8993.toChar(), 4354.toChar(), 130.toChar(), 6915.toChar(), 2837.toChar(), 11462.toChar(), 5767.toChar(), 953.toChar(), 8541.toChar(), 9813.toChar(), 118.toChar(), 7222.toChar(), 2197.toChar(), + 3006.toChar(), 9545.toChar(), 563.toChar(), 9314.toChar(), 2625.toChar(), 11340.toChar(), 4821.toChar(), 2639.toChar(), 7266.toChar(), 5828.toChar(), 6561.toChar(), 7698.toChar(), 3328.toChar(), 6512.toChar(), 1351.toChar(), 7311.toChar(), + 6553.toChar(), 8155.toChar(), 1305.toChar(), 722.toChar(), 5146.toChar(), 4043.toChar(), 12288.toChar(), 10810.toChar(), 2545.toChar(), 3621.toChar(), 8747.toChar(), 8785.toChar(), 1646.toChar(), 1212.toChar(), 5860.toChar(), 3195.toChar(), + 7203.toChar(), 10963.toChar(), 3201.toChar(), 3014.toChar(), 955.toChar(), 11499.toChar(), 9970.toChar(), 11119.toChar(), 3135.toChar(), 3712.toChar(), 7443.toChar(), 9542.toChar(), 7484.toChar(), 8736.toChar(), 9995.toChar(), 11227.toChar(), + 1635.toChar(), 9521.toChar(), 1177.toChar(), 8034.toChar(), 140.toChar(), 10436.toChar(), 11563.toChar(), 7678.toChar(), 4320.toChar(), 11289.toChar(), 9198.toChar(), 12208.toChar(), 2963.toChar(), 7393.toChar(), 2366.toChar(), 9238.toChar() + ) + private val psis_bitrev_montgomery = charArrayOf( + 4075.toChar(), 6974.toChar(), 7373.toChar(), 7965.toChar(), 3262.toChar(), 5079.toChar(), 522.toChar(), 2169.toChar(), 6364.toChar(), 1018.toChar(), 1041.toChar(), 8775.toChar(), 2344.toChar(), 11011.toChar(), 5574.toChar(), 1973.toChar(), + 4536.toChar(), 1050.toChar(), 6844.toChar(), 3860.toChar(), 3818.toChar(), 6118.toChar(), 2683.toChar(), 1190.toChar(), 4789.toChar(), 7822.toChar(), 7540.toChar(), 6752.toChar(), 5456.toChar(), 4449.toChar(), 3789.toChar(), 12142.toChar(), + 11973.toChar(), 382.toChar(), 3988.toChar(), 468.toChar(), 6843.toChar(), 5339.toChar(), 6196.toChar(), 3710.toChar(), 11316.toChar(), 1254.toChar(), 5435.toChar(), 10930.toChar(), 3998.toChar(), 10256.toChar(), 10367.toChar(), 3879.toChar(), + 11889.toChar(), 1728.toChar(), 6137.toChar(), 4948.toChar(), 5862.toChar(), 6136.toChar(), 3643.toChar(), 6874.toChar(), 8724.toChar(), 654.toChar(), 10302.toChar(), 1702.toChar(), 7083.toChar(), 6760.toChar(), 56.toChar(), 3199.toChar(), + 9987.toChar(), 605.toChar(), 11785.toChar(), 8076.toChar(), 5594.toChar(), 9260.toChar(), 6403.toChar(), 4782.toChar(), 6212.toChar(), 4624.toChar(), 9026.toChar(), 8689.toChar(), 4080.toChar(), 11868.toChar(), 6221.toChar(), 3602.toChar(), + 975.toChar(), 8077.toChar(), 8851.toChar(), 9445.toChar(), 5681.toChar(), 3477.toChar(), 1105.toChar(), 142.toChar(), 241.toChar(), 12231.toChar(), 1003.toChar(), 3532.toChar(), 5009.toChar(), 1956.toChar(), 6008.toChar(), 11404.toChar(), + 7377.toChar(), 2049.toChar(), 10968.toChar(), 12097.toChar(), 7591.toChar(), 5057.toChar(), 3445.toChar(), 4780.toChar(), 2920.toChar(), 7048.toChar(), 3127.toChar(), 8120.toChar(), 11279.toChar(), 6821.toChar(), 11502.toChar(), 8807.toChar(), + 12138.toChar(), 2127.toChar(), 2839.toChar(), 3957.toChar(), 431.toChar(), 1579.toChar(), 6383.toChar(), 9784.toChar(), 5874.toChar(), 677.toChar(), 3336.toChar(), 6234.toChar(), 2766.toChar(), 1323.toChar(), 9115.toChar(), 12237.toChar(), + 2031.toChar(), 6956.toChar(), 6413.toChar(), 2281.toChar(), 3969.toChar(), 3991.toChar(), 12133.toChar(), 9522.toChar(), 4737.toChar(), 10996.toChar(), 4774.toChar(), 5429.toChar(), 11871.toChar(), 3772.toChar(), 453.toChar(), 5908.toChar(), + 2882.toChar(), 1805.toChar(), 2051.toChar(), 1954.toChar(), 11713.toChar(), 3963.toChar(), 2447.toChar(), 6142.toChar(), 8174.toChar(), 3030.toChar(), 1843.toChar(), 2361.toChar(), 12071.toChar(), 2908.toChar(), 3529.toChar(), 3434.toChar(), + 3202.toChar(), 7796.toChar(), 2057.toChar(), 5369.toChar(), 11939.toChar(), 1512.toChar(), 6906.toChar(), 10474.toChar(), 11026.toChar(), 49.toChar(), 10806.toChar(), 5915.toChar(), 1489.toChar(), 9789.toChar(), 5942.toChar(), 10706.toChar(), + 10431.toChar(), 7535.toChar(), 426.toChar(), 8974.toChar(), 3757.toChar(), 10314.toChar(), 9364.toChar(), 347.toChar(), 5868.toChar(), 9551.toChar(), 9634.toChar(), 6554.toChar(), 10596.toChar(), 9280.toChar(), 11566.toChar(), 174.toChar(), + 2948.toChar(), 2503.toChar(), 6507.toChar(), 10723.toChar(), 11606.toChar(), 2459.toChar(), 64.toChar(), 3656.toChar(), 8455.toChar(), 5257.toChar(), 5919.toChar(), 7856.toChar(), 1747.toChar(), 9166.toChar(), 5486.toChar(), 9235.toChar(), + 6065.toChar(), 835.toChar(), 3570.toChar(), 4240.toChar(), 11580.toChar(), 4046.toChar(), 10970.toChar(), 9139.toChar(), 1058.toChar(), 8210.toChar(), 11848.toChar(), 922.toChar(), 7967.toChar(), 1958.toChar(), 10211.toChar(), 1112.toChar(), + 3728.toChar(), 4049.toChar(), 11130.toChar(), 5990.toChar(), 1404.toChar(), 325.toChar(), 948.toChar(), 11143.toChar(), 6190.toChar(), 295.toChar(), 11637.toChar(), 5766.toChar(), 8212.toChar(), 8273.toChar(), 2919.toChar(), 8527.toChar(), + 6119.toChar(), 6992.toChar(), 8333.toChar(), 1360.toChar(), 2555.toChar(), 6167.toChar(), 1200.toChar(), 7105.toChar(), 7991.toChar(), 3329.toChar(), 9597.toChar(), 12121.toChar(), 5106.toChar(), 5961.toChar(), 10695.toChar(), 10327.toChar(), + 3051.toChar(), 9923.toChar(), 4896.toChar(), 9326.toChar(), 81.toChar(), 3091.toChar(), 1000.toChar(), 7969.toChar(), 4611.toChar(), 726.toChar(), 1853.toChar(), 12149.toChar(), 4255.toChar(), 11112.toChar(), 2768.toChar(), 10654.toChar(), + 1062.toChar(), 2294.toChar(), 3553.toChar(), 4805.toChar(), 2747.toChar(), 4846.toChar(), 8577.toChar(), 9154.toChar(), 1170.toChar(), 2319.toChar(), 790.toChar(), 11334.toChar(), 9275.toChar(), 9088.toChar(), 1326.toChar(), 5086.toChar(), + 9094.toChar(), 6429.toChar(), 11077.toChar(), 10643.toChar(), 3504.toChar(), 3542.toChar(), 8668.toChar(), 9744.toChar(), 1479.toChar(), 1.toChar(), 8246.toChar(), 7143.toChar(), 11567.toChar(), 10984.toChar(), 4134.toChar(), 5736.toChar(), + 4978.toChar(), 10938.toChar(), 5777.toChar(), 8961.toChar(), 4591.toChar(), 5728.toChar(), 6461.toChar(), 5023.toChar(), 9650.toChar(), 7468.toChar(), 949.toChar(), 9664.toChar(), 2975.toChar(), 11726.toChar(), 2744.toChar(), 9283.toChar(), + 10092.toChar(), 5067.toChar(), 12171.toChar(), 2476.toChar(), 3748.toChar(), 11336.toChar(), 6522.toChar(), 827.toChar(), 9452.toChar(), 5374.toChar(), 12159.toChar(), 7935.toChar(), 3296.toChar(), 3949.toChar(), 9893.toChar(), 4452.toChar(), + 10908.toChar(), 2525.toChar(), 3584.toChar(), 8112.toChar(), 8011.toChar(), 10616.toChar(), 4989.toChar(), 6958.toChar(), 11809.toChar(), 9447.toChar(), 12280.toChar(), 1022.toChar(), 11950.toChar(), 9821.toChar(), 11745.toChar(), 5791.toChar(), + 5092.toChar(), 2089.toChar(), 9005.toChar(), 2881.toChar(), 3289.toChar(), 2013.toChar(), 9048.toChar(), 729.toChar(), 7901.toChar(), 1260.toChar(), 5755.toChar(), 4632.toChar(), 11955.toChar(), 2426.toChar(), 10593.toChar(), 1428.toChar(), + 4890.toChar(), 5911.toChar(), 3932.toChar(), 9558.toChar(), 8830.toChar(), 3637.toChar(), 5542.toChar(), 145.toChar(), 5179.toChar(), 8595.toChar(), 3707.toChar(), 10530.toChar(), 355.toChar(), 3382.toChar(), 4231.toChar(), 9741.toChar(), + 1207.toChar(), 9041.toChar(), 7012.toChar(), 1168.toChar(), 10146.toChar(), 11224.toChar(), 4645.toChar(), 11885.toChar(), 10911.toChar(), 10377.toChar(), 435.toChar(), 7952.toChar(), 4096.toChar(), 493.toChar(), 9908.toChar(), 6845.toChar(), + 6039.toChar(), 2422.toChar(), 2187.toChar(), 9723.toChar(), 8643.toChar(), 9852.toChar(), 9302.toChar(), 6022.toChar(), 7278.toChar(), 1002.toChar(), 4284.toChar(), 5088.toChar(), 1607.toChar(), 7313.toChar(), 875.toChar(), 8509.toChar(), + 9430.toChar(), 1045.toChar(), 2481.toChar(), 5012.toChar(), 7428.toChar(), 354.toChar(), 6591.toChar(), 9377.toChar(), 11847.toChar(), 2401.toChar(), 1067.toChar(), 7188.toChar(), 11516.toChar(), 390.toChar(), 8511.toChar(), 8456.toChar(), + 7270.toChar(), 545.toChar(), 8585.toChar(), 9611.toChar(), 12047.toChar(), 1537.toChar(), 4143.toChar(), 4714.toChar(), 4885.toChar(), 1017.toChar(), 5084.toChar(), 1632.toChar(), 3066.toChar(), 27.toChar(), 1440.toChar(), 8526.toChar(), + 9273.toChar(), 12046.toChar(), 11618.toChar(), 9289.toChar(), 3400.toChar(), 9890.toChar(), 3136.toChar(), 7098.toChar(), 8758.toChar(), 11813.toChar(), 7384.toChar(), 3985.toChar(), 11869.toChar(), 6730.toChar(), 10745.toChar(), 10111.toChar(), + 2249.toChar(), 4048.toChar(), 2884.toChar(), 11136.toChar(), 2126.toChar(), 1630.toChar(), 9103.toChar(), 5407.toChar(), 2686.toChar(), 9042.toChar(), 2969.toChar(), 8311.toChar(), 9424.toChar(), 9919.toChar(), 8779.toChar(), 5332.toChar(), + 10626.toChar(), 1777.toChar(), 4654.toChar(), 10863.toChar(), 7351.toChar(), 3636.toChar(), 9585.toChar(), 5291.toChar(), 8374.toChar(), 2166.toChar(), 4919.toChar(), 12176.toChar(), 9140.toChar(), 12129.toChar(), 7852.toChar(), 12286.toChar(), + 4895.toChar(), 10805.toChar(), 2780.toChar(), 5195.toChar(), 2305.toChar(), 7247.toChar(), 9644.toChar(), 4053.toChar(), 10600.toChar(), 3364.toChar(), 3271.toChar(), 4057.toChar(), 4414.toChar(), 9442.toChar(), 7917.toChar(), 2174.toChar(), + 3947.toChar(), 11951.toChar(), 2455.toChar(), 6599.toChar(), 10545.toChar(), 10975.toChar(), 3654.toChar(), 2894.toChar(), 7681.toChar(), 7126.toChar(), 7287.toChar(), 12269.toChar(), 4119.toChar(), 3343.toChar(), 2151.toChar(), 1522.toChar(), + 7174.toChar(), 7350.toChar(), 11041.toChar(), 2442.toChar(), 2148.toChar(), 5959.toChar(), 6492.toChar(), 8330.toChar(), 8945.toChar(), 5598.toChar(), 3624.toChar(), 10397.toChar(), 1325.toChar(), 6565.toChar(), 1945.toChar(), 11260.toChar(), + 10077.toChar(), 2674.toChar(), 3338.toChar(), 3276.toChar(), 11034.toChar(), 506.toChar(), 6505.toChar(), 1392.toChar(), 5478.toChar(), 8778.toChar(), 1178.toChar(), 2776.toChar(), 3408.toChar(), 10347.toChar(), 11124.toChar(), 2575.toChar(), + 9489.toChar(), 12096.toChar(), 6092.toChar(), 10058.toChar(), 4167.toChar(), 6085.toChar(), 923.toChar(), 11251.toChar(), 11912.toChar(), 4578.toChar(), 10669.toChar(), 11914.toChar(), 425.toChar(), 10453.toChar(), 392.toChar(), 10104.toChar(), + 8464.toChar(), 4235.toChar(), 8761.toChar(), 7376.toChar(), 2291.toChar(), 3375.toChar(), 7954.toChar(), 8896.toChar(), 6617.toChar(), 7790.toChar(), 1737.toChar(), 11667.toChar(), 3982.toChar(), 9342.toChar(), 6680.toChar(), 636.toChar(), + 6825.toChar(), 7383.toChar(), 512.toChar(), 4670.toChar(), 2900.toChar(), 12050.toChar(), 7735.toChar(), 994.toChar(), 1687.toChar(), 11883.toChar(), 7021.toChar(), 146.toChar(), 10485.toChar(), 1403.toChar(), 5189.toChar(), 6094.toChar(), + 2483.toChar(), 2054.toChar(), 3042.toChar(), 10945.toChar(), 3981.toChar(), 10821.toChar(), 11826.toChar(), 8882.toChar(), 8151.toChar(), 180.toChar(), 9600.toChar(), 7684.toChar(), 5219.toChar(), 10880.toChar(), 6780.toChar(), 204.toChar(), + 11232.toChar(), 2600.toChar(), 7584.toChar(), 3121.toChar(), 3017.toChar(), 11053.toChar(), 7814.toChar(), 7043.toChar(), 4251.toChar(), 4739.toChar(), 11063.toChar(), 6771.toChar(), 7073.toChar(), 9261.toChar(), 2360.toChar(), 11925.toChar(), + 1928.toChar(), 11825.toChar(), 8024.toChar(), 3678.toChar(), 3205.toChar(), 3359.toChar(), 11197.toChar(), 5209.toChar(), 8581.toChar(), 3238.toChar(), 8840.toChar(), 1136.toChar(), 9363.toChar(), 1826.toChar(), 3171.toChar(), 4489.toChar(), + 7885.toChar(), 346.toChar(), 2068.toChar(), 1389.toChar(), 8257.toChar(), 3163.toChar(), 4840.toChar(), 6127.toChar(), 8062.toChar(), 8921.toChar(), 612.toChar(), 4238.toChar(), 10763.toChar(), 8067.toChar(), 125.toChar(), 11749.toChar(), + 10125.toChar(), 5416.toChar(), 2110.toChar(), 716.toChar(), 9839.toChar(), 10584.toChar(), 11475.toChar(), 11873.toChar(), 3448.toChar(), 343.toChar(), 1908.toChar(), 4538.toChar(), 10423.toChar(), 7078.toChar(), 4727.toChar(), 1208.toChar(), + 11572.toChar(), 3589.toChar(), 2982.toChar(), 1373.toChar(), 1721.toChar(), 10753.toChar(), 4103.toChar(), 2429.toChar(), 4209.toChar(), 5412.toChar(), 5993.toChar(), 9011.toChar(), 438.toChar(), 3515.toChar(), 7228.toChar(), 1218.toChar(), + 8347.toChar(), 5232.toChar(), 8682.toChar(), 1327.toChar(), 7508.toChar(), 4924.toChar(), 448.toChar(), 1014.toChar(), 10029.toChar(), 12221.toChar(), 4566.toChar(), 5836.toChar(), 12229.toChar(), 2717.toChar(), 1535.toChar(), 3200.toChar(), + 5588.toChar(), 5845.toChar(), 412.toChar(), 5102.toChar(), 7326.toChar(), 3744.toChar(), 3056.toChar(), 2528.toChar(), 7406.toChar(), 8314.toChar(), 9202.toChar(), 6454.toChar(), 6613.toChar(), 1417.toChar(), 10032.toChar(), 7784.toChar(), + 1518.toChar(), 3765.toChar(), 4176.toChar(), 5063.toChar(), 9828.toChar(), 2275.toChar(), 6636.toChar(), 4267.toChar(), 6463.toChar(), 2065.toChar(), 7725.toChar(), 3495.toChar(), 8328.toChar(), 8755.toChar(), 8144.toChar(), 10533.toChar(), + 5966.toChar(), 12077.toChar(), 9175.toChar(), 9520.toChar(), 5596.toChar(), 6302.toChar(), 8400.toChar(), 579.toChar(), 6781.toChar(), 11014.toChar(), 5734.toChar(), 11113.toChar(), 11164.toChar(), 4860.toChar(), 1131.toChar(), 10844.toChar(), + 9068.toChar(), 8016.toChar(), 9694.toChar(), 3837.toChar(), 567.toChar(), 9348.toChar(), 7000.toChar(), 6627.toChar(), 7699.toChar(), 5082.toChar(), 682.toChar(), 11309.toChar(), 5207.toChar(), 4050.toChar(), 7087.toChar(), 844.toChar(), + 7434.toChar(), 3769.toChar(), 293.toChar(), 9057.toChar(), 6940.toChar(), 9344.toChar(), 10883.toChar(), 2633.toChar(), 8190.toChar(), 3944.toChar(), 5530.toChar(), 5604.toChar(), 3480.toChar(), 2171.toChar(), 9282.toChar(), 11024.toChar(), + 2213.toChar(), 8136.toChar(), 3805.toChar(), 767.toChar(), 12239.toChar(), 216.toChar(), 11520.toChar(), 6763.toChar(), 10353.toChar(), 7.toChar(), 8566.toChar(), 845.toChar(), 7235.toChar(), 3154.toChar(), 4360.toChar(), 3285.toChar(), + 10268.toChar(), 2832.toChar(), 3572.toChar(), 1282.toChar(), 7559.toChar(), 3229.toChar(), 8360.toChar(), 10583.toChar(), 6105.toChar(), 3120.toChar(), 6643.toChar(), 6203.toChar(), 8536.toChar(), 8348.toChar(), 6919.toChar(), 3536.toChar(), + 9199.toChar(), 10891.toChar(), 11463.toChar(), 5043.toChar(), 1658.toChar(), 5618.toChar(), 8787.toChar(), 5789.toChar(), 4719.toChar(), 751.toChar(), 11379.toChar(), 6389.toChar(), 10783.toChar(), 3065.toChar(), 7806.toChar(), 6586.toChar(), + 2622.toChar(), 5386.toChar(), 510.toChar(), 7628.toChar(), 6921.toChar(), 578.toChar(), 10345.toChar(), 11839.toChar(), 8929.toChar(), 4684.toChar(), 12226.toChar(), 7154.toChar(), 9916.toChar(), 7302.toChar(), 8481.toChar(), 3670.toChar(), + 11066.toChar(), 2334.toChar(), 1590.toChar(), 7878.toChar(), 10734.toChar(), 1802.toChar(), 1891.toChar(), 5103.toChar(), 6151.toChar(), 8820.toChar(), 3418.toChar(), 7846.toChar(), 9951.toChar(), 4693.toChar(), 417.toChar(), 9996.toChar(), + 9652.toChar(), 4510.toChar(), 2946.toChar(), 5461.toChar(), 365.toChar(), 881.toChar(), 1927.toChar(), 1015.toChar(), 11675.toChar(), 11009.toChar(), 1371.toChar(), 12265.toChar(), 2485.toChar(), 11385.toChar(), 5039.toChar(), 6742.toChar(), + 8449.toChar(), 1842.toChar(), 12217.toChar(), 8176.toChar(), 9577.toChar(), 4834.toChar(), 7937.toChar(), 9461.toChar(), 2643.toChar(), 11194.toChar(), 3045.toChar(), 6508.toChar(), 4094.toChar(), 3451.toChar(), 7911.toChar(), 11048.toChar(), + 5406.toChar(), 4665.toChar(), 3020.toChar(), 6616.toChar(), 11345.toChar(), 7519.toChar(), 3669.toChar(), 5287.toChar(), 1790.toChar(), 7014.toChar(), 5410.toChar(), 11038.toChar(), 11249.toChar(), 2035.toChar(), 6125.toChar(), 10407.toChar(), + 4565.toChar(), 7315.toChar(), 5078.toChar(), 10506.toChar(), 2840.toChar(), 2478.toChar(), 9270.toChar(), 4194.toChar(), 9195.toChar(), 4518.toChar(), 7469.toChar(), 1160.toChar(), 6878.toChar(), 2730.toChar(), 10421.toChar(), 10036.toChar(), + 1734.toChar(), 3815.toChar(), 10939.toChar(), 5832.toChar(), 10595.toChar(), 10759.toChar(), 4423.toChar(), 8420.toChar(), 9617.toChar(), 7119.toChar(), 11010.toChar(), 11424.toChar(), 9173.toChar(), 189.toChar(), 10080.toChar(), 10526.toChar(), + 3466.toChar(), 10588.toChar(), 7592.toChar(), 3578.toChar(), 11511.toChar(), 7785.toChar(), 9663.toChar(), 530.toChar(), 12150.toChar(), 8957.toChar(), 2532.toChar(), 3317.toChar(), 9349.toChar(), 10243.toChar(), 1481.toChar(), 9332.toChar(), + 3454.toChar(), 3758.toChar(), 7899.toChar(), 4218.toChar(), 2593.toChar(), 11410.toChar(), 2276.toChar(), 982.toChar(), 6513.toChar(), 1849.toChar(), 8494.toChar(), 9021.toChar(), 4523.toChar(), 7988.toChar(), 8.toChar(), 457.toChar(), + 648.toChar(), 150.toChar(), 8000.toChar(), 2307.toChar(), 2301.toChar(), 874.toChar(), 5650.toChar(), 170.toChar(), 9462.toChar(), 2873.toChar(), 9855.toChar(), 11498.toChar(), 2535.toChar(), 11169.toChar(), 5808.toChar(), 12268.toChar(), + 9687.toChar(), 1901.toChar(), 7171.toChar(), 11787.toChar(), 3846.toChar(), 1573.toChar(), 6063.toChar(), 3793.toChar(), 466.toChar(), 11259.toChar(), 10608.toChar(), 3821.toChar(), 6320.toChar(), 4649.toChar(), 6263.toChar(), 2929.toChar() + ) + private val psis_inv_montgomery = charArrayOf( + 256.toChar(), 10570.toChar(), 1510.toChar(), 7238.toChar(), 1034.toChar(), 7170.toChar(), 6291.toChar(), 7921.toChar(), 11665.toChar(), 3422.toChar(), 4000.toChar(), 2327.toChar(), 2088.toChar(), 5565.toChar(), 795.toChar(), 10647.toChar(), + 1521.toChar(), 5484.toChar(), 2539.toChar(), 7385.toChar(), 1055.toChar(), 7173.toChar(), 8047.toChar(), 11683.toChar(), 1669.toChar(), 1994.toChar(), 3796.toChar(), 5809.toChar(), 4341.toChar(), 9398.toChar(), 11876.toChar(), 12230.toChar(), + 10525.toChar(), 12037.toChar(), 12253.toChar(), 3506.toChar(), 4012.toChar(), 9351.toChar(), 4847.toChar(), 2448.toChar(), 7372.toChar(), 9831.toChar(), 3160.toChar(), 2207.toChar(), 5582.toChar(), 2553.toChar(), 7387.toChar(), 6322.toChar(), + 9681.toChar(), 1383.toChar(), 10731.toChar(), 1533.toChar(), 219.toChar(), 5298.toChar(), 4268.toChar(), 7632.toChar(), 6357.toChar(), 9686.toChar(), 8406.toChar(), 4712.toChar(), 9451.toChar(), 10128.toChar(), 4958.toChar(), 5975.toChar(), + 11387.toChar(), 8649.toChar(), 11769.toChar(), 6948.toChar(), 11526.toChar(), 12180.toChar(), 1740.toChar(), 10782.toChar(), 6807.toChar(), 2728.toChar(), 7412.toChar(), 4570.toChar(), 4164.toChar(), 4106.toChar(), 11120.toChar(), 12122.toChar(), + 8754.toChar(), 11784.toChar(), 3439.toChar(), 5758.toChar(), 11356.toChar(), 6889.toChar(), 9762.toChar(), 11928.toChar(), 1704.toChar(), 1999.toChar(), 10819.toChar(), 12079.toChar(), 12259.toChar(), 7018.toChar(), 11536.toChar(), 1648.toChar(), + 1991.toChar(), 2040.toChar(), 2047.toChar(), 2048.toChar(), 10826.toChar(), 12080.toChar(), 8748.toChar(), 8272.toChar(), 8204.toChar(), 1172.toChar(), 1923.toChar(), 7297.toChar(), 2798.toChar(), 7422.toChar(), 6327.toChar(), 4415.toChar(), + 7653.toChar(), 6360.toChar(), 11442.toChar(), 12168.toChar(), 7005.toChar(), 8023.toChar(), 9924.toChar(), 8440.toChar(), 8228.toChar(), 2931.toChar(), 7441.toChar(), 1063.toChar(), 3663.toChar(), 5790.toChar(), 9605.toChar(), 10150.toChar(), + 1450.toChar(), 8985.toChar(), 11817.toChar(), 10466.toChar(), 10273.toChar(), 12001.toChar(), 3470.toChar(), 7518.toChar(), 1074.toChar(), 1909.toChar(), 7295.toChar(), 9820.toChar(), 4914.toChar(), 702.toChar(), 5367.toChar(), 7789.toChar(), + 8135.toChar(), 9940.toChar(), 1420.toChar(), 3714.toChar(), 11064.toChar(), 12114.toChar(), 12264.toChar(), 1752.toChar(), 5517.toChar(), 9566.toChar(), 11900.toChar(), 1700.toChar(), 3754.toChar(), 5803.toChar(), 829.toChar(), 1874.toChar(), + 7290.toChar(), 2797.toChar(), 10933.toChar(), 5073.toChar(), 7747.toChar(), 8129.toChar(), 6428.toChar(), 6185.toChar(), 11417.toChar(), 1631.toChar(), 233.toChar(), 5300.toChar(), 9535.toChar(), 10140.toChar(), 11982.toChar(), 8734.toChar(), + 8270.toChar(), 2937.toChar(), 10953.toChar(), 8587.toChar(), 8249.toChar(), 2934.toChar(), 9197.toChar(), 4825.toChar(), 5956.toChar(), 4362.toChar(), 9401.toChar(), 1343.toChar(), 3703.toChar(), 529.toChar(), 10609.toChar(), 12049.toChar(), + 6988.toChar(), 6265.toChar(), 895.toChar(), 3639.toChar(), 4031.toChar(), 4087.toChar(), 4095.toChar(), 585.toChar(), 10617.toChar(), 8539.toChar(), 4731.toChar(), 4187.toChar(), 9376.toChar(), 3095.toChar(), 9220.toChar(), 10095.toChar(), + 10220.toChar(), 1460.toChar(), 10742.toChar(), 12068.toChar(), 1724.toChar(), 5513.toChar(), 11321.toChar(), 6884.toChar(), 2739.toChar(), 5658.toChar(), 6075.toChar(), 4379.toChar(), 11159.toChar(), 10372.toChar(), 8504.toChar(), 4726.toChar(), + 9453.toChar(), 3106.toChar(), 7466.toChar(), 11600.toChar(), 10435.toChar(), 8513.toChar(), 9994.toChar(), 8450.toChar(), 9985.toChar(), 3182.toChar(), 10988.toChar(), 8592.toChar(), 2983.toChar(), 9204.toChar(), 4826.toChar(), 2445.toChar(), + 5616.toChar(), 6069.toChar(), 867.toChar(), 3635.toChar(), 5786.toChar(), 11360.toChar(), 5134.toChar(), 2489.toChar(), 10889.toChar(), 12089.toChar(), 1727.toChar(), 7269.toChar(), 2794.toChar(), 9177.toChar(), 1311.toChar(), 5454.toChar(), + 9557.toChar(), 6632.toChar(), 2703.toChar(), 9164.toChar(), 10087.toChar(), 1441.toChar(), 3717.toChar(), 531.toChar(), 3587.toChar(), 2268.toChar(), 324.toChar(), 5313.toChar(), 759.toChar(), 1864.toChar(), 5533.toChar(), 2546.toChar(), + 7386.toChar(), 9833.toChar(), 8427.toChar(), 4715.toChar(), 11207.toChar(), 1601.toChar(), 7251.toChar(), 4547.toChar(), 11183.toChar(), 12131.toChar(), 1733.toChar(), 10781.toChar(), 10318.toChar(), 1474.toChar(), 10744.toChar(), 5046.toChar(), + 4232.toChar(), 11138.toChar(), 10369.toChar(), 6748.toChar(), 964.toChar(), 7160.toChar(), 4534.toChar(), 7670.toChar(), 8118.toChar(), 8182.toChar(), 4680.toChar(), 11202.toChar(), 6867.toChar(), 981.toChar(), 8918.toChar(), 1274.toChar(), + 182.toChar(), 26.toChar(), 7026.toChar(), 8026.toChar(), 11680.toChar(), 12202.toChar(), 10521.toChar(), 1503.toChar(), 7237.toChar(), 4545.toChar(), 5916.toChar(), 9623.toChar(), 8397.toChar(), 11733.toChar(), 10454.toChar(), 3249.toChar(), + 9242.toChar(), 6587.toChar(), 941.toChar(), 1890.toChar(), 270.toChar(), 10572.toChar(), 6777.toChar(), 9746.toChar(), 6659.toChar(), 6218.toChar(), 6155.toChar(), 6146.toChar(), 878.toChar(), 1881.toChar(), 7291.toChar(), 11575.toChar(), + 12187.toChar(), 1741.toChar(), 7271.toChar(), 8061.toChar(), 11685.toChar(), 6936.toChar(), 4502.toChar(), 9421.toChar(), 4857.toChar(), 4205.toChar(), 7623.toChar(), 1089.toChar(), 10689.toChar(), 1527.toChar(), 8996.toChar(), 10063.toChar(), + 11971.toChar(), 10488.toChar(), 6765.toChar(), 2722.toChar(), 3900.toChar(), 9335.toChar(), 11867.toChar(), 6962.toChar(), 11528.toChar(), 5158.toChar(), 4248.toChar(), 4118.toChar(), 5855.toChar(), 2592.toChar(), 5637.toChar(), 6072.toChar(), + 2623.toChar(), 7397.toChar(), 8079.toChar(), 9932.toChar(), 4930.toChar(), 5971.toChar(), 853.toChar(), 3633.toChar(), 519.toChar(), 8852.toChar(), 11798.toChar(), 3441.toChar(), 11025.toChar(), 1575.toChar(), 225.toChar(), 8810.toChar(), + 11792.toChar(), 12218.toChar(), 3501.toChar(), 9278.toChar(), 3081.toChar(), 9218.toChar(), 4828.toChar(), 7712.toChar(), 8124.toChar(), 11694.toChar(), 12204.toChar(), 3499.toChar(), 4011.toChar(), 573.toChar(), 3593.toChar(), 5780.toChar(), + 7848.toChar(), 9899.toChar(), 10192.toChar(), 1456.toChar(), 208.toChar(), 7052.toChar(), 2763.toChar(), 7417.toChar(), 11593.toChar(), 10434.toChar(), 12024.toChar(), 8740.toChar(), 11782.toChar(), 10461.toChar(), 3250.toChar(), 5731.toChar(), + 7841.toChar(), 9898.toChar(), 1414.toChar(), 202.toChar(), 3540.toChar(), 7528.toChar(), 2831.toChar(), 2160.toChar(), 10842.toChar(), 5060.toChar(), 4234.toChar(), 4116.toChar(), 588.toChar(), 84.toChar(), 12.toChar(), 7024.toChar(), + 2759.toChar(), 9172.toChar(), 6577.toChar(), 11473.toChar(), 1639.toChar(), 9012.toChar(), 3043.toChar(), 7457.toChar(), 6332.toChar(), 11438.toChar(), 1634.toChar(), 1989.toChar(), 9062.toChar(), 11828.toChar(), 8712.toChar(), 11778.toChar(), + 12216.toChar(), 10523.toChar(), 6770.toChar(), 9745.toChar(), 10170.toChar(), 4964.toChar(), 9487.toChar(), 6622.toChar(), 946.toChar(), 8913.toChar(), 6540.toChar(), 6201.toChar(), 4397.toChar(), 9406.toChar(), 8366.toChar(), 9973.toChar(), + 8447.toChar(), 8229.toChar(), 11709.toChar(), 8695.toChar(), 10020.toChar(), 3187.toChar(), 5722.toChar(), 2573.toChar(), 10901.toChar(), 6824.toChar(), 4486.toChar(), 4152.toChar(), 9371.toChar(), 8361.toChar(), 2950.toChar(), 2177.toChar(), + 311.toChar(), 1800.toChar(), 9035.toChar(), 8313.toChar(), 11721.toChar(), 3430.toChar(), 490.toChar(), 70.toChar(), 10.toChar(), 1757.toChar(), 251.toChar(), 3547.toChar(), 7529.toChar(), 11609.toChar(), 3414.toChar(), 7510.toChar(), + 4584.toChar(), 4166.toChar(), 9373.toChar(), 1339.toChar(), 5458.toChar(), 7802.toChar(), 11648.toChar(), 1664.toChar(), 7260.toChar(), 9815.toChar(), 10180.toChar(), 6721.toChar(), 9738.toChar(), 10169.toChar(), 8475.toChar(), 8233.toChar(), + 9954.toChar(), 1422.toChar(), 8981.toChar(), 1283.toChar(), 5450.toChar(), 11312.toChar(), 1616.toChar(), 3742.toChar(), 11068.toChar(), 10359.toChar(), 4991.toChar(), 713.toChar(), 3613.toChar(), 9294.toChar(), 8350.toChar(), 4704.toChar(), + 672.toChar(), 96.toChar(), 7036.toChar(), 9783.toChar(), 11931.toChar(), 3460.toChar(), 5761.toChar(), 823.toChar(), 10651.toChar(), 12055.toChar(), 10500.toChar(), 1500.toChar(), 5481.toChar(), 783.toChar(), 3623.toChar(), 11051.toChar(), + 8601.toChar(), 8251.toChar(), 8201.toChar(), 11705.toChar(), 10450.toChar(), 5004.toChar(), 4226.toChar(), 7626.toChar(), 2845.toChar(), 2162.toChar(), 3820.toChar(), 7568.toChar(), 9859.toChar(), 3164.toChar(), 452.toChar(), 10598.toChar(), + 1514.toChar(), 5483.toChar(), 6050.toChar(), 6131.toChar(), 4387.toChar(), 7649.toChar(), 8115.toChar(), 6426.toChar(), 918.toChar(), 8909.toChar(), 8295.toChar(), 1185.toChar(), 5436.toChar(), 11310.toChar(), 8638.toChar(), 1234.toChar(), + 5443.toChar(), 11311.toChar(), 5127.toChar(), 2488.toChar(), 2111.toChar(), 10835.toChar(), 5059.toChar(), 7745.toChar(), 2862.toChar(), 3920.toChar(), 560.toChar(), 80.toChar(), 1767.toChar(), 2008.toChar(), 3798.toChar(), 11076.toChar(), + 6849.toChar(), 2734.toChar(), 10924.toChar(), 12094.toChar(), 8750.toChar(), 1250.toChar(), 10712.toChar(), 6797.toChar(), 971.toChar(), 7161.toChar(), 1023.toChar(), 8924.toChar(), 4786.toChar(), 7706.toChar(), 4612.toChar(), 4170.toChar(), + 7618.toChar(), 6355.toChar(), 4419.toChar(), 5898.toChar(), 11376.toChar(), 10403.toChar(), 10264.toChar(), 6733.toChar(), 4473.toChar(), 639.toChar(), 5358.toChar(), 2521.toChar(), 9138.toChar(), 3061.toChar(), 5704.toChar(), 4326.toChar(), + 618.toChar(), 5355.toChar(), 765.toChar(), 5376.toChar(), 768.toChar(), 7132.toChar(), 4530.toChar(), 9425.toChar(), 3102.toChar(), 9221.toChar(), 6584.toChar(), 11474.toChar(), 10417.toChar(), 10266.toChar(), 12000.toChar(), 6981.toChar(), + 6264.toChar(), 4406.toChar(), 2385.toChar(), 7363.toChar(), 4563.toChar(), 4163.toChar(), 7617.toChar(), 9866.toChar(), 3165.toChar(), 9230.toChar(), 11852.toChar(), 10471.toChar(), 5007.toChar(), 5982.toChar(), 11388.toChar(), 5138.toChar(), + 734.toChar(), 3616.toChar(), 11050.toChar(), 12112.toChar(), 6997.toChar(), 11533.toChar(), 12181.toChar(), 10518.toChar(), 12036.toChar(), 3475.toChar(), 2252.toChar(), 7344.toChar(), 9827.toChar(), 4915.toChar(), 9480.toChar(), 6621.toChar(), + 4457.toChar(), 7659.toChar(), 9872.toChar(), 6677.toChar(), 4465.toChar(), 4149.toChar(), 7615.toChar(), 4599.toChar(), 657.toChar(), 3605.toChar(), 515.toChar(), 10607.toChar(), 6782.toChar(), 4480.toChar(), 640.toChar(), 1847.toChar(), + 3775.toChar(), 5806.toChar(), 2585.toChar(), 5636.toChar(), 9583.toChar(), 1369.toChar(), 10729.toChar(), 8555.toChar(), 10000.toChar(), 11962.toChar(), 5220.toChar(), 7768.toChar(), 8132.toChar(), 8184.toChar(), 9947.toChar(), 1421.toChar(), + 203.toChar(), 29.toChar(), 8782.toChar(), 11788.toChar(), 1684.toChar(), 10774.toChar(), 10317.toChar(), 4985.toChar(), 9490.toChar(), 8378.toChar(), 4708.toChar(), 11206.toChar(), 5112.toChar(), 5997.toChar(), 7879.toChar(), 11659.toChar(), + 12199.toChar(), 8765.toChar(), 10030.toChar(), 4944.toChar(), 5973.toChar(), 6120.toChar(), 6141.toChar(), 6144.toChar(), 7900.toChar(), 11662.toChar(), 1666.toChar(), 238.toChar(), 34.toChar(), 3516.toChar(), 5769.toChar(), 9602.toChar(), + 8394.toChar(), 9977.toChar(), 6692.toChar(), 956.toChar(), 10670.toChar(), 6791.toChar(), 9748.toChar(), 11926.toChar(), 8726.toChar(), 11780.toChar(), 5194.toChar(), 742.toChar(), 106.toChar(), 8793.toChar(), 10034.toChar(), 3189.toChar(), + 10989.toChar(), 5081.toChar(), 4237.toChar(), 5872.toChar(), 4350.toChar(), 2377.toChar(), 10873.toChar(), 6820.toChar(), 6241.toChar(), 11425.toChar(), 10410.toChar(), 10265.toChar(), 3222.toChar(), 5727.toChar(), 9596.toChar(), 4882.toChar(), + 2453.toChar(), 2106.toChar(), 3812.toChar(), 11078.toChar(), 12116.toChar(), 5242.toChar(), 4260.toChar(), 11142.toChar(), 8614.toChar(), 11764.toChar(), 12214.toChar(), 5256.toChar(), 4262.toChar(), 4120.toChar(), 11122.toChar(), 5100.toChar(), + 11262.toChar(), 5120.toChar(), 2487.toChar(), 5622.toChar(), 9581.toChar(), 8391.toChar(), 8221.toChar(), 2930.toChar(), 10952.toChar(), 12098.toChar(), 6995.toChar(), 6266.toChar(), 9673.toChar(), 4893.toChar(), 699.toChar(), 3611.toChar(), + 4027.toChar(), 5842.toChar(), 11368.toChar(), 1624.toChar(), 232.toChar(), 8811.toChar(), 8281.toChar(), 1183.toChar(), 169.toChar(), 8802.toChar(), 3013.toChar(), 2186.toChar(), 5579.toChar(), 797.toChar(), 3625.toChar(), 4029.toChar(), + 11109.toChar(), 1587.toChar(), 7249.toChar(), 11569.toChar(), 8675.toChar(), 6506.toChar(), 2685.toChar(), 10917.toChar(), 12093.toChar(), 12261.toChar(), 12285.toChar(), 1755.toChar(), 7273.toChar(), 1039.toChar(), 1904.toChar(), 272.toChar(), + 3550.toChar(), 9285.toChar(), 3082.toChar(), 5707.toChar(), 6082.toChar(), 4380.toChar(), 7648.toChar(), 11626.toChar(), 5172.toChar(), 4250.toChar(), 9385.toChar(), 8363.toChar(), 8217.toChar(), 4685.toChar(), 5936.toChar(), 848.toChar(), + 8899.toChar(), 6538.toChar(), 934.toChar(), 1889.toChar(), 3781.toChar(), 9318.toChar(), 10109.toChar(), 10222.toChar(), 6727.toChar(), 961.toChar(), 5404.toChar(), 772.toChar(), 5377.toChar(), 9546.toChar(), 8386.toChar(), 1198.toChar(), + 8949.toChar(), 3034.toChar(), 2189.toChar(), 7335.toChar(), 4559.toChar(), 5918.toChar(), 2601.toChar(), 10905.toChar(), 5069.toChar(), 9502.toChar(), 3113.toChar(), 7467.toChar(), 8089.toChar(), 11689.toChar(), 5181.toChar(), 9518.toChar(), + 8382.toChar(), 2953.toChar(), 3933.toChar(), 4073.toChar(), 4093.toChar(), 7607.toChar(), 8109.toChar(), 2914.toChar(), 5683.toChar(), 4323.toChar(), 11151.toChar(), 1593.toChar(), 10761.toChar(), 6804.toChar(), 972.toChar(), 3650.toChar(), + 2277.toChar(), 5592.toChar(), 4310.toChar(), 7638.toChar(), 9869.toChar(), 4921.toChar(), 703.toChar(), 1856.toChar(), 9043.toChar(), 4803.toChar(), 9464.toChar(), 1352.toChar(), 8971.toChar(), 11815.toChar(), 5199.toChar(), 7765.toChar(), + 6376.toChar(), 4422.toChar(), 7654.toChar(), 2849.toChar(), 407.toChar(), 8836.toChar(), 6529.toChar(), 7955.toChar(), 2892.toChar(), 9191.toChar(), 1313.toChar(), 10721.toChar(), 12065.toChar(), 12257.toChar(), 1751.toChar(), 9028.toChar(), + 8312.toChar(), 2943.toChar(), 2176.toChar(), 3822.toChar(), 546.toChar(), 78.toChar(), 8789.toChar(), 11789.toChar(), 10462.toChar(), 12028.toChar(), 6985.toChar(), 4509.toChar(), 9422.toChar(), 1346.toChar(), 5459.toChar(), 4291.toChar(), + 613.toChar(), 10621.toChar(), 6784.toChar(), 9747.toChar(), 3148.toChar(), 7472.toChar(), 2823.toChar(), 5670.toChar(), 810.toChar(), 7138.toChar(), 8042.toChar(), 4660.toChar(), 7688.toChar(), 6365.toChar(), 6176.toChar(), 6149.toChar(), + 2634.toChar(), 5643.toChar(), 9584.toChar(), 10147.toChar(), 11983.toChar(), 5223.toChar(), 9524.toChar(), 11894.toChar(), 10477.toChar(), 8519.toChar(), 1217.toChar(), 3685.toChar(), 2282.toChar(), 326.toChar(), 10580.toChar(), 3267.toChar(), + 7489.toChar(), 4581.toChar(), 2410.toChar(), 5611.toChar(), 11335.toChar(), 6886.toChar(), 8006.toChar(), 8166.toChar(), 11700.toChar(), 3427.toChar(), 11023.toChar(), 8597.toChar(), 10006.toChar(), 3185.toChar(), 455.toChar(), 65.toChar(), + 5276.toChar(), 7776.toChar(), 4622.toChar(), 5927.toChar(), 7869.toChar(), 9902.toChar(), 11948.toChar(), 5218.toChar(), 2501.toChar(), 5624.toChar(), 2559.toChar(), 10899.toChar(), 1557.toChar(), 1978.toChar(), 10816.toChar(), 10323.toChar(), + 8497.toChar(), 4725.toChar(), 675.toChar(), 1852.toChar(), 10798.toChar(), 12076.toChar(), 10503.toChar(), 3256.toChar(), 9243.toChar(), 3076.toChar(), 2195.toChar(), 10847.toChar(), 12083.toChar(), 10504.toChar(), 12034.toChar(), 10497.toChar() + ) + private val bitrev_table_combined = intArrayOf( + 524289, 262146, 786435, 131076, 655365, 393222, 917511, 65544, 589833, 327690, 851979, 196620, 720909, 458766, 983055, 32784, + 557073, 294930, 819219, 163860, 688149, 426006, 950295, 98328, 622617, 360474, 884763, 229404, 753693, 491550, 1015839, 540705, + 278562, 802851, 147492, 671781, 409638, 933927, 81960, 606249, 344106, 868395, 213036, 737325, 475182, 999471, 573489, 311346, + 835635, 180276, 704565, 442422, 966711, 114744, 639033, 376890, 901179, 245820, 770109, 507966, 1032255, 532545, 270402, 794691, + 139332, 663621, 401478, 925767, 598089, 335946, 860235, 204876, 729165, 467022, 991311, 565329, 303186, 827475, 172116, 696405, + 434262, 958551, 106584, 630873, 368730, 893019, 237660, 761949, 499806, 1024095, 548961, 286818, 811107, 155748, 680037, 417894, + 942183, 614505, 352362, 876651, 221292, 745581, 483438, 1007727, 581745, 319602, 843891, 188532, 712821, 450678, 974967, 647289, + 385146, 909435, 254076, 778365, 516222, 1040511, 528513, 266370, 790659, 659589, 397446, 921735, 594057, 331914, 856203, 200844, + 725133, 462990, 987279, 561297, 299154, 823443, 168084, 692373, 430230, 954519, 626841, 364698, 888987, 233628, 757917, 495774, + 1020063, 544929, 282786, 807075, 676005, 413862, 938151, 610473, 348330, 872619, 217260, 741549, 479406, 1003695, 577713, 315570, + 839859, 708789, 446646, 970935, 643257, 381114, 905403, 250044, 774333, 512190, 1036479, 536769, 274626, 798915, 667845, 405702, + 929991, 602313, 340170, 864459, 733389, 471246, 995535, 569553, 307410, 831699, 700629, 438486, 962775, 635097, 372954, 897243, + 241884, 766173, 504030, 1028319, 553185, 291042, 815331, 684261, 422118, 946407, 618729, 356586, 880875, 749805, 487662, 1011951, + 585969, 323826, 848115, 717045, 454902, 979191, 651513, 389370, 913659, 782589, 520446, 1044735, 526593, 788739, 657669, 395526, + 919815, 592137, 329994, 854283, 723213, 461070, 985359, 559377, 297234, 821523, 690453, 428310, 952599, 624921, 362778, 887067, + 755997, 493854, 1018143, 543009, 805155, 674085, 411942, 936231, 608553, 346410, 870699, 739629, 477486, 1001775, 575793, 837939, + 706869, 444726, 969015, 641337, 379194, 903483, 772413, 510270, 1034559, 534849, 796995, 665925, 403782, 928071, 600393, 862539, + 731469, 469326, 993615, 567633, 829779, 698709, 436566, 960855, 633177, 371034, 895323, 764253, 502110, 1026399, 551265, 813411, + 682341, 420198, 944487, 616809, 878955, 747885, 485742, 1010031, 584049, 846195, 715125, 452982, 977271, 649593, 911739, 780669, + 518526, 1042815, 530817, 792963, 661893, 924039, 596361, 858507, 727437, 465294, 989583, 563601, 825747, 694677, 432534, 956823, + 629145, 891291, 760221, 498078, 1022367, 547233, 809379, 678309, 940455, 612777, 874923, 743853, 481710, 1005999, 580017, 842163, + 711093, 973239, 645561, 907707, 776637, 514494, 1038783, 539073, 801219, 670149, 932295, 604617, 866763, 735693, 997839, 571857, + 834003, 702933, 965079, 637401, 899547, 768477, 506334, 1030623, 555489, 817635, 686565, 948711, 621033, 883179, 752109, 1014255, + 588273, 850419, 719349, 981495, 653817, 915963, 784893, 1047039, 787971, 656901, 919047, 591369, 853515, 722445, 984591, 558609, + 820755, 689685, 951831, 624153, 886299, 755229, 1017375, 804387, 673317, 935463, 607785, 869931, 738861, 1001007, 837171, 706101, + 968247, 640569, 902715, 771645, 1033791, 796227, 665157, 927303, 861771, 730701, 992847, 829011, 697941, 960087, 632409, 894555, + 763485, 1025631, 812643, 681573, 943719, 878187, 747117, 1009263, 845427, 714357, 976503, 910971, 779901, 1042047, 792195, 923271, + 857739, 726669, 988815, 824979, 693909, 956055, 890523, 759453, 1021599, 808611, 939687, 874155, 743085, 1005231, 841395, 972471, + 906939, 775869, 1038015, 800451, 931527, 865995, 997071, 833235, 964311, 898779, 767709, 1029855, 816867, 947943, 882411, 1013487, + 849651, 980727, 915195, 1046271, 921351, 855819, 986895, 823059, 954135, 888603, 1019679, 937767, 872235, 1003311, 970551, 905019, + 1036095, 929607, 995151, 962391, 896859, 1027935, 946023, 1011567, 978807, 1044351, 991119, 958359, 1023903, 1007535, 1040319, 1032159 + ) + } } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHopeTor.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHopeTor.kt index 45c6c774..2bd7a907 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHopeTor.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/NewHopeTor.kt @@ -5,978 +5,142 @@ * Original authors: Erdem Alkim, Léo Ducas, Thomas Pöppelmann, Peter Schwabe * Java port: Rhys Weatherley */ - -package com.bitchat.android.noise.southernstorm.crypto; - -import java.util.Arrays; +package com.bitchat.android.noise.southernstorm.crypto /** * New Hope key exchange algorithm, "torref" variant. * * This version of New Hope implements the alternative constant-time * method for generating the public "a" value for anonymity networks - * like Tor. It is not binary-compatible with the standard New Hope - * implementation in the NewHope class. - * - * Reference: https://cryptojedi.org/papers/newhope-20160803.pdf - * - * @see NewHope + * like Tor. */ -public class NewHopeTor extends NewHope { +open class NewHopeTor : NewHope() { - public NewHopeTor() {} + override fun uniform(coeffs: CharArray, seed: ByteArray) { + val state = LongArray(25) + val nblocks = 16 + val buf = ByteArray(SHAKE128_RATE * nblocks) + val x = CharArray(buf.size / 2) - @Override - protected void uniform(char[] coeffs, byte[] seed) - { - long[] state = new long [25]; - int nblocks=16; - byte[] buf = new byte [SHAKE128_RATE*nblocks]; - char[] x = new char [buf.length / 2]; + try { + shake128_absorb(state, seed, 0, SEEDBYTES) + do { + shake128_squeezeblocks(buf, 0, nblocks, state) + for (i in 0 until buf.size step 2) { + x[i / 2] = ((buf[i].toInt() and 0xff) or ((buf[i + 1].toInt() and 0xff) shl 8)).toChar() + } + } while (discardtopoly(coeffs, x)) + } finally { + state.fill(0L) + buf.fill(0) + x.fill(0.toChar()) + } + } - try { - shake128_absorb(state, seed, 0, SEEDBYTES); - do - { - shake128_squeezeblocks(buf, 0, nblocks, state); - for (int i = buf.length - 2; i >= 0; i -= 2) - { - x[i / 2] = (char)((buf[i] & 0xff) | ((buf[i+1] & 0xff) << 8)); - } - } - while (discardtopoly(coeffs, x)); - } finally { - Arrays.fill(state, 0); - Arrays.fill(buf, (byte)0); - Arrays.fill(x, (char)0); - } - } + companion object { + private fun discardtopoly(coeffs: CharArray, x: CharArray): Boolean { + for (i in 0 until 16) { + batcher84(x, i) + } - private static boolean discardtopoly(char[] coeffs, char[] x) - { - int i, r=0; + // Check whether we're safe + var r = 0 + for (i in 1008 until 1024) { + r = r or (61444 - x[i].code) + } + if ((r shr 31) != 0) return true - for(i=0;i<16;i++) - batcher84(x, i); + // If we are, copy coefficients to polynomial + x.copyInto(coeffs, destinationOffset = 0, startIndex = 0, endIndex = PARAM_N) + return false + } - // Check whether we're safe: - for(i=1008;i<1024;i++) - r |= 61444 - x[i]; - if((r >>= 31) != 0) return true; + private fun batcher84(x: CharArray, offset: Int) { + fun cmpSwap(idx1: Int, idx2: Int) { + val off1 = offset + idx1 + val off2 = offset + idx2 + val c = 61444 - x[off1].code + val t = (x[off1].code xor x[off2].code) and (c shr 31) + x[off1] = (x[off1].code xor t).toChar() + x[off2] = (x[off2].code xor t).toChar() + } - // If we are, copy coefficients to polynomial: - for(i=0;i> 31); x[offset + 0] ^= t; x[offset + 16] ^= t; - c = 61444 - x[offset + 32]; t = (x[offset + 32] ^ x[offset + 48]) & (c >> 31); x[offset + 32] ^= t; x[offset + 48] ^= t; - c = 61444 - x[offset + 0]; t = (x[offset + 0] ^ x[offset + 32]) & (c >> 31); x[offset + 0] ^= t; x[offset + 32] ^= t; - c = 61444 - x[offset + 16]; t = (x[offset + 16] ^ x[offset + 48]) & (c >> 31); x[offset + 16] ^= t; x[offset + 48] ^= t; - c = 61444 - x[offset + 16]; t = (x[offset + 16] ^ x[offset + 32]) & (c >> 31); x[offset + 16] ^= t; x[offset + 32] ^= t; - c = 61444 - x[offset + 64]; t = (x[offset + 64] ^ x[offset + 80]) & (c >> 31); x[offset + 64] ^= t; x[offset + 80] ^= t; - c = 61444 - x[offset + 96]; t = (x[offset + 96] ^ x[offset + 112]) & (c >> 31); x[offset + 96] ^= t; x[offset + 112] ^= t; - c = 61444 - x[offset + 64]; t = (x[offset + 64] ^ x[offset + 96]) & (c >> 31); x[offset + 64] ^= t; x[offset + 96] ^= t; - c = 61444 - x[offset + 80]; t = (x[offset + 80] ^ x[offset + 112]) & (c >> 31); x[offset + 80] ^= t; x[offset + 112] ^= t; - c = 61444 - x[offset + 80]; t = (x[offset + 80] ^ x[offset + 96]) & (c >> 31); x[offset + 80] ^= t; x[offset + 96] ^= t; - c = 61444 - x[offset + 0]; t = (x[offset + 0] ^ x[offset + 64]) & (c >> 31); x[offset + 0] ^= t; x[offset + 64] ^= t; - c = 61444 - x[offset + 32]; t = (x[offset + 32] ^ x[offset + 96]) & (c >> 31); x[offset + 32] ^= t; x[offset + 96] ^= t; - c = 61444 - x[offset + 32]; t = (x[offset + 32] ^ x[offset + 64]) & (c >> 31); x[offset + 32] ^= t; x[offset + 64] ^= t; - c = 61444 - x[offset + 16]; t = (x[offset + 16] ^ x[offset + 80]) & (c >> 31); x[offset + 16] ^= t; x[offset + 80] ^= t; - c = 61444 - x[offset + 48]; t = (x[offset + 48] ^ x[offset + 112]) & (c >> 31); x[offset + 48] ^= t; x[offset + 112] ^= t; - c = 61444 - x[offset + 48]; t = (x[offset + 48] ^ x[offset + 80]) & (c >> 31); x[offset + 48] ^= t; x[offset + 80] ^= t; - c = 61444 - x[offset + 16]; t = (x[offset + 16] ^ x[offset + 32]) & (c >> 31); x[offset + 16] ^= t; x[offset + 32] ^= t; - c = 61444 - x[offset + 48]; t = (x[offset + 48] ^ x[offset + 64]) & (c >> 31); x[offset + 48] ^= t; x[offset + 64] ^= t; - c = 61444 - x[offset + 80]; t = (x[offset + 80] ^ x[offset + 96]) & (c >> 31); x[offset + 80] ^= t; x[offset + 96] ^= t; - c = 61444 - x[offset + 128]; t = (x[offset + 128] ^ x[offset + 144]) & (c >> 31); x[offset + 128] ^= t; x[offset + 144] ^= t; - c = 61444 - x[offset + 160]; t = (x[offset + 160] ^ x[offset + 176]) & (c >> 31); x[offset + 160] ^= t; x[offset + 176] ^= t; - c = 61444 - x[offset + 128]; t = (x[offset + 128] ^ x[offset + 160]) & (c >> 31); x[offset + 128] ^= t; x[offset + 160] ^= t; - c = 61444 - x[offset + 144]; t = (x[offset + 144] ^ x[offset + 176]) & (c >> 31); x[offset + 144] ^= t; x[offset + 176] ^= t; - c = 61444 - x[offset + 144]; t = (x[offset + 144] ^ x[offset + 160]) & (c >> 31); x[offset + 144] ^= t; x[offset + 160] ^= t; - c = 61444 - x[offset + 192]; t = (x[offset + 192] ^ x[offset + 208]) & (c >> 31); x[offset + 192] ^= t; x[offset + 208] ^= t; - c = 61444 - x[offset + 224]; t = (x[offset + 224] ^ x[offset + 240]) & (c >> 31); x[offset + 224] ^= t; x[offset + 240] ^= t; - c = 61444 - x[offset + 192]; t = (x[offset + 192] ^ x[offset + 224]) & (c >> 31); x[offset + 192] ^= t; x[offset + 224] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 240]) & (c >> 31); x[offset + 208] ^= t; x[offset + 240] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 224]) & (c >> 31); x[offset + 208] ^= t; x[offset + 224] ^= t; - c = 61444 - x[offset + 128]; t = (x[offset + 128] ^ x[offset + 192]) & (c >> 31); x[offset + 128] ^= t; x[offset + 192] ^= t; - c = 61444 - x[offset + 160]; t = (x[offset + 160] ^ x[offset + 224]) & (c >> 31); x[offset + 160] ^= t; x[offset + 224] ^= t; - c = 61444 - x[offset + 160]; t = (x[offset + 160] ^ x[offset + 192]) & (c >> 31); x[offset + 160] ^= t; x[offset + 192] ^= t; - c = 61444 - x[offset + 144]; t = (x[offset + 144] ^ x[offset + 208]) & (c >> 31); x[offset + 144] ^= t; x[offset + 208] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 240]) & (c >> 31); x[offset + 176] ^= t; x[offset + 240] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 208]) & (c >> 31); x[offset + 176] ^= t; x[offset + 208] ^= t; - c = 61444 - x[offset + 144]; t = (x[offset + 144] ^ x[offset + 160]) & (c >> 31); x[offset + 144] ^= t; x[offset + 160] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 192]) & (c >> 31); x[offset + 176] ^= t; x[offset + 192] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 224]) & (c >> 31); x[offset + 208] ^= t; x[offset + 224] ^= t; - c = 61444 - x[offset + 0]; t = (x[offset + 0] ^ x[offset + 128]) & (c >> 31); x[offset + 0] ^= t; x[offset + 128] ^= t; - c = 61444 - x[offset + 64]; t = (x[offset + 64] ^ x[offset + 192]) & (c >> 31); x[offset + 64] ^= t; x[offset + 192] ^= t; - c = 61444 - x[offset + 64]; t = (x[offset + 64] ^ x[offset + 128]) & (c >> 31); x[offset + 64] ^= t; x[offset + 128] ^= t; - c = 61444 - x[offset + 32]; t = (x[offset + 32] ^ x[offset + 160]) & (c >> 31); x[offset + 32] ^= t; x[offset + 160] ^= t; - c = 61444 - x[offset + 96]; t = (x[offset + 96] ^ x[offset + 224]) & (c >> 31); x[offset + 96] ^= t; x[offset + 224] ^= t; - c = 61444 - x[offset + 96]; t = (x[offset + 96] ^ x[offset + 160]) & (c >> 31); x[offset + 96] ^= t; x[offset + 160] ^= t; - c = 61444 - x[offset + 32]; t = (x[offset + 32] ^ x[offset + 64]) & (c >> 31); x[offset + 32] ^= t; x[offset + 64] ^= t; - c = 61444 - x[offset + 96]; t = (x[offset + 96] ^ x[offset + 128]) & (c >> 31); x[offset + 96] ^= t; x[offset + 128] ^= t; - c = 61444 - x[offset + 160]; t = (x[offset + 160] ^ x[offset + 192]) & (c >> 31); x[offset + 160] ^= t; x[offset + 192] ^= t; - c = 61444 - x[offset + 16]; t = (x[offset + 16] ^ x[offset + 144]) & (c >> 31); x[offset + 16] ^= t; x[offset + 144] ^= t; - c = 61444 - x[offset + 80]; t = (x[offset + 80] ^ x[offset + 208]) & (c >> 31); x[offset + 80] ^= t; x[offset + 208] ^= t; - c = 61444 - x[offset + 80]; t = (x[offset + 80] ^ x[offset + 144]) & (c >> 31); x[offset + 80] ^= t; x[offset + 144] ^= t; - c = 61444 - x[offset + 48]; t = (x[offset + 48] ^ x[offset + 176]) & (c >> 31); x[offset + 48] ^= t; x[offset + 176] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 240]) & (c >> 31); x[offset + 112] ^= t; x[offset + 240] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 176]) & (c >> 31); x[offset + 112] ^= t; x[offset + 176] ^= t; - c = 61444 - x[offset + 48]; t = (x[offset + 48] ^ x[offset + 80]) & (c >> 31); x[offset + 48] ^= t; x[offset + 80] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 144]) & (c >> 31); x[offset + 112] ^= t; x[offset + 144] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 208]) & (c >> 31); x[offset + 176] ^= t; x[offset + 208] ^= t; - c = 61444 - x[offset + 16]; t = (x[offset + 16] ^ x[offset + 32]) & (c >> 31); x[offset + 16] ^= t; x[offset + 32] ^= t; - c = 61444 - x[offset + 48]; t = (x[offset + 48] ^ x[offset + 64]) & (c >> 31); x[offset + 48] ^= t; x[offset + 64] ^= t; - c = 61444 - x[offset + 80]; t = (x[offset + 80] ^ x[offset + 96]) & (c >> 31); x[offset + 80] ^= t; x[offset + 96] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 128]) & (c >> 31); x[offset + 112] ^= t; x[offset + 128] ^= t; - c = 61444 - x[offset + 144]; t = (x[offset + 144] ^ x[offset + 160]) & (c >> 31); x[offset + 144] ^= t; x[offset + 160] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 192]) & (c >> 31); x[offset + 176] ^= t; x[offset + 192] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 224]) & (c >> 31); x[offset + 208] ^= t; x[offset + 224] ^= t; - c = 61444 - x[offset + 256]; t = (x[offset + 256] ^ x[offset + 272]) & (c >> 31); x[offset + 256] ^= t; x[offset + 272] ^= t; - c = 61444 - x[offset + 288]; t = (x[offset + 288] ^ x[offset + 304]) & (c >> 31); x[offset + 288] ^= t; x[offset + 304] ^= t; - c = 61444 - x[offset + 256]; t = (x[offset + 256] ^ x[offset + 288]) & (c >> 31); x[offset + 256] ^= t; x[offset + 288] ^= t; - c = 61444 - x[offset + 272]; t = (x[offset + 272] ^ x[offset + 304]) & (c >> 31); x[offset + 272] ^= t; x[offset + 304] ^= t; - c = 61444 - x[offset + 272]; t = (x[offset + 272] ^ x[offset + 288]) & (c >> 31); x[offset + 272] ^= t; x[offset + 288] ^= t; - c = 61444 - x[offset + 320]; t = (x[offset + 320] ^ x[offset + 336]) & (c >> 31); x[offset + 320] ^= t; x[offset + 336] ^= t; - c = 61444 - x[offset + 352]; t = (x[offset + 352] ^ x[offset + 368]) & (c >> 31); x[offset + 352] ^= t; x[offset + 368] ^= t; - c = 61444 - x[offset + 320]; t = (x[offset + 320] ^ x[offset + 352]) & (c >> 31); x[offset + 320] ^= t; x[offset + 352] ^= t; - c = 61444 - x[offset + 336]; t = (x[offset + 336] ^ x[offset + 368]) & (c >> 31); x[offset + 336] ^= t; x[offset + 368] ^= t; - c = 61444 - x[offset + 336]; t = (x[offset + 336] ^ x[offset + 352]) & (c >> 31); x[offset + 336] ^= t; x[offset + 352] ^= t; - c = 61444 - x[offset + 256]; t = (x[offset + 256] ^ x[offset + 320]) & (c >> 31); x[offset + 256] ^= t; x[offset + 320] ^= t; - c = 61444 - x[offset + 288]; t = (x[offset + 288] ^ x[offset + 352]) & (c >> 31); x[offset + 288] ^= t; x[offset + 352] ^= t; - c = 61444 - x[offset + 288]; t = (x[offset + 288] ^ x[offset + 320]) & (c >> 31); x[offset + 288] ^= t; x[offset + 320] ^= t; - c = 61444 - x[offset + 272]; t = (x[offset + 272] ^ x[offset + 336]) & (c >> 31); x[offset + 272] ^= t; x[offset + 336] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 368]) & (c >> 31); x[offset + 304] ^= t; x[offset + 368] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 336]) & (c >> 31); x[offset + 304] ^= t; x[offset + 336] ^= t; - c = 61444 - x[offset + 272]; t = (x[offset + 272] ^ x[offset + 288]) & (c >> 31); x[offset + 272] ^= t; x[offset + 288] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 320]) & (c >> 31); x[offset + 304] ^= t; x[offset + 320] ^= t; - c = 61444 - x[offset + 336]; t = (x[offset + 336] ^ x[offset + 352]) & (c >> 31); x[offset + 336] ^= t; x[offset + 352] ^= t; - c = 61444 - x[offset + 384]; t = (x[offset + 384] ^ x[offset + 400]) & (c >> 31); x[offset + 384] ^= t; x[offset + 400] ^= t; - c = 61444 - x[offset + 416]; t = (x[offset + 416] ^ x[offset + 432]) & (c >> 31); x[offset + 416] ^= t; x[offset + 432] ^= t; - c = 61444 - x[offset + 384]; t = (x[offset + 384] ^ x[offset + 416]) & (c >> 31); x[offset + 384] ^= t; x[offset + 416] ^= t; - c = 61444 - x[offset + 400]; t = (x[offset + 400] ^ x[offset + 432]) & (c >> 31); x[offset + 400] ^= t; x[offset + 432] ^= t; - c = 61444 - x[offset + 400]; t = (x[offset + 400] ^ x[offset + 416]) & (c >> 31); x[offset + 400] ^= t; x[offset + 416] ^= t; - c = 61444 - x[offset + 448]; t = (x[offset + 448] ^ x[offset + 464]) & (c >> 31); x[offset + 448] ^= t; x[offset + 464] ^= t; - c = 61444 - x[offset + 480]; t = (x[offset + 480] ^ x[offset + 496]) & (c >> 31); x[offset + 480] ^= t; x[offset + 496] ^= t; - c = 61444 - x[offset + 448]; t = (x[offset + 448] ^ x[offset + 480]) & (c >> 31); x[offset + 448] ^= t; x[offset + 480] ^= t; - c = 61444 - x[offset + 464]; t = (x[offset + 464] ^ x[offset + 496]) & (c >> 31); x[offset + 464] ^= t; x[offset + 496] ^= t; - c = 61444 - x[offset + 464]; t = (x[offset + 464] ^ x[offset + 480]) & (c >> 31); x[offset + 464] ^= t; x[offset + 480] ^= t; - c = 61444 - x[offset + 384]; t = (x[offset + 384] ^ x[offset + 448]) & (c >> 31); x[offset + 384] ^= t; x[offset + 448] ^= t; - c = 61444 - x[offset + 416]; t = (x[offset + 416] ^ x[offset + 480]) & (c >> 31); x[offset + 416] ^= t; x[offset + 480] ^= t; - c = 61444 - x[offset + 416]; t = (x[offset + 416] ^ x[offset + 448]) & (c >> 31); x[offset + 416] ^= t; x[offset + 448] ^= t; - c = 61444 - x[offset + 400]; t = (x[offset + 400] ^ x[offset + 464]) & (c >> 31); x[offset + 400] ^= t; x[offset + 464] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 496]) & (c >> 31); x[offset + 432] ^= t; x[offset + 496] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 464]) & (c >> 31); x[offset + 432] ^= t; x[offset + 464] ^= t; - c = 61444 - x[offset + 400]; t = (x[offset + 400] ^ x[offset + 416]) & (c >> 31); x[offset + 400] ^= t; x[offset + 416] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 448]) & (c >> 31); x[offset + 432] ^= t; x[offset + 448] ^= t; - c = 61444 - x[offset + 464]; t = (x[offset + 464] ^ x[offset + 480]) & (c >> 31); x[offset + 464] ^= t; x[offset + 480] ^= t; - c = 61444 - x[offset + 256]; t = (x[offset + 256] ^ x[offset + 384]) & (c >> 31); x[offset + 256] ^= t; x[offset + 384] ^= t; - c = 61444 - x[offset + 320]; t = (x[offset + 320] ^ x[offset + 448]) & (c >> 31); x[offset + 320] ^= t; x[offset + 448] ^= t; - c = 61444 - x[offset + 320]; t = (x[offset + 320] ^ x[offset + 384]) & (c >> 31); x[offset + 320] ^= t; x[offset + 384] ^= t; - c = 61444 - x[offset + 288]; t = (x[offset + 288] ^ x[offset + 416]) & (c >> 31); x[offset + 288] ^= t; x[offset + 416] ^= t; - c = 61444 - x[offset + 352]; t = (x[offset + 352] ^ x[offset + 480]) & (c >> 31); x[offset + 352] ^= t; x[offset + 480] ^= t; - c = 61444 - x[offset + 352]; t = (x[offset + 352] ^ x[offset + 416]) & (c >> 31); x[offset + 352] ^= t; x[offset + 416] ^= t; - c = 61444 - x[offset + 288]; t = (x[offset + 288] ^ x[offset + 320]) & (c >> 31); x[offset + 288] ^= t; x[offset + 320] ^= t; - c = 61444 - x[offset + 352]; t = (x[offset + 352] ^ x[offset + 384]) & (c >> 31); x[offset + 352] ^= t; x[offset + 384] ^= t; - c = 61444 - x[offset + 416]; t = (x[offset + 416] ^ x[offset + 448]) & (c >> 31); x[offset + 416] ^= t; x[offset + 448] ^= t; - c = 61444 - x[offset + 272]; t = (x[offset + 272] ^ x[offset + 400]) & (c >> 31); x[offset + 272] ^= t; x[offset + 400] ^= t; - c = 61444 - x[offset + 336]; t = (x[offset + 336] ^ x[offset + 464]) & (c >> 31); x[offset + 336] ^= t; x[offset + 464] ^= t; - c = 61444 - x[offset + 336]; t = (x[offset + 336] ^ x[offset + 400]) & (c >> 31); x[offset + 336] ^= t; x[offset + 400] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 432]) & (c >> 31); x[offset + 304] ^= t; x[offset + 432] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 496]) & (c >> 31); x[offset + 368] ^= t; x[offset + 496] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 432]) & (c >> 31); x[offset + 368] ^= t; x[offset + 432] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 336]) & (c >> 31); x[offset + 304] ^= t; x[offset + 336] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 400]) & (c >> 31); x[offset + 368] ^= t; x[offset + 400] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 464]) & (c >> 31); x[offset + 432] ^= t; x[offset + 464] ^= t; - c = 61444 - x[offset + 272]; t = (x[offset + 272] ^ x[offset + 288]) & (c >> 31); x[offset + 272] ^= t; x[offset + 288] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 320]) & (c >> 31); x[offset + 304] ^= t; x[offset + 320] ^= t; - c = 61444 - x[offset + 336]; t = (x[offset + 336] ^ x[offset + 352]) & (c >> 31); x[offset + 336] ^= t; x[offset + 352] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 384]) & (c >> 31); x[offset + 368] ^= t; x[offset + 384] ^= t; - c = 61444 - x[offset + 400]; t = (x[offset + 400] ^ x[offset + 416]) & (c >> 31); x[offset + 400] ^= t; x[offset + 416] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 448]) & (c >> 31); x[offset + 432] ^= t; x[offset + 448] ^= t; - c = 61444 - x[offset + 464]; t = (x[offset + 464] ^ x[offset + 480]) & (c >> 31); x[offset + 464] ^= t; x[offset + 480] ^= t; - c = 61444 - x[offset + 0]; t = (x[offset + 0] ^ x[offset + 256]) & (c >> 31); x[offset + 0] ^= t; x[offset + 256] ^= t; - c = 61444 - x[offset + 128]; t = (x[offset + 128] ^ x[offset + 384]) & (c >> 31); x[offset + 128] ^= t; x[offset + 384] ^= t; - c = 61444 - x[offset + 128]; t = (x[offset + 128] ^ x[offset + 256]) & (c >> 31); x[offset + 128] ^= t; x[offset + 256] ^= t; - c = 61444 - x[offset + 64]; t = (x[offset + 64] ^ x[offset + 320]) & (c >> 31); x[offset + 64] ^= t; x[offset + 320] ^= t; - c = 61444 - x[offset + 192]; t = (x[offset + 192] ^ x[offset + 448]) & (c >> 31); x[offset + 192] ^= t; x[offset + 448] ^= t; - c = 61444 - x[offset + 192]; t = (x[offset + 192] ^ x[offset + 320]) & (c >> 31); x[offset + 192] ^= t; x[offset + 320] ^= t; - c = 61444 - x[offset + 64]; t = (x[offset + 64] ^ x[offset + 128]) & (c >> 31); x[offset + 64] ^= t; x[offset + 128] ^= t; - c = 61444 - x[offset + 192]; t = (x[offset + 192] ^ x[offset + 256]) & (c >> 31); x[offset + 192] ^= t; x[offset + 256] ^= t; - c = 61444 - x[offset + 320]; t = (x[offset + 320] ^ x[offset + 384]) & (c >> 31); x[offset + 320] ^= t; x[offset + 384] ^= t; - c = 61444 - x[offset + 32]; t = (x[offset + 32] ^ x[offset + 288]) & (c >> 31); x[offset + 32] ^= t; x[offset + 288] ^= t; - c = 61444 - x[offset + 160]; t = (x[offset + 160] ^ x[offset + 416]) & (c >> 31); x[offset + 160] ^= t; x[offset + 416] ^= t; - c = 61444 - x[offset + 160]; t = (x[offset + 160] ^ x[offset + 288]) & (c >> 31); x[offset + 160] ^= t; x[offset + 288] ^= t; - c = 61444 - x[offset + 96]; t = (x[offset + 96] ^ x[offset + 352]) & (c >> 31); x[offset + 96] ^= t; x[offset + 352] ^= t; - c = 61444 - x[offset + 224]; t = (x[offset + 224] ^ x[offset + 480]) & (c >> 31); x[offset + 224] ^= t; x[offset + 480] ^= t; - c = 61444 - x[offset + 224]; t = (x[offset + 224] ^ x[offset + 352]) & (c >> 31); x[offset + 224] ^= t; x[offset + 352] ^= t; - c = 61444 - x[offset + 96]; t = (x[offset + 96] ^ x[offset + 160]) & (c >> 31); x[offset + 96] ^= t; x[offset + 160] ^= t; - c = 61444 - x[offset + 224]; t = (x[offset + 224] ^ x[offset + 288]) & (c >> 31); x[offset + 224] ^= t; x[offset + 288] ^= t; - c = 61444 - x[offset + 352]; t = (x[offset + 352] ^ x[offset + 416]) & (c >> 31); x[offset + 352] ^= t; x[offset + 416] ^= t; - c = 61444 - x[offset + 32]; t = (x[offset + 32] ^ x[offset + 64]) & (c >> 31); x[offset + 32] ^= t; x[offset + 64] ^= t; - c = 61444 - x[offset + 96]; t = (x[offset + 96] ^ x[offset + 128]) & (c >> 31); x[offset + 96] ^= t; x[offset + 128] ^= t; - c = 61444 - x[offset + 160]; t = (x[offset + 160] ^ x[offset + 192]) & (c >> 31); x[offset + 160] ^= t; x[offset + 192] ^= t; - c = 61444 - x[offset + 224]; t = (x[offset + 224] ^ x[offset + 256]) & (c >> 31); x[offset + 224] ^= t; x[offset + 256] ^= t; - c = 61444 - x[offset + 288]; t = (x[offset + 288] ^ x[offset + 320]) & (c >> 31); x[offset + 288] ^= t; x[offset + 320] ^= t; - c = 61444 - x[offset + 352]; t = (x[offset + 352] ^ x[offset + 384]) & (c >> 31); x[offset + 352] ^= t; x[offset + 384] ^= t; - c = 61444 - x[offset + 416]; t = (x[offset + 416] ^ x[offset + 448]) & (c >> 31); x[offset + 416] ^= t; x[offset + 448] ^= t; - c = 61444 - x[offset + 16]; t = (x[offset + 16] ^ x[offset + 272]) & (c >> 31); x[offset + 16] ^= t; x[offset + 272] ^= t; - c = 61444 - x[offset + 144]; t = (x[offset + 144] ^ x[offset + 400]) & (c >> 31); x[offset + 144] ^= t; x[offset + 400] ^= t; - c = 61444 - x[offset + 144]; t = (x[offset + 144] ^ x[offset + 272]) & (c >> 31); x[offset + 144] ^= t; x[offset + 272] ^= t; - c = 61444 - x[offset + 80]; t = (x[offset + 80] ^ x[offset + 336]) & (c >> 31); x[offset + 80] ^= t; x[offset + 336] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 464]) & (c >> 31); x[offset + 208] ^= t; x[offset + 464] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 336]) & (c >> 31); x[offset + 208] ^= t; x[offset + 336] ^= t; - c = 61444 - x[offset + 80]; t = (x[offset + 80] ^ x[offset + 144]) & (c >> 31); x[offset + 80] ^= t; x[offset + 144] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 272]) & (c >> 31); x[offset + 208] ^= t; x[offset + 272] ^= t; - c = 61444 - x[offset + 336]; t = (x[offset + 336] ^ x[offset + 400]) & (c >> 31); x[offset + 336] ^= t; x[offset + 400] ^= t; - c = 61444 - x[offset + 48]; t = (x[offset + 48] ^ x[offset + 304]) & (c >> 31); x[offset + 48] ^= t; x[offset + 304] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 432]) & (c >> 31); x[offset + 176] ^= t; x[offset + 432] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 304]) & (c >> 31); x[offset + 176] ^= t; x[offset + 304] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 368]) & (c >> 31); x[offset + 112] ^= t; x[offset + 368] ^= t; - c = 61444 - x[offset + 240]; t = (x[offset + 240] ^ x[offset + 496]) & (c >> 31); x[offset + 240] ^= t; x[offset + 496] ^= t; - c = 61444 - x[offset + 240]; t = (x[offset + 240] ^ x[offset + 368]) & (c >> 31); x[offset + 240] ^= t; x[offset + 368] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 176]) & (c >> 31); x[offset + 112] ^= t; x[offset + 176] ^= t; - c = 61444 - x[offset + 240]; t = (x[offset + 240] ^ x[offset + 304]) & (c >> 31); x[offset + 240] ^= t; x[offset + 304] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 432]) & (c >> 31); x[offset + 368] ^= t; x[offset + 432] ^= t; - c = 61444 - x[offset + 48]; t = (x[offset + 48] ^ x[offset + 80]) & (c >> 31); x[offset + 48] ^= t; x[offset + 80] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 144]) & (c >> 31); x[offset + 112] ^= t; x[offset + 144] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 208]) & (c >> 31); x[offset + 176] ^= t; x[offset + 208] ^= t; - c = 61444 - x[offset + 240]; t = (x[offset + 240] ^ x[offset + 272]) & (c >> 31); x[offset + 240] ^= t; x[offset + 272] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 336]) & (c >> 31); x[offset + 304] ^= t; x[offset + 336] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 400]) & (c >> 31); x[offset + 368] ^= t; x[offset + 400] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 464]) & (c >> 31); x[offset + 432] ^= t; x[offset + 464] ^= t; - c = 61444 - x[offset + 16]; t = (x[offset + 16] ^ x[offset + 32]) & (c >> 31); x[offset + 16] ^= t; x[offset + 32] ^= t; - c = 61444 - x[offset + 48]; t = (x[offset + 48] ^ x[offset + 64]) & (c >> 31); x[offset + 48] ^= t; x[offset + 64] ^= t; - c = 61444 - x[offset + 80]; t = (x[offset + 80] ^ x[offset + 96]) & (c >> 31); x[offset + 80] ^= t; x[offset + 96] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 128]) & (c >> 31); x[offset + 112] ^= t; x[offset + 128] ^= t; - c = 61444 - x[offset + 144]; t = (x[offset + 144] ^ x[offset + 160]) & (c >> 31); x[offset + 144] ^= t; x[offset + 160] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 192]) & (c >> 31); x[offset + 176] ^= t; x[offset + 192] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 224]) & (c >> 31); x[offset + 208] ^= t; x[offset + 224] ^= t; - c = 61444 - x[offset + 240]; t = (x[offset + 240] ^ x[offset + 256]) & (c >> 31); x[offset + 240] ^= t; x[offset + 256] ^= t; - c = 61444 - x[offset + 272]; t = (x[offset + 272] ^ x[offset + 288]) & (c >> 31); x[offset + 272] ^= t; x[offset + 288] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 320]) & (c >> 31); x[offset + 304] ^= t; x[offset + 320] ^= t; - c = 61444 - x[offset + 336]; t = (x[offset + 336] ^ x[offset + 352]) & (c >> 31); x[offset + 336] ^= t; x[offset + 352] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 384]) & (c >> 31); x[offset + 368] ^= t; x[offset + 384] ^= t; - c = 61444 - x[offset + 400]; t = (x[offset + 400] ^ x[offset + 416]) & (c >> 31); x[offset + 400] ^= t; x[offset + 416] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 448]) & (c >> 31); x[offset + 432] ^= t; x[offset + 448] ^= t; - c = 61444 - x[offset + 464]; t = (x[offset + 464] ^ x[offset + 480]) & (c >> 31); x[offset + 464] ^= t; x[offset + 480] ^= t; - c = 61444 - x[offset + 512]; t = (x[offset + 512] ^ x[offset + 528]) & (c >> 31); x[offset + 512] ^= t; x[offset + 528] ^= t; - c = 61444 - x[offset + 544]; t = (x[offset + 544] ^ x[offset + 560]) & (c >> 31); x[offset + 544] ^= t; x[offset + 560] ^= t; - c = 61444 - x[offset + 512]; t = (x[offset + 512] ^ x[offset + 544]) & (c >> 31); x[offset + 512] ^= t; x[offset + 544] ^= t; - c = 61444 - x[offset + 528]; t = (x[offset + 528] ^ x[offset + 560]) & (c >> 31); x[offset + 528] ^= t; x[offset + 560] ^= t; - c = 61444 - x[offset + 528]; t = (x[offset + 528] ^ x[offset + 544]) & (c >> 31); x[offset + 528] ^= t; x[offset + 544] ^= t; - c = 61444 - x[offset + 576]; t = (x[offset + 576] ^ x[offset + 592]) & (c >> 31); x[offset + 576] ^= t; x[offset + 592] ^= t; - c = 61444 - x[offset + 608]; t = (x[offset + 608] ^ x[offset + 624]) & (c >> 31); x[offset + 608] ^= t; x[offset + 624] ^= t; - c = 61444 - x[offset + 576]; t = (x[offset + 576] ^ x[offset + 608]) & (c >> 31); x[offset + 576] ^= t; x[offset + 608] ^= t; - c = 61444 - x[offset + 592]; t = (x[offset + 592] ^ x[offset + 624]) & (c >> 31); x[offset + 592] ^= t; x[offset + 624] ^= t; - c = 61444 - x[offset + 592]; t = (x[offset + 592] ^ x[offset + 608]) & (c >> 31); x[offset + 592] ^= t; x[offset + 608] ^= t; - c = 61444 - x[offset + 512]; t = (x[offset + 512] ^ x[offset + 576]) & (c >> 31); x[offset + 512] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 544]; t = (x[offset + 544] ^ x[offset + 608]) & (c >> 31); x[offset + 544] ^= t; x[offset + 608] ^= t; - c = 61444 - x[offset + 544]; t = (x[offset + 544] ^ x[offset + 576]) & (c >> 31); x[offset + 544] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 528]; t = (x[offset + 528] ^ x[offset + 592]) & (c >> 31); x[offset + 528] ^= t; x[offset + 592] ^= t; - c = 61444 - x[offset + 560]; t = (x[offset + 560] ^ x[offset + 624]) & (c >> 31); x[offset + 560] ^= t; x[offset + 624] ^= t; - c = 61444 - x[offset + 560]; t = (x[offset + 560] ^ x[offset + 592]) & (c >> 31); x[offset + 560] ^= t; x[offset + 592] ^= t; - c = 61444 - x[offset + 528]; t = (x[offset + 528] ^ x[offset + 544]) & (c >> 31); x[offset + 528] ^= t; x[offset + 544] ^= t; - c = 61444 - x[offset + 560]; t = (x[offset + 560] ^ x[offset + 576]) & (c >> 31); x[offset + 560] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 592]; t = (x[offset + 592] ^ x[offset + 608]) & (c >> 31); x[offset + 592] ^= t; x[offset + 608] ^= t; - c = 61444 - x[offset + 640]; t = (x[offset + 640] ^ x[offset + 656]) & (c >> 31); x[offset + 640] ^= t; x[offset + 656] ^= t; - c = 61444 - x[offset + 672]; t = (x[offset + 672] ^ x[offset + 688]) & (c >> 31); x[offset + 672] ^= t; x[offset + 688] ^= t; - c = 61444 - x[offset + 640]; t = (x[offset + 640] ^ x[offset + 672]) & (c >> 31); x[offset + 640] ^= t; x[offset + 672] ^= t; - c = 61444 - x[offset + 656]; t = (x[offset + 656] ^ x[offset + 688]) & (c >> 31); x[offset + 656] ^= t; x[offset + 688] ^= t; - c = 61444 - x[offset + 656]; t = (x[offset + 656] ^ x[offset + 672]) & (c >> 31); x[offset + 656] ^= t; x[offset + 672] ^= t; - c = 61444 - x[offset + 704]; t = (x[offset + 704] ^ x[offset + 720]) & (c >> 31); x[offset + 704] ^= t; x[offset + 720] ^= t; - c = 61444 - x[offset + 736]; t = (x[offset + 736] ^ x[offset + 752]) & (c >> 31); x[offset + 736] ^= t; x[offset + 752] ^= t; - c = 61444 - x[offset + 704]; t = (x[offset + 704] ^ x[offset + 736]) & (c >> 31); x[offset + 704] ^= t; x[offset + 736] ^= t; - c = 61444 - x[offset + 720]; t = (x[offset + 720] ^ x[offset + 752]) & (c >> 31); x[offset + 720] ^= t; x[offset + 752] ^= t; - c = 61444 - x[offset + 720]; t = (x[offset + 720] ^ x[offset + 736]) & (c >> 31); x[offset + 720] ^= t; x[offset + 736] ^= t; - c = 61444 - x[offset + 640]; t = (x[offset + 640] ^ x[offset + 704]) & (c >> 31); x[offset + 640] ^= t; x[offset + 704] ^= t; - c = 61444 - x[offset + 672]; t = (x[offset + 672] ^ x[offset + 736]) & (c >> 31); x[offset + 672] ^= t; x[offset + 736] ^= t; - c = 61444 - x[offset + 672]; t = (x[offset + 672] ^ x[offset + 704]) & (c >> 31); x[offset + 672] ^= t; x[offset + 704] ^= t; - c = 61444 - x[offset + 656]; t = (x[offset + 656] ^ x[offset + 720]) & (c >> 31); x[offset + 656] ^= t; x[offset + 720] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 752]) & (c >> 31); x[offset + 688] ^= t; x[offset + 752] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 720]) & (c >> 31); x[offset + 688] ^= t; x[offset + 720] ^= t; - c = 61444 - x[offset + 656]; t = (x[offset + 656] ^ x[offset + 672]) & (c >> 31); x[offset + 656] ^= t; x[offset + 672] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 704]) & (c >> 31); x[offset + 688] ^= t; x[offset + 704] ^= t; - c = 61444 - x[offset + 720]; t = (x[offset + 720] ^ x[offset + 736]) & (c >> 31); x[offset + 720] ^= t; x[offset + 736] ^= t; - c = 61444 - x[offset + 512]; t = (x[offset + 512] ^ x[offset + 640]) & (c >> 31); x[offset + 512] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 576]; t = (x[offset + 576] ^ x[offset + 704]) & (c >> 31); x[offset + 576] ^= t; x[offset + 704] ^= t; - c = 61444 - x[offset + 576]; t = (x[offset + 576] ^ x[offset + 640]) & (c >> 31); x[offset + 576] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 544]; t = (x[offset + 544] ^ x[offset + 672]) & (c >> 31); x[offset + 544] ^= t; x[offset + 672] ^= t; - c = 61444 - x[offset + 608]; t = (x[offset + 608] ^ x[offset + 736]) & (c >> 31); x[offset + 608] ^= t; x[offset + 736] ^= t; - c = 61444 - x[offset + 608]; t = (x[offset + 608] ^ x[offset + 672]) & (c >> 31); x[offset + 608] ^= t; x[offset + 672] ^= t; - c = 61444 - x[offset + 544]; t = (x[offset + 544] ^ x[offset + 576]) & (c >> 31); x[offset + 544] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 608]; t = (x[offset + 608] ^ x[offset + 640]) & (c >> 31); x[offset + 608] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 672]; t = (x[offset + 672] ^ x[offset + 704]) & (c >> 31); x[offset + 672] ^= t; x[offset + 704] ^= t; - c = 61444 - x[offset + 528]; t = (x[offset + 528] ^ x[offset + 656]) & (c >> 31); x[offset + 528] ^= t; x[offset + 656] ^= t; - c = 61444 - x[offset + 592]; t = (x[offset + 592] ^ x[offset + 720]) & (c >> 31); x[offset + 592] ^= t; x[offset + 720] ^= t; - c = 61444 - x[offset + 592]; t = (x[offset + 592] ^ x[offset + 656]) & (c >> 31); x[offset + 592] ^= t; x[offset + 656] ^= t; - c = 61444 - x[offset + 560]; t = (x[offset + 560] ^ x[offset + 688]) & (c >> 31); x[offset + 560] ^= t; x[offset + 688] ^= t; - c = 61444 - x[offset + 624]; t = (x[offset + 624] ^ x[offset + 752]) & (c >> 31); x[offset + 624] ^= t; x[offset + 752] ^= t; - c = 61444 - x[offset + 624]; t = (x[offset + 624] ^ x[offset + 688]) & (c >> 31); x[offset + 624] ^= t; x[offset + 688] ^= t; - c = 61444 - x[offset + 560]; t = (x[offset + 560] ^ x[offset + 592]) & (c >> 31); x[offset + 560] ^= t; x[offset + 592] ^= t; - c = 61444 - x[offset + 624]; t = (x[offset + 624] ^ x[offset + 656]) & (c >> 31); x[offset + 624] ^= t; x[offset + 656] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 720]) & (c >> 31); x[offset + 688] ^= t; x[offset + 720] ^= t; - c = 61444 - x[offset + 528]; t = (x[offset + 528] ^ x[offset + 544]) & (c >> 31); x[offset + 528] ^= t; x[offset + 544] ^= t; - c = 61444 - x[offset + 560]; t = (x[offset + 560] ^ x[offset + 576]) & (c >> 31); x[offset + 560] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 592]; t = (x[offset + 592] ^ x[offset + 608]) & (c >> 31); x[offset + 592] ^= t; x[offset + 608] ^= t; - c = 61444 - x[offset + 624]; t = (x[offset + 624] ^ x[offset + 640]) & (c >> 31); x[offset + 624] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 656]; t = (x[offset + 656] ^ x[offset + 672]) & (c >> 31); x[offset + 656] ^= t; x[offset + 672] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 704]) & (c >> 31); x[offset + 688] ^= t; x[offset + 704] ^= t; - c = 61444 - x[offset + 720]; t = (x[offset + 720] ^ x[offset + 736]) & (c >> 31); x[offset + 720] ^= t; x[offset + 736] ^= t; - c = 61444 - x[offset + 768]; t = (x[offset + 768] ^ x[offset + 784]) & (c >> 31); x[offset + 768] ^= t; x[offset + 784] ^= t; - c = 61444 - x[offset + 800]; t = (x[offset + 800] ^ x[offset + 816]) & (c >> 31); x[offset + 800] ^= t; x[offset + 816] ^= t; - c = 61444 - x[offset + 768]; t = (x[offset + 768] ^ x[offset + 800]) & (c >> 31); x[offset + 768] ^= t; x[offset + 800] ^= t; - c = 61444 - x[offset + 784]; t = (x[offset + 784] ^ x[offset + 816]) & (c >> 31); x[offset + 784] ^= t; x[offset + 816] ^= t; - c = 61444 - x[offset + 784]; t = (x[offset + 784] ^ x[offset + 800]) & (c >> 31); x[offset + 784] ^= t; x[offset + 800] ^= t; - c = 61444 - x[offset + 832]; t = (x[offset + 832] ^ x[offset + 848]) & (c >> 31); x[offset + 832] ^= t; x[offset + 848] ^= t; - c = 61444 - x[offset + 864]; t = (x[offset + 864] ^ x[offset + 880]) & (c >> 31); x[offset + 864] ^= t; x[offset + 880] ^= t; - c = 61444 - x[offset + 832]; t = (x[offset + 832] ^ x[offset + 864]) & (c >> 31); x[offset + 832] ^= t; x[offset + 864] ^= t; - c = 61444 - x[offset + 848]; t = (x[offset + 848] ^ x[offset + 880]) & (c >> 31); x[offset + 848] ^= t; x[offset + 880] ^= t; - c = 61444 - x[offset + 848]; t = (x[offset + 848] ^ x[offset + 864]) & (c >> 31); x[offset + 848] ^= t; x[offset + 864] ^= t; - c = 61444 - x[offset + 768]; t = (x[offset + 768] ^ x[offset + 832]) & (c >> 31); x[offset + 768] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 800]; t = (x[offset + 800] ^ x[offset + 864]) & (c >> 31); x[offset + 800] ^= t; x[offset + 864] ^= t; - c = 61444 - x[offset + 800]; t = (x[offset + 800] ^ x[offset + 832]) & (c >> 31); x[offset + 800] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 784]; t = (x[offset + 784] ^ x[offset + 848]) & (c >> 31); x[offset + 784] ^= t; x[offset + 848] ^= t; - c = 61444 - x[offset + 816]; t = (x[offset + 816] ^ x[offset + 880]) & (c >> 31); x[offset + 816] ^= t; x[offset + 880] ^= t; - c = 61444 - x[offset + 816]; t = (x[offset + 816] ^ x[offset + 848]) & (c >> 31); x[offset + 816] ^= t; x[offset + 848] ^= t; - c = 61444 - x[offset + 784]; t = (x[offset + 784] ^ x[offset + 800]) & (c >> 31); x[offset + 784] ^= t; x[offset + 800] ^= t; - c = 61444 - x[offset + 816]; t = (x[offset + 816] ^ x[offset + 832]) & (c >> 31); x[offset + 816] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 848]; t = (x[offset + 848] ^ x[offset + 864]) & (c >> 31); x[offset + 848] ^= t; x[offset + 864] ^= t; - c = 61444 - x[offset + 896]; t = (x[offset + 896] ^ x[offset + 912]) & (c >> 31); x[offset + 896] ^= t; x[offset + 912] ^= t; - c = 61444 - x[offset + 928]; t = (x[offset + 928] ^ x[offset + 944]) & (c >> 31); x[offset + 928] ^= t; x[offset + 944] ^= t; - c = 61444 - x[offset + 896]; t = (x[offset + 896] ^ x[offset + 928]) & (c >> 31); x[offset + 896] ^= t; x[offset + 928] ^= t; - c = 61444 - x[offset + 912]; t = (x[offset + 912] ^ x[offset + 944]) & (c >> 31); x[offset + 912] ^= t; x[offset + 944] ^= t; - c = 61444 - x[offset + 912]; t = (x[offset + 912] ^ x[offset + 928]) & (c >> 31); x[offset + 912] ^= t; x[offset + 928] ^= t; - c = 61444 - x[offset + 960]; t = (x[offset + 960] ^ x[offset + 976]) & (c >> 31); x[offset + 960] ^= t; x[offset + 976] ^= t; - c = 61444 - x[offset + 992]; t = (x[offset + 992] ^ x[offset + 1008]) & (c >> 31); x[offset + 992] ^= t; x[offset + 1008] ^= t; - c = 61444 - x[offset + 960]; t = (x[offset + 960] ^ x[offset + 992]) & (c >> 31); x[offset + 960] ^= t; x[offset + 992] ^= t; - c = 61444 - x[offset + 976]; t = (x[offset + 976] ^ x[offset + 1008]) & (c >> 31); x[offset + 976] ^= t; x[offset + 1008] ^= t; - c = 61444 - x[offset + 976]; t = (x[offset + 976] ^ x[offset + 992]) & (c >> 31); x[offset + 976] ^= t; x[offset + 992] ^= t; - c = 61444 - x[offset + 896]; t = (x[offset + 896] ^ x[offset + 960]) & (c >> 31); x[offset + 896] ^= t; x[offset + 960] ^= t; - c = 61444 - x[offset + 928]; t = (x[offset + 928] ^ x[offset + 992]) & (c >> 31); x[offset + 928] ^= t; x[offset + 992] ^= t; - c = 61444 - x[offset + 928]; t = (x[offset + 928] ^ x[offset + 960]) & (c >> 31); x[offset + 928] ^= t; x[offset + 960] ^= t; - c = 61444 - x[offset + 912]; t = (x[offset + 912] ^ x[offset + 976]) & (c >> 31); x[offset + 912] ^= t; x[offset + 976] ^= t; - c = 61444 - x[offset + 944]; t = (x[offset + 944] ^ x[offset + 1008]) & (c >> 31); x[offset + 944] ^= t; x[offset + 1008] ^= t; - c = 61444 - x[offset + 944]; t = (x[offset + 944] ^ x[offset + 976]) & (c >> 31); x[offset + 944] ^= t; x[offset + 976] ^= t; - c = 61444 - x[offset + 912]; t = (x[offset + 912] ^ x[offset + 928]) & (c >> 31); x[offset + 912] ^= t; x[offset + 928] ^= t; - c = 61444 - x[offset + 944]; t = (x[offset + 944] ^ x[offset + 960]) & (c >> 31); x[offset + 944] ^= t; x[offset + 960] ^= t; - c = 61444 - x[offset + 976]; t = (x[offset + 976] ^ x[offset + 992]) & (c >> 31); x[offset + 976] ^= t; x[offset + 992] ^= t; - c = 61444 - x[offset + 768]; t = (x[offset + 768] ^ x[offset + 896]) & (c >> 31); x[offset + 768] ^= t; x[offset + 896] ^= t; - c = 61444 - x[offset + 832]; t = (x[offset + 832] ^ x[offset + 960]) & (c >> 31); x[offset + 832] ^= t; x[offset + 960] ^= t; - c = 61444 - x[offset + 832]; t = (x[offset + 832] ^ x[offset + 896]) & (c >> 31); x[offset + 832] ^= t; x[offset + 896] ^= t; - c = 61444 - x[offset + 800]; t = (x[offset + 800] ^ x[offset + 928]) & (c >> 31); x[offset + 800] ^= t; x[offset + 928] ^= t; - c = 61444 - x[offset + 864]; t = (x[offset + 864] ^ x[offset + 992]) & (c >> 31); x[offset + 864] ^= t; x[offset + 992] ^= t; - c = 61444 - x[offset + 864]; t = (x[offset + 864] ^ x[offset + 928]) & (c >> 31); x[offset + 864] ^= t; x[offset + 928] ^= t; - c = 61444 - x[offset + 800]; t = (x[offset + 800] ^ x[offset + 832]) & (c >> 31); x[offset + 800] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 864]; t = (x[offset + 864] ^ x[offset + 896]) & (c >> 31); x[offset + 864] ^= t; x[offset + 896] ^= t; - c = 61444 - x[offset + 928]; t = (x[offset + 928] ^ x[offset + 960]) & (c >> 31); x[offset + 928] ^= t; x[offset + 960] ^= t; - c = 61444 - x[offset + 784]; t = (x[offset + 784] ^ x[offset + 912]) & (c >> 31); x[offset + 784] ^= t; x[offset + 912] ^= t; - c = 61444 - x[offset + 848]; t = (x[offset + 848] ^ x[offset + 976]) & (c >> 31); x[offset + 848] ^= t; x[offset + 976] ^= t; - c = 61444 - x[offset + 848]; t = (x[offset + 848] ^ x[offset + 912]) & (c >> 31); x[offset + 848] ^= t; x[offset + 912] ^= t; - c = 61444 - x[offset + 816]; t = (x[offset + 816] ^ x[offset + 944]) & (c >> 31); x[offset + 816] ^= t; x[offset + 944] ^= t; - c = 61444 - x[offset + 880]; t = (x[offset + 880] ^ x[offset + 1008]) & (c >> 31); x[offset + 880] ^= t; x[offset + 1008] ^= t; - c = 61444 - x[offset + 880]; t = (x[offset + 880] ^ x[offset + 944]) & (c >> 31); x[offset + 880] ^= t; x[offset + 944] ^= t; - c = 61444 - x[offset + 816]; t = (x[offset + 816] ^ x[offset + 848]) & (c >> 31); x[offset + 816] ^= t; x[offset + 848] ^= t; - c = 61444 - x[offset + 880]; t = (x[offset + 880] ^ x[offset + 912]) & (c >> 31); x[offset + 880] ^= t; x[offset + 912] ^= t; - c = 61444 - x[offset + 944]; t = (x[offset + 944] ^ x[offset + 976]) & (c >> 31); x[offset + 944] ^= t; x[offset + 976] ^= t; - c = 61444 - x[offset + 784]; t = (x[offset + 784] ^ x[offset + 800]) & (c >> 31); x[offset + 784] ^= t; x[offset + 800] ^= t; - c = 61444 - x[offset + 816]; t = (x[offset + 816] ^ x[offset + 832]) & (c >> 31); x[offset + 816] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 848]; t = (x[offset + 848] ^ x[offset + 864]) & (c >> 31); x[offset + 848] ^= t; x[offset + 864] ^= t; - c = 61444 - x[offset + 880]; t = (x[offset + 880] ^ x[offset + 896]) & (c >> 31); x[offset + 880] ^= t; x[offset + 896] ^= t; - c = 61444 - x[offset + 912]; t = (x[offset + 912] ^ x[offset + 928]) & (c >> 31); x[offset + 912] ^= t; x[offset + 928] ^= t; - c = 61444 - x[offset + 944]; t = (x[offset + 944] ^ x[offset + 960]) & (c >> 31); x[offset + 944] ^= t; x[offset + 960] ^= t; - c = 61444 - x[offset + 976]; t = (x[offset + 976] ^ x[offset + 992]) & (c >> 31); x[offset + 976] ^= t; x[offset + 992] ^= t; - c = 61444 - x[offset + 512]; t = (x[offset + 512] ^ x[offset + 768]) & (c >> 31); x[offset + 512] ^= t; x[offset + 768] ^= t; - c = 61444 - x[offset + 640]; t = (x[offset + 640] ^ x[offset + 896]) & (c >> 31); x[offset + 640] ^= t; x[offset + 896] ^= t; - c = 61444 - x[offset + 640]; t = (x[offset + 640] ^ x[offset + 768]) & (c >> 31); x[offset + 640] ^= t; x[offset + 768] ^= t; - c = 61444 - x[offset + 576]; t = (x[offset + 576] ^ x[offset + 832]) & (c >> 31); x[offset + 576] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 704]; t = (x[offset + 704] ^ x[offset + 960]) & (c >> 31); x[offset + 704] ^= t; x[offset + 960] ^= t; - c = 61444 - x[offset + 704]; t = (x[offset + 704] ^ x[offset + 832]) & (c >> 31); x[offset + 704] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 576]; t = (x[offset + 576] ^ x[offset + 640]) & (c >> 31); x[offset + 576] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 704]; t = (x[offset + 704] ^ x[offset + 768]) & (c >> 31); x[offset + 704] ^= t; x[offset + 768] ^= t; - c = 61444 - x[offset + 832]; t = (x[offset + 832] ^ x[offset + 896]) & (c >> 31); x[offset + 832] ^= t; x[offset + 896] ^= t; - c = 61444 - x[offset + 544]; t = (x[offset + 544] ^ x[offset + 800]) & (c >> 31); x[offset + 544] ^= t; x[offset + 800] ^= t; - c = 61444 - x[offset + 672]; t = (x[offset + 672] ^ x[offset + 928]) & (c >> 31); x[offset + 672] ^= t; x[offset + 928] ^= t; - c = 61444 - x[offset + 672]; t = (x[offset + 672] ^ x[offset + 800]) & (c >> 31); x[offset + 672] ^= t; x[offset + 800] ^= t; - c = 61444 - x[offset + 608]; t = (x[offset + 608] ^ x[offset + 864]) & (c >> 31); x[offset + 608] ^= t; x[offset + 864] ^= t; - c = 61444 - x[offset + 736]; t = (x[offset + 736] ^ x[offset + 992]) & (c >> 31); x[offset + 736] ^= t; x[offset + 992] ^= t; - c = 61444 - x[offset + 736]; t = (x[offset + 736] ^ x[offset + 864]) & (c >> 31); x[offset + 736] ^= t; x[offset + 864] ^= t; - c = 61444 - x[offset + 608]; t = (x[offset + 608] ^ x[offset + 672]) & (c >> 31); x[offset + 608] ^= t; x[offset + 672] ^= t; - c = 61444 - x[offset + 736]; t = (x[offset + 736] ^ x[offset + 800]) & (c >> 31); x[offset + 736] ^= t; x[offset + 800] ^= t; - c = 61444 - x[offset + 864]; t = (x[offset + 864] ^ x[offset + 928]) & (c >> 31); x[offset + 864] ^= t; x[offset + 928] ^= t; - c = 61444 - x[offset + 544]; t = (x[offset + 544] ^ x[offset + 576]) & (c >> 31); x[offset + 544] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 608]; t = (x[offset + 608] ^ x[offset + 640]) & (c >> 31); x[offset + 608] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 672]; t = (x[offset + 672] ^ x[offset + 704]) & (c >> 31); x[offset + 672] ^= t; x[offset + 704] ^= t; - c = 61444 - x[offset + 736]; t = (x[offset + 736] ^ x[offset + 768]) & (c >> 31); x[offset + 736] ^= t; x[offset + 768] ^= t; - c = 61444 - x[offset + 800]; t = (x[offset + 800] ^ x[offset + 832]) & (c >> 31); x[offset + 800] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 864]; t = (x[offset + 864] ^ x[offset + 896]) & (c >> 31); x[offset + 864] ^= t; x[offset + 896] ^= t; - c = 61444 - x[offset + 928]; t = (x[offset + 928] ^ x[offset + 960]) & (c >> 31); x[offset + 928] ^= t; x[offset + 960] ^= t; - c = 61444 - x[offset + 528]; t = (x[offset + 528] ^ x[offset + 784]) & (c >> 31); x[offset + 528] ^= t; x[offset + 784] ^= t; - c = 61444 - x[offset + 656]; t = (x[offset + 656] ^ x[offset + 912]) & (c >> 31); x[offset + 656] ^= t; x[offset + 912] ^= t; - c = 61444 - x[offset + 656]; t = (x[offset + 656] ^ x[offset + 784]) & (c >> 31); x[offset + 656] ^= t; x[offset + 784] ^= t; - c = 61444 - x[offset + 592]; t = (x[offset + 592] ^ x[offset + 848]) & (c >> 31); x[offset + 592] ^= t; x[offset + 848] ^= t; - c = 61444 - x[offset + 720]; t = (x[offset + 720] ^ x[offset + 976]) & (c >> 31); x[offset + 720] ^= t; x[offset + 976] ^= t; - c = 61444 - x[offset + 720]; t = (x[offset + 720] ^ x[offset + 848]) & (c >> 31); x[offset + 720] ^= t; x[offset + 848] ^= t; - c = 61444 - x[offset + 592]; t = (x[offset + 592] ^ x[offset + 656]) & (c >> 31); x[offset + 592] ^= t; x[offset + 656] ^= t; - c = 61444 - x[offset + 720]; t = (x[offset + 720] ^ x[offset + 784]) & (c >> 31); x[offset + 720] ^= t; x[offset + 784] ^= t; - c = 61444 - x[offset + 848]; t = (x[offset + 848] ^ x[offset + 912]) & (c >> 31); x[offset + 848] ^= t; x[offset + 912] ^= t; - c = 61444 - x[offset + 560]; t = (x[offset + 560] ^ x[offset + 816]) & (c >> 31); x[offset + 560] ^= t; x[offset + 816] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 944]) & (c >> 31); x[offset + 688] ^= t; x[offset + 944] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 816]) & (c >> 31); x[offset + 688] ^= t; x[offset + 816] ^= t; - c = 61444 - x[offset + 624]; t = (x[offset + 624] ^ x[offset + 880]) & (c >> 31); x[offset + 624] ^= t; x[offset + 880] ^= t; - c = 61444 - x[offset + 752]; t = (x[offset + 752] ^ x[offset + 1008]) & (c >> 31); x[offset + 752] ^= t; x[offset + 1008] ^= t; - c = 61444 - x[offset + 752]; t = (x[offset + 752] ^ x[offset + 880]) & (c >> 31); x[offset + 752] ^= t; x[offset + 880] ^= t; - c = 61444 - x[offset + 624]; t = (x[offset + 624] ^ x[offset + 688]) & (c >> 31); x[offset + 624] ^= t; x[offset + 688] ^= t; - c = 61444 - x[offset + 752]; t = (x[offset + 752] ^ x[offset + 816]) & (c >> 31); x[offset + 752] ^= t; x[offset + 816] ^= t; - c = 61444 - x[offset + 880]; t = (x[offset + 880] ^ x[offset + 944]) & (c >> 31); x[offset + 880] ^= t; x[offset + 944] ^= t; - c = 61444 - x[offset + 560]; t = (x[offset + 560] ^ x[offset + 592]) & (c >> 31); x[offset + 560] ^= t; x[offset + 592] ^= t; - c = 61444 - x[offset + 624]; t = (x[offset + 624] ^ x[offset + 656]) & (c >> 31); x[offset + 624] ^= t; x[offset + 656] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 720]) & (c >> 31); x[offset + 688] ^= t; x[offset + 720] ^= t; - c = 61444 - x[offset + 752]; t = (x[offset + 752] ^ x[offset + 784]) & (c >> 31); x[offset + 752] ^= t; x[offset + 784] ^= t; - c = 61444 - x[offset + 816]; t = (x[offset + 816] ^ x[offset + 848]) & (c >> 31); x[offset + 816] ^= t; x[offset + 848] ^= t; - c = 61444 - x[offset + 880]; t = (x[offset + 880] ^ x[offset + 912]) & (c >> 31); x[offset + 880] ^= t; x[offset + 912] ^= t; - c = 61444 - x[offset + 944]; t = (x[offset + 944] ^ x[offset + 976]) & (c >> 31); x[offset + 944] ^= t; x[offset + 976] ^= t; - c = 61444 - x[offset + 528]; t = (x[offset + 528] ^ x[offset + 544]) & (c >> 31); x[offset + 528] ^= t; x[offset + 544] ^= t; - c = 61444 - x[offset + 560]; t = (x[offset + 560] ^ x[offset + 576]) & (c >> 31); x[offset + 560] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 592]; t = (x[offset + 592] ^ x[offset + 608]) & (c >> 31); x[offset + 592] ^= t; x[offset + 608] ^= t; - c = 61444 - x[offset + 624]; t = (x[offset + 624] ^ x[offset + 640]) & (c >> 31); x[offset + 624] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 656]; t = (x[offset + 656] ^ x[offset + 672]) & (c >> 31); x[offset + 656] ^= t; x[offset + 672] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 704]) & (c >> 31); x[offset + 688] ^= t; x[offset + 704] ^= t; - c = 61444 - x[offset + 720]; t = (x[offset + 720] ^ x[offset + 736]) & (c >> 31); x[offset + 720] ^= t; x[offset + 736] ^= t; - c = 61444 - x[offset + 752]; t = (x[offset + 752] ^ x[offset + 768]) & (c >> 31); x[offset + 752] ^= t; x[offset + 768] ^= t; - c = 61444 - x[offset + 784]; t = (x[offset + 784] ^ x[offset + 800]) & (c >> 31); x[offset + 784] ^= t; x[offset + 800] ^= t; - c = 61444 - x[offset + 816]; t = (x[offset + 816] ^ x[offset + 832]) & (c >> 31); x[offset + 816] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 848]; t = (x[offset + 848] ^ x[offset + 864]) & (c >> 31); x[offset + 848] ^= t; x[offset + 864] ^= t; - c = 61444 - x[offset + 880]; t = (x[offset + 880] ^ x[offset + 896]) & (c >> 31); x[offset + 880] ^= t; x[offset + 896] ^= t; - c = 61444 - x[offset + 912]; t = (x[offset + 912] ^ x[offset + 928]) & (c >> 31); x[offset + 912] ^= t; x[offset + 928] ^= t; - c = 61444 - x[offset + 944]; t = (x[offset + 944] ^ x[offset + 960]) & (c >> 31); x[offset + 944] ^= t; x[offset + 960] ^= t; - c = 61444 - x[offset + 976]; t = (x[offset + 976] ^ x[offset + 992]) & (c >> 31); x[offset + 976] ^= t; x[offset + 992] ^= t; - c = 61444 - x[offset + 0]; t = (x[offset + 0] ^ x[offset + 512]) & (c >> 31); x[offset + 0] ^= t; x[offset + 512] ^= t; - c = 61444 - x[offset + 256]; t = (x[offset + 256] ^ x[offset + 768]) & (c >> 31); x[offset + 256] ^= t; x[offset + 768] ^= t; - c = 61444 - x[offset + 256]; t = (x[offset + 256] ^ x[offset + 512]) & (c >> 31); x[offset + 256] ^= t; x[offset + 512] ^= t; - c = 61444 - x[offset + 128]; t = (x[offset + 128] ^ x[offset + 640]) & (c >> 31); x[offset + 128] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 384]; t = (x[offset + 384] ^ x[offset + 896]) & (c >> 31); x[offset + 384] ^= t; x[offset + 896] ^= t; - c = 61444 - x[offset + 384]; t = (x[offset + 384] ^ x[offset + 640]) & (c >> 31); x[offset + 384] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 128]; t = (x[offset + 128] ^ x[offset + 256]) & (c >> 31); x[offset + 128] ^= t; x[offset + 256] ^= t; - c = 61444 - x[offset + 384]; t = (x[offset + 384] ^ x[offset + 512]) & (c >> 31); x[offset + 384] ^= t; x[offset + 512] ^= t; - c = 61444 - x[offset + 640]; t = (x[offset + 640] ^ x[offset + 768]) & (c >> 31); x[offset + 640] ^= t; x[offset + 768] ^= t; - c = 61444 - x[offset + 64]; t = (x[offset + 64] ^ x[offset + 576]) & (c >> 31); x[offset + 64] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 320]; t = (x[offset + 320] ^ x[offset + 832]) & (c >> 31); x[offset + 320] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 320]; t = (x[offset + 320] ^ x[offset + 576]) & (c >> 31); x[offset + 320] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 192]; t = (x[offset + 192] ^ x[offset + 704]) & (c >> 31); x[offset + 192] ^= t; x[offset + 704] ^= t; - c = 61444 - x[offset + 448]; t = (x[offset + 448] ^ x[offset + 960]) & (c >> 31); x[offset + 448] ^= t; x[offset + 960] ^= t; - c = 61444 - x[offset + 448]; t = (x[offset + 448] ^ x[offset + 704]) & (c >> 31); x[offset + 448] ^= t; x[offset + 704] ^= t; - c = 61444 - x[offset + 192]; t = (x[offset + 192] ^ x[offset + 320]) & (c >> 31); x[offset + 192] ^= t; x[offset + 320] ^= t; - c = 61444 - x[offset + 448]; t = (x[offset + 448] ^ x[offset + 576]) & (c >> 31); x[offset + 448] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 704]; t = (x[offset + 704] ^ x[offset + 832]) & (c >> 31); x[offset + 704] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 64]; t = (x[offset + 64] ^ x[offset + 128]) & (c >> 31); x[offset + 64] ^= t; x[offset + 128] ^= t; - c = 61444 - x[offset + 192]; t = (x[offset + 192] ^ x[offset + 256]) & (c >> 31); x[offset + 192] ^= t; x[offset + 256] ^= t; - c = 61444 - x[offset + 320]; t = (x[offset + 320] ^ x[offset + 384]) & (c >> 31); x[offset + 320] ^= t; x[offset + 384] ^= t; - c = 61444 - x[offset + 448]; t = (x[offset + 448] ^ x[offset + 512]) & (c >> 31); x[offset + 448] ^= t; x[offset + 512] ^= t; - c = 61444 - x[offset + 576]; t = (x[offset + 576] ^ x[offset + 640]) & (c >> 31); x[offset + 576] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 704]; t = (x[offset + 704] ^ x[offset + 768]) & (c >> 31); x[offset + 704] ^= t; x[offset + 768] ^= t; - c = 61444 - x[offset + 832]; t = (x[offset + 832] ^ x[offset + 896]) & (c >> 31); x[offset + 832] ^= t; x[offset + 896] ^= t; - c = 61444 - x[offset + 32]; t = (x[offset + 32] ^ x[offset + 544]) & (c >> 31); x[offset + 32] ^= t; x[offset + 544] ^= t; - c = 61444 - x[offset + 288]; t = (x[offset + 288] ^ x[offset + 800]) & (c >> 31); x[offset + 288] ^= t; x[offset + 800] ^= t; - c = 61444 - x[offset + 288]; t = (x[offset + 288] ^ x[offset + 544]) & (c >> 31); x[offset + 288] ^= t; x[offset + 544] ^= t; - c = 61444 - x[offset + 160]; t = (x[offset + 160] ^ x[offset + 672]) & (c >> 31); x[offset + 160] ^= t; x[offset + 672] ^= t; - c = 61444 - x[offset + 416]; t = (x[offset + 416] ^ x[offset + 928]) & (c >> 31); x[offset + 416] ^= t; x[offset + 928] ^= t; - c = 61444 - x[offset + 416]; t = (x[offset + 416] ^ x[offset + 672]) & (c >> 31); x[offset + 416] ^= t; x[offset + 672] ^= t; - c = 61444 - x[offset + 160]; t = (x[offset + 160] ^ x[offset + 288]) & (c >> 31); x[offset + 160] ^= t; x[offset + 288] ^= t; - c = 61444 - x[offset + 416]; t = (x[offset + 416] ^ x[offset + 544]) & (c >> 31); x[offset + 416] ^= t; x[offset + 544] ^= t; - c = 61444 - x[offset + 672]; t = (x[offset + 672] ^ x[offset + 800]) & (c >> 31); x[offset + 672] ^= t; x[offset + 800] ^= t; - c = 61444 - x[offset + 96]; t = (x[offset + 96] ^ x[offset + 608]) & (c >> 31); x[offset + 96] ^= t; x[offset + 608] ^= t; - c = 61444 - x[offset + 352]; t = (x[offset + 352] ^ x[offset + 864]) & (c >> 31); x[offset + 352] ^= t; x[offset + 864] ^= t; - c = 61444 - x[offset + 352]; t = (x[offset + 352] ^ x[offset + 608]) & (c >> 31); x[offset + 352] ^= t; x[offset + 608] ^= t; - c = 61444 - x[offset + 224]; t = (x[offset + 224] ^ x[offset + 736]) & (c >> 31); x[offset + 224] ^= t; x[offset + 736] ^= t; - c = 61444 - x[offset + 480]; t = (x[offset + 480] ^ x[offset + 992]) & (c >> 31); x[offset + 480] ^= t; x[offset + 992] ^= t; - c = 61444 - x[offset + 480]; t = (x[offset + 480] ^ x[offset + 736]) & (c >> 31); x[offset + 480] ^= t; x[offset + 736] ^= t; - c = 61444 - x[offset + 224]; t = (x[offset + 224] ^ x[offset + 352]) & (c >> 31); x[offset + 224] ^= t; x[offset + 352] ^= t; - c = 61444 - x[offset + 480]; t = (x[offset + 480] ^ x[offset + 608]) & (c >> 31); x[offset + 480] ^= t; x[offset + 608] ^= t; - c = 61444 - x[offset + 736]; t = (x[offset + 736] ^ x[offset + 864]) & (c >> 31); x[offset + 736] ^= t; x[offset + 864] ^= t; - c = 61444 - x[offset + 96]; t = (x[offset + 96] ^ x[offset + 160]) & (c >> 31); x[offset + 96] ^= t; x[offset + 160] ^= t; - c = 61444 - x[offset + 224]; t = (x[offset + 224] ^ x[offset + 288]) & (c >> 31); x[offset + 224] ^= t; x[offset + 288] ^= t; - c = 61444 - x[offset + 352]; t = (x[offset + 352] ^ x[offset + 416]) & (c >> 31); x[offset + 352] ^= t; x[offset + 416] ^= t; - c = 61444 - x[offset + 480]; t = (x[offset + 480] ^ x[offset + 544]) & (c >> 31); x[offset + 480] ^= t; x[offset + 544] ^= t; - c = 61444 - x[offset + 608]; t = (x[offset + 608] ^ x[offset + 672]) & (c >> 31); x[offset + 608] ^= t; x[offset + 672] ^= t; - c = 61444 - x[offset + 736]; t = (x[offset + 736] ^ x[offset + 800]) & (c >> 31); x[offset + 736] ^= t; x[offset + 800] ^= t; - c = 61444 - x[offset + 864]; t = (x[offset + 864] ^ x[offset + 928]) & (c >> 31); x[offset + 864] ^= t; x[offset + 928] ^= t; - c = 61444 - x[offset + 32]; t = (x[offset + 32] ^ x[offset + 64]) & (c >> 31); x[offset + 32] ^= t; x[offset + 64] ^= t; - c = 61444 - x[offset + 96]; t = (x[offset + 96] ^ x[offset + 128]) & (c >> 31); x[offset + 96] ^= t; x[offset + 128] ^= t; - c = 61444 - x[offset + 160]; t = (x[offset + 160] ^ x[offset + 192]) & (c >> 31); x[offset + 160] ^= t; x[offset + 192] ^= t; - c = 61444 - x[offset + 224]; t = (x[offset + 224] ^ x[offset + 256]) & (c >> 31); x[offset + 224] ^= t; x[offset + 256] ^= t; - c = 61444 - x[offset + 288]; t = (x[offset + 288] ^ x[offset + 320]) & (c >> 31); x[offset + 288] ^= t; x[offset + 320] ^= t; - c = 61444 - x[offset + 352]; t = (x[offset + 352] ^ x[offset + 384]) & (c >> 31); x[offset + 352] ^= t; x[offset + 384] ^= t; - c = 61444 - x[offset + 416]; t = (x[offset + 416] ^ x[offset + 448]) & (c >> 31); x[offset + 416] ^= t; x[offset + 448] ^= t; - c = 61444 - x[offset + 480]; t = (x[offset + 480] ^ x[offset + 512]) & (c >> 31); x[offset + 480] ^= t; x[offset + 512] ^= t; - c = 61444 - x[offset + 544]; t = (x[offset + 544] ^ x[offset + 576]) & (c >> 31); x[offset + 544] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 608]; t = (x[offset + 608] ^ x[offset + 640]) & (c >> 31); x[offset + 608] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 672]; t = (x[offset + 672] ^ x[offset + 704]) & (c >> 31); x[offset + 672] ^= t; x[offset + 704] ^= t; - c = 61444 - x[offset + 736]; t = (x[offset + 736] ^ x[offset + 768]) & (c >> 31); x[offset + 736] ^= t; x[offset + 768] ^= t; - c = 61444 - x[offset + 800]; t = (x[offset + 800] ^ x[offset + 832]) & (c >> 31); x[offset + 800] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 864]; t = (x[offset + 864] ^ x[offset + 896]) & (c >> 31); x[offset + 864] ^= t; x[offset + 896] ^= t; - c = 61444 - x[offset + 928]; t = (x[offset + 928] ^ x[offset + 960]) & (c >> 31); x[offset + 928] ^= t; x[offset + 960] ^= t; - c = 61444 - x[offset + 16]; t = (x[offset + 16] ^ x[offset + 528]) & (c >> 31); x[offset + 16] ^= t; x[offset + 528] ^= t; - c = 61444 - x[offset + 272]; t = (x[offset + 272] ^ x[offset + 784]) & (c >> 31); x[offset + 272] ^= t; x[offset + 784] ^= t; - c = 61444 - x[offset + 272]; t = (x[offset + 272] ^ x[offset + 528]) & (c >> 31); x[offset + 272] ^= t; x[offset + 528] ^= t; - c = 61444 - x[offset + 144]; t = (x[offset + 144] ^ x[offset + 656]) & (c >> 31); x[offset + 144] ^= t; x[offset + 656] ^= t; - c = 61444 - x[offset + 400]; t = (x[offset + 400] ^ x[offset + 912]) & (c >> 31); x[offset + 400] ^= t; x[offset + 912] ^= t; - c = 61444 - x[offset + 400]; t = (x[offset + 400] ^ x[offset + 656]) & (c >> 31); x[offset + 400] ^= t; x[offset + 656] ^= t; - c = 61444 - x[offset + 144]; t = (x[offset + 144] ^ x[offset + 272]) & (c >> 31); x[offset + 144] ^= t; x[offset + 272] ^= t; - c = 61444 - x[offset + 400]; t = (x[offset + 400] ^ x[offset + 528]) & (c >> 31); x[offset + 400] ^= t; x[offset + 528] ^= t; - c = 61444 - x[offset + 656]; t = (x[offset + 656] ^ x[offset + 784]) & (c >> 31); x[offset + 656] ^= t; x[offset + 784] ^= t; - c = 61444 - x[offset + 80]; t = (x[offset + 80] ^ x[offset + 592]) & (c >> 31); x[offset + 80] ^= t; x[offset + 592] ^= t; - c = 61444 - x[offset + 336]; t = (x[offset + 336] ^ x[offset + 848]) & (c >> 31); x[offset + 336] ^= t; x[offset + 848] ^= t; - c = 61444 - x[offset + 336]; t = (x[offset + 336] ^ x[offset + 592]) & (c >> 31); x[offset + 336] ^= t; x[offset + 592] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 720]) & (c >> 31); x[offset + 208] ^= t; x[offset + 720] ^= t; - c = 61444 - x[offset + 464]; t = (x[offset + 464] ^ x[offset + 976]) & (c >> 31); x[offset + 464] ^= t; x[offset + 976] ^= t; - c = 61444 - x[offset + 464]; t = (x[offset + 464] ^ x[offset + 720]) & (c >> 31); x[offset + 464] ^= t; x[offset + 720] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 336]) & (c >> 31); x[offset + 208] ^= t; x[offset + 336] ^= t; - c = 61444 - x[offset + 464]; t = (x[offset + 464] ^ x[offset + 592]) & (c >> 31); x[offset + 464] ^= t; x[offset + 592] ^= t; - c = 61444 - x[offset + 720]; t = (x[offset + 720] ^ x[offset + 848]) & (c >> 31); x[offset + 720] ^= t; x[offset + 848] ^= t; - c = 61444 - x[offset + 80]; t = (x[offset + 80] ^ x[offset + 144]) & (c >> 31); x[offset + 80] ^= t; x[offset + 144] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 272]) & (c >> 31); x[offset + 208] ^= t; x[offset + 272] ^= t; - c = 61444 - x[offset + 336]; t = (x[offset + 336] ^ x[offset + 400]) & (c >> 31); x[offset + 336] ^= t; x[offset + 400] ^= t; - c = 61444 - x[offset + 464]; t = (x[offset + 464] ^ x[offset + 528]) & (c >> 31); x[offset + 464] ^= t; x[offset + 528] ^= t; - c = 61444 - x[offset + 592]; t = (x[offset + 592] ^ x[offset + 656]) & (c >> 31); x[offset + 592] ^= t; x[offset + 656] ^= t; - c = 61444 - x[offset + 720]; t = (x[offset + 720] ^ x[offset + 784]) & (c >> 31); x[offset + 720] ^= t; x[offset + 784] ^= t; - c = 61444 - x[offset + 848]; t = (x[offset + 848] ^ x[offset + 912]) & (c >> 31); x[offset + 848] ^= t; x[offset + 912] ^= t; - c = 61444 - x[offset + 48]; t = (x[offset + 48] ^ x[offset + 560]) & (c >> 31); x[offset + 48] ^= t; x[offset + 560] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 816]) & (c >> 31); x[offset + 304] ^= t; x[offset + 816] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 560]) & (c >> 31); x[offset + 304] ^= t; x[offset + 560] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 688]) & (c >> 31); x[offset + 176] ^= t; x[offset + 688] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 944]) & (c >> 31); x[offset + 432] ^= t; x[offset + 944] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 688]) & (c >> 31); x[offset + 432] ^= t; x[offset + 688] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 304]) & (c >> 31); x[offset + 176] ^= t; x[offset + 304] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 560]) & (c >> 31); x[offset + 432] ^= t; x[offset + 560] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 816]) & (c >> 31); x[offset + 688] ^= t; x[offset + 816] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 624]) & (c >> 31); x[offset + 112] ^= t; x[offset + 624] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 880]) & (c >> 31); x[offset + 368] ^= t; x[offset + 880] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 624]) & (c >> 31); x[offset + 368] ^= t; x[offset + 624] ^= t; - c = 61444 - x[offset + 240]; t = (x[offset + 240] ^ x[offset + 752]) & (c >> 31); x[offset + 240] ^= t; x[offset + 752] ^= t; - c = 61444 - x[offset + 496]; t = (x[offset + 496] ^ x[offset + 1008]) & (c >> 31); x[offset + 496] ^= t; x[offset + 1008] ^= t; - c = 61444 - x[offset + 496]; t = (x[offset + 496] ^ x[offset + 752]) & (c >> 31); x[offset + 496] ^= t; x[offset + 752] ^= t; - c = 61444 - x[offset + 240]; t = (x[offset + 240] ^ x[offset + 368]) & (c >> 31); x[offset + 240] ^= t; x[offset + 368] ^= t; - c = 61444 - x[offset + 496]; t = (x[offset + 496] ^ x[offset + 624]) & (c >> 31); x[offset + 496] ^= t; x[offset + 624] ^= t; - c = 61444 - x[offset + 752]; t = (x[offset + 752] ^ x[offset + 880]) & (c >> 31); x[offset + 752] ^= t; x[offset + 880] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 176]) & (c >> 31); x[offset + 112] ^= t; x[offset + 176] ^= t; - c = 61444 - x[offset + 240]; t = (x[offset + 240] ^ x[offset + 304]) & (c >> 31); x[offset + 240] ^= t; x[offset + 304] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 432]) & (c >> 31); x[offset + 368] ^= t; x[offset + 432] ^= t; - c = 61444 - x[offset + 496]; t = (x[offset + 496] ^ x[offset + 560]) & (c >> 31); x[offset + 496] ^= t; x[offset + 560] ^= t; - c = 61444 - x[offset + 624]; t = (x[offset + 624] ^ x[offset + 688]) & (c >> 31); x[offset + 624] ^= t; x[offset + 688] ^= t; - c = 61444 - x[offset + 752]; t = (x[offset + 752] ^ x[offset + 816]) & (c >> 31); x[offset + 752] ^= t; x[offset + 816] ^= t; - c = 61444 - x[offset + 880]; t = (x[offset + 880] ^ x[offset + 944]) & (c >> 31); x[offset + 880] ^= t; x[offset + 944] ^= t; - c = 61444 - x[offset + 48]; t = (x[offset + 48] ^ x[offset + 80]) & (c >> 31); x[offset + 48] ^= t; x[offset + 80] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 144]) & (c >> 31); x[offset + 112] ^= t; x[offset + 144] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 208]) & (c >> 31); x[offset + 176] ^= t; x[offset + 208] ^= t; - c = 61444 - x[offset + 240]; t = (x[offset + 240] ^ x[offset + 272]) & (c >> 31); x[offset + 240] ^= t; x[offset + 272] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 336]) & (c >> 31); x[offset + 304] ^= t; x[offset + 336] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 400]) & (c >> 31); x[offset + 368] ^= t; x[offset + 400] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 464]) & (c >> 31); x[offset + 432] ^= t; x[offset + 464] ^= t; - c = 61444 - x[offset + 496]; t = (x[offset + 496] ^ x[offset + 528]) & (c >> 31); x[offset + 496] ^= t; x[offset + 528] ^= t; - c = 61444 - x[offset + 560]; t = (x[offset + 560] ^ x[offset + 592]) & (c >> 31); x[offset + 560] ^= t; x[offset + 592] ^= t; - c = 61444 - x[offset + 624]; t = (x[offset + 624] ^ x[offset + 656]) & (c >> 31); x[offset + 624] ^= t; x[offset + 656] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 720]) & (c >> 31); x[offset + 688] ^= t; x[offset + 720] ^= t; - c = 61444 - x[offset + 752]; t = (x[offset + 752] ^ x[offset + 784]) & (c >> 31); x[offset + 752] ^= t; x[offset + 784] ^= t; - c = 61444 - x[offset + 816]; t = (x[offset + 816] ^ x[offset + 848]) & (c >> 31); x[offset + 816] ^= t; x[offset + 848] ^= t; - c = 61444 - x[offset + 880]; t = (x[offset + 880] ^ x[offset + 912]) & (c >> 31); x[offset + 880] ^= t; x[offset + 912] ^= t; - c = 61444 - x[offset + 944]; t = (x[offset + 944] ^ x[offset + 976]) & (c >> 31); x[offset + 944] ^= t; x[offset + 976] ^= t; - c = 61444 - x[offset + 16]; t = (x[offset + 16] ^ x[offset + 32]) & (c >> 31); x[offset + 16] ^= t; x[offset + 32] ^= t; - c = 61444 - x[offset + 48]; t = (x[offset + 48] ^ x[offset + 64]) & (c >> 31); x[offset + 48] ^= t; x[offset + 64] ^= t; - c = 61444 - x[offset + 80]; t = (x[offset + 80] ^ x[offset + 96]) & (c >> 31); x[offset + 80] ^= t; x[offset + 96] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 128]) & (c >> 31); x[offset + 112] ^= t; x[offset + 128] ^= t; - c = 61444 - x[offset + 144]; t = (x[offset + 144] ^ x[offset + 160]) & (c >> 31); x[offset + 144] ^= t; x[offset + 160] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 192]) & (c >> 31); x[offset + 176] ^= t; x[offset + 192] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 224]) & (c >> 31); x[offset + 208] ^= t; x[offset + 224] ^= t; - c = 61444 - x[offset + 240]; t = (x[offset + 240] ^ x[offset + 256]) & (c >> 31); x[offset + 240] ^= t; x[offset + 256] ^= t; - c = 61444 - x[offset + 272]; t = (x[offset + 272] ^ x[offset + 288]) & (c >> 31); x[offset + 272] ^= t; x[offset + 288] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 320]) & (c >> 31); x[offset + 304] ^= t; x[offset + 320] ^= t; - c = 61444 - x[offset + 336]; t = (x[offset + 336] ^ x[offset + 352]) & (c >> 31); x[offset + 336] ^= t; x[offset + 352] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 384]) & (c >> 31); x[offset + 368] ^= t; x[offset + 384] ^= t; - c = 61444 - x[offset + 400]; t = (x[offset + 400] ^ x[offset + 416]) & (c >> 31); x[offset + 400] ^= t; x[offset + 416] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 448]) & (c >> 31); x[offset + 432] ^= t; x[offset + 448] ^= t; - c = 61444 - x[offset + 464]; t = (x[offset + 464] ^ x[offset + 480]) & (c >> 31); x[offset + 464] ^= t; x[offset + 480] ^= t; - c = 61444 - x[offset + 496]; t = (x[offset + 496] ^ x[offset + 512]) & (c >> 31); x[offset + 496] ^= t; x[offset + 512] ^= t; - c = 61444 - x[offset + 528]; t = (x[offset + 528] ^ x[offset + 544]) & (c >> 31); x[offset + 528] ^= t; x[offset + 544] ^= t; - c = 61444 - x[offset + 560]; t = (x[offset + 560] ^ x[offset + 576]) & (c >> 31); x[offset + 560] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 592]; t = (x[offset + 592] ^ x[offset + 608]) & (c >> 31); x[offset + 592] ^= t; x[offset + 608] ^= t; - c = 61444 - x[offset + 624]; t = (x[offset + 624] ^ x[offset + 640]) & (c >> 31); x[offset + 624] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 656]; t = (x[offset + 656] ^ x[offset + 672]) & (c >> 31); x[offset + 656] ^= t; x[offset + 672] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 704]) & (c >> 31); x[offset + 688] ^= t; x[offset + 704] ^= t; - c = 61444 - x[offset + 720]; t = (x[offset + 720] ^ x[offset + 736]) & (c >> 31); x[offset + 720] ^= t; x[offset + 736] ^= t; - c = 61444 - x[offset + 752]; t = (x[offset + 752] ^ x[offset + 768]) & (c >> 31); x[offset + 752] ^= t; x[offset + 768] ^= t; - c = 61444 - x[offset + 784]; t = (x[offset + 784] ^ x[offset + 800]) & (c >> 31); x[offset + 784] ^= t; x[offset + 800] ^= t; - c = 61444 - x[offset + 816]; t = (x[offset + 816] ^ x[offset + 832]) & (c >> 31); x[offset + 816] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 848]; t = (x[offset + 848] ^ x[offset + 864]) & (c >> 31); x[offset + 848] ^= t; x[offset + 864] ^= t; - c = 61444 - x[offset + 880]; t = (x[offset + 880] ^ x[offset + 896]) & (c >> 31); x[offset + 880] ^= t; x[offset + 896] ^= t; - c = 61444 - x[offset + 912]; t = (x[offset + 912] ^ x[offset + 928]) & (c >> 31); x[offset + 912] ^= t; x[offset + 928] ^= t; - c = 61444 - x[offset + 944]; t = (x[offset + 944] ^ x[offset + 960]) & (c >> 31); x[offset + 944] ^= t; x[offset + 960] ^= t; - c = 61444 - x[offset + 976]; t = (x[offset + 976] ^ x[offset + 992]) & (c >> 31); x[offset + 976] ^= t; x[offset + 992] ^= t; - c = 61444 - x[offset + 1024]; t = (x[offset + 1024] ^ x[offset + 1040]) & (c >> 31); x[offset + 1024] ^= t; x[offset + 1040] ^= t; - c = 61444 - x[offset + 1056]; t = (x[offset + 1056] ^ x[offset + 1072]) & (c >> 31); x[offset + 1056] ^= t; x[offset + 1072] ^= t; - c = 61444 - x[offset + 1024]; t = (x[offset + 1024] ^ x[offset + 1056]) & (c >> 31); x[offset + 1024] ^= t; x[offset + 1056] ^= t; - c = 61444 - x[offset + 1040]; t = (x[offset + 1040] ^ x[offset + 1072]) & (c >> 31); x[offset + 1040] ^= t; x[offset + 1072] ^= t; - c = 61444 - x[offset + 1040]; t = (x[offset + 1040] ^ x[offset + 1056]) & (c >> 31); x[offset + 1040] ^= t; x[offset + 1056] ^= t; - c = 61444 - x[offset + 1088]; t = (x[offset + 1088] ^ x[offset + 1104]) & (c >> 31); x[offset + 1088] ^= t; x[offset + 1104] ^= t; - c = 61444 - x[offset + 1120]; t = (x[offset + 1120] ^ x[offset + 1136]) & (c >> 31); x[offset + 1120] ^= t; x[offset + 1136] ^= t; - c = 61444 - x[offset + 1088]; t = (x[offset + 1088] ^ x[offset + 1120]) & (c >> 31); x[offset + 1088] ^= t; x[offset + 1120] ^= t; - c = 61444 - x[offset + 1104]; t = (x[offset + 1104] ^ x[offset + 1136]) & (c >> 31); x[offset + 1104] ^= t; x[offset + 1136] ^= t; - c = 61444 - x[offset + 1104]; t = (x[offset + 1104] ^ x[offset + 1120]) & (c >> 31); x[offset + 1104] ^= t; x[offset + 1120] ^= t; - c = 61444 - x[offset + 1024]; t = (x[offset + 1024] ^ x[offset + 1088]) & (c >> 31); x[offset + 1024] ^= t; x[offset + 1088] ^= t; - c = 61444 - x[offset + 1056]; t = (x[offset + 1056] ^ x[offset + 1120]) & (c >> 31); x[offset + 1056] ^= t; x[offset + 1120] ^= t; - c = 61444 - x[offset + 1056]; t = (x[offset + 1056] ^ x[offset + 1088]) & (c >> 31); x[offset + 1056] ^= t; x[offset + 1088] ^= t; - c = 61444 - x[offset + 1040]; t = (x[offset + 1040] ^ x[offset + 1104]) & (c >> 31); x[offset + 1040] ^= t; x[offset + 1104] ^= t; - c = 61444 - x[offset + 1072]; t = (x[offset + 1072] ^ x[offset + 1136]) & (c >> 31); x[offset + 1072] ^= t; x[offset + 1136] ^= t; - c = 61444 - x[offset + 1072]; t = (x[offset + 1072] ^ x[offset + 1104]) & (c >> 31); x[offset + 1072] ^= t; x[offset + 1104] ^= t; - c = 61444 - x[offset + 1040]; t = (x[offset + 1040] ^ x[offset + 1056]) & (c >> 31); x[offset + 1040] ^= t; x[offset + 1056] ^= t; - c = 61444 - x[offset + 1072]; t = (x[offset + 1072] ^ x[offset + 1088]) & (c >> 31); x[offset + 1072] ^= t; x[offset + 1088] ^= t; - c = 61444 - x[offset + 1104]; t = (x[offset + 1104] ^ x[offset + 1120]) & (c >> 31); x[offset + 1104] ^= t; x[offset + 1120] ^= t; - c = 61444 - x[offset + 1152]; t = (x[offset + 1152] ^ x[offset + 1168]) & (c >> 31); x[offset + 1152] ^= t; x[offset + 1168] ^= t; - c = 61444 - x[offset + 1184]; t = (x[offset + 1184] ^ x[offset + 1200]) & (c >> 31); x[offset + 1184] ^= t; x[offset + 1200] ^= t; - c = 61444 - x[offset + 1152]; t = (x[offset + 1152] ^ x[offset + 1184]) & (c >> 31); x[offset + 1152] ^= t; x[offset + 1184] ^= t; - c = 61444 - x[offset + 1168]; t = (x[offset + 1168] ^ x[offset + 1200]) & (c >> 31); x[offset + 1168] ^= t; x[offset + 1200] ^= t; - c = 61444 - x[offset + 1168]; t = (x[offset + 1168] ^ x[offset + 1184]) & (c >> 31); x[offset + 1168] ^= t; x[offset + 1184] ^= t; - c = 61444 - x[offset + 1216]; t = (x[offset + 1216] ^ x[offset + 1232]) & (c >> 31); x[offset + 1216] ^= t; x[offset + 1232] ^= t; - c = 61444 - x[offset + 1248]; t = (x[offset + 1248] ^ x[offset + 1264]) & (c >> 31); x[offset + 1248] ^= t; x[offset + 1264] ^= t; - c = 61444 - x[offset + 1216]; t = (x[offset + 1216] ^ x[offset + 1248]) & (c >> 31); x[offset + 1216] ^= t; x[offset + 1248] ^= t; - c = 61444 - x[offset + 1232]; t = (x[offset + 1232] ^ x[offset + 1264]) & (c >> 31); x[offset + 1232] ^= t; x[offset + 1264] ^= t; - c = 61444 - x[offset + 1232]; t = (x[offset + 1232] ^ x[offset + 1248]) & (c >> 31); x[offset + 1232] ^= t; x[offset + 1248] ^= t; - c = 61444 - x[offset + 1152]; t = (x[offset + 1152] ^ x[offset + 1216]) & (c >> 31); x[offset + 1152] ^= t; x[offset + 1216] ^= t; - c = 61444 - x[offset + 1184]; t = (x[offset + 1184] ^ x[offset + 1248]) & (c >> 31); x[offset + 1184] ^= t; x[offset + 1248] ^= t; - c = 61444 - x[offset + 1184]; t = (x[offset + 1184] ^ x[offset + 1216]) & (c >> 31); x[offset + 1184] ^= t; x[offset + 1216] ^= t; - c = 61444 - x[offset + 1168]; t = (x[offset + 1168] ^ x[offset + 1232]) & (c >> 31); x[offset + 1168] ^= t; x[offset + 1232] ^= t; - c = 61444 - x[offset + 1200]; t = (x[offset + 1200] ^ x[offset + 1264]) & (c >> 31); x[offset + 1200] ^= t; x[offset + 1264] ^= t; - c = 61444 - x[offset + 1200]; t = (x[offset + 1200] ^ x[offset + 1232]) & (c >> 31); x[offset + 1200] ^= t; x[offset + 1232] ^= t; - c = 61444 - x[offset + 1168]; t = (x[offset + 1168] ^ x[offset + 1184]) & (c >> 31); x[offset + 1168] ^= t; x[offset + 1184] ^= t; - c = 61444 - x[offset + 1200]; t = (x[offset + 1200] ^ x[offset + 1216]) & (c >> 31); x[offset + 1200] ^= t; x[offset + 1216] ^= t; - c = 61444 - x[offset + 1232]; t = (x[offset + 1232] ^ x[offset + 1248]) & (c >> 31); x[offset + 1232] ^= t; x[offset + 1248] ^= t; - c = 61444 - x[offset + 1024]; t = (x[offset + 1024] ^ x[offset + 1152]) & (c >> 31); x[offset + 1024] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 1088]; t = (x[offset + 1088] ^ x[offset + 1216]) & (c >> 31); x[offset + 1088] ^= t; x[offset + 1216] ^= t; - c = 61444 - x[offset + 1088]; t = (x[offset + 1088] ^ x[offset + 1152]) & (c >> 31); x[offset + 1088] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 1056]; t = (x[offset + 1056] ^ x[offset + 1184]) & (c >> 31); x[offset + 1056] ^= t; x[offset + 1184] ^= t; - c = 61444 - x[offset + 1120]; t = (x[offset + 1120] ^ x[offset + 1248]) & (c >> 31); x[offset + 1120] ^= t; x[offset + 1248] ^= t; - c = 61444 - x[offset + 1120]; t = (x[offset + 1120] ^ x[offset + 1184]) & (c >> 31); x[offset + 1120] ^= t; x[offset + 1184] ^= t; - c = 61444 - x[offset + 1056]; t = (x[offset + 1056] ^ x[offset + 1088]) & (c >> 31); x[offset + 1056] ^= t; x[offset + 1088] ^= t; - c = 61444 - x[offset + 1120]; t = (x[offset + 1120] ^ x[offset + 1152]) & (c >> 31); x[offset + 1120] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 1184]; t = (x[offset + 1184] ^ x[offset + 1216]) & (c >> 31); x[offset + 1184] ^= t; x[offset + 1216] ^= t; - c = 61444 - x[offset + 1040]; t = (x[offset + 1040] ^ x[offset + 1168]) & (c >> 31); x[offset + 1040] ^= t; x[offset + 1168] ^= t; - c = 61444 - x[offset + 1104]; t = (x[offset + 1104] ^ x[offset + 1232]) & (c >> 31); x[offset + 1104] ^= t; x[offset + 1232] ^= t; - c = 61444 - x[offset + 1104]; t = (x[offset + 1104] ^ x[offset + 1168]) & (c >> 31); x[offset + 1104] ^= t; x[offset + 1168] ^= t; - c = 61444 - x[offset + 1072]; t = (x[offset + 1072] ^ x[offset + 1200]) & (c >> 31); x[offset + 1072] ^= t; x[offset + 1200] ^= t; - c = 61444 - x[offset + 1136]; t = (x[offset + 1136] ^ x[offset + 1264]) & (c >> 31); x[offset + 1136] ^= t; x[offset + 1264] ^= t; - c = 61444 - x[offset + 1136]; t = (x[offset + 1136] ^ x[offset + 1200]) & (c >> 31); x[offset + 1136] ^= t; x[offset + 1200] ^= t; - c = 61444 - x[offset + 1072]; t = (x[offset + 1072] ^ x[offset + 1104]) & (c >> 31); x[offset + 1072] ^= t; x[offset + 1104] ^= t; - c = 61444 - x[offset + 1136]; t = (x[offset + 1136] ^ x[offset + 1168]) & (c >> 31); x[offset + 1136] ^= t; x[offset + 1168] ^= t; - c = 61444 - x[offset + 1200]; t = (x[offset + 1200] ^ x[offset + 1232]) & (c >> 31); x[offset + 1200] ^= t; x[offset + 1232] ^= t; - c = 61444 - x[offset + 1040]; t = (x[offset + 1040] ^ x[offset + 1056]) & (c >> 31); x[offset + 1040] ^= t; x[offset + 1056] ^= t; - c = 61444 - x[offset + 1072]; t = (x[offset + 1072] ^ x[offset + 1088]) & (c >> 31); x[offset + 1072] ^= t; x[offset + 1088] ^= t; - c = 61444 - x[offset + 1104]; t = (x[offset + 1104] ^ x[offset + 1120]) & (c >> 31); x[offset + 1104] ^= t; x[offset + 1120] ^= t; - c = 61444 - x[offset + 1136]; t = (x[offset + 1136] ^ x[offset + 1152]) & (c >> 31); x[offset + 1136] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 1168]; t = (x[offset + 1168] ^ x[offset + 1184]) & (c >> 31); x[offset + 1168] ^= t; x[offset + 1184] ^= t; - c = 61444 - x[offset + 1200]; t = (x[offset + 1200] ^ x[offset + 1216]) & (c >> 31); x[offset + 1200] ^= t; x[offset + 1216] ^= t; - c = 61444 - x[offset + 1232]; t = (x[offset + 1232] ^ x[offset + 1248]) & (c >> 31); x[offset + 1232] ^= t; x[offset + 1248] ^= t; - c = 61444 - x[offset + 1280]; t = (x[offset + 1280] ^ x[offset + 1296]) & (c >> 31); x[offset + 1280] ^= t; x[offset + 1296] ^= t; - c = 61444 - x[offset + 1312]; t = (x[offset + 1312] ^ x[offset + 1328]) & (c >> 31); x[offset + 1312] ^= t; x[offset + 1328] ^= t; - c = 61444 - x[offset + 1280]; t = (x[offset + 1280] ^ x[offset + 1312]) & (c >> 31); x[offset + 1280] ^= t; x[offset + 1312] ^= t; - c = 61444 - x[offset + 1296]; t = (x[offset + 1296] ^ x[offset + 1328]) & (c >> 31); x[offset + 1296] ^= t; x[offset + 1328] ^= t; - c = 61444 - x[offset + 1296]; t = (x[offset + 1296] ^ x[offset + 1312]) & (c >> 31); x[offset + 1296] ^= t; x[offset + 1312] ^= t; - c = 61444 - x[offset + 1296]; t = (x[offset + 1296] ^ x[offset + 1312]) & (c >> 31); x[offset + 1296] ^= t; x[offset + 1312] ^= t; - c = 61444 - x[offset + 1296]; t = (x[offset + 1296] ^ x[offset + 1312]) & (c >> 31); x[offset + 1296] ^= t; x[offset + 1312] ^= t; - c = 61444 - x[offset + 1024]; t = (x[offset + 1024] ^ x[offset + 1280]) & (c >> 31); x[offset + 1024] ^= t; x[offset + 1280] ^= t; - c = 61444 - x[offset + 1152]; t = (x[offset + 1152] ^ x[offset + 1280]) & (c >> 31); x[offset + 1152] ^= t; x[offset + 1280] ^= t; - c = 61444 - x[offset + 1088]; t = (x[offset + 1088] ^ x[offset + 1152]) & (c >> 31); x[offset + 1088] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 1216]; t = (x[offset + 1216] ^ x[offset + 1280]) & (c >> 31); x[offset + 1216] ^= t; x[offset + 1280] ^= t; - c = 61444 - x[offset + 1056]; t = (x[offset + 1056] ^ x[offset + 1312]) & (c >> 31); x[offset + 1056] ^= t; x[offset + 1312] ^= t; - c = 61444 - x[offset + 1184]; t = (x[offset + 1184] ^ x[offset + 1312]) & (c >> 31); x[offset + 1184] ^= t; x[offset + 1312] ^= t; - c = 61444 - x[offset + 1120]; t = (x[offset + 1120] ^ x[offset + 1184]) & (c >> 31); x[offset + 1120] ^= t; x[offset + 1184] ^= t; - c = 61444 - x[offset + 1248]; t = (x[offset + 1248] ^ x[offset + 1312]) & (c >> 31); x[offset + 1248] ^= t; x[offset + 1312] ^= t; - c = 61444 - x[offset + 1056]; t = (x[offset + 1056] ^ x[offset + 1088]) & (c >> 31); x[offset + 1056] ^= t; x[offset + 1088] ^= t; - c = 61444 - x[offset + 1120]; t = (x[offset + 1120] ^ x[offset + 1152]) & (c >> 31); x[offset + 1120] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 1184]; t = (x[offset + 1184] ^ x[offset + 1216]) & (c >> 31); x[offset + 1184] ^= t; x[offset + 1216] ^= t; - c = 61444 - x[offset + 1248]; t = (x[offset + 1248] ^ x[offset + 1280]) & (c >> 31); x[offset + 1248] ^= t; x[offset + 1280] ^= t; - c = 61444 - x[offset + 1040]; t = (x[offset + 1040] ^ x[offset + 1296]) & (c >> 31); x[offset + 1040] ^= t; x[offset + 1296] ^= t; - c = 61444 - x[offset + 1168]; t = (x[offset + 1168] ^ x[offset + 1296]) & (c >> 31); x[offset + 1168] ^= t; x[offset + 1296] ^= t; - c = 61444 - x[offset + 1104]; t = (x[offset + 1104] ^ x[offset + 1168]) & (c >> 31); x[offset + 1104] ^= t; x[offset + 1168] ^= t; - c = 61444 - x[offset + 1232]; t = (x[offset + 1232] ^ x[offset + 1296]) & (c >> 31); x[offset + 1232] ^= t; x[offset + 1296] ^= t; - c = 61444 - x[offset + 1072]; t = (x[offset + 1072] ^ x[offset + 1328]) & (c >> 31); x[offset + 1072] ^= t; x[offset + 1328] ^= t; - c = 61444 - x[offset + 1200]; t = (x[offset + 1200] ^ x[offset + 1328]) & (c >> 31); x[offset + 1200] ^= t; x[offset + 1328] ^= t; - c = 61444 - x[offset + 1136]; t = (x[offset + 1136] ^ x[offset + 1200]) & (c >> 31); x[offset + 1136] ^= t; x[offset + 1200] ^= t; - c = 61444 - x[offset + 1264]; t = (x[offset + 1264] ^ x[offset + 1328]) & (c >> 31); x[offset + 1264] ^= t; x[offset + 1328] ^= t; - c = 61444 - x[offset + 1072]; t = (x[offset + 1072] ^ x[offset + 1104]) & (c >> 31); x[offset + 1072] ^= t; x[offset + 1104] ^= t; - c = 61444 - x[offset + 1136]; t = (x[offset + 1136] ^ x[offset + 1168]) & (c >> 31); x[offset + 1136] ^= t; x[offset + 1168] ^= t; - c = 61444 - x[offset + 1200]; t = (x[offset + 1200] ^ x[offset + 1232]) & (c >> 31); x[offset + 1200] ^= t; x[offset + 1232] ^= t; - c = 61444 - x[offset + 1264]; t = (x[offset + 1264] ^ x[offset + 1296]) & (c >> 31); x[offset + 1264] ^= t; x[offset + 1296] ^= t; - c = 61444 - x[offset + 1040]; t = (x[offset + 1040] ^ x[offset + 1056]) & (c >> 31); x[offset + 1040] ^= t; x[offset + 1056] ^= t; - c = 61444 - x[offset + 1072]; t = (x[offset + 1072] ^ x[offset + 1088]) & (c >> 31); x[offset + 1072] ^= t; x[offset + 1088] ^= t; - c = 61444 - x[offset + 1104]; t = (x[offset + 1104] ^ x[offset + 1120]) & (c >> 31); x[offset + 1104] ^= t; x[offset + 1120] ^= t; - c = 61444 - x[offset + 1136]; t = (x[offset + 1136] ^ x[offset + 1152]) & (c >> 31); x[offset + 1136] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 1168]; t = (x[offset + 1168] ^ x[offset + 1184]) & (c >> 31); x[offset + 1168] ^= t; x[offset + 1184] ^= t; - c = 61444 - x[offset + 1200]; t = (x[offset + 1200] ^ x[offset + 1216]) & (c >> 31); x[offset + 1200] ^= t; x[offset + 1216] ^= t; - c = 61444 - x[offset + 1232]; t = (x[offset + 1232] ^ x[offset + 1248]) & (c >> 31); x[offset + 1232] ^= t; x[offset + 1248] ^= t; - c = 61444 - x[offset + 1264]; t = (x[offset + 1264] ^ x[offset + 1280]) & (c >> 31); x[offset + 1264] ^= t; x[offset + 1280] ^= t; - c = 61444 - x[offset + 1296]; t = (x[offset + 1296] ^ x[offset + 1312]) & (c >> 31); x[offset + 1296] ^= t; x[offset + 1312] ^= t; - c = 61444 - x[offset + 1152]; t = (x[offset + 1152] ^ x[offset + 1280]) & (c >> 31); x[offset + 1152] ^= t; x[offset + 1280] ^= t; - c = 61444 - x[offset + 1088]; t = (x[offset + 1088] ^ x[offset + 1152]) & (c >> 31); x[offset + 1088] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 1216]; t = (x[offset + 1216] ^ x[offset + 1280]) & (c >> 31); x[offset + 1216] ^= t; x[offset + 1280] ^= t; - c = 61444 - x[offset + 1184]; t = (x[offset + 1184] ^ x[offset + 1312]) & (c >> 31); x[offset + 1184] ^= t; x[offset + 1312] ^= t; - c = 61444 - x[offset + 1120]; t = (x[offset + 1120] ^ x[offset + 1184]) & (c >> 31); x[offset + 1120] ^= t; x[offset + 1184] ^= t; - c = 61444 - x[offset + 1248]; t = (x[offset + 1248] ^ x[offset + 1312]) & (c >> 31); x[offset + 1248] ^= t; x[offset + 1312] ^= t; - c = 61444 - x[offset + 1056]; t = (x[offset + 1056] ^ x[offset + 1088]) & (c >> 31); x[offset + 1056] ^= t; x[offset + 1088] ^= t; - c = 61444 - x[offset + 1120]; t = (x[offset + 1120] ^ x[offset + 1152]) & (c >> 31); x[offset + 1120] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 1184]; t = (x[offset + 1184] ^ x[offset + 1216]) & (c >> 31); x[offset + 1184] ^= t; x[offset + 1216] ^= t; - c = 61444 - x[offset + 1248]; t = (x[offset + 1248] ^ x[offset + 1280]) & (c >> 31); x[offset + 1248] ^= t; x[offset + 1280] ^= t; - c = 61444 - x[offset + 1168]; t = (x[offset + 1168] ^ x[offset + 1296]) & (c >> 31); x[offset + 1168] ^= t; x[offset + 1296] ^= t; - c = 61444 - x[offset + 1104]; t = (x[offset + 1104] ^ x[offset + 1168]) & (c >> 31); x[offset + 1104] ^= t; x[offset + 1168] ^= t; - c = 61444 - x[offset + 1232]; t = (x[offset + 1232] ^ x[offset + 1296]) & (c >> 31); x[offset + 1232] ^= t; x[offset + 1296] ^= t; - c = 61444 - x[offset + 1200]; t = (x[offset + 1200] ^ x[offset + 1328]) & (c >> 31); x[offset + 1200] ^= t; x[offset + 1328] ^= t; - c = 61444 - x[offset + 1136]; t = (x[offset + 1136] ^ x[offset + 1200]) & (c >> 31); x[offset + 1136] ^= t; x[offset + 1200] ^= t; - c = 61444 - x[offset + 1264]; t = (x[offset + 1264] ^ x[offset + 1328]) & (c >> 31); x[offset + 1264] ^= t; x[offset + 1328] ^= t; - c = 61444 - x[offset + 1072]; t = (x[offset + 1072] ^ x[offset + 1104]) & (c >> 31); x[offset + 1072] ^= t; x[offset + 1104] ^= t; - c = 61444 - x[offset + 1136]; t = (x[offset + 1136] ^ x[offset + 1168]) & (c >> 31); x[offset + 1136] ^= t; x[offset + 1168] ^= t; - c = 61444 - x[offset + 1200]; t = (x[offset + 1200] ^ x[offset + 1232]) & (c >> 31); x[offset + 1200] ^= t; x[offset + 1232] ^= t; - c = 61444 - x[offset + 1264]; t = (x[offset + 1264] ^ x[offset + 1296]) & (c >> 31); x[offset + 1264] ^= t; x[offset + 1296] ^= t; - c = 61444 - x[offset + 1040]; t = (x[offset + 1040] ^ x[offset + 1056]) & (c >> 31); x[offset + 1040] ^= t; x[offset + 1056] ^= t; - c = 61444 - x[offset + 1072]; t = (x[offset + 1072] ^ x[offset + 1088]) & (c >> 31); x[offset + 1072] ^= t; x[offset + 1088] ^= t; - c = 61444 - x[offset + 1104]; t = (x[offset + 1104] ^ x[offset + 1120]) & (c >> 31); x[offset + 1104] ^= t; x[offset + 1120] ^= t; - c = 61444 - x[offset + 1136]; t = (x[offset + 1136] ^ x[offset + 1152]) & (c >> 31); x[offset + 1136] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 1168]; t = (x[offset + 1168] ^ x[offset + 1184]) & (c >> 31); x[offset + 1168] ^= t; x[offset + 1184] ^= t; - c = 61444 - x[offset + 1200]; t = (x[offset + 1200] ^ x[offset + 1216]) & (c >> 31); x[offset + 1200] ^= t; x[offset + 1216] ^= t; - c = 61444 - x[offset + 1232]; t = (x[offset + 1232] ^ x[offset + 1248]) & (c >> 31); x[offset + 1232] ^= t; x[offset + 1248] ^= t; - c = 61444 - x[offset + 1264]; t = (x[offset + 1264] ^ x[offset + 1280]) & (c >> 31); x[offset + 1264] ^= t; x[offset + 1280] ^= t; - c = 61444 - x[offset + 1296]; t = (x[offset + 1296] ^ x[offset + 1312]) & (c >> 31); x[offset + 1296] ^= t; x[offset + 1312] ^= t; - c = 61444 - x[offset + 0]; t = (x[offset + 0] ^ x[offset + 1024]) & (c >> 31); x[offset + 0] ^= t; x[offset + 1024] ^= t; - c = 61444 - x[offset + 512]; t = (x[offset + 512] ^ x[offset + 1024]) & (c >> 31); x[offset + 512] ^= t; x[offset + 1024] ^= t; - c = 61444 - x[offset + 256]; t = (x[offset + 256] ^ x[offset + 1280]) & (c >> 31); x[offset + 256] ^= t; x[offset + 1280] ^= t; - c = 61444 - x[offset + 768]; t = (x[offset + 768] ^ x[offset + 1280]) & (c >> 31); x[offset + 768] ^= t; x[offset + 1280] ^= t; - c = 61444 - x[offset + 256]; t = (x[offset + 256] ^ x[offset + 512]) & (c >> 31); x[offset + 256] ^= t; x[offset + 512] ^= t; - c = 61444 - x[offset + 768]; t = (x[offset + 768] ^ x[offset + 1024]) & (c >> 31); x[offset + 768] ^= t; x[offset + 1024] ^= t; - c = 61444 - x[offset + 128]; t = (x[offset + 128] ^ x[offset + 1152]) & (c >> 31); x[offset + 128] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 640]; t = (x[offset + 640] ^ x[offset + 1152]) & (c >> 31); x[offset + 640] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 384]; t = (x[offset + 384] ^ x[offset + 640]) & (c >> 31); x[offset + 384] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 896]; t = (x[offset + 896] ^ x[offset + 1152]) & (c >> 31); x[offset + 896] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 128]; t = (x[offset + 128] ^ x[offset + 256]) & (c >> 31); x[offset + 128] ^= t; x[offset + 256] ^= t; - c = 61444 - x[offset + 384]; t = (x[offset + 384] ^ x[offset + 512]) & (c >> 31); x[offset + 384] ^= t; x[offset + 512] ^= t; - c = 61444 - x[offset + 640]; t = (x[offset + 640] ^ x[offset + 768]) & (c >> 31); x[offset + 640] ^= t; x[offset + 768] ^= t; - c = 61444 - x[offset + 896]; t = (x[offset + 896] ^ x[offset + 1024]) & (c >> 31); x[offset + 896] ^= t; x[offset + 1024] ^= t; - c = 61444 - x[offset + 1152]; t = (x[offset + 1152] ^ x[offset + 1280]) & (c >> 31); x[offset + 1152] ^= t; x[offset + 1280] ^= t; - c = 61444 - x[offset + 64]; t = (x[offset + 64] ^ x[offset + 1088]) & (c >> 31); x[offset + 64] ^= t; x[offset + 1088] ^= t; - c = 61444 - x[offset + 576]; t = (x[offset + 576] ^ x[offset + 1088]) & (c >> 31); x[offset + 576] ^= t; x[offset + 1088] ^= t; - c = 61444 - x[offset + 320]; t = (x[offset + 320] ^ x[offset + 576]) & (c >> 31); x[offset + 320] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 832]; t = (x[offset + 832] ^ x[offset + 1088]) & (c >> 31); x[offset + 832] ^= t; x[offset + 1088] ^= t; - c = 61444 - x[offset + 192]; t = (x[offset + 192] ^ x[offset + 1216]) & (c >> 31); x[offset + 192] ^= t; x[offset + 1216] ^= t; - c = 61444 - x[offset + 704]; t = (x[offset + 704] ^ x[offset + 1216]) & (c >> 31); x[offset + 704] ^= t; x[offset + 1216] ^= t; - c = 61444 - x[offset + 448]; t = (x[offset + 448] ^ x[offset + 704]) & (c >> 31); x[offset + 448] ^= t; x[offset + 704] ^= t; - c = 61444 - x[offset + 960]; t = (x[offset + 960] ^ x[offset + 1216]) & (c >> 31); x[offset + 960] ^= t; x[offset + 1216] ^= t; - c = 61444 - x[offset + 192]; t = (x[offset + 192] ^ x[offset + 320]) & (c >> 31); x[offset + 192] ^= t; x[offset + 320] ^= t; - c = 61444 - x[offset + 448]; t = (x[offset + 448] ^ x[offset + 576]) & (c >> 31); x[offset + 448] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 704]; t = (x[offset + 704] ^ x[offset + 832]) & (c >> 31); x[offset + 704] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 960]; t = (x[offset + 960] ^ x[offset + 1088]) & (c >> 31); x[offset + 960] ^= t; x[offset + 1088] ^= t; - c = 61444 - x[offset + 64]; t = (x[offset + 64] ^ x[offset + 128]) & (c >> 31); x[offset + 64] ^= t; x[offset + 128] ^= t; - c = 61444 - x[offset + 192]; t = (x[offset + 192] ^ x[offset + 256]) & (c >> 31); x[offset + 192] ^= t; x[offset + 256] ^= t; - c = 61444 - x[offset + 320]; t = (x[offset + 320] ^ x[offset + 384]) & (c >> 31); x[offset + 320] ^= t; x[offset + 384] ^= t; - c = 61444 - x[offset + 448]; t = (x[offset + 448] ^ x[offset + 512]) & (c >> 31); x[offset + 448] ^= t; x[offset + 512] ^= t; - c = 61444 - x[offset + 576]; t = (x[offset + 576] ^ x[offset + 640]) & (c >> 31); x[offset + 576] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 704]; t = (x[offset + 704] ^ x[offset + 768]) & (c >> 31); x[offset + 704] ^= t; x[offset + 768] ^= t; - c = 61444 - x[offset + 832]; t = (x[offset + 832] ^ x[offset + 896]) & (c >> 31); x[offset + 832] ^= t; x[offset + 896] ^= t; - c = 61444 - x[offset + 960]; t = (x[offset + 960] ^ x[offset + 1024]) & (c >> 31); x[offset + 960] ^= t; x[offset + 1024] ^= t; - c = 61444 - x[offset + 1088]; t = (x[offset + 1088] ^ x[offset + 1152]) & (c >> 31); x[offset + 1088] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 1216]; t = (x[offset + 1216] ^ x[offset + 1280]) & (c >> 31); x[offset + 1216] ^= t; x[offset + 1280] ^= t; - c = 61444 - x[offset + 32]; t = (x[offset + 32] ^ x[offset + 1056]) & (c >> 31); x[offset + 32] ^= t; x[offset + 1056] ^= t; - c = 61444 - x[offset + 544]; t = (x[offset + 544] ^ x[offset + 1056]) & (c >> 31); x[offset + 544] ^= t; x[offset + 1056] ^= t; - c = 61444 - x[offset + 288]; t = (x[offset + 288] ^ x[offset + 1312]) & (c >> 31); x[offset + 288] ^= t; x[offset + 1312] ^= t; - c = 61444 - x[offset + 800]; t = (x[offset + 800] ^ x[offset + 1312]) & (c >> 31); x[offset + 800] ^= t; x[offset + 1312] ^= t; - c = 61444 - x[offset + 288]; t = (x[offset + 288] ^ x[offset + 544]) & (c >> 31); x[offset + 288] ^= t; x[offset + 544] ^= t; - c = 61444 - x[offset + 800]; t = (x[offset + 800] ^ x[offset + 1056]) & (c >> 31); x[offset + 800] ^= t; x[offset + 1056] ^= t; - c = 61444 - x[offset + 160]; t = (x[offset + 160] ^ x[offset + 1184]) & (c >> 31); x[offset + 160] ^= t; x[offset + 1184] ^= t; - c = 61444 - x[offset + 672]; t = (x[offset + 672] ^ x[offset + 1184]) & (c >> 31); x[offset + 672] ^= t; x[offset + 1184] ^= t; - c = 61444 - x[offset + 416]; t = (x[offset + 416] ^ x[offset + 672]) & (c >> 31); x[offset + 416] ^= t; x[offset + 672] ^= t; - c = 61444 - x[offset + 928]; t = (x[offset + 928] ^ x[offset + 1184]) & (c >> 31); x[offset + 928] ^= t; x[offset + 1184] ^= t; - c = 61444 - x[offset + 160]; t = (x[offset + 160] ^ x[offset + 288]) & (c >> 31); x[offset + 160] ^= t; x[offset + 288] ^= t; - c = 61444 - x[offset + 416]; t = (x[offset + 416] ^ x[offset + 544]) & (c >> 31); x[offset + 416] ^= t; x[offset + 544] ^= t; - c = 61444 - x[offset + 672]; t = (x[offset + 672] ^ x[offset + 800]) & (c >> 31); x[offset + 672] ^= t; x[offset + 800] ^= t; - c = 61444 - x[offset + 928]; t = (x[offset + 928] ^ x[offset + 1056]) & (c >> 31); x[offset + 928] ^= t; x[offset + 1056] ^= t; - c = 61444 - x[offset + 1184]; t = (x[offset + 1184] ^ x[offset + 1312]) & (c >> 31); x[offset + 1184] ^= t; x[offset + 1312] ^= t; - c = 61444 - x[offset + 96]; t = (x[offset + 96] ^ x[offset + 1120]) & (c >> 31); x[offset + 96] ^= t; x[offset + 1120] ^= t; - c = 61444 - x[offset + 608]; t = (x[offset + 608] ^ x[offset + 1120]) & (c >> 31); x[offset + 608] ^= t; x[offset + 1120] ^= t; - c = 61444 - x[offset + 352]; t = (x[offset + 352] ^ x[offset + 608]) & (c >> 31); x[offset + 352] ^= t; x[offset + 608] ^= t; - c = 61444 - x[offset + 864]; t = (x[offset + 864] ^ x[offset + 1120]) & (c >> 31); x[offset + 864] ^= t; x[offset + 1120] ^= t; - c = 61444 - x[offset + 224]; t = (x[offset + 224] ^ x[offset + 1248]) & (c >> 31); x[offset + 224] ^= t; x[offset + 1248] ^= t; - c = 61444 - x[offset + 736]; t = (x[offset + 736] ^ x[offset + 1248]) & (c >> 31); x[offset + 736] ^= t; x[offset + 1248] ^= t; - c = 61444 - x[offset + 480]; t = (x[offset + 480] ^ x[offset + 736]) & (c >> 31); x[offset + 480] ^= t; x[offset + 736] ^= t; - c = 61444 - x[offset + 992]; t = (x[offset + 992] ^ x[offset + 1248]) & (c >> 31); x[offset + 992] ^= t; x[offset + 1248] ^= t; - c = 61444 - x[offset + 224]; t = (x[offset + 224] ^ x[offset + 352]) & (c >> 31); x[offset + 224] ^= t; x[offset + 352] ^= t; - c = 61444 - x[offset + 480]; t = (x[offset + 480] ^ x[offset + 608]) & (c >> 31); x[offset + 480] ^= t; x[offset + 608] ^= t; - c = 61444 - x[offset + 736]; t = (x[offset + 736] ^ x[offset + 864]) & (c >> 31); x[offset + 736] ^= t; x[offset + 864] ^= t; - c = 61444 - x[offset + 992]; t = (x[offset + 992] ^ x[offset + 1120]) & (c >> 31); x[offset + 992] ^= t; x[offset + 1120] ^= t; - c = 61444 - x[offset + 96]; t = (x[offset + 96] ^ x[offset + 160]) & (c >> 31); x[offset + 96] ^= t; x[offset + 160] ^= t; - c = 61444 - x[offset + 224]; t = (x[offset + 224] ^ x[offset + 288]) & (c >> 31); x[offset + 224] ^= t; x[offset + 288] ^= t; - c = 61444 - x[offset + 352]; t = (x[offset + 352] ^ x[offset + 416]) & (c >> 31); x[offset + 352] ^= t; x[offset + 416] ^= t; - c = 61444 - x[offset + 480]; t = (x[offset + 480] ^ x[offset + 544]) & (c >> 31); x[offset + 480] ^= t; x[offset + 544] ^= t; - c = 61444 - x[offset + 608]; t = (x[offset + 608] ^ x[offset + 672]) & (c >> 31); x[offset + 608] ^= t; x[offset + 672] ^= t; - c = 61444 - x[offset + 736]; t = (x[offset + 736] ^ x[offset + 800]) & (c >> 31); x[offset + 736] ^= t; x[offset + 800] ^= t; - c = 61444 - x[offset + 864]; t = (x[offset + 864] ^ x[offset + 928]) & (c >> 31); x[offset + 864] ^= t; x[offset + 928] ^= t; - c = 61444 - x[offset + 992]; t = (x[offset + 992] ^ x[offset + 1056]) & (c >> 31); x[offset + 992] ^= t; x[offset + 1056] ^= t; - c = 61444 - x[offset + 1120]; t = (x[offset + 1120] ^ x[offset + 1184]) & (c >> 31); x[offset + 1120] ^= t; x[offset + 1184] ^= t; - c = 61444 - x[offset + 1248]; t = (x[offset + 1248] ^ x[offset + 1312]) & (c >> 31); x[offset + 1248] ^= t; x[offset + 1312] ^= t; - c = 61444 - x[offset + 32]; t = (x[offset + 32] ^ x[offset + 64]) & (c >> 31); x[offset + 32] ^= t; x[offset + 64] ^= t; - c = 61444 - x[offset + 96]; t = (x[offset + 96] ^ x[offset + 128]) & (c >> 31); x[offset + 96] ^= t; x[offset + 128] ^= t; - c = 61444 - x[offset + 160]; t = (x[offset + 160] ^ x[offset + 192]) & (c >> 31); x[offset + 160] ^= t; x[offset + 192] ^= t; - c = 61444 - x[offset + 224]; t = (x[offset + 224] ^ x[offset + 256]) & (c >> 31); x[offset + 224] ^= t; x[offset + 256] ^= t; - c = 61444 - x[offset + 288]; t = (x[offset + 288] ^ x[offset + 320]) & (c >> 31); x[offset + 288] ^= t; x[offset + 320] ^= t; - c = 61444 - x[offset + 352]; t = (x[offset + 352] ^ x[offset + 384]) & (c >> 31); x[offset + 352] ^= t; x[offset + 384] ^= t; - c = 61444 - x[offset + 416]; t = (x[offset + 416] ^ x[offset + 448]) & (c >> 31); x[offset + 416] ^= t; x[offset + 448] ^= t; - c = 61444 - x[offset + 480]; t = (x[offset + 480] ^ x[offset + 512]) & (c >> 31); x[offset + 480] ^= t; x[offset + 512] ^= t; - c = 61444 - x[offset + 544]; t = (x[offset + 544] ^ x[offset + 576]) & (c >> 31); x[offset + 544] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 608]; t = (x[offset + 608] ^ x[offset + 640]) & (c >> 31); x[offset + 608] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 672]; t = (x[offset + 672] ^ x[offset + 704]) & (c >> 31); x[offset + 672] ^= t; x[offset + 704] ^= t; - c = 61444 - x[offset + 736]; t = (x[offset + 736] ^ x[offset + 768]) & (c >> 31); x[offset + 736] ^= t; x[offset + 768] ^= t; - c = 61444 - x[offset + 800]; t = (x[offset + 800] ^ x[offset + 832]) & (c >> 31); x[offset + 800] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 864]; t = (x[offset + 864] ^ x[offset + 896]) & (c >> 31); x[offset + 864] ^= t; x[offset + 896] ^= t; - c = 61444 - x[offset + 928]; t = (x[offset + 928] ^ x[offset + 960]) & (c >> 31); x[offset + 928] ^= t; x[offset + 960] ^= t; - c = 61444 - x[offset + 992]; t = (x[offset + 992] ^ x[offset + 1024]) & (c >> 31); x[offset + 992] ^= t; x[offset + 1024] ^= t; - c = 61444 - x[offset + 1056]; t = (x[offset + 1056] ^ x[offset + 1088]) & (c >> 31); x[offset + 1056] ^= t; x[offset + 1088] ^= t; - c = 61444 - x[offset + 1120]; t = (x[offset + 1120] ^ x[offset + 1152]) & (c >> 31); x[offset + 1120] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 1184]; t = (x[offset + 1184] ^ x[offset + 1216]) & (c >> 31); x[offset + 1184] ^= t; x[offset + 1216] ^= t; - c = 61444 - x[offset + 1248]; t = (x[offset + 1248] ^ x[offset + 1280]) & (c >> 31); x[offset + 1248] ^= t; x[offset + 1280] ^= t; - c = 61444 - x[offset + 16]; t = (x[offset + 16] ^ x[offset + 1040]) & (c >> 31); x[offset + 16] ^= t; x[offset + 1040] ^= t; - c = 61444 - x[offset + 528]; t = (x[offset + 528] ^ x[offset + 1040]) & (c >> 31); x[offset + 528] ^= t; x[offset + 1040] ^= t; - c = 61444 - x[offset + 272]; t = (x[offset + 272] ^ x[offset + 1296]) & (c >> 31); x[offset + 272] ^= t; x[offset + 1296] ^= t; - c = 61444 - x[offset + 784]; t = (x[offset + 784] ^ x[offset + 1296]) & (c >> 31); x[offset + 784] ^= t; x[offset + 1296] ^= t; - c = 61444 - x[offset + 272]; t = (x[offset + 272] ^ x[offset + 528]) & (c >> 31); x[offset + 272] ^= t; x[offset + 528] ^= t; - c = 61444 - x[offset + 784]; t = (x[offset + 784] ^ x[offset + 1040]) & (c >> 31); x[offset + 784] ^= t; x[offset + 1040] ^= t; - c = 61444 - x[offset + 144]; t = (x[offset + 144] ^ x[offset + 1168]) & (c >> 31); x[offset + 144] ^= t; x[offset + 1168] ^= t; - c = 61444 - x[offset + 656]; t = (x[offset + 656] ^ x[offset + 1168]) & (c >> 31); x[offset + 656] ^= t; x[offset + 1168] ^= t; - c = 61444 - x[offset + 400]; t = (x[offset + 400] ^ x[offset + 656]) & (c >> 31); x[offset + 400] ^= t; x[offset + 656] ^= t; - c = 61444 - x[offset + 912]; t = (x[offset + 912] ^ x[offset + 1168]) & (c >> 31); x[offset + 912] ^= t; x[offset + 1168] ^= t; - c = 61444 - x[offset + 144]; t = (x[offset + 144] ^ x[offset + 272]) & (c >> 31); x[offset + 144] ^= t; x[offset + 272] ^= t; - c = 61444 - x[offset + 400]; t = (x[offset + 400] ^ x[offset + 528]) & (c >> 31); x[offset + 400] ^= t; x[offset + 528] ^= t; - c = 61444 - x[offset + 656]; t = (x[offset + 656] ^ x[offset + 784]) & (c >> 31); x[offset + 656] ^= t; x[offset + 784] ^= t; - c = 61444 - x[offset + 912]; t = (x[offset + 912] ^ x[offset + 1040]) & (c >> 31); x[offset + 912] ^= t; x[offset + 1040] ^= t; - c = 61444 - x[offset + 1168]; t = (x[offset + 1168] ^ x[offset + 1296]) & (c >> 31); x[offset + 1168] ^= t; x[offset + 1296] ^= t; - c = 61444 - x[offset + 80]; t = (x[offset + 80] ^ x[offset + 1104]) & (c >> 31); x[offset + 80] ^= t; x[offset + 1104] ^= t; - c = 61444 - x[offset + 592]; t = (x[offset + 592] ^ x[offset + 1104]) & (c >> 31); x[offset + 592] ^= t; x[offset + 1104] ^= t; - c = 61444 - x[offset + 336]; t = (x[offset + 336] ^ x[offset + 592]) & (c >> 31); x[offset + 336] ^= t; x[offset + 592] ^= t; - c = 61444 - x[offset + 848]; t = (x[offset + 848] ^ x[offset + 1104]) & (c >> 31); x[offset + 848] ^= t; x[offset + 1104] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 1232]) & (c >> 31); x[offset + 208] ^= t; x[offset + 1232] ^= t; - c = 61444 - x[offset + 720]; t = (x[offset + 720] ^ x[offset + 1232]) & (c >> 31); x[offset + 720] ^= t; x[offset + 1232] ^= t; - c = 61444 - x[offset + 464]; t = (x[offset + 464] ^ x[offset + 720]) & (c >> 31); x[offset + 464] ^= t; x[offset + 720] ^= t; - c = 61444 - x[offset + 976]; t = (x[offset + 976] ^ x[offset + 1232]) & (c >> 31); x[offset + 976] ^= t; x[offset + 1232] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 336]) & (c >> 31); x[offset + 208] ^= t; x[offset + 336] ^= t; - c = 61444 - x[offset + 464]; t = (x[offset + 464] ^ x[offset + 592]) & (c >> 31); x[offset + 464] ^= t; x[offset + 592] ^= t; - c = 61444 - x[offset + 720]; t = (x[offset + 720] ^ x[offset + 848]) & (c >> 31); x[offset + 720] ^= t; x[offset + 848] ^= t; - c = 61444 - x[offset + 976]; t = (x[offset + 976] ^ x[offset + 1104]) & (c >> 31); x[offset + 976] ^= t; x[offset + 1104] ^= t; - c = 61444 - x[offset + 80]; t = (x[offset + 80] ^ x[offset + 144]) & (c >> 31); x[offset + 80] ^= t; x[offset + 144] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 272]) & (c >> 31); x[offset + 208] ^= t; x[offset + 272] ^= t; - c = 61444 - x[offset + 336]; t = (x[offset + 336] ^ x[offset + 400]) & (c >> 31); x[offset + 336] ^= t; x[offset + 400] ^= t; - c = 61444 - x[offset + 464]; t = (x[offset + 464] ^ x[offset + 528]) & (c >> 31); x[offset + 464] ^= t; x[offset + 528] ^= t; - c = 61444 - x[offset + 592]; t = (x[offset + 592] ^ x[offset + 656]) & (c >> 31); x[offset + 592] ^= t; x[offset + 656] ^= t; - c = 61444 - x[offset + 720]; t = (x[offset + 720] ^ x[offset + 784]) & (c >> 31); x[offset + 720] ^= t; x[offset + 784] ^= t; - c = 61444 - x[offset + 848]; t = (x[offset + 848] ^ x[offset + 912]) & (c >> 31); x[offset + 848] ^= t; x[offset + 912] ^= t; - c = 61444 - x[offset + 976]; t = (x[offset + 976] ^ x[offset + 1040]) & (c >> 31); x[offset + 976] ^= t; x[offset + 1040] ^= t; - c = 61444 - x[offset + 1104]; t = (x[offset + 1104] ^ x[offset + 1168]) & (c >> 31); x[offset + 1104] ^= t; x[offset + 1168] ^= t; - c = 61444 - x[offset + 1232]; t = (x[offset + 1232] ^ x[offset + 1296]) & (c >> 31); x[offset + 1232] ^= t; x[offset + 1296] ^= t; - c = 61444 - x[offset + 48]; t = (x[offset + 48] ^ x[offset + 1072]) & (c >> 31); x[offset + 48] ^= t; x[offset + 1072] ^= t; - c = 61444 - x[offset + 560]; t = (x[offset + 560] ^ x[offset + 1072]) & (c >> 31); x[offset + 560] ^= t; x[offset + 1072] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 1328]) & (c >> 31); x[offset + 304] ^= t; x[offset + 1328] ^= t; - c = 61444 - x[offset + 816]; t = (x[offset + 816] ^ x[offset + 1328]) & (c >> 31); x[offset + 816] ^= t; x[offset + 1328] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 560]) & (c >> 31); x[offset + 304] ^= t; x[offset + 560] ^= t; - c = 61444 - x[offset + 816]; t = (x[offset + 816] ^ x[offset + 1072]) & (c >> 31); x[offset + 816] ^= t; x[offset + 1072] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 1200]) & (c >> 31); x[offset + 176] ^= t; x[offset + 1200] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 1200]) & (c >> 31); x[offset + 688] ^= t; x[offset + 1200] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 688]) & (c >> 31); x[offset + 432] ^= t; x[offset + 688] ^= t; - c = 61444 - x[offset + 944]; t = (x[offset + 944] ^ x[offset + 1200]) & (c >> 31); x[offset + 944] ^= t; x[offset + 1200] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 304]) & (c >> 31); x[offset + 176] ^= t; x[offset + 304] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 560]) & (c >> 31); x[offset + 432] ^= t; x[offset + 560] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 816]) & (c >> 31); x[offset + 688] ^= t; x[offset + 816] ^= t; - c = 61444 - x[offset + 944]; t = (x[offset + 944] ^ x[offset + 1072]) & (c >> 31); x[offset + 944] ^= t; x[offset + 1072] ^= t; - c = 61444 - x[offset + 1200]; t = (x[offset + 1200] ^ x[offset + 1328]) & (c >> 31); x[offset + 1200] ^= t; x[offset + 1328] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 1136]) & (c >> 31); x[offset + 112] ^= t; x[offset + 1136] ^= t; - c = 61444 - x[offset + 624]; t = (x[offset + 624] ^ x[offset + 1136]) & (c >> 31); x[offset + 624] ^= t; x[offset + 1136] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 624]) & (c >> 31); x[offset + 368] ^= t; x[offset + 624] ^= t; - c = 61444 - x[offset + 880]; t = (x[offset + 880] ^ x[offset + 1136]) & (c >> 31); x[offset + 880] ^= t; x[offset + 1136] ^= t; - c = 61444 - x[offset + 240]; t = (x[offset + 240] ^ x[offset + 1264]) & (c >> 31); x[offset + 240] ^= t; x[offset + 1264] ^= t; - c = 61444 - x[offset + 752]; t = (x[offset + 752] ^ x[offset + 1264]) & (c >> 31); x[offset + 752] ^= t; x[offset + 1264] ^= t; - c = 61444 - x[offset + 496]; t = (x[offset + 496] ^ x[offset + 752]) & (c >> 31); x[offset + 496] ^= t; x[offset + 752] ^= t; - c = 61444 - x[offset + 1008]; t = (x[offset + 1008] ^ x[offset + 1264]) & (c >> 31); x[offset + 1008] ^= t; x[offset + 1264] ^= t; - c = 61444 - x[offset + 240]; t = (x[offset + 240] ^ x[offset + 368]) & (c >> 31); x[offset + 240] ^= t; x[offset + 368] ^= t; - c = 61444 - x[offset + 496]; t = (x[offset + 496] ^ x[offset + 624]) & (c >> 31); x[offset + 496] ^= t; x[offset + 624] ^= t; - c = 61444 - x[offset + 752]; t = (x[offset + 752] ^ x[offset + 880]) & (c >> 31); x[offset + 752] ^= t; x[offset + 880] ^= t; - c = 61444 - x[offset + 1008]; t = (x[offset + 1008] ^ x[offset + 1136]) & (c >> 31); x[offset + 1008] ^= t; x[offset + 1136] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 176]) & (c >> 31); x[offset + 112] ^= t; x[offset + 176] ^= t; - c = 61444 - x[offset + 240]; t = (x[offset + 240] ^ x[offset + 304]) & (c >> 31); x[offset + 240] ^= t; x[offset + 304] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 432]) & (c >> 31); x[offset + 368] ^= t; x[offset + 432] ^= t; - c = 61444 - x[offset + 496]; t = (x[offset + 496] ^ x[offset + 560]) & (c >> 31); x[offset + 496] ^= t; x[offset + 560] ^= t; - c = 61444 - x[offset + 624]; t = (x[offset + 624] ^ x[offset + 688]) & (c >> 31); x[offset + 624] ^= t; x[offset + 688] ^= t; - c = 61444 - x[offset + 752]; t = (x[offset + 752] ^ x[offset + 816]) & (c >> 31); x[offset + 752] ^= t; x[offset + 816] ^= t; - c = 61444 - x[offset + 880]; t = (x[offset + 880] ^ x[offset + 944]) & (c >> 31); x[offset + 880] ^= t; x[offset + 944] ^= t; - c = 61444 - x[offset + 1008]; t = (x[offset + 1008] ^ x[offset + 1072]) & (c >> 31); x[offset + 1008] ^= t; x[offset + 1072] ^= t; - c = 61444 - x[offset + 1136]; t = (x[offset + 1136] ^ x[offset + 1200]) & (c >> 31); x[offset + 1136] ^= t; x[offset + 1200] ^= t; - c = 61444 - x[offset + 1264]; t = (x[offset + 1264] ^ x[offset + 1328]) & (c >> 31); x[offset + 1264] ^= t; x[offset + 1328] ^= t; - c = 61444 - x[offset + 48]; t = (x[offset + 48] ^ x[offset + 80]) & (c >> 31); x[offset + 48] ^= t; x[offset + 80] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 144]) & (c >> 31); x[offset + 112] ^= t; x[offset + 144] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 208]) & (c >> 31); x[offset + 176] ^= t; x[offset + 208] ^= t; - c = 61444 - x[offset + 240]; t = (x[offset + 240] ^ x[offset + 272]) & (c >> 31); x[offset + 240] ^= t; x[offset + 272] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 336]) & (c >> 31); x[offset + 304] ^= t; x[offset + 336] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 400]) & (c >> 31); x[offset + 368] ^= t; x[offset + 400] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 464]) & (c >> 31); x[offset + 432] ^= t; x[offset + 464] ^= t; - c = 61444 - x[offset + 496]; t = (x[offset + 496] ^ x[offset + 528]) & (c >> 31); x[offset + 496] ^= t; x[offset + 528] ^= t; - c = 61444 - x[offset + 560]; t = (x[offset + 560] ^ x[offset + 592]) & (c >> 31); x[offset + 560] ^= t; x[offset + 592] ^= t; - c = 61444 - x[offset + 624]; t = (x[offset + 624] ^ x[offset + 656]) & (c >> 31); x[offset + 624] ^= t; x[offset + 656] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 720]) & (c >> 31); x[offset + 688] ^= t; x[offset + 720] ^= t; - c = 61444 - x[offset + 752]; t = (x[offset + 752] ^ x[offset + 784]) & (c >> 31); x[offset + 752] ^= t; x[offset + 784] ^= t; - c = 61444 - x[offset + 816]; t = (x[offset + 816] ^ x[offset + 848]) & (c >> 31); x[offset + 816] ^= t; x[offset + 848] ^= t; - c = 61444 - x[offset + 880]; t = (x[offset + 880] ^ x[offset + 912]) & (c >> 31); x[offset + 880] ^= t; x[offset + 912] ^= t; - c = 61444 - x[offset + 944]; t = (x[offset + 944] ^ x[offset + 976]) & (c >> 31); x[offset + 944] ^= t; x[offset + 976] ^= t; - c = 61444 - x[offset + 1008]; t = (x[offset + 1008] ^ x[offset + 1040]) & (c >> 31); x[offset + 1008] ^= t; x[offset + 1040] ^= t; - c = 61444 - x[offset + 1072]; t = (x[offset + 1072] ^ x[offset + 1104]) & (c >> 31); x[offset + 1072] ^= t; x[offset + 1104] ^= t; - c = 61444 - x[offset + 1136]; t = (x[offset + 1136] ^ x[offset + 1168]) & (c >> 31); x[offset + 1136] ^= t; x[offset + 1168] ^= t; - c = 61444 - x[offset + 1200]; t = (x[offset + 1200] ^ x[offset + 1232]) & (c >> 31); x[offset + 1200] ^= t; x[offset + 1232] ^= t; - c = 61444 - x[offset + 1264]; t = (x[offset + 1264] ^ x[offset + 1296]) & (c >> 31); x[offset + 1264] ^= t; x[offset + 1296] ^= t; - c = 61444 - x[offset + 16]; t = (x[offset + 16] ^ x[offset + 32]) & (c >> 31); x[offset + 16] ^= t; x[offset + 32] ^= t; - c = 61444 - x[offset + 48]; t = (x[offset + 48] ^ x[offset + 64]) & (c >> 31); x[offset + 48] ^= t; x[offset + 64] ^= t; - c = 61444 - x[offset + 80]; t = (x[offset + 80] ^ x[offset + 96]) & (c >> 31); x[offset + 80] ^= t; x[offset + 96] ^= t; - c = 61444 - x[offset + 112]; t = (x[offset + 112] ^ x[offset + 128]) & (c >> 31); x[offset + 112] ^= t; x[offset + 128] ^= t; - c = 61444 - x[offset + 144]; t = (x[offset + 144] ^ x[offset + 160]) & (c >> 31); x[offset + 144] ^= t; x[offset + 160] ^= t; - c = 61444 - x[offset + 176]; t = (x[offset + 176] ^ x[offset + 192]) & (c >> 31); x[offset + 176] ^= t; x[offset + 192] ^= t; - c = 61444 - x[offset + 208]; t = (x[offset + 208] ^ x[offset + 224]) & (c >> 31); x[offset + 208] ^= t; x[offset + 224] ^= t; - c = 61444 - x[offset + 240]; t = (x[offset + 240] ^ x[offset + 256]) & (c >> 31); x[offset + 240] ^= t; x[offset + 256] ^= t; - c = 61444 - x[offset + 272]; t = (x[offset + 272] ^ x[offset + 288]) & (c >> 31); x[offset + 272] ^= t; x[offset + 288] ^= t; - c = 61444 - x[offset + 304]; t = (x[offset + 304] ^ x[offset + 320]) & (c >> 31); x[offset + 304] ^= t; x[offset + 320] ^= t; - c = 61444 - x[offset + 336]; t = (x[offset + 336] ^ x[offset + 352]) & (c >> 31); x[offset + 336] ^= t; x[offset + 352] ^= t; - c = 61444 - x[offset + 368]; t = (x[offset + 368] ^ x[offset + 384]) & (c >> 31); x[offset + 368] ^= t; x[offset + 384] ^= t; - c = 61444 - x[offset + 400]; t = (x[offset + 400] ^ x[offset + 416]) & (c >> 31); x[offset + 400] ^= t; x[offset + 416] ^= t; - c = 61444 - x[offset + 432]; t = (x[offset + 432] ^ x[offset + 448]) & (c >> 31); x[offset + 432] ^= t; x[offset + 448] ^= t; - c = 61444 - x[offset + 464]; t = (x[offset + 464] ^ x[offset + 480]) & (c >> 31); x[offset + 464] ^= t; x[offset + 480] ^= t; - c = 61444 - x[offset + 496]; t = (x[offset + 496] ^ x[offset + 512]) & (c >> 31); x[offset + 496] ^= t; x[offset + 512] ^= t; - c = 61444 - x[offset + 528]; t = (x[offset + 528] ^ x[offset + 544]) & (c >> 31); x[offset + 528] ^= t; x[offset + 544] ^= t; - c = 61444 - x[offset + 560]; t = (x[offset + 560] ^ x[offset + 576]) & (c >> 31); x[offset + 560] ^= t; x[offset + 576] ^= t; - c = 61444 - x[offset + 592]; t = (x[offset + 592] ^ x[offset + 608]) & (c >> 31); x[offset + 592] ^= t; x[offset + 608] ^= t; - c = 61444 - x[offset + 624]; t = (x[offset + 624] ^ x[offset + 640]) & (c >> 31); x[offset + 624] ^= t; x[offset + 640] ^= t; - c = 61444 - x[offset + 656]; t = (x[offset + 656] ^ x[offset + 672]) & (c >> 31); x[offset + 656] ^= t; x[offset + 672] ^= t; - c = 61444 - x[offset + 688]; t = (x[offset + 688] ^ x[offset + 704]) & (c >> 31); x[offset + 688] ^= t; x[offset + 704] ^= t; - c = 61444 - x[offset + 720]; t = (x[offset + 720] ^ x[offset + 736]) & (c >> 31); x[offset + 720] ^= t; x[offset + 736] ^= t; - c = 61444 - x[offset + 752]; t = (x[offset + 752] ^ x[offset + 768]) & (c >> 31); x[offset + 752] ^= t; x[offset + 768] ^= t; - c = 61444 - x[offset + 784]; t = (x[offset + 784] ^ x[offset + 800]) & (c >> 31); x[offset + 784] ^= t; x[offset + 800] ^= t; - c = 61444 - x[offset + 816]; t = (x[offset + 816] ^ x[offset + 832]) & (c >> 31); x[offset + 816] ^= t; x[offset + 832] ^= t; - c = 61444 - x[offset + 848]; t = (x[offset + 848] ^ x[offset + 864]) & (c >> 31); x[offset + 848] ^= t; x[offset + 864] ^= t; - c = 61444 - x[offset + 880]; t = (x[offset + 880] ^ x[offset + 896]) & (c >> 31); x[offset + 880] ^= t; x[offset + 896] ^= t; - c = 61444 - x[offset + 912]; t = (x[offset + 912] ^ x[offset + 928]) & (c >> 31); x[offset + 912] ^= t; x[offset + 928] ^= t; - c = 61444 - x[offset + 944]; t = (x[offset + 944] ^ x[offset + 960]) & (c >> 31); x[offset + 944] ^= t; x[offset + 960] ^= t; - c = 61444 - x[offset + 976]; t = (x[offset + 976] ^ x[offset + 992]) & (c >> 31); x[offset + 976] ^= t; x[offset + 992] ^= t; - c = 61444 - x[offset + 1008]; t = (x[offset + 1008] ^ x[offset + 1024]) & (c >> 31); x[offset + 1008] ^= t; x[offset + 1024] ^= t; - c = 61444 - x[offset + 1040]; t = (x[offset + 1040] ^ x[offset + 1056]) & (c >> 31); x[offset + 1040] ^= t; x[offset + 1056] ^= t; - c = 61444 - x[offset + 1072]; t = (x[offset + 1072] ^ x[offset + 1088]) & (c >> 31); x[offset + 1072] ^= t; x[offset + 1088] ^= t; - c = 61444 - x[offset + 1104]; t = (x[offset + 1104] ^ x[offset + 1120]) & (c >> 31); x[offset + 1104] ^= t; x[offset + 1120] ^= t; - c = 61444 - x[offset + 1136]; t = (x[offset + 1136] ^ x[offset + 1152]) & (c >> 31); x[offset + 1136] ^= t; x[offset + 1152] ^= t; - c = 61444 - x[offset + 1168]; t = (x[offset + 1168] ^ x[offset + 1184]) & (c >> 31); x[offset + 1168] ^= t; x[offset + 1184] ^= t; - c = 61444 - x[offset + 1200]; t = (x[offset + 1200] ^ x[offset + 1216]) & (c >> 31); x[offset + 1200] ^= t; x[offset + 1216] ^= t; - c = 61444 - x[offset + 1232]; t = (x[offset + 1232] ^ x[offset + 1248]) & (c >> 31); x[offset + 1232] ^= t; x[offset + 1248] ^= t; - c = 61444 - x[offset + 1264]; t = (x[offset + 1264] ^ x[offset + 1280]) & (c >> 31); x[offset + 1264] ^= t; x[offset + 1280] ^= t; - c = 61444 - x[offset + 1296]; t = (x[offset + 1296] ^ x[offset + 1312]) & (c >> 31); x[offset + 1296] ^= t; x[offset + 1312] ^= t; - } + cmpSwap(0, 16); cmpSwap(32, 48); cmpSwap(0, 32); cmpSwap(16, 48); cmpSwap(16, 32) + cmpSwap(64, 80); cmpSwap(96, 112); cmpSwap(64, 96); cmpSwap(80, 112); cmpSwap(80, 96) + cmpSwap(0, 64); cmpSwap(32, 96); cmpSwap(32, 64); cmpSwap(16, 80); cmpSwap(48, 112); cmpSwap(48, 80); cmpSwap(16, 32); cmpSwap(48, 64); cmpSwap(80, 96) + cmpSwap(128, 144); cmpSwap(160, 176); cmpSwap(128, 160); cmpSwap(144, 176); cmpSwap(144, 160) + cmpSwap(192, 208); cmpSwap(224, 240); cmpSwap(192, 224); cmpSwap(208, 240); cmpSwap(208, 224) + cmpSwap(128, 192); cmpSwap(160, 224); cmpSwap(160, 192); cmpSwap(144, 208); cmpSwap(176, 240); cmpSwap(176, 208); cmpSwap(144, 160); cmpSwap(176, 192); cmpSwap(208, 224) + cmpSwap(0, 128); cmpSwap(64, 192); cmpSwap(64, 128); cmpSwap(32, 160); cmpSwap(96, 224); cmpSwap(96, 160); cmpSwap(32, 64); cmpSwap(96, 128); cmpSwap(160, 192) + cmpSwap(16, 144); cmpSwap(80, 208); cmpSwap(80, 144); cmpSwap(48, 176); cmpSwap(112, 240); cmpSwap(112, 176); cmpSwap(48, 80); cmpSwap(112, 144); cmpSwap(176, 208) + cmpSwap(16, 32); cmpSwap(48, 64); cmpSwap(80, 96); cmpSwap(112, 128); cmpSwap(144, 160); cmpSwap(176, 192); cmpSwap(208, 224) + cmpSwap(256, 272); cmpSwap(288, 304); cmpSwap(256, 288); cmpSwap(272, 304); cmpSwap(272, 288) + cmpSwap(320, 336); cmpSwap(352, 368); cmpSwap(320, 352); cmpSwap(336, 368); cmpSwap(336, 352) + cmpSwap(256, 320); cmpSwap(288, 352); cmpSwap(288, 320); cmpSwap(272, 336); cmpSwap(304, 368); cmpSwap(304, 336); cmpSwap(272, 288); cmpSwap(304, 320); cmpSwap(336, 352) + cmpSwap(384, 400); cmpSwap(416, 432); cmpSwap(384, 416); cmpSwap(400, 432); cmpSwap(400, 416) + cmpSwap(448, 464); cmpSwap(480, 496); cmpSwap(448, 480); cmpSwap(464, 496); cmpSwap(464, 480) + cmpSwap(384, 448); cmpSwap(416, 480); cmpSwap(416, 448); cmpSwap(400, 464); cmpSwap(432, 496); cmpSwap(432, 464); cmpSwap(400, 416); cmpSwap(432, 448); cmpSwap(464, 480) + cmpSwap(256, 384); cmpSwap(320, 448); cmpSwap(320, 384); cmpSwap(288, 416); cmpSwap(352, 480); cmpSwap(352, 416); cmpSwap(288, 320); cmpSwap(352, 384); cmpSwap(416, 448) + cmpSwap(272, 400); cmpSwap(336, 464); cmpSwap(336, 400); cmpSwap(304, 432); cmpSwap(368, 496); cmpSwap(368, 432); cmpSwap(304, 336); cmpSwap(368, 400); cmpSwap(432, 464) + cmpSwap(272, 288); cmpSwap(304, 320); cmpSwap(336, 352); cmpSwap(368, 384); cmpSwap(400, 416); cmpSwap(432, 448); cmpSwap(464, 480) + cmpSwap(0, 256); cmpSwap(128, 384); cmpSwap(128, 256); cmpSwap(64, 320); cmpSwap(192, 448); cmpSwap(192, 320); cmpSwap(64, 128); cmpSwap(192, 256); cmpSwap(320, 384) + cmpSwap(32, 288); cmpSwap(160, 416); cmpSwap(160, 288); cmpSwap(96, 352); cmpSwap(224, 480); cmpSwap(224, 352); cmpSwap(96, 160); cmpSwap(224, 288); cmpSwap(352, 416) + cmpSwap(32, 64); cmpSwap(96, 128); cmpSwap(160, 192); cmpSwap(224, 256); cmpSwap(288, 320); cmpSwap(352, 384); cmpSwap(416, 448) + cmpSwap(16, 272); cmpSwap(144, 400); cmpSwap(144, 272); cmpSwap(80, 336); cmpSwap(208, 464); cmpSwap(208, 336); cmpSwap(80, 144); cmpSwap(208, 272); cmpSwap(336, 400) + cmpSwap(48, 304); cmpSwap(176, 432); cmpSwap(176, 304); cmpSwap(112, 368); cmpSwap(240, 496); cmpSwap(240, 368); cmpSwap(112, 176); cmpSwap(240, 304); cmpSwap(368, 432) + cmpSwap(48, 80); cmpSwap(112, 144); cmpSwap(176, 208); cmpSwap(240, 272); cmpSwap(304, 336); cmpSwap(368, 400); cmpSwap(432, 464) + cmpSwap(16, 32); cmpSwap(48, 64); cmpSwap(80, 96); cmpSwap(112, 128); cmpSwap(144, 160); cmpSwap(176, 192); cmpSwap(208, 224); cmpSwap(240, 256); cmpSwap(272, 288); cmpSwap(304, 320); cmpSwap(336, 352); cmpSwap(368, 384); cmpSwap(400, 416); cmpSwap(432, 448); cmpSwap(464, 480) + cmpSwap(512, 528); cmpSwap(544, 560); cmpSwap(512, 544); cmpSwap(528, 560); cmpSwap(528, 544) + cmpSwap(576, 592); cmpSwap(608, 624); cmpSwap(576, 608); cmpSwap(592, 624); cmpSwap(592, 608) + cmpSwap(512, 576); cmpSwap(544, 608); cmpSwap(544, 576); cmpSwap(528, 592); cmpSwap(560, 624); cmpSwap(560, 592); cmpSwap(528, 544); cmpSwap(560, 576); cmpSwap(592, 608) + cmpSwap(640, 656); cmpSwap(672, 688); cmpSwap(640, 672); cmpSwap(656, 688); cmpSwap(656, 672) + cmpSwap(704, 720); cmpSwap(736, 752); cmpSwap(704, 736); cmpSwap(720, 752); cmpSwap(720, 736) + cmpSwap(640, 704); cmpSwap(672, 736); cmpSwap(672, 704); cmpSwap(656, 720); cmpSwap(688, 752); cmpSwap(688, 720); cmpSwap(656, 672); cmpSwap(688, 704); cmpSwap(720, 736) + cmpSwap(512, 640); cmpSwap(576, 704); cmpSwap(576, 640); cmpSwap(544, 672); cmpSwap(608, 736); cmpSwap(608, 672); cmpSwap(544, 576); cmpSwap(608, 640); cmpSwap(672, 704) + cmpSwap(528, 656); cmpSwap(592, 720); cmpSwap(592, 656); cmpSwap(560, 688); cmpSwap(624, 752); cmpSwap(624, 688); cmpSwap(560, 592); cmpSwap(624, 656); cmpSwap(688, 720) + cmpSwap(528, 544); cmpSwap(560, 576); cmpSwap(592, 608); cmpSwap(624, 640); cmpSwap(656, 672); cmpSwap(688, 704); cmpSwap(720, 736) + cmpSwap(768, 784); cmpSwap(800, 816); cmpSwap(768, 800); cmpSwap(784, 816); cmpSwap(784, 800) + cmpSwap(832, 848); cmpSwap(864, 880); cmpSwap(832, 864); cmpSwap(848, 880); cmpSwap(848, 864) + cmpSwap(768, 832); cmpSwap(800, 864); cmpSwap(800, 832); cmpSwap(784, 848); cmpSwap(816, 880); cmpSwap(816, 848); cmpSwap(784, 800); cmpSwap(816, 832); cmpSwap(848, 864) + cmpSwap(896, 912); cmpSwap(928, 944); cmpSwap(896, 928); cmpSwap(912, 944); cmpSwap(912, 928) + cmpSwap(960, 976); cmpSwap(992, 1008); cmpSwap(960, 992); cmpSwap(976, 1008); cmpSwap(976, 992) + cmpSwap(896, 960); cmpSwap(928, 992); cmpSwap(928, 960); cmpSwap(912, 976); cmpSwap(944, 1008); cmpSwap(944, 976); cmpSwap(912, 928); cmpSwap(944, 960); cmpSwap(976, 992) + cmpSwap(768, 896); cmpSwap(832, 960); cmpSwap(832, 896); cmpSwap(800, 928); cmpSwap(864, 992); cmpSwap(864, 928); cmpSwap(800, 832); cmpSwap(864, 896); cmpSwap(928, 960) + cmpSwap(784, 912); cmpSwap(848, 976); cmpSwap(848, 912); cmpSwap(816, 944); cmpSwap(880, 1008); cmpSwap(880, 944); cmpSwap(816, 848); cmpSwap(880, 912); cmpSwap(944, 976) + cmpSwap(784, 800); cmpSwap(816, 832); cmpSwap(848, 864); cmpSwap(880, 896); cmpSwap(912, 928); cmpSwap(944, 960); cmpSwap(976, 992) + cmpSwap(512, 768); cmpSwap(640, 896); cmpSwap(640, 768); cmpSwap(576, 832); cmpSwap(704, 960); cmpSwap(704, 832); cmpSwap(576, 640); cmpSwap(704, 768); cmpSwap(832, 896) + cmpSwap(544, 800); cmpSwap(672, 928); cmpSwap(672, 800); cmpSwap(608, 864); cmpSwap(736, 992); cmpSwap(736, 864); cmpSwap(608, 672); cmpSwap(736, 800); cmpSwap(864, 928) + cmpSwap(544, 576); cmpSwap(608, 640); cmpSwap(672, 704); cmpSwap(736, 768); cmpSwap(800, 832); cmpSwap(864, 896); cmpSwap(928, 960) + cmpSwap(528, 784); cmpSwap(656, 912); cmpSwap(656, 784); cmpSwap(592, 848); cmpSwap(720, 976); cmpSwap(720, 848); cmpSwap(592, 656); cmpSwap(720, 784); cmpSwap(848, 912) + cmpSwap(560, 816); cmpSwap(688, 944); cmpSwap(688, 816); cmpSwap(624, 880); cmpSwap(752, 1008); cmpSwap(752, 880); cmpSwap(624, 688); cmpSwap(752, 816); cmpSwap(880, 944) + cmpSwap(560, 592); cmpSwap(624, 656); cmpSwap(688, 720); cmpSwap(752, 784); cmpSwap(816, 848); cmpSwap(880, 912); cmpSwap(944, 976) + cmpSwap(528, 544); cmpSwap(560, 576); cmpSwap(592, 608); cmpSwap(624, 640); cmpSwap(656, 672); cmpSwap(688, 704); cmpSwap(720, 736); cmpSwap(752, 768); cmpSwap(784, 800); cmpSwap(816, 832); cmpSwap(848, 864); cmpSwap(880, 896); cmpSwap(912, 928); cmpSwap(944, 960); cmpSwap(976, 992) + cmpSwap(0, 512); cmpSwap(256, 768); cmpSwap(256, 512); cmpSwap(128, 640); cmpSwap(384, 896); cmpSwap(384, 640); cmpSwap(128, 256); cmpSwap(384, 512); cmpSwap(640, 768) + cmpSwap(64, 576); cmpSwap(320, 832); cmpSwap(320, 576); cmpSwap(192, 704); cmpSwap(448, 960); cmpSwap(448, 704); cmpSwap(192, 320); cmpSwap(448, 576); cmpSwap(704, 832) + cmpSwap(64, 128); cmpSwap(192, 256); cmpSwap(320, 384); cmpSwap(448, 512); cmpSwap(576, 640); cmpSwap(704, 768); cmpSwap(832, 896) + cmpSwap(32, 544); cmpSwap(288, 800); cmpSwap(288, 544); cmpSwap(160, 672); cmpSwap(416, 928); cmpSwap(416, 672); cmpSwap(160, 288); cmpSwap(416, 544); cmpSwap(672, 800) + cmpSwap(96, 608); cmpSwap(352, 864); cmpSwap(352, 608); cmpSwap(224, 736); cmpSwap(480, 992); cmpSwap(480, 736); cmpSwap(224, 352); cmpSwap(480, 608); cmpSwap(736, 864) + cmpSwap(96, 160); cmpSwap(224, 288); cmpSwap(352, 416); cmpSwap(480, 544); cmpSwap(608, 672); cmpSwap(736, 800); cmpSwap(864, 928) + cmpSwap(32, 64); cmpSwap(96, 128); cmpSwap(160, 192); cmpSwap(224, 256); cmpSwap(288, 320); cmpSwap(352, 384); cmpSwap(416, 448); cmpSwap(480, 512); cmpSwap(544, 576); cmpSwap(608, 640); cmpSwap(672, 704); cmpSwap(736, 768); cmpSwap(800, 832); cmpSwap(864, 896); cmpSwap(928, 960) + cmpSwap(16, 528); cmpSwap(272, 784); cmpSwap(272, 528); cmpSwap(144, 656); cmpSwap(400, 912); cmpSwap(400, 656); cmpSwap(144, 272); cmpSwap(400, 528); cmpSwap(656, 784) + cmpSwap(80, 592); cmpSwap(336, 848); cmpSwap(336, 592); cmpSwap(208, 720); cmpSwap(464, 976); cmpSwap(464, 720); cmpSwap(208, 336); cmpSwap(464, 592); cmpSwap(720, 848) + cmpSwap(80, 144); cmpSwap(208, 272); cmpSwap(336, 400); cmpSwap(464, 528); cmpSwap(592, 656); cmpSwap(720, 784); cmpSwap(848, 912) + cmpSwap(48, 560); cmpSwap(304, 816); cmpSwap(304, 560); cmpSwap(176, 688); cmpSwap(432, 944); cmpSwap(432, 688); cmpSwap(176, 304); cmpSwap(432, 560); cmpSwap(688, 816) + cmpSwap(112, 624); cmpSwap(368, 880); cmpSwap(368, 624); cmpSwap(240, 752); cmpSwap(496, 1008); cmpSwap(496, 752); cmpSwap(240, 368); cmpSwap(496, 624); cmpSwap(752, 880) + cmpSwap(112, 176); cmpSwap(240, 304); cmpSwap(368, 432); cmpSwap(496, 560); cmpSwap(624, 688); cmpSwap(752, 816); cmpSwap(880, 944) + cmpSwap(48, 80); cmpSwap(112, 144); cmpSwap(176, 208); cmpSwap(240, 272); cmpSwap(304, 336); cmpSwap(368, 400); cmpSwap(432, 464); cmpSwap(496, 528); cmpSwap(560, 592); cmpSwap(624, 656); cmpSwap(688, 720); cmpSwap(752, 784); cmpSwap(816, 848); cmpSwap(880, 912); cmpSwap(944, 976) + cmpSwap(16, 32); cmpSwap(48, 64); cmpSwap(80, 96); cmpSwap(112, 128); cmpSwap(144, 160); cmpSwap(176, 192); cmpSwap(208, 224); cmpSwap(240, 256); cmpSwap(272, 288); cmpSwap(304, 320); cmpSwap(336, 352); cmpSwap(368, 384); cmpSwap(400, 416); cmpSwap(432, 448); cmpSwap(464, 480); cmpSwap(496, 512); cmpSwap(528, 544); cmpSwap(560, 576); cmpSwap(592, 608); cmpSwap(624, 640); cmpSwap(656, 672); cmpSwap(688, 704); cmpSwap(720, 736); cmpSwap(752, 768); cmpSwap(784, 800); cmpSwap(816, 832); cmpSwap(848, 864); cmpSwap(880, 896); cmpSwap(912, 928); cmpSwap(944, 960); cmpSwap(976, 992) + cmpSwap(0, 1024); cmpSwap(512, 1024); cmpSwap(256, 1280); cmpSwap(768, 1280); cmpSwap(256, 512); cmpSwap(768, 1024); cmpSwap(128, 1152); cmpSwap(640, 1152); cmpSwap(384, 640); cmpSwap(896, 1152); cmpSwap(128, 256); cmpSwap(384, 512); cmpSwap(640, 768); cmpSwap(896, 1024); cmpSwap(1152, 1280) + cmpSwap(64, 1088); cmpSwap(576, 1088); cmpSwap(320, 576); cmpSwap(832, 1088); cmpSwap(192, 1216); cmpSwap(704, 1216); cmpSwap(448, 704); cmpSwap(960, 1216); cmpSwap(192, 320); cmpSwap(448, 576); cmpSwap(704, 832); cmpSwap(960, 1088); cmpSwap(64, 128); cmpSwap(192, 256); cmpSwap(320, 384); cmpSwap(448, 512); cmpSwap(576, 640); cmpSwap(704, 768); cmpSwap(832, 896); cmpSwap(960, 1024); cmpSwap(1088, 1152); cmpSwap(1216, 1280) + cmpSwap(32, 1056); cmpSwap(544, 1056); cmpSwap(288, 1312); cmpSwap(800, 1312); cmpSwap(288, 544); cmpSwap(800, 1056); cmpSwap(160, 1184); cmpSwap(672, 1184); cmpSwap(416, 672); cmpSwap(928, 1184); cmpSwap(160, 288); cmpSwap(416, 544); cmpSwap(672, 800); cmpSwap(928, 1056); cmpSwap(1184, 1312) + cmpSwap(96, 1120); cmpSwap(608, 1120); cmpSwap(352, 608); cmpSwap(864, 1120); cmpSwap(224, 1248); cmpSwap(736, 1248); cmpSwap(480, 736); cmpSwap(992, 1248); cmpSwap(224, 352); cmpSwap(480, 608); cmpSwap(736, 864); cmpSwap(992, 1120); cmpSwap(96, 160); cmpSwap(224, 288); cmpSwap(352, 416); cmpSwap(480, 544); cmpSwap(608, 672); cmpSwap(736, 800); cmpSwap(864, 928); cmpSwap(992, 1056); cmpSwap(1120, 1184); cmpSwap(1248, 1312) + cmpSwap(32, 64); cmpSwap(96, 128); cmpSwap(160, 192); cmpSwap(224, 256); cmpSwap(288, 320); cmpSwap(352, 384); cmpSwap(416, 448); cmpSwap(480, 512); cmpSwap(544, 576); cmpSwap(608, 640); cmpSwap(672, 704); cmpSwap(736, 768); cmpSwap(800, 832); cmpSwap(864, 896); cmpSwap(928, 960); cmpSwap(992, 1024); cmpSwap(1056, 1088); cmpSwap(1120, 1152); cmpSwap(1184, 1216); cmpSwap(1248, 1280); cmpSwap(1312, 1344) + cmpSwap(16, 1040); cmpSwap(528, 1040); cmpSwap(272, 1296); cmpSwap(784, 1296); cmpSwap(272, 528); cmpSwap(784, 1040); cmpSwap(144, 1168); cmpSwap(656, 1168); cmpSwap(400, 656); cmpSwap(912, 1168); cmpSwap(144, 272); cmpSwap(400, 528); cmpSwap(656, 784); cmpSwap(912, 1040); cmpSwap(1168, 1296) + cmpSwap(80, 1104); cmpSwap(592, 1104); cmpSwap(336, 592); cmpSwap(848, 1104); cmpSwap(208, 1232); cmpSwap(720, 1232); cmpSwap(464, 720); cmpSwap(976, 1232); cmpSwap(208, 336); cmpSwap(464, 592); cmpSwap(720, 848); cmpSwap(976, 1104); cmpSwap(80, 144); cmpSwap(208, 272); cmpSwap(336, 400); cmpSwap(464, 528); cmpSwap(592, 656); cmpSwap(720, 784); cmpSwap(848, 912); cmpSwap(976, 1040); cmpSwap(1104, 1168); cmpSwap(1232, 1296) + cmpSwap(48, 1072); cmpSwap(560, 1072); cmpSwap(304, 1328); cmpSwap(816, 1328); cmpSwap(304, 560); cmpSwap(816, 1072); cmpSwap(176, 1200); cmpSwap(688, 1200); cmpSwap(432, 688); cmpSwap(944, 1200); cmpSwap(176, 304); cmpSwap(432, 560); cmpSwap(688, 816); cmpSwap(944, 1072); cmpSwap(1200, 1328) + cmpSwap(112, 1136); cmpSwap(624, 1136); cmpSwap(368, 624); cmpSwap(880, 1136); cmpSwap(240, 1264); cmpSwap(752, 1264); cmpSwap(496, 752); cmpSwap(1008, 1264); cmpSwap(240, 368); cmpSwap(496, 624); cmpSwap(752, 880); cmpSwap(1008, 1136); cmpSwap(112, 176); cmpSwap(240, 304); cmpSwap(368, 432); cmpSwap(496, 560); cmpSwap(624, 688); cmpSwap(752, 816); cmpSwap(880, 944); cmpSwap(1008, 1072); cmpSwap(1136, 1200); cmpSwap(1264, 1328) + cmpSwap(48, 80); cmpSwap(112, 144); cmpSwap(176, 208); cmpSwap(240, 272); cmpSwap(304, 336); cmpSwap(368, 400); cmpSwap(432, 464); cmpSwap(496, 528); cmpSwap(560, 592); cmpSwap(624, 656); cmpSwap(688, 720); cmpSwap(752, 784); cmpSwap(816, 848); cmpSwap(880, 912); cmpSwap(944, 976); cmpSwap(1008, 1040); cmpSwap(1072, 1104); cmpSwap(1136, 1168); cmpSwap(1200, 1232); cmpSwap(1264, 1296); cmpSwap(1328, 1360) + cmpSwap(16, 32); cmpSwap(48, 64); cmpSwap(80, 96); cmpSwap(112, 128); cmpSwap(144, 160); cmpSwap(176, 192); cmpSwap(208, 224); cmpSwap(240, 256); cmpSwap(272, 288); cmpSwap(304, 320); cmpSwap(336, 352); cmpSwap(368, 384); cmpSwap(400, 416); cmpSwap(432, 448); cmpSwap(464, 480); cmpSwap(496, 512); cmpSwap(528, 544); cmpSwap(560, 576); cmpSwap(592, 608); cmpSwap(624, 640); cmpSwap(656, 672); cmpSwap(688, 704); cmpSwap(720, 736); cmpSwap(752, 768); cmpSwap(784, 800); cmpSwap(816, 832); cmpSwap(848, 864); cmpSwap(880, 896); cmpSwap(912, 928); cmpSwap(944, 960); cmpSwap(976, 992); cmpSwap(1008, 1024); cmpSwap(1040, 1056); cmpSwap(1072, 1088); cmpSwap(1104, 1120); cmpSwap(1136, 1152); cmpSwap(1168, 1184); cmpSwap(1200, 1216); cmpSwap(1232, 1248); cmpSwap(1264, 1280); cmpSwap(1296, 1312); cmpSwap(1328, 1344); cmpSwap(1360, 1376) + } + } } From f09d4b90d3f7d513c457bba6c3b6e799a454f2f5 Mon Sep 17 00:00:00 2001 From: yet300 Date: Sat, 9 May 2026 16:28:38 +0400 Subject: [PATCH 15/37] Update ChaChaPolyCipherState to access ChaChaCore via INSTANCE field --- .../southernstorm/protocol/ChaChaPolyCipherState.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/ChaChaPolyCipherState.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/ChaChaPolyCipherState.java index bbb8c37b..a64182bd 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/ChaChaPolyCipherState.java +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/ChaChaPolyCipherState.java @@ -80,7 +80,7 @@ class ChaChaPolyCipherState implements CipherState { @Override public void initializeKey(byte[] key, int offset) { - ChaChaCore.initKey256(input, key, offset); + ChaChaCore.INSTANCE.initKey256(input, key, offset); n = 0; haskey = true; } @@ -138,8 +138,8 @@ class ChaChaPolyCipherState implements CipherState { { if (n == -1L) throw new IllegalStateException("Nonce has wrapped around"); - ChaChaCore.initIV(input, n++); - ChaChaCore.hash(output, input); + ChaChaCore.INSTANCE.initIV(input, n++); + ChaChaCore.INSTANCE.hash(output, input); Arrays.fill(polyKey, (byte)0); xorBlock(polyKey, 0, polyKey, 0, 32, output); poly.reset(polyKey, 0); @@ -200,7 +200,7 @@ class ChaChaPolyCipherState implements CipherState { int tempLen = 64; if (tempLen > length) tempLen = length; - ChaChaCore.hash(output, input); + ChaChaCore.INSTANCE.hash(output, input); xorBlock(plaintext, plaintextOffset, ciphertext, ciphertextOffset, tempLen, output); if (++(input[12]) == 0) ++(input[13]); From f9923992f0153f7e51329acea1f77f92b02c2f80 Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:29:34 +0400 Subject: [PATCH 16/37] Rename .java to .kt --- .../android/noise/southernstorm/protocol/{Noise.java => Noise.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/{Noise.java => Noise.kt} (100%) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Noise.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Noise.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Noise.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Noise.kt From bea44f988f1503519ae731d2c701c694f2cb01df Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:29:34 +0400 Subject: [PATCH 17/37] Convert Noise utility class to Kotlin - Migrate `Noise.java` to `Noise.kt`. - Refactor factory methods using Kotlin `when` expressions and destructuring. - Use `ByteArray.fill()` and `copyOfRange()` for internal utility functions. - Update reflection logic in `throwBadTagException` for better readability. - Apply `@JvmStatic` and `const` to maintain compatibility with existing Java callers. --- .../noise/southernstorm/protocol/Noise.kt | 369 ++++++++---------- 1 file changed, 166 insertions(+), 203 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Noise.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Noise.kt index c8fb7196..2a105b5e 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Noise.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Noise.kt @@ -19,219 +19,182 @@ * 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 java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.Arrays; - -import javax.crypto.BadPaddingException; - -import com.bitchat.android.noise.southernstorm.crypto.Blake2bMessageDigest; -import com.bitchat.android.noise.southernstorm.crypto.Blake2sMessageDigest; -import com.bitchat.android.noise.southernstorm.crypto.SHA256MessageDigest; -import com.bitchat.android.noise.southernstorm.crypto.SHA512MessageDigest; +import com.bitchat.android.noise.southernstorm.crypto.Blake2bMessageDigest +import com.bitchat.android.noise.southernstorm.crypto.Blake2sMessageDigest +import com.bitchat.android.noise.southernstorm.crypto.SHA256MessageDigest +import com.bitchat.android.noise.southernstorm.crypto.SHA512MessageDigest +import java.security.MessageDigest +import java.security.NoSuchAlgorithmException +import java.security.SecureRandom +import javax.crypto.BadPaddingException /** * Utility functions for the Noise protocol library. */ -public final class Noise { +object Noise { + /** + * Maximum length for Noise packets. + */ + const val MAX_PACKET_LEN: Int = 65535 - /** - * Maximum length for Noise packets. - */ - public static final int MAX_PACKET_LEN = 65535; - - private static SecureRandom random = new SecureRandom(); - - /** - * Generates random data using the system random number generator. - * - * @param data The data buffer to fill with random data. - */ - public static void random(byte[] data) - { - random.nextBytes(data); - } + private val random = SecureRandom() - private static boolean forceFallbacks = false; - - /** - * Force the use of plain Java fallback crypto implementations. - * - * @param force Set to true for force fallbacks, false to - * try to use the system implementation before falling back. - * - * This function is intended for testing purposes to toggle between - * the system JCA/JCE implementations and the plain Java fallback - * reference implementations. - */ - public static void setForceFallbacks(boolean force) - { - forceFallbacks = force; - } + private var forceFallbacks = false - /** - * Creates a Diffie-Hellman object from its Noise protocol name. - * - * @param name The name of the DH algorithm; e.g. "25519", "448", etc. - * - * @return The Diffie-Hellman object if the name is recognized. - * - * @throws NoSuchAlgorithmException The name is not recognized as a - * valid Noise protocol name, or there is no cryptography provider - * in the system that implements the algorithm. - */ - public static DHState createDH(String name) throws NoSuchAlgorithmException - { - if (name.equals("25519")) - return new Curve25519DHState(); - if (name.equals("448")) - return new Curve448DHState(); - if (name.equals("NewHope")) - return new NewHopeDHState(); - throw new NoSuchAlgorithmException("Unknown Noise DH algorithm name: " + name); - } + /** + * Generates random data using the system random number generator. + * + * @param data The data buffer to fill with random data. + */ + @JvmStatic + fun random(data: ByteArray) { + random.nextBytes(data) + } - /** - * Creates a cipher object from its Noise protocol name. - * - * @param name The name of the cipher algorithm; e.g. "AESGCM", "ChaChaPoly", etc. - * - * @return The cipher object if the name is recognized. - * - * @throws NoSuchAlgorithmException The name is not recognized as a - * valid Noise protocol name, or there is no cryptography provider - * in the system that implements the algorithm. - */ - public static CipherState createCipher(String name) throws NoSuchAlgorithmException - { - if (name.equals("AESGCM")) { - if (forceFallbacks) - return new AESGCMFallbackCipherState(); - // "AES/GCM/NoPadding" exists in some recent JDK's but it is flaky - // to use and not easily back-portable to older Android versions. - // We instead emulate AESGCM on top of "AES/CTR/NoPadding". - try { - return new AESGCMOnCtrCipherState(); - } catch (NoSuchAlgorithmException e1) { - // Could not find anything useful in the JCA/JCE so - // use the pure Java fallback implementation instead. - return new AESGCMFallbackCipherState(); - } - } else if (name.equals("ChaChaPoly")) { - return new ChaChaPolyCipherState(); - } - throw new NoSuchAlgorithmException("Unknown Noise cipher algorithm name: " + name); - } - - /** - * Creates a hash object from its Noise protocol name. - * - * @param name The name of the hash algorithm; e.g. "SHA256", "BLAKE2s", etc. - * - * @return The hash object if the name is recognized. - * - * @throws NoSuchAlgorithmException The name is not recognized as a - * valid Noise protocol name, or there is no cryptography provider - * in the system that implements the algorithm. - */ - public static MessageDigest createHash(String name) throws NoSuchAlgorithmException - { - // Look for a JCA/JCE provider first and if that doesn't work, - // use the fallback implementations in this library instead. - // The only algorithm that is required to be implemented by a - // JDK is "SHA-256", although "SHA-512" is fairly common as well. - if (name.equals("SHA256")) { - if (forceFallbacks) - return new SHA256MessageDigest(); - try { - return MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - return new SHA256MessageDigest(); - } - } else if (name.equals("SHA512")) { - if (forceFallbacks) - return new SHA512MessageDigest(); - try { - return MessageDigest.getInstance("SHA-512"); - } catch (NoSuchAlgorithmException e) { - return new SHA512MessageDigest(); - } - } else if (name.equals("BLAKE2b")) { - // Bouncy Castle registers the BLAKE2b variant we - // want under the name "BLAKE2B-512". - if (forceFallbacks) - return new Blake2bMessageDigest(); - try { - return MessageDigest.getInstance("BLAKE2B-512"); - } catch (NoSuchAlgorithmException e) { - return new Blake2bMessageDigest(); - } - } else if (name.equals("BLAKE2s")) { - // Bouncy Castle doesn't currently (June 2016) have an - // implementation of BLAKE2s, but look for the most - // obvious provider name in case one is added in the future. - if (forceFallbacks) - return new Blake2sMessageDigest(); - try { - return MessageDigest.getInstance("BLAKE2S-256"); - } catch (NoSuchAlgorithmException e) { - return new Blake2sMessageDigest(); - } - } - throw new NoSuchAlgorithmException("Unknown Noise hash algorithm name: " + name); - } + /** + * Force the use of plain Java fallback crypto implementations. + * + * @param force Set to true for force fallbacks, false to + * try to use the system implementation before falling back. + * + * This function is intended for testing purposes to toggle between + * the system JCA/JCE implementations and the plain Java fallback + * reference implementations. + */ + fun setForceFallbacks(force: Boolean) { + forceFallbacks = force + } - // The rest of this class consists of internal utility functions - // that are not part of the public API. + /** + * Creates a Diffie-Hellman object from its Noise protocol name. + * + * @param name The name of the DH algorithm; e.g. "25519", "448", etc. + * + * @return The Diffie-Hellman object if the name is recognized. + * + * @throws NoSuchAlgorithmException The name is not recognized as a + * valid Noise protocol name, or there is no cryptography provider + * in the system that implements the algorithm. + */ + @JvmStatic + @Throws(NoSuchAlgorithmException::class) + fun createDH(name: String): DHState = when (name) { + "25519" -> Curve25519DHState() + "448" -> Curve448DHState() + "NewHope" -> NewHopeDHState() + else -> throw NoSuchAlgorithmException("Unknown Noise DH algorithm name: $name") + } - /** - * Destroys the contents of a byte array. - * - * @param array The array whose contents should be destroyed. - */ - static void destroy(byte[] array) - { - Arrays.fill(array, (byte)0); - } + /** + * Creates a cipher object from its Noise protocol name. + * + * @param name The name of the cipher algorithm; e.g. "AESGCM", "ChaChaPoly", etc. + * + * @return The cipher object if the name is recognized. + * + * @throws NoSuchAlgorithmException The name is not recognized as a + * valid Noise protocol name, or there is no cryptography provider + * in the system that implements the algorithm. + */ + @JvmStatic + @Throws(NoSuchAlgorithmException::class) + fun createCipher(name: String): CipherState = when (name) { + "AESGCM" -> { + if (forceFallbacks) { + AESGCMFallbackCipherState() + } else { + // "AES/GCM/NoPadding" exists in some recent JDK's but it is flaky + // to use and not easily back-portable to older Android versions. + // We instead emulate AESGCM on top of "AES/CTR/NoPadding". + try { + AESGCMOnCtrCipherState() + } catch (_: NoSuchAlgorithmException) { + AESGCMFallbackCipherState() + } + } + } + "ChaChaPoly" -> ChaChaPolyCipherState() + else -> throw NoSuchAlgorithmException("Unknown Noise cipher algorithm name: $name") + } - /** - * Makes a copy of part of an array. - * - * @param data The buffer containing the data to copy. - * @param offset Offset of the first byte to copy. - * @param length The number of bytes to copy. - * - * @return A new array with a copy of the sub-array. - */ - static byte[] copySubArray(byte[] data, int offset, int length) - { - byte[] copy = new byte [length]; - System.arraycopy(data, offset, copy, 0, length); - return copy; - } - - /** - * Throws an instance of AEADBadTagException. - * - * @throws BadPaddingException The AEAD exception. - * - * If the underlying JDK does not have the AEADBadTagException - * class, then this function will instead throw an instance of - * the superclass BadPaddingException. - */ - static void throwBadTagException() throws BadPaddingException - { - try { - Class c = Class.forName("javax.crypto.AEADBadTagException"); - throw (BadPaddingException)(c.newInstance()); - } catch (ClassNotFoundException e) { - } catch (InstantiationException e) { - } catch (IllegalAccessException e) { - } - throw new BadPaddingException(); - } + /** + * Creates a hash object from its Noise protocol name. + * + * @param name The name of the hash algorithm; e.g. "SHA256", "BLAKE2s", etc. + * + * @return The hash object if the name is recognized. + * + * @throws NoSuchAlgorithmException The name is not recognized as a + * valid Noise protocol name, or there is no cryptography provider + * in the system that implements the algorithm. + */ + @JvmStatic + @Throws(NoSuchAlgorithmException::class) + fun createHash(name: String): MessageDigest { + // Look for a JCA/JCE provider first and if that doesn't work, + // use the fallback implementations in this library instead. + // The only algorithm that is required to be implemented by a + // JDK is "SHA-256", although "SHA-512" is fairly common as well. + val (jcaName, fallback) = when (name) { + "SHA256" -> "SHA-256" to ::SHA256MessageDigest + "SHA512" -> "SHA-512" to ::SHA512MessageDigest + "BLAKE2b" -> "BLAKE2B-512" to ::Blake2bMessageDigest + "BLAKE2s" -> "BLAKE2S-256" to ::Blake2sMessageDigest + else -> throw NoSuchAlgorithmException("Unknown Noise hash algorithm name: $name") + } + if (forceFallbacks) return fallback() + return try { + MessageDigest.getInstance(jcaName) + } catch (_: NoSuchAlgorithmException) { + fallback() + } + } + + // The rest of this class consists of internal utility functions + // that are not part of the public API. + /** + * Destroys the contents of a byte array. + * + * @param array The array whose contents should be destroyed. + */ + @JvmStatic + fun destroy(array: ByteArray) { + array.fill(0) + } + + /** + * Makes a copy of part of an array. + * + * @param data The buffer containing the data to copy. + * @param offset Offset of the first byte to copy. + * @param length The number of bytes to copy. + * + * @return A new array with a copy of the sub-array. + */ + @JvmStatic + fun copySubArray(data: ByteArray, offset: Int, length: Int): ByteArray = + data.copyOfRange(offset, offset + length) + + /** + * Throws an instance of AEADBadTagException. + * + * @throws BadPaddingException The AEAD exception. + * + * If the underlying JDK does not have the AEADBadTagException + * class, then this function will instead throw an instance of + * the superclass BadPaddingException. + */ + @JvmStatic + @Throws(BadPaddingException::class) + fun throwBadTagException() { + val aead = try { + val c = Class.forName("javax.crypto.AEADBadTagException") + c.getDeclaredConstructor().newInstance() as BadPaddingException + } catch (_: ReflectiveOperationException) { + null + } + throw aead ?: BadPaddingException() + } } From f7477d87e9df7609e73c9f90069f460da6af2bb8 Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:32:46 +0400 Subject: [PATCH 18/37] Rename .java to .kt --- .../noise/southernstorm/protocol/{Pattern.java => Pattern.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/{Pattern.java => Pattern.kt} (100%) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Pattern.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Pattern.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Pattern.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Pattern.kt From 67808f22b8f18243fbc8d63aad99ae24e86a0794 Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:32:46 +0400 Subject: [PATCH 19/37] Convert Pattern.java to Kotlin and refactor lookup logic to use a Map. --- .../noise/southernstorm/protocol/Pattern.kt | 1070 ++++------------- 1 file changed, 261 insertions(+), 809 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Pattern.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Pattern.kt index 2a625c90..2ab07c95 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Pattern.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Pattern.kt @@ -19,817 +19,269 @@ * 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 /** * Information about all supported handshake patterns. */ -class Pattern { - - private Pattern() {} - - // Token codes. - public static final short S = 1; - public static final short E = 2; - public static final short EE = 3; - public static final short ES = 4; - public static final short SE = 5; - public static final short SS = 6; - public static final short F = 7; - public static final short FF = 8; - public static final short FLIP_DIR = 255; - - // Pattern flag bits. - public static final short FLAG_LOCAL_STATIC = 0x0001; - public static final short FLAG_LOCAL_EPHEMERAL = 0x0002; - public static final short FLAG_LOCAL_REQUIRED = 0x0004; - public static final short FLAG_LOCAL_EPHEM_REQ = 0x0008; - public static final short FLAG_LOCAL_HYBRID = 0x0010; - public static final short FLAG_LOCAL_HYBRID_REQ = 0x0020; - public static final short FLAG_REMOTE_STATIC = 0x0100; - public static final short FLAG_REMOTE_EPHEMERAL = 0x0200; - public static final short FLAG_REMOTE_REQUIRED = 0x0400; - public static final short FLAG_REMOTE_EPHEM_REQ = 0x0800; - public static final short FLAG_REMOTE_HYBRID = 0x1000; - public static final short FLAG_REMOTE_HYBRID_REQ = 0x2000; - - private static final short[] noise_pattern_N = { - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_REQUIRED, - - E, - ES - }; - - private static final short[] noise_pattern_K = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_REQUIRED, - - E, - ES, - SS - }; - - private static final short[] noise_pattern_X = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_REQUIRED, - - E, - ES, - S, - SS - }; - - private static final short[] noise_pattern_NN = { - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - EE - }; - - private static final short[] noise_pattern_NK = { - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_REQUIRED, - - E, - ES, - FLIP_DIR, - E, - EE - }; - - private static final short[] noise_pattern_NX = { - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - EE, - S, - ES - }; - - private static final short[] noise_pattern_XN = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - EE, - FLIP_DIR, - S, - SE - }; - - private static final short[] noise_pattern_XK = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_REQUIRED, - - E, - ES, - FLIP_DIR, - E, - EE, - FLIP_DIR, - S, - SE - }; - - private static final short[] noise_pattern_XX = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - EE, - S, - ES, - FLIP_DIR, - S, - SE - }; - - private static final short[] noise_pattern_KN = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - EE, - SE - }; - - private static final short[] noise_pattern_KK = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_REQUIRED, - - E, - ES, - SS, - FLIP_DIR, - E, - EE, - SE - }; - - private static final short[] noise_pattern_KX = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - EE, - SE, - S, - ES - }; - - private static final short[] noise_pattern_IN = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_EPHEMERAL, - - E, - S, - FLIP_DIR, - E, - EE, - SE - }; - - private static final short[] noise_pattern_IK = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_REQUIRED, - - E, - ES, - S, - SS, - FLIP_DIR, - E, - EE, - SE - }; - - private static final short[] noise_pattern_IX = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - S, - FLIP_DIR, - E, - EE, - SE, - S, - ES - }; - - private static final short[] noise_pattern_XXfallback = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_EPHEM_REQ, - - E, - EE, - S, - SE, - FLIP_DIR, - S, - ES - }; - - private static final short[] noise_pattern_Xnoidh = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_REQUIRED, - - E, - S, - ES, - SS - }; - - private static final short[] noise_pattern_NXnoidh = { - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - S, - EE, - ES - }; - - private static final short[] noise_pattern_XXnoidh = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - S, - EE, - ES, - FLIP_DIR, - S, - SE - }; - - private static final short[] noise_pattern_KXnoidh = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - S, - EE, - SE, - ES - }; - - private static final short[] noise_pattern_IKnoidh = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_REQUIRED, - - E, - S, - ES, - SS, - FLIP_DIR, - E, - EE, - SE - }; - - private static final short[] noise_pattern_IXnoidh = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - S, - FLIP_DIR, - E, - S, - EE, - SE, - ES - }; - - private static final short[] noise_pattern_NNhfs = { - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - EE, - FF - }; - - private static final short[] noise_pattern_NKhfs = { - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID | - FLAG_REMOTE_REQUIRED, - - E, - F, - ES, - FLIP_DIR, - E, - F, - EE, - FF - }; - - private static final short[] noise_pattern_NXhfs = { - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - EE, - FF, - S, - ES - }; - - private static final short[] noise_pattern_XNhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - EE, - FF, - FLIP_DIR, - S, - SE - }; - - private static final short[] noise_pattern_XKhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID | - FLAG_REMOTE_REQUIRED, - - E, - F, - ES, - FLIP_DIR, - E, - F, - EE, - FF, - FLIP_DIR, - S, - SE - }; - - private static final short[] noise_pattern_XXhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - EE, - FF, - S, - ES, - FLIP_DIR, - S, - SE - }; - - private static final short[] noise_pattern_KNhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - EE, - FF, - SE - }; - - private static final short[] noise_pattern_KKhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID | - FLAG_REMOTE_REQUIRED, - - E, - F, - ES, - SS, - FLIP_DIR, - E, - F, - EE, - FF, - SE - }; - - private static final short[] noise_pattern_KXhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - EE, - FF, - SE, - S, - ES - }; - - private static final short[] noise_pattern_INhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - S, - FLIP_DIR, - E, - F, - EE, - FF, - SE - }; - - private static final short[] noise_pattern_IKhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID | - FLAG_REMOTE_REQUIRED, - - E, - F, - ES, - S, - SS, - FLIP_DIR, - E, - F, - EE, - FF, - SE - }; - - private static final short[] noise_pattern_IXhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - S, - FLIP_DIR, - E, - F, - EE, - FF, - SE, - S, - ES - }; - - private static final short[] noise_pattern_XXfallback_hfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_EPHEM_REQ | - FLAG_REMOTE_HYBRID | - FLAG_REMOTE_HYBRID_REQ, - - E, - F, - EE, - FF, - S, - SE, - FLIP_DIR, - S, - ES - }; - - private static final short[] noise_pattern_NXnoidh_hfs = { - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - S, - EE, - FF, - ES - }; - - private static final short[] noise_pattern_XXnoidh_hfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - S, - EE, - FF, - ES, - FLIP_DIR, - S, - SE - }; - - private static final short[] noise_pattern_KXnoidh_hfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - S, - EE, - FF, - SE, - ES - }; - - private static final short[] noise_pattern_IKnoidh_hfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - S, - ES, - SS, - FLIP_DIR, - E, - F, - EE, - FF, - SE - }; - - private static final short[] noise_pattern_IXnoidh_hfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - S, - FLIP_DIR, - E, - F, - S, - EE, - FF, - SE, - ES - }; - - /** - * Look up the description information for a pattern. - * - * @param name The name of the pattern. - * @return The pattern description or null. - */ - public static short[] lookup(String name) - { - if (name.equals("N")) - return noise_pattern_N; - else if (name.equals("K")) - return noise_pattern_K; - else if (name.equals("X")) - return noise_pattern_X; - else if (name.equals("NN")) - return noise_pattern_NN; - else if (name.equals("NK")) - return noise_pattern_NK; - else if (name.equals("NX")) - return noise_pattern_NX; - else if (name.equals("XN")) - return noise_pattern_XN; - else if (name.equals("XK")) - return noise_pattern_XK; - else if (name.equals("XX")) - return noise_pattern_XX; - else if (name.equals("KN")) - return noise_pattern_KN; - else if (name.equals("KK")) - return noise_pattern_KK; - else if (name.equals("KX")) - return noise_pattern_KX; - else if (name.equals("IN")) - return noise_pattern_IN; - else if (name.equals("IK")) - return noise_pattern_IK; - else if (name.equals("IX")) - return noise_pattern_IX; - else if (name.equals("XXfallback")) - return noise_pattern_XXfallback; - else if (name.equals("Xnoidh")) - return noise_pattern_Xnoidh; - else if (name.equals("NXnoidh")) - return noise_pattern_NXnoidh; - else if (name.equals("XXnoidh")) - return noise_pattern_XXnoidh; - else if (name.equals("KXnoidh")) - return noise_pattern_KXnoidh; - else if (name.equals("IKnoidh")) - return noise_pattern_IKnoidh; - else if (name.equals("IXnoidh")) - return noise_pattern_IXnoidh; - else if (name.equals("NNhfs")) - return noise_pattern_NNhfs; - else if (name.equals("NKhfs")) - return noise_pattern_NKhfs; - else if (name.equals("NXhfs")) - return noise_pattern_NXhfs; - else if (name.equals("XNhfs")) - return noise_pattern_XNhfs; - else if (name.equals("XKhfs")) - return noise_pattern_XKhfs; - else if (name.equals("XXhfs")) - return noise_pattern_XXhfs; - else if (name.equals("KNhfs")) - return noise_pattern_KNhfs; - else if (name.equals("KKhfs")) - return noise_pattern_KKhfs; - else if (name.equals("KXhfs")) - return noise_pattern_KXhfs; - else if (name.equals("INhfs")) - return noise_pattern_INhfs; - else if (name.equals("IKhfs")) - return noise_pattern_IKhfs; - else if (name.equals("IXhfs")) - return noise_pattern_IXhfs; - else if (name.equals("XXfallback+hfs")) - return noise_pattern_XXfallback_hfs; - else if (name.equals("NXnoidh+hfs")) - return noise_pattern_NXnoidh_hfs; - else if (name.equals("XXnoidh+hfs")) - return noise_pattern_XXnoidh_hfs; - else if (name.equals("KXnoidh+hfs")) - return noise_pattern_KXnoidh_hfs; - else if (name.equals("IKnoidh+hfs")) - return noise_pattern_IKnoidh_hfs; - else if (name.equals("IXnoidh+hfs")) - return noise_pattern_IXnoidh_hfs; - return null; - } - - /** - * Reverses the local and remote flags for a pattern. - * - * @param flags The flags, assuming that the initiator is "local". - * @return The reversed flags, with the responder now being "local". - */ - public static short reverseFlags(short flags) - { - return (short)(((flags >> 8) & 0x00FF) | ((flags << 8) & 0xFF00)); - } +internal object Pattern { + // Token codes. + const val S: Short = 1 + const val E: Short = 2 + const val EE: Short = 3 + const val ES: Short = 4 + const val SE: Short = 5 + const val SS: Short = 6 + const val F: Short = 7 + const val FF: Short = 8 + const val FLIP_DIR: Short = 255 + + // Pattern flag bits. + const val FLAG_LOCAL_STATIC: Short = 0x0001 + const val FLAG_LOCAL_EPHEMERAL: Short = 0x0002 + const val FLAG_LOCAL_REQUIRED: Short = 0x0004 + const val FLAG_LOCAL_EPHEM_REQ: Short = 0x0008 + const val FLAG_LOCAL_HYBRID: Short = 0x0010 + const val FLAG_LOCAL_HYBRID_REQ: Short = 0x0020 + const val FLAG_REMOTE_STATIC: Short = 0x0100 + const val FLAG_REMOTE_EPHEMERAL: Short = 0x0200 + const val FLAG_REMOTE_REQUIRED: Short = 0x0400 + const val FLAG_REMOTE_EPHEM_REQ: Short = 0x0800 + const val FLAG_REMOTE_HYBRID: Short = 0x1000 + const val FLAG_REMOTE_HYBRID_REQ: Short = 0x2000 + + private fun flags(vararg bits: Short): Short { + var v = 0 + for (b in bits) v = v or b.toInt() + return v.toShort() + } + + private val patterns: Map = mapOf( + "N" to shortArrayOf( + flags(FLAG_LOCAL_EPHEMERAL, FLAG_REMOTE_STATIC, FLAG_REMOTE_REQUIRED), + E, ES + ), + "K" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_REQUIRED, + FLAG_REMOTE_STATIC, FLAG_REMOTE_REQUIRED), + E, ES, SS + ), + "X" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, + FLAG_REMOTE_STATIC, FLAG_REMOTE_REQUIRED), + E, ES, S, SS + ), + "NN" to shortArrayOf( + flags(FLAG_LOCAL_EPHEMERAL, FLAG_REMOTE_EPHEMERAL), + E, FLIP_DIR, E, EE + ), + "NK" to shortArrayOf( + flags(FLAG_LOCAL_EPHEMERAL, FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, + FLAG_REMOTE_REQUIRED), + E, ES, FLIP_DIR, E, EE + ), + "NX" to shortArrayOf( + flags(FLAG_LOCAL_EPHEMERAL, FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL), + E, FLIP_DIR, E, EE, S, ES + ), + "XN" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_REMOTE_EPHEMERAL), + E, FLIP_DIR, E, EE, FLIP_DIR, S, SE + ), + "XK" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_REQUIRED), + E, ES, FLIP_DIR, E, EE, FLIP_DIR, S, SE + ), + "XX" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL), + E, FLIP_DIR, E, EE, S, ES, FLIP_DIR, S, SE + ), + "KN" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_REQUIRED, + FLAG_REMOTE_EPHEMERAL), + E, FLIP_DIR, E, EE, SE + ), + "KK" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_REQUIRED, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_REQUIRED), + E, ES, SS, FLIP_DIR, E, EE, SE + ), + "KX" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_REQUIRED, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL), + E, FLIP_DIR, E, EE, SE, S, ES + ), + "IN" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_REMOTE_EPHEMERAL), + E, S, FLIP_DIR, E, EE, SE + ), + "IK" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_REQUIRED), + E, ES, S, SS, FLIP_DIR, E, EE, SE + ), + "IX" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL), + E, S, FLIP_DIR, E, EE, SE, S, ES + ), + "XXfallback" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_EPHEM_REQ), + E, EE, S, SE, FLIP_DIR, S, ES + ), + "Xnoidh" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, + FLAG_REMOTE_STATIC, FLAG_REMOTE_REQUIRED), + E, S, ES, SS + ), + "NXnoidh" to shortArrayOf( + flags(FLAG_LOCAL_EPHEMERAL, FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL), + E, FLIP_DIR, E, S, EE, ES + ), + "XXnoidh" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL), + E, FLIP_DIR, E, S, EE, ES, FLIP_DIR, S, SE + ), + "KXnoidh" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_REQUIRED, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL), + E, FLIP_DIR, E, S, EE, SE, ES + ), + "IKnoidh" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_REQUIRED), + E, S, ES, SS, FLIP_DIR, E, EE, SE + ), + "IXnoidh" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL), + E, S, FLIP_DIR, E, S, EE, SE, ES + ), + "NNhfs" to shortArrayOf( + flags(FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_HYBRID, + FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_HYBRID), + E, F, FLIP_DIR, E, F, EE, FF + ), + "NKhfs" to shortArrayOf( + flags(FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_HYBRID, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_HYBRID, + FLAG_REMOTE_REQUIRED), + E, F, ES, FLIP_DIR, E, F, EE, FF + ), + "NXhfs" to shortArrayOf( + flags(FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_HYBRID, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_HYBRID), + E, F, FLIP_DIR, E, F, EE, FF, S, ES + ), + "XNhfs" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_HYBRID, + FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_HYBRID), + E, F, FLIP_DIR, E, F, EE, FF, FLIP_DIR, S, SE + ), + "XKhfs" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_HYBRID, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_HYBRID, + FLAG_REMOTE_REQUIRED), + E, F, ES, FLIP_DIR, E, F, EE, FF, FLIP_DIR, S, SE + ), + "XXhfs" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_HYBRID, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_HYBRID), + E, F, FLIP_DIR, E, F, EE, FF, S, ES, FLIP_DIR, S, SE + ), + "KNhfs" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_REQUIRED, + FLAG_LOCAL_HYBRID, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_HYBRID), + E, F, FLIP_DIR, E, F, EE, FF, SE + ), + "KKhfs" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_REQUIRED, + FLAG_LOCAL_HYBRID, FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, + FLAG_REMOTE_HYBRID, FLAG_REMOTE_REQUIRED), + E, F, ES, SS, FLIP_DIR, E, F, EE, FF, SE + ), + "KXhfs" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_REQUIRED, + FLAG_LOCAL_HYBRID, FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, + FLAG_REMOTE_HYBRID), + E, F, FLIP_DIR, E, F, EE, FF, SE, S, ES + ), + "INhfs" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_HYBRID, + FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_HYBRID), + E, F, S, FLIP_DIR, E, F, EE, FF, SE + ), + "IKhfs" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_HYBRID, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_HYBRID, + FLAG_REMOTE_REQUIRED), + E, F, ES, S, SS, FLIP_DIR, E, F, EE, FF, SE + ), + "IXhfs" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_HYBRID, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_HYBRID), + E, F, S, FLIP_DIR, E, F, EE, FF, SE, S, ES + ), + "XXfallback+hfs" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_HYBRID, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_EPHEM_REQ, + FLAG_REMOTE_HYBRID, FLAG_REMOTE_HYBRID_REQ), + E, F, EE, FF, S, SE, FLIP_DIR, S, ES + ), + "NXnoidh+hfs" to shortArrayOf( + flags(FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_HYBRID, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_HYBRID), + E, F, FLIP_DIR, E, F, S, EE, FF, ES + ), + "XXnoidh+hfs" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_HYBRID, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_HYBRID), + E, F, FLIP_DIR, E, F, S, EE, FF, ES, FLIP_DIR, S, SE + ), + "KXnoidh+hfs" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_REQUIRED, + FLAG_LOCAL_HYBRID, FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, + FLAG_REMOTE_HYBRID), + E, F, FLIP_DIR, E, F, S, EE, FF, SE, ES + ), + // Note: FLAG_REMOTE_EPHEMERAL is listed twice in the original Java + // pattern definition. Kept here verbatim — the duplicate or-bit is a + // no-op, so behavior matches the upstream implementation. + "IKnoidh+hfs" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_HYBRID, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_EPHEMERAL, + FLAG_REMOTE_HYBRID), + E, F, S, ES, SS, FLIP_DIR, E, F, EE, FF, SE + ), + "IXnoidh+hfs" to shortArrayOf( + flags(FLAG_LOCAL_STATIC, FLAG_LOCAL_EPHEMERAL, FLAG_LOCAL_HYBRID, + FLAG_REMOTE_STATIC, FLAG_REMOTE_EPHEMERAL, FLAG_REMOTE_HYBRID), + E, F, S, FLIP_DIR, E, F, S, EE, FF, SE, ES + ), + ) + + /** + * Look up the description information for a pattern. + * + * @param name The name of the pattern. + * @return The pattern description or null. + */ + @JvmStatic + fun lookup(name: String): ShortArray? = patterns[name] + + /** + * Reverses the local and remote flags for a pattern. + * + * @param flags The flags, assuming that the initiator is "local". + * @return The reversed flags, with the responder now being "local". + */ + @JvmStatic + fun reverseFlags(flags: Short): Short { + val v = flags.toInt() and 0xFFFF + return (((v shr 8) and 0x00FF) or ((v shl 8) and 0xFF00)).toShort() + } } From ec60cb35434228710871f813448618ef0c914e93 Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:36:07 +0400 Subject: [PATCH 20/37] Rename .java to .kt --- .../southernstorm/protocol/{CipherState.java => CipherState.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/{CipherState.java => CipherState.kt} (100%) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherState.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherState.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherState.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherState.kt From e8882d6ab11cf961afda76e1f546584b41c8679f Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:36:07 +0400 Subject: [PATCH 21/37] 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 { From 8b3fe53c997d6fdecd96d9c2f18bfcdda4c77fcf Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:37:47 +0400 Subject: [PATCH 22/37] Rename .java to .kt --- .../protocol/{CipherStatePair.java => CipherStatePair.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/{CipherStatePair.java => CipherStatePair.kt} (100%) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherStatePair.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherStatePair.kt similarity index 100% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherStatePair.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherStatePair.kt From fbb13424395cc156557fc7f747dbbf6a37de172a Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:37:47 +0400 Subject: [PATCH 23/37] Convert CipherStatePair from Java to Kotlin --- .../southernstorm/protocol/CipherStatePair.kt | 112 ++++++------------ 1 file changed, 37 insertions(+), 75 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherStatePair.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherStatePair.kt index 88563fce..9214b8f5 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherStatePair.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/CipherStatePair.kt @@ -19,88 +19,50 @@ * 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 /** * Class that contains a pair of CipherState objects. - * + * * CipherState pairs typically arise when HandshakeState.split() is called. + * + * @param sender The CipherState to use to send packets to the remote party. + * @param receiver The CipherState to use to receive packets from the remote party. */ -public final class CipherStatePair implements Destroyable { +class CipherStatePair( + var sender: CipherState?, + var receiver: CipherState?, +) : Destroyable { - private CipherState send; - private CipherState recv; + /** + * Destroys the receiving CipherState and retains only the sending CipherState. + * + * This function is intended for use with one-way handshake patterns. + */ + fun senderOnly() { + receiver?.destroy() + receiver = null + } - /** - * Constructs a pair of CipherState objects. - * - * @param sender The CipherState to use to send packets to the remote party. - * @param receiver The CipherState to use to receive packets from the remote party. - */ - public CipherStatePair(CipherState sender, CipherState receiver) - { - send = sender; - recv = receiver; - } + /** + * Destroys the sending CipherState and retains only the receiving CipherState. + * + * This function is intended for use with one-way handshake patterns. + */ + fun receiverOnly() { + sender?.destroy() + sender = null + } - /** - * Gets the CipherState to use to send packets to the remote party. - * - * @return The sending CipherState. - */ - public CipherState getSender() { - return send; - } - - /** - * Gets the CipherState to use to receive packets from the remote party. - * - * @return The receiving CipherState. - */ - public CipherState getReceiver() { - return recv; - } + /** + * Swaps the sender and receiver. + */ + fun swap() { + sender = receiver.also { receiver = sender } + } - /** - * Destroys the receiving CipherState and retains only the sending CipherState. - * - * This function is intended for use with one-way handshake patterns. - */ - public void senderOnly() - { - if (recv != null) { - recv.destroy(); - recv = null; - } - } - - /** - * Destroys the sending CipherState and retains only the receiving CipherState. - * - * This function is intended for use with one-way handshake patterns. - */ - public void receiverOnly() - { - if (send != null) { - send.destroy(); - send = null; - } - } - - /** - * Swaps the sender and receiver. - */ - public void swap() - { - CipherState temp = send; - send = recv; - recv = temp; - } - - @Override - public void destroy() { - senderOnly(); - receiverOnly(); - } + override fun destroy() { + senderOnly() + receiverOnly() + } } From 597d9a1ba9c74442c42b064153f95cb8af06c22c Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:43:48 +0400 Subject: [PATCH 24/37] refactor(noise/protocol): drop invalid @JvmName on CipherState.macLength MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove `@get:JvmName("getMACLength")` — Kotlin rejects @JvmName on interface getters and all remaining callers in this module are Kotlin. - macLength is now exposed idiomatically as a Kotlin property. Co-Authored-By: Claude Opus 4.7 --- .../protocol/AESGCMFallbackCipherState.java | 265 ---- .../protocol/AESGCMFallbackCipherState.kt | 263 ++++ .../protocol/AESGCMOnCtrCipherState.java | 335 ----- .../protocol/AESGCMOnCtrCipherState.kt | 333 +++++ .../protocol/ChaChaPolyCipherState.java | 290 ---- .../protocol/ChaChaPolyCipherState.kt | 296 ++++ .../southernstorm/protocol/CipherState.kt | 3 - .../protocol/Curve25519DHState.java | 156 -- .../protocol/Curve25519DHState.kt | 131 ++ .../protocol/Curve448DHState.java | 156 -- .../southernstorm/protocol/Curve448DHState.kt | 131 ++ .../noise/southernstorm/protocol/DHState.java | 156 -- .../noise/southernstorm/protocol/DHState.kt | 154 ++ .../{DHStateHybrid.java => DHStateHybrid.kt} | 60 +- .../protocol/HandshakeState.java | 1290 ----------------- .../southernstorm/protocol/HandshakeState.kt | 1196 +++++++++++++++ .../protocol/NewHopeDHState.java | 340 ----- .../southernstorm/protocol/NewHopeDHState.kt | 266 ++++ .../protocol/SymmetricState.java | 532 ------- .../southernstorm/protocol/SymmetricState.kt | 601 ++++++++ .../southernstorm/protocol/package-info.java | 7 - 21 files changed, 3400 insertions(+), 3561 deletions(-) delete mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMFallbackCipherState.java create mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMFallbackCipherState.kt delete mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMOnCtrCipherState.java create mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMOnCtrCipherState.kt delete mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/ChaChaPolyCipherState.java create mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/ChaChaPolyCipherState.kt delete mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve25519DHState.java create mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve25519DHState.kt delete mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve448DHState.java create mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve448DHState.kt delete mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHState.java create mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHState.kt rename app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/{DHStateHybrid.java => DHStateHybrid.kt} (53%) delete mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/HandshakeState.java create mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/HandshakeState.kt delete mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/NewHopeDHState.java create mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/NewHopeDHState.kt delete mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.java create mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.kt delete mode 100644 app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/package-info.java diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMFallbackCipherState.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMFallbackCipherState.java deleted file mode 100644 index 683dcfc4..00000000 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMFallbackCipherState.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (C) 2016 Southern Storm Software, Pty Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * 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; - -import java.util.Arrays; - -import javax.crypto.BadPaddingException; -import javax.crypto.ShortBufferException; - -import com.bitchat.android.noise.southernstorm.crypto.GHASH; -import com.bitchat.android.noise.southernstorm.crypto.RijndaelAES; - -/** - * Fallback implementation of "AESGCM" on platforms where - * the JCA/JCE does not have a suitable GCM or CTR provider. - */ -class AESGCMFallbackCipherState implements CipherState { - - private RijndaelAES aes; - private long n; - private byte[] iv; - private byte[] enciv; - private byte[] hashKey; - private GHASH ghash; - private boolean haskey; - - /** - * Constructs a new cipher state for the "AESGCM" algorithm. - */ - public AESGCMFallbackCipherState() - { - aes = new RijndaelAES(); - n = 0; - iv = new byte [16]; - enciv = new byte [16]; - hashKey = new byte [16]; - ghash = new GHASH(); - haskey = false; - } - - @Override - public void destroy() { - aes.destroy(); - ghash.destroy(); - Noise.destroy(hashKey); - Noise.destroy(iv); - Noise.destroy(enciv); - } - - @Override - public String getCipherName() { - return "AESGCM"; - } - - @Override - public int getKeyLength() { - return 32; - } - - @Override - public int getMACLength() { - return haskey ? 16 : 0; - } - - @Override - public void initializeKey(byte[] key, int offset) { - // Set up the AES key. - aes.setupEnc(key, offset, 256); - haskey = true; - - // Generate the hashing key by encrypting a block of zeroes. - Arrays.fill(hashKey, (byte)0); - aes.encrypt(hashKey, 0, hashKey, 0); - ghash.reset(hashKey, 0); - - // Reset the nonce. - n = 0; - } - - @Override - public boolean hasKey() { - return haskey; - } - - /** - * Set up to encrypt or decrypt the next packet. - * - * @param ad The associated data for the packet. - */ - private void setup(byte[] ad) - { - // Check for nonce wrap-around. - if (n == -1L) - throw new IllegalStateException("Nonce has wrapped around"); - - // Format the counter/IV block. - iv[0] = 0; - iv[1] = 0; - iv[2] = 0; - iv[3] = 0; - iv[4] = (byte)(n >> 56); - iv[5] = (byte)(n >> 48); - iv[6] = (byte)(n >> 40); - iv[7] = (byte)(n >> 32); - iv[8] = (byte)(n >> 24); - iv[9] = (byte)(n >> 16); - iv[10] = (byte)(n >> 8); - iv[11] = (byte)n; - iv[12] = 0; - iv[13] = 0; - iv[14] = 0; - iv[15] = 1; - ++n; - - // 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, (byte)0); - aes.encrypt(iv, 0, hashKey, 0); - - // Initialize the GHASH with the associated data value. - ghash.reset(); - if (ad != null) { - ghash.update(ad, 0, ad.length); - ghash.pad(); - } - } - - /** - * Encrypts a block in CTR mode. - * - * @param plaintext The plaintext to encrypt. - * @param plaintextOffset Offset of the first plaintext byte. - * @param ciphertext The resulting ciphertext. - * @param ciphertextOffset Offset of the first ciphertext byte. - * @param length The number of bytes to encrypt. - * - * This function can also be used to decrypt. - */ - private void encryptCTR(byte[] plaintext, int plaintextOffset, byte[] ciphertext, int ciphertextOffset, int length) - { - while (length > 0) { - // Increment the IV and encrypt it to get the next keystream block. - if (++(iv[15]) == 0) - if (++(iv[14]) == 0) - if (++(iv[13]) == 0) - ++(iv[12]); - aes.encrypt(iv, 0, enciv, 0); - - // XOR the keystream block with the plaintext to create the ciphertext. - int temp = length; - if (temp > 16) - temp = 16; - for (int index = 0; index < temp; ++index) - ciphertext[ciphertextOffset + index] = (byte)(plaintext[plaintextOffset + index] ^ enciv[index]); - - // Advance to the next block. - plaintextOffset += temp; - ciphertextOffset += temp; - length -= temp; - } - } - - @Override - public int encryptWithAd(byte[] ad, byte[] plaintext, int plaintextOffset, - byte[] ciphertext, int ciphertextOffset, int length) - throws ShortBufferException { - int space; - if (ciphertextOffset < 0 || ciphertextOffset > ciphertext.length) - throw new IllegalArgumentException(); - if (length < 0 || plaintextOffset < 0 || plaintextOffset > plaintext.length || length > plaintext.length || (plaintext.length - plaintextOffset) < length) - throw new IllegalArgumentException(); - space = ciphertext.length - ciphertextOffset; - if (!haskey) { - // The key is not set yet - return the plaintext as-is. - if (length > space) - throw new ShortBufferException(); - if (plaintext != ciphertext || plaintextOffset != ciphertextOffset) - System.arraycopy(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length); - return length; - } - if (space < 16 || length > (space - 16)) - throw new ShortBufferException(); - setup(ad); - encryptCTR(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length); - ghash.update(ciphertext, ciphertextOffset, length); - ghash.pad(ad != null ? ad.length : 0, length); - ghash.finish(ciphertext, ciphertextOffset + length, 16); - for (int index = 0; index < 16; ++index) - ciphertext[ciphertextOffset + length + index] ^= hashKey[index]; - return length + 16; - } - - @Override - public int decryptWithAd(byte[] ad, byte[] ciphertext, - int ciphertextOffset, byte[] plaintext, int plaintextOffset, - int length) throws ShortBufferException, BadPaddingException { - int space; - if (ciphertextOffset < 0 || ciphertextOffset > ciphertext.length) - throw new IllegalArgumentException(); - else - space = ciphertext.length - ciphertextOffset; - if (length > space) - throw new ShortBufferException(); - if (length < 0 || plaintextOffset < 0 || plaintextOffset > plaintext.length || length > ciphertext.length || (ciphertext.length - ciphertextOffset) < length) - throw new IllegalArgumentException(); - space = plaintext.length - plaintextOffset; - if (!haskey) { - // The key is not set yet - return the ciphertext as-is. - if (length > space) - throw new ShortBufferException(); - if (plaintext != ciphertext || plaintextOffset != ciphertextOffset) - System.arraycopy(ciphertext, ciphertextOffset, plaintext, plaintextOffset, length); - return length; - } - if (length < 16) - Noise.throwBadTagException(); - int dataLen = length - 16; - if (dataLen > space) - throw new ShortBufferException(); - setup(ad); - ghash.update(ciphertext, ciphertextOffset, dataLen); - ghash.pad(ad != null ? ad.length : 0, dataLen); - ghash.finish(enciv, 0, 16); - int temp = 0; - for (int index = 0; index < 16; ++index) - temp |= (hashKey[index] ^ enciv[index] ^ ciphertext[ciphertextOffset + dataLen + index]); - if ((temp & 0xFF) != 0) - Noise.throwBadTagException(); - encryptCTR(ciphertext, ciphertextOffset, plaintext, plaintextOffset, dataLen); - return dataLen; - } - - @Override - public CipherState fork(byte[] key, int offset) { - CipherState cipher; - cipher = new AESGCMFallbackCipherState(); - cipher.initializeKey(key, offset); - return cipher; - } - - @Override - public void setNonce(long nonce) { - n = nonce; - } -} 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 new file mode 100644 index 00000000..4820385a --- /dev/null +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMFallbackCipherState.kt @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2016 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * 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 + +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 + +/** + * Fallback implementation of "AESGCM" on platforms where + * 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 + } + + override fun destroy() { + aes.destroy() + ghash.destroy() + destroy(hashKey) + destroy(iv) + destroy(enciv) + } + + val cipherName: String + get() = "AESGCM" + + val keyLength: Int + get() = 32 + + val mACLength: Int + get() = if (haskey) 16 else 0 + + override fun initializeKey(key: ByteArray, offset: Int) { + // Set up the AES key. + aes.setupEnc(key, offset, 256) + haskey = true + + // Generate the hashing key by encrypting a block of zeroes. + Arrays.fill(hashKey, 0.toByte()) + aes.encrypt(hashKey, 0, hashKey, 0) + ghash.reset(hashKey, 0) + + + // Reset the nonce. + n = 0 + } + + override fun hasKey(): Boolean { + return haskey + } + + /** + * Set up to encrypt or decrypt the next packet. + * + * @param ad The associated data for the packet. + */ + private fun setup(ad: ByteArray?) { + // Check for nonce wrap-around. + check(n != -1L) { "Nonce has wrapped around" } + + + // Format the counter/IV block. + iv[0] = 0 + iv[1] = 0 + iv[2] = 0 + iv[3] = 0 + iv[4] = (n shr 56).toByte() + iv[5] = (n shr 48).toByte() + iv[6] = (n shr 40).toByte() + iv[7] = (n shr 32).toByte() + iv[8] = (n shr 24).toByte() + iv[9] = (n shr 16).toByte() + iv[10] = (n shr 8).toByte() + iv[11] = n.toByte() + iv[12] = 0 + iv[13] = 0 + iv[14] = 0 + iv[15] = 1 + ++n + + + // 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()) + aes.encrypt(iv, 0, hashKey, 0) + + + // Initialize the GHASH with the associated data value. + ghash.reset() + if (ad != null) { + ghash.update(ad, 0, ad.size) + ghash.pad() + } + } + + /** + * Encrypts a block in CTR mode. + * + * @param plaintext The plaintext to encrypt. + * @param plaintextOffset Offset of the first plaintext byte. + * @param ciphertext The resulting ciphertext. + * @param ciphertextOffset Offset of the first ciphertext byte. + * @param length The number of bytes to encrypt. + * + * This function can also be used to decrypt. + */ + private fun encryptCTR( + plaintext: ByteArray, + plaintextOffset: Int, + ciphertext: ByteArray, + ciphertextOffset: Int, + length: Int + ) { + var plaintextOffset = plaintextOffset + 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]) + aes.encrypt(iv, 0, enciv, 0) + + + // XOR the keystream block with the plaintext to create the ciphertext. + var temp = length + if (temp > 16) temp = 16 + for (index in 0.. ciphertext.size)) + require(!(length < 0 || plaintextOffset < 0 || plaintextOffset > plaintext.size || length > plaintext.size || (plaintext.size - plaintextOffset) < length)) + space = 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( + plaintext, + plaintextOffset, + ciphertext, + ciphertextOffset, + length + ) + return length + } + if (space < 16 || length > (space - 16)) throw ShortBufferException() + setup(ad) + encryptCTR(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length) + 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() + return length + 16 + } + + @Throws(ShortBufferException::class, BadPaddingException::class) + override fun decryptWithAd( + ad: ByteArray?, + ciphertext: ByteArray, + ciphertextOffset: Int, + plaintext: ByteArray, + plaintextOffset: Int, + length: Int + ): Int { + var space: Int + require(!(ciphertextOffset < 0 || ciphertextOffset > ciphertext.size)) + space = 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( + ciphertext, + ciphertextOffset, + plaintext, + plaintextOffset, + length + ) + return length + } + if (length < 16) throwBadTagException() + val dataLen = length - 16 + if (dataLen > space) throw ShortBufferException() + setup(ad) + ghash.update(ciphertext, ciphertextOffset, dataLen) + ghash.pad((if (ad != null) ad.size else 0).toLong(), dataLen.toLong()) + ghash.finish(enciv, 0, 16) + var temp = 0 + for (index in 0..15) temp = + temp or (hashKey[index].toInt() xor enciv[index].toInt() xor ciphertext[ciphertextOffset + dataLen + index].toInt()) + if ((temp and 0xFF) != 0) throwBadTagException() + encryptCTR(ciphertext, ciphertextOffset, plaintext, plaintextOffset, dataLen) + return dataLen + } + + override fun fork(key: ByteArray, offset: Int): CipherState { + val cipher: CipherState + cipher = AESGCMFallbackCipherState() + cipher.initializeKey(key, offset) + return cipher + } + + override fun setNonce(nonce: Long) { + n = nonce + } +} diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMOnCtrCipherState.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMOnCtrCipherState.java deleted file mode 100644 index 7795cbdb..00000000 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMOnCtrCipherState.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (C) 2016 Southern Storm Software, Pty Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * 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; - -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -import com.bitchat.android.noise.southernstorm.crypto.GHASH; - -/** - * Emulates the "AESGCM" cipher for Noise using the "AES/CTR/NoPadding" - * transformation from JCA/JCE. - * - * This class is used on platforms that don't have "AES/GCM/NoPadding", - * but which do have the older "AES/CTR/NoPadding". - */ -class AESGCMOnCtrCipherState implements CipherState { - - private Cipher cipher; - private SecretKeySpec keySpec; - private long n; - private byte[] iv; - private byte[] hashKey; - private GHASH ghash; - - /** - * Constructs a new cipher state for the "AESGCM" algorithm. - * - * @throws NoSuchAlgorithmException The system does not have a - * provider for this algorithm. - */ - public AESGCMOnCtrCipherState() throws NoSuchAlgorithmException - { - try { - cipher = Cipher.getInstance("AES/CTR/NoPadding"); - } catch (NoSuchPaddingException e) { - // AES/CTR is available, but not the unpadded version? Huh? - throw new NoSuchAlgorithmException("AES/CTR/NoPadding not available", e); - } - keySpec = null; - n = 0; - iv = new byte [16]; - hashKey = new byte [16]; - ghash = new GHASH(); - - // 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. - try { - SecretKeySpec spec = new SecretKeySpec(new byte [32], "AES"); - IvParameterSpec params = new IvParameterSpec(iv); - cipher.init(Cipher.ENCRYPT_MODE, spec, params); - } catch (InvalidKeyException e) { - throw new NoSuchAlgorithmException("AES/CTR/NoPadding does not support 256-bit keys", e); - } catch (InvalidAlgorithmParameterException e) { - throw new NoSuchAlgorithmException("AES/CTR/NoPadding does not support 256-bit keys", e); - } - } - - @Override - public void destroy() { - // There doesn't seem to be a standard API to clean out a Cipher. - // So we instead set the key and IV to all-zeroes to hopefully - // destroy the sensitive data in the cipher instance. - ghash.destroy(); - Noise.destroy(hashKey); - Noise.destroy(iv); - keySpec = new SecretKeySpec(new byte [32], "AES"); - IvParameterSpec params = new IvParameterSpec(iv); - try { - cipher.init(Cipher.ENCRYPT_MODE, keySpec, params); - } catch (InvalidKeyException e) { - // Shouldn't happen. - } catch (InvalidAlgorithmParameterException e) { - // Shouldn't happen. - } - } - - @Override - public String getCipherName() { - return "AESGCM"; - } - - @Override - public int getKeyLength() { - return 32; - } - - @Override - public int getMACLength() { - return keySpec != null ? 16 : 0; - } - - @Override - public void initializeKey(byte[] key, int offset) { - // Set the encryption key. - keySpec = new SecretKeySpec(key, offset, 32, "AES"); - - // Generate the hashing key by encrypting a block of zeroes. - Arrays.fill(iv, (byte)0); - Arrays.fill(hashKey, (byte)0); - try { - cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv)); - } catch (InvalidKeyException e) { - // Shouldn't happen. - throw new IllegalStateException(e); - } catch (InvalidAlgorithmParameterException e) { - // Shouldn't happen. - throw new IllegalStateException(e); - } - try { - int result = cipher.update(hashKey, 0, 16, hashKey, 0); - cipher.doFinal(hashKey, result); - } catch (ShortBufferException e) { - // Shouldn't happen. - throw new IllegalStateException(e); - } catch (IllegalBlockSizeException e) { - // Shouldn't happen. - throw new IllegalStateException(e); - } catch (BadPaddingException e) { - // Shouldn't happen. - throw new IllegalStateException(e); - } - ghash.reset(hashKey, 0); - - // Reset the nonce. - n = 0; - } - - @Override - public boolean hasKey() { - return keySpec != null; - } - - /** - * Set up to encrypt or decrypt the next packet. - * - * @param ad The associated data for the packet. - */ - private void setup(byte[] ad) throws InvalidKeyException, InvalidAlgorithmParameterException - { - // Check for nonce wrap-around. - if (n == -1L) - throw new IllegalStateException("Nonce has wrapped around"); - - // Format the counter/IV block for AES/CTR/NoPadding. - iv[0] = 0; - iv[1] = 0; - iv[2] = 0; - iv[3] = 0; - iv[4] = (byte)(n >> 56); - iv[5] = (byte)(n >> 48); - iv[6] = (byte)(n >> 40); - iv[7] = (byte)(n >> 32); - iv[8] = (byte)(n >> 24); - iv[9] = (byte)(n >> 16); - iv[10] = (byte)(n >> 8); - iv[11] = (byte)n; - iv[12] = 0; - iv[13] = 0; - iv[14] = 0; - iv[15] = 1; - ++n; - - // Initialize the CTR mode cipher with the key and IV. - cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv)); - - // 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, (byte)0); - try { - cipher.update(hashKey, 0, 16, hashKey, 0); - } catch (ShortBufferException e) { - // Shouldn't happen. - throw new IllegalStateException(e); - } - - // Initialize the GHASH with the associated data value. - ghash.reset(); - if (ad != null) { - ghash.update(ad, 0, ad.length); - ghash.pad(); - } - } - - @Override - public int encryptWithAd(byte[] ad, byte[] plaintext, int plaintextOffset, - byte[] ciphertext, int ciphertextOffset, int length) - throws ShortBufferException { - int space; - if (ciphertextOffset < 0 || ciphertextOffset > ciphertext.length) - throw new IllegalArgumentException(); - if (length < 0 || plaintextOffset < 0 || plaintextOffset > plaintext.length || length > plaintext.length || (plaintext.length - plaintextOffset) < length) - throw new IllegalArgumentException(); - space = ciphertext.length - ciphertextOffset; - if (keySpec == null) { - // The key is not set yet - return the plaintext as-is. - if (length > space) - throw new ShortBufferException(); - if (plaintext != ciphertext || plaintextOffset != ciphertextOffset) - System.arraycopy(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length); - return length; - } - if (space < 16 || length > (space - 16)) - throw new ShortBufferException(); - try { - setup(ad); - int result = cipher.update(plaintext, plaintextOffset, length, ciphertext, ciphertextOffset); - cipher.doFinal(ciphertext, ciphertextOffset + result); - } catch (InvalidKeyException e) { - // Shouldn't happen. - throw new IllegalStateException(e); - } catch (InvalidAlgorithmParameterException e) { - // Shouldn't happen. - throw new IllegalStateException(e); - } catch (IllegalBlockSizeException e) { - // Shouldn't happen. - throw new IllegalStateException(e); - } catch (BadPaddingException e) { - // Shouldn't happen. - throw new IllegalStateException(e); - } - ghash.update(ciphertext, ciphertextOffset, length); - ghash.pad(ad != null ? ad.length : 0, length); - ghash.finish(ciphertext, ciphertextOffset + length, 16); - for (int index = 0; index < 16; ++index) - ciphertext[ciphertextOffset + length + index] ^= hashKey[index]; - return length + 16; - } - - @Override - public int decryptWithAd(byte[] ad, byte[] ciphertext, - int ciphertextOffset, byte[] plaintext, int plaintextOffset, - int length) throws ShortBufferException, BadPaddingException { - int space; - if (ciphertextOffset < 0 || ciphertextOffset > ciphertext.length) - throw new IllegalArgumentException(); - else - space = ciphertext.length - ciphertextOffset; - if (length > space) - throw new ShortBufferException(); - if (length < 0 || plaintextOffset < 0 || plaintextOffset > plaintext.length || length > ciphertext.length || (ciphertext.length - ciphertextOffset) < length) - throw new IllegalArgumentException(); - space = plaintext.length - plaintextOffset; - if (keySpec == null) { - // The key is not set yet - return the ciphertext as-is. - if (length > space) - throw new ShortBufferException(); - if (plaintext != ciphertext || plaintextOffset != ciphertextOffset) - System.arraycopy(ciphertext, ciphertextOffset, plaintext, plaintextOffset, length); - return length; - } - if (length < 16) - Noise.throwBadTagException(); - int dataLen = length - 16; - if (dataLen > space) - throw new ShortBufferException(); - try { - setup(ad); - } catch (InvalidKeyException e) { - // Shouldn't happen. - throw new IllegalStateException(e); - } catch (InvalidAlgorithmParameterException e) { - // Shouldn't happen. - throw new IllegalStateException(e); - } - ghash.update(ciphertext, ciphertextOffset, dataLen); - ghash.pad(ad != null ? ad.length : 0, dataLen); - ghash.finish(iv, 0, 16); - int temp = 0; - for (int index = 0; index < 16; ++index) - temp |= (hashKey[index] ^ iv[index] ^ ciphertext[ciphertextOffset + dataLen + index]); - if ((temp & 0xFF) != 0) - Noise.throwBadTagException(); - try { - int result = cipher.update(ciphertext, ciphertextOffset, dataLen, plaintext, plaintextOffset); - cipher.doFinal(plaintext, plaintextOffset + result); - } catch (IllegalBlockSizeException e) { - // Shouldn't happen. - throw new IllegalStateException(e); - } catch (BadPaddingException e) { - // Shouldn't happen. - throw new IllegalStateException(e); - } - return dataLen; - } - - @Override - public CipherState fork(byte[] key, int offset) { - CipherState cipher; - try { - cipher = new AESGCMOnCtrCipherState(); - } catch (NoSuchAlgorithmException e) { - // Shouldn't happen. - return null; - } - cipher.initializeKey(key, offset); - return cipher; - } - - @Override - public void setNonce(long nonce) { - n = nonce; - } -} 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 new file mode 100644 index 00000000..9794bb88 --- /dev/null +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/AESGCMOnCtrCipherState.kt @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2016 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * 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 + +import com.bitchat.android.noise.southernstorm.crypto.GHASH +import com.bitchat.android.noise.southernstorm.protocol.Noise.destroy +import com.bitchat.android.noise.southernstorm.protocol.Noise.throwBadTagException +import java.security.InvalidAlgorithmParameterException +import java.security.InvalidKeyException +import java.security.NoSuchAlgorithmException +import java.util.Arrays +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException +import javax.crypto.ShortBufferException +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +/** + * Emulates the "AESGCM" cipher for Noise using the "AES/CTR/NoPadding" + * transformation from JCA/JCE. + * + * This class is used on platforms that don't have "AES/GCM/NoPadding", + * but which do have the older "AES/CTR/NoPadding". + */ +internal class AESGCMOnCtrCipherState : CipherState { + private val cipher: Cipher? = null + private var keySpec: SecretKeySpec? + private var n: Long + private val iv: ByteArray + private val hashKey: ByteArray + private val ghash: GHASH + + /** + * Constructs a new cipher state for the "AESGCM" algorithm. + * + * @throws NoSuchAlgorithmException The system does not have a + * provider for this algorithm. + */ + init { + try { + cipher = 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) + } + keySpec = null + n = 0 + iv = ByteArray(16) + hashKey = ByteArray(16) + ghash = GHASH() + + + // 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. + try { + val spec = SecretKeySpec(ByteArray(32), "AES") + val params = IvParameterSpec(iv) + cipher.init(Cipher.ENCRYPT_MODE, spec, params) + } catch (e: InvalidKeyException) { + throw NoSuchAlgorithmException("AES/CTR/NoPadding does not support 256-bit keys", e) + } catch (e: InvalidAlgorithmParameterException) { + throw NoSuchAlgorithmException("AES/CTR/NoPadding does not support 256-bit keys", e) + } + } + + override fun destroy() { + // There doesn't seem to be a standard API to clean out a Cipher. + // So we instead set the key and IV to all-zeroes to hopefully + // destroy the sensitive data in the cipher instance. + ghash.destroy() + destroy(hashKey) + destroy(iv) + keySpec = SecretKeySpec(ByteArray(32), "AES") + val params = IvParameterSpec(iv) + try { + cipher!!.init(Cipher.ENCRYPT_MODE, keySpec, params) + } catch (e: InvalidKeyException) { + // Shouldn't happen. + } catch (e: InvalidAlgorithmParameterException) { + // Shouldn't happen. + } + } + + val cipherName: String + get() = "AESGCM" + + val keyLength: Int + get() = 32 + + val mACLength: Int + get() = if (keySpec != null) 16 else 0 + + override fun initializeKey(key: ByteArray?, offset: Int) { + // Set the encryption key. + keySpec = SecretKeySpec(key, offset, 32, "AES") + + + // Generate the hashing key by encrypting a block of zeroes. + Arrays.fill(iv, 0.toByte()) + Arrays.fill(hashKey, 0.toByte()) + try { + cipher!!.init(Cipher.ENCRYPT_MODE, keySpec, IvParameterSpec(iv)) + } catch (e: InvalidKeyException) { + // Shouldn't happen. + throw IllegalStateException(e) + } catch (e: InvalidAlgorithmParameterException) { + // Shouldn't happen. + throw IllegalStateException(e) + } + try { + val result = cipher.update(hashKey, 0, 16, hashKey, 0) + cipher.doFinal(hashKey, result) + } catch (e: ShortBufferException) { + // Shouldn't happen. + throw IllegalStateException(e) + } catch (e: IllegalBlockSizeException) { + // Shouldn't happen. + throw IllegalStateException(e) + } catch (e: BadPaddingException) { + // Shouldn't happen. + throw IllegalStateException(e) + } + ghash.reset(hashKey, 0) + + + // Reset the nonce. + n = 0 + } + + override fun hasKey(): Boolean { + return keySpec != null + } + + /** + * Set up to encrypt or decrypt the next packet. + * + * @param ad The associated data for the packet. + */ + @Throws(InvalidKeyException::class, InvalidAlgorithmParameterException::class) + private fun setup(ad: ByteArray?) { + // Check for nonce wrap-around. + check(n != -1L) { "Nonce has wrapped around" } + + + // Format the counter/IV block for AES/CTR/NoPadding. + iv[0] = 0 + iv[1] = 0 + iv[2] = 0 + iv[3] = 0 + iv[4] = (n shr 56).toByte() + iv[5] = (n shr 48).toByte() + iv[6] = (n shr 40).toByte() + iv[7] = (n shr 32).toByte() + iv[8] = (n shr 24).toByte() + iv[9] = (n shr 16).toByte() + iv[10] = (n shr 8).toByte() + iv[11] = n.toByte() + iv[12] = 0 + iv[13] = 0 + iv[14] = 0 + iv[15] = 1 + ++n + + + // Initialize the CTR mode cipher with the key and IV. + cipher!!.init(Cipher.ENCRYPT_MODE, keySpec, IvParameterSpec(iv)) + + + // 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()) + try { + cipher.update(hashKey, 0, 16, hashKey, 0) + } catch (e: ShortBufferException) { + // Shouldn't happen. + throw IllegalStateException(e) + } + + + // Initialize the GHASH with the associated data value. + ghash.reset() + if (ad != null) { + ghash.update(ad, 0, ad.size) + ghash.pad() + } + } + + @Throws(ShortBufferException::class) + override fun encryptWithAd( + 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 + 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( + plaintext, + plaintextOffset, + ciphertext, + ciphertextOffset, + length + ) + return length + } + if (space < 16 || length > (space - 16)) throw ShortBufferException() + try { + setup(ad) + val result = + cipher!!.update(plaintext, plaintextOffset, length, ciphertext, ciphertextOffset) + cipher.doFinal(ciphertext, ciphertextOffset + result) + } catch (e: InvalidKeyException) { + // Shouldn't happen. + throw IllegalStateException(e) + } catch (e: InvalidAlgorithmParameterException) { + // Shouldn't happen. + throw IllegalStateException(e) + } catch (e: IllegalBlockSizeException) { + // Shouldn't happen. + throw IllegalStateException(e) + } catch (e: BadPaddingException) { + // Shouldn't happen. + throw IllegalStateException(e) + } + 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() + return length + 16 + } + + @Throws(ShortBufferException::class, BadPaddingException::class) + override fun decryptWithAd( + ad: ByteArray?, + ciphertext: ByteArray, + ciphertextOffset: Int, + plaintext: ByteArray, + plaintextOffset: Int, + length: Int + ): Int { + var space: Int + require(!(ciphertextOffset < 0 || ciphertextOffset > ciphertext.size)) + space = 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 (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( + ciphertext, + ciphertextOffset, + plaintext, + plaintextOffset, + length + ) + return length + } + if (length < 16) throwBadTagException() + val dataLen = length - 16 + if (dataLen > space) throw ShortBufferException() + try { + setup(ad) + } catch (e: InvalidKeyException) { + // Shouldn't happen. + throw IllegalStateException(e) + } catch (e: InvalidAlgorithmParameterException) { + // Shouldn't happen. + throw IllegalStateException(e) + } + ghash.update(ciphertext, ciphertextOffset, dataLen) + ghash.pad((if (ad != null) ad.size else 0).toLong(), dataLen.toLong()) + ghash.finish(iv, 0, 16) + var temp = 0 + for (index in 0..15) temp = + temp or (hashKey[index].toInt() xor iv[index].toInt() xor ciphertext[ciphertextOffset + dataLen + index].toInt()) + if ((temp and 0xFF) != 0) throwBadTagException() + try { + val result = + cipher!!.update(ciphertext, ciphertextOffset, dataLen, plaintext, plaintextOffset) + cipher.doFinal(plaintext, plaintextOffset + result) + } catch (e: IllegalBlockSizeException) { + // Shouldn't happen. + throw IllegalStateException(e) + } catch (e: BadPaddingException) { + // Shouldn't happen. + throw IllegalStateException(e) + } + return dataLen + } + + override fun fork(key: ByteArray, offset: Int): CipherState { + val cipher: CipherState + try { + cipher = AESGCMOnCtrCipherState() + } catch (e: NoSuchAlgorithmException) { + // Shouldn't happen. + return null + } + cipher.initializeKey(key, offset) + return cipher + } + + override fun setNonce(nonce: Long) { + n = nonce + } +} diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/ChaChaPolyCipherState.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/ChaChaPolyCipherState.java deleted file mode 100644 index a64182bd..00000000 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/ChaChaPolyCipherState.java +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (C) 2016 Southern Storm Software, Pty Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * 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; - -import java.util.Arrays; - -import javax.crypto.BadPaddingException; -import javax.crypto.ShortBufferException; - -import com.bitchat.android.noise.southernstorm.crypto.ChaChaCore; -import com.bitchat.android.noise.southernstorm.crypto.Poly1305; - -/** - * Implements the ChaChaPoly cipher for Noise. - */ -class ChaChaPolyCipherState implements CipherState { - - private Poly1305 poly; - private int[] input; - private int[] output; - private byte[] polyKey; - long n; - private boolean haskey; - - /** - * Constructs a new cipher state for the "ChaChaPoly" algorithm. - */ - public ChaChaPolyCipherState() - { - poly = new Poly1305(); - input = new int [16]; - output = new int [16]; - polyKey = new byte [32]; - n = 0; - haskey = false; - } - - @Override - public void destroy() { - poly.destroy(); - Arrays.fill(input, 0); - Arrays.fill(output, 0); - Noise.destroy(polyKey); - } - - @Override - public String getCipherName() { - return "ChaChaPoly"; - } - - @Override - public int getKeyLength() { - return 32; - } - - @Override - public int getMACLength() { - return haskey ? 16 : 0; - } - - @Override - public void initializeKey(byte[] key, int offset) { - ChaChaCore.INSTANCE.initKey256(input, key, offset); - n = 0; - haskey = true; - } - - @Override - public boolean hasKey() { - return haskey; - } - - /** - * XOR's the output of ChaCha20 with a byte buffer. - * - * @param input The input byte buffer. - * @param inputOffset The offset of the first input byte. - * @param output The output byte buffer (can be the same as the input). - * @param outputOffset The offset of the first output byte. - * @param length The number of bytes to XOR between 1 and 64. - * @param block The ChaCha20 output block. - */ - private static void xorBlock(byte[] input, int inputOffset, byte[] output, int outputOffset, int length, int[] block) - { - int posn = 0; - int value; - while (length >= 4) { - value = block[posn++]; - output[outputOffset] = (byte)(input[inputOffset] ^ value); - output[outputOffset + 1] = (byte)(input[inputOffset + 1] ^ (value >> 8)); - output[outputOffset + 2] = (byte)(input[inputOffset + 2] ^ (value >> 16)); - output[outputOffset + 3] = (byte)(input[inputOffset + 3] ^ (value >> 24)); - inputOffset += 4; - outputOffset += 4; - length -= 4; - } - if (length == 3) { - value = block[posn]; - output[outputOffset] = (byte)(input[inputOffset] ^ value); - output[outputOffset + 1] = (byte)(input[inputOffset + 1] ^ (value >> 8)); - output[outputOffset + 2] = (byte)(input[inputOffset + 2] ^ (value >> 16)); - } else if (length == 2) { - value = block[posn]; - output[outputOffset] = (byte)(input[inputOffset] ^ value); - output[outputOffset + 1] = (byte)(input[inputOffset + 1] ^ (value >> 8)); - } else if (length == 1) { - value = block[posn]; - output[outputOffset] = (byte)(input[inputOffset] ^ value); - } - } - - /** - * Set up to encrypt or decrypt the next packet. - * - * @param ad The associated data for the packet. - */ - private void setup(byte[] ad) - { - if (n == -1L) - throw new IllegalStateException("Nonce has wrapped around"); - ChaChaCore.INSTANCE.initIV(input, n++); - ChaChaCore.INSTANCE.hash(output, input); - Arrays.fill(polyKey, (byte)0); - xorBlock(polyKey, 0, polyKey, 0, 32, output); - poly.reset(polyKey, 0); - if (ad != null) { - poly.update(ad, 0, ad.length); - poly.pad(); - } - if (++(input[12]) == 0) - ++(input[13]); - } - - /** - * Puts a 64-bit integer into a buffer in little-endian order. - * - * @param output The output buffer. - * @param offset The offset into the output buffer. - * @param value The 64-bit integer value. - */ - private static void putLittleEndian64(byte[] output, int offset, long value) - { - output[offset] = (byte)value; - output[offset + 1] = (byte)(value >> 8); - output[offset + 2] = (byte)(value >> 16); - output[offset + 3] = (byte)(value >> 24); - output[offset + 4] = (byte)(value >> 32); - output[offset + 5] = (byte)(value >> 40); - output[offset + 6] = (byte)(value >> 48); - output[offset + 7] = (byte)(value >> 56); - } - - /** - * Finishes up the authentication tag for a packet. - * - * @param ad The associated data. - * @param length The length of the plaintext data. - */ - private void finish(byte[] ad, int length) - { - poly.pad(); - putLittleEndian64(polyKey, 0, ad != null ? ad.length : 0); - putLittleEndian64(polyKey, 8, length); - poly.update(polyKey, 0, 16); - poly.finish(polyKey, 0); - } - - /** - * Encrypts or decrypts a buffer of bytes for the active packet. - * - * @param plaintext The plaintext data to be encrypted. - * @param plaintextOffset The offset to the first plaintext byte. - * @param ciphertext The ciphertext data that results from encryption. - * @param ciphertextOffset The offset to the first ciphertext byte. - * @param length The number of bytes to encrypt. - */ - private void encrypt(byte[] plaintext, int plaintextOffset, - byte[] ciphertext, int ciphertextOffset, int length) { - while (length > 0) { - int tempLen = 64; - if (tempLen > length) - tempLen = length; - ChaChaCore.INSTANCE.hash(output, input); - xorBlock(plaintext, plaintextOffset, ciphertext, ciphertextOffset, tempLen, output); - if (++(input[12]) == 0) - ++(input[13]); - plaintextOffset += tempLen; - ciphertextOffset += tempLen; - length -= tempLen; - } - } - - @Override - public int encryptWithAd(byte[] ad, byte[] plaintext, int plaintextOffset, - byte[] ciphertext, int ciphertextOffset, int length) throws ShortBufferException { - int space; - if (ciphertextOffset < 0 || ciphertextOffset > ciphertext.length) - throw new IllegalArgumentException(); - if (length < 0 || plaintextOffset < 0 || plaintextOffset > plaintext.length || length > plaintext.length || (plaintext.length - plaintextOffset) < length) - throw new IllegalArgumentException(); - space = ciphertext.length - ciphertextOffset; - if (!haskey) { - // The key is not set yet - return the plaintext as-is. - if (length > space) - throw new ShortBufferException(); - if (plaintext != ciphertext || plaintextOffset != ciphertextOffset) - System.arraycopy(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length); - return length; - } - if (space < 16 || length > (space - 16)) - throw new ShortBufferException(); - setup(ad); - encrypt(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length); - poly.update(ciphertext, ciphertextOffset, length); - finish(ad, length); - System.arraycopy(polyKey, 0, ciphertext, ciphertextOffset + length, 16); - return length + 16; - } - - @Override - public int decryptWithAd(byte[] ad, byte[] ciphertext, - int ciphertextOffset, byte[] plaintext, int plaintextOffset, - int length) throws ShortBufferException, BadPaddingException { - int space; - if (ciphertextOffset < 0 || ciphertextOffset > ciphertext.length) - throw new IllegalArgumentException(); - else - space = ciphertext.length - ciphertextOffset; - if (length > space) - throw new ShortBufferException(); - if (length < 0 || plaintextOffset < 0 || plaintextOffset > plaintext.length || length > ciphertext.length || (ciphertext.length - ciphertextOffset) < length) - throw new IllegalArgumentException(); - space = plaintext.length - plaintextOffset; - if (!haskey) { - // The key is not set yet - return the ciphertext as-is. - if (length > space) - throw new ShortBufferException(); - if (plaintext != ciphertext || plaintextOffset != ciphertextOffset) - System.arraycopy(ciphertext, ciphertextOffset, plaintext, plaintextOffset, length); - return length; - } - if (length < 16) - Noise.throwBadTagException(); - int dataLen = length - 16; - if (dataLen > space) - throw new ShortBufferException(); - setup(ad); - poly.update(ciphertext, ciphertextOffset, dataLen); - finish(ad, dataLen); - int temp = 0; - for (int index = 0; index < 16; ++index) - temp |= (polyKey[index] ^ ciphertext[ciphertextOffset + dataLen + index]); - if ((temp & 0xFF) != 0) - Noise.throwBadTagException(); - encrypt(ciphertext, ciphertextOffset, plaintext, plaintextOffset, dataLen); - return dataLen; - } - - @Override - public CipherState fork(byte[] key, int offset) { - CipherState cipher = new ChaChaPolyCipherState(); - cipher.initializeKey(key, offset); - return cipher; - } - - @Override - public void setNonce(long nonce) { - n = nonce; - } -} 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 new file mode 100644 index 00000000..8e988b39 --- /dev/null +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/ChaChaPolyCipherState.kt @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2016 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * 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 + +import com.bitchat.android.noise.southernstorm.crypto.ChaChaCore.hash +import com.bitchat.android.noise.southernstorm.crypto.ChaChaCore.initIV +import com.bitchat.android.noise.southernstorm.crypto.ChaChaCore.initKey256 +import com.bitchat.android.noise.southernstorm.crypto.Poly1305 +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 + +/** + * Implements the ChaChaPoly cipher for Noise. + */ +internal class ChaChaPolyCipherState : CipherState { + private val poly: Poly1305 + private val input: IntArray + private val output: IntArray + private val polyKey: ByteArray + var n: Long = 0 + private var haskey = false + + /** + * Constructs a new cipher state for the "ChaChaPoly" algorithm. + */ + init { + poly = Poly1305() + input = IntArray(16) + output = IntArray(16) + polyKey = ByteArray(32) + } + + override fun destroy() { + poly.destroy() + Arrays.fill(input, 0) + Arrays.fill(output, 0) + destroy(polyKey) + } + + val cipherName: String + get() = "ChaChaPoly" + + val keyLength: Int + get() = 32 + + val mACLength: Int + get() = if (haskey) 16 else 0 + + override fun initializeKey(key: ByteArray, offset: Int) { + initKey256(input, key, offset) + n = 0 + haskey = true + } + + override fun hasKey(): Boolean { + return haskey + } + + /** + * Set up to encrypt or decrypt the next packet. + * + * @param ad The associated data for the packet. + */ + private fun setup(ad: ByteArray?) { + check(n != -1L) { "Nonce has wrapped around" } + initIV(input, n++) + hash(output, input) + Arrays.fill(polyKey, 0.toByte()) + xorBlock(polyKey, 0, polyKey, 0, 32, output) + poly.reset(polyKey, 0) + if (ad != null) { + poly.update(ad, 0, ad.size) + poly.pad() + } + if (++(input[12]) == 0) ++(input[13]) + } + + /** + * Finishes up the authentication tag for a packet. + * + * @param ad The associated data. + * @param length The length of the plaintext data. + */ + private fun finish(ad: ByteArray?, length: Int) { + poly.pad() + putLittleEndian64(polyKey, 0, (if (ad != null) ad.size else 0).toLong()) + putLittleEndian64(polyKey, 8, length.toLong()) + poly.update(polyKey, 0, 16) + poly.finish(polyKey, 0) + } + + /** + * Encrypts or decrypts a buffer of bytes for the active packet. + * + * @param plaintext The plaintext data to be encrypted. + * @param plaintextOffset The offset to the first plaintext byte. + * @param ciphertext The ciphertext data that results from encryption. + * @param ciphertextOffset The offset to the first ciphertext byte. + * @param length The number of bytes to encrypt. + */ + private fun encrypt( + plaintext: ByteArray, plaintextOffset: Int, + ciphertext: ByteArray, ciphertextOffset: Int, length: Int + ) { + var plaintextOffset = plaintextOffset + var ciphertextOffset = ciphertextOffset + var length = length + while (length > 0) { + var tempLen = 64 + if (tempLen > length) tempLen = length + hash(output, input) + xorBlock(plaintext, plaintextOffset, ciphertext, ciphertextOffset, tempLen, output) + if (++(input[12]) == 0) ++(input[13]) + plaintextOffset += tempLen + ciphertextOffset += tempLen + length -= tempLen + } + } + + @Throws(ShortBufferException::class) + override fun encryptWithAd( + 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 + 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( + plaintext, + plaintextOffset, + ciphertext, + ciphertextOffset, + length + ) + return length + } + if (space < 16 || length > (space - 16)) throw ShortBufferException() + setup(ad) + encrypt(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length) + poly.update(ciphertext, ciphertextOffset, length) + finish(ad, length) + System.arraycopy(polyKey, 0, ciphertext, ciphertextOffset + length, 16) + return length + 16 + } + + @Throws(ShortBufferException::class, BadPaddingException::class) + override fun decryptWithAd( + ad: ByteArray?, + ciphertext: ByteArray, + ciphertextOffset: Int, + plaintext: ByteArray, + plaintextOffset: Int, + length: Int + ): Int { + var space: Int + require(!(ciphertextOffset < 0 || ciphertextOffset > ciphertext.size)) + space = 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( + ciphertext, + ciphertextOffset, + plaintext, + plaintextOffset, + length + ) + return length + } + if (length < 16) throwBadTagException() + val dataLen = length - 16 + if (dataLen > space) throw ShortBufferException() + setup(ad) + poly.update(ciphertext, ciphertextOffset, dataLen) + finish(ad, dataLen) + var temp = 0 + for (index in 0..15) temp = + temp or (polyKey[index].toInt() xor ciphertext[ciphertextOffset + dataLen + index].toInt()) + if ((temp and 0xFF) != 0) throwBadTagException() + encrypt(ciphertext, ciphertextOffset, plaintext, plaintextOffset, dataLen) + return dataLen + } + + override fun fork(key: ByteArray, offset: Int): CipherState { + val cipher: CipherState = ChaChaPolyCipherState() + cipher.initializeKey(key, offset) + return cipher + } + + override fun setNonce(nonce: Long) { + n = nonce + } + + companion object { + /** + * XOR's the output of ChaCha20 with a byte buffer. + * + * @param input The input byte buffer. + * @param inputOffset The offset of the first input byte. + * @param output The output byte buffer (can be the same as the input). + * @param outputOffset The offset of the first output byte. + * @param length The number of bytes to XOR between 1 and 64. + * @param block The ChaCha20 output block. + */ + private fun xorBlock( + input: ByteArray, + inputOffset: Int, + output: ByteArray, + outputOffset: Int, + length: Int, + block: IntArray + ) { + var inputOffset = inputOffset + var outputOffset = outputOffset + var length = length + var posn = 0 + var value: Int + while (length >= 4) { + 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() + output[outputOffset + 3] = + (input[inputOffset + 3].toInt() xor (value shr 24)).toByte() + inputOffset += 4 + 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() + } + } + + /** + * Puts a 64-bit integer into a buffer in little-endian order. + * + * @param output The output buffer. + * @param offset The offset into the output buffer. + * @param value The 64-bit integer value. + */ + private fun putLittleEndian64(output: ByteArray, offset: Int, value: Long) { + output[offset] = value.toByte() + output[offset + 1] = (value shr 8).toByte() + output[offset + 2] = (value shr 16).toByte() + output[offset + 3] = (value shr 24).toByte() + output[offset + 4] = (value shr 32).toByte() + output[offset + 5] = (value shr 40).toByte() + output[offset + 6] = (value shr 48).toByte() + output[offset + 7] = (value shr 56).toByte() + } + } +} 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 67efd831..df29a681 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 @@ -53,9 +53,6 @@ interface CipherState : Destroyable { * @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 /** diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve25519DHState.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve25519DHState.java deleted file mode 100644 index 95b81e5a..00000000 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve25519DHState.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2016 Southern Storm Software, Pty Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * 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; - -import java.util.Arrays; - -import com.bitchat.android.noise.southernstorm.crypto.Curve25519; - -/** - * Implementation of the Curve25519 algorithm for the Noise protocol. - */ -class Curve25519DHState implements DHState { - - private byte[] publicKey; - private byte[] privateKey; - private int mode; - - /** - * Constructs a new Diffie-Hellman object for Curve25519. - */ - public Curve25519DHState() - { - publicKey = new byte [32]; - privateKey = new byte [32]; - mode = 0; - } - - @Override - public void destroy() { - clearKey(); - } - - @Override - public String getDHName() { - return "25519"; - } - - @Override - public int getPublicKeyLength() { - return 32; - } - - @Override - public int getPrivateKeyLength() { - return 32; - } - - @Override - public int getSharedKeyLength() { - return 32; - } - - @Override - public void generateKeyPair() { - Noise.random(privateKey); - Curve25519.eval(publicKey, 0, privateKey, null); - mode = 0x03; - } - - @Override - public void getPublicKey(byte[] key, int offset) { - System.arraycopy(publicKey, 0, key, offset, 32); - } - - @Override - public void setPublicKey(byte[] key, int offset) { - System.arraycopy(key, offset, publicKey, 0, 32); - Arrays.fill(privateKey, (byte)0); - mode = 0x01; - } - - @Override - public void getPrivateKey(byte[] key, int offset) { - System.arraycopy(privateKey, 0, key, offset, 32); - } - - @Override - public void setPrivateKey(byte[] key, int offset) { - System.arraycopy(key, offset, privateKey, 0, 32); - Curve25519.eval(publicKey, 0, privateKey, null); - mode = 0x03; - } - - @Override - public void setToNullPublicKey() { - Arrays.fill(publicKey, (byte)0); - Arrays.fill(privateKey, (byte)0); - mode = 0x01; - } - - @Override - public void clearKey() { - Noise.destroy(publicKey); - Noise.destroy(privateKey); - mode = 0; - } - - @Override - public boolean hasPublicKey() { - return (mode & 0x01) != 0; - } - - @Override - public boolean hasPrivateKey() { - return (mode & 0x02) != 0; - } - - @Override - public boolean isNullPublicKey() { - if ((mode & 0x01) == 0) - return false; - int temp = 0; - for (int index = 0; index < 32; ++index) - temp |= publicKey[index]; - return temp == 0; - } - - @Override - public void calculate(byte[] sharedKey, int offset, DHState publicDH) { - if (!(publicDH instanceof Curve25519DHState)) - throw new IllegalArgumentException("Incompatible DH algorithms"); - Curve25519.eval(sharedKey, offset, privateKey, ((Curve25519DHState)publicDH).publicKey); - } - - @Override - public void copyFrom(DHState other) { - if (!(other instanceof Curve25519DHState)) - throw new IllegalStateException("Mismatched DH key objects"); - if (other == this) - return; - Curve25519DHState dh = (Curve25519DHState)other; - System.arraycopy(dh.privateKey, 0, privateKey, 0, 32); - System.arraycopy(dh.publicKey, 0, publicKey, 0, 32); - mode = dh.mode; - } -} 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 new file mode 100644 index 00000000..19a022eb --- /dev/null +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve25519DHState.kt @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2016 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * 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 + +import com.bitchat.android.noise.southernstorm.crypto.Curve25519 +import com.bitchat.android.noise.southernstorm.protocol.Noise.destroy +import com.bitchat.android.noise.southernstorm.protocol.Noise.random +import java.util.Arrays + +/** + * Implementation of the Curve25519 algorithm for the Noise protocol. + */ +internal class Curve25519DHState : DHState { + private val publicKey: ByteArray + private val privateKey: ByteArray + private var mode = 0 + + /** + * Constructs a new Diffie-Hellman object for Curve25519. + */ + init { + publicKey = ByteArray(32) + privateKey = ByteArray(32) + } + + override fun destroy() { + clearKey() + } + + override fun getDHName(): String { + return "25519" + } + + override fun getPublicKeyLength(): Int { + return 32 + } + + override fun getPrivateKeyLength(): Int { + return 32 + } + + override fun getSharedKeyLength(): Int { + return 32 + } + + override fun generateKeyPair() { + random(privateKey) + Curve25519.eval(publicKey, 0, privateKey, null) + mode = 0x03 + } + + override fun getPublicKey(key: ByteArray, offset: Int) { + System.arraycopy(publicKey, 0, key, offset, 32) + } + + override fun setPublicKey(key: ByteArray, offset: Int) { + System.arraycopy(key, offset, publicKey, 0, 32) + Arrays.fill(privateKey, 0.toByte()) + mode = 0x01 + } + + override fun getPrivateKey(key: ByteArray, offset: Int) { + System.arraycopy(privateKey, 0, key, offset, 32) + } + + override fun setPrivateKey(key: ByteArray, offset: Int) { + System.arraycopy(key, offset, privateKey, 0, 32) + Curve25519.eval(publicKey, 0, privateKey, null) + mode = 0x03 + } + + override fun setToNullPublicKey() { + Arrays.fill(publicKey, 0.toByte()) + Arrays.fill(privateKey, 0.toByte()) + mode = 0x01 + } + + override fun clearKey() { + destroy(publicKey) + destroy(privateKey) + mode = 0 + } + + override fun hasPublicKey(): Boolean { + return (mode and 0x01) != 0 + } + + override fun hasPrivateKey(): Boolean { + return (mode and 0x02) != 0 + } + + override fun isNullPublicKey(): Boolean { + if ((mode and 0x01) == 0) return false + var temp = 0 + for (index in 0..31) temp = temp or publicKey[index].toInt() + return temp == 0 + } + + override fun calculate(sharedKey: ByteArray, offset: Int, publicDH: DHState?) { + require(publicDH is Curve25519DHState) { "Incompatible DH algorithms" } + Curve25519.eval(sharedKey, offset, privateKey, publicDH.publicKey) + } + + override fun copyFrom(other: DHState?) { + check(other is Curve25519DHState) { "Mismatched DH key objects" } + if (other === this) return + val dh = other + System.arraycopy(dh.privateKey, 0, privateKey, 0, 32) + System.arraycopy(dh.publicKey, 0, publicKey, 0, 32) + mode = dh.mode + } +} diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve448DHState.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve448DHState.java deleted file mode 100644 index 2ab449f6..00000000 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve448DHState.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2016 Southern Storm Software, Pty Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * 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; - -import java.util.Arrays; - -import com.bitchat.android.noise.southernstorm.crypto.Curve448; - -/** - * Implementation of the Curve448 algorithm for the Noise protocol. - */ -class Curve448DHState implements DHState { - - private byte[] publicKey; - private byte[] privateKey; - private int mode; - - /** - * Constructs a new Diffie-Hellman object for Curve448. - */ - public Curve448DHState() - { - publicKey = new byte [56]; - privateKey = new byte [56]; - mode = 0; - } - - @Override - public void destroy() { - clearKey(); - } - - @Override - public String getDHName() { - return "448"; - } - - @Override - public int getPublicKeyLength() { - return 56; - } - - @Override - public int getPrivateKeyLength() { - return 56; - } - - @Override - public int getSharedKeyLength() { - return 56; - } - - @Override - public void generateKeyPair() { - Noise.random(privateKey); - Curve448.eval(publicKey, 0, privateKey, null); - mode = 0x03; - } - - @Override - public void getPublicKey(byte[] key, int offset) { - System.arraycopy(publicKey, 0, key, offset, 56); - } - - @Override - public void setPublicKey(byte[] key, int offset) { - System.arraycopy(key, offset, publicKey, 0, 56); - Arrays.fill(privateKey, (byte)0); - mode = 0x01; - } - - @Override - public void getPrivateKey(byte[] key, int offset) { - System.arraycopy(privateKey, 0, key, offset, 56); - } - - @Override - public void setPrivateKey(byte[] key, int offset) { - System.arraycopy(key, offset, privateKey, 0, 56); - Curve448.eval(publicKey, 0, privateKey, null); - mode = 0x03; - } - - @Override - public void setToNullPublicKey() { - Arrays.fill(publicKey, (byte)0); - Arrays.fill(privateKey, (byte)0); - mode = 0x01; - } - - @Override - public void clearKey() { - Noise.destroy(publicKey); - Noise.destroy(privateKey); - mode = 0; - } - - @Override - public boolean hasPublicKey() { - return (mode & 0x01) != 0; - } - - @Override - public boolean hasPrivateKey() { - return (mode & 0x02) != 0; - } - - @Override - public boolean isNullPublicKey() { - if ((mode & 0x01) == 0) - return false; - int temp = 0; - for (int index = 0; index < 56; ++index) - temp |= publicKey[index]; - return temp == 0; - } - - @Override - public void calculate(byte[] sharedKey, int offset, DHState publicDH) { - if (!(publicDH instanceof Curve448DHState)) - throw new IllegalArgumentException("Incompatible DH algorithms"); - Curve448.eval(sharedKey, offset, privateKey, ((Curve448DHState)publicDH).publicKey); - } - - @Override - public void copyFrom(DHState other) { - if (!(other instanceof Curve448DHState)) - throw new IllegalStateException("Mismatched DH key objects"); - if (other == this) - return; - Curve448DHState dh = (Curve448DHState)other; - System.arraycopy(dh.privateKey, 0, privateKey, 0, 56); - System.arraycopy(dh.publicKey, 0, publicKey, 0, 56); - mode = dh.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 new file mode 100644 index 00000000..160e892e --- /dev/null +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/Curve448DHState.kt @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2016 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * 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 + +import com.bitchat.android.noise.southernstorm.crypto.Curve448 +import com.bitchat.android.noise.southernstorm.protocol.Noise.destroy +import com.bitchat.android.noise.southernstorm.protocol.Noise.random +import java.util.Arrays + +/** + * Implementation of the Curve448 algorithm for the Noise protocol. + */ +internal class Curve448DHState : DHState { + private val publicKey: ByteArray + private val privateKey: ByteArray + private var mode = 0 + + /** + * Constructs a new Diffie-Hellman object for Curve448. + */ + init { + publicKey = ByteArray(56) + privateKey = ByteArray(56) + } + + override fun destroy() { + clearKey() + } + + override fun getDHName(): String { + return "448" + } + + override fun getPublicKeyLength(): Int { + return 56 + } + + override fun getPrivateKeyLength(): Int { + return 56 + } + + override fun getSharedKeyLength(): Int { + return 56 + } + + override fun generateKeyPair() { + random(privateKey) + Curve448.eval(publicKey, 0, privateKey, null) + mode = 0x03 + } + + override fun getPublicKey(key: ByteArray, offset: Int) { + System.arraycopy(publicKey, 0, key, offset, 56) + } + + override fun setPublicKey(key: ByteArray, offset: Int) { + System.arraycopy(key, offset, publicKey, 0, 56) + Arrays.fill(privateKey, 0.toByte()) + mode = 0x01 + } + + override fun getPrivateKey(key: ByteArray, offset: Int) { + System.arraycopy(privateKey, 0, key, offset, 56) + } + + override fun setPrivateKey(key: ByteArray, offset: Int) { + System.arraycopy(key, offset, privateKey, 0, 56) + Curve448.eval(publicKey, 0, privateKey, null) + mode = 0x03 + } + + override fun setToNullPublicKey() { + Arrays.fill(publicKey, 0.toByte()) + Arrays.fill(privateKey, 0.toByte()) + mode = 0x01 + } + + override fun clearKey() { + destroy(publicKey) + destroy(privateKey) + mode = 0 + } + + override fun hasPublicKey(): Boolean { + return (mode and 0x01) != 0 + } + + override fun hasPrivateKey(): Boolean { + return (mode and 0x02) != 0 + } + + override fun isNullPublicKey(): Boolean { + if ((mode and 0x01) == 0) return false + var temp = 0 + for (index in 0..55) temp = temp or publicKey[index].toInt() + return temp == 0 + } + + override fun calculate(sharedKey: ByteArray, offset: Int, publicDH: DHState?) { + require(publicDH is Curve448DHState) { "Incompatible DH algorithms" } + Curve448.eval(sharedKey, offset, privateKey, publicDH.publicKey) + } + + override fun copyFrom(other: DHState?) { + check(other is Curve448DHState) { "Mismatched DH key objects" } + if (other === this) return + val dh = other + System.arraycopy(dh.privateKey, 0, privateKey, 0, 56) + System.arraycopy(dh.publicKey, 0, publicKey, 0, 56) + mode = dh.mode + } +} diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHState.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHState.java deleted file mode 100644 index 2b2477ce..00000000 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHState.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2016 Southern Storm Software, Pty Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * 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; - -/** - * Interface to a Diffie-Hellman algorithm for the Noise protocol. - */ -public interface DHState extends Destroyable { - - /** - * Gets the Noise protocol name for this Diffie-Hellman algorithm. - * - * @return The algorithm name. - */ - String getDHName(); - - /** - * Gets the length of public keys for this algorithm. - * - * @return The length of public keys in bytes. - */ - int getPublicKeyLength(); - - /** - * Gets the length of private keys for this algorithm. - * - * @return The length of private keys in bytes. - */ - int getPrivateKeyLength(); - - /** - * Gets the length of shared keys for this algorithm. - * - * @return The length of shared keys in bytes. - */ - int getSharedKeyLength(); - - /** - * Generates a new random keypair. - */ - void generateKeyPair(); - - /** - * Gets the public key associated with this object. - * - * @param key The buffer to copy the public key to. - * @param offset The first offset in the key buffer to copy to. - */ - void getPublicKey(byte[] key, int offset); - - /** - * Sets the public key for this object. - * - * @param key The buffer containing the public key. - * @param offset The first offset in the buffer that contains the key. - * - * If this object previously held a key pair, then this function - * will change it into a public key only object. - */ - void setPublicKey(byte[] key, int offset); - - /** - * Gets the private key associated with this object. - * - * @param key The buffer to copy the private key to. - * @param offset The first offset in the key buffer to copy to. - */ - void getPrivateKey(byte[] key, int offset); - - /** - * Sets the private key for this object. - * - * @param key The buffer containing the [rivate key. - * @param offset The first offset in the buffer that contains the key. - * - * If this object previously held only a public key, then - * this function will change it into a key pair. - */ - void setPrivateKey(byte[] key, int offset); - - /** - * Sets this object to the null public key and clears the private key. - */ - void setToNullPublicKey(); - - /** - * Clears the key pair. - */ - void clearKey(); - - /** - * Determine if this object contains a public key. - * - * @return Returns true if this object contains a public key, - * or false if the public key has not yet been set. - */ - boolean hasPublicKey(); - - /** - * Determine if this object contains a private key. - * - * @return Returns true if this object contains a private key, - * or false if the private key has not yet been set. - */ - boolean hasPrivateKey(); - - /** - * Determine if the public key in this object is the special null value. - * - * @return Returns true if the public key is the special null value, - * or false otherwise. - */ - boolean isNullPublicKey(); - - /** - * Performs a Diffie-Hellman calculation with this object as the private key. - * - * @param sharedKey Buffer to put the shared key into. - * @param offset Offset of the first byte for the shared key. - * @param publicDH Object that contains the public key for the calculation. - * - * @throws IllegalArgumentException The publicDH object is not the same - * type as this object, or one of the objects does not contain a valid key. - */ - void calculate(byte[] sharedKey, int offset, DHState publicDH); - - /** - * Copies the key values from another DH object of the same type. - * - * @param other The other DH object to copy from - * - * @throws IllegalStateException The other DH object does not have - * the same type as this object. - */ - void copyFrom(DHState other); -} diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHState.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHState.kt new file mode 100644 index 00000000..a01259f8 --- /dev/null +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHState.kt @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2016 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * 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 + +/** + * Interface to a Diffie-Hellman algorithm for the Noise protocol. + */ +interface DHState : Destroyable { + /** + * Gets the Noise protocol name for this Diffie-Hellman algorithm. + * + * @return The algorithm name. + */ + val dHName: String? + + /** + * Gets the length of public keys for this algorithm. + * + * @return The length of public keys in bytes. + */ + val publicKeyLength: Int + + /** + * Gets the length of private keys for this algorithm. + * + * @return The length of private keys in bytes. + */ + val privateKeyLength: Int + + /** + * Gets the length of shared keys for this algorithm. + * + * @return The length of shared keys in bytes. + */ + val sharedKeyLength: Int + + /** + * Generates a new random keypair. + */ + fun generateKeyPair() + + /** + * Gets the public key associated with this object. + * + * @param key The buffer to copy the public key to. + * @param offset The first offset in the key buffer to copy to. + */ + fun getPublicKey(key: ByteArray?, offset: Int) + + /** + * Sets the public key for this object. + * + * @param key The buffer containing the public key. + * @param offset The first offset in the buffer that contains the key. + * + * If this object previously held a key pair, then this function + * will change it into a public key only object. + */ + fun setPublicKey(key: ByteArray?, offset: Int) + + /** + * Gets the private key associated with this object. + * + * @param key The buffer to copy the private key to. + * @param offset The first offset in the key buffer to copy to. + */ + fun getPrivateKey(key: ByteArray?, offset: Int) + + /** + * Sets the private key for this object. + * + * @param key The buffer containing the [rivate key. + * @param offset The first offset in the buffer that contains the key. + * + * If this object previously held only a public key, then + * this function will change it into a key pair. + */ + fun setPrivateKey(key: ByteArray?, offset: Int) + + /** + * Sets this object to the null public key and clears the private key. + */ + fun setToNullPublicKey() + + /** + * Clears the key pair. + */ + fun clearKey() + + /** + * Determine if this object contains a public key. + * + * @return Returns true if this object contains a public key, + * or false if the public key has not yet been set. + */ + fun hasPublicKey(): Boolean + + /** + * Determine if this object contains a private key. + * + * @return Returns true if this object contains a private key, + * or false if the private key has not yet been set. + */ + fun hasPrivateKey(): Boolean + + /** + * Determine if the public key in this object is the special null value. + * + * @return Returns true if the public key is the special null value, + * or false otherwise. + */ + val isNullPublicKey: Boolean + + /** + * Performs a Diffie-Hellman calculation with this object as the private key. + * + * @param sharedKey Buffer to put the shared key into. + * @param offset Offset of the first byte for the shared key. + * @param publicDH Object that contains the public key for the calculation. + * + * @throws IllegalArgumentException The publicDH object is not the same + * type as this object, or one of the objects does not contain a valid key. + */ + fun calculate(sharedKey: ByteArray?, offset: Int, publicDH: DHState?) + + /** + * Copies the key values from another DH object of the same type. + * + * @param other The other DH object to copy from + * + * @throws IllegalStateException The other DH object does not have + * the same type as this object. + */ + fun copyFrom(other: DHState?) +} diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHStateHybrid.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHStateHybrid.kt similarity index 53% rename from app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHStateHybrid.java rename to app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHStateHybrid.kt index 1446684b..9c714ddc 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHStateHybrid.java +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHStateHybrid.kt @@ -19,42 +19,40 @@ * 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 /** * Additional API for DH objects that need special handling for * hybrid operations. */ -public interface DHStateHybrid extends DHState { +interface DHStateHybrid : DHState { + /** + * Generates a new random keypair relative to the parameters + * in another object. + * + * @param remote The remote party in this communication to obtain parameters. + * + * @throws IllegalStateException The other or remote DH object does not have + * the same type as this object. + */ + fun generateKeyPair(remote: DHState?) - /** - * Generates a new random keypair relative to the parameters - * in another object. - * - * @param remote The remote party in this communication to obtain parameters. - * - * @throws IllegalStateException The other or remote DH object does not have - * the same type as this object. - */ - void generateKeyPair(DHState remote); + /** + * Copies the key values from another DH object of the same type. + * + * @param other The other DH object to copy from + * @param remote The remote party in this communication to obtain parameters. + * + * @throws IllegalStateException The other or remote DH object does not have + * the same type as this object. + */ + fun copyFrom(other: DHState?, remote: DHState?) - /** - * Copies the key values from another DH object of the same type. - * - * @param other The other DH object to copy from - * @param remote The remote party in this communication to obtain parameters. - * - * @throws IllegalStateException The other or remote DH object does not have - * the same type as this object. - */ - void copyFrom(DHState other, DHState remote); - - /** - * Specifies the local peer object prior to setting a public key - * on a remote object. - * - * @param local The local peer object. - */ - void specifyPeer(DHState local); + /** + * Specifies the local peer object prior to setting a public key + * on a remote object. + * + * @param local The local peer object. + */ + fun specifyPeer(local: DHState?) } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/HandshakeState.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/HandshakeState.java deleted file mode 100644 index 91fec78a..00000000 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/HandshakeState.java +++ /dev/null @@ -1,1290 +0,0 @@ -/* - * Copyright (C) 2016 Southern Storm Software, Pty Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * 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; - -import android.util.Log; - -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - -import javax.crypto.BadPaddingException; -import javax.crypto.ShortBufferException; - -/** - * Interface to a Noise handshake. - */ -public class HandshakeState implements Destroyable { - - private static final String TAG = "AndroidHandshake"; - - private SymmetricState symmetric; - private boolean isInitiator; - private DHState localKeyPair; - private DHState localEphemeral; - private DHState localHybrid; - private DHState remotePublicKey; - private DHState remoteEphemeral; - private DHState remoteHybrid; - private DHState fixedEphemeral; - private DHState fixedHybrid; - private int action; - private int requirements; - private short[] pattern; - private int patternIndex; - private byte[] preSharedKey; - private byte[] prologue; - - /** - * Enumerated value that indicates that the handshake object - * is handling the initiator role. - */ - public static final int INITIATOR = 1; - - /** - * Enumerated value that indicates that the handshake object - * is handling the responder role. - */ - public static final int RESPONDER = 2; - - /** - * No action is required of the application yet because the - * handshake has not started. - */ - public static final int NO_ACTION = 0; - - /** - * The HandshakeState expects the application to write the - * next message payload for the handshake. - */ - public static final int WRITE_MESSAGE = 1; - - /** - * The HandshakeState expects the application to read the - * next message payload from the handshake. - */ - public static final int READ_MESSAGE = 2; - - /** - * The handshake has failed due to some kind of error. - */ - public static final int FAILED = 3; - - /** - * The handshake is over and the application is expected to call - * split() and begin data session communications. - */ - public static final int SPLIT = 4; - - /** - * The handshake is complete and the data session ciphers - * have been split() out successfully. - */ - public static final int COMPLETE = 5; - - /** - * Local static keypair is required for the handshake. - */ - private static final int LOCAL_REQUIRED = 0x01; - - /** - * Remote static keypai is required for the handshake. - */ - private static final int REMOTE_REQUIRED = 0x02; - - /** - * Pre-shared key is required for the handshake. - */ - private static final int PSK_REQUIRED = 0x04; - - /** - * Ephemeral key for fallback pre-message has been provided. - */ - private static final int FALLBACK_PREMSG = 0x08; - - /** - * The local public key is part of the pre-message. - */ - private static final int LOCAL_PREMSG = 0x10; - - /** - * The remote public key is part of the pre-message. - */ - private static final int REMOTE_PREMSG = 0x20; - - /** - * Fallback is possible from this pattern (two-way, ends in "K"). - */ - private static final int FALLBACK_POSSIBLE = 0x40; - - /** - * Creates a new Noise handshake. - * - * @param protocolName The name of the Noise protocol. - * @param role The role, HandshakeState.INITIATOR or HandshakeState.RESPONDER. - * - * @throws IllegalArgumentException The protocolName is not - * formatted correctly, or the role is not recognized. - * - * @throws NoSuchAlgorithmException One of the cryptographic algorithms - * that is specified in the protocolName is not supported. - */ - public HandshakeState(String protocolName, int role) throws NoSuchAlgorithmException - { - // Parse the protocol name into its components. - String[] components = protocolName.split("_"); - if (components.length != 5) - throw new IllegalArgumentException("Protocol name must have 5 components"); - String prefix = components[0]; - String patternId = components[1]; - String dh = components[2]; - String hybrid = null; - String cipher = components[3]; - String hash = components[4]; - if (!prefix.equals("Noise") && !prefix.equals("NoisePSK")) - throw new IllegalArgumentException("Prefix must be Noise or NoisePSK"); - pattern = Pattern.lookup(patternId); - if (pattern == null) - throw new IllegalArgumentException("Handshake pattern is not recognized"); - short flags = pattern[0]; - int extraReqs = 0; - if ((flags & Pattern.FLAG_REMOTE_REQUIRED) != 0 && patternId.length() > 1) - extraReqs |= FALLBACK_POSSIBLE; - if (role == RESPONDER) { - // Reverse the pattern flags so that the responder is "local". - flags = Pattern.reverseFlags(flags); - } - int index = dh.indexOf('+'); - if (index != -1) { - // The DH name has two components: regular and hybrid. - hybrid = dh.substring(index + 1); - dh = dh.substring(0, index); - if ((flags & Pattern.FLAG_LOCAL_HYBRID) == 0 || (flags & Pattern.FLAG_REMOTE_HYBRID) == 0) - throw new IllegalArgumentException("Hybrid function specified for non-hybrid pattern"); - } else { - if ((flags & Pattern.FLAG_LOCAL_HYBRID) != 0 || (flags & Pattern.FLAG_REMOTE_HYBRID) != 0) - throw new IllegalArgumentException("Hybrid function not specified for hybrid pattern"); - } - - // Check that the role is correctly specified. - if (role != INITIATOR && role != RESPONDER) - throw new IllegalArgumentException("Role must be initiator or responder"); - - // Initialize this object. This will also create the cipher and hash objects. - symmetric = new SymmetricState(protocolName, cipher, hash); - isInitiator = (role == INITIATOR); - action = NO_ACTION; - requirements = extraReqs | computeRequirements(flags, prefix, role, false); - patternIndex = 1; - - // Create the DH objects that we will need later. - if ((flags & Pattern.FLAG_LOCAL_STATIC) != 0) - localKeyPair = Noise.createDH(dh); - if ((flags & Pattern.FLAG_LOCAL_EPHEMERAL) != 0) - localEphemeral = Noise.createDH(dh); - if ((flags & Pattern.FLAG_LOCAL_HYBRID) != 0) - localHybrid = Noise.createDH(hybrid); - if ((flags & Pattern.FLAG_REMOTE_STATIC) != 0) - remotePublicKey = Noise.createDH(dh); - if ((flags & Pattern.FLAG_REMOTE_EPHEMERAL) != 0) - remoteEphemeral = Noise.createDH(dh); - if ((flags & Pattern.FLAG_REMOTE_HYBRID) != 0) - remoteHybrid = Noise.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 instanceof DHStateHybrid) - throw new NoSuchAlgorithmException("Cannot use '" + localKeyPair.getDHName() + "' for static keys"); - if (localEphemeral instanceof DHStateHybrid) - throw new NoSuchAlgorithmException("Cannot use '" + localEphemeral.getDHName() + "' for ephemeral keys"); - if (remotePublicKey instanceof DHStateHybrid) - throw new NoSuchAlgorithmException("Cannot use '" + remotePublicKey.getDHName() + "' for static keys"); - if (remoteEphemeral instanceof DHStateHybrid) - throw new NoSuchAlgorithmException("Cannot use '" + remoteEphemeral.getDHName() + "' for ephemeral keys"); - } - - /** - * Gets the name of the Noise protocol. - * - * @return The protocol name. - */ - public String getProtocolName() - { - return symmetric.getProtocolName(); - } - - /** - * Gets the role for this handshake. - * - * @return The role, HandshakeState.INITIATOR or HandshakeState.RESPONDER. - */ - public int getRole() - { - return isInitiator ? INITIATOR : RESPONDER; - } - - /** - * Determine if this handshake needs a pre-shared key value - * and one has not been configured yet. - * - * @return true if a pre-shared key is needed; false if not. - */ - public boolean needsPreSharedKey() - { - if (preSharedKey != null) - return false; - else - return (requirements & PSK_REQUIRED) != 0; - } - - /** - * Determine if this object has already been configured with a - * pre-shared key. - * - * @return true if the pre-shared key has already been configured; - * false if one is not needed or it has not been configured yet. - */ - public boolean hasPreSharedKey() - { - return preSharedKey != null; - } - - /** - * Sets the pre-shared key for this handshake. - * - * @param key Buffer containing the pre-shared key value. - * @param offset Offset into the buffer of the first byte of the key. - * @param length The length of the pre-shared key, which must be 32. - * - * @throws IllegalArgumentException The length is not 32. - * - * @throws UnsupportedOperationException Pre-shared keys are not - * supported for this handshake type. - * - * @throws IllegalStateException The handshake has already started, - * so the pre-shared key can no longer be set. - */ - public void setPreSharedKey(byte[] key, int offset, int length) - { - if (length != 32) { - throw new IllegalArgumentException - ("Pre-shared keys must be 32 bytes in length"); - } - if ((requirements & PSK_REQUIRED) == 0) { - throw new UnsupportedOperationException - ("Pre-shared keys are not supported for this handshake"); - } - if (action != NO_ACTION) { - throw new IllegalStateException - ("Handshake has already started; cannot set pre-shared key"); - } - if (preSharedKey != null) { - Noise.destroy(preSharedKey); - preSharedKey = null; - } - preSharedKey = Noise.copySubArray(key, offset, length); - } - - /** - * Sets the prologue for this handshake. - * - * @param prologue Buffer containing the prologue value. - * @param offset Offset into the buffer of the first byte of the prologue. - * @param length The length of the prologue in bytes. - * - * @throws IllegalStateException The handshake has already started, - * so the prologue can no longer be set. - */ - public void setPrologue(byte[] prologue, int offset, int length) - { - if (action != NO_ACTION) { - throw new IllegalStateException - ("Handshake has already started; cannot set prologue"); - } - if (this.prologue != null) { - Noise.destroy(this.prologue); - this.prologue = null; - } - this.prologue = Noise.copySubArray(prologue, offset, length); - } - - /** - * Gets the keypair object for the local static key. - * - * @return The keypair, or null if a local static key is not required. - */ - public DHState getLocalKeyPair() - { - return localKeyPair; - } - - /** - * Determine if this handshake requires a local static key. - * - * @return true if a local static key is needed; false if not. - * - * If the local static key has already been set, then this function - * will return false. - */ - public boolean needsLocalKeyPair() - { - if (localKeyPair != null) - return !localKeyPair.hasPrivateKey(); - else - return false; - } - - /** - * Determine if this handshake has already been configured - * with a local static key. - * - * @return true if the local static key has been configured; - * false if not. - */ - public boolean hasLocalKeyPair() - { - if (localKeyPair != null) - return localKeyPair.hasPrivateKey(); - else - return false; - } - - /** - * Gets the public key object for the remote static key. - * - * @return The public key, or null if a remote static key - * is not required. - */ - public DHState getRemotePublicKey() - { - return remotePublicKey; - } - - /** - * Determine if this handshake requires a remote static key. - * - * @return true if a remote static key is needed; false if not. - * - * If the remote static key has already been set, then this function - * will return false. - */ - public boolean needsRemotePublicKey() - { - if (remotePublicKey != null) - return !remotePublicKey.hasPublicKey(); - else - return false; - } - - /** - * Determine if this handshake has already been configured - * with a remote static key. - * - * @return true if the remote static key has been configured; - * false if not. - */ - public boolean hasRemotePublicKey() - { - if (remotePublicKey != null) - return remotePublicKey.hasPublicKey(); - else - return false; - } - - /** - * Gets the DHState object containing a fixed local ephemeral - * key value for this handshake. - * - * @return The fixed ephemeral key object, or null if a local - * ephemeral key is not required by this handshake. - * - * This function is intended for testing only. It can be used - * to establish a fixed ephemeral key for test vectors. This - * function should not be used in real applications. - */ - public DHState getFixedEphemeralKey() - { - if (fixedEphemeral != null) - return fixedEphemeral; - if (localEphemeral == null) - return null; - try { - fixedEphemeral = Noise.createDH(localEphemeral.getDHName()); - } catch (NoSuchAlgorithmException e) { - // This shouldn't happen - the local ephemeral key would - // have already been created with the same name! - fixedEphemeral = null; - } - return fixedEphemeral; - } - - /** - * Gets the DHState object containing a fixed local hybrid - * key value for this handshake. - * - * @return The fixed hybrid key object, or null if a local - * hybrid key is not required by this handshake. - * - * This function is intended for testing only. It can be used - * to establish a fixed hybrid key for test vectors. This - * function should not be used in real applications. - */ - public DHState getFixedHybridKey() - { - if (fixedHybrid != null) - return fixedHybrid; - if (localHybrid == null) - return null; - try { - fixedHybrid = Noise.createDH(localHybrid.getDHName()); - } catch (NoSuchAlgorithmException e) { - // This shouldn't happen - the local hybrid key would - // have already been created with the same name! - fixedHybrid = null; - } - return fixedHybrid; - } - - // Empty value for when the prologue is not supplied. - private static final byte[] emptyPrologue = new byte [0]; - - /** - * Converts a byte array to hex string for logging (matching iOS hex format) - */ - private static String bytesToHex(byte[] bytes) { - StringBuilder sb = new StringBuilder(); - for (byte b : bytes) { - sb.append(String.format("%02x", b)); - } - return sb.toString(); - } - - /** - * Starts the handshake running. - * - * This function is called after all of the handshake parameters have been - * provided to the HandshakeState object. This function should be followed - * by calls to writeMessage() or readMessage() to process the handshake - * messages. The getAction() function indicates the action to take next. - * - * @throws IllegalStateException The handshake has already started, or one or - * more of the required parameters has not been supplied. - * - * @throws UnsupportedOperationException An attempt was made to start a - * fallback handshake pattern without first calling fallback() on a - * previous handshake. - * - * @see #getAction() - * @see #writeMessage(byte[], int, byte[], int, int) - * @see #readMessage(byte[], int, int, byte[], int) - * @see #fallback() - */ - public void start() - { - if (action != NO_ACTION) { - throw new IllegalStateException - ("Handshake has already started; cannot start again"); - } - if ((pattern[0] & Pattern.FLAG_REMOTE_EPHEM_REQ) != 0 && - (requirements & FALLBACK_PREMSG) == 0) { - throw new UnsupportedOperationException - ("Cannot start a fallback pattern"); - } - - // Check that we have satisfied all of the pattern requirements. - if ((requirements & LOCAL_REQUIRED) != 0) { - if (localKeyPair == null || !localKeyPair.hasPrivateKey()) - throw new IllegalStateException("Local static key required"); - } - if ((requirements & REMOTE_REQUIRED) != 0) { - if (remotePublicKey == null || !remotePublicKey.hasPublicKey()) - throw new IllegalStateException("Remote static key required"); - } - if ((requirements & PSK_REQUIRED) != 0) { - if (preSharedKey == null) - throw new IllegalStateException("Pre-shared key required"); - } - - // Log the symmetric state BEFORE any mixing operations (matching iOS) - Log.d(TAG, "=== ANDROID HANDSHAKE START - INITIAL STATE ==="); - Log.d(TAG, "Protocol: " + symmetric.getProtocolName()); - Log.d(TAG, "Role: " + (isInitiator ? "INITIATOR" : "RESPONDER")); - Log.d(TAG, "Initial symmetric hash: " + bytesToHex(symmetric.getHandshakeHash())); - - // Hash the prologue value. - Log.d(TAG, "Mixing empty prologue"); - if (prologue != null) - symmetric.mixHash(prologue, 0, prologue.length); - else - symmetric.mixHash(emptyPrologue, 0, 0); - Log.d(TAG, "Hash after empty prologue: " + bytesToHex(symmetric.getHandshakeHash())); - - // Hash the pre-shared key into the chaining key and handshake hash. - if (preSharedKey != null) - symmetric.mixPreSharedKey(preSharedKey); - - // Mix the pre-supplied public keys into the handshake hash. - if (isInitiator) { - Log.d(TAG, "XX pattern - no pre-message keys to mix"); - if ((requirements & LOCAL_PREMSG) != 0) - symmetric.mixPublicKey(localKeyPair); - if ((requirements & FALLBACK_PREMSG) != 0) { - symmetric.mixPublicKey(remoteEphemeral); - if (remoteHybrid != null) - symmetric.mixPublicKey(remoteHybrid); - if (preSharedKey != null) - symmetric.mixPublicKeyIntoCK(remoteEphemeral); - } - if ((requirements & REMOTE_PREMSG) != 0) - symmetric.mixPublicKey(remotePublicKey); - } else { - Log.d(TAG, "XX pattern - no pre-message keys to mix"); - if ((requirements & REMOTE_PREMSG) != 0) - symmetric.mixPublicKey(remotePublicKey); - if ((requirements & FALLBACK_PREMSG) != 0) { - symmetric.mixPublicKey(localEphemeral); - if (localHybrid != null) - symmetric.mixPublicKey(localHybrid); - if (preSharedKey != null) - symmetric.mixPublicKeyIntoCK(localEphemeral); - } - if ((requirements & LOCAL_PREMSG) != 0) - symmetric.mixPublicKey(localKeyPair); - } - - // Log final state after all initialization (matching iOS) - Log.d(TAG, "=== ANDROID HANDSHAKE START - FINAL STATE ==="); - Log.d(TAG, "Final symmetric hash after mixPreMessageKeys(): " + bytesToHex(symmetric.getHandshakeHash())); - Log.d(TAG, "==========================================="); - - // The handshake has officially started - set the first action. - if (isInitiator) - action = WRITE_MESSAGE; - else - action = READ_MESSAGE; - } - - /** - * Falls back to the "XXfallback" handshake pattern. - * - * This function is intended used to help implement the "Noise Pipes" protocol. - * It resets a HandshakeState object with the original handshake pattern - * (usually "IK"), converting it into an object with the handshake pattern - * "XXfallback". Information from the previous session such as the local - * keypair, the initiator's ephemeral key, the prologue value, and the - * pre-shared key, are passed to the new session. - * - * Once the fallback has been initiated, the application can set - * new values for the handshake parameters if the values from the - * previous session do not apply. For example, the application may - * use a different prologue for the fallback than for the original - * session. - * - * After setting any new parameters, the application calls start() - * again to restart the handshake from where it left off before the fallback. - * - * Note that this function reverses the roles of initiator and responder. - * - * @throws UnsupportedOperationException The current handshake pattern - * is not compatible with "XXfallback". - * - * @throws IllegalStateException The previous protocol has not started - * or it has not reached the fallback position yet. - * - * @throws NoSuchAlgorithmException One of the cryptographic algorithms - * that is specified in the new protocolName is not supported. - * - * @see #start() - */ - public void fallback() throws NoSuchAlgorithmException - { - fallback("XXfallback"); - } - - /** - * Falls back to another handshake pattern. - * - * @param patternName The name of the pattern to fall back to; - * e.g. "XXfallback", "NXfallback", etc. - * - * This function resets a HandshakeState object with the original - * handshake pattern, and converts it into an object with the new handshake - * patternName. Information from the previous session such as the local - * keypair, the initiator's ephemeral key, the prologue value, and the - * pre-shared key, are passed to the new session. - * - * Once the fallback has been initiated, the application can set - * new values for the handshake parameters if the values from the - * previous session do not apply. For example, the application may - * use a different prologue for the fallback than for the original - * session. - * - * After setting any new parameters, the application calls start() - * again to restart the handshake from where it left off before the fallback. - * - * The new pattern may have greater key requirements than the original; - * for example changing from "NK" from "XXfallback" requires that the - * initiator's static public key be set. The application is responsible for - * setting any extra keys before calling start(). - * - * Note that this function reverses the roles of initiator and responder. - * - * @throws UnsupportedOperationException The current handshake pattern - * is not compatible with the patternName, or patternName is not a - * fallback pattern. - * - * @throws IllegalStateException The previous protocol has not started - * or it has not reached the fallback position yet. - * - * @throws NoSuchAlgorithmException One of the cryptographic algorithms - * that is specified in the new protocolName is not supported. - * - * @see #start() - */ - public void fallback(String patternName) throws NoSuchAlgorithmException - { - // The original pattern must end in "K" for fallback to be possible. - if ((requirements & FALLBACK_POSSIBLE) == 0) - throw new UnsupportedOperationException("Previous handshake pattern does not support fallback"); - - // Check that "patternName" supports fallback. - short[] newPattern = Pattern.lookup(patternName); - if (newPattern == null || (newPattern[0] & Pattern.FLAG_REMOTE_EPHEM_REQ) == 0) - throw new UnsupportedOperationException("New pattern is not a fallback pattern"); - - // The initiator should be waiting for a return message from the - // responder, and the responder should have failed on the first - // handshake message from the initiator. We also allow the - // responder to fallback after processing the first message - // successfully; it decides to always fall back anyway. - if (isInitiator) { - if ((action != FAILED && action != READ_MESSAGE) || !localEphemeral.hasPublicKey()) - throw new IllegalStateException("Initiator cannot fall back from this state"); - } else { - if ((action != FAILED && action != WRITE_MESSAGE) || !remoteEphemeral.hasPublicKey()) - throw new IllegalStateException("Responder cannot fall back from this state"); - } - - // Format a new protocol name for the fallback variant - // and recreate the SymmetricState object. - String[] components = symmetric.getProtocolName().split("_"); - components[1] = patternName; - StringBuilder builder = new StringBuilder(); - builder.append(components[0]); - for (int index = 1; index < components.length; ++index) { - builder.append('_'); - builder.append(components[index]); - } - String name = builder.toString(); - SymmetricState newSymmetric = new SymmetricState(name, components[3], components[4]); - symmetric.destroy(); - symmetric = newSymmetric; - - // Convert the HandshakeState to the "XXfallback" pattern. - if (isInitiator) { - if (remoteEphemeral != null) - remoteEphemeral.clearKey(); - if (remoteHybrid != null) - remoteHybrid.clearKey(); - if (remotePublicKey != null) - remotePublicKey.clearKey(); - isInitiator = false; - } else { - if (localEphemeral != null) - localEphemeral.clearKey(); - if (localHybrid != null) - localHybrid.clearKey(); - if ((newPattern[0] & Pattern.FLAG_REMOTE_REQUIRED) == 0 && remotePublicKey != null) - remotePublicKey.clearKey(); - isInitiator = true; - } - action = NO_ACTION; - pattern = newPattern; - patternIndex = 1; - short flags = pattern[0]; - if (!isInitiator) { - // Reverse the pattern flags so that the responder is "local". - flags = Pattern.reverseFlags(flags); - } - requirements = computeRequirements(flags, components[0], isInitiator ? INITIATOR : RESPONDER, true); - } - - /** - * Gets the next action that the application should perform for - * the handshake part of the protocol. - * - * @return One of HandshakeState.NO_ACTION, HandshakeState.WRITE_MESSAGE, - * HandshakeState.READ_MESSAGE, HandshakeState.SPLIT, or - * HandshakeState.FAILED. - */ - public int getAction() - { - return action; - } - - /** - * Mixes the result of a Diffie-Hellman calculation into the chaining key. - * - * @param local Local private key object. - * @param remote Remote public key object. - */ - private void mixDH(DHState local, DHState remote) - { - if (local == null || remote == null) - throw new IllegalStateException("Pattern definition error"); - int len = local.getSharedKeyLength(); - byte[] shared = new byte [len]; - try { - local.calculate(shared, 0, remote); - symmetric.mixKey(shared, 0, len); - } finally { - Noise.destroy(shared); - } - } - - /** - * Writes a message payload during the handshake. - * - * @param message The buffer that will be populated with the - * handshake packet to be written to the transport. - * @param messageOffset First offset within the message buffer - * to be populated. - * @param payload Buffer containing the payload to add to the - * handshake message; can be null if there is no payload. - * @param payloadOffset Offset into the payload buffer of the - * first payload buffer. - * @param payloadLength Length of the payload in bytes. - * - * @return The length of the data written to the message buffer. - * - * @throws IllegalStateException The action is not WRITE_MESSAGE. - * - * @throws IllegalArgumentException The payload is null, but - * payloadOffset or payloadLength is non-zero. - * - * @throws ShortBufferException The message buffer does not have - * enough space for the handshake message. - * - * @see #getAction() - * @see #readMessage(byte[], int, int, byte[], int) - */ - public int writeMessage(byte[] message, int messageOffset, byte[] payload, int payloadOffset, int payloadLength) throws ShortBufferException - { - int messagePosn = messageOffset; - boolean success = false; - - // Validate the parameters and state. - if (action != WRITE_MESSAGE) { - throw new IllegalStateException - ("Handshake state does not allow writing messages"); - } - if (payload == null && (payloadOffset != 0 || payloadLength != 0)) { - throw new IllegalArgumentException("Invalid payload argument"); - } - if (messageOffset > message.length) { - throw new ShortBufferException(); - } - - // Format the message. - try { - // Process tokens until the direction changes or the patten ends. - for (;;) { - if (patternIndex >= pattern.length) { - // The pattern has finished, so the next action is "split". - action = SPLIT; - break; - } - short token = pattern[patternIndex++]; - if (token == Pattern.FLIP_DIR) { - // Change directions, so this message is complete and the - // next action is "read message". - action = READ_MESSAGE; - break; - } - int space = message.length - messagePosn; - int len, macLen; - switch (token) { - case Pattern.E: - { - // Generate a local ephemeral keypair and add the public - // key to the message. If we are running fixed vector tests, - // then the ephemeral key may have already been provided. - if (localEphemeral == null) - throw new IllegalStateException("Pattern definition error"); - if (fixedEphemeral == null) - localEphemeral.generateKeyPair(); - else - localEphemeral.copyFrom(fixedEphemeral); - len = localEphemeral.getPublicKeyLength(); - if (space < len) - throw new ShortBufferException(); - localEphemeral.getPublicKey(message, messagePosn); - symmetric.mixHash(message, messagePosn, len); - - // If the protocol is using pre-shared keys, then also mix - // the local ephemeral key into the chaining key. - if (preSharedKey != null) - symmetric.mixKey(message, messagePosn, len); - messagePosn += len; - } - break; - - case Pattern.S: - { - // Encrypt the local static public key and add it to the message. - if (localKeyPair == null) - throw new IllegalStateException("Pattern definition error"); - len = localKeyPair.getPublicKeyLength(); - macLen = symmetric.getMACLength(); - if (space < (len + macLen)) - throw new ShortBufferException(); - localKeyPair.getPublicKey(message, messagePosn); - messagePosn += symmetric.encryptAndHash(message, messagePosn, message, messagePosn, len); - } - break; - - case Pattern.EE: - { - // DH operation with initiator and responder ephemeral keys. - mixDH(localEphemeral, remoteEphemeral); - } - break; - - case Pattern.ES: - { - // DH operation with initiator ephemeral and responder static keys. - if (isInitiator) - mixDH(localEphemeral, remotePublicKey); - else - mixDH(localKeyPair, remoteEphemeral); - } - break; - - case Pattern.SE: - { - // DH operation with initiator static and responder ephemeral keys. - if (isInitiator) - mixDH(localKeyPair, remoteEphemeral); - else - mixDH(localEphemeral, remotePublicKey); - } - break; - - case Pattern.SS: - { - // DH operation with initiator and responder static keys. - mixDH(localKeyPair, remotePublicKey); - } - break; - - case Pattern.F: - { - // Generate a local hybrid keypair and add the public - // key to the message. If we are running fixed vector tests, - // then a fixed hybrid key may have already been provided. - if (localHybrid == null) - throw new IllegalStateException("Pattern definition error"); - if (localHybrid instanceof DHStateHybrid) { - // The DH object is something like New Hope which needs to - // generate keys relative to the other party's public key. - DHStateHybrid hybrid = (DHStateHybrid)localHybrid; - if (fixedHybrid == null) - hybrid.generateKeyPair(remoteHybrid); - else - hybrid.copyFrom(fixedHybrid, remoteHybrid); - } else { - if (fixedHybrid == null) - localHybrid.generateKeyPair(); - else - localHybrid.copyFrom(fixedHybrid); - } - len = localHybrid.getPublicKeyLength(); - if (space < len) - throw new ShortBufferException(); - macLen = symmetric.getMACLength(); - if (space < (len + macLen)) - throw new ShortBufferException(); - localHybrid.getPublicKey(message, messagePosn); - messagePosn += symmetric.encryptAndHash(message, messagePosn, message, messagePosn, len); - } - break; - - case Pattern.FF: - { - // DH operation with initiator and responder hybrid keys. - mixDH(localHybrid, remoteHybrid); - } - break; - - default: - { - // Unknown token code. Abort. - throw new IllegalStateException("Unknown handshake token " + Integer.toString(token)); - } - } - } - - // Add the payload to the message buffer and encrypt it. - if (payload != null) - messagePosn += symmetric.encryptAndHash(payload, payloadOffset, message, messagePosn, payloadLength); - else - messagePosn += symmetric.encryptAndHash(message, messagePosn, message, messagePosn, 0); - success = true; - } finally { - // 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.length - messageOffset, (byte)0); - action = FAILED; - } - } - return messagePosn - messageOffset; - } - - /** - * Reads a message payload during the handshake. - * - * @param message Buffer containing the incoming handshake - * that was read from the transport. - * @param messageOffset Offset of the first message byte. - * @param messageLength The length of the incoming message. - * @param payload Buffer that will be populated with the message payload. - * @param payloadOffset Offset of the first byte in the - * payload buffer to be populated with payload data. - * - * @return The length of the payload. - * - * @throws IllegalStateException The action is not READ_MESSAGE. - * - * @throws ShortBufferException The message buffer does not have - * sufficient bytes for a valid message or the payload buffer does - * not have enough space for the decrypted payload. - * - * @throws BadPaddingException A MAC value in the message failed - * to verify. - * - * @see #getAction() - * @see #writeMessage(byte[], int, byte[], int, int) - */ - public int readMessage(byte[] message, int messageOffset, int messageLength, byte[] payload, int payloadOffset) throws ShortBufferException, BadPaddingException - { - boolean success = false; - int messageEnd = messageOffset + messageLength; - - // Validate the parameters. - if (action != READ_MESSAGE) { - throw new IllegalStateException - ("Handshake state does not allow reading messages"); - } - if (messageOffset > message.length || payloadOffset > payload.length) { - throw new ShortBufferException(); - } - if (messageLength > (message.length - messageOffset)) { - throw new ShortBufferException(); - } - - // Process the message. - try { - // Process tokens until the direction changes or the patten ends. - for (;;) { - if (patternIndex >= pattern.length) { - // The pattern has finished, so the next action is "split". - action = SPLIT; - break; - } - short token = pattern[patternIndex++]; - if (token == Pattern.FLIP_DIR) { - // Change directions, so this message is complete and the - // next action is "write message". - action = WRITE_MESSAGE; - break; - } - int space = messageEnd - messageOffset; - int len, macLen; - switch (token) { - case Pattern.E: - { - // Save the remote ephemeral key and hash it. - if (remoteEphemeral == null) - throw new IllegalStateException("Pattern definition error"); - len = remoteEphemeral.getPublicKeyLength(); - if (space < len) - throw new ShortBufferException(); - symmetric.mixHash(message, messageOffset, len); - remoteEphemeral.setPublicKey(message, messageOffset); - if (remoteEphemeral.isNullPublicKey()) { - // The remote ephemeral key is null, which means that it is - // not contributing anything to the security of the session - // and is in fact downgrading the security to "none at all" - // in some of the message patterns. Reject all such keys. - throw new BadPaddingException("Null remote public key"); - } - - // If the protocol is using pre-shared keys, then also mix - // the remote ephemeral key into the chaining key. - if (preSharedKey != null) - symmetric.mixKey(message, messageOffset, len); - messageOffset += len; - } - break; - - case Pattern.S: - { - // Decrypt and read the remote static key. - if (remotePublicKey == null) - throw new IllegalStateException("Pattern definition error"); - len = remotePublicKey.getPublicKeyLength(); - macLen = symmetric.getMACLength(); - if (space < (len + macLen)) - throw new ShortBufferException(); - byte[] temp = new byte [len]; - try { - if (symmetric.decryptAndHash(message, messageOffset, temp, 0, len + macLen) != len) - throw new ShortBufferException(); - remotePublicKey.setPublicKey(temp, 0); - } finally { - Noise.destroy(temp); - } - messageOffset += len + macLen; - } - break; - - case Pattern.EE: - { - // DH operation with initiator and responder ephemeral keys. - mixDH(localEphemeral, remoteEphemeral); - } - break; - - case Pattern.ES: - { - // DH operation with initiator ephemeral and responder static keys. - if (isInitiator) - mixDH(localEphemeral, remotePublicKey); - else - mixDH(localKeyPair, remoteEphemeral); - } - break; - - case Pattern.SE: - { - // DH operation with initiator static and responder ephemeral keys. - if (isInitiator) - mixDH(localKeyPair, remoteEphemeral); - else - mixDH(localEphemeral, remotePublicKey); - } - break; - - case Pattern.SS: - { - // DH operation with initiator and responder static keys. - mixDH(localKeyPair, remotePublicKey); - } - break; - - case Pattern.F: - { - // Decrypt and read the remote hybrid ephemeral key. - if (remoteHybrid == null) - throw new IllegalStateException("Pattern definition error"); - if (remoteHybrid instanceof DHStateHybrid) { - // The DH object is something like New Hope. The public key - // length may need to change based on whether we already have - // generated a local hybrid keypair or not. - ((DHStateHybrid)remoteHybrid).specifyPeer(localHybrid); - } - len = remoteHybrid.getPublicKeyLength(); - macLen = symmetric.getMACLength(); - if (space < (len + macLen)) - throw new ShortBufferException(); - byte[] temp = new byte [len]; - try { - if (symmetric.decryptAndHash(message, messageOffset, temp, 0, len + macLen) != len) - throw new ShortBufferException(); - remoteHybrid.setPublicKey(temp, 0); - } finally { - Noise.destroy(temp); - } - messageOffset += len + macLen; - } - break; - - case Pattern.FF: - { - // DH operation with initiator and responder hybrid keys. - mixDH(localHybrid, remoteHybrid); - } - break; - - default: - { - // Unknown token code. Abort. - throw new IllegalStateException("Unknown handshake token " + Integer.toString(token)); - } - } - } - - // Decrypt the message payload. - int payloadLength = symmetric.decryptAndHash(message, messageOffset, payload, payloadOffset, messageEnd - messageOffset); - success = true; - return payloadLength; - } finally { - // 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.length - payloadOffset, (byte)0); - action = FAILED; - } - } - } - - /** - * Splits the transport encryption CipherState objects out of - * this HandshakeState object once the handshake completes. - * - * @return The pair of ciphers for sending and receiving. - * - * @throws IllegalStateException The action is not SPLIT. - */ - public CipherStatePair split() - { - if (action != SPLIT) { - throw new IllegalStateException - ("Handshake has not finished"); - } - CipherStatePair pair = symmetric.split(); - if (!isInitiator) - pair.swap(); - action = COMPLETE; - return pair; - } - - /** - * Splits the transport encryption CipherState objects out of - * this HandshakeObject after mixing 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 IllegalStateException The action is not SPLIT. - * - * @throws IllegalArgumentException The length is not 0 or 32. - */ - public CipherStatePair split(byte[] secondaryKey, int offset, int length) - { - if (action != SPLIT) { - throw new IllegalStateException - ("Handshake has not finished"); - } - CipherStatePair pair = symmetric.split(secondaryKey, offset, length); - if (!isInitiator) { - // Swap the sender and receiver objects for the responder - // to make it easier on the application to know which is which. - pair.swap(); - } - action = COMPLETE; - return pair; - } - - /** - * Gets the current value of the handshake hash. - * - * @return The handshake hash. This must not be modified by the caller. - * - * @throws IllegalStateException The action is not SPLIT or COMPLETE. - */ - public byte[] getHandshakeHash() - { - if (action != SPLIT && action != COMPLETE) { - throw new IllegalStateException - ("Handshake has not completed"); - } - return symmetric.getHandshakeHash(); - } - - @Override - public void destroy() { - if (symmetric != null) - symmetric.destroy(); - if (localKeyPair != null) - localKeyPair.destroy(); - if (localEphemeral != null) - localEphemeral.destroy(); - if (localHybrid != null) - localHybrid.destroy(); - if (remotePublicKey != null) - remotePublicKey.destroy(); - if (remoteEphemeral != null) - remoteEphemeral.destroy(); - if (remoteHybrid != null) - remoteHybrid.destroy(); - if (fixedEphemeral != null) - fixedEphemeral.destroy(); - if (fixedHybrid != null) - fixedHybrid.destroy(); - if (preSharedKey != null) - Noise.destroy(preSharedKey); - if (prologue != null) - Noise.destroy(prologue); - } - - /** - * Computes the requirements for a handshake. - * - * @param flags The flags from the handshake's pattern. - * @param prefix The prefix from the protocol name; typically - * "Noise" or "NoisePSK". - * @param role The role, HandshakeState.INITIATOR or HandshakeState.RESPONDER. - * @param isFallback Set to true if we need the requirements for a - * fallback pattern; false for a regular pattern. - * - * @return The set of requirements for the handshake. - */ - private static int computeRequirements(short flags, String prefix, int role, boolean isFallback) - { - int requirements = 0; - if ((flags & Pattern.FLAG_LOCAL_STATIC) != 0) { - requirements |= LOCAL_REQUIRED; - } - if ((flags & Pattern.FLAG_LOCAL_REQUIRED) != 0) { - requirements |= LOCAL_REQUIRED; - requirements |= LOCAL_PREMSG; - } - if ((flags & Pattern.FLAG_REMOTE_REQUIRED) != 0) { - requirements |= REMOTE_REQUIRED; - requirements |= REMOTE_PREMSG; - } - if ((flags & (Pattern.FLAG_REMOTE_EPHEM_REQ | - Pattern.FLAG_LOCAL_EPHEM_REQ)) != 0) { - if (isFallback) - requirements |= FALLBACK_PREMSG; - } - if (prefix.equals("NoisePSK")) { - requirements |= PSK_REQUIRED; - } - return requirements; - } -} 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 new file mode 100644 index 00000000..e2605db1 --- /dev/null +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/HandshakeState.kt @@ -0,0 +1,1196 @@ +/* + * Copyright (C) 2016 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * 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 + +import android.util.Log +import com.bitchat.android.noise.southernstorm.protocol.Noise.copySubArray +import com.bitchat.android.noise.southernstorm.protocol.Noise.createDH +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 + +/** + * Interface to a Noise handshake. + */ +class HandshakeState(protocolName: String, role: Int) : Destroyable { + private var symmetric: SymmetricState? + private var isInitiator: Boolean + + /** + * Gets the keypair object for the local static key. + * + * @return The keypair, or null if a local static key is not required. + */ + var localKeyPair: DHState? = null + private set + private var localEphemeral: DHState? = null + private var localHybrid: DHState? = null + + /** + * Gets the public key object for the remote static key. + * + * @return The public key, or null if a remote static key + * is not required. + */ + var remotePublicKey: DHState? = null + private set + private var remoteEphemeral: DHState? = null + private var remoteHybrid: DHState? = null + private var fixedEphemeral: DHState? = null + private var fixedHybrid: DHState? = null + + /** + * Gets the next action that the application should perform for + * the handshake part of the protocol. + * + * @return One of HandshakeState.NO_ACTION, HandshakeState.WRITE_MESSAGE, + * HandshakeState.READ_MESSAGE, HandshakeState.SPLIT, or + * HandshakeState.FAILED. + */ + var action: Int + private set + private var requirements: Int + private var pattern: ShortArray? + private var patternIndex: Int + private var preSharedKey: ByteArray? + private var prologue: ByteArray? + + val protocolName: String? + /** + * Gets the name of the Noise protocol. + * + * @return The protocol name. + */ + get() = symmetric!!.getProtocolName() + + val role: Int + /** + * Gets the role for this handshake. + * + * @return The role, HandshakeState.INITIATOR or HandshakeState.RESPONDER. + */ + get() = if (isInitiator) INITIATOR else RESPONDER + + /** + * Determine if this handshake needs a pre-shared key value + * and one has not been configured yet. + * + * @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 + } + + /** + * Determine if this object has already been configured with a + * pre-shared key. + * + * @return true if the pre-shared key has already been configured; + * false if one is not needed or it has not been configured yet. + */ + fun hasPreSharedKey(): Boolean { + return preSharedKey != null + } + + /** + * Sets the pre-shared key for this handshake. + * + * @param key Buffer containing the pre-shared key value. + * @param offset Offset into the buffer of the first byte of the key. + * @param length The length of the pre-shared key, which must be 32. + * + * @throws IllegalArgumentException The length is not 32. + * + * @throws UnsupportedOperationException Pre-shared keys are not + * supported for this handshake type. + * + * @throws IllegalStateException The handshake has already started, + * so the pre-shared key can no longer be set. + */ + fun setPreSharedKey(key: ByteArray, offset: Int, length: Int) { + require(length == 32) { "Pre-shared keys must be 32 bytes in length" } + if ((requirements and PSK_REQUIRED) == 0) { + throw UnsupportedOperationException("Pre-shared keys are not supported for this handshake") + } + check(action == NO_ACTION) { "Handshake has already started; cannot set pre-shared key" } + if (preSharedKey != null) { + Noise.destroy(preSharedKey!!) + preSharedKey = null + } + preSharedKey = copySubArray(key, offset, length) + } + + /** + * Sets the prologue for this handshake. + * + * @param prologue Buffer containing the prologue value. + * @param offset Offset into the buffer of the first byte of the prologue. + * @param length The length of the prologue in bytes. + * + * @throws IllegalStateException The handshake has already started, + * so the prologue can no longer be set. + */ + fun setPrologue(prologue: ByteArray, offset: Int, length: Int) { + check(action == NO_ACTION) { "Handshake has already started; cannot set prologue" } + if (this.prologue != null) { + Noise.destroy(this.prologue!!) + this.prologue = null + } + this.prologue = copySubArray(prologue, offset, length) + } + + /** + * Determine if this handshake requires a local static key. + * + * @return true if a local static key is needed; false if not. + * + * If the local static key has already been set, then this function + * will return false. + */ + fun needsLocalKeyPair(): Boolean { + if (localKeyPair != null) return !localKeyPair!!.hasPrivateKey() + else return false + } + + /** + * Determine if this handshake has already been configured + * with a local static key. + * + * @return true if the local static key has been configured; + * false if not. + */ + fun hasLocalKeyPair(): Boolean { + if (localKeyPair != null) return localKeyPair!!.hasPrivateKey() + else return false + } + + /** + * Determine if this handshake requires a remote static key. + * + * @return true if a remote static key is needed; false if not. + * + * If the remote static key has already been set, then this function + * will return false. + */ + fun needsRemotePublicKey(): Boolean { + if (remotePublicKey != null) return !remotePublicKey!!.hasPublicKey() + else return false + } + + /** + * Determine if this handshake has already been configured + * with a remote static key. + * + * @return true if the remote static key has been configured; + * false if not. + */ + fun hasRemotePublicKey(): Boolean { + if (remotePublicKey != null) return remotePublicKey!!.hasPublicKey() + else return false + } + + val fixedEphemeralKey: DHState? + /** + * Gets the DHState object containing a fixed local ephemeral + * key value for this handshake. + * + * @return The fixed ephemeral key object, or null if a local + * ephemeral key is not required by this handshake. + * + * This function is intended for testing only. It can be used + * to establish a fixed ephemeral key for test vectors. This + * function should not be used in real applications. + */ + get() { + if (fixedEphemeral != null) return fixedEphemeral + if (localEphemeral == null) return null + try { + fixedEphemeral = + createDH(localEphemeral!!.getDHName()) + } catch (e: NoSuchAlgorithmException) { + // This shouldn't happen - the local ephemeral key would + // have already been created with the same name! + fixedEphemeral = null + } + return fixedEphemeral + } + + val fixedHybridKey: DHState? + /** + * Gets the DHState object containing a fixed local hybrid + * key value for this handshake. + * + * @return The fixed hybrid key object, or null if a local + * hybrid key is not required by this handshake. + * + * This function is intended for testing only. It can be used + * to establish a fixed hybrid key for test vectors. This + * function should not be used in real applications. + */ + get() { + if (fixedHybrid != null) return fixedHybrid + if (localHybrid == null) return null + try { + fixedHybrid = + createDH(localHybrid!!.getDHName()) + } catch (e: NoSuchAlgorithmException) { + // This shouldn't happen - the local hybrid key would + // have already been created with the same name! + fixedHybrid = null + } + return fixedHybrid + } + + /** + * Creates a new Noise handshake. + * + * @param protocolName The name of the Noise protocol. + * @param role The role, HandshakeState.INITIATOR or HandshakeState.RESPONDER. + * + * @throws IllegalArgumentException The protocolName is not + * formatted correctly, or the role is not recognized. + * + * @throws NoSuchAlgorithmException One of the cryptographic algorithms + * that is specified in the protocolName is not supported. + */ + init { + // Parse the protocol name into its components. + val components = + protocolName.split("_".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + require(components.size == 5) { "Protocol name must have 5 components" } + val prefix = components[0] + val patternId = components[1] + var dh = components[2] + var hybrid: String? = null + val cipher: String? = components[3] + val hash: String? = components[4] + require(!(prefix != "Noise" && prefix != "NoisePSK")) { "Prefix must be Noise or NoisePSK" } + pattern = lookup(patternId) + requireNotNull(pattern) { "Handshake pattern is not recognized" } + var flags = pattern!![0] + var extraReqs = 0 + if ((flags.toInt() and Pattern.FLAG_REMOTE_REQUIRED.toInt()) != 0 && patternId.length > 1) extraReqs = + extraReqs or FALLBACK_POSSIBLE + if (role == RESPONDER) { + // Reverse the pattern flags so that the responder is "local". + flags = reverseFlags(flags) + } + val index = dh.indexOf('+') + if (index != -1) { + // The DH name has two components: regular and hybrid. + hybrid = dh.substring(index + 1) + dh = dh.substring(0, index) + require(!((flags.toInt() and Pattern.FLAG_LOCAL_HYBRID.toInt()) == 0 || (flags.toInt() and Pattern.FLAG_REMOTE_HYBRID.toInt()) == 0)) { "Hybrid function specified for non-hybrid pattern" } + } else { + require(!((flags.toInt() and Pattern.FLAG_LOCAL_HYBRID.toInt()) != 0 || (flags.toInt() and Pattern.FLAG_REMOTE_HYBRID.toInt()) != 0)) { "Hybrid function not specified for hybrid pattern" } + } + + // Check that the role is correctly specified. + require(!(role != INITIATOR && role != RESPONDER)) { "Role must be initiator or responder" } + + // Initialize this object. This will also create the cipher and hash objects. + symmetric = SymmetricState(protocolName, cipher, hash) + isInitiator = (role == INITIATOR) + action = NO_ACTION + requirements = extraReqs or computeRequirements(flags, prefix, role, false) + patternIndex = 1 + + + // Create the DH objects that we will need later. + if ((flags.toInt() and Pattern.FLAG_LOCAL_STATIC.toInt()) != 0) localKeyPair = createDH(dh) + 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!!) + 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!!) + + + // 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!!.getDHName() + "' for static keys") + if (localEphemeral is DHStateHybrid) throw NoSuchAlgorithmException("Cannot use '" + localEphemeral!!.getDHName() + "' for ephemeral keys") + if (remotePublicKey is DHStateHybrid) throw NoSuchAlgorithmException("Cannot use '" + remotePublicKey!!.getDHName() + "' for static keys") + if (remoteEphemeral is DHStateHybrid) throw NoSuchAlgorithmException("Cannot use '" + remoteEphemeral!!.getDHName() + "' for ephemeral keys") + } + + /** + * Starts the handshake running. + * + * This function is called after all of the handshake parameters have been + * provided to the HandshakeState object. This function should be followed + * by calls to writeMessage() or readMessage() to process the handshake + * messages. The getAction() function indicates the action to take next. + * + * @throws IllegalStateException The handshake has already started, or one or + * more of the required parameters has not been supplied. + * + * @throws UnsupportedOperationException An attempt was made to start a + * fallback handshake pattern without first calling fallback() on a + * previous handshake. + * + * @see .getAction + * @see .writeMessage + * @see .readMessage + * @see .fallback + */ + fun start() { + check(action == NO_ACTION) { "Handshake has already started; cannot start again" } + if ((pattern!![0].toInt() and Pattern.FLAG_REMOTE_EPHEM_REQ.toInt()) != 0 && + (requirements and FALLBACK_PREMSG) == 0 + ) { + throw UnsupportedOperationException("Cannot start a fallback pattern") + } + + + // Check that we have satisfied all of the pattern requirements. + if ((requirements and LOCAL_REQUIRED) != 0) { + check(!(localKeyPair == null || !localKeyPair!!.hasPrivateKey())) { "Local static key required" } + } + if ((requirements and REMOTE_REQUIRED) != 0) { + check(!(remotePublicKey == null || !remotePublicKey!!.hasPublicKey())) { "Remote static key required" } + } + if ((requirements and PSK_REQUIRED) != 0) { + checkNotNull(preSharedKey) { "Pre-shared key required" } + } + + // Log the symmetric state BEFORE any mixing operations (matching iOS) + Log.d(TAG, "=== ANDROID HANDSHAKE START - INITIAL STATE ===") + Log.d(TAG, "Protocol: " + symmetric!!.getProtocolName()) + Log.d(TAG, "Role: " + (if (isInitiator) "INITIATOR" else "RESPONDER")) + Log.d(TAG, "Initial symmetric hash: " + bytesToHex(symmetric!!.getHandshakeHash())) + + + // Hash the prologue value. + Log.d(TAG, "Mixing empty prologue") + if (prologue != null) symmetric!!.mixHash(prologue, 0, prologue!!.size) + else symmetric!!.mixHash(emptyPrologue, 0, 0) + Log.d(TAG, "Hash after empty prologue: " + bytesToHex(symmetric!!.getHandshakeHash())) + + + // Hash the pre-shared key into the chaining key and handshake hash. + if (preSharedKey != null) symmetric!!.mixPreSharedKey(preSharedKey) + + + // Mix the pre-supplied public keys into the handshake hash. + if (isInitiator) { + Log.d(TAG, "XX pattern - no pre-message keys to mix") + if ((requirements and LOCAL_PREMSG) != 0) symmetric!!.mixPublicKey(localKeyPair) + if ((requirements and FALLBACK_PREMSG) != 0) { + symmetric!!.mixPublicKey(remoteEphemeral) + if (remoteHybrid != null) symmetric!!.mixPublicKey(remoteHybrid) + if (preSharedKey != null) symmetric!!.mixPublicKeyIntoCK(remoteEphemeral) + } + if ((requirements and REMOTE_PREMSG) != 0) symmetric!!.mixPublicKey(remotePublicKey) + } else { + Log.d(TAG, "XX pattern - no pre-message keys to mix") + if ((requirements and REMOTE_PREMSG) != 0) symmetric!!.mixPublicKey(remotePublicKey) + if ((requirements and FALLBACK_PREMSG) != 0) { + symmetric!!.mixPublicKey(localEphemeral) + if (localHybrid != null) symmetric!!.mixPublicKey(localHybrid) + if (preSharedKey != null) symmetric!!.mixPublicKeyIntoCK(localEphemeral) + } + if ((requirements and LOCAL_PREMSG) != 0) symmetric!!.mixPublicKey(localKeyPair) + } + + // Log final state after all initialization (matching iOS) + Log.d(TAG, "=== ANDROID HANDSHAKE START - FINAL STATE ===") + Log.d( + TAG, + "Final symmetric hash after mixPreMessageKeys(): " + bytesToHex(symmetric!!.getHandshakeHash()) + ) + Log.d(TAG, "===========================================") + + // The handshake has officially started - set the first action. + if (isInitiator) action = WRITE_MESSAGE + else action = READ_MESSAGE + } + + /** + * Falls back to another handshake pattern. + * + * @param patternName The name of the pattern to fall back to; + * e.g. "XXfallback", "NXfallback", etc. + * + * This function resets a HandshakeState object with the original + * handshake pattern, and converts it into an object with the new handshake + * patternName. Information from the previous session such as the local + * keypair, the initiator's ephemeral key, the prologue value, and the + * pre-shared key, are passed to the new session. + * + * Once the fallback has been initiated, the application can set + * new values for the handshake parameters if the values from the + * previous session do not apply. For example, the application may + * use a different prologue for the fallback than for the original + * session. + * + * After setting any new parameters, the application calls start() + * again to restart the handshake from where it left off before the fallback. + * + * The new pattern may have greater key requirements than the original; + * for example changing from "NK" from "XXfallback" requires that the + * initiator's static public key be set. The application is responsible for + * setting any extra keys before calling start(). + * + * Note that this function reverses the roles of initiator and responder. + * + * @throws UnsupportedOperationException The current handshake pattern + * is not compatible with the patternName, or patternName is not a + * fallback pattern. + * + * @throws IllegalStateException The previous protocol has not started + * or it has not reached the fallback position yet. + * + * @throws NoSuchAlgorithmException One of the cryptographic algorithms + * that is specified in the new protocolName is not supported. + * + * @see .start + */ + /** + * Falls back to the "XXfallback" handshake pattern. + * + * This function is intended used to help implement the "Noise Pipes" protocol. + * It resets a HandshakeState object with the original handshake pattern + * (usually "IK"), converting it into an object with the handshake pattern + * "XXfallback". Information from the previous session such as the local + * keypair, the initiator's ephemeral key, the prologue value, and the + * pre-shared key, are passed to the new session. + * + * Once the fallback has been initiated, the application can set + * new values for the handshake parameters if the values from the + * previous session do not apply. For example, the application may + * use a different prologue for the fallback than for the original + * session. + * + * After setting any new parameters, the application calls start() + * again to restart the handshake from where it left off before the fallback. + * + * Note that this function reverses the roles of initiator and responder. + * + * @throws UnsupportedOperationException The current handshake pattern + * is not compatible with "XXfallback". + * + * @throws IllegalStateException The previous protocol has not started + * or it has not reached the fallback position yet. + * + * @throws NoSuchAlgorithmException One of the cryptographic algorithms + * that is specified in the new protocolName is not supported. + * + * @see .start + */ + @JvmOverloads + @Throws(NoSuchAlgorithmException::class) + fun fallback(patternName: String = "XXfallback") { + // The original pattern must end in "K" for fallback to be possible. + if ((requirements and FALLBACK_POSSIBLE) == 0) throw UnsupportedOperationException("Previous handshake pattern does not support fallback") + + // Check that "patternName" supports fallback. + val newPattern = lookup(patternName) + if (newPattern == null || (newPattern[0].toInt() and Pattern.FLAG_REMOTE_EPHEM_REQ.toInt()) == 0) throw UnsupportedOperationException( + "New pattern is not a fallback pattern" + ) + + // The initiator should be waiting for a return message from the + // responder, and the responder should have failed on the first + // handshake message from the initiator. We also allow the + // responder to fallback after processing the first message + // successfully; it decides to always fall back anyway. + if (isInitiator) { + check(!((action != FAILED && action != READ_MESSAGE) || !localEphemeral!!.hasPublicKey())) { "Initiator cannot fall back from this state" } + } else { + check(!((action != FAILED && action != WRITE_MESSAGE) || !remoteEphemeral!!.hasPublicKey())) { "Responder cannot fall back from this state" } + } + + + // Format a new protocol name for the fallback variant + // and recreate the SymmetricState object. + val components = + symmetric!!.getProtocolName().split("_".toRegex()).dropLastWhile { it.isEmpty() } + .toTypedArray() + components[1] = patternName + val builder = StringBuilder() + builder.append(components[0]) + for (index in 1.. message.size) { + throw ShortBufferException() + } + + + // Format the message. + try { + // Process tokens until the direction changes or the patten ends. + while (true) { + if (patternIndex >= pattern!!.size) { + // The pattern has finished, so the next action is "split". + action = SPLIT + break + } + val token = pattern!![patternIndex++] + if (token == Pattern.FLIP_DIR) { + // Change directions, so this message is complete and the + // next action is "read message". + action = READ_MESSAGE + break + } + val space = message.size - messagePosn + val len: Int + val macLen: Int + when (token) { + Pattern.E -> { + // Generate a local ephemeral keypair and add the public + // key to the message. If we are running fixed vector tests, + // then the ephemeral key may have already been provided. + checkNotNull(localEphemeral) { "Pattern definition error" } + if (fixedEphemeral == null) localEphemeral!!.generateKeyPair() + else localEphemeral!!.copyFrom(fixedEphemeral) + len = localEphemeral!!.getPublicKeyLength() + if (space < len) throw ShortBufferException() + localEphemeral!!.getPublicKey(message, messagePosn) + symmetric!!.mixHash(message, messagePosn, len) + + // If the protocol is using pre-shared keys, then also mix + // the local ephemeral key into the chaining key. + if (preSharedKey != null) symmetric!!.mixKey(message, messagePosn, len) + messagePosn += len + } + + Pattern.S -> { + // Encrypt the local static public key and add it to the message. + checkNotNull(localKeyPair) { "Pattern definition error" } + len = localKeyPair!!.getPublicKeyLength() + macLen = symmetric!!.getMACLength() + if (space < (len + macLen)) throw ShortBufferException() + localKeyPair!!.getPublicKey(message, messagePosn) + messagePosn += symmetric!!.encryptAndHash( + message, + messagePosn, + message, + messagePosn, + len + ) + } + + Pattern.EE -> { + // DH operation with initiator and responder ephemeral keys. + mixDH(localEphemeral!!, remoteEphemeral!!) + } + + Pattern.ES -> { + // DH operation with initiator ephemeral and responder static keys. + if (isInitiator) mixDH(localEphemeral!!, remotePublicKey!!) + else mixDH(localKeyPair!!, remoteEphemeral!!) + } + + Pattern.SE -> { + // DH operation with initiator static and responder ephemeral keys. + if (isInitiator) mixDH(localKeyPair!!, remoteEphemeral!!) + else mixDH(localEphemeral!!, remotePublicKey!!) + } + + Pattern.SS -> { + // DH operation with initiator and responder static keys. + mixDH(localKeyPair!!, remotePublicKey!!) + } + + Pattern.F -> { + // Generate a local hybrid keypair and add the public + // key to the message. If we are running fixed vector tests, + // then a fixed hybrid key may have already been provided. + checkNotNull(localHybrid) { "Pattern definition error" } + if (localHybrid is DHStateHybrid) { + // The DH object is something like New Hope which needs to + // generate keys relative to the other party's public key. + val hybrid = localHybrid as DHStateHybrid + if (fixedHybrid == null) hybrid.generateKeyPair(remoteHybrid) + else hybrid.copyFrom(fixedHybrid, remoteHybrid) + } else { + if (fixedHybrid == null) localHybrid!!.generateKeyPair() + else localHybrid!!.copyFrom(fixedHybrid) + } + len = localHybrid!!.getPublicKeyLength() + if (space < len) throw ShortBufferException() + macLen = symmetric!!.getMACLength() + if (space < (len + macLen)) throw ShortBufferException() + localHybrid!!.getPublicKey(message, messagePosn) + messagePosn += symmetric!!.encryptAndHash( + message, + messagePosn, + message, + messagePosn, + len + ) + } + + Pattern.FF -> { + // DH operation with initiator and responder hybrid keys. + mixDH(localHybrid!!, remoteHybrid!!) + } + + else -> { + // Unknown token code. Abort. + throw IllegalStateException( + "Unknown handshake token " + token.toInt().toString() + ) + } + } + } + + + // Add the payload to the message buffer and encrypt it. + if (payload != null) messagePosn += symmetric!!.encryptAndHash( + payload, + payloadOffset, + message, + messagePosn, + payloadLength + ) + else messagePosn += symmetric!!.encryptAndHash( + message, + messagePosn, + message, + messagePosn, + 0 + ) + success = true + } finally { + // 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()) + action = FAILED + } + } + return messagePosn - messageOffset + } + + /** + * Reads a message payload during the handshake. + * + * @param message Buffer containing the incoming handshake + * that was read from the transport. + * @param messageOffset Offset of the first message byte. + * @param messageLength The length of the incoming message. + * @param payload Buffer that will be populated with the message payload. + * @param payloadOffset Offset of the first byte in the + * payload buffer to be populated with payload data. + * + * @return The length of the payload. + * + * @throws IllegalStateException The action is not READ_MESSAGE. + * + * @throws ShortBufferException The message buffer does not have + * sufficient bytes for a valid message or the payload buffer does + * not have enough space for the decrypted payload. + * + * @throws BadPaddingException A MAC value in the message failed + * to verify. + * + * @see .getAction + * @see .writeMessage + */ + @Throws(ShortBufferException::class, BadPaddingException::class) + fun readMessage( + message: ByteArray, + messageOffset: Int, + messageLength: Int, + payload: ByteArray, + payloadOffset: Int + ): Int { + var messageOffset = messageOffset + var success = false + val messageEnd = messageOffset + messageLength + + + // Validate the parameters. + check(action == READ_MESSAGE) { "Handshake state does not allow reading messages" } + if (messageOffset > message.size || payloadOffset > payload.size) { + throw ShortBufferException() + } + if (messageLength > (message.size - messageOffset)) { + throw ShortBufferException() + } + + + // Process the message. + try { + // Process tokens until the direction changes or the patten ends. + while (true) { + if (patternIndex >= pattern!!.size) { + // The pattern has finished, so the next action is "split". + action = SPLIT + break + } + val token = pattern!![patternIndex++] + if (token == Pattern.FLIP_DIR) { + // Change directions, so this message is complete and the + // next action is "write message". + action = WRITE_MESSAGE + break + } + val space = messageEnd - messageOffset + val len: Int + val macLen: Int + when (token) { + Pattern.E -> { + // Save the remote ephemeral key and hash it. + checkNotNull(remoteEphemeral) { "Pattern definition error" } + len = remoteEphemeral!!.getPublicKeyLength() + if (space < len) throw ShortBufferException() + symmetric!!.mixHash(message, messageOffset, len) + remoteEphemeral!!.setPublicKey(message, messageOffset) + if (remoteEphemeral!!.isNullPublicKey()) { + // The remote ephemeral key is null, which means that it is + // not contributing anything to the security of the session + // and is in fact downgrading the security to "none at all" + // in some of the message patterns. Reject all such keys. + throw BadPaddingException("Null remote public key") + } + + // If the protocol is using pre-shared keys, then also mix + // the remote ephemeral key into the chaining key. + if (preSharedKey != null) symmetric!!.mixKey(message, messageOffset, len) + messageOffset += len + } + + Pattern.S -> { + // Decrypt and read the remote static key. + checkNotNull(remotePublicKey) { "Pattern definition error" } + len = remotePublicKey!!.getPublicKeyLength() + macLen = symmetric!!.getMACLength() + if (space < (len + macLen)) throw ShortBufferException() + val temp = ByteArray(len) + try { + if (symmetric!!.decryptAndHash( + message, + messageOffset, + temp, + 0, + len + macLen + ) != len + ) throw ShortBufferException() + remotePublicKey!!.setPublicKey(temp, 0) + } finally { + destroy(temp) + } + messageOffset += len + macLen + } + + Pattern.EE -> { + // DH operation with initiator and responder ephemeral keys. + mixDH(localEphemeral!!, remoteEphemeral!!) + } + + Pattern.ES -> { + // DH operation with initiator ephemeral and responder static keys. + if (isInitiator) mixDH(localEphemeral!!, remotePublicKey!!) + else mixDH(localKeyPair!!, remoteEphemeral!!) + } + + Pattern.SE -> { + // DH operation with initiator static and responder ephemeral keys. + if (isInitiator) mixDH(localKeyPair!!, remoteEphemeral!!) + else mixDH(localEphemeral!!, remotePublicKey!!) + } + + Pattern.SS -> { + // DH operation with initiator and responder static keys. + mixDH(localKeyPair!!, remotePublicKey!!) + } + + Pattern.F -> { + // Decrypt and read the remote hybrid ephemeral key. + checkNotNull(remoteHybrid) { "Pattern definition error" } + if (remoteHybrid is DHStateHybrid) { + // The DH object is something like New Hope. The public key + // length may need to change based on whether we already have + // generated a local hybrid keypair or not. + (remoteHybrid as DHStateHybrid).specifyPeer(localHybrid) + } + len = remoteHybrid!!.getPublicKeyLength() + macLen = symmetric!!.getMACLength() + if (space < (len + macLen)) throw ShortBufferException() + val temp = ByteArray(len) + try { + if (symmetric!!.decryptAndHash( + message, + messageOffset, + temp, + 0, + len + macLen + ) != len + ) throw ShortBufferException() + remoteHybrid!!.setPublicKey(temp, 0) + } finally { + destroy(temp) + } + messageOffset += len + macLen + } + + Pattern.FF -> { + // DH operation with initiator and responder hybrid keys. + mixDH(localHybrid!!, remoteHybrid!!) + } + + else -> { + // Unknown token code. Abort. + throw IllegalStateException( + "Unknown handshake token " + token.toInt().toString() + ) + } + } + } + + + // Decrypt the message payload. + val payloadLength = symmetric!!.decryptAndHash( + message, + messageOffset, + payload, + payloadOffset, + messageEnd - messageOffset + ) + success = true + return payloadLength + } finally { + // 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()) + action = FAILED + } + } + } + + /** + * Splits the transport encryption CipherState objects out of + * this HandshakeState object once the handshake completes. + * + * @return The pair of ciphers for sending and receiving. + * + * @throws IllegalStateException The action is not SPLIT. + */ + fun split(): CipherStatePair { + check(action == SPLIT) { "Handshake has not finished" } + val pair = symmetric!!.split() + if (!isInitiator) pair.swap() + action = COMPLETE + return pair + } + + /** + * Splits the transport encryption CipherState objects out of + * this HandshakeObject after mixing 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 IllegalStateException The action is not SPLIT. + * + * @throws IllegalArgumentException The length is not 0 or 32. + */ + fun split(secondaryKey: ByteArray?, offset: Int, length: Int): CipherStatePair { + check(action == SPLIT) { "Handshake has not finished" } + val pair = symmetric!!.split(secondaryKey, offset, length) + if (!isInitiator) { + // Swap the sender and receiver objects for the responder + // to make it easier on the application to know which is which. + pair.swap() + } + action = COMPLETE + return pair + } + + val handshakeHash: ByteArray? + /** + * Gets the current value of the handshake hash. + * + * @return The handshake hash. This must not be modified by the caller. + * + * @throws IllegalStateException The action is not SPLIT or COMPLETE. + */ + get() { + check(!(action != SPLIT && action != COMPLETE)) { "Handshake has not completed" } + return symmetric!!.getHandshakeHash() + } + + override fun destroy() { + if (symmetric != null) symmetric!!.destroy() + if (localKeyPair != null) localKeyPair!!.destroy() + if (localEphemeral != null) localEphemeral!!.destroy() + if (localHybrid != null) localHybrid!!.destroy() + if (remotePublicKey != null) remotePublicKey!!.destroy() + if (remoteEphemeral != null) remoteEphemeral!!.destroy() + if (remoteHybrid != null) remoteHybrid!!.destroy() + if (fixedEphemeral != null) fixedEphemeral!!.destroy() + if (fixedHybrid != null) fixedHybrid!!.destroy() + if (preSharedKey != null) Noise.destroy(preSharedKey!!) + if (prologue != null) Noise.destroy(prologue!!) + } + + companion object { + private const val TAG = "AndroidHandshake" + + /** + * Enumerated value that indicates that the handshake object + * is handling the initiator role. + */ + const val INITIATOR: Int = 1 + + /** + * Enumerated value that indicates that the handshake object + * is handling the responder role. + */ + const val RESPONDER: Int = 2 + + /** + * No action is required of the application yet because the + * handshake has not started. + */ + const val NO_ACTION: Int = 0 + + /** + * The HandshakeState expects the application to write the + * next message payload for the handshake. + */ + const val WRITE_MESSAGE: Int = 1 + + /** + * The HandshakeState expects the application to read the + * next message payload from the handshake. + */ + const val READ_MESSAGE: Int = 2 + + /** + * The handshake has failed due to some kind of error. + */ + const val FAILED: Int = 3 + + /** + * The handshake is over and the application is expected to call + * split() and begin data session communications. + */ + const val SPLIT: Int = 4 + + /** + * The handshake is complete and the data session ciphers + * have been split() out successfully. + */ + const val COMPLETE: Int = 5 + + /** + * Local static keypair is required for the handshake. + */ + private const val LOCAL_REQUIRED = 0x01 + + /** + * Remote static keypai is required for the handshake. + */ + private const val REMOTE_REQUIRED = 0x02 + + /** + * Pre-shared key is required for the handshake. + */ + private const val PSK_REQUIRED = 0x04 + + /** + * Ephemeral key for fallback pre-message has been provided. + */ + private const val FALLBACK_PREMSG = 0x08 + + /** + * The local public key is part of the pre-message. + */ + private const val LOCAL_PREMSG = 0x10 + + /** + * The remote public key is part of the pre-message. + */ + private const val REMOTE_PREMSG = 0x20 + + /** + * Fallback is possible from this pattern (two-way, ends in "K"). + */ + private const val FALLBACK_POSSIBLE = 0x40 + + // Empty value for when the prologue is not supplied. + private val emptyPrologue = ByteArray(0) + + /** + * Converts a byte array to hex string for logging (matching iOS hex format) + */ + private fun bytesToHex(bytes: ByteArray): String { + val sb = StringBuilder() + for (b in bytes) { + sb.append(String.format("%02x", b)) + } + return sb.toString() + } + + /** + * Computes the requirements for a handshake. + * + * @param flags The flags from the handshake's pattern. + * @param prefix The prefix from the protocol name; typically + * "Noise" or "NoisePSK". + * @param role The role, HandshakeState.INITIATOR or HandshakeState.RESPONDER. + * @param isFallback Set to true if we need the requirements for a + * fallback pattern; false for a regular pattern. + * + * @return The set of requirements for the handshake. + */ + private fun computeRequirements( + flags: Short, + prefix: String, + role: Int, + isFallback: Boolean + ): Int { + var requirements = 0 + if ((flags.toInt() and Pattern.FLAG_LOCAL_STATIC.toInt()) != 0) { + requirements = requirements or LOCAL_REQUIRED + } + if ((flags.toInt() and Pattern.FLAG_LOCAL_REQUIRED.toInt()) != 0) { + requirements = requirements or LOCAL_REQUIRED + requirements = requirements or LOCAL_PREMSG + } + if ((flags.toInt() and Pattern.FLAG_REMOTE_REQUIRED.toInt()) != 0) { + requirements = requirements or REMOTE_REQUIRED + requirements = requirements or REMOTE_PREMSG + } + if ((flags.toInt() and (Pattern.FLAG_REMOTE_EPHEM_REQ.toInt() or + Pattern.FLAG_LOCAL_EPHEM_REQ.toInt())) != 0 + ) { + if (isFallback) requirements = requirements or FALLBACK_PREMSG + } + if (prefix == "NoisePSK") { + requirements = requirements or PSK_REQUIRED + } + return requirements + } + } +} diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/NewHopeDHState.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/NewHopeDHState.java deleted file mode 100644 index bb3c275f..00000000 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/NewHopeDHState.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (C) 2016 Southern Storm Software, Pty Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * 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; - -import java.util.Arrays; - -import com.bitchat.android.noise.southernstorm.crypto.NewHope; -import com.bitchat.android.noise.southernstorm.crypto.NewHopeTor; - -/** - * Implementation of the New Hope post-quantum algorithm for the Noise protocol. - */ -final class NewHopeDHState implements DHStateHybrid { - - enum KeyType - { - None, - AlicePrivate, - AlicePublic, - BobPrivate, - BobPublic, - BobCalculated; - } - - private NewHopeTor nh; - private byte[] publicKey; - private byte[] privateKey; - private KeyType keyType; - - /** - * Special version of NewHopeTor that allows explicit random data - * to be specified for test vectors. - */ - private class NewHopeWithPrivateKey extends NewHopeTor { - - byte[] randomData; - - public NewHopeWithPrivateKey(byte[] randomData) - { - this.randomData = randomData; - } - - @Override - protected void randombytes(byte[] buffer) - { - System.arraycopy(randomData, 0, buffer, 0, buffer.length); - } - } - - /** - * Constructs a new key exchange object for New Hope. - */ - public NewHopeDHState() { - nh = null; - publicKey = null; - privateKey = null; - keyType = KeyType.None; - } - - private boolean isAlice() { - return keyType == KeyType.AlicePrivate || keyType == KeyType.AlicePublic; - } - - @Override - public void destroy() { - clearKey(); - } - - @Override - public String getDHName() { - return "NewHope"; - } - - @Override - public int getPublicKeyLength() { - if (isAlice()) - return NewHope.SENDABYTES; - else - return NewHope.SENDBBYTES; - } - - @Override - public int getPrivateKeyLength() { - // New Hope doesn't have private keys in the same sense as - // Curve25519 and Curve448. Instead return the number of - // random bytes that we need to generate each key type. - if (isAlice()) - return 64; - else - return 32; - } - - @Override - public int getSharedKeyLength() { - return NewHope.SHAREDBYTES; - } - - @Override - public void generateKeyPair() { - clearKey(); - keyType = KeyType.AlicePrivate; - nh = new NewHopeTor(); - publicKey = new byte [NewHope.SENDABYTES]; - nh.keygen(publicKey, 0); - } - - @Override - public void generateKeyPair(DHState remote) { - if (remote == null) { - // No remote public key, so always generate in Alice mode. - generateKeyPair(); - return; - } else if (!(remote instanceof NewHopeDHState)) { - throw new IllegalStateException("Mismatched DH objects"); - } - NewHopeDHState r = (NewHopeDHState)remote; - if (r.isAlice() && r.publicKey != null) { - // We have a remote public key for Alice, so generate in Bob mode. - clearKey(); - keyType = KeyType.BobCalculated; - nh = new NewHopeTor(); - publicKey = new byte [NewHope.SENDBBYTES]; - privateKey = new byte [NewHope.SHAREDBYTES]; - nh.sharedb(privateKey, 0, publicKey, 0, r.publicKey, 0); - } else { - generateKeyPair(); - } - } - - @Override - public void getPublicKey(byte[] key, int offset) { - if (publicKey != null) - System.arraycopy(publicKey, 0, key, offset, getPublicKeyLength()); - else - Arrays.fill(key, 0, getPublicKeyLength(), (byte)0); - } - - @Override - public void setPublicKey(byte[] key, int offset) { - if (publicKey != null) - Noise.destroy(publicKey); - publicKey = new byte [getPublicKeyLength()]; - System.arraycopy(key, 0, publicKey, 0, publicKey.length); - } - - @Override - public void getPrivateKey(byte[] key, int offset) { - if (privateKey != null) - System.arraycopy(privateKey, 0, key, offset, getPrivateKeyLength()); - else - Arrays.fill(key, 0, getPrivateKeyLength(), (byte)0); - } - - @Override - public void setPrivateKey(byte[] key, int offset) { - clearKey(); - // Guess the key type from the length of the test data. - if (offset == 0 && key.length == 64) - keyType = KeyType.AlicePrivate; - else - keyType = KeyType.BobPrivate; - privateKey = new byte [getPrivateKeyLength()]; - System.arraycopy(key, 0, privateKey, 0, privateKey.length); - } - - @Override - public void setToNullPublicKey() { - // Null public keys are not supported by New Hope. - // Destroy the current values but otherwise ignore. - clearKey(); - } - - @Override - public void clearKey() { - if (nh != null) { - nh.destroy(); - nh = null; - } - if (publicKey != null) { - Noise.destroy(publicKey); - publicKey = null; - } - if (privateKey != null) { - Noise.destroy(privateKey); - privateKey = null; - } - keyType = KeyType.None; - } - - @Override - public boolean hasPublicKey() { - return publicKey != null; - } - - @Override - public boolean hasPrivateKey() { - return privateKey != null; - } - - @Override - public boolean isNullPublicKey() { - return false; - } - - @Override - public void calculate(byte[] sharedKey, int offset, DHState publicDH) { - if (!(publicDH instanceof NewHopeDHState)) - throw new IllegalArgumentException("Incompatible DH algorithms"); - NewHopeDHState other = (NewHopeDHState)publicDH; - if (keyType == KeyType.AlicePrivate) { - // Compute the shared key for Alice. - nh.shareda(sharedKey, 0, other.publicKey, 0); - } else if (keyType == KeyType.BobCalculated) { - // The shared key for Bob was already computed when the key was generated. - System.arraycopy(privateKey, 0, sharedKey, 0, NewHope.SHAREDBYTES); - } else { - throw new IllegalStateException("Cannot calculate with this DH object"); - } - } - - @Override - public void copyFrom(DHState other) { - if (!(other instanceof NewHopeDHState)) - throw new IllegalStateException("Mismatched DH key objects"); - if (other == this) - return; - NewHopeDHState dh = (NewHopeDHState)other; - clearKey(); - switch (dh.keyType) { - case None: - break; - - case AlicePrivate: - if (dh.privateKey != null) { - keyType = KeyType.AlicePrivate; - privateKey = new byte [dh.privateKey.length]; - System.arraycopy(dh.privateKey, 0, privateKey, 0, privateKey.length); - } else { - throw new IllegalStateException("Cannot copy generated key for Alice"); - } - break; - - case BobPrivate: - case BobCalculated: - throw new IllegalStateException("Cannot copy private key for Bob without public key for Alice"); - - case AlicePublic: - case BobPublic: - keyType = dh.keyType; - publicKey = new byte [dh.publicKey.length]; - System.arraycopy(dh.publicKey, 0, publicKey, 0, publicKey.length); - break; - } - } - - @Override - public void copyFrom(DHState other, DHState remote) { - if (remote == null) { - copyFrom(other); - return; - } - if (!(other instanceof NewHopeDHState) || !(remote instanceof NewHopeDHState)) - throw new IllegalStateException("Mismatched DH key objects"); - if (other == this) - return; - NewHopeDHState dh = (NewHopeDHState)other; - NewHopeDHState remotedh = (NewHopeDHState)remote; - clearKey(); - switch (dh.keyType) { - case None: - break; - - case AlicePrivate: - if (dh.privateKey != null) { - // Generate Alice's public and private key now. - keyType = KeyType.AlicePrivate; - nh = new NewHopeWithPrivateKey(dh.privateKey); - publicKey = new byte [NewHope.SENDABYTES]; - nh.keygen(publicKey, 0); - } else { - throw new IllegalStateException("Cannot copy generated key for Alice"); - } - break; - - case BobPrivate: - if (dh.privateKey != null && remotedh.keyType == KeyType.AlicePublic) { - // Now we know the public key for Alice, we can calculate Bob's public and shared keys. - keyType = KeyType.BobCalculated; - nh = new NewHopeWithPrivateKey(dh.privateKey); - publicKey = new byte [NewHope.SENDBBYTES]; - privateKey = new byte [NewHope.SHAREDBYTES]; - nh.sharedb(privateKey, 0, publicKey, 0, remotedh.publicKey, 0); - } else { - throw new IllegalStateException("Cannot copy private key for Bob without public key for Alice"); - } - break; - - case BobCalculated: - throw new IllegalStateException("Cannot copy generated key for Bob"); - - case AlicePublic: - case BobPublic: - keyType = dh.keyType; - publicKey = new byte [dh.publicKey.length]; - System.arraycopy(dh.publicKey, 0, publicKey, 0, publicKey.length); - break; - } - } - - @Override - public void specifyPeer(DHState local) { - if (!(local instanceof NewHopeDHState)) - return; - clearKey(); - if (((NewHopeDHState)local).keyType == KeyType.AlicePrivate) - keyType = KeyType.BobPublic; - else - keyType = KeyType.AlicePublic; - } -} 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 new file mode 100644 index 00000000..1225504f --- /dev/null +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/NewHopeDHState.kt @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2016 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * 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 + +import com.bitchat.android.noise.southernstorm.crypto.NewHope +import com.bitchat.android.noise.southernstorm.crypto.NewHopeTor +import java.util.Arrays + +/** + * Implementation of the New Hope post-quantum algorithm for the Noise protocol. + */ +internal class NewHopeDHState : DHStateHybrid { + internal enum class KeyType { + None, + AlicePrivate, + AlicePublic, + BobPrivate, + BobPublic, + BobCalculated + } + + private var nh: NewHopeTor? = null + private var publicKey: ByteArray? = null + private var privateKey: ByteArray? = null + private var keyType: KeyType + + /** + * Special version of NewHopeTor that allows explicit random data + * to be specified for test vectors. + */ + private inner class NewHopeWithPrivateKey(var randomData: ByteArray) : NewHopeTor() { + override fun randombytes(buffer: ByteArray) { + System.arraycopy(randomData, 0, buffer, 0, buffer.size) + } + } + + /** + * Constructs a new key exchange object for New Hope. + */ + init { + keyType = KeyType.None + } + + private val isAlice: Boolean + get() = keyType == KeyType.AlicePrivate || keyType == KeyType.AlicePublic + + override fun destroy() { + clearKey() + } + + override fun getDHName(): String { + return "NewHope" + } + + override fun getPublicKeyLength(): Int { + if (this.isAlice) return NewHope.SENDABYTES + else return NewHope.SENDBBYTES + } + + override fun getPrivateKeyLength(): Int { + // New Hope doesn't have private keys in the same sense as + // Curve25519 and Curve448. Instead return the number of + // random bytes that we need to generate each key type. + if (this.isAlice) return 64 + else return 32 + } + + override fun getSharedKeyLength(): Int { + return NewHope.SHAREDBYTES + } + + override fun generateKeyPair() { + clearKey() + keyType = KeyType.AlicePrivate + nh = NewHopeTor() + publicKey = ByteArray(NewHope.SENDABYTES) + nh!!.keygen(publicKey!!, 0) + } + + override fun generateKeyPair(remote: DHState?) { + if (remote == null) { + // No remote public key, so always generate in Alice mode. + generateKeyPair() + return + } else check(remote is NewHopeDHState) { "Mismatched DH objects" } + val r = remote + if (r.isAlice && r.publicKey != null) { + // We have a remote public key for Alice, so generate in Bob mode. + clearKey() + keyType = KeyType.BobCalculated + nh = NewHopeTor() + publicKey = ByteArray(NewHope.SENDBBYTES) + privateKey = ByteArray(NewHope.SHAREDBYTES) + nh!!.sharedb(privateKey!!, 0, publicKey!!, 0, r.publicKey!!, 0) + } else { + generateKeyPair() + } + } + + override fun getPublicKey(key: ByteArray, offset: Int) { + if (publicKey != null) System.arraycopy(publicKey, 0, key, offset, getPublicKeyLength()) + else Arrays.fill(key, 0, getPublicKeyLength(), 0.toByte()) + } + + override fun setPublicKey(key: ByteArray, offset: Int) { + if (publicKey != null) Noise.destroy(publicKey!!) + publicKey = ByteArray(getPublicKeyLength()) + System.arraycopy(key, 0, publicKey, 0, publicKey!!.size) + } + + override fun getPrivateKey(key: ByteArray, offset: Int) { + if (privateKey != null) System.arraycopy(privateKey, 0, key, offset, getPrivateKeyLength()) + else Arrays.fill(key, 0, getPrivateKeyLength(), 0.toByte()) + } + + override fun setPrivateKey(key: ByteArray, offset: Int) { + clearKey() + // Guess the key type from the length of the test data. + if (offset == 0 && key.size == 64) keyType = KeyType.AlicePrivate + else keyType = KeyType.BobPrivate + privateKey = ByteArray(getPrivateKeyLength()) + System.arraycopy(key, 0, privateKey, 0, privateKey!!.size) + } + + override fun setToNullPublicKey() { + // Null public keys are not supported by New Hope. + // Destroy the current values but otherwise ignore. + clearKey() + } + + override fun clearKey() { + if (nh != null) { + nh!!.destroy() + nh = null + } + if (publicKey != null) { + Noise.destroy(publicKey!!) + publicKey = null + } + if (privateKey != null) { + Noise.destroy(privateKey!!) + privateKey = null + } + keyType = KeyType.None + } + + override fun hasPublicKey(): Boolean { + return publicKey != null + } + + override fun hasPrivateKey(): Boolean { + return privateKey != null + } + + override fun isNullPublicKey(): Boolean { + return false + } + + override fun calculate(sharedKey: ByteArray, offset: Int, publicDH: DHState?) { + require(publicDH is NewHopeDHState) { "Incompatible DH algorithms" } + val other = publicDH + if (keyType == KeyType.AlicePrivate) { + // Compute the shared key for Alice. + nh!!.shareda(sharedKey, 0, other.publicKey!!, 0) + } else if (keyType == KeyType.BobCalculated) { + // The shared key for Bob was already computed when the key was generated. + System.arraycopy(privateKey, 0, sharedKey, 0, NewHope.SHAREDBYTES) + } else { + throw IllegalStateException("Cannot calculate with this DH object") + } + } + + override fun copyFrom(other: DHState?) { + check(other is NewHopeDHState) { "Mismatched DH key objects" } + if (other === this) return + val dh = other + clearKey() + when (dh.keyType) { + KeyType.None -> {} + KeyType.AlicePrivate -> if (dh.privateKey != null) { + keyType = KeyType.AlicePrivate + privateKey = ByteArray(dh.privateKey!!.size) + System.arraycopy(dh.privateKey, 0, privateKey, 0, privateKey!!.size) + } else { + throw IllegalStateException("Cannot copy generated key for Alice") + } + + KeyType.BobPrivate, KeyType.BobCalculated -> throw IllegalStateException("Cannot copy private key for Bob without public key for Alice") + + KeyType.AlicePublic, KeyType.BobPublic -> { + keyType = dh.keyType + publicKey = ByteArray(dh.publicKey!!.size) + System.arraycopy(dh.publicKey, 0, publicKey, 0, publicKey!!.size) + } + } + } + + override fun copyFrom(other: DHState?, remote: DHState?) { + if (remote == null) { + copyFrom(other) + return + } + check(!(other !is NewHopeDHState || remote !is NewHopeDHState)) { "Mismatched DH key objects" } + if (other === this) return + val dh = other + val remotedh = remote + clearKey() + when (dh.keyType) { + KeyType.None -> {} + KeyType.AlicePrivate -> if (dh.privateKey != null) { + // Generate Alice's public and private key now. + keyType = KeyType.AlicePrivate + nh = NewHopeWithPrivateKey(dh.privateKey!!) + publicKey = ByteArray(NewHope.SENDABYTES) + nh!!.keygen(publicKey!!, 0) + } else { + throw IllegalStateException("Cannot copy generated key for Alice") + } + + KeyType.BobPrivate -> if (dh.privateKey != null && remotedh.keyType == KeyType.AlicePublic) { + // Now we know the public key for Alice, we can calculate Bob's public and shared keys. + keyType = KeyType.BobCalculated + nh = NewHopeWithPrivateKey(dh.privateKey!!) + publicKey = ByteArray(NewHope.SENDBBYTES) + privateKey = ByteArray(NewHope.SHAREDBYTES) + nh!!.sharedb(privateKey!!, 0, publicKey!!, 0, remotedh.publicKey!!, 0) + } else { + throw IllegalStateException("Cannot copy private key for Bob without public key for Alice") + } + + KeyType.BobCalculated -> throw IllegalStateException("Cannot copy generated key for Bob") + + KeyType.AlicePublic, KeyType.BobPublic -> { + keyType = dh.keyType + publicKey = ByteArray(dh.publicKey!!.size) + System.arraycopy(dh.publicKey, 0, publicKey, 0, publicKey!!.size) + } + } + } + + override fun specifyPeer(local: DHState?) { + if (local !is NewHopeDHState) return + clearKey() + if (local.keyType == KeyType.AlicePrivate) keyType = KeyType.BobPublic + else keyType = KeyType.AlicePublic + } +} 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 deleted file mode 100644 index d669baa3..00000000 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.java +++ /dev/null @@ -1,532 +0,0 @@ -/* - * Copyright (C) 2016 Southern Storm Software, Pty Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * 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; - -import android.util.Log; - -import java.io.UnsupportedEncodingException; -import java.security.DigestException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - -import javax.crypto.BadPaddingException; -import javax.crypto.ShortBufferException; - -/** - * Symmetric state for helping manage a Noise handshake. - */ -class SymmetricState implements Destroyable { - - private static final String TAG = "AndroidSymmetric"; - - private String name; - private CipherState cipher; - private MessageDigest hash; - private byte[] ck; - private byte[] h; - private byte[] prev_h; - - /** - * Converts a byte array to hex string for logging (matching iOS hex format) - */ - private static String bytesToHex(byte[] bytes) { - StringBuilder sb = new StringBuilder(); - for (byte b : bytes) { - sb.append(String.format("%02x", b)); - } - return sb.toString(); - } - - /** - * Constructs a new symmetric state object. - * - * @param protocolName The name of the Noise protocol, which is assumed to be valid. - * @param cipherName The name of the cipher within protocolName. - * @param hashName The name of the hash within protocolName. - * - * @throws NoSuchAlgorithmException The cipher or hash algorithm in the - * protocol name is not supported. - */ - public SymmetricState(String protocolName, String cipherName, String hashName) throws NoSuchAlgorithmException - { - name = protocolName; - cipher = Noise.createCipher(cipherName); - hash = Noise.createHash(hashName); - int hashLength = hash.getDigestLength(); - ck = new byte [hashLength]; - h = new byte [hashLength]; - prev_h = new byte [hashLength]; - - byte[] protocolNameBytes; - try { - protocolNameBytes = protocolName.getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - // If UTF-8 is not supported, then we are definitely in trouble! - throw new UnsupportedOperationException("UTF-8 encoding is not supported"); - } - - if (protocolNameBytes.length <= hashLength) { - System.arraycopy(protocolNameBytes, 0, h, 0, protocolNameBytes.length); - Arrays.fill(h, protocolNameBytes.length, h.length, (byte)0); - } else { - hashOne(protocolNameBytes, 0, protocolNameBytes.length, h, 0, h.length); - } - - System.arraycopy(h, 0, ck, 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): " + bytesToHex(h)); - Log.d(TAG, "Initial chaining key (ck): " + bytesToHex(ck)); - Log.d(TAG, "Hash length: " + h.length); - Log.d(TAG, "========================================="); - } - - /** - * Gets the name of the Noise protocol. - * - * @return The protocol name. - */ - public String getProtocolName() - { - return name; - } - - /** - * Gets the length of MAC values in the current state. - * - * @return The length of the MAC value for the underlying cipher - * or zero if the cipher has not yet been initialized with a key. - */ - public int getMACLength() - { - return cipher.mACLength; - } - - /** - * Mixes data into the chaining key. - * - * @param data The buffer containing the data to mix in. - * @param offset The offset of the first data byte to mix in. - * @param length The number of bytes to mix in. - */ - public void mixKey(byte[] data, int offset, int length) - { - // LOGGING: Before mixKey operation (matching iOS) - byte[] inputData = new byte[length]; - System.arraycopy(data, offset, inputData, 0, length); - Log.d(TAG, "*** Android mixKey() BEFORE ***"); - Log.d(TAG, "Input data (" + length + " bytes): " + bytesToHex(inputData)); - Log.d(TAG, "Current CK: " + bytesToHex(ck)); - Log.d(TAG, "Current Hash: " + bytesToHex(h)); - - 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); - cipher.initializeKey(tempKey, 0); - } finally { - Noise.destroy(tempKey); - } - - // LOGGING: After mixKey operation (matching iOS) - 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.mACLength > 0)); - } - - /** - * Mixes data into the handshake hash. - * - * @param data The buffer containing the data to mix in. - * @param offset The offset of the first data byte to mix in. - * @param length The number of bytes to mix in. - */ - public void mixHash(byte[] data, int offset, int length) - { - // LOGGING: Before mixHash operation (matching iOS) - byte[] inputData = new byte[length]; - System.arraycopy(data, offset, inputData, 0, length); - Log.d(TAG, "*** Android mixHash() BEFORE ***"); - Log.d(TAG, "Input data (" + length + " bytes): " + bytesToHex(inputData)); - Log.d(TAG, "Current Hash: " + bytesToHex(h)); - - hashTwo(h, 0, h.length, data, offset, length, h, 0, h.length); - - // LOGGING: After mixHash operation (matching iOS) - Log.d(TAG, "*** Android mixHash() AFTER ***"); - Log.d(TAG, "New Hash: " + bytesToHex(h)); - } - - /** - * Mixes a pre-shared key into the chaining key and handshake hash. - * - * @param key The pre-shared key value. - */ - public void mixPreSharedKey(byte[] key) - { - byte[] temp = new byte [hash.getDigestLength()]; - try { - hkdf(ck, 0, ck.length, key, 0, key.length, ck, 0, ck.length, temp, 0, temp.length); - mixHash(temp, 0, temp.length); - } finally { - Noise.destroy(temp); - } - } - - /** - * Mixes a pre-supplied public key into the handshake hash. - * - * @param dh The object containing the public key. - */ - public void mixPublicKey(DHState dh) - { - byte[] temp = new byte [dh.getPublicKeyLength()]; - try { - dh.getPublicKey(temp, 0); - mixHash(temp, 0, temp.length); - } finally { - Noise.destroy(temp); - } - } - - /** - * Mixes a pre-supplied public key into the chaining key. - * - * @param dh The object containing the public key. - */ - public void mixPublicKeyIntoCK(DHState dh) - { - byte[] temp = new byte [dh.getPublicKeyLength()]; - try { - dh.getPublicKey(temp, 0); - mixKey(temp, 0, temp.length); - } finally { - Noise.destroy(temp); - } - } - - /** - * Encrypts a block of plaintext and mixes the ciphertext into the handshake hash. - * - * @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. - * - * @throws ShortBufferException There is not enough space in the - * ciphertext buffer for the encrypted data plus MAC value. - * - * 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. - */ - public int encryptAndHash(byte[] plaintext, int plaintextOffset, byte[] ciphertext, int ciphertextOffset, int length) throws ShortBufferException - { - int ciphertextLength = cipher.encryptWithAd(h, plaintext, plaintextOffset, ciphertext, ciphertextOffset, length); - mixHash(ciphertext, ciphertextOffset, ciphertextLength); - return ciphertextLength; - } - - /** - * Decrypts a block of ciphertext and mixes it into the handshake hash. - * - * @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 There is not enough space in the plaintext - * buffer for the decrypted data. - * - * @throws BadPaddingException The MAC value failed to verify. - * - * The plaintext and ciphertext buffers can be the same for in-place - * decryption. In that case, ciphertextOffset must be identical to - * plaintextOffset. - */ - public int decryptAndHash(byte[] ciphertext, int ciphertextOffset, byte[] plaintext, int plaintextOffset, int length) throws ShortBufferException, BadPaddingException - { - System.arraycopy(h, 0, prev_h, 0, h.length); - mixHash(ciphertext, ciphertextOffset, length); - return cipher.decryptWithAd(prev_h, ciphertext, ciphertextOffset, plaintext, plaintextOffset, length); - } - - /** - * Splits the symmetric state into two ciphers for session encryption. - * - * @return The pair of ciphers for sending and receiving. - */ - public CipherStatePair split() - { - return split(new byte[0], 0, 0); - } - - /** - * 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. - */ - public CipherStatePair split(byte[] secondaryKey, int offset, int length) - { - if (length != 0 && length != 32) - throw new IllegalArgumentException("Secondary keys must be 0 or 32 bytes in length"); - int keyLength = cipher.keyLength; - byte[] k1 = new byte [keyLength]; - byte[] k2 = new byte [keyLength]; - try { - hkdf(ck, 0, ck.length, secondaryKey, offset, length, k1, 0, k1.length, k2, 0, k2.length); - CipherState c1 = null; - CipherState c2 = null; - CipherStatePair pair = null; - try { - c1 = cipher.fork(k1, 0); - c2 = cipher.fork(k2, 0); - pair = new CipherStatePair(c1, c2); - } finally { - 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(); - pair = null; - } - } - return pair; - } finally { - Noise.destroy(k1); - Noise.destroy(k2); - } - } - - /** - * Gets the current value of the handshake hash. - * - * @return The handshake hash. This must not be modified by the caller. - * - * The handshake hash value is only of use to the application after - * split() has been called. - */ - public byte[] getHandshakeHash() - { - return h; - } - - @Override - public void destroy() { - if (cipher != null) { - cipher.destroy(); - cipher = null; - } - if (hash != null) { - // The built-in fallback implementations are destroyable. - // JCA/JCE implementations aren't, so try reset() instead. - if (hash instanceof Destroyable) - ((Destroyable)hash).destroy(); - else - hash.reset(); - hash = null; - } - if (ck != null) { - Noise.destroy(ck); - ck = null; - } - if (h != null) { - Noise.destroy(h); - h = null; - } - if (prev_h != null) { - Noise.destroy(prev_h); - prev_h = null; - } - } - - /** - * Hashes a single data buffer. - * - * @param data The buffer containing the data to hash. - * @param offset Offset into the data buffer of the first byte to hash. - * @param length Length of the data to be hashed. - * @param output The buffer to receive the output hash value. - * @param outputOffset Offset into the output buffer to place the hash value. - * @param outputLength The length of the hash output. - * - * The output buffer can be the same as the input data buffer. - */ - private void hashOne(byte[] data, int offset, int length, byte[] output, int outputOffset, int outputLength) - { - hash.reset(); - hash.update(data, offset, length); - try { - hash.digest(output, outputOffset, outputLength); - } catch (DigestException e) { - Arrays.fill(output, outputOffset, outputLength, (byte)0); - } - } - - /** - * Hashes two data buffers. - * - * @param data1 The buffer containing the first data to hash. - * @param offset1 Offset into the first data buffer of the first byte to hash. - * @param length1 Length of the first data to be hashed. - * @param data2 The buffer containing the second data to hash. - * @param offset2 Offset into the second data buffer of the first byte to hash. - * @param length2 Length of the second data to be hashed. - * @param output The buffer to receive the output hash value. - * @param outputOffset Offset into the output buffer to place the hash value. - * @param outputLength The length of the hash output. - * - * The output buffer can be same as either of the input buffers. - */ - private void hashTwo(byte[] data1, int offset1, int length1, - byte[] data2, int offset2, int length2, - byte[] output, int outputOffset, int outputLength) - { - hash.reset(); - hash.update(data1, offset1, length1); - hash.update(data2, offset2, length2); - try { - hash.digest(output, outputOffset, outputLength); - } catch (DigestException e) { - Arrays.fill(output, outputOffset, outputLength, (byte)0); - } - } - - /** - * Computes a HMAC value using key and data values. - * - * @param key The buffer that contains the key. - * @param keyOffset The offset of the key in the key buffer. - * @param keyLength The length of the key in bytes. - * @param data The buffer that contains the data. - * @param dataOffset The offset of the data in the data buffer. - * @param dataLength The length of the data in bytes. - * @param output The output buffer to place the HMAC value in. - * @param outputOffset Offset into the output buffer for the HMAC value. - * @param outputLength The length of the HMAC output. - */ - private void hmac(byte[] key, int keyOffset, int keyLength, - byte[] data, int dataOffset, int dataLength, - byte[] output, int outputOffset, int outputLength) - { - // In all of the algorithms of interest to us, the block length - // is twice the size of the hash length. - int hashLength = hash.getDigestLength(); - int blockLength = hashLength * 2; - byte[] block = new byte [blockLength]; - int index; - try { - if (keyLength <= blockLength) { - System.arraycopy(key, keyOffset, block, 0, keyLength); - Arrays.fill(block, keyLength, blockLength, (byte)0); - } else { - hash.reset(); - hash.update(key, keyOffset, keyLength); - hash.digest(block, 0, hashLength); - Arrays.fill(block, hashLength, blockLength, (byte)0); - } - for (index = 0; index < blockLength; ++index) - block[index] ^= (byte)0x36; - hash.reset(); - hash.update(block, 0, blockLength); - hash.update(data, dataOffset, dataLength); - hash.digest(output, outputOffset, hashLength); - for (index = 0; index < blockLength; ++index) - block[index] ^= (byte)(0x36 ^ 0x5C); - hash.reset(); - hash.update(block, 0, blockLength); - hash.update(output, outputOffset, hashLength); - hash.digest(output, outputOffset, outputLength); - } catch (DigestException e) { - Arrays.fill(output, outputOffset, outputLength, (byte)0); - } finally { - Noise.destroy(block); - } - } - - /** - * Computes a HKDF value. - * - * @param key The buffer that contains the key. - * @param keyOffset The offset of the key in the key buffer. - * @param keyLength The length of the key in bytes. - * @param data The buffer that contains the data. - * @param dataOffset The offset of the data in the data buffer. - * @param dataLength The length of the data in bytes. - * @param output1 The first output buffer. - * @param output1Offset Offset into the first output buffer. - * @param output1Length Length of the first output which can be - * less than the hash length. - * @param output2 The second output buffer. - * @param output2Offset Offset into the second output buffer. - * @param output2Length Length of the second output which can be - * less than the hash length. - */ - private void hkdf(byte[] key, int keyOffset, int keyLength, - byte[] data, int dataOffset, int dataLength, - byte[] output1, int output1Offset, int output1Length, - byte[] output2, int output2Offset, int output2Length) - { - int hashLength = hash.getDigestLength(); - byte[] tempKey = new byte [hashLength]; - byte[] tempHash = new byte [hashLength + 1]; - try { - hmac(key, keyOffset, keyLength, data, dataOffset, dataLength, tempKey, 0, hashLength); - tempHash[0] = (byte)0x01; - hmac(tempKey, 0, hashLength, tempHash, 0, 1, tempHash, 0, hashLength); - System.arraycopy(tempHash, 0, output1, output1Offset, output1Length); - tempHash[hashLength] = (byte)0x02; - hmac(tempKey, 0, hashLength, tempHash, 0, hashLength + 1, tempHash, 0, hashLength); - System.arraycopy(tempHash, 0, output2, output2Offset, output2Length); - } finally { - Noise.destroy(tempKey); - Noise.destroy(tempHash); - } - } -} 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 new file mode 100644 index 00000000..c1328047 --- /dev/null +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.kt @@ -0,0 +1,601 @@ +/* + * Copyright (C) 2016 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * 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 + +import android.util.Log +import com.bitchat.android.noise.southernstorm.protocol.Noise.createCipher +import com.bitchat.android.noise.southernstorm.protocol.Noise.createHash +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 + +/** + * Symmetric state for helping manage a Noise handshake. + */ +internal class SymmetricState(protocolName: String, cipherName: String, hashName: String) : + Destroyable { + /** + * Gets the name of the Noise protocol. + * + * @return The protocol name. + */ + val protocolName: String? + private var cipher: CipherState? + private var hash: MessageDigest? + private var ck: ByteArray? + + /** + * Gets the current value of the handshake hash. + * + * @return The handshake hash. This must not be modified by the caller. + * + * The handshake hash value is only of use to the application after + * split() has been called. + */ + var handshakeHash: ByteArray? + private set + private var prev_h: ByteArray? + + /** + * Constructs a new symmetric state object. + * + * @param protocolName The name of the Noise protocol, which is assumed to be valid. + * @param cipherName The name of the cipher within protocolName. + * @param hashName The name of the hash within protocolName. + * + * @throws NoSuchAlgorithmException The cipher or hash algorithm in the + * protocol name is not supported. + */ + init { + this.protocolName = protocolName + cipher = createCipher(cipherName) + hash = createHash(hashName) + val hashLength = hash!!.getDigestLength() + ck = ByteArray(hashLength) + this.handshakeHash = ByteArray(hashLength) + prev_h = ByteArray(hashLength) + + val protocolNameBytes: ByteArray + try { + protocolNameBytes = protocolName.toByteArray(charset("UTF-8")) + } catch (e: 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() + ) + } else { + hashOne( + protocolNameBytes, 0, protocolNameBytes.size, + this.handshakeHash!!, 0, handshakeHash!!.size + ) + } + + System.arraycopy(this.handshakeHash, 0, ck, 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, "=========================================") + } + + val mACLength: Int + /** + * Gets the length of MAC values in the current state. + * + * @return The length of the MAC value for the underlying cipher + * or zero if the cipher has not yet been initialized with a key. + */ + get() = cipher.mACLength + + /** + * Mixes data into the chaining key. + * + * @param data The buffer containing the data to mix in. + * @param offset The offset of the first data byte to mix in. + * @param length The number of bytes to mix in. + */ + 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) + 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!! + ) + ) + + val keyLength = cipher!!.keyLength + val tempKey = ByteArray(keyLength) + try { + hkdf( + ck!!, + 0, + ck!!.size, + data, + offset, + length, + ck!!, + 0, + ck!!.size, + tempKey, + 0, + keyLength + ) + cipher!!.initializeKey(tempKey, 0) + } finally { + destroy(tempKey) + } + + + // 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, "Cipher now has key: " + (cipher.mACLength > 0)) + } + + /** + * Mixes data into the handshake hash. + * + * @param data The buffer containing the data to mix in. + * @param offset The offset of the first data byte to mix in. + * @param length The number of bytes to mix in. + */ + 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) + Log.d(TAG, "*** Android mixHash() BEFORE ***") + Log.d(TAG, "Input data (" + length + " bytes): " + bytesToHex(inputData)) + Log.d( + TAG, "Current Hash: " + Companion.bytesToHex( + this.handshakeHash!! + ) + ) + + hashTwo( + this.handshakeHash!!, 0, handshakeHash!!.size, data, offset, length, + this.handshakeHash!!, 0, handshakeHash!!.size + ) + + + // LOGGING: After mixHash operation (matching iOS) + Log.d(TAG, "*** Android mixHash() AFTER ***") + Log.d( + TAG, "New Hash: " + Companion.bytesToHex( + this.handshakeHash!! + ) + ) + } + + /** + * Mixes a pre-shared key into the chaining key and handshake hash. + * + * @param key The pre-shared key value. + */ + fun mixPreSharedKey(key: ByteArray) { + val temp = ByteArray(hash!!.getDigestLength()) + try { + hkdf(ck!!, 0, ck!!.size, key, 0, key.size, ck!!, 0, ck!!.size, temp, 0, temp.size) + mixHash(temp, 0, temp.size) + } finally { + destroy(temp) + } + } + + /** + * Mixes a pre-supplied public key into the handshake hash. + * + * @param dh The object containing the public key. + */ + fun mixPublicKey(dh: DHState) { + val temp = ByteArray(dh.getPublicKeyLength()) + try { + dh.getPublicKey(temp, 0) + mixHash(temp, 0, temp.size) + } finally { + destroy(temp) + } + } + + /** + * Mixes a pre-supplied public key into the chaining key. + * + * @param dh The object containing the public key. + */ + fun mixPublicKeyIntoCK(dh: DHState) { + val temp = ByteArray(dh.getPublicKeyLength()) + try { + dh.getPublicKey(temp, 0) + mixKey(temp, 0, temp.size) + } finally { + destroy(temp) + } + } + + /** + * Encrypts a block of plaintext and mixes the ciphertext into the handshake hash. + * + * @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. + * + * @throws ShortBufferException There is not enough space in the + * ciphertext buffer for the encrypted data plus MAC value. + * + * 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 encryptAndHash( + plaintext: ByteArray, + plaintextOffset: Int, + ciphertext: ByteArray, + ciphertextOffset: Int, + length: Int + ): Int { + val ciphertextLength = cipher!!.encryptWithAd( + this.handshakeHash, + plaintext, + plaintextOffset, + ciphertext, + ciphertextOffset, + length + ) + mixHash(ciphertext, ciphertextOffset, ciphertextLength) + return ciphertextLength + } + + /** + * Decrypts a block of ciphertext and mixes it into the handshake hash. + * + * @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 There is not enough space in the plaintext + * buffer for the decrypted data. + * + * @throws BadPaddingException The MAC value failed to verify. + * + * 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 decryptAndHash( + ciphertext: ByteArray, + ciphertextOffset: Int, + plaintext: ByteArray, + plaintextOffset: Int, + length: Int + ): Int { + System.arraycopy(this.handshakeHash, 0, prev_h, 0, handshakeHash!!.size) + mixHash(ciphertext, ciphertextOffset, length) + return cipher!!.decryptWithAd( + prev_h, + ciphertext, + ciphertextOffset, + plaintext, + plaintextOffset, + length + ) + } + + /** + * 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), + offset: Int = 0, + length: Int = 0 + ): CipherStatePair { + require(!(length != 0 && length != 32)) { "Secondary keys must be 0 or 32 bytes in length" } + val keyLength = cipher!!.keyLength + val k1 = ByteArray(keyLength) + val k2 = ByteArray(keyLength) + try { + hkdf(ck!!, 0, ck!!.size, secondaryKey, offset, length, k1, 0, k1.size, k2, 0, k2.size) + var c1: CipherState? = null + var c2: CipherState? = null + var pair: CipherStatePair? = null + try { + c1 = cipher!!.fork(k1, 0) + c2 = cipher!!.fork(k2, 0) + pair = CipherStatePair(c1, c2) + } finally { + 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() + pair = null + } + } + return pair!! + } finally { + destroy(k1) + destroy(k2) + } + } + + override fun destroy() { + if (cipher != null) { + cipher!!.destroy() + cipher = null + } + if (hash != null) { + // The built-in fallback implementations are destroyable. + // JCA/JCE implementations aren't, so try reset() instead. + if (hash is Destroyable) (hash as Destroyable).destroy() + else hash!!.reset() + hash = null + } + if (ck != null) { + Noise.destroy(ck!!) + ck = null + } + if (this.handshakeHash != null) { + Noise.destroy(this.handshakeHash!!) + this.handshakeHash = null + } + if (prev_h != null) { + Noise.destroy(prev_h!!) + prev_h = null + } + } + + /** + * Hashes a single data buffer. + * + * @param data The buffer containing the data to hash. + * @param offset Offset into the data buffer of the first byte to hash. + * @param length Length of the data to be hashed. + * @param output The buffer to receive the output hash value. + * @param outputOffset Offset into the output buffer to place the hash value. + * @param outputLength The length of the hash output. + * + * The output buffer can be the same as the input data buffer. + */ + private fun hashOne( + data: ByteArray, + offset: Int, + length: Int, + output: ByteArray, + outputOffset: Int, + outputLength: Int + ) { + hash!!.reset() + hash!!.update(data, offset, length) + try { + hash!!.digest(output, outputOffset, outputLength) + } catch (e: DigestException) { + Arrays.fill(output, outputOffset, outputLength, 0.toByte()) + } + } + + /** + * Hashes two data buffers. + * + * @param data1 The buffer containing the first data to hash. + * @param offset1 Offset into the first data buffer of the first byte to hash. + * @param length1 Length of the first data to be hashed. + * @param data2 The buffer containing the second data to hash. + * @param offset2 Offset into the second data buffer of the first byte to hash. + * @param length2 Length of the second data to be hashed. + * @param output The buffer to receive the output hash value. + * @param outputOffset Offset into the output buffer to place the hash value. + * @param outputLength The length of the hash output. + * + * The output buffer can be same as either of the input buffers. + */ + private fun hashTwo( + data1: ByteArray, offset1: Int, length1: Int, + data2: ByteArray, offset2: Int, length2: Int, + output: ByteArray, outputOffset: Int, outputLength: Int + ) { + hash!!.reset() + hash!!.update(data1, offset1, length1) + hash!!.update(data2, offset2, length2) + try { + hash!!.digest(output, outputOffset, outputLength) + } catch (e: DigestException) { + Arrays.fill(output, outputOffset, outputLength, 0.toByte()) + } + } + + /** + * Computes a HMAC value using key and data values. + * + * @param key The buffer that contains the key. + * @param keyOffset The offset of the key in the key buffer. + * @param keyLength The length of the key in bytes. + * @param data The buffer that contains the data. + * @param dataOffset The offset of the data in the data buffer. + * @param dataLength The length of the data in bytes. + * @param output The output buffer to place the HMAC value in. + * @param outputOffset Offset into the output buffer for the HMAC value. + * @param outputLength The length of the HMAC output. + */ + private fun hmac( + key: ByteArray, keyOffset: Int, keyLength: Int, + data: ByteArray, dataOffset: Int, dataLength: Int, + output: ByteArray, outputOffset: Int, outputLength: Int + ) { + // 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 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()) + } else { + hash!!.reset() + hash!!.update(key, keyOffset, keyLength) + hash!!.digest(block, 0, hashLength) + Arrays.fill(block, hashLength, blockLength, 0.toByte()) + } + index = 0 + while (index < blockLength) { + block[index] = block[index].toInt() xor 0x36.toByte().toInt() + ++index + } + hash!!.reset() + hash!!.update(block, 0, blockLength) + hash!!.update(data, dataOffset, dataLength) + hash!!.digest(output, outputOffset, hashLength) + index = 0 + while (index < blockLength) { + block[index] = block[index].toInt() xor (0x36 xor 0x5C).toByte().toInt() + ++index + } + hash!!.reset() + 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()) + } finally { + destroy(block) + } + } + + /** + * Computes a HKDF value. + * + * @param key The buffer that contains the key. + * @param keyOffset The offset of the key in the key buffer. + * @param keyLength The length of the key in bytes. + * @param data The buffer that contains the data. + * @param dataOffset The offset of the data in the data buffer. + * @param dataLength The length of the data in bytes. + * @param output1 The first output buffer. + * @param output1Offset Offset into the first output buffer. + * @param output1Length Length of the first output which can be + * less than the hash length. + * @param output2 The second output buffer. + * @param output2Offset Offset into the second output buffer. + * @param output2Length Length of the second output which can be + * less than the hash length. + */ + private fun hkdf( + key: ByteArray, keyOffset: Int, keyLength: Int, + data: ByteArray, dataOffset: Int, dataLength: Int, + output1: ByteArray, output1Offset: Int, output1Length: Int, + output2: ByteArray, output2Offset: Int, output2Length: Int + ) { + val hashLength = hash!!.getDigestLength() + val tempKey = ByteArray(hashLength) + val tempHash = ByteArray(hashLength + 1) + try { + hmac(key, keyOffset, keyLength, data, dataOffset, dataLength, tempKey, 0, hashLength) + tempHash[0] = 0x01.toByte() + hmac(tempKey, 0, hashLength, tempHash, 0, 1, tempHash, 0, hashLength) + System.arraycopy(tempHash, 0, output1, output1Offset, output1Length) + tempHash[hashLength] = 0x02.toByte() + hmac(tempKey, 0, hashLength, tempHash, 0, hashLength + 1, tempHash, 0, hashLength) + System.arraycopy(tempHash, 0, output2, output2Offset, output2Length) + } finally { + destroy(tempKey) + destroy(tempHash) + } + } + + companion object { + private const val TAG = "AndroidSymmetric" + + /** + * Converts a byte array to hex string for logging (matching iOS hex format) + */ + private fun bytesToHex(bytes: ByteArray): String { + val sb = StringBuilder() + for (b in bytes) { + sb.append(String.format("%02x", b)) + } + return sb.toString() + } + } +} diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/package-info.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/package-info.java deleted file mode 100644 index 8027d7ca..00000000 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ - -/** - * Provides classes for communicating via the Noise protocol. - * - * Reference: http://noiseprotocol.org - */ -package com.bitchat.android.noise.southernstorm.protocol; From c04027774ffe9d4953d549432df0e18b34deda7b Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:56:13 +0400 Subject: [PATCH 25/37] refactor(noise/protocol): migrate DHState interface to Kotlin - Convert getDHName/getPublicKeyLength/getPrivateKeyLength/getSharedKeyLength from Java getters into idiomatic Kotlin `val` properties. - Tighten nullability: key buffers and DH peer parameters are non-null per the original Java @param contract; isNullPublicKey kept as a function so existing call sites (`isNullPublicKey()`) keep resolving. - Trim redundant KDoc; preserve upstream license header. Co-Authored-By: Claude Opus 4.7 --- .../noise/southernstorm/protocol/DHState.kt | 73 ++++++------------- 1 file changed, 24 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHState.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHState.kt index a01259f8..17138473 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHState.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHState.kt @@ -25,32 +25,16 @@ package com.bitchat.android.noise.southernstorm.protocol * Interface to a Diffie-Hellman algorithm for the Noise protocol. */ interface DHState : Destroyable { - /** - * Gets the Noise protocol name for this Diffie-Hellman algorithm. - * - * @return The algorithm name. - */ - val dHName: String? + /** The Noise protocol name for this Diffie-Hellman algorithm. */ + val dhName: String - /** - * Gets the length of public keys for this algorithm. - * - * @return The length of public keys in bytes. - */ + /** Length of public keys for this algorithm, in bytes. */ val publicKeyLength: Int - /** - * Gets the length of private keys for this algorithm. - * - * @return The length of private keys in bytes. - */ + /** Length of private keys for this algorithm, in bytes. */ val privateKeyLength: Int - /** - * Gets the length of shared keys for this algorithm. - * - * @return The length of shared keys in bytes. - */ + /** Length of shared keys for this algorithm, in bytes. */ val sharedKeyLength: Int /** @@ -60,41 +44,41 @@ interface DHState : Destroyable { /** * Gets the public key associated with this object. - * + * * @param key The buffer to copy the public key to. * @param offset The first offset in the key buffer to copy to. */ - fun getPublicKey(key: ByteArray?, offset: Int) + fun getPublicKey(key: ByteArray, offset: Int) /** * Sets the public key for this object. - * + * * @param key The buffer containing the public key. * @param offset The first offset in the buffer that contains the key. - * + * * If this object previously held a key pair, then this function * will change it into a public key only object. */ - fun setPublicKey(key: ByteArray?, offset: Int) + fun setPublicKey(key: ByteArray, offset: Int) /** * Gets the private key associated with this object. - * + * * @param key The buffer to copy the private key to. * @param offset The first offset in the key buffer to copy to. */ - fun getPrivateKey(key: ByteArray?, offset: Int) + fun getPrivateKey(key: ByteArray, offset: Int) /** * Sets the private key for this object. - * - * @param key The buffer containing the [rivate key. + * + * @param key The buffer containing the private key. * @param offset The first offset in the buffer that contains the key. - * + * * If this object previously held only a public key, then * this function will change it into a key pair. */ - fun setPrivateKey(key: ByteArray?, offset: Int) + fun setPrivateKey(key: ByteArray, offset: Int) /** * Sets this object to the null public key and clears the private key. @@ -108,47 +92,38 @@ interface DHState : Destroyable { /** * Determine if this object contains a public key. - * - * @return Returns true if this object contains a public key, - * or false if the public key has not yet been set. */ fun hasPublicKey(): Boolean /** * Determine if this object contains a private key. - * - * @return Returns true if this object contains a private key, - * or false if the private key has not yet been set. */ fun hasPrivateKey(): Boolean /** * Determine if the public key in this object is the special null value. - * - * @return Returns true if the public key is the special null value, - * or false otherwise. */ - val isNullPublicKey: Boolean + fun isNullPublicKey(): Boolean /** * Performs a Diffie-Hellman calculation with this object as the private key. - * + * * @param sharedKey Buffer to put the shared key into. * @param offset Offset of the first byte for the shared key. * @param publicDH Object that contains the public key for the calculation. - * + * * @throws IllegalArgumentException The publicDH object is not the same * type as this object, or one of the objects does not contain a valid key. */ - fun calculate(sharedKey: ByteArray?, offset: Int, publicDH: DHState?) + fun calculate(sharedKey: ByteArray, offset: Int, publicDH: DHState) /** * Copies the key values from another DH object of the same type. - * - * @param other The other DH object to copy from - * + * + * @param other The other DH object to copy from. + * * @throws IllegalStateException The other DH object does not have * the same type as this object. */ - fun copyFrom(other: DHState?) + fun copyFrom(other: DHState) } From 2655aa7636502d678eebe0d590d77217079b296c Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:57:22 +0400 Subject: [PATCH 26/37] refactor(noise/protocol): align DHStateHybrid with non-null DHState - Match the tightened `copyFrom` signature on DHState by making the primary `other` parameter non-null while keeping `remote` nullable (a missing peer is meaningful for hybrid algorithms). - Note the inheritance relationship between the single- and two-arg copyFrom overloads. Co-Authored-By: Claude Opus 4.7 --- .../android/noise/southernstorm/protocol/DHStateHybrid.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHStateHybrid.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHStateHybrid.kt index 9c714ddc..7a589517 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHStateHybrid.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/DHStateHybrid.kt @@ -37,6 +37,9 @@ interface DHStateHybrid : DHState { */ fun generateKeyPair(remote: DHState?) + // Note: copyFrom(other) is inherited from DHState; the two-arg overload + // below adds the remote-peer parameter required by hybrid algorithms. + /** * Copies the key values from another DH object of the same type. * @@ -46,7 +49,7 @@ interface DHStateHybrid : DHState { * @throws IllegalStateException The other or remote DH object does not have * the same type as this object. */ - fun copyFrom(other: DHState?, remote: DHState?) + fun copyFrom(other: DHState, remote: DHState?) /** * Specifies the local peer object prior to setting a public key From e7c0f28f76963c86194d7bb88198a720485fb37f Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:57:31 +0400 Subject: [PATCH 27/37] refactor(noise/protocol): migrate Curve25519DHState to Kotlin - Override DHState properties (`dhName`, `publicKeyLength`, `privateKeyLength`, `sharedKeyLength`) instead of Java-style getters. - Replace `Arrays.fill(arr, 0.toByte())` with `arr.fill(0)`. - Drop redundant `val dh = other` alias after the `is` smart cast. - Preserve bit-identical crypto behavior (Curve25519.eval ordering, null-public-key constant-time scan, mode bitfield semantics). Co-Authored-By: Claude Opus 4.7 --- .../protocol/Curve25519DHState.kt | 57 ++++++------------- 1 file changed, 16 insertions(+), 41 deletions(-) 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 19a022eb..573ff5f9 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 @@ -24,43 +24,23 @@ package com.bitchat.android.noise.southernstorm.protocol import com.bitchat.android.noise.southernstorm.crypto.Curve25519 import com.bitchat.android.noise.southernstorm.protocol.Noise.destroy import com.bitchat.android.noise.southernstorm.protocol.Noise.random -import java.util.Arrays /** * Implementation of the Curve25519 algorithm for the Noise protocol. */ internal class Curve25519DHState : DHState { - private val publicKey: ByteArray - private val privateKey: ByteArray + private val publicKey: ByteArray = ByteArray(32) + private val privateKey: ByteArray = ByteArray(32) private var mode = 0 - /** - * Constructs a new Diffie-Hellman object for Curve25519. - */ - init { - publicKey = ByteArray(32) - privateKey = ByteArray(32) - } - override fun destroy() { clearKey() } - override fun getDHName(): String { - return "25519" - } - - override fun getPublicKeyLength(): Int { - return 32 - } - - override fun getPrivateKeyLength(): Int { - return 32 - } - - override fun getSharedKeyLength(): Int { - return 32 - } + override val dhName: String get() = "25519" + override val publicKeyLength: Int get() = 32 + override val privateKeyLength: Int get() = 32 + override val sharedKeyLength: Int get() = 32 override fun generateKeyPair() { random(privateKey) @@ -74,7 +54,7 @@ internal class Curve25519DHState : DHState { override fun setPublicKey(key: ByteArray, offset: Int) { System.arraycopy(key, offset, publicKey, 0, 32) - Arrays.fill(privateKey, 0.toByte()) + privateKey.fill(0) mode = 0x01 } @@ -89,8 +69,8 @@ internal class Curve25519DHState : DHState { } override fun setToNullPublicKey() { - Arrays.fill(publicKey, 0.toByte()) - Arrays.fill(privateKey, 0.toByte()) + publicKey.fill(0) + privateKey.fill(0) mode = 0x01 } @@ -100,13 +80,9 @@ internal class Curve25519DHState : DHState { mode = 0 } - override fun hasPublicKey(): Boolean { - return (mode and 0x01) != 0 - } + override fun hasPublicKey(): Boolean = (mode and 0x01) != 0 - override fun hasPrivateKey(): Boolean { - return (mode and 0x02) != 0 - } + override fun hasPrivateKey(): Boolean = (mode and 0x02) != 0 override fun isNullPublicKey(): Boolean { if ((mode and 0x01) == 0) return false @@ -115,17 +91,16 @@ internal class Curve25519DHState : DHState { return temp == 0 } - override fun calculate(sharedKey: ByteArray, offset: Int, publicDH: DHState?) { + override fun calculate(sharedKey: ByteArray, offset: Int, publicDH: DHState) { require(publicDH is Curve25519DHState) { "Incompatible DH algorithms" } Curve25519.eval(sharedKey, offset, privateKey, publicDH.publicKey) } - override fun copyFrom(other: DHState?) { + override fun copyFrom(other: DHState) { check(other is Curve25519DHState) { "Mismatched DH key objects" } if (other === this) return - val dh = other - System.arraycopy(dh.privateKey, 0, privateKey, 0, 32) - System.arraycopy(dh.publicKey, 0, publicKey, 0, 32) - mode = dh.mode + System.arraycopy(other.privateKey, 0, privateKey, 0, 32) + System.arraycopy(other.publicKey, 0, publicKey, 0, 32) + mode = other.mode } } From f3306091d50dd86df2718322bf8e35e52cef8a14 Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:57:40 +0400 Subject: [PATCH 28/37] refactor(noise/protocol): migrate Curve448DHState to Kotlin - Override DHState properties instead of Java-style getters. - Use `ByteArray.fill(0)` in place of `Arrays.fill(arr, 0.toByte())`. - Preserve bit-identical crypto behavior (56-byte key sizes, Curve448.eval call signature, constant-time null-key scan). Co-Authored-By: Claude Opus 4.7 --- .../southernstorm/protocol/Curve448DHState.kt | 57 ++++++------------- 1 file changed, 16 insertions(+), 41 deletions(-) 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 160e892e..c322973f 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 @@ -24,43 +24,23 @@ package com.bitchat.android.noise.southernstorm.protocol import com.bitchat.android.noise.southernstorm.crypto.Curve448 import com.bitchat.android.noise.southernstorm.protocol.Noise.destroy import com.bitchat.android.noise.southernstorm.protocol.Noise.random -import java.util.Arrays /** * Implementation of the Curve448 algorithm for the Noise protocol. */ internal class Curve448DHState : DHState { - private val publicKey: ByteArray - private val privateKey: ByteArray + private val publicKey: ByteArray = ByteArray(56) + private val privateKey: ByteArray = ByteArray(56) private var mode = 0 - /** - * Constructs a new Diffie-Hellman object for Curve448. - */ - init { - publicKey = ByteArray(56) - privateKey = ByteArray(56) - } - override fun destroy() { clearKey() } - override fun getDHName(): String { - return "448" - } - - override fun getPublicKeyLength(): Int { - return 56 - } - - override fun getPrivateKeyLength(): Int { - return 56 - } - - override fun getSharedKeyLength(): Int { - return 56 - } + override val dhName: String get() = "448" + override val publicKeyLength: Int get() = 56 + override val privateKeyLength: Int get() = 56 + override val sharedKeyLength: Int get() = 56 override fun generateKeyPair() { random(privateKey) @@ -74,7 +54,7 @@ internal class Curve448DHState : DHState { override fun setPublicKey(key: ByteArray, offset: Int) { System.arraycopy(key, offset, publicKey, 0, 56) - Arrays.fill(privateKey, 0.toByte()) + privateKey.fill(0) mode = 0x01 } @@ -89,8 +69,8 @@ internal class Curve448DHState : DHState { } override fun setToNullPublicKey() { - Arrays.fill(publicKey, 0.toByte()) - Arrays.fill(privateKey, 0.toByte()) + publicKey.fill(0) + privateKey.fill(0) mode = 0x01 } @@ -100,13 +80,9 @@ internal class Curve448DHState : DHState { mode = 0 } - override fun hasPublicKey(): Boolean { - return (mode and 0x01) != 0 - } + override fun hasPublicKey(): Boolean = (mode and 0x01) != 0 - override fun hasPrivateKey(): Boolean { - return (mode and 0x02) != 0 - } + override fun hasPrivateKey(): Boolean = (mode and 0x02) != 0 override fun isNullPublicKey(): Boolean { if ((mode and 0x01) == 0) return false @@ -115,17 +91,16 @@ internal class Curve448DHState : DHState { return temp == 0 } - override fun calculate(sharedKey: ByteArray, offset: Int, publicDH: DHState?) { + override fun calculate(sharedKey: ByteArray, offset: Int, publicDH: DHState) { require(publicDH is Curve448DHState) { "Incompatible DH algorithms" } Curve448.eval(sharedKey, offset, privateKey, publicDH.publicKey) } - override fun copyFrom(other: DHState?) { + override fun copyFrom(other: DHState) { check(other is Curve448DHState) { "Mismatched DH key objects" } if (other === this) return - val dh = other - System.arraycopy(dh.privateKey, 0, privateKey, 0, 56) - System.arraycopy(dh.publicKey, 0, publicKey, 0, 56) - mode = dh.mode + System.arraycopy(other.privateKey, 0, privateKey, 0, 56) + System.arraycopy(other.publicKey, 0, publicKey, 0, 56) + mode = other.mode } } From 2b4db2f634a1444858f8827b80c6d29d0033bcac Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:57:54 +0400 Subject: [PATCH 29/37] refactor(noise/protocol): migrate NewHopeDHState to Kotlin - Override DHState/DHStateHybrid properties idiomatically; collapse branchy length getters via single-expression `if`. - Replace `Arrays.fill` and `arraycopy` of in-memory key buffers with `ByteArray.copyOf()` where the intent is "duplicate buffer". - Use safe `?.let` / local `val` patterns for the nullable `nh`, `publicKey`, `privateKey` fields to satisfy Kotlin smart-cast rules. - Restructure the if/else-if chains in `calculate` and `copyFrom` as `when` blocks for readability. - Crypto parity preserved: `shareda` and the BobCalculated arraycopy intentionally use offset=0 to match the original Java, which never honored the caller's `offset` parameter; documented in a comment. Co-Authored-By: Claude Opus 4.7 --- .../southernstorm/protocol/NewHopeDHState.kt | 184 ++++++++---------- 1 file changed, 80 insertions(+), 104 deletions(-) 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 1225504f..85e489ef 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 @@ -23,7 +23,6 @@ package com.bitchat.android.noise.southernstorm.protocol import com.bitchat.android.noise.southernstorm.crypto.NewHope import com.bitchat.android.noise.southernstorm.crypto.NewHopeTor -import java.util.Arrays /** * Implementation of the New Hope post-quantum algorithm for the Noise protocol. @@ -41,25 +40,18 @@ internal class NewHopeDHState : DHStateHybrid { private var nh: NewHopeTor? = null private var publicKey: ByteArray? = null private var privateKey: ByteArray? = null - private var keyType: KeyType + private var keyType: KeyType = KeyType.None /** * Special version of NewHopeTor that allows explicit random data * to be specified for test vectors. */ - private inner class NewHopeWithPrivateKey(var randomData: ByteArray) : NewHopeTor() { + private inner class NewHopeWithPrivateKey(private val randomData: ByteArray) : NewHopeTor() { override fun randombytes(buffer: ByteArray) { System.arraycopy(randomData, 0, buffer, 0, buffer.size) } } - /** - * Constructs a new key exchange object for New Hope. - */ - init { - keyType = KeyType.None - } - private val isAlice: Boolean get() = keyType == KeyType.AlicePrivate || keyType == KeyType.AlicePublic @@ -67,26 +59,18 @@ internal class NewHopeDHState : DHStateHybrid { clearKey() } - override fun getDHName(): String { - return "NewHope" - } + override val dhName: String get() = "NewHope" - override fun getPublicKeyLength(): Int { - if (this.isAlice) return NewHope.SENDABYTES - else return NewHope.SENDBBYTES - } + override val publicKeyLength: Int + get() = if (isAlice) NewHope.SENDABYTES else NewHope.SENDBBYTES - override fun getPrivateKeyLength(): Int { + override val privateKeyLength: Int // New Hope doesn't have private keys in the same sense as // Curve25519 and Curve448. Instead return the number of // random bytes that we need to generate each key type. - if (this.isAlice) return 64 - else return 32 - } + get() = if (isAlice) 64 else 32 - override fun getSharedKeyLength(): Int { - return NewHope.SHAREDBYTES - } + override val sharedKeyLength: Int get() = NewHope.SHAREDBYTES override fun generateKeyPair() { clearKey() @@ -101,44 +85,49 @@ internal class NewHopeDHState : DHStateHybrid { // No remote public key, so always generate in Alice mode. generateKeyPair() return - } else check(remote is NewHopeDHState) { "Mismatched DH objects" } - val r = remote - if (r.isAlice && r.publicKey != null) { + } + check(remote is NewHopeDHState) { "Mismatched DH objects" } + if (remote.isAlice && remote.publicKey != null) { // We have a remote public key for Alice, so generate in Bob mode. clearKey() keyType = KeyType.BobCalculated nh = NewHopeTor() publicKey = ByteArray(NewHope.SENDBBYTES) privateKey = ByteArray(NewHope.SHAREDBYTES) - nh!!.sharedb(privateKey!!, 0, publicKey!!, 0, r.publicKey!!, 0) + nh!!.sharedb(privateKey!!, 0, publicKey!!, 0, remote.publicKey!!, 0) } else { generateKeyPair() } } override fun getPublicKey(key: ByteArray, offset: Int) { - if (publicKey != null) System.arraycopy(publicKey, 0, key, offset, getPublicKeyLength()) - else Arrays.fill(key, 0, getPublicKeyLength(), 0.toByte()) + val pk = publicKey + if (pk != null) System.arraycopy(pk, 0, key, offset, publicKeyLength) + else key.fill(0, offset, offset + publicKeyLength) } override fun setPublicKey(key: ByteArray, offset: Int) { - if (publicKey != null) Noise.destroy(publicKey!!) - publicKey = ByteArray(getPublicKeyLength()) - System.arraycopy(key, 0, publicKey, 0, publicKey!!.size) + publicKey?.let { Noise.destroy(it) } + val len = publicKeyLength + val pk = ByteArray(len) + System.arraycopy(key, offset, pk, 0, len) + publicKey = pk } override fun getPrivateKey(key: ByteArray, offset: Int) { - if (privateKey != null) System.arraycopy(privateKey, 0, key, offset, getPrivateKeyLength()) - else Arrays.fill(key, 0, getPrivateKeyLength(), 0.toByte()) + val sk = privateKey + if (sk != null) System.arraycopy(sk, 0, key, offset, privateKeyLength) + else key.fill(0, offset, offset + privateKeyLength) } override fun setPrivateKey(key: ByteArray, offset: Int) { clearKey() // Guess the key type from the length of the test data. - if (offset == 0 && key.size == 64) keyType = KeyType.AlicePrivate - else keyType = KeyType.BobPrivate - privateKey = ByteArray(getPrivateKeyLength()) - System.arraycopy(key, 0, privateKey, 0, privateKey!!.size) + 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 } override fun setToNullPublicKey() { @@ -148,111 +137,99 @@ internal class NewHopeDHState : DHStateHybrid { } override fun clearKey() { - if (nh != null) { - nh!!.destroy() + nh?.let { + it.destroy() nh = null } - if (publicKey != null) { - Noise.destroy(publicKey!!) + publicKey?.let { + Noise.destroy(it) publicKey = null } - if (privateKey != null) { - Noise.destroy(privateKey!!) + privateKey?.let { + Noise.destroy(it) privateKey = null } keyType = KeyType.None } - override fun hasPublicKey(): Boolean { - return publicKey != null - } + override fun hasPublicKey(): Boolean = publicKey != null - override fun hasPrivateKey(): Boolean { - return privateKey != null - } + override fun hasPrivateKey(): Boolean = privateKey != null - override fun isNullPublicKey(): Boolean { - return false - } + override fun isNullPublicKey(): Boolean = false - override fun calculate(sharedKey: ByteArray, offset: Int, publicDH: DHState?) { + override fun calculate(sharedKey: ByteArray, offset: Int, publicDH: DHState) { require(publicDH is NewHopeDHState) { "Incompatible DH algorithms" } - val other = publicDH - if (keyType == KeyType.AlicePrivate) { - // Compute the shared key for Alice. - nh!!.shareda(sharedKey, 0, other.publicKey!!, 0) - } else if (keyType == KeyType.BobCalculated) { - // The shared key for Bob was already computed when the key was generated. - System.arraycopy(privateKey, 0, sharedKey, 0, NewHope.SHAREDBYTES) - } else { - throw IllegalStateException("Cannot calculate with this DH object") + when (keyType) { + KeyType.AlicePrivate -> { + // Compute the shared key for Alice. + // Note: matches original Java — offset is intentionally unused; + // the shared key is always written starting at index 0. + nh!!.shareda(sharedKey, 0, publicDH.publicKey!!, 0) + } + KeyType.BobCalculated -> { + // The shared key for Bob was already computed when the key was generated. + System.arraycopy(privateKey!!, 0, sharedKey, 0, NewHope.SHAREDBYTES) + } + else -> throw IllegalStateException("Cannot calculate with this DH object") } } - override fun copyFrom(other: DHState?) { + override fun copyFrom(other: DHState) { check(other is NewHopeDHState) { "Mismatched DH key objects" } if (other === this) return - val dh = other clearKey() - when (dh.keyType) { + when (other.keyType) { KeyType.None -> {} - KeyType.AlicePrivate -> if (dh.privateKey != null) { + KeyType.AlicePrivate -> { + val src = other.privateKey ?: throw IllegalStateException("Cannot copy generated key for Alice") keyType = KeyType.AlicePrivate - privateKey = ByteArray(dh.privateKey!!.size) - System.arraycopy(dh.privateKey, 0, privateKey, 0, privateKey!!.size) - } else { - throw IllegalStateException("Cannot copy generated key for Alice") + privateKey = src.copyOf() } - - KeyType.BobPrivate, KeyType.BobCalculated -> throw IllegalStateException("Cannot copy private key for Bob without public key for Alice") - + KeyType.BobPrivate, KeyType.BobCalculated -> + throw IllegalStateException("Cannot copy private key for Bob without public key for Alice") KeyType.AlicePublic, KeyType.BobPublic -> { - keyType = dh.keyType - publicKey = ByteArray(dh.publicKey!!.size) - System.arraycopy(dh.publicKey, 0, publicKey, 0, publicKey!!.size) + keyType = other.keyType + publicKey = other.publicKey!!.copyOf() } } } - override fun copyFrom(other: DHState?, remote: DHState?) { + override fun copyFrom(other: DHState, remote: DHState?) { if (remote == null) { copyFrom(other) return } - check(!(other !is NewHopeDHState || remote !is NewHopeDHState)) { "Mismatched DH key objects" } + check(other is NewHopeDHState && remote is NewHopeDHState) { "Mismatched DH key objects" } if (other === this) return - val dh = other - val remotedh = remote clearKey() - when (dh.keyType) { + when (other.keyType) { KeyType.None -> {} - KeyType.AlicePrivate -> if (dh.privateKey != null) { + KeyType.AlicePrivate -> { + val src = other.privateKey ?: throw IllegalStateException("Cannot copy generated key for Alice") // Generate Alice's public and private key now. keyType = KeyType.AlicePrivate - nh = NewHopeWithPrivateKey(dh.privateKey!!) + nh = NewHopeWithPrivateKey(src) publicKey = ByteArray(NewHope.SENDABYTES) nh!!.keygen(publicKey!!, 0) - } else { - throw IllegalStateException("Cannot copy generated key for Alice") } - - KeyType.BobPrivate -> if (dh.privateKey != null && remotedh.keyType == KeyType.AlicePublic) { - // Now we know the public key for Alice, we can calculate Bob's public and shared keys. - keyType = KeyType.BobCalculated - nh = NewHopeWithPrivateKey(dh.privateKey!!) - publicKey = ByteArray(NewHope.SENDBBYTES) - privateKey = ByteArray(NewHope.SHAREDBYTES) - nh!!.sharedb(privateKey!!, 0, publicKey!!, 0, remotedh.publicKey!!, 0) - } else { - throw IllegalStateException("Cannot copy private key for Bob without public key for Alice") + KeyType.BobPrivate -> { + val src = other.privateKey + if (src != null && remote.keyType == KeyType.AlicePublic) { + // Now we know the public key for Alice, we can calculate Bob's public and shared keys. + keyType = KeyType.BobCalculated + nh = NewHopeWithPrivateKey(src) + publicKey = ByteArray(NewHope.SENDBBYTES) + privateKey = ByteArray(NewHope.SHAREDBYTES) + nh!!.sharedb(privateKey!!, 0, publicKey!!, 0, remote.publicKey!!, 0) + } else { + throw IllegalStateException("Cannot copy private key for Bob without public key for Alice") + } } - KeyType.BobCalculated -> throw IllegalStateException("Cannot copy generated key for Bob") - KeyType.AlicePublic, KeyType.BobPublic -> { - keyType = dh.keyType - publicKey = ByteArray(dh.publicKey!!.size) - System.arraycopy(dh.publicKey, 0, publicKey, 0, publicKey!!.size) + keyType = other.keyType + publicKey = other.publicKey!!.copyOf() } } } @@ -260,7 +237,6 @@ internal class NewHopeDHState : DHStateHybrid { override fun specifyPeer(local: DHState?) { if (local !is NewHopeDHState) return clearKey() - if (local.keyType == KeyType.AlicePrivate) keyType = KeyType.BobPublic - else keyType = KeyType.AlicePublic + keyType = if (local.keyType == KeyType.AlicePrivate) KeyType.BobPublic else KeyType.AlicePublic } } From eabe6eb622b4f422175fbcbeb28c91639a79e02a Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:58:05 +0400 Subject: [PATCH 30/37] refactor(noise/protocol): migrate ChaChaPolyCipherState to Kotlin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Override CipherState properties (`cipherName`, `keyLength`, `macLength`) instead of Java-style getters. - Initialize `val` fields inline and drop the now-empty constructor `init` block. - Replace `Arrays.fill(arr, 0.toByte())` with `arr.fill(0)` and `Arrays.fill(intArr, 0)` with `intArr.fill(0)`. - Tighten counter increments (`++input[12]`) — Kotlin no longer needs the parenthesized form the IDE emitted. - Crypto path (ChaCha20 keystream, Poly1305 MAC, AD padding) is unchanged byte-for-byte. Co-Authored-By: Claude Opus 4.7 --- .../protocol/ChaChaPolyCipherState.kt | 42 ++++++------------- 1 file changed, 13 insertions(+), 29 deletions(-) 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 8e988b39..10e64d94 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 @@ -27,7 +27,6 @@ import com.bitchat.android.noise.southernstorm.crypto.ChaChaCore.initKey256 import com.bitchat.android.noise.southernstorm.crypto.Poly1305 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 @@ -35,38 +34,23 @@ import javax.crypto.ShortBufferException * Implements the ChaChaPoly cipher for Noise. */ internal class ChaChaPolyCipherState : CipherState { - private val poly: Poly1305 - private val input: IntArray - private val output: IntArray - private val polyKey: ByteArray - var n: Long = 0 + private val poly: Poly1305 = Poly1305() + private val input: IntArray = IntArray(16) + private val output: IntArray = IntArray(16) + private val polyKey: ByteArray = ByteArray(32) + private var n: Long = 0 private var haskey = false - /** - * Constructs a new cipher state for the "ChaChaPoly" algorithm. - */ - init { - poly = Poly1305() - input = IntArray(16) - output = IntArray(16) - polyKey = ByteArray(32) - } - override fun destroy() { poly.destroy() - Arrays.fill(input, 0) - Arrays.fill(output, 0) + input.fill(0) + output.fill(0) destroy(polyKey) } - val cipherName: String - get() = "ChaChaPoly" - - val keyLength: Int - get() = 32 - - val mACLength: Int - get() = if (haskey) 16 else 0 + override val cipherName: String get() = "ChaChaPoly" + override val keyLength: Int get() = 32 + override val macLength: Int get() = if (haskey) 16 else 0 override fun initializeKey(key: ByteArray, offset: Int) { initKey256(input, key, offset) @@ -87,14 +71,14 @@ internal class ChaChaPolyCipherState : CipherState { check(n != -1L) { "Nonce has wrapped around" } initIV(input, n++) hash(output, input) - Arrays.fill(polyKey, 0.toByte()) + polyKey.fill(0) xorBlock(polyKey, 0, polyKey, 0, 32, output) poly.reset(polyKey, 0) if (ad != null) { poly.update(ad, 0, ad.size) poly.pad() } - if (++(input[12]) == 0) ++(input[13]) + if (++input[12] == 0) ++input[13] } /** @@ -132,7 +116,7 @@ internal class ChaChaPolyCipherState : CipherState { if (tempLen > length) tempLen = length hash(output, input) xorBlock(plaintext, plaintextOffset, ciphertext, ciphertextOffset, tempLen, output) - if (++(input[12]) == 0) ++(input[13]) + if (++input[12] == 0) ++input[13] plaintextOffset += tempLen ciphertextOffset += tempLen length -= tempLen From e0a390996f9022d22da918f662d04f2cc4a9ce48 Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:58:47 +0400 Subject: [PATCH 31/37] refactor(noise/protocol): migrate AESGCMOnCtrCipherState to Kotlin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Override CipherState properties; tighten `initializeKey` parameter type to non-null `ByteArray`. - Promote the constructor to a `@Throws(NoSuchAlgorithmException)` primary constructor with the `Cipher` instance assigned once as a non-null `val`, removing the bogus `cipher: Cipher? = null` initializer the IDE emitted. - Replace `Arrays.fill(arr, 0.toByte())` with `arr.fill(0)`. - Inline-byte XOR loop in `encryptWithAd` now casts back to `Byte` explicitly to satisfy Kotlin's stricter type rules. - `fork()` rethrows the (impossible) NoSuchAlgorithmException as IllegalStateException instead of returning null — matches the non-null contract on `CipherState.fork`. - AES/CTR keystream and GHASH paths preserved byte-for-byte. Co-Authored-By: Claude Opus 4.7 --- .../protocol/AESGCMOnCtrCipherState.kt | 77 ++++++++----------- 1 file changed, 31 insertions(+), 46 deletions(-) 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 9794bb88..d68b5d17 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 @@ -27,7 +27,6 @@ import com.bitchat.android.noise.southernstorm.protocol.Noise.throwBadTagExcepti import java.security.InvalidAlgorithmParameterException import java.security.InvalidKeyException import java.security.NoSuchAlgorithmException -import java.util.Arrays import javax.crypto.BadPaddingException import javax.crypto.Cipher import javax.crypto.IllegalBlockSizeException @@ -43,33 +42,23 @@ import javax.crypto.spec.SecretKeySpec * This class is used on platforms that don't have "AES/GCM/NoPadding", * but which do have the older "AES/CTR/NoPadding". */ -internal class AESGCMOnCtrCipherState : CipherState { - private val cipher: Cipher? = null - private var keySpec: SecretKeySpec? - private var n: Long - private val iv: ByteArray - private val hashKey: ByteArray - private val ghash: GHASH +internal class AESGCMOnCtrCipherState +@Throws(NoSuchAlgorithmException::class) +constructor() : CipherState { + private val cipher: Cipher + private var keySpec: SecretKeySpec? = null + private var n: Long = 0 + private val iv: ByteArray = ByteArray(16) + private val hashKey: ByteArray = ByteArray(16) + private val ghash: GHASH = GHASH() - /** - * Constructs a new cipher state for the "AESGCM" algorithm. - * - * @throws NoSuchAlgorithmException The system does not have a - * provider for this algorithm. - */ init { - try { - cipher = Cipher.getInstance("AES/CTR/NoPadding") + 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) } - keySpec = null - n = 0 - iv = ByteArray(16) - hashKey = ByteArray(16) - ghash = GHASH() - // Try to set a 256-bit key on the cipher. Some JCE's are // configured to disallow 256-bit AES if an extra policy @@ -95,7 +84,7 @@ internal class AESGCMOnCtrCipherState : CipherState { keySpec = SecretKeySpec(ByteArray(32), "AES") val params = IvParameterSpec(iv) try { - cipher!!.init(Cipher.ENCRYPT_MODE, keySpec, params) + cipher.init(Cipher.ENCRYPT_MODE, keySpec, params) } catch (e: InvalidKeyException) { // Shouldn't happen. } catch (e: InvalidAlgorithmParameterException) { @@ -103,25 +92,20 @@ internal class AESGCMOnCtrCipherState : CipherState { } } - val cipherName: String - get() = "AESGCM" + override val cipherName: String get() = "AESGCM" + override val keyLength: Int get() = 32 + override val macLength: Int get() = if (keySpec != null) 16 else 0 - val keyLength: Int - get() = 32 - - val mACLength: Int - get() = if (keySpec != null) 16 else 0 - - override fun initializeKey(key: ByteArray?, offset: Int) { + override fun initializeKey(key: ByteArray, offset: Int) { // Set the encryption key. keySpec = SecretKeySpec(key, offset, 32, "AES") // Generate the hashing key by encrypting a block of zeroes. - Arrays.fill(iv, 0.toByte()) - Arrays.fill(hashKey, 0.toByte()) + iv.fill(0) + hashKey.fill(0) try { - cipher!!.init(Cipher.ENCRYPT_MODE, keySpec, IvParameterSpec(iv)) + cipher.init(Cipher.ENCRYPT_MODE, keySpec, IvParameterSpec(iv)) } catch (e: InvalidKeyException) { // Shouldn't happen. throw IllegalStateException(e) @@ -185,12 +169,12 @@ internal class AESGCMOnCtrCipherState : CipherState { // Initialize the CTR mode cipher with the key and IV. - cipher!!.init(Cipher.ENCRYPT_MODE, keySpec, IvParameterSpec(iv)) + cipher.init(Cipher.ENCRYPT_MODE, keySpec, IvParameterSpec(iv)) // 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) try { cipher.update(hashKey, 0, 16, hashKey, 0) } catch (e: ShortBufferException) { @@ -232,7 +216,7 @@ internal class AESGCMOnCtrCipherState : CipherState { try { setup(ad) val result = - cipher!!.update(plaintext, plaintextOffset, length, ciphertext, ciphertextOffset) + cipher.update(plaintext, plaintextOffset, length, ciphertext, ciphertextOffset) cipher.doFinal(ciphertext, ciphertextOffset + result) } catch (e: InvalidKeyException) { // Shouldn't happen. @@ -250,8 +234,10 @@ internal class AESGCMOnCtrCipherState : 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 } @@ -303,7 +289,7 @@ internal class AESGCMOnCtrCipherState : CipherState { if ((temp and 0xFF) != 0) throwBadTagException() try { val result = - cipher!!.update(ciphertext, ciphertextOffset, dataLen, plaintext, plaintextOffset) + cipher.update(ciphertext, ciphertextOffset, dataLen, plaintext, plaintextOffset) cipher.doFinal(plaintext, plaintextOffset + result) } catch (e: IllegalBlockSizeException) { // Shouldn't happen. @@ -316,12 +302,11 @@ internal class AESGCMOnCtrCipherState : CipherState { } override fun fork(key: ByteArray, offset: Int): CipherState { - val cipher: CipherState - try { - cipher = AESGCMOnCtrCipherState() + val cipher: CipherState = try { + AESGCMOnCtrCipherState() } catch (e: NoSuchAlgorithmException) { - // Shouldn't happen. - return null + // Shouldn't happen — this state already exists, so the algorithm is available. + throw IllegalStateException(e) } cipher.initializeKey(key, offset) return cipher From 6f8fcf8022e214c1878ba7de3e74f6b59b209b9e Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:59:39 +0400 Subject: [PATCH 32/37] refactor(noise/protocol): migrate AESGCMFallbackCipherState to Kotlin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- .../protocol/AESGCMFallbackCipherState.kt | 63 +++++++++---------- 1 file changed, 28 insertions(+), 35 deletions(-) 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 4820385a..fff41baa 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 @@ -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 } From 1f2c62d2052343ac4677f7a43f5b025a6f553a73 Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 17:59:54 +0400 Subject: [PATCH 33/37] refactor(noise/protocol): migrate SymmetricState to Kotlin - Expose `protocolName` as a non-null Kotlin property and rename the IDE's `mACLength` accessor to `macLength` (delegates to `cipher.macLength`). - Update internal `dh.getPublicKeyLength()` calls to use the new property form. - Rewrite the HMAC inner/outer-pad XOR loops as `for`-in-range with an explicit `.toByte()` on the result, satisfying Kotlin's stricter Int-vs-Byte rules. - Keep `split()`'s `secondaryKey` parameter nullable to match the original Java contract; an empty buffer is substituted when null so the HKDF helper signature can stay non-null. - HKDF/HMAC/handshake-hash logic is otherwise unchanged, including the iOS-parity debug logging. Co-Authored-By: Claude Opus 4.7 --- .../southernstorm/protocol/SymmetricState.kt | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) 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 c1328047..4c2ac2de 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 @@ -42,7 +42,7 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName * * @return The protocol name. */ - val protocolName: String? + val protocolName: String private var cipher: CipherState? private var hash: MessageDigest? private var ck: ByteArray? @@ -120,14 +120,12 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName Log.d(TAG, "=========================================") } - val mACLength: Int - /** - * Gets the length of MAC values in the current state. - * - * @return The length of the MAC value for the underlying cipher - * or zero if the cipher has not yet been initialized with a key. - */ - get() = cipher.mACLength + /** + * Length of MAC values in the current state, or zero if the cipher + * has not yet been initialized with a key. + */ + val macLength: Int + get() = cipher!!.macLength /** * Mixes data into the chaining key. @@ -180,7 +178,7 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName this.handshakeHash!! ) ) - Log.d(TAG, "Cipher now has key: " + (cipher.mACLength > 0)) + Log.d(TAG, "Cipher now has key: " + (cipher!!.macLength > 0)) } /** @@ -238,7 +236,7 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName * @param dh The object containing the public key. */ fun mixPublicKey(dh: DHState) { - val temp = ByteArray(dh.getPublicKeyLength()) + val temp = ByteArray(dh.publicKeyLength) try { dh.getPublicKey(temp, 0) mixHash(temp, 0, temp.size) @@ -253,7 +251,7 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName * @param dh The object containing the public key. */ fun mixPublicKeyIntoCK(dh: DHState) { - val temp = ByteArray(dh.getPublicKeyLength()) + val temp = ByteArray(dh.publicKeyLength) try { dh.getPublicKey(temp, 0) mixKey(temp, 0, temp.size) @@ -366,7 +364,7 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName */ @JvmOverloads fun split( - secondaryKey: ByteArray = ByteArray(0), + secondaryKey: ByteArray? = ByteArray(0), offset: Int = 0, length: Int = 0 ): CipherStatePair { @@ -375,7 +373,10 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName val k1 = ByteArray(keyLength) val k2 = ByteArray(keyLength) try { - hkdf(ck!!, 0, ck!!.size, secondaryKey, offset, length, k1, 0, k1.size, k2, 0, k2.size) + // When length == 0 the buffer is unused by HKDF; substitute an + // empty array so the helper signature can stay non-null. + val sk = secondaryKey ?: ByteArray(0) + hkdf(ck!!, 0, ck!!.size, sk, offset, length, k1, 0, k1.size, k2, 0, k2.size) var c1: CipherState? = null var c2: CipherState? = null var pair: CipherStatePair? = null @@ -518,19 +519,15 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName hash!!.digest(block, 0, hashLength) Arrays.fill(block, hashLength, blockLength, 0.toByte()) } - index = 0 - while (index < blockLength) { - block[index] = block[index].toInt() xor 0x36.toByte().toInt() - ++index + for (i in 0 until blockLength) { + block[i] = (block[i].toInt() xor 0x36).toByte() } hash!!.reset() hash!!.update(block, 0, blockLength) hash!!.update(data, dataOffset, dataLength) hash!!.digest(output, outputOffset, hashLength) - index = 0 - while (index < blockLength) { - block[index] = block[index].toInt() xor (0x36 xor 0x5C).toByte().toInt() - ++index + for (i in 0 until blockLength) { + block[i] = (block[i].toInt() xor (0x36 xor 0x5C)).toByte() } hash!!.reset() hash!!.update(block, 0, blockLength) From 84f70ff12d8f06a373562e9084a5d526a36b7243 Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 18:00:06 +0400 Subject: [PATCH 34/37] refactor(noise/protocol): migrate HandshakeState to Kotlin - Use Kotlin properties (`protocolName`, `handshakeHash`) and update every getter call (`getDHName/getPublicKeyLength/getMACLength/ getSharedKeyLength/getProtocolName/getHandshakeHash`) to the new property names exposed by the migrated DHState / SymmetricState / CipherState interfaces. - Initialize `pattern` non-null via `requireNotNull(lookup(...))`, and give the other late-init `var` fields (`patternIndex`, `preSharedKey`, `prologue`) sensible defaults so the class compiles cleanly. - Resolve smart-cast errors on mutable nullable fields (`prologue`, `fixedEphemeral`, `fixedHybrid`, `localHybrid`) by copying to local `val`s before use. - Replace `preSharedKey != null` / `mixPreSharedKey(preSharedKey)` guards with `?.let` blocks, and pass `localKeyPair!!` etc. into `mixPublicKey` to satisfy its now-non-null parameter. - Tighten the cipher/hash name locals in the protocol-name parser from `String?` to `String` so the SymmetricState constructor accepts them. - Token-processing loop, fallback bookkeeping and message buffer layout are unchanged. Co-Authored-By: Claude Opus 4.7 --- .../southernstorm/protocol/HandshakeState.kt | 142 +++++++++--------- 1 file changed, 69 insertions(+), 73 deletions(-) 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 e2605db1..a0dc7cfd 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 @@ -73,18 +73,14 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { var action: Int private set private var requirements: Int - private var pattern: ShortArray? - private var patternIndex: Int - private var preSharedKey: ByteArray? - private var prologue: ByteArray? + private var pattern: ShortArray + private var patternIndex: Int = 1 + private var preSharedKey: ByteArray? = null + private var prologue: ByteArray? = null - val protocolName: String? - /** - * Gets the name of the Noise protocol. - * - * @return The protocol name. - */ - get() = symmetric!!.getProtocolName() + /** Name of the Noise protocol. */ + val protocolName: String + get() = symmetric!!.protocolName val role: Int /** @@ -230,7 +226,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { if (localEphemeral == null) return null try { fixedEphemeral = - createDH(localEphemeral!!.getDHName()) + createDH(localEphemeral!!.dhName) } catch (e: NoSuchAlgorithmException) { // This shouldn't happen - the local ephemeral key would // have already been created with the same name! @@ -256,7 +252,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { if (localHybrid == null) return null try { fixedHybrid = - createDH(localHybrid!!.getDHName()) + createDH(localHybrid!!.dhName) } catch (e: NoSuchAlgorithmException) { // This shouldn't happen - the local hybrid key would // have already been created with the same name! @@ -286,12 +282,11 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { val patternId = components[1] var dh = components[2] var hybrid: String? = null - val cipher: String? = components[3] - val hash: String? = components[4] + val cipher: String = components[3] + val hash: String = components[4] require(!(prefix != "Noise" && prefix != "NoisePSK")) { "Prefix must be Noise or NoisePSK" } - pattern = lookup(patternId) - requireNotNull(pattern) { "Handshake pattern is not recognized" } - var flags = pattern!![0] + pattern = requireNotNull(lookup(patternId)) { "Handshake pattern is not recognized" } + var flags = pattern[0] var extraReqs = 0 if ((flags.toInt() and Pattern.FLAG_REMOTE_REQUIRED.toInt()) != 0 && patternId.length > 1) extraReqs = extraReqs or FALLBACK_POSSIBLE @@ -336,10 +331,10 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { // 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!!.getDHName() + "' for static keys") - if (localEphemeral is DHStateHybrid) throw NoSuchAlgorithmException("Cannot use '" + localEphemeral!!.getDHName() + "' for ephemeral keys") - if (remotePublicKey is DHStateHybrid) throw NoSuchAlgorithmException("Cannot use '" + remotePublicKey!!.getDHName() + "' for static keys") - if (remoteEphemeral is DHStateHybrid) throw NoSuchAlgorithmException("Cannot use '" + remoteEphemeral!!.getDHName() + "' 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") } /** @@ -364,7 +359,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { */ fun start() { check(action == NO_ACTION) { "Handshake has already started; cannot start again" } - if ((pattern!![0].toInt() and Pattern.FLAG_REMOTE_EPHEM_REQ.toInt()) != 0 && + if ((pattern[0].toInt() and Pattern.FLAG_REMOTE_EPHEM_REQ.toInt()) != 0 && (requirements and FALLBACK_PREMSG) == 0 ) { throw UnsupportedOperationException("Cannot start a fallback pattern") @@ -384,48 +379,49 @@ 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!!.getProtocolName()) + Log.d(TAG, "Protocol: " + symmetric!!.protocolName) Log.d(TAG, "Role: " + (if (isInitiator) "INITIATOR" else "RESPONDER")) - Log.d(TAG, "Initial symmetric hash: " + bytesToHex(symmetric!!.getHandshakeHash())) + Log.d(TAG, "Initial symmetric hash: " + bytesToHex(symmetric!!.handshakeHash!!)) // Hash the prologue value. Log.d(TAG, "Mixing empty prologue") - if (prologue != null) symmetric!!.mixHash(prologue, 0, prologue!!.size) + 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!!.getHandshakeHash())) + Log.d(TAG, "Hash after empty prologue: " + bytesToHex(symmetric!!.handshakeHash!!)) // Hash the pre-shared key into the chaining key and handshake hash. - if (preSharedKey != null) symmetric!!.mixPreSharedKey(preSharedKey) + preSharedKey?.let { symmetric!!.mixPreSharedKey(it) } // Mix the pre-supplied public keys into the handshake hash. if (isInitiator) { Log.d(TAG, "XX pattern - no pre-message keys to mix") - if ((requirements and LOCAL_PREMSG) != 0) symmetric!!.mixPublicKey(localKeyPair) + if ((requirements and LOCAL_PREMSG) != 0) symmetric!!.mixPublicKey(localKeyPair!!) if ((requirements and FALLBACK_PREMSG) != 0) { - symmetric!!.mixPublicKey(remoteEphemeral) - if (remoteHybrid != null) symmetric!!.mixPublicKey(remoteHybrid) - if (preSharedKey != null) symmetric!!.mixPublicKeyIntoCK(remoteEphemeral) + symmetric!!.mixPublicKey(remoteEphemeral!!) + remoteHybrid?.let { symmetric!!.mixPublicKey(it) } + if (preSharedKey != null) symmetric!!.mixPublicKeyIntoCK(remoteEphemeral!!) } - if ((requirements and REMOTE_PREMSG) != 0) symmetric!!.mixPublicKey(remotePublicKey) + if ((requirements and REMOTE_PREMSG) != 0) symmetric!!.mixPublicKey(remotePublicKey!!) } else { Log.d(TAG, "XX pattern - no pre-message keys to mix") - if ((requirements and REMOTE_PREMSG) != 0) symmetric!!.mixPublicKey(remotePublicKey) + if ((requirements and REMOTE_PREMSG) != 0) symmetric!!.mixPublicKey(remotePublicKey!!) if ((requirements and FALLBACK_PREMSG) != 0) { - symmetric!!.mixPublicKey(localEphemeral) - if (localHybrid != null) symmetric!!.mixPublicKey(localHybrid) - if (preSharedKey != null) symmetric!!.mixPublicKeyIntoCK(localEphemeral) + symmetric!!.mixPublicKey(localEphemeral!!) + localHybrid?.let { symmetric!!.mixPublicKey(it) } + if (preSharedKey != null) symmetric!!.mixPublicKeyIntoCK(localEphemeral!!) } - if ((requirements and LOCAL_PREMSG) != 0) symmetric!!.mixPublicKey(localKeyPair) + if ((requirements and LOCAL_PREMSG) != 0) symmetric!!.mixPublicKey(localKeyPair!!) } // Log final state after all initialization (matching iOS) Log.d(TAG, "=== ANDROID HANDSHAKE START - FINAL STATE ===") Log.d( TAG, - "Final symmetric hash after mixPreMessageKeys(): " + bytesToHex(symmetric!!.getHandshakeHash()) + "Final symmetric hash after mixPreMessageKeys(): " + bytesToHex(symmetric!!.handshakeHash!!) ) Log.d(TAG, "===========================================") @@ -533,7 +529,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { // Format a new protocol name for the fallback variant // and recreate the SymmetricState object. val components = - symmetric!!.getProtocolName().split("_".toRegex()).dropLastWhile { it.isEmpty() } + symmetric!!.protocolName.split("_".toRegex()).dropLastWhile { it.isEmpty() } .toTypedArray() components[1] = patternName val builder = StringBuilder() @@ -563,7 +559,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { action = NO_ACTION pattern = newPattern patternIndex = 1 - var flags = pattern!![0] + var flags = pattern[0] if (!isInitiator) { // Reverse the pattern flags so that the responder is "local". flags = reverseFlags(flags) @@ -584,7 +580,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { */ private fun mixDH(local: DHState, remote: DHState) { check(!(local == null || remote == null)) { "Pattern definition error" } - val len = local.getSharedKeyLength() + val len = local.sharedKeyLength val shared = ByteArray(len) try { local.calculate(shared, 0, remote) @@ -643,12 +639,12 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { try { // Process tokens until the direction changes or the patten ends. while (true) { - if (patternIndex >= pattern!!.size) { + if (patternIndex >= pattern.size) { // The pattern has finished, so the next action is "split". action = SPLIT break } - val token = pattern!![patternIndex++] + val token = pattern[patternIndex++] if (token == Pattern.FLIP_DIR) { // Change directions, so this message is complete and the // next action is "read message". @@ -664,9 +660,10 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { // key to the message. If we are running fixed vector tests, // then the ephemeral key may have already been provided. checkNotNull(localEphemeral) { "Pattern definition error" } - if (fixedEphemeral == null) localEphemeral!!.generateKeyPair() - else localEphemeral!!.copyFrom(fixedEphemeral) - len = localEphemeral!!.getPublicKeyLength() + val fe = fixedEphemeral + if (fe == null) localEphemeral!!.generateKeyPair() + else localEphemeral!!.copyFrom(fe) + len = localEphemeral!!.publicKeyLength if (space < len) throw ShortBufferException() localEphemeral!!.getPublicKey(message, messagePosn) symmetric!!.mixHash(message, messagePosn, len) @@ -680,8 +677,8 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { Pattern.S -> { // Encrypt the local static public key and add it to the message. checkNotNull(localKeyPair) { "Pattern definition error" } - len = localKeyPair!!.getPublicKeyLength() - macLen = symmetric!!.getMACLength() + len = localKeyPair!!.publicKeyLength + macLen = symmetric!!.macLength if (space < (len + macLen)) throw ShortBufferException() localKeyPair!!.getPublicKey(message, messagePosn) messagePosn += symmetric!!.encryptAndHash( @@ -720,19 +717,20 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { // key to the message. If we are running fixed vector tests, // then a fixed hybrid key may have already been provided. checkNotNull(localHybrid) { "Pattern definition error" } - if (localHybrid is DHStateHybrid) { + val fh = fixedHybrid + val lh = localHybrid!! + if (lh is DHStateHybrid) { // The DH object is something like New Hope which needs to // generate keys relative to the other party's public key. - val hybrid = localHybrid as DHStateHybrid - if (fixedHybrid == null) hybrid.generateKeyPair(remoteHybrid) - else hybrid.copyFrom(fixedHybrid, remoteHybrid) + if (fh == null) lh.generateKeyPair(remoteHybrid) + else lh.copyFrom(fh, remoteHybrid) } else { - if (fixedHybrid == null) localHybrid!!.generateKeyPair() - else localHybrid!!.copyFrom(fixedHybrid) + if (fh == null) lh.generateKeyPair() + else lh.copyFrom(fh) } - len = localHybrid!!.getPublicKeyLength() + len = localHybrid!!.publicKeyLength if (space < len) throw ShortBufferException() - macLen = symmetric!!.getMACLength() + macLen = symmetric!!.macLength if (space < (len + macLen)) throw ShortBufferException() localHybrid!!.getPublicKey(message, messagePosn) messagePosn += symmetric!!.encryptAndHash( @@ -838,12 +836,12 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { try { // Process tokens until the direction changes or the patten ends. while (true) { - if (patternIndex >= pattern!!.size) { + if (patternIndex >= pattern.size) { // The pattern has finished, so the next action is "split". action = SPLIT break } - val token = pattern!![patternIndex++] + val token = pattern[patternIndex++] if (token == Pattern.FLIP_DIR) { // Change directions, so this message is complete and the // next action is "write message". @@ -857,7 +855,7 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { Pattern.E -> { // Save the remote ephemeral key and hash it. checkNotNull(remoteEphemeral) { "Pattern definition error" } - len = remoteEphemeral!!.getPublicKeyLength() + len = remoteEphemeral!!.publicKeyLength if (space < len) throw ShortBufferException() symmetric!!.mixHash(message, messageOffset, len) remoteEphemeral!!.setPublicKey(message, messageOffset) @@ -878,8 +876,8 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { Pattern.S -> { // Decrypt and read the remote static key. checkNotNull(remotePublicKey) { "Pattern definition error" } - len = remotePublicKey!!.getPublicKeyLength() - macLen = symmetric!!.getMACLength() + len = remotePublicKey!!.publicKeyLength + macLen = symmetric!!.macLength if (space < (len + macLen)) throw ShortBufferException() val temp = ByteArray(len) try { @@ -929,8 +927,8 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { // generated a local hybrid keypair or not. (remoteHybrid as DHStateHybrid).specifyPeer(localHybrid) } - len = remoteHybrid!!.getPublicKeyLength() - macLen = symmetric!!.getMACLength() + len = remoteHybrid!!.publicKeyLength + macLen = symmetric!!.macLength if (space < (len + macLen)) throw ShortBufferException() val temp = ByteArray(len) try { @@ -1026,17 +1024,15 @@ class HandshakeState(protocolName: String, role: Int) : Destroyable { return pair } + /** + * Current value of the handshake hash, which must not be modified by the caller. + * + * @throws IllegalStateException The action is not SPLIT or COMPLETE. + */ val handshakeHash: ByteArray? - /** - * Gets the current value of the handshake hash. - * - * @return The handshake hash. This must not be modified by the caller. - * - * @throws IllegalStateException The action is not SPLIT or COMPLETE. - */ get() { - check(!(action != SPLIT && action != COMPLETE)) { "Handshake has not completed" } - return symmetric!!.getHandshakeHash() + check(action == SPLIT || action == COMPLETE) { "Handshake has not completed" } + return symmetric!!.handshakeHash } override fun destroy() { From 544d56970856b040f4727d380456fc6792b0a5c2 Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 18:00:24 +0400 Subject: [PATCH 35/37] refactor(noise): update NoiseSession for Kotlin-property accessors External callers must use the property form for HandshakeState and CipherStatePair members that the protocol package now exposes as `val`s: - handshakeState.getLocalKeyPair() -> handshakeState.localKeyPair - handshakeState.getAction() -> handshakeState.action - handshakeState.getRemotePublicKey() -> handshakeState.remotePublicKey - handshakeState.getHandshakeHash() -> handshakeState.handshakeHash - cipherPair.getSender() / getReceiver() -> .sender / .receiver DH operations with arguments (`setPrivateKey`, `getPublicKey`, etc.) remain regular method calls. Co-Authored-By: Claude Opus 4.7 --- .../com/bitchat/android/noise/NoiseSession.kt | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/noise/NoiseSession.kt b/app/src/main/java/com/bitchat/android/noise/NoiseSession.kt index dc4a3d50..5fe1ab59 100644 --- a/app/src/main/java/com/bitchat/android/noise/NoiseSession.kt +++ b/app/src/main/java/com/bitchat/android/noise/NoiseSession.kt @@ -1,9 +1,8 @@ package com.bitchat.android.noise import android.util.Log -import com.bitchat.android.noise.southernstorm.protocol.* -import com.bitchat.android.util.toHexString -import java.security.SecureRandom +import com.bitchat.android.noise.southernstorm.protocol.CipherState +import com.bitchat.android.noise.southernstorm.protocol.HandshakeState /** @@ -251,7 +250,7 @@ class NoiseSession( if (handshakeState?.needsLocalKeyPair() == true) { Log.d(TAG, "Local static key pair is required for XX pattern") - val localKeyPair = handshakeState?.getLocalKeyPair() + val localKeyPair = handshakeState?.localKeyPair if (localKeyPair != null) { // FIXED: Use the provided persistent identity keys with our local fork // Our local fork properly supports setting pre-existing keys @@ -369,7 +368,7 @@ class NoiseSession( Log.d(TAG, "Read handshake message, payload length: $payloadLength currentPattern: $currentPattern") // Check what action the handshake state wants us to take next - val action = handshakeStateLocal.getAction() + val action = handshakeStateLocal.action Log.d(TAG, "Handshake action after processing message: $action") return when (action) { @@ -380,7 +379,7 @@ class NoiseSession( currentPattern++ val response = responseBuffer.copyOf(responseLength) - Log.d(TAG, "Generated handshake response: ${response.size} bytes, action still: ${handshakeStateLocal.getAction()} currentPattern: $currentPattern") + Log.d(TAG, "Generated handshake response: ${response.size} bytes, action still: ${handshakeStateLocal.action} currentPattern: $currentPattern") completeHandshake() response } @@ -430,12 +429,12 @@ class NoiseSession( // Split handshake state into transport ciphers val cipherPair = handshakeState?.split() - sendCipher = cipherPair?.getSender() - receiveCipher = cipherPair?.getReceiver() + sendCipher = cipherPair?.sender + receiveCipher = cipherPair?.receiver // Extract remote static key if available if (handshakeState?.hasRemotePublicKey() == true) { - val remoteDH = handshakeState?.getRemotePublicKey() + val remoteDH = handshakeState?.remotePublicKey if (remoteDH != null) { remoteStaticPublicKey = ByteArray(32) remoteDH.getPublicKey(remoteStaticPublicKey!!, 0) @@ -444,7 +443,7 @@ class NoiseSession( } // Extract handshake hash for channel binding - handshakeHash = handshakeState?.getHandshakeHash() + handshakeHash = handshakeState?.handshakeHash // Clean up handshake state handshakeState?.destroy() From f1645e73f2c83ff3ee9ef063f07e212dcbd0b6f9 Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 18:18:23 +0400 Subject: [PATCH 36/37] refactor(noise/protocol): polish Kotlin idioms and remove Java legacy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address IDE/compiler warnings across the protocol/ package after the Java to Kotlin migration: - Replace reference-identity ByteArray checks `!=` with `!==` in ChaChaPoly/AESGCM*CipherState (in-place encrypt/decrypt guards) to silence the "dangerous array comparison" warning while preserving the original semantics. - Drop `java.util.Arrays.fill` and `Arrays`-style helpers in favour of ByteArray.fill / copyInto / copyOfRange where appropriate. Cold paths in NewHopeDHState, Curve25519DHState, Curve448DHState and parts of SymmetricState now use Kotlin extensions; tight crypto inner loops (HMAC body, ChaChaPoly) keep System.arraycopy intentionally. - Use property access (`digestLength`) instead of `getDigestLength()`. - Fold redundant if/else into `if-expression`, drop redundant `val space: Int` + assignment, redundant `Companion.` qualifiers, and unused `val cipher` re-assign in fork(). - Rename `prev_h` to `prevH` (style). - Replace string concatenation with templates in log statements and exception messages. - Convert unused catch parameters `e` to `_` and remove stale local `var index: Int` in hmac(). - Strip stale `@param secondaryKey/offset/length` KDoc block above SymmetricState.split (the params moved to the merged doc). - Drop dead `check(!(local == null || remote == null))` from HandshakeState.mixDH — Kotlin types are already non-null. Crypto behaviour is unchanged; output of every method matches the prior Kotlin/Java implementation byte-for-byte. Co-Authored-By: Claude Opus 4.7 --- .../protocol/AESGCMFallbackCipherState.kt | 10 +- .../protocol/AESGCMOnCtrCipherState.kt | 27 ++-- .../protocol/ChaChaPolyCipherState.kt | 46 +++--- .../protocol/Curve25519DHState.kt | 12 +- .../southernstorm/protocol/Curve448DHState.kt | 12 +- .../southernstorm/protocol/HandshakeState.kt | 63 ++++---- .../southernstorm/protocol/NewHopeDHState.kt | 16 +-- .../southernstorm/protocol/SymmetricState.kt | 135 +++++++----------- 8 files changed, 139 insertions(+), 182 deletions(-) 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() } From f2dc1388ca190f855d72e1c9b76c155fb5268977 Mon Sep 17 00:00:00 2001 From: yet Date: Fri, 15 May 2026 18:26:30 +0400 Subject: [PATCH 37/37] refactor(noise/protocol): convert remaining log concatenations to templates Missed by the prior idiomatic-polish pass: SymmetricState had 13 Log.d calls still using "..." + bytesToHex(...) instead of string templates. Co-Authored-By: Claude Opus 4.7 --- .../southernstorm/protocol/SymmetricState.kt | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) 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 4a8f8878..cefd5aae 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 @@ -101,9 +101,9 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName // 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): " + bytesToHex(initialHash)) - Log.d(TAG, "Initial chaining key (ck): " + bytesToHex(ck!!)) - Log.d(TAG, "Hash length: " + initialHash.size) + 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, "=========================================") } @@ -126,9 +126,9 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName val inputData = ByteArray(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: " + bytesToHex(ck!!)) - Log.d(TAG, "Current Hash: " + 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(handshakeHash!!)}") val keyLength = cipher!!.keyLength val tempKey = ByteArray(keyLength) @@ -155,9 +155,9 @@ 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: " + bytesToHex(ck!!)) - Log.d(TAG, "Hash unchanged: " + bytesToHex(this.handshakeHash!!)) - Log.d(TAG, "Cipher now has key: " + (cipher!!.macLength > 0)) + Log.d(TAG, "New CK: ${bytesToHex(ck!!)}") + Log.d(TAG, "Hash unchanged: ${bytesToHex(handshakeHash!!)}") + Log.d(TAG, "Cipher now has key: ${cipher!!.macLength > 0}") } /** @@ -172,8 +172,8 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName val inputData = ByteArray(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: " + bytesToHex(this.handshakeHash!!)) + Log.d(TAG, "Input data ($length bytes): ${bytesToHex(inputData)}") + Log.d(TAG, "Current Hash: ${bytesToHex(handshakeHash!!)}") val h = handshakeHash!! hashTwo( @@ -184,7 +184,7 @@ internal class SymmetricState(protocolName: String, cipherName: String, hashName // LOGGING: After mixHash operation (matching iOS) Log.d(TAG, "*** Android mixHash() AFTER ***") - Log.d(TAG, "New Hash: " + bytesToHex(this.handshakeHash!!)) + Log.d(TAG, "New Hash: ${bytesToHex(handshakeHash!!)}") } /**