Merge f2dc1388ca190f855d72e1c9b76c155fb5268977 into c8f45408b1984753a4df7f05aff03ceaa17bbba6

This commit is contained in:
Ruslan 2026-07-26 12:24:00 +03:00 committed by GitHub
commit fa62e63c35
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
55 changed files with 8657 additions and 11482 deletions

View File

@ -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
/**
@ -263,7 +262,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
@ -393,7 +392,7 @@ class NoiseSession(
Log.d(TAG, "Read handshake message, payload length: $payloadLength prefix=$readPrefix 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) {
@ -404,7 +403,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
}
@ -454,12 +453,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)
@ -468,7 +467,7 @@ class NoiseSession(
}
// Extract handshake hash for channel binding
handshakeHash = handshakeState?.getHandshakeHash()
handshakeHash = handshakeState?.handshakeHash
// Clean up handshake state
handshakeState?.destroy()

View File

@ -1,243 +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.crypto;
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.
*
* This implementation only supports message digesting with an output
* 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 {
private long[] h;
private byte[] block;
private long[] m;
private long[] v;
private long length;
private int posn;
/**
* 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
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;
}
@Override
protected int engineGetDigestLength() {
return 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
protected void engineUpdate(byte input) {
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;
}
}
// 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);
// 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 long rightRotate32(long v)
{
return v << 32 | (v >>> 32);
}
private static long rightRotate24(long v)
{
return v << 40 | (v >>> 24);
}
private static long rightRotate16(long v)
{
return v << 48 | (v >>> 16);
}
private static long rightRotate63(long v)
{
return v << 1 | (v >>> 63);
}
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);
}
}

View File

@ -0,0 +1,217 @@
/*
* 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.crypto
import com.bitchat.android.noise.southernstorm.protocol.Destroyable
import java.security.DigestException
import java.security.MessageDigest
/**
* Fallback implementation of BLAKE2b for the Noise library.
*
* This implementation only supports message digesting with an output
* length of 64 bytes and a limit of 2^64 - 1 bytes of input.
* Keyed hashing and variable-length digests are not supported.
*/
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
init {
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
}
@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 fun engineGetDigestLength(): Int = 64
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 fun engineUpdate(input: Byte) {
if (posn >= 128) {
transform(0)
posn = 0
}
block[posn++] = input
++length
}
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
input.copyInto(block, posn, currentOffset, currentOffset + temp)
posn += temp
length += temp.toLong()
currentOffset += temp
remainingLen -= temp
}
}
private fun transform(f0: Long) {
var currentOffset = 0
// 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
}
// Format the block to be hashed.
for (index in 0 until 8) {
v[index] = h[index]
}
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
// 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<ByteArray> = 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)
}
}

View File

@ -1,233 +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.crypto;
import java.security.DigestException;
import java.security.MessageDigest;
import java.util.Arrays;
import com.bitchat.android.noise.southernstorm.protocol.Destroyable;
/**
* Fallback implementation of BLAKE2s for the Noise library.
*
* This implementation only supports message digesting with an output
* 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;
/**
* 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;
}
@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
protected void engineUpdate(byte input) {
if (posn >= 64) {
transform(0);
posn = 0;
}
block[posn++] = input;
++length;
}
@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;
}
}
// 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);
}
private static int rightRotate12(int v)
{
return v << 20 | (v >>> 12);
}
private static int rightRotate8(int v)
{
return v << 24 | (v >>> 8);
}
private static int rightRotate7(int v)
{
return v << 25 | (v >>> 7);
}
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]);
}
@Override
public void destroy() {
Arrays.fill(h, (int)0);
Arrays.fill(block, (byte)0);
Arrays.fill(m, (int)0);
Arrays.fill(v, (int)0);
}
}

View File

@ -0,0 +1,206 @@
/*
* 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.crypto
import com.bitchat.android.noise.southernstorm.protocol.Destroyable
import java.security.DigestException
import java.security.MessageDigest
/**
* Fallback implementation of BLAKE2s for the Noise library.
*
* This implementation only supports message digesting with an output
* length of 32 bytes. Keyed hashing and variable-length digests are
* not supported.
*/
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
init {
engineReset()
}
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
}
@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 fun engineGetDigestLength(): Int = 32
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
}
override fun engineUpdate(input: Byte) {
if (posn >= 64) {
transform(0)
posn = 0
}
block[posn++] = input
++length
}
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
input.copyInto(block, posn, currentOffset, currentOffset + temp)
posn += temp
length += temp.toLong()
currentOffset += temp
remainingLen -= temp
}
}
private fun transform(f0: Int) {
var currentOffset = 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<ByteArray> = 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)
}
}

View File

@ -1,200 +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.crypto;
/**
* Implementation of the ChaCha20 core hash transformation.
*/
public final class ChaChaCore {
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);
// 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];
}
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 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);
}
/**
* 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 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);
}
private static int leftRotate12(int v)
{
return v << 12 | (v >>> 20);
}
private static int leftRotate8(int v)
{
return v << 8 | (v >>> 24);
}
private static int leftRotate7(int v)
{
return v << 7 | (v >>> 25);
}
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]);
}
}

View File

@ -0,0 +1,182 @@
/*
* 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.crypto
/**
* Implementation of the ChaCha20 core hash transformation.
*/
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)
// 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)
}
// Add the input block to the output.
for (index in 0 until 16) {
output[index] += input[index]
}
}
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 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.
*/
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 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
}
/**
* 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()
}
/**
* 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 fun leftRotate16(v: Int): Int = (v shl 16) or (v ushr 16)
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])
}
}

View File

@ -1,531 +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.crypto;
import java.util.Arrays;
/**
* 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 {
// 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;
/**
* 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];
}
/**
* 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);
}
/**
* 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;
}
// 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);
}
/**
* 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;
}
}
// 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;
}
// 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);
}
/**
* 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);
}
/**
* 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);
}
/**
* 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);
}
/**
* 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;
}
// 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;
}
}
/**
* 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.
// 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);
}
// 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);
}
}
/**
* 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);
// 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);
}
/**
* 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;
// 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;
// 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);
// 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;
}
}
// Final conditional swaps.
cswap(swap, x_2, x_3);
cswap(swap, z_2, 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.
*/
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);
}
}
// 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.
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);
// 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 (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();
}
}
}

View File

@ -0,0 +1,482 @@
/*
* 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.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
*/
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)
/**
* 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)
}
/**
* 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
}
// 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)
}
}
/**
* 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
}
}
// 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
}
// 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)
}
/**
* 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]
}
// 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
}
// 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 fun square(result: IntArray, x: IntArray) {
mul(result, x, x)
}
/**
* 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)
}
/**
* 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)
}
/**
* 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
}
// 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
}
/**
* 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.
// 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)
}
// 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)
}
}
/**
* 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)
// 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)
}
/**
* 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
// 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
// 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)
// 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
}
}
// Final conditional swaps.
cswap(swap, x_2, x_3)
cswap(swap, z_2, z_3)
}
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
/**
* 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
}
}
/**
* 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))
}
}
// 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()
}
}
}
}

View File

@ -1,622 +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.
*/
/*
Portions of this code were extracted from the p448/arch_32 field
arithmetic implementation in Ed448-Goldilocks and converted from
C into Java. The LICENSE.txt file for the imported code follows:
----
The MIT License (MIT)
Copyright (c) 2011 Stanford University.
Copyright (c) 2014 Cryptography Research, Inc.
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.crypto;
import java.util.Arrays;
/**
* Implementation of the Curve448 elliptic curve algorithm.
*
* Reference: RFC 7748
*/
public final class Curve448 {
// 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 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);
}
/* Beginning of code imported from Ed448-Goldilocks */
private static long widemul_32(int a, int b)
{
return ((long)a) * b;
}
// p448_mul()
private void mul(int[] c, int[] a, int[] b)
{
long accum0 = 0, accum1 = 0, accum2 = 0;
int mask = (1<<28) - 1;
int i,j;
for (i=0; i<8; i++) {
aa[i] = a[i] + a[i+8];
bb[i] = b[i] + b[i+8];
}
for (j=0; j<8; j++) {
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]);
}
accum1 -= accum2;
accum0 += accum2;
accum2 = 0;
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]);
}
accum1 += accum2;
accum0 += accum2;
c[j] = ((int)(accum0)) & mask;
c[j+8] = ((int)(accum1)) & mask;
accum0 >>>= 28;
accum1 >>>= 28;
}
accum0 += accum1;
accum0 += c[8];
accum1 += c[0];
c[8] = ((int)(accum0)) & mask;
c[0] = ((int)(accum1)) & mask;
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)
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,
followed by another zero bit, and a final one bit.
The naive implementation that squares for every bit and multiplies
for every 1 bit requires 893 multiplications. The following can
do the same operation in 483 multiplications. The basic idea is to
create bit patterns and then "shift" them into position. We start
with a 4 bit pattern 1111, which we can square 4 times to get
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);
}
/**
* 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;
// 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;
// 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;
}
}
// 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;
}
// 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);
// 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 & 0x01) != 0;
}
}

View File

@ -0,0 +1,567 @@
/*
* 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.
*/
/*
Portions of this code were extracted from the p448/arch_32 field
arithmetic implementation in Ed448-Goldilocks and converted from
C into Java. The LICENSE.txt file for the imported code follows:
----
The MIT License (MIT)
Copyright (c) 2011 Stanford University.
Copyright (c) 2014 Cryptography Research, Inc.
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.crypto
/**
* Implementation of the Curve448 elliptic curve algorithm.
*
* Reference: RFC 7748
*/
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)
/**
* 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)
}
// p448_mul()
private fun mul(c: IntArray, a: IntArray, b: IntArray) {
var accum0 = 0L
var accum1 = 0L
val mask = (1 shl 28) - 1
for (i in 0 until 8) {
aa[i] = a[i] + a[i + 8]
bb[i] = b[i] + b[i + 8]
}
for (j in 0 until 8) {
var accum2 = 0L
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])
}
accum1 -= accum2
accum0 += accum2
accum2 = 0
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
c[j] = accum0.toInt() and mask
c[j + 8] = accum1.toInt() and mask
accum0 = accum0 ushr 28
accum1 = accum1 ushr 28
}
accum0 += accum1
accum0 += c[8].toLong()
accum1 += c[0].toLong()
c[8] = accum0.toInt() and mask
c[0] = accum1.toInt() and mask
accum0 = accum0 ushr 28
accum1 = accum1 ushr 28
c[9] += accum0.toInt()
c[1] += accum1.toInt()
}
/* 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)
}
/**
* 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,
followed by another zero bit, and a final one bit.
The naive implementation that squares for every bit and multiplies
for every 1 bit requires 893 multiplications. The following can
do the same operation in 483 multiplications. The basic idea is to
create bit patterns and then "shift" them into position. We start
with a 4 bit pattern 1111, which we can square 4 times to get
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)
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 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.
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)
// 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)
}
companion object {
/* Beginning of code imported from Ed448-Goldilocks */
private fun widemul_32(a: Int, b: Int): Long = (a.toLong()) * b
// 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)
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])
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
}
}
}

View File

@ -1,204 +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.crypto;
import java.util.Arrays;
import com.bitchat.android.noise.southernstorm.protocol.Destroyable;
/**
* Implementation of the GHASH primitive for GCM.
*/
public final class GHASH implements Destroyable {
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.
*/
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.
*/
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.
*/
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;
}
}
/**
* 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);
}
}
@Override
public void destroy() {
Arrays.fill(H, 0L);
Arrays.fill(Y, (byte)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);
}
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;
}
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];
// 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));
// XOR V with Z if the bit is 1.
Z0 ^= (V0 & mask);
Z1 ^= (V1 & mask);
// Rotate V right by 1 bit.
mask = ((~(V1 & 0x01)) + 1) & 0xE100000000000000L;
V1 = (V1 >>> 1) | (V0 << 63);
V0 = (V0 >>> 1) ^ mask;
}
}
// We have finished the block so copy Z into Y and byte-swap.
writeBigEndian(Y, 0, Z0);
writeBigEndian(Y, 8, Z1);
}
}

View File

@ -0,0 +1,184 @@
/*
* 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.crypto
import com.bitchat.android.noise.southernstorm.protocol.Destroyable
/**
* Implementation of the GHASH primitive for GCM.
*/
class GHASH : Destroyable {
private val h = LongArray(2)
private val y = ByteArray(16)
private var posn: Int = 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 the GHASH object but retains the previous key.
*/
fun reset() {
y.fill(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
}
}
}
/**
* 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.
*/
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
}
}
/**
* 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)
}
}
override fun destroy() {
h.fill(0L)
y.fill(0)
}
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 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()
}
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]
// 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())
// XOR V with Z if the bit is 1.
z0 = z0 xor (v0 and mask)
z1 = z1 xor (v1 and mask)
// 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)
}
}
}

View File

@ -0,0 +1,831 @@
/*
* Based on the public domain C reference code for New Hope.
* This Java version is also placed into the public domain.
*
* 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.security.SecureRandom
/**
* NewHope key exchange algorithm.
*
* This class implements the standard "ref" version of the New Hope
* algorithm.
*
* @see NewHopeTor
*/
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())
}
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()
}
}
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)
var m = t0 - PARAM_Q
var mask = m shr 15
t0 = m xor ((t0 xor m) and mask)
m = t1 - PARAM_Q
mask = m shr 15
t1 = m xor ((t1 xor m) and mask)
m = t2 - PARAM_Q
mask = m shr 15
t2 = m xor ((t2 xor m) and mask)
m = t3 - PARAM_Q
mask = m shr 15
t3 = m xor ((t3 xor m) and mask)
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()
}
}
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
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
coeffs[i] = (a + PARAM_Q - b).toChar()
}
} finally {
buf.fill(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()
}
}
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()
}
}
fun ntt() {
mul_coefficients(coeffs, psis_bitrev_montgomery)
ntt_global(coeffs, omegas_montgomery)
}
fun invntt() {
bitrev_vector(coeffs)
ntt_global(coeffs, omegas_inv_montgomery)
mul_coefficients(coeffs, psis_inv_montgomery)
}
}
/**
* 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)
try {
shake128_absorb(state, seed, 0, SEEDBYTES)
shake128_squeezeblocks(buf, 0, nblocks, state)
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)
}
}
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
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 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)
}
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()
}
}
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()
}
}
private fun montgomery_reduce(a: Int): Int {
val u = (a * 12287) and ((1 shl 18) - 1)
return (a + u * PARAM_Q) ushr 18
}
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
}
// -------------- error_correction.c --------------
private fun abs(v: Int): Int {
val mask = v shr 31
return (v xor mask) - mask
}
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 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 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
)
}
}

View File

@ -1,982 +0,0 @@
/*
* Based on the public domain C reference code for New Hope.
* This Java version is also placed into the public domain.
*
* 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;
/**
* 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
*/
public class NewHopeTor extends NewHope {
public NewHopeTor() {}
@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 (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);
}
}
private static boolean discardtopoly(char[] coeffs, char[] x)
{
int i, r=0;
for(i=0;i<16;i++)
batcher84(x, i);
// Check whether we're safe:
for(i=1008;i<1024;i++)
r |= 61444 - x[i];
if((r >>= 31) != 0) return true;
// If we are, copy coefficients to polynomial:
for(i=0;i<PARAM_N;i++)
coeffs[i] = x[i];
return false;
}
private static void batcher84(char[] x, int offset)
{
int c, t;
c = 61444 - x[offset + 0]; t = (x[offset + 0] ^ x[offset + 16]) & (c >> 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;
}
}

View File

@ -0,0 +1,146 @@
/*
* Based on the public domain C reference code for New Hope.
* This Java version is also placed into the public domain.
*
* Original authors: Erdem Alkim, Léo Ducas, Thomas Pöppelmann, Peter Schwabe
* Java port: Rhys Weatherley
*/
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.
*/
open class NewHopeTor : NewHope() {
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)
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())
}
}
companion object {
private fun discardtopoly(coeffs: CharArray, x: CharArray): Boolean {
for (i in 0 until 16) {
batcher84(x, i)
}
// 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
// If we are, copy coefficients to polynomial
x.copyInto(coeffs, destinationOffset = 0, startIndex = 0, endIndex = PARAM_N)
return false
}
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()
}
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)
}
}
}

View File

@ -1,327 +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.crypto;
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 {
// 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.
*/
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);
}
/**
* 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;
}
}
}
}
/**
* 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.
// 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);
// 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];
// 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;
}
}
/**
* 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;
}
@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);
}
}

View File

@ -0,0 +1,317 @@
/*
* 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.crypto
import com.bitchat.android.noise.southernstorm.protocol.Destroyable
/**
* Simple implementation of the Poly1305 message authenticator.
*/
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
/**
* 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) {
// Kotlin KMP Array Copy
key.copyInto(destination = nonce, destinationOffset = 0, startIndex = offset + 16, endIndex = offset + 32)
h.fill(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].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)
}
/**
* 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
}
}
}
}
/**
* 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
}
}
/**
* 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)
}
// 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.
// 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)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,244 +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.crypto;
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 {
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));
}
private void transform(byte[] m, int offset)
{
int a, b, c, d, e, f, g, h;
int temp1, temp2;
int index;
// 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;
}
// 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;
}
}

View File

@ -0,0 +1,225 @@
/*
* 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.crypto
import com.bitchat.android.noise.southernstorm.protocol.Destroyable
import java.security.DigestException
import java.security.MessageDigest
/**
* Fallback implementation of SHA256.
*/
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
init {
engineReset()
}
override fun destroy() {
h.fill(0)
block.fill(0.toByte())
w.fill(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.toByte())
}
return digest
}
@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))
}
}
}

View File

@ -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.crypto;
import java.security.DigestException;
import java.security.MessageDigest;
import java.util.Arrays;
import com.bitchat.android.noise.southernstorm.protocol.Destroyable;
/**
* Fallback implementation of SHA512.
*
* 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 {
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));
}
private void transform(byte[] m, int offset)
{
long a, b, c, d, e, f, g, h;
long temp1, temp2;
int index;
// 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;
}
}

View File

@ -0,0 +1,243 @@
/*
* 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.crypto
import com.bitchat.android.noise.southernstorm.protocol.Destroyable
import java.security.DigestException
import java.security.MessageDigest
/**
* Fallback implementation of SHA512.
*
* 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.
*/
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
init {
engineReset()
}
override fun destroy() {
h.fill(0L)
block.fill(0.toByte())
w.fill(0L)
}
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))
}
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -0,0 +1,254 @@
/*
* 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 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 = 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()
ghash.destroy()
destroy(hashKey)
destroy(iv)
destroy(enciv)
}
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.
aes.setupEnc(key, offset, 256)
haskey = true
// Generate the hashing key by encrypting a block of zeroes.
hashKey.fill(0)
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.
hashKey.fill(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.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 (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)
// XOR the keystream block with the plaintext to create the ciphertext.
var temp = length
if (temp > 16) temp = 16
for (index in 0..<temp) ciphertext[ciphertextOffset + index] =
(plaintext[plaintextOffset + index].toInt() xor enciv[index].toInt()).toByte()
// Advance to the next block.
plaintextOffset += temp
ciphertextOffset += temp
length -= temp
}
}
@Throws(ShortBufferException::class)
override fun encryptWithAd(
ad: ByteArray?, plaintext: ByteArray, plaintextOffset: Int,
ciphertext: ByteArray, ciphertextOffset: Int, length: Int
): Int {
require(!(ciphertextOffset < 0 || ciphertextOffset > ciphertext.size))
require(!(length < 0 || plaintextOffset < 0 || plaintextOffset > plaintext.size || length > plaintext.size || (plaintext.size - plaintextOffset) < length))
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(
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()).toByte()
}
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 = AESGCMFallbackCipherState()
cipher.initializeKey(key, offset)
return cipher
}
override fun setNonce(nonce: Long) {
n = nonce
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,315 @@
/*
* 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 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
@Throws(NoSuchAlgorithmException::class)
constructor() : CipherState {
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)
private val hashKey: ByteArray = ByteArray(16)
private val ghash: GHASH = GHASH()
init {
// 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 (_: InvalidKeyException) {
// Shouldn't happen.
} catch (_: InvalidAlgorithmParameterException) {
// Shouldn't happen.
}
}
override val cipherName: String get() = "AESGCM"
override val keyLength: Int get() = 32
override 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.
iv.fill(0)
hashKey.fill(0)
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.
hashKey.fill(0)
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 {
require(!(ciphertextOffset < 0 || ciphertextOffset > ciphertext.size))
require(!(length < 0 || plaintextOffset < 0 || plaintextOffset > plaintext.size || length > plaintext.size || (plaintext.size - plaintextOffset) < length))
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(
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()).toByte()
}
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((ad?.size ?: 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 {
AESGCMOnCtrCipherState()
} catch (e: NoSuchAlgorithmException) {
// Shouldn't happen — this state already exists, so the algorithm is available.
throw IllegalStateException(e)
}
cipher.initializeKey(key, offset)
return cipher
}
override fun setNonce(nonce: Long) {
n = nonce
}
}

View File

@ -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.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.initIV(input, n++);
ChaChaCore.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.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;
}
}

View File

@ -0,0 +1,282 @@
/*
* 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 javax.crypto.BadPaddingException
import javax.crypto.ShortBufferException
/**
* Implements the ChaChaPoly cipher for Noise.
*/
internal class ChaChaPolyCipherState : CipherState {
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
override fun destroy() {
poly.destroy()
input.fill(0)
output.fill(0)
destroy(polyKey)
}
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)
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)
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]
}
/**
* 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, (ad?.size ?: 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 {
require(!(ciphertextOffset < 0 || ciphertextOffset > ciphertext.size))
require(!(length < 0 || plaintextOffset < 0 || plaintextOffset > plaintext.size || length > plaintext.size || (plaintext.size - plaintextOffset) < length))
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(
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 {
require(!(ciphertextOffset < 0 || ciphertextOffset > ciphertext.size))
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(
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
}
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()
}
}
}
/**
* 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()
}
}
}

View File

@ -1,162 +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 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 {
/**
* 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.
*/
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();
/**
* 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);
/**
* 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;
/**
* 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;
/**
* 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);
}

View File

@ -0,0 +1,176 @@
/*
* 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 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.
*/
interface CipherState : Destroyable {
/**
* Gets the Noise protocol name for this cipher.
*
* @return The cipher name.
*/
val cipherName: String
/**
* 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 MAC values for this cipher.
*
* @return The length of MAC values in bytes, or zero if the
* key has not yet been initialized.
*/
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
*/
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
*/
fun hasKey(): Boolean
/**
* 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
/**
* 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)
}

View File

@ -1,106 +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;
/**
* Class that contains a pair of CipherState objects.
*
* CipherState pairs typically arise when HandshakeState.split() is called.
*/
public final class CipherStatePair implements Destroyable {
private CipherState send;
private CipherState recv;
/**
* 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;
}
/**
* 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;
}
/**
* 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();
}
}

View File

@ -0,0 +1,68 @@
/*
* 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
/**
* 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.
*/
class CipherStatePair(
var sender: CipherState?,
var receiver: CipherState?,
) : Destroyable {
/**
* 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
}
/**
* 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
}
/**
* Swaps the sender and receiver.
*/
fun swap() {
sender = receiver.also { receiver = sender }
}
override fun destroy() {
senderOnly()
receiverOnly()
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,106 @@
/*
* 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
/**
* Implementation of the Curve25519 algorithm for the Noise protocol.
*/
internal class Curve25519DHState : DHState {
private val publicKey: ByteArray = ByteArray(32)
private val privateKey: ByteArray = ByteArray(32)
private var mode = 0
override fun destroy() {
clearKey()
}
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)
Curve25519.eval(publicKey, 0, privateKey, null)
mode = 0x03
}
override fun getPublicKey(key: ByteArray, offset: Int) {
publicKey.copyInto(key, offset, 0, 32)
}
override fun setPublicKey(key: ByteArray, offset: Int) {
key.copyInto(publicKey, 0, offset, offset + 32)
privateKey.fill(0)
mode = 0x01
}
override fun getPrivateKey(key: ByteArray, offset: Int) {
privateKey.copyInto(key, offset, 0, 32)
}
override fun setPrivateKey(key: ByteArray, offset: Int) {
key.copyInto(privateKey, 0, offset, offset + 32)
Curve25519.eval(publicKey, 0, privateKey, null)
mode = 0x03
}
override fun setToNullPublicKey() {
publicKey.fill(0)
privateKey.fill(0)
mode = 0x01
}
override fun clearKey() {
destroy(publicKey)
destroy(privateKey)
mode = 0
}
override fun hasPublicKey(): Boolean = (mode and 0x01) != 0
override fun hasPrivateKey(): Boolean = (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
other.privateKey.copyInto(privateKey, 0, 0, 32)
other.publicKey.copyInto(publicKey, 0, 0, 32)
mode = other.mode
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,106 @@
/*
* 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
/**
* Implementation of the Curve448 algorithm for the Noise protocol.
*/
internal class Curve448DHState : DHState {
private val publicKey: ByteArray = ByteArray(56)
private val privateKey: ByteArray = ByteArray(56)
private var mode = 0
override fun destroy() {
clearKey()
}
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)
Curve448.eval(publicKey, 0, privateKey, null)
mode = 0x03
}
override fun getPublicKey(key: ByteArray, offset: Int) {
publicKey.copyInto(key, offset, 0, 56)
}
override fun setPublicKey(key: ByteArray, offset: Int) {
key.copyInto(publicKey, 0, offset, offset + 56)
privateKey.fill(0)
mode = 0x01
}
override fun getPrivateKey(key: ByteArray, offset: Int) {
privateKey.copyInto(key, offset, 0, 56)
}
override fun setPrivateKey(key: ByteArray, offset: Int) {
key.copyInto(privateKey, 0, offset, offset + 56)
Curve448.eval(publicKey, 0, privateKey, null)
mode = 0x03
}
override fun setToNullPublicKey() {
publicKey.fill(0)
privateKey.fill(0)
mode = 0x01
}
override fun clearKey() {
destroy(publicKey)
destroy(privateKey)
mode = 0
}
override fun hasPublicKey(): Boolean = (mode and 0x01) != 0
override fun hasPrivateKey(): Boolean = (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
other.privateKey.copyInto(privateKey, 0, 0, 56)
other.publicKey.copyInto(publicKey, 0, 0, 56)
mode = other.mode
}
}

View File

@ -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);
}

View File

@ -0,0 +1,129 @@
/*
* 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 {
/** The Noise protocol name for this Diffie-Hellman algorithm. */
val dhName: String
/** Length of public keys for this algorithm, in bytes. */
val publicKeyLength: Int
/** Length of private keys for this algorithm, in bytes. */
val privateKeyLength: Int
/** Length of shared keys for this algorithm, 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 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)
/**
* 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.
*/
fun hasPublicKey(): Boolean
/**
* Determine if this object contains a private key.
*/
fun hasPrivateKey(): Boolean
/**
* Determine if the public key in this object is the special null value.
*/
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)
/**
* 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)
}

View File

@ -19,42 +19,43 @@
* 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);
// 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.
*
* @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);
/**
* 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?)
/**
* 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?)
}

View File

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

View File

@ -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;
}
}

View File

@ -0,0 +1,238 @@
/*
* 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
/**
* 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 = KeyType.None
/**
* Special version of NewHopeTor that allows explicit random data
* to be specified for test vectors.
*/
private inner class NewHopeWithPrivateKey(private val randomData: ByteArray) : NewHopeTor() {
override fun randombytes(buffer: ByteArray) {
randomData.copyInto(buffer, 0, 0, buffer.size)
}
}
private val isAlice: Boolean
get() = keyType == KeyType.AlicePrivate || keyType == KeyType.AlicePublic
override fun destroy() {
clearKey()
}
override val dhName: String get() = "NewHope"
override val publicKeyLength: Int
get() = if (isAlice) NewHope.SENDABYTES else NewHope.SENDBBYTES
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.
get() = if (isAlice) 64 else 32
override val sharedKeyLength: Int get() = 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
}
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, remote.publicKey!!, 0)
} else {
generateKeyPair()
}
}
override fun getPublicKey(key: ByteArray, offset: Int) {
val pk = publicKey
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
publicKey = key.copyOfRange(offset, offset + len)
}
override fun getPrivateKey(key: ByteArray, offset: Int) {
val sk = privateKey
if (sk != null) sk.copyInto(key, offset, 0, 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.
keyType = if (offset == 0 && key.size == 64) KeyType.AlicePrivate else KeyType.BobPrivate
val len = privateKeyLength
privateKey = key.copyOfRange(offset, offset + len)
}
override fun setToNullPublicKey() {
// Null public keys are not supported by New Hope.
// Destroy the current values but otherwise ignore.
clearKey()
}
override fun clearKey() {
nh?.let {
it.destroy()
nh = null
}
publicKey?.let {
Noise.destroy(it)
publicKey = null
}
privateKey?.let {
Noise.destroy(it)
privateKey = null
}
keyType = KeyType.None
}
override fun hasPublicKey(): Boolean = publicKey != null
override fun hasPrivateKey(): Boolean = privateKey != null
override fun isNullPublicKey(): Boolean = false
override fun calculate(sharedKey: ByteArray, offset: Int, publicDH: DHState) {
require(publicDH is NewHopeDHState) { "Incompatible DH algorithms" }
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.
privateKey!!.copyInto(sharedKey, 0, 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
clearKey()
when (other.keyType) {
KeyType.None -> {}
KeyType.AlicePrivate -> {
val src = other.privateKey ?: throw IllegalStateException("Cannot copy generated key for Alice")
keyType = KeyType.AlicePrivate
privateKey = src.copyOf()
}
KeyType.BobPrivate, KeyType.BobCalculated ->
throw IllegalStateException("Cannot copy private key for Bob without public key for Alice")
KeyType.AlicePublic, KeyType.BobPublic -> {
keyType = other.keyType
publicKey = other.publicKey!!.copyOf()
}
}
}
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
clearKey()
when (other.keyType) {
KeyType.None -> {}
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(src)
publicKey = ByteArray(NewHope.SENDABYTES)
nh!!.keygen(publicKey!!, 0)
}
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 = other.keyType
publicKey = other.publicKey!!.copyOf()
}
}
}
override fun specifyPeer(local: DHState?) {
if (local !is NewHopeDHState) return
clearKey()
keyType = if (local.keyType == KeyType.AlicePrivate) KeyType.BobPublic else KeyType.AlicePublic
}
}

View File

@ -1,237 +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.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;
/**
* Utility functions for the Noise protocol library.
*/
public final class Noise {
/**
* 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 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;
}
/**
* 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);
}
/**
* 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);
}
// 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.
*/
static void destroy(byte[] array)
{
Arrays.fill(array, (byte)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.
*/
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();
}
}

View File

@ -0,0 +1,200 @@
/*
* 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.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.
*/
object Noise {
/**
* Maximum length for Noise packets.
*/
const val MAX_PACKET_LEN: Int = 65535
private val random = SecureRandom()
private var forceFallbacks = false
/**
* 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)
}
/**
* 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
}
/**
* 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")
}
/**
* 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")
}
/**
* 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()
}
}

View File

@ -1,835 +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;
/**
* 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));
}
}

View File

@ -0,0 +1,287 @@
/*
* 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
/**
* Information about all supported handshake patterns.
*/
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<String, ShortArray> = 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()
}
}

View File

@ -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.getMACLength();
}
/**
* 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.getKeyLength();
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.getMACLength() > 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.getKeyLength();
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);
}
}
}

View File

@ -0,0 +1,565 @@
/*
* 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 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 prevH: 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!!.digestLength
ck = ByteArray(hashLength)
val initialHash = ByteArray(hashLength)
this.handshakeHash = initialHash
prevH = ByteArray(hashLength)
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) {
protocolNameBytes.copyInto(initialHash, 0, 0, protocolNameBytes.size)
initialHash.fill(0, protocolNameBytes.size, initialHash.size)
} else {
hashOne(
protocolNameBytes, 0, protocolNameBytes.size,
initialHash, 0, initialHash.size
)
}
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): ${bytesToHex(initialHash)}")
Log.d(TAG, "Initial chaining key (ck): ${bytesToHex(ck!!)}")
Log.d(TAG, "Hash length: ${initialHash.size}")
Log.d(TAG, "=========================================")
}
/**
* 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.
*
* @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)
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(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: ${bytesToHex(ck!!)}")
Log.d(TAG, "Hash unchanged: ${bytesToHex(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)
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(handshakeHash!!)}")
val h = handshakeHash!!
hashTwo(
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: ${bytesToHex(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!!.digestLength)
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.publicKeyLength)
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.publicKeyLength)
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 {
val h = handshakeHash!!
h.copyInto(prevH!!, 0, 0, h.size)
mixHash(ciphertext, ciphertextOffset, length)
return cipher!!.decryptWithAd(
prevH,
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.
*/
@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 {
// 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
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.
c1?.destroy()
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 (prevH != null) {
Noise.destroy(prevH!!)
prevH = 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 (_: DigestException) {
output.fill(0, outputOffset, outputLength)
}
}
/**
* 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 (_: DigestException) {
output.fill(0, outputOffset, outputLength)
}
}
/**
* 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!!.digestLength
val blockLength = hashLength * 2
val block = ByteArray(blockLength)
try {
if (keyLength <= blockLength) {
System.arraycopy(key, keyOffset, block, 0, keyLength)
block.fill(0, keyLength, blockLength)
} else {
hash!!.reset()
hash!!.update(key, keyOffset, keyLength)
hash!!.digest(block, 0, hashLength)
block.fill(0, hashLength, blockLength)
}
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)
for (i in 0 until blockLength) {
block[i] = (block[i].toInt() xor (0x36 xor 0x5C)).toByte()
}
hash!!.reset()
hash!!.update(block, 0, blockLength)
hash!!.update(output, outputOffset, hashLength)
hash!!.digest(output, outputOffset, outputLength)
} catch (_: DigestException) {
output.fill(0, outputOffset, outputLength)
} 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!!.digestLength
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("%02x".format(b))
}
return sb.toString()
}
}
}

View File

@ -1,7 +0,0 @@
/**
* Provides classes for communicating via the Noise protocol.
*
* Reference: http://noiseprotocol.org
*/
package com.bitchat.android.noise.southernstorm.protocol;