Created
May 7, 2018 14:43
-
-
Save lifthrasiir/14331fdcfedb111bb8a68d3bda8c690a to your computer and use it in GitHub Desktop.
Semi-direct translation of TweetNaCl 20140427 (public domain)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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