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() }