Refactor: Convert SHA-256 and SHA-512 to pure Kotlin for KMP

- Translated `SHA256MessageDigest` and `SHA512MessageDigest` to Kotlin.
- Removed `java.util.Arrays` and `System.arraycopy` dependencies, using
  Kotlin's native `.fill()` and `.copyInto()`.
- Refactored legacy `while` loops into idiomatic Kotlin `for` loops.
- Improved variable scoping in the `transform` function by declaring
  local variables at the point of use.
This commit is contained in:
yet300 2026-05-08 17:19:07 +04:00
parent c11f5258e3
commit 544e51ff6b
4 changed files with 400 additions and 1552 deletions

View File

@ -19,226 +19,207 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.bitchat.android.noise.southernstorm.crypto
package com.bitchat.android.noise.southernstorm.crypto;
import com.bitchat.android.noise.southernstorm.protocol.Destroyable
import java.security.DigestException
import java.security.MessageDigest
import java.security.DigestException;
import java.security.MessageDigest;
import java.util.Arrays;
import com.bitchat.android.noise.southernstorm.protocol.Destroyable;
/**
* Fallback implementation of SHA256.
*/
public class SHA256MessageDigest extends MessageDigest implements Destroyable {
class SHA256MessageDigest : MessageDigest("SHA-256"), Destroyable {
private val h = IntArray(8)
private val block = ByteArray(64)
private val w = IntArray(64)
private var length: Long = 0
private var posn = 0
private int[] h;
private byte[] block;
private int[] w;
private long length;
private int posn;
/**
* Constructs a new SHA256 message digest object.
*/
public SHA256MessageDigest() {
super("SHA-256");
h = new int [8];
block = new byte [64];
w = new int [64];
engineReset();
}
@Override
public void destroy() {
Arrays.fill(h, (int)0);
Arrays.fill(block, (byte)0);
Arrays.fill(w, (int)0);
}
private static void writeBE32(byte[] buf, int offset, int value)
{
buf[offset] = (byte)(value >> 24);
buf[offset + 1] = (byte)(value >> 16);
buf[offset + 2] = (byte)(value >> 8);
buf[offset + 3] = (byte)value;
}
@Override
protected byte[] engineDigest() {
byte[] digest = new byte [32];
try {
engineDigest(digest, 0, 32);
} catch (DigestException e) {
// Shouldn't happen, but just in case.
Arrays.fill(digest, (byte)0);
}
return digest;
}
@Override
protected int engineDigest(byte[] buf, int offset, int len) throws DigestException
{
if (len < 32)
throw new DigestException("Invalid digest length for SHA256");
if (posn <= (64 - 9)) {
block[posn] = (byte)0x80;
Arrays.fill(block, posn + 1, 64 - 8, (byte)0);
} else {
block[posn] = (byte)0x80;
Arrays.fill(block, posn + 1, 64, (byte)0);
transform(block, 0);
Arrays.fill(block, 0, 64 - 8, (byte)0);
}
writeBE32(block, 64 - 8, (int)(length >> 32));
writeBE32(block, 64 - 4, (int)length);
transform(block, 0);
posn = 0;
for (int index = 0; index < 8; ++index)
writeBE32(buf, offset + index * 4, h[index]);
return 32;
}
@Override
protected int engineGetDigestLength() {
return 32;
}
@Override
protected void engineReset() {
h[0] = 0x6A09E667;
h[1] = 0xBB67AE85;
h[2] = 0x3C6EF372;
h[3] = 0xA54FF53A;
h[4] = 0x510E527F;
h[5] = 0x9B05688C;
h[6] = 0x1F83D9AB;
h[7] = 0x5BE0CD19;
length = 0;
posn = 0;
}
@Override
protected void engineUpdate(byte input) {
block[posn++] = input;
length += 8;
if (posn >= 64) {
transform(block, 0);
posn = 0;
}
}
@Override
protected void engineUpdate(byte[] input, int offset, int len) {
while (len > 0) {
if (posn == 0 && len >= 64) {
transform(input, offset);
offset += 64;
len -= 64;
length += 64 * 8;
} else {
int temp = 64 - posn;
if (temp > len)
temp = len;
System.arraycopy(input, offset, block, posn, temp);
posn += temp;
length += temp * 8;
if (posn >= 64) {
transform(block, 0);
posn = 0;
}
offset += temp;
len -= temp;
}
}
}
private static final int[] k = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
private static int rightRotate(int value, int n)
{
return (value >>> n) | (value << (32 - n));
init {
engineReset()
}
private void transform(byte[] m, int offset)
{
int a, b, c, d, e, f, g, h;
int temp1, temp2;
int index;
override fun destroy() {
h.fill(0)
block.fill(0.toByte())
w.fill(0)
}
// Initialize working variables to the current hash value.
a = this.h[0];
b = this.h[1];
c = this.h[2];
d = this.h[3];
e = this.h[4];
f = this.h[5];
g = this.h[6];
h = this.h[7];
// Convert the 16 input message words from big endian to host byte order.
for (index = 0; index < 16; ++index) {
w[index] = ((m[offset] & 0xFF) << 24) |
((m[offset + 1] & 0xFF) << 16) |
((m[offset + 2] & 0xFF) << 8) |
(m[offset + 3] & 0xFF);
offset += 4;
}
// Extend the first 16 words to 64.
for (index = 16; index < 64; ++index) {
w[index] = w[index - 16] + w[index - 7] +
(rightRotate(w[index - 15], 7) ^
rightRotate(w[index - 15], 18) ^
(w[index - 15] >>> 3)) +
(rightRotate(w[index - 2], 17) ^
rightRotate(w[index - 2], 19) ^
(w[index - 2] >>> 10));
}
// Compression function main loop.
for (index = 0; index < 64; ++index) {
temp1 = (h) + k[index] + w[index] +
(rightRotate((e), 6) ^ rightRotate((e), 11) ^ rightRotate((e), 25)) +
(((e) & (f)) ^ ((~(e)) & (g)));
temp2 = (rightRotate((a), 2) ^ rightRotate((a), 13) ^ rightRotate((a), 22)) +
(((a) & (b)) ^ ((a) & (c)) ^ ((b) & (c)));
h = g;
g = f;
f = e;
e = d + temp1;
d = c;
c = b;
b = a;
a = temp1 + temp2;
}
override fun engineDigest(): ByteArray {
val digest = ByteArray(32)
try {
engineDigest(digest, 0, 32)
} catch (e: DigestException) {
// Shouldn't happen, but just in case.
digest.fill(0.toByte())
}
return digest
}
// Add the compressed chunk to the current hash value.
this.h[0] += a;
this.h[1] += b;
this.h[2] += c;
this.h[3] += d;
this.h[4] += e;
this.h[5] += f;
this.h[6] += g;
this.h[7] += h;
}
@Throws(DigestException::class)
override fun engineDigest(buf: ByteArray, offset: Int, len: Int): Int {
if (len < 32) throw DigestException("Invalid digest length for SHA256")
if (posn <= (64 - 9)) {
block[posn] = 0x80.toByte()
block.fill(0.toByte(), posn + 1, 64 - 8)
} else {
block[posn] = 0x80.toByte()
block.fill(0.toByte(), posn + 1, 64)
transform(block, 0)
block.fill(0.toByte(), 0, 64 - 8)
}
writeBE32(block, 64 - 8, (length shr 32).toInt())
writeBE32(block, 64 - 4, length.toInt())
transform(block, 0)
posn = 0
for (index in 0..7) writeBE32(buf, offset + index * 4, h[index])
return 32
}
override fun engineGetDigestLength(): Int {
return 32
}
override fun engineReset() {
h[0] = 0x6A09E667
h[1] = -0x4498517b
h[2] = 0x3C6EF372
h[3] = -0x5ab00ac6
h[4] = 0x510E527F
h[5] = -0x64fa9774
h[6] = 0x1F83D9AB
h[7] = 0x5BE0CD19
length = 0
posn = 0
}
override fun engineUpdate(input: Byte) {
block[posn++] = input
length += 8
if (posn >= 64) {
transform(block, 0)
posn = 0
}
}
override fun engineUpdate(input: ByteArray, offset: Int, len: Int) {
var offset = offset
var len = len
while (len > 0) {
if (posn == 0 && len >= 64) {
transform(input, offset)
offset += 64
len -= 64
length += (64 * 8).toLong()
} else {
var temp = 64 - posn
if (temp > len) temp = len
System.arraycopy(input, offset, block, posn, temp)
posn += temp
length += (temp * 8).toLong()
if (posn >= 64) {
transform(block, 0)
posn = 0
}
offset += temp
len -= temp
}
}
}
private fun transform(m: ByteArray, offset: Int) {
var currentOffset = offset
// Initialize working variables to the current hash value.
var a = this.h[0]
var b = this.h[1]
var c = this.h[2]
var d = this.h[3]
var e = this.h[4]
var f = this.h[5]
var g = this.h[6]
var h = this.h[7]
// Convert the 16 input message words from big endian to host byte order.
for (index in 0 until 16) {
w[index] = ((m[currentOffset].toInt() and 0xFF) shl 24) or
((m[currentOffset + 1].toInt() and 0xFF) shl 16) or
((m[currentOffset + 2].toInt() and 0xFF) shl 8) or
(m[currentOffset + 3].toInt() and 0xFF)
currentOffset += 4
}
// Extend the first 16 words to 64.
for (index in 16 until 64) {
w[index] = w[index - 16] + w[index - 7] +
(rightRotate(w[index - 15], 7) xor
rightRotate(w[index - 15], 18) xor
(w[index - 15] ushr 3)) +
(rightRotate(w[index - 2], 17) xor
rightRotate(w[index - 2], 19) xor
(w[index - 2] ushr 10))
}
// Compression function main loop.
for (index in 0 until 64) {
val temp1 = h + k[index] + w[index] +
(rightRotate(e, 6) xor rightRotate(e, 11) xor rightRotate(e, 25)) +
((e and f) xor (e.inv() and g))
val temp2 = (rightRotate(a, 2) xor rightRotate(a, 13) xor rightRotate(a, 22)) +
((a and b) xor (a and c) xor (b and c))
h = g
g = f
f = e
e = d + temp1
d = c
c = b
b = a
a = temp1 + temp2
}
// Add the compressed chunk to the current hash value.
this.h[0] += a
this.h[1] += b
this.h[2] += c
this.h[3] += d
this.h[4] += e
this.h[5] += f
this.h[6] += g
this.h[7] += h
}
companion object {
private fun writeBE32(buf: ByteArray, offset: Int, value: Int) {
buf[offset] = (value shr 24).toByte()
buf[offset + 1] = (value shr 16).toByte()
buf[offset + 2] = (value shr 8).toByte()
buf[offset + 3] = value.toByte()
}
private val k = intArrayOf(
0x428a2f98, 0x71374491, -0x4a3f0431, -0x164a245b,
0x3956c25b, 0x59f111f1, -0x6dc07d5c, -0x54e3a12b,
-0x27f85568, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, -0x7f214e02, -0x6423f959, -0x3e640e8c,
-0x1b64963f, -0x1041b87a, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
-0x67c1aeae, -0x57ce3993, -0x4ffcd838, -0x40a68039,
-0x391ff40d, -0x2a586eb9, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, -0x7e3d36d2, -0x6d8dd37b,
-0x5d40175f, -0x57e599b5, -0x3db47490, -0x3893ae5d,
-0x2e6d17e7, -0x2966f9dc, -0xbf1ca7b, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, -0x7b3787ec, -0x7338fdf8,
-0x6f410006, -0x5baf9315, -0x41065c09, -0x398e870e
)
private fun rightRotate(value: Int, n: Int): Int {
return (value ushr n) or (value shl (32 - n))
}
}
}

View File

@ -19,14 +19,11 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.bitchat.android.noise.southernstorm.crypto
package com.bitchat.android.noise.southernstorm.crypto;
import java.security.DigestException;
import java.security.MessageDigest;
import java.util.Arrays;
import com.bitchat.android.noise.southernstorm.protocol.Destroyable;
import com.bitchat.android.noise.southernstorm.protocol.Destroyable
import java.security.DigestException
import java.security.MessageDigest
/**
* Fallback implementation of SHA512.
@ -34,232 +31,213 @@ import com.bitchat.android.noise.southernstorm.protocol.Destroyable;
* Note: This implementation is limited to a maximum 2^56 - 1 bytes of input.
* That is, we don't bother trying to implement 128-bit length values.
*/
public class SHA512MessageDigest extends MessageDigest implements Destroyable {
class SHA512MessageDigest : MessageDigest("SHA-512"), Destroyable {
private val h = LongArray(8)
private val block = ByteArray(128)
private val w = LongArray(80)
private var length: Long = 0
private var posn = 0
private long[] h;
private byte[] block;
private long[] w;
private long length;
private int posn;
/**
* Constructs a new SHA512 message digest object.
*/
public SHA512MessageDigest() {
super("SHA-512");
h = new long [8];
block = new byte [128];
w = new long [80];
engineReset();
}
@Override
public void destroy() {
Arrays.fill(h, (long)0);
Arrays.fill(block, (byte)0);
Arrays.fill(w, (long)0);
}
private static void writeBE64(byte[] buf, int offset, long value)
{
buf[offset] = (byte)(value >> 56);
buf[offset + 1] = (byte)(value >> 48);
buf[offset + 2] = (byte)(value >> 40);
buf[offset + 3] = (byte)(value >> 32);
buf[offset + 4] = (byte)(value >> 24);
buf[offset + 5] = (byte)(value >> 16);
buf[offset + 6] = (byte)(value >> 8);
buf[offset + 7] = (byte)value;
}
@Override
protected byte[] engineDigest() {
byte[] digest = new byte [64];
try {
engineDigest(digest, 0, 64);
} catch (DigestException e) {
// Shouldn't happen, but just in case.
Arrays.fill(digest, (byte)0);
}
return digest;
}
@Override
protected int engineDigest(byte[] buf, int offset, int len) throws DigestException
{
if (len < 64)
throw new DigestException("Invalid digest length for SHA512");
if (posn <= (128 - 17)) {
block[posn] = (byte)0x80;
Arrays.fill(block, posn + 1, 128 - 8, (byte)0);
} else {
block[posn] = (byte)0x80;
Arrays.fill(block, posn + 1, 128, (byte)0);
transform(block, 0);
Arrays.fill(block, 0, 128 - 8, (byte)0);
}
writeBE64(block, 128 - 8, length);
transform(block, 0);
posn = 0;
for (int index = 0; index < 8; ++index)
writeBE64(buf, offset + index * 8, h[index]);
return 64;
}
@Override
protected int engineGetDigestLength() {
return 64;
}
@Override
protected void engineReset() {
h[0] = 0x6a09e667f3bcc908L;
h[1] = 0xbb67ae8584caa73bL;
h[2] = 0x3c6ef372fe94f82bL;
h[3] = 0xa54ff53a5f1d36f1L;
h[4] = 0x510e527fade682d1L;
h[5] = 0x9b05688c2b3e6c1fL;
h[6] = 0x1f83d9abfb41bd6bL;
h[7] = 0x5be0cd19137e2179L;
length = 0;
posn = 0;
}
@Override
protected void engineUpdate(byte input) {
block[posn++] = input;
length += 8;
if (posn >= 128) {
transform(block, 0);
posn = 0;
}
}
@Override
protected void engineUpdate(byte[] input, int offset, int len) {
while (len > 0) {
if (posn == 0 && len >= 128) {
transform(input, offset);
offset += 128;
len -= 128;
length += 128 * 8;
} else {
int temp = 128 - posn;
if (temp > len)
temp = len;
System.arraycopy(input, offset, block, posn, temp);
posn += temp;
length += temp * 8;
if (posn >= 128) {
transform(block, 0);
posn = 0;
}
offset += temp;
len -= temp;
}
}
}
private static final long[] k = {
0x428A2F98D728AE22L, 0x7137449123EF65CDL, 0xB5C0FBCFEC4D3B2FL,
0xE9B5DBA58189DBBCL, 0x3956C25BF348B538L, 0x59F111F1B605D019L,
0x923F82A4AF194F9BL, 0xAB1C5ED5DA6D8118L, 0xD807AA98A3030242L,
0x12835B0145706FBEL, 0x243185BE4EE4B28CL, 0x550C7DC3D5FFB4E2L,
0x72BE5D74F27B896FL, 0x80DEB1FE3B1696B1L, 0x9BDC06A725C71235L,
0xC19BF174CF692694L, 0xE49B69C19EF14AD2L, 0xEFBE4786384F25E3L,
0x0FC19DC68B8CD5B5L, 0x240CA1CC77AC9C65L, 0x2DE92C6F592B0275L,
0x4A7484AA6EA6E483L, 0x5CB0A9DCBD41FBD4L, 0x76F988DA831153B5L,
0x983E5152EE66DFABL, 0xA831C66D2DB43210L, 0xB00327C898FB213FL,
0xBF597FC7BEEF0EE4L, 0xC6E00BF33DA88FC2L, 0xD5A79147930AA725L,
0x06CA6351E003826FL, 0x142929670A0E6E70L, 0x27B70A8546D22FFCL,
0x2E1B21385C26C926L, 0x4D2C6DFC5AC42AEDL, 0x53380D139D95B3DFL,
0x650A73548BAF63DEL, 0x766A0ABB3C77B2A8L, 0x81C2C92E47EDAEE6L,
0x92722C851482353BL, 0xA2BFE8A14CF10364L, 0xA81A664BBC423001L,
0xC24B8B70D0F89791L, 0xC76C51A30654BE30L, 0xD192E819D6EF5218L,
0xD69906245565A910L, 0xF40E35855771202AL, 0x106AA07032BBD1B8L,
0x19A4C116B8D2D0C8L, 0x1E376C085141AB53L, 0x2748774CDF8EEB99L,
0x34B0BCB5E19B48A8L, 0x391C0CB3C5C95A63L, 0x4ED8AA4AE3418ACBL,
0x5B9CCA4F7763E373L, 0x682E6FF3D6B2B8A3L, 0x748F82EE5DEFB2FCL,
0x78A5636F43172F60L, 0x84C87814A1F0AB72L, 0x8CC702081A6439ECL,
0x90BEFFFA23631E28L, 0xA4506CEBDE82BDE9L, 0xBEF9A3F7B2C67915L,
0xC67178F2E372532BL, 0xCA273ECEEA26619CL, 0xD186B8C721C0C207L,
0xEADA7DD6CDE0EB1EL, 0xF57D4F7FEE6ED178L, 0x06F067AA72176FBAL,
0x0A637DC5A2C898A6L, 0x113F9804BEF90DAEL, 0x1B710B35131C471BL,
0x28DB77F523047D84L, 0x32CAAB7B40C72493L, 0x3C9EBE0A15C9BEBCL,
0x431D67C49C100D4CL, 0x4CC5D4BECB3E42B6L, 0x597F299CFC657E2AL,
0x5FCB6FAB3AD6FAECL, 0x6C44198C4A475817L
};
private static long rightRotate(long value, int n)
{
return (value >>> n) | (value << (64 - n));
init {
engineReset()
}
private void transform(byte[] m, int offset)
{
long a, b, c, d, e, f, g, h;
long temp1, temp2;
int index;
override fun destroy() {
h.fill(0L)
block.fill(0.toByte())
w.fill(0L)
}
// Initialize working variables to the current hash value.
a = this.h[0];
b = this.h[1];
c = this.h[2];
d = this.h[3];
e = this.h[4];
f = this.h[5];
g = this.h[6];
h = this.h[7];
// Convert the 16 input message words from big endian to host byte order.
for (index = 0; index < 16; ++index) {
w[index] = ((m[offset] & 0xFFL) << 56) |
((m[offset + 1] & 0xFFL) << 48) |
((m[offset + 2] & 0xFFL) << 40) |
((m[offset + 3] & 0xFFL) << 32) |
((m[offset + 4] & 0xFFL) << 24) |
((m[offset + 5] & 0xFFL) << 16) |
((m[offset + 6] & 0xFFL) << 8) |
(m[offset + 7] & 0xFFL);
offset += 8;
}
// Extend the first 16 words to 80.
for (index = 16; index < 80; ++index) {
w[index] = w[index - 16] + w[index - 7] +
(rightRotate(w[index - 15], 1) ^
rightRotate(w[index - 15], 8) ^
(w[index - 15] >>> 7)) +
(rightRotate(w[index - 2], 19) ^
rightRotate(w[index - 2], 61) ^
(w[index - 2] >>> 6));
}
// Compression function main loop.
for (index = 0; index < 80; ++index) {
temp1 = (h) + k[index] + w[index] +
(rightRotate((e), 14) ^ rightRotate((e), 18) ^ rightRotate((e), 41)) +
(((e) & (f)) ^ ((~(e)) & (g)));
temp2 = (rightRotate((a), 28) ^ rightRotate((a), 34) ^ rightRotate((a), 39)) +
(((a) & (b)) ^ ((a) & (c)) ^ ((b) & (c)));
h = g;
g = f;
f = e;
e = d + temp1;
d = c;
c = b;
b = a;
a = temp1 + temp2;
}
// Add the compressed chunk to the current hash value.
this.h[0] += a;
this.h[1] += b;
this.h[2] += c;
this.h[3] += d;
this.h[4] += e;
this.h[5] += f;
this.h[6] += g;
this.h[7] += h;
}
override fun engineDigest(): ByteArray {
val digest = ByteArray(64)
try {
engineDigest(digest, 0, 64)
} catch (e: DigestException) {
// Shouldn't happen, but just in case.
digest.fill(0.toByte())
}
return digest
}
@Throws(DigestException::class)
override fun engineDigest(buf: ByteArray, offset: Int, len: Int): Int {
if (len < 64) throw DigestException("Invalid digest length for SHA512")
if (posn <= (128 - 17)) {
block[posn] = 0x80.toByte()
block.fill(0.toByte(), posn + 1, 128 - 8)
} else {
block[posn] = 0x80.toByte()
block.fill(0.toByte(), posn + 1, 128)
transform(block, 0)
block.fill(0.toByte(), 0, 128 - 8)
}
writeBE64(block, 128 - 8, length)
transform(block, 0)
posn = 0
for (index in 0..7) writeBE64(buf, offset + index * 8, h[index])
return 64
}
override fun engineGetDigestLength(): Int = 64
override fun engineReset() {
h[0] = 0x6a09e667f3bcc908L
h[1] = -0x4498517a7b3558c5L
h[2] = 0x3c6ef372fe94f82bL
h[3] = -0x5ab00ac5a0e2c90fL
h[4] = 0x510e527fade682d1L
h[5] = -0x64fa9773d4c193e1L
h[6] = 0x1f83d9abfb41bd6bL
h[7] = 0x5be0cd19137e2179L
length = 0
posn = 0
}
override fun engineUpdate(input: Byte) {
block[posn++] = input
length += 8
if (posn >= 128) {
transform(block, 0)
posn = 0
}
}
override fun engineUpdate(input: ByteArray, offset: Int, len: Int) {
var offset = offset
var len = len
while (len > 0) {
if (posn == 0 && len >= 128) {
transform(input, offset)
offset += 128
len -= 128
length += (128 * 8).toLong()
} else {
var temp = 128 - posn
if (temp > len) temp = len
System.arraycopy(input, offset, block, posn, temp)
posn += temp
length += (temp * 8).toLong()
if (posn >= 128) {
transform(block, 0)
posn = 0
}
offset += temp
len -= temp
}
}
}
private fun transform(m: ByteArray, offset: Int) {
var currentOffset = offset
// Initialize working variables to the current hash value.
var a = this.h[0]
var b = this.h[1]
var c = this.h[2]
var d = this.h[3]
var e = this.h[4]
var f = this.h[5]
var g = this.h[6]
var h = this.h[7]
for (index in 0 until 16) {
w[index] = ((m[currentOffset].toLong() and 0xFFL) shl 56) or
((m[currentOffset + 1].toLong() and 0xFFL) shl 48) or
((m[currentOffset + 2].toLong() and 0xFFL) shl 40) or
((m[currentOffset + 3].toLong() and 0xFFL) shl 32) or
((m[currentOffset + 4].toLong() and 0xFFL) shl 24) or
((m[currentOffset + 5].toLong() and 0xFFL) shl 16) or
((m[currentOffset + 6].toLong() and 0xFFL) shl 8) or
(m[currentOffset + 7].toLong() and 0xFFL)
currentOffset += 8
}
// Extend the first 16 words to 80.
for (index in 16 until 80) {
w[index] = w[index - 16] + w[index - 7] +
(rightRotate(w[index - 15], 1) xor
rightRotate(w[index - 15], 8) xor
(w[index - 15] ushr 7)) +
(rightRotate(w[index - 2], 19) xor
rightRotate(w[index - 2], 61) xor
(w[index - 2] ushr 6))
}
// Compression function main loop.
for (index in 0 until 80) {
val temp1 = h + k[index] + w[index] +
(rightRotate(e, 14) xor rightRotate(e, 18) xor rightRotate(e, 41)) +
((e and f) xor (e.inv() and g))
val temp2 = (rightRotate(a, 28) xor rightRotate(a, 34) xor rightRotate(a, 39)) +
((a and b) xor (a and c) xor (b and c))
h = g
g = f
f = e
e = d + temp1
d = c
c = b
b = a
a = temp1 + temp2
}
// Add the compressed chunk to the current hash value.
this.h[0] += a
this.h[1] += b
this.h[2] += c
this.h[3] += d
this.h[4] += e
this.h[5] += f
this.h[6] += g
this.h[7] += h
}
companion object {
private fun writeBE64(buf: ByteArray, offset: Int, value: Long) {
buf[offset] = (value shr 56).toByte()
buf[offset + 1] = (value shr 48).toByte()
buf[offset + 2] = (value shr 40).toByte()
buf[offset + 3] = (value shr 32).toByte()
buf[offset + 4] = (value shr 24).toByte()
buf[offset + 5] = (value shr 16).toByte()
buf[offset + 6] = (value shr 8).toByte()
buf[offset + 7] = value.toByte()
}
private val k = longArrayOf(
0x428A2F98D728AE22L, 0x7137449123EF65CDL, -0x4a3f043013b2c4d1L,
-0x164a245a7e762444L, 0x3956C25BF348B538L, 0x59F111F1B605D019L,
-0x6dc07d5b50e6b065L, -0x54e3a12a25927ee8L, -0x27f855675cfcfdbeL,
0x12835B0145706FBEL, 0x243185BE4EE4B28CL, 0x550C7DC3D5FFB4E2L,
0x72BE5D74F27B896FL, -0x7f214e01c4e9694fL, -0x6423f958da38edcbL,
-0x3e640e8b3096d96cL, -0x1b64963e610eb52eL, -0x1041b879c7b0da1dL,
0x0FC19DC68B8CD5B5L, 0x240CA1CC77AC9C65L, 0x2DE92C6F592B0275L,
0x4A7484AA6EA6E483L, 0x5CB0A9DCBD41FBD4L, 0x76F988DA831153B5L,
-0x67c1aead11992055L, -0x57ce3992d24bcdf0L, -0x4ffcd8376704dec1L,
-0x40a680384110f11cL, -0x391ff40cc257703eL, -0x2a586eb86cf558dbL,
0x06CA6351E003826FL, 0x142929670A0E6E70L, 0x27B70A8546D22FFCL,
0x2E1B21385C26C926L, 0x4D2C6DFC5AC42AEDL, 0x53380D139D95B3DFL,
0x650A73548BAF63DEL, 0x766A0ABB3C77B2A8L, -0x7e3d36d1b812511aL,
-0x6d8dd37aeb7dcac5L, -0x5d40175eb30efc9cL, -0x57e599b443bdcfffL,
-0x3db4748f2f07686fL, -0x3893ae5cf9ab41d0L, -0x2e6d17e62910ade8L,
-0x2966f9dbaa9a56f0L, -0xbf1ca7aa88edfd6L, 0x106AA07032BBD1B8L,
0x19A4C116B8D2D0C8L, 0x1E376C085141AB53L, 0x2748774CDF8EEB99L,
0x34B0BCB5E19B48A8L, 0x391C0CB3C5C95A63L, 0x4ED8AA4AE3418ACBL,
0x5B9CCA4F7763E373L, 0x682E6FF3D6B2B8A3L, 0x748F82EE5DEFB2FCL,
0x78A5636F43172F60L, -0x7b3787eb5e0f548eL, -0x7338fdf7e59bc614L,
-0x6f410005dc9ce1d8L, -0x5baf9314217d4217L, -0x41065c084d3986ebL,
-0x398e870d1c8dacd5L, -0x35d8c13115d99e64L, -0x2e794738de3f3df9L,
-0x15258229321f14e2L, -0xa82b08011912e88L, 0x06F067AA72176FBAL,
0x0A637DC5A2C898A6L, 0x113F9804BEF90DAEL, 0x1B710B35131C471BL,
0x28DB77F523047D84L, 0x32CAAB7B40C72493L, 0x3C9EBE0A15C9BEBCL,
0x431D67C49C100D4CL, 0x4CC5D4BECB3E42B6L, 0x597F299CFC657E2AL,
0x5FCB6FAB3AD6FAECL, 0x6C44198C4A475817L
)
private fun rightRotate(value: Long, n: Int): Long {
return (value ushr n) or (value shl (64 - n))
}
}
}

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;