/* _____ _
* |_ _| |_ _ _ ___ ___ _ __ __ _
* | | | ' \| '_/ -_) -_) ' \/ _` |_
* |_| |_||_|_| \___\___|_|_|_\__,_(_)
*
* Threema Java Client
* Copyright (c) 2015-2020 Threema GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
#include
#include
#define ROUNDS 20
typedef unsigned int uint32;
static const unsigned char sigma[16] = "expand 32-byte k";
int crypto_stream_salsa20_ref(
unsigned char *c,unsigned long long clen,
const unsigned char *n,
const unsigned char *k
);
int crypto_stream_salsa20_ref_xor(
unsigned char *c,
const unsigned char *m,unsigned long long mlen,
const unsigned char *n,
const unsigned char *k
);
int crypto_stream_salsa20_ref_xor_skip32(
unsigned char *c0,
unsigned char *c,unsigned long coffset,
const unsigned char *m,unsigned long moffset,
unsigned long long mlen,
const unsigned char *n,
const unsigned char *k
);
JNIEXPORT jint JNICALL Java_com_neilalexander_jnacl_crypto_salsa20_crypto_1stream_1native(JNIEnv* env, jclass cls,
jbyteArray carr, jint clen, jbyteArray narr, jint noffset, jbyteArray karr)
{
jbyte *c;
jbyte n[8];
jbyte k[32];
int res;
if ((*env)->GetArrayLength(env, carr) < clen) {
/* bad length */
return 1;
}
(*env)->GetByteArrayRegion(env, narr, noffset, 8, n);
(*env)->GetByteArrayRegion(env, karr, 0, 32, k);
c = (*env)->GetPrimitiveArrayCritical(env, carr, NULL);
if (c == NULL)
return 4;
res = crypto_stream_salsa20_ref((unsigned char *)c, clen, (unsigned char *)n, (unsigned char *)k);
(*env)->ReleasePrimitiveArrayCritical(env, carr, c, 0);
return res;
}
JNIEXPORT jint JNICALL Java_com_neilalexander_jnacl_crypto_salsa20_crypto_1stream_1xor_1native(JNIEnv* env, jclass cls,
jbyteArray carr, jbyteArray marr, jint mlen, jbyteArray narr, jint noffset, jbyteArray karr)
{
jbyte *c, *m;
jbyte n[8];
jbyte k[32];
int res;
if ((*env)->GetArrayLength(env, marr) < mlen || (*env)->GetArrayLength(env, carr) < mlen) {
/* bad length */
return 1;
}
(*env)->GetByteArrayRegion(env, narr, noffset, 8, n);
(*env)->GetByteArrayRegion(env, karr, 0, 32, k);
c = (*env)->GetPrimitiveArrayCritical(env, carr, NULL);
if (c == NULL)
return 4;
m = (*env)->GetPrimitiveArrayCritical(env, marr, NULL);
if (m == NULL) {
(*env)->ReleasePrimitiveArrayCritical(env, carr, c, 0);
return 5;
}
res = crypto_stream_salsa20_ref_xor((unsigned char *)c, (unsigned char *)m, mlen, (unsigned char *)n, (unsigned char *)k);
(*env)->ReleasePrimitiveArrayCritical(env, marr, m, 0);
(*env)->ReleasePrimitiveArrayCritical(env, carr, c, 0);
return res;
}
JNIEXPORT jint JNICALL Java_com_neilalexander_jnacl_crypto_salsa20_crypto_1stream_1xor_1skip32_1native(JNIEnv* env, jclass cls,
jbyteArray c0arr, jbyteArray carr, jint coffset, jbyteArray marr, jint moffset, jint mlen, jbyteArray narr, jint noffset, jbyteArray karr)
{
jbyte c0[32];
jbyte *c, *m;
jbyte n[8];
jbyte k[32];
int res;
if ((*env)->GetArrayLength(env, marr) < (moffset+mlen) || (*env)->GetArrayLength(env, carr) < (coffset+mlen)) {
/* bad length */
return 1;
}
(*env)->GetByteArrayRegion(env, narr, noffset, 8, n);
(*env)->GetByteArrayRegion(env, karr, 0, 32, k);
c = (*env)->GetPrimitiveArrayCritical(env, carr, NULL);
if (c == NULL)
return 4;
m = (*env)->GetPrimitiveArrayCritical(env, marr, NULL);
if (m == NULL) {
(*env)->ReleasePrimitiveArrayCritical(env, carr, c, 0);
return 5;
}
res = crypto_stream_salsa20_ref_xor_skip32((unsigned char *)c0, (unsigned char *)c, coffset, (unsigned char *)m, moffset, mlen, (unsigned char *)n, (unsigned char *)k);
(*env)->ReleasePrimitiveArrayCritical(env, marr, m, 0);
(*env)->ReleasePrimitiveArrayCritical(env, carr, c, 0);
if (c0arr != NULL)
(*env)->SetByteArrayRegion(env, c0arr, 0, 32, c0);
return res;
}
/* Public Domain code copied verbatim from NaCl below */
static uint32 rotate(uint32 u,int c)
{
return (u << c) | (u >> (32 - c));
}
static uint32 load_littleendian(const unsigned char *x)
{
return
(uint32) (x[0]) \
| (((uint32) (x[1])) << 8) \
| (((uint32) (x[2])) << 16) \
| (((uint32) (x[3])) << 24)
;
}
static void store_littleendian(unsigned char *x,uint32 u)
{
x[0] = u; u >>= 8;
x[1] = u; u >>= 8;
x[2] = u; u >>= 8;
x[3] = u;
}
int crypto_core_salsa20_ref(
unsigned char *out,
const unsigned char *in,
const unsigned char *k,
const unsigned char *c
)
{
uint32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
uint32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
int i;
j0 = x0 = load_littleendian(c + 0);
j1 = x1 = load_littleendian(k + 0);
j2 = x2 = load_littleendian(k + 4);
j3 = x3 = load_littleendian(k + 8);
j4 = x4 = load_littleendian(k + 12);
j5 = x5 = load_littleendian(c + 4);
j6 = x6 = load_littleendian(in + 0);
j7 = x7 = load_littleendian(in + 4);
j8 = x8 = load_littleendian(in + 8);
j9 = x9 = load_littleendian(in + 12);
j10 = x10 = load_littleendian(c + 8);
j11 = x11 = load_littleendian(k + 16);
j12 = x12 = load_littleendian(k + 20);
j13 = x13 = load_littleendian(k + 24);
j14 = x14 = load_littleendian(k + 28);
j15 = x15 = load_littleendian(c + 12);
for (i = ROUNDS;i > 0;i -= 2) {
x4 ^= rotate( x0+x12, 7);
x8 ^= rotate( x4+ x0, 9);
x12 ^= rotate( x8+ x4,13);
x0 ^= rotate(x12+ x8,18);
x9 ^= rotate( x5+ x1, 7);
x13 ^= rotate( x9+ x5, 9);
x1 ^= rotate(x13+ x9,13);
x5 ^= rotate( x1+x13,18);
x14 ^= rotate(x10+ x6, 7);
x2 ^= rotate(x14+x10, 9);
x6 ^= rotate( x2+x14,13);
x10 ^= rotate( x6+ x2,18);
x3 ^= rotate(x15+x11, 7);
x7 ^= rotate( x3+x15, 9);
x11 ^= rotate( x7+ x3,13);
x15 ^= rotate(x11+ x7,18);
x1 ^= rotate( x0+ x3, 7);
x2 ^= rotate( x1+ x0, 9);
x3 ^= rotate( x2+ x1,13);
x0 ^= rotate( x3+ x2,18);
x6 ^= rotate( x5+ x4, 7);
x7 ^= rotate( x6+ x5, 9);
x4 ^= rotate( x7+ x6,13);
x5 ^= rotate( x4+ x7,18);
x11 ^= rotate(x10+ x9, 7);
x8 ^= rotate(x11+x10, 9);
x9 ^= rotate( x8+x11,13);
x10 ^= rotate( x9+ x8,18);
x12 ^= rotate(x15+x14, 7);
x13 ^= rotate(x12+x15, 9);
x14 ^= rotate(x13+x12,13);
x15 ^= rotate(x14+x13,18);
}
x0 += j0;
x1 += j1;
x2 += j2;
x3 += j3;
x4 += j4;
x5 += j5;
x6 += j6;
x7 += j7;
x8 += j8;
x9 += j9;
x10 += j10;
x11 += j11;
x12 += j12;
x13 += j13;
x14 += j14;
x15 += j15;
store_littleendian(out + 0,x0);
store_littleendian(out + 4,x1);
store_littleendian(out + 8,x2);
store_littleendian(out + 12,x3);
store_littleendian(out + 16,x4);
store_littleendian(out + 20,x5);
store_littleendian(out + 24,x6);
store_littleendian(out + 28,x7);
store_littleendian(out + 32,x8);
store_littleendian(out + 36,x9);
store_littleendian(out + 40,x10);
store_littleendian(out + 44,x11);
store_littleendian(out + 48,x12);
store_littleendian(out + 52,x13);
store_littleendian(out + 56,x14);
store_littleendian(out + 60,x15);
return 0;
}
int crypto_core_hsalsa20_ref(
unsigned char *out,
const unsigned char *in,
const unsigned char *k,
const unsigned char *c
)
{
uint32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
uint32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
int i;
j0 = x0 = load_littleendian(c + 0);
j1 = x1 = load_littleendian(k + 0);
j2 = x2 = load_littleendian(k + 4);
j3 = x3 = load_littleendian(k + 8);
j4 = x4 = load_littleendian(k + 12);
j5 = x5 = load_littleendian(c + 4);
j6 = x6 = load_littleendian(in + 0);
j7 = x7 = load_littleendian(in + 4);
j8 = x8 = load_littleendian(in + 8);
j9 = x9 = load_littleendian(in + 12);
j10 = x10 = load_littleendian(c + 8);
j11 = x11 = load_littleendian(k + 16);
j12 = x12 = load_littleendian(k + 20);
j13 = x13 = load_littleendian(k + 24);
j14 = x14 = load_littleendian(k + 28);
j15 = x15 = load_littleendian(c + 12);
for (i = ROUNDS;i > 0;i -= 2) {
x4 ^= rotate( x0+x12, 7);
x8 ^= rotate( x4+ x0, 9);
x12 ^= rotate( x8+ x4,13);
x0 ^= rotate(x12+ x8,18);
x9 ^= rotate( x5+ x1, 7);
x13 ^= rotate( x9+ x5, 9);
x1 ^= rotate(x13+ x9,13);
x5 ^= rotate( x1+x13,18);
x14 ^= rotate(x10+ x6, 7);
x2 ^= rotate(x14+x10, 9);
x6 ^= rotate( x2+x14,13);
x10 ^= rotate( x6+ x2,18);
x3 ^= rotate(x15+x11, 7);
x7 ^= rotate( x3+x15, 9);
x11 ^= rotate( x7+ x3,13);
x15 ^= rotate(x11+ x7,18);
x1 ^= rotate( x0+ x3, 7);
x2 ^= rotate( x1+ x0, 9);
x3 ^= rotate( x2+ x1,13);
x0 ^= rotate( x3+ x2,18);
x6 ^= rotate( x5+ x4, 7);
x7 ^= rotate( x6+ x5, 9);
x4 ^= rotate( x7+ x6,13);
x5 ^= rotate( x4+ x7,18);
x11 ^= rotate(x10+ x9, 7);
x8 ^= rotate(x11+x10, 9);
x9 ^= rotate( x8+x11,13);
x10 ^= rotate( x9+ x8,18);
x12 ^= rotate(x15+x14, 7);
x13 ^= rotate(x12+x15, 9);
x14 ^= rotate(x13+x12,13);
x15 ^= rotate(x14+x13,18);
}
x0 += j0;
x1 += j1;
x2 += j2;
x3 += j3;
x4 += j4;
x5 += j5;
x6 += j6;
x7 += j7;
x8 += j8;
x9 += j9;
x10 += j10;
x11 += j11;
x12 += j12;
x13 += j13;
x14 += j14;
x15 += j15;
x0 -= load_littleendian(c + 0);
x5 -= load_littleendian(c + 4);
x10 -= load_littleendian(c + 8);
x15 -= load_littleendian(c + 12);
x6 -= load_littleendian(in + 0);
x7 -= load_littleendian(in + 4);
x8 -= load_littleendian(in + 8);
x9 -= load_littleendian(in + 12);
store_littleendian(out + 0,x0);
store_littleendian(out + 4,x5);
store_littleendian(out + 8,x10);
store_littleendian(out + 12,x15);
store_littleendian(out + 16,x6);
store_littleendian(out + 20,x7);
store_littleendian(out + 24,x8);
store_littleendian(out + 28,x9);
return 0;
}
int crypto_stream_salsa20_ref_xor(
unsigned char *c,
const unsigned char *m,unsigned long long mlen,
const unsigned char *n,
const unsigned char *k
)
{
unsigned char in[16];
unsigned char block[64];
int i;
unsigned int u;
if (!mlen) return 0;
for (i = 0;i < 8;++i) in[i] = n[i];
for (i = 8;i < 16;++i) in[i] = 0;
while (mlen >= 64) {
crypto_core_salsa20_ref(block,in,k,sigma);
for (i = 0;i < 64;++i) c[i] = m[i] ^ block[i];
u = 1;
for (i = 8;i < 16;++i) {
u += (unsigned int) in[i];
in[i] = u;
u >>= 8;
}
mlen -= 64;
c += 64;
m += 64;
}
if (mlen) {
crypto_core_salsa20_ref(block,in,k,sigma);
for (i = 0;i < mlen;++i) c[i] = m[i] ^ block[i];
}
return 0;
}
int crypto_stream_salsa20_ref_xor_skip32(
unsigned char *c0,
unsigned char *c,unsigned long coffset,
const unsigned char *m,unsigned long moffset,
unsigned long long mlen,
const unsigned char *n,
const unsigned char *k
)
{
unsigned char in[16];
unsigned char blk1[64];
unsigned char blk2[64];
unsigned char *prevblock;
unsigned char *curblock;
unsigned char *tmpblock;
int i;
unsigned int u;
if (!mlen) return 0;
for (i = 0;i < 8;++i) in[i] = n[i];
for (i = 8;i < 16;++i) in[i] = 0;
prevblock = blk1;
curblock = blk2;
crypto_core_salsa20_ref(prevblock,in,k,sigma);
if (c0 != NULL) {
for (i = 0; i < 32; i++) c0[i] = prevblock[i];
}
while (mlen >= 64) {
u = 1;
for (i = 8;i < 16;++i) {
u += (unsigned int) in[i];
in[i] = u;
u >>= 8;
}
crypto_core_salsa20_ref(curblock,in,k,sigma);
for (i = 0;i < 32;++i) c[i+coffset] = m[i+moffset] ^ prevblock[i+32];
for (i = 32;i < 64;++i) c[i+coffset] = m[i+moffset] ^ curblock[i-32];
mlen -= 64;
c += 64;
m += 64;
tmpblock = prevblock;
prevblock = curblock;
curblock = tmpblock;
}
if (mlen) {
u = 1;
for (i = 8;i < 16;++i) {
u += (unsigned int) in[i];
in[i] = u;
u >>= 8;
}
crypto_core_salsa20_ref(curblock,in,k,sigma);
for (i = 0;i < mlen && i < 32;++i) c[i+coffset] = m[i+moffset] ^ prevblock[i+32];
for (i = 32;i < mlen && i < 64;++i) c[i+coffset] = m[i+moffset] ^ curblock[i-32];
}
return 0;
}
int crypto_stream_salsa20_ref(
unsigned char *c,unsigned long long clen,
const unsigned char *n,
const unsigned char *k
)
{
unsigned char in[16];
unsigned char block[64];
int i;
unsigned int u;
if (!clen) return 0;
for (i = 0;i < 8;++i) in[i] = n[i];
for (i = 8;i < 16;++i) in[i] = 0;
while (clen >= 64) {
crypto_core_salsa20_ref(c,in,k,sigma);
u = 1;
for (i = 8;i < 16;++i) {
u += (unsigned int) in[i];
in[i] = u;
u >>= 8;
}
clen -= 64;
c += 64;
}
if (clen) {
crypto_core_salsa20_ref(block,in,k,sigma);
for (i = 0;i < clen;++i) c[i] = block[i];
}
return 0;
}