mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-11-01 07:51:15 +00:00
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.
This commit is contained in:
parent
1d99434a25
commit
6ac2769f63
@ -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])
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user