Skip to content

Instantly share code, notes, and snippets.

@lifthrasiir
Created May 7, 2018 14:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lifthrasiir/14331fdcfedb111bb8a68d3bda8c690a to your computer and use it in GitHub Desktop.
Save lifthrasiir/14331fdcfedb111bb8a68d3bda8c690a to your computer and use it in GitHub Desktop.
Semi-direct translation of TweetNaCl 20140427 (public domain)
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
namespace TweetNaCl
{
static class NaCl
{
public const int SECRET_BOX_KEY_BYTES = 32;
public const int SECRET_BOX_NONCE_BYTES = 24;
public const int SECRET_BOX_ZERO_BYTES = 32;
public const int SECRET_BOX_BOX_ZERO_BYTES = 16;
public const int SECRET_BOX_MAC_BYTES = SECRET_BOX_ZERO_BYTES - SECRET_BOX_BOX_ZERO_BYTES;
private static readonly byte[] _0 = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
private static readonly byte[] _9 = new byte[32] { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
private static readonly long[] gf0 = new long[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
private static readonly long[] gf1 = new long[16] { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
private static readonly long[] _121665 = new long[16] { 0xDB41, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
private static readonly long[] D = new long[16] { 0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203 };
private static readonly long[] D2 = new long[16] { 0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406 };
private static readonly long[] X = new long[16] { 0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169 };
private static readonly long[] Y = new long[16] { 0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666 };
private static readonly long[] I = new long[16] { 0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83};
private static readonly RNGCryptoServiceProvider cryptoServiceProvider = new RNGCryptoServiceProvider();
public static void RandomBytes(byte[] arr, int off, int count)
{
byte[] buf = new byte[count];
cryptoServiceProvider.GetBytes(buf);
Array.Copy(buf, 0, arr, off, count);
}
private static uint L32(uint x, int c)
{
return (x << c) | (x >> (32 - c));
}
private static uint ld32(byte[] xarr, int xoff)
{
return ((uint)xarr[3 + xoff] << 24) | ((uint)xarr[2 + xoff] << 16) | ((uint)xarr[1 + xoff] << 8) | (uint)xarr[xoff];
}
private static ulong dl64(byte[] xarr, int xoff)
{
return
((ulong)xarr[xoff] << 56) | ((ulong)xarr[1 + xoff] << 48) | ((ulong)xarr[2 + xoff] << 40) | ((ulong)xarr[3 + xoff] << 32) |
((ulong)xarr[4 + xoff] << 24) | ((ulong)xarr[5 + xoff] << 16) | ((ulong)xarr[6 + xoff] << 8) | (ulong)xarr[7 + xoff];
}
private static void st32(byte[] xarr, int xoff, uint u)
{
xarr[xoff] = (byte)u;
xarr[1 + xoff] = (byte)(u >> 8);
xarr[2 + xoff] = (byte)(u >> 16);
xarr[3 + xoff] = (byte)(u >> 24);
}
private static void ts64(byte[] xarr, int xoff, ulong u)
{
xarr[7 + xoff] = (byte)u;
xarr[6 + xoff] = (byte)(u >> 8);
xarr[5 + xoff] = (byte)(u >> 16);
xarr[4 + xoff] = (byte)(u >> 24);
xarr[3 + xoff] = (byte)(u >> 32);
xarr[2 + xoff] = (byte)(u >> 40);
xarr[1 + xoff] = (byte)(u >> 48);
xarr[xoff] = (byte)(u >> 56);
}
private static bool vn(byte[] xarr, int xoff, byte[] yarr, int yoff, int n)
{
byte d = 0;
for (int i = 0; i < n; ++i)
{
d |= (byte)(xarr[i + xoff] ^ yarr[i + yoff]);
}
return (1 & ((byte)(d - 1) >> 8)) == 0;
}
public static bool Verify16(byte[] xarr, int xoff, byte[] yarr, int yoff)
{
return vn(xarr, xoff, yarr, yoff, 16);
}
public static bool Verify32(byte[] xarr, int xoff, byte[] yarr, int yoff)
{
return vn(xarr, xoff, yarr, yoff, 32);
}
private static void core(byte[] outarr, int outoff, byte[] inarr, int inoff, byte[] karr, int koff, byte[] carr, int coff, bool h)
{
uint[] w = new uint[16];
uint[] x = new uint[16];
uint[] y = new uint[16];
for (int i = 0; i < 4; ++i)
{
x[5 * i] = ld32(carr, 4 * i + coff);
x[1 + i] = ld32(karr, 4 * i + koff);
x[6 + i] = ld32(inarr, 4 * i + inoff);
x[11 + i] = ld32(karr, 16 + 4 * i + koff);
}
for (int i = 0; i < 16; ++i)
{
y[i] = x[i];
}
for (int i = 0; i < 20; ++i)
{
for (int j = 0; j < 4; ++j)
{
uint t0 = x[(5 * j + 0) % 16];
uint t1 = x[(5 * j + 4) % 16];
uint t2 = x[(5 * j + 8) % 16];
uint t3 = x[(5 * j + 12) % 16];
t1 ^= L32(t0 + t3, 7);
t2 ^= L32(t1 + t0, 9);
t3 ^= L32(t2 + t1, 13);
t0 ^= L32(t3 + t2, 18);
w[4 * j + (j + 0) % 4] = t0;
w[4 * j + (j + 1) % 4] = t1;
w[4 * j + (j + 2) % 4] = t2;
w[4 * j + (j + 3) % 4] = t3;
}
for (int m = 0; m < 16; ++m)
{
x[m] = w[m];
}
}
if (h)
{
for (int i = 0; i < 16; ++i)
{
x[i] += y[i];
}
for (int i = 0; i < 4; ++i)
{
x[5 * i] -= ld32(carr, 4 * i + coff);
x[6 + i] -= ld32(inarr, 4 * i + inoff);
}
for (int i = 0; i < 4; ++i)
{
st32(outarr, 4 * i + outoff, x[5 * i]);
st32(outarr, 16 + 4 * i + outoff, x[6 + i]);
}
}
else
{
for (int i = 0; i < 16; ++i)
{
st32(outarr, 4 * i + outoff, x[i] + y[i]);
}
}
}
public static void CoreSalsa20(byte[] outarr, int outoff, byte[] inarr, int inoff, byte[] karr, int koff, byte[] carr, int coff)
{
core(outarr, outoff, inarr, inoff, karr, koff, carr, coff, false);
}
public static void CoreHSalsa20(byte[] outarr, int outoff, byte[] inarr, int inoff, byte[] karr, int koff, byte[] carr, int coff)
{
core(outarr, outoff, inarr, inoff, karr, koff, carr, coff, true);
}
private static readonly byte[] sigma = new byte[16] { 101, 120, 112, 97, 110, 100, 32, 51, 50, 45, 98, 121, 116, 101, 32, 107 };
public static void StreamSalsa20Xor(byte[] carr, int coff, byte[] marr, int moff, int b, byte[] narr, int noff, byte[] karr, int koff)
{
byte[] z = new byte[16];
byte[] x = new byte[64];
if (b == 0)
{
return;
}
for (int i = 0; i < 16; ++i)
{
z[i] = 0;
}
for (int i = 0; i < 8; ++i)
{
z[i] = narr[i + noff];
}
while (b >= 64)
{
CoreSalsa20(x, 0, z, 0, karr, koff, sigma, 0);
for (int i = 0; i < 64; ++i)
{
carr[i + coff] = (byte)((marr != null ? marr[i + moff] : 0) ^ x[i]);
}
uint u = 1;
for (int i = 8; i < 16; ++i)
{
u += z[i];
z[i] = (byte)u;
u >>= 8;
}
b -= 64;
coff += 64;
if (marr != null)
{
moff += 64;
}
}
if (b != 0)
{
CoreSalsa20(x, 0, z, 0, karr, koff, sigma, 0);
for (int i = 0; i < b; ++i)
{
carr[i + coff] = (byte)((marr != null ? marr[i + moff] : 0) ^ x[i]);
}
}
}
public static void StreamSalsa20(byte[] carr, int coff, int d, byte[] narr, int noff, byte[] karr, int koff)
{
StreamSalsa20Xor(carr, coff, null, 0, d, narr, noff, karr, koff);
}
public static void Stream(byte[] carr, int coff, int d, byte[] narr, int noff, byte[] karr, int koff)
{
byte[] s = new byte[32];
CoreHSalsa20(s, 0, narr, noff, karr, koff, sigma, 0);
StreamSalsa20(carr, coff, d, narr, noff + 16, s, 0);
}
public static void StreamXor(byte[] carr, int coff, byte[] marr, int moff, int d, byte[] narr, int noff, byte[] karr, int koff)
{
byte[] s = new byte[32];
CoreHSalsa20(s, 0, narr, noff, karr, koff, sigma, 0);
StreamSalsa20Xor(carr, coff, marr, moff, d, narr, noff + 16, s, 0);
}
public static void StreamSalsa20XorSkip(byte[] carr, int coff, byte[] marr, int moff, int b, byte[] narr, int noff, byte[] karr, int koff, int skip)
{
byte[] z = new byte[16];
byte[] x = new byte[64];
if (b == 0)
{
return;
}
for (int i = 0; i < 16; ++i)
{
z[i] = 0;
}
for (int i = 0; i < 8; ++i)
{
z[i] = narr[i + noff];
}
while (skip >= 64)
{
ulong u = 1;
for (int i = 8; i < 16; ++i)
{
u += z[i];
z[i] = (byte)u;
u >>= 8;
}
skip -= 64;
}
if (skip > 0)
{
CoreSalsa20(x, 0, z, 0, karr, koff, sigma, 0);
int i = 0;
while (i < 64 - skip && i < b)
{
carr[i + coff] = (byte)((marr != null ? marr[i + moff] : 0) ^ x[i + skip]);
++i;
}
b -= i;
coff += i;
if (marr != null)
{
moff += i;
}
ulong u = 1;
for (i = 8; i < 16; ++i)
{
u += z[i];
z[i] = (byte)u;
u >>= 8;
}
}
while (b >= 64)
{
CoreSalsa20(x, 0, z, 0, karr, koff, sigma, 0);
for (int i = 0; i < 64; ++i)
{
carr[i + coff] = (byte)((marr != null ? marr[i + moff] : 0) ^ x[i]);
}
ulong u = 1;
for (int i = 8; i < 16; ++i)
{
u += z[i];
z[i] = (byte)u;
u >>= 8;
}
b -= 64;
coff += 64;
if (marr != null)
{
moff += 64;
}
}
if (b > 0)
{
CoreSalsa20(x, 0, z, 0, karr, koff, sigma, 0);
for (int i = 0; i < b; ++i)
{
carr[i + coff] = (byte)((marr != null ? marr[i + moff] : 0) ^ x[i]);
}
}
}
private static void add1305(uint[] h, uint[] c)
{
uint u = 0;
for (int j = 0; j < 17; ++j)
{
u += h[j] + c[j];
h[j] = u & 255;
u >>= 8;
}
}
private static readonly uint[] minusp = new uint[17] { 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252 };
public static void OneTimeAuth(byte[] outarr, int outoff, byte[] marr, int moff, int n, byte[] karr, int koff)
{
uint[] x = new uint[17];
uint[] r = new uint[17];
uint[] h = new uint[17];
uint[] c = new uint[17];
uint[] g = new uint[17];
for (int j = 0; j < 17; ++j)
{
r[j] = h[j] = 0;
}
for (int j = 0; j < 16; ++j)
{
r[j] = karr[j + koff];
}
r[3] &= 15;
r[4] &= 252;
r[7] &= 15;
r[8] &= 252;
r[11] &= 15;
r[12] &= 252;
r[15] &= 15;
while (n > 0)
{
for (int j = 0; j < 17; ++j)
{
c[j] = 0;
}
int l;
for (l = 0; l < 16 && l < n; ++l)
{
c[l] = marr[l + moff];
}
c[l] = 1;
moff += l;
n -= l;
add1305(h, c);
for (int i = 0; i < 17; ++i)
{
x[i] = 0;
for (int j = 0; j < 17; ++j)
{
x[i] += h[j] * (j <= i ? r[i - j] : 320 * r[i + 17 - j]);
}
}
for (int i = 0; i < 17; ++i)
{
h[i] = x[i];
}
uint u = 0;
for (int j = 0; j < 16; ++j)
{
u += h[j];
h[j] = u & 255;
u >>= 8;
}
u += h[16];
h[16] = u & 3;
u = 5 * (u >> 2);
for (int j = 0; j < 16; ++j)
{
u += h[j];
h[j] = u & 255;
u >>= 8;
}
u += h[16];
h[16] = u;
}
for (int j = 0; j < 17; ++j)
{
g[j] = h[j];
}
add1305(h, minusp);
uint s = ~(h[16] >> 7) + 1;
for (int j = 0; j < 17; ++j)
{
h[j] ^= s & (g[j] ^ h[j]);
}
for (int j = 0; j < 16; ++j)
{
c[j] = karr[j + 16 + koff];
}
c[16] = 0;
add1305(h, c);
for (int j = 0; j < 16; ++j)
{
outarr[j + outoff] = (byte)h[j];
}
}
public static bool OneTimeAuthVerify(byte[] harr, int hoff, byte[] marr, int moff, int n, byte[] karr, int koff)
{
byte[] x = new byte[16];
OneTimeAuth(x, 0, marr, moff, n, karr, koff);
return Verify16(harr, hoff, x, 0);
}
public static bool SecretBox(byte[] carr, int coff, byte[] marr, int moff, int d, byte[] narr, int noff, byte[] karr, int koff)
{
if (d < 32)
{
return false;
}
StreamXor(carr, coff, marr, moff, d, narr, noff, karr, koff);
OneTimeAuth(carr, 16 + coff, carr, 32 + coff, d - 32, carr, coff);
for (int i = 0; i < 16; ++i)
{
carr[i + coff] = 0;
}
return true;
}
public static void SecretBoxEasy(byte[] carr, int coff, byte[] marr, int moff, int d, byte[] narr, int noff, byte[] karr, int koff)
{
byte[] s = new byte[32];
byte[] x = new byte[32];
CoreHSalsa20(s, 0, narr, noff, karr, koff, sigma, 0);
StreamSalsa20(x, 0, 32, narr, 16 + noff, s, 0);
StreamSalsa20XorSkip(carr, 16 + coff, marr, moff, d, narr, 16 + noff, s, 0, 32);
OneTimeAuth(carr, coff, carr, 16 + coff, d, x, 0);
}
public static bool SecretBoxOpen(byte[] marr, int moff, byte[] carr, int coff, int d, byte[] narr, int noff, byte[] karr, int koff)
{
if (d < 32)
{
return false;
}
byte[] x = new byte[32];
Stream(x, 0, 32, narr, noff, karr, koff);
if (!OneTimeAuthVerify(carr, 16 + coff, carr, 32 + coff, d - 32, x, 0))
{
return false;
}
StreamXor(marr, moff, carr, coff, d, narr, noff, karr, koff);
for (int i = 0; i < 16; ++i)
{
carr[i + coff] = 0;
}
return true;
}
public static bool SecretBoxEasyOpen(byte[] marr, int moff, byte[] carr, int coff, int d, byte[] narr, int noff, byte[] karr, int koff)
{
if (d < 16)
{
return false;
}
byte[] s = new byte[32];
byte[] x = new byte[32];
CoreHSalsa20(s, 0, narr, noff, karr, koff, sigma, 0);
StreamSalsa20(x, 0, 32, narr, 16 + noff, s, 0);
if (!OneTimeAuthVerify(carr, coff, carr, 16 + coff, d - 16, x, 0))
{
return false;
}
StreamSalsa20XorSkip(marr, moff, carr, 16 + coff, d - 16, narr, 16 + noff, s, 0, 32);
return true;
}
private static void set25519(long[] r, long[] a)
{
for (int i = 0; i < 16; ++i)
{
r[i] = a[i];
}
}
private static void car25519(long[] o)
{
for (int i = 0; i < 16; ++i)
{
o[i] += 1 << 16;
long c = o[i] >> 16;
o[i < 15 ? i + 1 : 0] += c - 1 + (i == 15 ? 37 * (c - 1) : 0);
o[i] -= c << 16;
}
}
private static void sel25519(long[] p, long[] q, int b)
{
long c = ~(b - 1);
for (int i = 0; i < 16; ++i)
{
long t = c & (p[i] ^ q[i]);
p[i] ^= t;
q[i] ^= t;
}
}
private static void pack25519(byte[] oarr, int ooff, long[] n)
{
long[] m = new long[16];
long[] t = new long[16];
for (int i = 0; i < 16; ++i)
{
t[i] = n[i];
}
car25519(t);
car25519(t);
car25519(t);
for (int j = 0; j < 2; ++j)
{
m[0] = t[0] - 0xffed;
for (int i = 1; i < 15; ++i)
{
m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
m[i - 1] &= 0xffff;
}
m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
int b = (int)((m[15] >> 16) & 1);
m[14] &= 0xffff;
sel25519(t, m, 1 - b);
}
for (int i = 0; i < 16; ++i)
{
oarr[2 * i + ooff] = (byte)(t[i] & 0xff);
oarr[2 * i + 1 + ooff] = (byte)(t[i] >> 8);
}
}
private static bool neq25519(long[] a, long[] b)
{
byte[] c = new byte[32];
byte[] d = new byte[32];
pack25519(c, 0, a);
pack25519(d, 0, b);
return Verify32(c, 0, d, 0);
}
private static byte par25519(long[] a)
{
byte[] d = new byte[32];
pack25519(d, 0, a);
return (byte)(d[0] & 1);
}
private static void unpack25519(long[] o, byte[] narr, int noff)
{
for (int i = 0; i < 16; ++i)
{
o[i] = narr[2 * i + noff] + ((long)narr[2 * i + 1 + noff] << 8);
}
o[15] &= 0x7fff;
}
private static void A(long[] o, long[] a, long[] b)
{
for (int i = 0; i < 16; ++i)
{
o[i] = a[i] + b[i];
}
}
private static void Z(long[] o, long[] a, long[] b)
{
for (int i = 0; i < 16; ++i)
{
o[i] = a[i] - b[i];
}
}
private static void M(long[] o, long[] a, long[] b)
{
long[] t = new long[31];
for (int i = 0; i < 31; ++i)
{
t[i] = 0;
}
for (int i = 0; i < 16; ++i)
{
for (int j = 0; j < 16; ++j)
{
t[i + j] += a[i] * b[j];
}
}
for (int i = 0; i < 15; ++i)
{
t[i] += 38 * t[i + 16];
}
for (int i = 0; i < 16; ++i)
{
o[i] = t[i];
}
car25519(o);
car25519(o);
}
private static void S(long[] o, long[] a)
{
M(o, a, a);
}
private static void inv25519(long[] o, long[] i)
{
long[] c = new long[16];
for (int a = 0; a < 16; ++a)
{
c[a] = i[a];
}
for (int a = 253; a >= 0; --a)
{
S(c, c);
if (a != 2 && a != 4)
{
M(c, c, i);
}
}
for (int a = 0; a < 16; ++a)
{
o[a] = c[a];
}
}
private static void pow2523(long[] o, long[] i)
{
long[] c = new long[16];
for (int a = 0; a < 16; ++a)
{
c[a] = i[a];
}
for (int a = 250; a >= 0; --a)
{
S(c, c);
if (a != 1)
{
M(c, c, i);
}
}
for (int a = 0; a < 16; ++a)
{
o[a] = c[a];
}
}
public static void ScalarMult(byte[] qarr, int qoff, byte[] narr, int noff, byte[] parr, int poff)
{
byte[] z = new byte[32];
long[] x0 = new long[16];
long[] x16 = new long[16];
long[] x32 = new long[16];
long[] x48 = new long[16];
long[] x64 = new long[16];
long[] a = new long[16];
long[] b = new long[16];
long[] c = new long[16];
long[] d = new long[16];
long[] e = new long[16];
long[] f = new long[16];
for (int i = 0; i < 31; ++i)
{
z[i] = narr[i + noff];
}
z[31] = (byte)((narr[31 + noff] & 127) | 64);
z[0] &= 248;
unpack25519(x0, parr, poff);
for (int i = 0; i < 16; ++i)
{
b[i] = x0[i];
d[i] = a[i] = c[i] = 0;
}
a[0] = d[0] = 1;
for (int i = 254; i >= 0; --i)
{
int r = (z[i >> 3] >> (i & 7)) & 1;
sel25519(a, b, r);
sel25519(c, d, r);
A(e, a, c);
Z(a, a, c);
A(c, b, d);
Z(b, b, d);
S(d, e);
S(f, a);
M(a, c, a);
M(c, b, e);
A(e, a, c);
Z(a, a, c);
S(b, a);
Z(c, d, f);
M(a, c, _121665);
A(a, a, d);
M(c, c, a);
M(a, d, f);
M(d, b, x0);
S(b, e);
sel25519(a, b, r);
sel25519(c, d, r);
}
for (int i = 0; i < 16; ++i)
{
x16[i] = a[i];
x32[i] = c[i];
x48[i] = b[i];
x64[i] = d[i];
}
inv25519(x32, x32);
M(x16, x16, x32);
pack25519(qarr, qoff, x16);
}
public static void ScalarMultBase(byte[] qarr, int qoff, byte[] narr, int noff)
{
ScalarMult(qarr, qoff, narr, noff, _9, 0);
}
public static void BoxKeypair(byte[] yarr, int yoff, byte[] xarr, int xoff)
{
RandomBytes(xarr, xoff, 32);
ScalarMultBase(yarr, yoff, xarr, xoff);
}
public static void BoxBeforeNM(byte[] karr, int koff, byte[] yarr, int yoff, byte[] xarr, int xoff)
{
byte[] s = new byte[32];
ScalarMult(s, 0, xarr, xoff, yarr, yoff);
CoreHSalsa20(karr, koff, _0, 0, s, 0, sigma, 0);
}
public static void BoxAfterNM(byte[] carr, int coff, byte[] marr, int moff, int d, byte[] narr, int noff, byte[] karr, int koff)
{
SecretBox(carr, coff, marr, moff, d, narr, noff, karr, koff);
}
public static void BoxEasyAfterNM(byte[] carr, int coff, byte[] marr, int moff, int d, byte[] narr, int noff, byte[] karr, int koff)
{
SecretBoxEasy(carr, coff, marr, moff, d, narr, noff, karr, koff);
}
public static bool BoxOpenAfterNM(byte[] marr, int moff, byte[] carr, int coff, int d, byte[] narr, int noff, byte[] karr, int koff)
{
return SecretBoxOpen(marr, moff, carr, coff, d, narr, noff, karr, koff);
}
public static bool BoxEasyOpenAfterNM(byte[] marr, int moff, byte[] carr, int coff, int d, byte[] narr, int noff, byte[] karr, int koff)
{
return SecretBoxEasyOpen(marr, moff, carr, coff, d, narr, noff, karr, koff);
}
public static void Box(byte[] carr, int coff, byte[] marr, int moff, int d, byte[] narr, int noff, byte[] yarr, int yoff, byte[] xarr, int xoff)
{
byte[] k = new byte[32];
BoxBeforeNM(k, 0, yarr, yoff, xarr, xoff);
BoxAfterNM(carr, coff, marr, moff, d, narr, noff, k, 0);
}
public static void BoxEasy(byte[] carr, int coff, byte[] marr, int moff, int d, byte[] narr, int noff, byte[] yarr, int yoff, byte[] xarr, int xoff)
{
byte[] k = new byte[32];
BoxBeforeNM(k, 0, yarr, yoff, xarr, xoff);
BoxEasyAfterNM(carr, coff, marr, moff, d, narr, noff, k, 0);
}
public static bool BoxOpen(byte[] marr, int moff, byte[] carr, int coff, int d, byte[] narr, int noff, byte[] yarr, int yoff, byte[] xarr, int xoff)
{
byte[] k = new byte[32];
BoxBeforeNM(k, 0, yarr, yoff, xarr, xoff);
return BoxOpenAfterNM(marr, moff, carr, coff, d, narr, noff, k, 0);
}
public static bool BoxEasyOpen(byte[] marr, int moff, byte[] carr, int coff, int d, byte[] narr, int noff, byte[] yarr, int yoff, byte[] xarr, int xoff)
{
byte[] k = new byte[32];
BoxBeforeNM(k, 0, yarr, yoff, xarr, xoff);
return BoxEasyOpenAfterNM(marr, moff, carr, coff, d, narr, noff, k, 0);
}
private static ulong R(ulong x, int c)
{
return (x >> c) | (x << (64 - c));
}
private static ulong Ch(ulong x, ulong y, ulong z)
{
return (x & y) ^ (~x & z);
}
private static ulong Maj(ulong x, ulong y, ulong z)
{
return (x & y) ^ (x & z) ^ (y & z);
}
private static ulong Sigma0(ulong x)
{
return R(x, 28) ^ R(x, 34) ^ R(x, 39);
}
private static ulong Sigma1(ulong x)
{
return R(x, 14) ^ R(x, 18) ^ R(x, 41);
}
private static ulong sigma0(ulong x)
{
return R(x, 1) ^ R(x, 8) ^ (x >> 7);
}
private static ulong sigma1(ulong x)
{
return R(x, 19) ^ R(x, 61) ^ (x >> 6);
}
private static readonly ulong[] K = new ulong[80]
{
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817,
};
public static int HashBlocks(byte[] xarr, int xoff, byte[] marr, int moff, int n)
{
ulong[] z = new ulong[8];
ulong[] b = new ulong[8];
ulong[] a = new ulong[8];
ulong[] w = new ulong[16];
for (int i = 0; i < 8; ++i)
{
z[i] = a[i] = dl64(xarr, 8 * i + xoff);
}
while (n >= 128)
{
for (int i = 0; i < 16; ++i)
{
w[i] = dl64(marr, 8 * i + moff);
}
for (int i = 0; i < 80; ++i)
{
for (int j = 0; j < 8; ++j)
{
b[j] = a[j];
}
ulong t = a[7] + Sigma1(a[4]) + Ch(a[4], a[5], a[6]) + K[i] + w[i % 16];
b[7] = t + Sigma0(a[0]) + Maj(a[0], a[1], a[2]);
b[3] += t;
for (int j = 0; j < 8; ++j)
{
a[(j + 1) % 8] = b[j];
}
if (i % 16 == 15)
{
for (int j = 0; j < 16; ++j)
{
w[j] += w[(j + 9) % 16] + sigma0(w[(j + 1) % 16]) + sigma1(w[(j + 14) % 16]);
}
}
}
for (int i = 0; i < 8; ++i)
{
a[i] += z[i];
z[i] = a[i];
}
moff += 128;
n -= 128;
}
for (int i = 0; i < 8; ++i)
{
ts64(xarr, 8 * i + xoff, z[i]);
}
return n;
}
private static readonly byte[] iv = new byte[64]
{
0x6a, 0x09, 0xe6, 0x67, 0xf3, 0xbc, 0xc9, 0x08,
0xbb, 0x67, 0xae, 0x85, 0x84, 0xca, 0xa7, 0x3b,
0x3c, 0x6e, 0xf3, 0x72, 0xfe, 0x94, 0xf8, 0x2b,
0xa5, 0x4f, 0xf5, 0x3a, 0x5f, 0x1d, 0x36, 0xf1,
0x51, 0x0e, 0x52, 0x7f, 0xad, 0xe6, 0x82, 0xd1,
0x9b, 0x05, 0x68, 0x8c, 0x2b, 0x3e, 0x6c, 0x1f,
0x1f, 0x83, 0xd9, 0xab, 0xfb, 0x41, 0xbd, 0x6b,
0x5b, 0xe0, 0xcd, 0x19, 0x13, 0x7e, 0x21, 0x79,
};
public static void Hash(byte[] outarr, int outoff, byte[] marr, int moff, int n)
{
byte[] h = new byte[64];
byte[] x = new byte[256];
ulong b = (ulong)n;
for (int i = 0; i < 64; ++i)
{
h[i] = iv[i];
}
HashBlocks(h, 0, marr, moff, n);
moff += n;
n &= 127;
moff -= n;
for (int i = 0; i < 256; ++i)
{
x[i] = 0;
}
for (int i = 0; i < n; ++i)
{
x[i] = marr[i + moff];
}
x[n] = 128;
n = 256 - (n < 112 ? 128 : 0);
x[n - 9] = (byte)(b >> 61);
ts64(x, n - 8, b << 3);
HashBlocks(h, 0, x, 0, n);
for (int i = 0; i < 64; ++i)
{
outarr[i + outoff] = h[i];
}
}
private static void add(long[] p0, long[] p1, long[] p2, long[] p3, long[] q0, long[] q1, long[] q2, long[] q3)
{
long[] a = new long[16];
long[] b = new long[16];
long[] c = new long[16];
long[] d = new long[16];
long[] t = new long[16];
long[] e = new long[16];
long[] f = new long[16];
long[] g = new long[16];
long[] h = new long[16];
Z(a, p1, p0);
Z(t, q1, q0);
M(a, a, t);
A(b, p0, p1);
A(t, q0, q1);
M(b, b, t);
M(c, p3, q3);
M(c, c, D2);
M(d, p2, q2);
A(d, d, d);
Z(e, b, a);
Z(f, d, c);
A(g, d, c);
A(h, b, a);
M(p0, e, f);
M(p1, h, g);
M(p2, g, f);
M(p3, e, h);
}
private static void cswap(long[] p0, long[] p1, long[] p2, long[] p3, long[] q0, long[] q1, long[] q2, long[] q3, int b)
{
sel25519(p0, q0, b);
sel25519(p1, q1, b);
sel25519(p2, q2, b);
sel25519(p3, q3, b);
}
private static void pack(byte[] rarr, int roff, long[] p0, long[] p1, long[] p2, long[] p3)
{
long[] tx = new long[16];
long[] ty = new long[16];
long[] zi = new long[16];
inv25519(zi, p2);
M(tx, p0, zi);
M(ty, p1, zi);
pack25519(rarr, roff, ty);
rarr[31 + roff] ^= (byte)(par25519(tx) << 7);
}
private static void scalarmult(long[] p0, long[] p1, long[] p2, long[] p3, long[] q0, long[] q1, long[] q2, long[] q3, byte[] sarr, int soff)
{
set25519(p0, gf0);
set25519(p1, gf1);
set25519(p2, gf1);
set25519(p3, gf0);
for (int i = 255; i >= 0; --i)
{
int b = (sarr[i / 8 + soff] >> (i & 7)) & 1;
cswap(p0, p1, p2, p3, q0, q1, q2, q3, b);
add(q0, q1, q2, q3, p0, p1, p2, p3);
add(p0, p1, p2, p3, p0, p1, p2, p3);
cswap(p0, p1, p2, p3, q0, q1, q2, q3, b);
}
}
private static void scalarbase(long[] p0, long[] p1, long[] p2, long[] p3, byte[] sarr, int soff)
{
long[] q0 = new long[16];
long[] q1 = new long[16];
long[] q2 = new long[16];
long[] q3 = new long[16];
set25519(q0, X);
set25519(q1, Y);
set25519(q2, gf1);
M(q3, X, Y);
scalarmult(p0, p1, p2, p3, q0, q1, q2, q3, sarr, soff);
}
public static void SignKeypair(byte[] pkarr, int pkoff, byte[] skarr, int skoff)
{
byte[] d = new byte[64];
long[] p0 = new long[16];
long[] p1 = new long[16];
long[] p2 = new long[16];
long[] p3 = new long[16];
RandomBytes(skarr, skoff, 32);
Hash(d, 0, skarr, skoff, 32);
d[0] &= 248;
d[31] &= 127;
d[31] |= 64;
scalarbase(p0, p1, p2, p3, d, 0);
pack(pkarr, pkoff, p0, p1, p2, p3);
for (int i = 0; i < 32; ++i)
{
skarr[32 + i + skoff] = pkarr[i + pkoff];
}
}
private static readonly long[] L = new long[32] { 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10 };
private static void modL(byte[] rarr, int roff, long[] x)
{
long carry;
for (int i = 63; i >= 32; --i)
{
carry = 0;
int j;
for (j = i - 32; j < i - 12; ++j)
{
x[j] += carry - 16 * x[i] * L[j - (i - 32)];
carry = (x[j] + 128) >> 8;
x[j] -= carry << 8;
}
x[j] += carry;
x[i] = 0;
}
carry = 0;
for (int j = 0; j < 32; ++j)
{
x[j] += carry - (x[31] >> 4) * L[j];
carry = x[j] >> 8;
x[j] &= 255;
}
for (int j = 0; j < 32; ++j)
{
x[j] -= carry * L[j];
}
for (int i = 0; i < 32; ++i)
{
x[i + 1] += x[i] >> 8;
rarr[i + roff] = (byte)(x[i] & 255);
}
}
private static void reduce(byte[] rarr)
{
long[] x = new long[64];
for (int i = 0; i < 64; ++i)
{
x[i] = rarr[i];
}
for (int i = 0; i < 64; ++i)
{
rarr[i] = 0;
}
modL(rarr, 0, x);
}
public static void Sign(byte[] smarr, int smoff, out int smlen, byte[] marr, int moff, int n, byte[] skarr, int skoff)
{
byte[] d = new byte[64];
byte[] h = new byte[64];
byte[] r = new byte[64];
long[] x = new long[64];
long[] p0 = new long[16];
long[] p1 = new long[16];
long[] p2 = new long[16];
long[] p3 = new long[16];
Hash(d, 0, skarr, skoff, 32);
d[0] &= 248;
d[31] &= 127;
d[31] |= 64;
smlen = n + 64;
for (int i = 0; i < n; ++i)
{
smarr[64 + i + smoff] = marr[i + moff];
}
for (int i = 0; i < 32; ++i)
{
smarr[32 + i + smoff] = d[32 + i];
}
Hash(r, 0, smarr, 32 + smoff, n + 32);
reduce(r);
scalarbase(p0, p1, p2, p3, r, 0);
pack(smarr, smoff, p0, p1, p2, p3);
for (int i = 0; i < 32; ++i)
{
smarr[i + 32 + smoff] = skarr[i + 32 + skoff];
}
Hash(h, 0, smarr, smoff, n + 64);
reduce(h);
for (int i = 0; i < 64; ++i)
{
x[i] = 0;
}
for (int i = 0; i < 32; ++i)
{
x[i] = r[i];
}
for (int i = 0; i < 32; ++i)
{
for (int j = 0; j < 32; ++j)
{
x[i + j] = h[i] * d[j];
}
}
modL(smarr, 32 + smoff, x);
}
private static bool unpackneg(long[] r0, long[] r1, long[] r2, long[] r3, byte[] parr, int poff)
{
long[] t = new long[16];
long[] chk = new long[16];
long[] num = new long[16];
long[] den = new long[16];
long[] den2 = new long[16];
long[] den4 = new long[16];
long[] den6 = new long[16];
set25519(r2, gf1);
unpack25519(r1, parr, poff);
S(num, r1);
M(den, num, D);
Z(num, num, r2);
A(den, r2, den);
S(den2, den);
S(den4, den2);
M(den6, den4, den2);
M(t, den6, num);
M(t, t, den);
pow2523(t, t);
M(t, t, num);
M(t, t, den);
M(t, t, den);
M(r0, t, den);
S(chk, r0);
M(chk, chk, den);
if (neq25519(chk, num))
{
M(r0, r0, I);
}
S(chk, r0);
M(chk, chk, den);
if (neq25519(chk, num))
{
return false;
}
if (par25519(r0) == (parr[31 + poff] >> 7))
{
Z(r0, gf0, r0);
}
M(r3, r0, r1);
return true;
}
public static bool SignOpen(byte[] marr, int moff, out int mlen, byte[] smarr, int smoff, int n, byte[] pkarr, int pkoff)
{
byte[] t = new byte[32];
byte[] h = new byte[64];
long[] p0 = new long[16];
long[] p1 = new long[16];
long[] p2 = new long[16];
long[] p3 = new long[16];
long[] q0 = new long[16];
long[] q1 = new long[16];
long[] q2 = new long[16];
long[] q3 = new long[16];
mlen = -1;
if (n < 64)
{
return false;
}
if (unpackneg(q0, q1, q2, q3, pkarr, pkoff))
{
return false;
}
for (int i = 0; i < n; ++i)
{
marr[i + moff] = smarr[i + smoff];
}
for (int i = 0; i < 32; ++i)
{
marr[i + 32 + moff] = pkarr[i + pkoff];
}
Hash(h, 0, marr, moff, n);
reduce(h);
scalarmult(p0, p1, p2, p3, q0, q1, q2, q3, h, 0);
scalarbase(q0, q1, q2, q3, smarr, 32 + smoff);
add(p0, p1, p2, p3, q0, q1, q2, q3);
pack(t, 0, p0, p1, p2, p3);
n -= 64;
if (Verify32(smarr, smoff, t, 0))
{
for (int i = 0; i < n; ++i)
{
marr[i + moff] = 0;
}
return false;
}
for (int i = 0; i < n; ++i)
{
marr[i + moff] = smarr[i + 64 + smoff];
}
mlen = n;
return true;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment