nacl-es/lowlevel.ts
2025-09-16 17:43:44 +08:00

984 lines
28 KiB
TypeScript

/*
NOTE: Ported in 2014 by Dmitry Chestnykh and Devi Mandiri. Public domain. Implementation derived from TweetNaCl (https://tweetnacl.cr.yp.to/) version 20140427.
*/
import { randomBytesByTargetSizeFromBegin } from "./_random_bytes.ts";
export interface KeyPair {
publicKey: Uint8Array;
secretKey: Uint8Array;
}
class u64 {
hi: number;
lo: number;
constructor(h: number, l: number) {
this.hi = h | 0 >>> 0;
this.lo = l | 0 >>> 0;
}
}
export function gf(init?: Float64Array): Float64Array {
const r: Float64Array = new Float64Array(16);
if (typeof init !== "undefined") {
for (let i: number = 0; i < init.length; i++) {
r[i] = init[i];
}
}
return r;
}
const _0: Uint8Array = new Uint8Array(16);
const _9: Uint8Array = new Uint8Array(32);
_9[0] = 9;
const gf0: Float64Array = gf();
const gf1: Float64Array = gf(Float64Array.from([1]));
const _121665: Float64Array = gf(Float64Array.from([0xDB41, 1]));
export const D: Float64Array = gf(Float64Array.from([0x78A3, 0x1359, 0x4DCA, 0x75EB, 0xD8AB, 0x4141, 0x0A4D, 0x0070, 0xE898, 0x7779, 0x4079, 0x8CC7, 0xFE73, 0x2B6F, 0x6CEE, 0x5203]));
const D2: Float64Array = gf(Float64Array.from([0xF159, 0x26B2, 0x9B94, 0xEBD6, 0xB156, 0x8283, 0x149A, 0x00E0, 0xD130, 0xEEF3, 0x80F2, 0x198E, 0xFCE7, 0x56DF, 0xD9DC, 0x2406]));
const I: Float64Array = gf(Float64Array.from([0xA0B0, 0x4A0E, 0x1B27, 0xC4EE, 0xE478, 0xAD2F, 0x1806, 0x2F43, 0xD7A7, 0x3DFB, 0x0099, 0x2B4D, 0xDF0B, 0x4FC1, 0x2480, 0x2B83]));
const X: Float64Array = gf(Float64Array.from([0xD51A, 0x8F25, 0x2D60, 0xC956, 0xA7B2, 0x9525, 0xC760, 0x692C, 0xDC5C, 0xFDD6, 0xE231, 0xC0A4, 0x53FE, 0xCD6E, 0x36D3, 0x2169]));
const Y: Float64Array = gf(Float64Array.from([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666]));
function L32(x: number, c: number): number {
return ((x << c) | (x >>> (32 - c)));
}
function ld32(x: Uint8Array, i: number): number {
let u: number = x[i + 3] & 0xFF;
u = (u << 8) | (x[i + 2] & 0xFF);
u = (u << 8) | (x[i + 1] & 0xFF);
return ((u << 8) | (x[i + 0] & 0xFF));
}
function dl64(x: Uint8Array, i: number): u64 {
return new u64((x[i] << 24) | (x[i + 1] << 16) | (x[i + 2] << 8) | x[i + 3], (x[i + 4] << 24) | (x[i + 5] << 16) | (x[i + 6] << 8) | x[i + 7]);
}
function st32(x: Uint8Array, j: number, u: number): void {
for (let i: number = 0; i < 4; i++) {
x[j + i] = u & 255;
u >>>= 8;
}
}
function ts64(x: Uint8Array, i: number, u: u64): void {
x[i] = (u.hi >> 24) & 0xFF;
x[i + 1] = (u.hi >> 16) & 0xFF;
x[i + 2] = (u.hi >> 8) & 0xFF;
x[i + 3] = u.hi & 0xFF;
x[i + 4] = (u.lo >> 24) & 0xFF;
x[i + 5] = (u.lo >> 16) & 0xFF;
x[i + 6] = (u.lo >> 8) & 0xFF;
x[i + 7] = u.lo & 0xFF;
}
export function _vn(x: Uint8Array, xi: number, y: Uint8Array, yi: number, n: number): number {
let d: number = 0;
for (let i: number = 0; i < n; i++) {
d |= x[xi + i] ^ y[yi + i];
};
return ((1 & ((d - 1) >>> 8)) - 1);
}
export function crypto_verify_16(x: Uint8Array, xi: number, y: Uint8Array, yi: number): number {
return _vn(x, xi, y, yi, 16);
}
export function crypto_verify_32(x: Uint8Array, xi: number, y: Uint8Array, yi: number): number {
return _vn(x, xi, y, yi, 32);
}
function core(out: Uint8Array, inp: Uint8Array, k: Uint8Array, c: Uint8Array, h: boolean): void {
const t: Uint32Array = new Uint32Array(4);
const w: Uint32Array = new Uint32Array(16);
const x: Uint32Array = new Uint32Array(16);
const y: Uint32Array = new Uint32Array(16);
for (let i: number = 0; i < 4; i++) {
x[5 * i] = ld32(c, 4 * i);
x[1 + i] = ld32(k, 4 * i);
x[6 + i] = ld32(inp, 4 * i);
x[11 + i] = ld32(k, 16 + 4 * i);
}
for (let i: number = 0; i < 16; i++) {
y[i] = x[i];
}
for (let i: number = 0; i < 20; i++) {
for (let j: number = 0; j < 4; j++) {
for (let m: number = 0; m < 4; m++) {
t[m] = x[(5 * j + 4 * m) % 16];
}
t[1] ^= L32((t[0] + t[3]) | 0, 7);
t[2] ^= L32((t[1] + t[0]) | 0, 9);
t[3] ^= L32((t[2] + t[1]) | 0, 13);
t[0] ^= L32((t[3] + t[2]) | 0, 18);
for (let m: number = 0; m < 4; m++) {
w[4 * j + (j + m) % 4] = t[m];
}
}
for (let m: number = 0; m < 16; m++) {
x[m] = w[m];
}
}
if (h) {
for (let i: number = 0; i < 16; i++) {
x[i] = (x[i] + y[i]) | 0;
}
for (let i: number = 0; i < 4; i++) {
x[5 * i] = (x[5 * i] - ld32(c, 4 * i)) | 0;
x[6 + i] = (x[6 + i] - ld32(inp, 4 * i)) | 0;
}
for (let i: number = 0; i < 4; i++) {
st32(out, 4 * i, x[5 * i]);
st32(out, 16 + 4 * i, x[6 + i]);
}
} else {
for (let i: number = 0; i < 16; i++) {
st32(out, 4 * i, (x[i] + y[i]) | 0);
}
}
}
function crypto_core_salsa20(out: Uint8Array, inp: Uint8Array, k: Uint8Array, c: Uint8Array): 0 {
core(out, inp, k, c, false);
return 0;
}
export function crypto_core_hsalsa20(out: Uint8Array, inp: Uint8Array, k: Uint8Array, c: Uint8Array): 0 {
core(out, inp, k, c, true);
return 0;
}
const sigma: Uint8Array = new Uint8Array([101, 120, 112, 97, 110, 100, 32, 51, 50, 45, 98, 121, 116, 101, 32, 107]);
// "expand 32-byte k"
export function crypto_stream_salsa20_xor(c: Uint8Array, cpos: number, m: Uint8Array | null, mpos: number, b: number, n: Uint8Array, k: Uint8Array): 0 {
const x: Uint8Array = new Uint8Array(64);
const z: Uint8Array = new Uint8Array(16);
if (!b) {
return 0;
}
for (let i: number = 0; i < 16; i++) {
z[i] = 0;
}
for (let i: number = 0; i < 8; i++) {
z[i] = n[i];
}
while (b >= 64) {
crypto_core_salsa20(x, z, k, sigma);
for (let i: number = 0; i < 64; i++) {
c[cpos + i] = (m ? m[mpos + i] : 0) ^ x[i];
}
let u: number = 1;
for (let i: number = 8; i < 16; i++) {
u = u + (z[i] & 0xFF) | 0;
z[i] = u & 0xFF;
u >>>= 8;
}
b -= 64;
cpos += 64;
if (m) {
mpos += 64;
}
}
if (b > 0) {
crypto_core_salsa20(x, z, k, sigma);
for (let i: number = 0; i < b; i++) {
c[cpos + i] = (m ? m[mpos + i] : 0) ^ x[i];
}
}
return 0;
}
export function crypto_stream_salsa20(c: Uint8Array, cpos: number, d: number, n: Uint8Array, k: Uint8Array): 0 {
return crypto_stream_salsa20_xor(c, cpos, null, 0, d, n, k);
}
export function crypto_stream(c: Uint8Array, cpos: number, d: number, n: Uint8Array, k: Uint8Array): 0 {
const s: Uint8Array = new Uint8Array(32);
crypto_core_hsalsa20(s, n, k, sigma);
return crypto_stream_salsa20(c, cpos, d, n.subarray(16), s);
}
export function crypto_stream_xor(c: Uint8Array, cpos: number, m: Uint8Array, mpos: number, d: number, n: Uint8Array, k: Uint8Array): 0 {
const s: Uint8Array = new Uint8Array(32);
crypto_core_hsalsa20(s, n, k, sigma);
return crypto_stream_salsa20_xor(c, cpos, m, mpos, d, n.subarray(16), s);
}
function add1305(h: Uint32Array, c: Uint32Array): void {
let u: number = 0;
for (let j: number = 0; j < 17; j++) {
u = (u + ((h[j] + c[j]) | 0)) | 0;
h[j] = u & 255;
u >>>= 8;
}
}
const minusp: Uint32Array = new Uint32Array([5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252]);
export function crypto_onetimeauth(out: Uint8Array, outpos: number, m: Uint8Array, mpos: number, n: number, k: Uint8Array): 0 {
const c = new Uint32Array(17);
const g = new Uint32Array(17);
const h = new Uint32Array(17);
const r = new Uint32Array(17);
const x = new Uint32Array(17);
let j: number;
for (j = 0; j < 17; j++) {
r[j] = h[j] = 0;
}
for (j = 0; j < 16; j++) {
r[j] = k[j];
}
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 (j = 0; j < 17; j++) {
c[j] = 0;
}
for (j = 0; (j < 16) && (j < n); ++j) {
c[j] = m[mpos + j];
}
c[j] = 1;
mpos += j;
n -= j;
add1305(h, c);
for (let i: number = 0; i < 17; i++) {
x[i] = 0;
for (j = 0; j < 17; j++) {
x[i] = (x[i] + (h[j] * ((j <= i) ? r[i - j] : ((320 * r[i + 17 - j]) | 0))) | 0) | 0;
}
}
for (let i: number = 0; i < 17; i++) {
h[i] = x[i];
}
let u: number = 0;
for (j = 0; j < 16; j++) {
u = (u + h[j]) | 0;
h[j] = u & 255;
u >>>= 8;
}
u = (u + h[16]) | 0;
h[16] = u & 3;
u = (5 * (u >>> 2)) | 0;
for (j = 0; j < 16; j++) {
u = (u + h[j]) | 0;
h[j] = u & 255;
u >>>= 8;
}
u = (u + h[16]) | 0;
h[16] = u;
}
for (j = 0; j < 17; j++) {
g[j] = h[j];
}
add1305(h, minusp);
const s: number = (-(h[16] >>> 7) | 0);
for (j = 0; j < 17; j++) {
h[j] ^= s & (g[j] ^ h[j]);
}
for (j = 0; j < 16; j++) {
c[j] = k[j + 16];
}
c[16] = 0;
add1305(h, c);
for (j = 0; j < 16; j++) {
out[outpos + j] = h[j];
}
return 0;
}
export function crypto_onetimeauth_verify(h: Uint8Array, hpos: number, m: Uint8Array, mpos: number, n: number, k: Uint8Array): number {
const x: Uint8Array = new Uint8Array(16);
crypto_onetimeauth(x, 0, m, mpos, n, k);
return crypto_verify_16(h, hpos, x, 0);
}
export function crypto_secretbox(c: Uint8Array, m: Uint8Array, d: number, n: Uint8Array, k: Uint8Array): -1 | 0 {
if (d < 32) {
return -1;
}
crypto_stream_xor(c, 0, m, 0, d, n, k);
crypto_onetimeauth(c, 16, c, 32, d - 32, c);
for (let i: number = 0; i < 16; i++) {
c[i] = 0;
}
return 0;
}
export function crypto_secretbox_open(m: Uint8Array, c: Uint8Array, d: number, n: Uint8Array, k: Uint8Array): -1 | 0 {
const x: Uint8Array = new Uint8Array(32);
if (d < 32) {
return -1;
}
crypto_stream(x, 0, 32, n, k);
if (crypto_onetimeauth_verify(c, 16, c, 32, d - 32, x) !== 0) {
return -1;
}
crypto_stream_xor(m, 0, c, 0, d, n, k);
for (let i: number = 0; i < 32; i++) {
m[i] = 0;
}
return 0;
}
export function set25519(r: Float64Array, a: Float64Array): void {
for (let i: number = 0; i < 16; i++) {
r[i] = a[i] | 0;
}
}
function car25519(o: Float64Array): void {
for (let i: number = 0; i < 16; i++) {
o[i] += 65536;
const c: number = Math.floor(o[i] / 65536);
o[(i + 1) * (i < 15 ? 1 : 0)] += c - 1 + 37 * (c - 1) * (i === 15 ? 1 : 0);
o[i] -= (c * 65536);
}
}
function sel25519(p: Float64Array, q: Float64Array, b: number): void {
const c: number = ~(b - 1);
for (let i: number = 0; i < 16; i++) {
const t: number = c & (p[i] ^ q[i]);
p[i] ^= t;
q[i] ^= t;
}
}
export function pack25519(o: Uint8Array, n: Float64Array): void {
const m: Float64Array = gf();
const t: Float64Array = gf();
for (let i: number = 0; i < 16; i++) {
t[i] = n[i];
}
car25519(t);
car25519(t);
car25519(t);
for (let j: number = 0; j < 2; j++) {
m[0] = t[0] - 0xFFED;
for (let i: number = 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);
const b: number = (m[15] >> 16) & 1;
m[14] &= 0xFFFF;
sel25519(t, m, 1 - b);
}
for (let i: number = 0; i < 16; i++) {
o[2 * i] = t[i] & 0xFF;
o[2 * i + 1] = t[i] >> 8;
}
}
function neq25519(a: Float64Array, b: Float64Array): number {
const c: Uint8Array = new Uint8Array(32);
const d: Uint8Array = new Uint8Array(32);
pack25519(c, a);
pack25519(d, b);
return crypto_verify_32(c, 0, d, 0);
}
function par25519(a: Float64Array): number {
const d: Uint8Array = new Uint8Array(32);
pack25519(d, a);
return (d[0] & 1);
}
export function unpack25519(o: Float64Array, n: Uint8Array): void {
for (let i: number = 0; i < 16; i++) {
o[i] = n[2 * i] + (n[2 * i + 1] << 8);
}
o[15] &= 0x7FFF;
}
export function A(o: Float64Array, a: Float64Array, b: Float64Array): void {
for (let i: number = 0; i < 16; i++) {
o[i] = (a[i] + b[i]) | 0;
}
}
export function Z(o: Float64Array, a: Float64Array, b: Float64Array): void {
for (let i: number = 0; i < 16; i++) {
o[i] = (a[i] - b[i]) | 0;
}
}
export function M(o: Float64Array, a: Float64Array, b: Float64Array): void {
const t: Float64Array = new Float64Array(31);
for (let i: number = 0; i < 31; i++) {
t[i] = 0;
}
for (let i: number = 0; i < 16; i++) {
for (let j: number = 0; j < 16; j++) {
t[i + j] += a[i] * b[j];
}
}
for (let i: number = 0; i < 15; i++) {
t[i] += 38 * t[i + 16];
}
for (let i: number = 0; i < 16; i++) {
o[i] = t[i];
}
car25519(o);
car25519(o);
}
export function S(o: Float64Array, a: Float64Array): void {
M(o, a, a);
}
function inv25519(o: Float64Array, i: Float64Array): void {
const c: Float64Array = gf();
for (let a: number = 0; a < 16; a++) {
c[a] = i[a];
}
for (let a: number = 253; a >= 0; a--) {
S(c, c);
if (a !== 2 && a !== 4) {
M(c, c, i);
}
}
for (let a: number = 0; a < 16; a++) {
o[a] = c[a];
}
}
export function pow2523(o: Float64Array, i: Float64Array): void {
const c: Float64Array = gf();
for (let a: number = 0; a < 16; a++) {
c[a] = i[a];
}
for (let a: number = 250; a >= 0; a--) {
S(c, c);
if (a !== 1) {
M(c, c, i);
}
}
for (let a: number = 0; a < 16; a++) {
o[a] = c[a];
}
}
export function crypto_scalarmult(q: Uint8Array, n: Uint8Array, p: Uint8Array): number {
const a: Float64Array = gf();
const b: Float64Array = gf();
const c: Float64Array = gf();
const d: Float64Array = gf();
const e: Float64Array = gf();
const f: Float64Array = gf();
const x: Float64Array = new Float64Array(80);
const z: Uint8Array = new Uint8Array(32);
for (let i: number = 0; i < 31; i++) {
z[i] = n[i];
}
z[31] = (n[31] & 127) | 64;
z[0] &= 248;
unpack25519(x, p);
for (let i: number = 0; i < 16; i++) {
b[i] = x[i];
d[i] = a[i] = c[i] = 0;
}
a[0] = d[0] = 1;
for (let i: number = 254; i >= 0; --i) {
const r: number = (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, x);
S(b, e);
sel25519(a, b, r);
sel25519(c, d, r);
}
for (let i: number = 0; i < 16; i++) {
x[i + 16] = a[i];
x[i + 32] = c[i];
x[i + 48] = b[i];
x[i + 64] = d[i];
}
const x32: Float64Array = x.subarray(32);
const x16: Float64Array = x.subarray(16);
inv25519(x32, x32);
M(x16, x16, x32);
pack25519(q, x16);
return 0;
}
export function crypto_scalarmult_base(q: Uint8Array, n: Uint8Array): number {
return crypto_scalarmult(q, n, _9);
}
export function crypto_box_keypair(y: Uint8Array, x: Uint8Array): number {
randomBytesByTargetSizeFromBegin(x, 32);
return crypto_scalarmult_base(y, x);
}
export function crypto_box_beforenm(k: Uint8Array, y: Uint8Array, x: Uint8Array): 0 {
const s: Uint8Array = new Uint8Array(32);
crypto_scalarmult(s, x, y);
return crypto_core_hsalsa20(k, _0, s, sigma);
}
export const crypto_box_afternm = crypto_secretbox;
const crypto_box_open_afternm = crypto_secretbox_open;
export function crypto_box(c: Uint8Array, m: Uint8Array, d: number, n: Uint8Array, y: Uint8Array, x: Uint8Array): -1 | 0 {
const k: Uint8Array = new Uint8Array(32);
crypto_box_beforenm(k, y, x);
return crypto_box_afternm(c, m, d, n, k);
}
export function crypto_box_open(m: Uint8Array, c: Uint8Array, d: number, n: Uint8Array, y: Uint8Array, x: Uint8Array): -1 | 0 {
const k: Uint8Array = new Uint8Array(32);
crypto_box_beforenm(k, y, x);
return crypto_box_open_afternm(m, c, d, n, k);
}
function add64(...inputs: readonly u64[]): u64 {
const m16: number = 65535;
let a: number = 0;
let b: number = 0;
let c: number = 0;
let d: number = 0;
for (const input of inputs) {
a += (input.lo & m16);
b += (input.lo >>> 16);
c += (input.hi & m16);
d += (input.hi >>> 16);
}
b += (a >>> 16);
c += (b >>> 16);
d += (c >>> 16);
return new u64((c & m16) | (d << 16), (a & m16) | (b << 16));
}
function shr64(x: u64, c: number): u64 {
return new u64(x.hi >>> c, (x.lo >>> c) | (x.hi << (32 - c)));
}
function xor64(...inputs: readonly u64[]): u64 {
let h: number = 0;
let l: number = 0;
for (const input of inputs) {
h ^= input.hi;
l ^= input.lo;
}
return new u64(h, l);
}
function R(x: u64, c: number): u64 {
let h: number = 0;
let l: number = 0;
const c1: number = 32 - c;
if (c < 32) {
h = (x.hi >>> c) | (x.lo << c1);
l = (x.lo >>> c) | (x.hi << c1);
} else if (c < 64) {
h = (x.lo >>> c) | (x.hi << c1);
l = (x.hi >>> c) | (x.lo << c1);
}
return new u64(h, l);
}
function Ch(x: u64, y: u64, z: u64): u64 {
return new u64((x.hi & y.hi) ^ (~x.hi & z.hi), (x.lo & y.lo) ^ (~x.lo & z.lo));
}
function Maj(x: u64, y: u64, z: u64): u64 {
return new u64((x.hi & y.hi) ^ (x.hi & z.hi) ^ (y.hi & z.hi), (x.lo & y.lo) ^ (x.lo & z.lo) ^ (y.lo & z.lo));
}
function Sigma0(x: u64): u64 {
return xor64(R(x, 28), R(x, 34), R(x, 39));
}
function Sigma1(x: u64): u64 {
return xor64(R(x, 14), R(x, 18), R(x, 41));
}
function sigma0(x: u64): u64 {
return xor64(R(x, 1), R(x, 8), shr64(x, 7));
}
function sigma1(x: u64): u64 {
return xor64(R(x, 19), R(x, 61), shr64(x, 6));
}
const K: u64[] = [
new u64(0x428A2F98, 0xD728AE22),
new u64(0x71374491, 0x23EF65CD),
new u64(0xB5C0FBCF, 0xEC4D3B2F),
new u64(0xE9B5DBA5, 0x8189DBBC),
new u64(0x3956C25B, 0xF348B538),
new u64(0x59F111F1, 0xB605D019),
new u64(0x923F82A4, 0xAF194F9B),
new u64(0xAB1C5ED5, 0xDA6D8118),
new u64(0xD807AA98, 0xA3030242),
new u64(0x12835B01, 0x45706FBE),
new u64(0x243185BE, 0x4EE4B28C),
new u64(0x550C7DC3, 0xD5FFB4E2),
new u64(0x72BE5D74, 0xF27B896F),
new u64(0x80DEB1FE, 0x3B1696B1),
new u64(0x9BDC06A7, 0x25C71235),
new u64(0xC19BF174, 0xCF692694),
new u64(0xE49B69C1, 0x9EF14AD2),
new u64(0xEFBE4786, 0x384F25E3),
new u64(0x0FC19DC6, 0x8B8CD5B5),
new u64(0x240CA1CC, 0x77AC9C65),
new u64(0x2DE92C6F, 0x592B0275),
new u64(0x4A7484AA, 0x6EA6E483),
new u64(0x5CB0A9DC, 0xBD41FBD4),
new u64(0x76F988DA, 0x831153B5),
new u64(0x983E5152, 0xEE66DFAB),
new u64(0xA831C66D, 0x2DB43210),
new u64(0xB00327C8, 0x98FB213F),
new u64(0xBF597FC7, 0xBEEF0EE4),
new u64(0xC6E00BF3, 0x3DA88FC2),
new u64(0xD5A79147, 0x930AA725),
new u64(0x06CA6351, 0xE003826F),
new u64(0x14292967, 0x0A0E6E70),
new u64(0x27B70A85, 0x46D22FFC),
new u64(0x2E1B2138, 0x5C26C926),
new u64(0x4D2C6DFC, 0x5AC42AED),
new u64(0x53380D13, 0x9D95B3DF),
new u64(0x650A7354, 0x8BAF63DE),
new u64(0x766A0ABB, 0x3C77B2A8),
new u64(0x81C2C92E, 0x47EDAEE6),
new u64(0x92722C85, 0x1482353B),
new u64(0xA2BFE8A1, 0x4CF10364),
new u64(0xA81A664B, 0xBC423001),
new u64(0xC24B8B70, 0xD0F89791),
new u64(0xC76C51A3, 0x0654BE30),
new u64(0xD192E819, 0xD6EF5218),
new u64(0xD6990624, 0x5565A910),
new u64(0xF40E3585, 0x5771202A),
new u64(0x106AA070, 0x32BBD1B8),
new u64(0x19A4C116, 0xB8D2D0C8),
new u64(0x1E376C08, 0x5141AB53),
new u64(0x2748774C, 0xDF8EEB99),
new u64(0x34B0BCB5, 0xE19B48A8),
new u64(0x391C0CB3, 0xC5C95A63),
new u64(0x4ED8AA4A, 0xE3418ACB),
new u64(0x5B9CCA4F, 0x7763E373),
new u64(0x682E6FF3, 0xD6B2B8A3),
new u64(0x748F82EE, 0x5DEFB2FC),
new u64(0x78A5636F, 0x43172F60),
new u64(0x84C87814, 0xA1F0AB72),
new u64(0x8CC70208, 0x1A6439EC),
new u64(0x90BEFFFA, 0x23631E28),
new u64(0xA4506CEB, 0xDE82BDE9),
new u64(0xBEF9A3F7, 0xB2C67915),
new u64(0xC67178F2, 0xE372532B),
new u64(0xCA273ECE, 0xEA26619C),
new u64(0xD186B8C7, 0x21C0C207),
new u64(0xEADA7DD6, 0xCDE0EB1E),
new u64(0xF57D4F7F, 0xEE6ED178),
new u64(0x06F067AA, 0x72176FBA),
new u64(0x0A637DC5, 0xA2C898A6),
new u64(0x113F9804, 0xBEF90DAE),
new u64(0x1B710B35, 0x131C471B),
new u64(0x28DB77F5, 0x23047D84),
new u64(0x32CAAB7B, 0x40C72493),
new u64(0x3C9EBE0A, 0x15C9BEBC),
new u64(0x431D67C4, 0x9C100D4C),
new u64(0x4CC5D4BE, 0xCB3E42B6),
new u64(0x597F299C, 0xFC657E2A),
new u64(0x5FCB6FAB, 0x3AD6FAEC),
new u64(0x6C44198C, 0x4A475817)
];
function crypto_hashblocks(x: Uint8Array, m: Uint8Array, n: number): number {
const a: u64[] = [];
const b: u64[] = [];
const w: u64[] = [];
const z: u64[] = [];
for (let i: number = 0; i < 8; i++) {
z[i] = a[i] = dl64(x, 8 * i);
}
let pos: number = 0;
while (n >= 128) {
for (let i: number = 0; i < 16; i++) {
w[i] = dl64(m, 8 * i + pos);
}
for (let i: number = 0; i < 80; i++) {
for (let j: number = 0; j < 8; j++) {
b[j] = a[j];
}
const t: u64 = add64(a[7], Sigma1(a[4]), Ch(a[4], a[5], a[6]), K[i], w[i % 16]);
b[7] = add64(t, Sigma0(a[0]), Maj(a[0], a[1], a[2]));
b[3] = add64(b[3], t);
for (let j: number = 0; j < 8; j++) {
a[(j + 1) % 8] = b[j];
}
if (i % 16 === 15) {
for (let j: number = 0; j < 16; j++) {
w[j] = add64(w[j], w[(j + 9) % 16], sigma0(w[(j + 1) % 16]), sigma1(w[(j + 14) % 16]));
}
}
}
for (let i: number = 0; i < 8; i++) {
a[i] = add64(a[i], z[i]);
z[i] = a[i];
}
pos += 128;
n -= 128;
}
for (let i: number = 0; i < 8; i++) {
ts64(x, 8 * i, z[i]);
}
return n;
}
const iv: Uint8Array = new Uint8Array([
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
]);
export function crypto_hash(out: Uint8Array, m: Uint8Array, n: number): 0 {
const b: number = n;
const h: Uint8Array = new Uint8Array(64);
const x: Uint8Array = new Uint8Array(256);
for (let i: number = 0; i < 64; i++) {
h[i] = iv[i];
}
crypto_hashblocks(h, m, n);
n %= 128;
for (let i: number = 0; i < 256; i++) {
x[i] = 0;
}
for (let i: number = 0; i < n; i++) {
x[i] = m[b - n + i];
}
x[n] = 128;
n = 256 - 128 * (n < 112 ? 1 : 0);
x[n - 9] = 0;
ts64(x, n - 8, new u64((b / 0x20000000) | 0, b << 3));
crypto_hashblocks(h, x, n);
for (let i: number = 0; i < 64; i++) {
out[i] = h[i];
}
return 0;
}
export function add(p: Float64Array[], q: Float64Array[]): void {
const a: Float64Array = gf();
const b: Float64Array = gf();
const c: Float64Array = gf();
const d: Float64Array = gf();
const e: Float64Array = gf();
const f: Float64Array = gf();
const g: Float64Array = gf();
const h: Float64Array = gf();
const t: Float64Array = gf();
Z(a, p[1], p[0]);
Z(t, q[1], q[0]);
M(a, a, t);
A(b, p[0], p[1]);
A(t, q[0], q[1]);
M(b, b, t);
M(c, p[3], q[3]);
M(c, c, D2);
M(d, p[2], q[2]);
A(d, d, d);
Z(e, b, a);
Z(f, d, c);
A(g, d, c);
A(h, b, a);
M(p[0], e, f);
M(p[1], h, g);
M(p[2], g, f);
M(p[3], e, h);
}
function cswap(p: Float64Array[], q: Float64Array[], b: number): void {
for (let i: number = 0; i < 4; i++) {
sel25519(p[i], q[i], b);
}
}
function pack(r: Uint8Array, p: Float64Array[]): void {
const tx: Float64Array = gf();
const ty: Float64Array = gf();
const zi: Float64Array = gf();
inv25519(zi, p[2]);
M(tx, p[0], zi);
M(ty, p[1], zi);
pack25519(r, ty);
r[31] ^= par25519(tx) << 7;
}
export function scalarmult(p: Float64Array[], q: Float64Array[], s: Uint8Array): void {
set25519(p[0], gf0);
set25519(p[1], gf1);
set25519(p[2], gf1);
set25519(p[3], gf0);
for (let i: number = 255; i >= 0; --i) {
const b: number = (s[(i / 8) | 0] >> (i & 7)) & 1;
cswap(p, q, b);
add(q, p);
add(p, p);
cswap(p, q, b);
}
}
export function scalarbase(p: Float64Array[], s: Uint8Array): void {
const q: Float64Array[] = [gf(), gf(), gf(), gf()];
set25519(q[0], X);
set25519(q[1], Y);
set25519(q[2], gf1);
M(q[3], X, Y);
scalarmult(p, q, s);
}
export function crypto_sign_keypair(pk: Uint8Array, sk: Uint8Array, seeded: boolean = false): 0 {
const d: Uint8Array = new Uint8Array(64);
const p: Float64Array[] = [gf(), gf(), gf(), gf()];
if (!seeded) {
randomBytesByTargetSizeFromBegin(sk, 32);
}
crypto_hash(d, sk, 32);
d[0] &= 248;
d[31] &= 127;
d[31] |= 64;
scalarbase(p, d);
pack(pk, p);
for (let i: number = 0; i < 32; i++) {
sk[i + 32] = pk[i];
}
return 0;
}
export const L: Float64Array = new Float64Array([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]);
export function modL(r: Uint8Array, x: Float64Array): void {
let carry: number;
let j: number;
let k: number;
for (let i: number = 63; i >= 32; --i) {
carry = 0;
for (j = i - 32, k = i - 12; j < k; ++j) {
x[j] += carry - 16 * x[i] * L[j - (i - 32)];
carry = Math.floor((x[j] + 128) / 256);
x[j] -= carry * 256;
}
x[j] += carry;
x[i] = 0;
}
carry = 0;
for (j = 0; j < 32; j++) {
x[j] += carry - (x[31] >> 4) * L[j];
carry = x[j] >> 8;
x[j] &= 255;
}
for (j = 0; j < 32; j++) {
x[j] -= carry * L[j];
}
for (let i: number = 0; i < 32; i++) {
x[i + 1] += x[i] >> 8;
r[i] = x[i] & 255;
}
}
function reduce(r: Uint8Array): void {
const x: Float64Array = new Float64Array(64);
for (let i: number = 0; i < 64; i++) {
x[i] = r[i];
}
for (let i: number = 0; i < 64; i++) {
r[i] = 0;
}
modL(r, x);
}
// NOTE: Difference from C - smlen returned, not passed as argument.
export function crypto_sign(sm: Uint8Array, m: Uint8Array, n: number, sk: Uint8Array): number {
const d: Uint8Array = new Uint8Array(64);
const h: Uint8Array = new Uint8Array(64);
const p: Float64Array[] = [gf(), gf(), gf(), gf()];
const r: Uint8Array = new Uint8Array(64);
const x: Float64Array = new Float64Array(64);
crypto_hash(d, sk, 32);
d[0] &= 248;
d[31] &= 127;
d[31] |= 64;
const smlen: number = n + 64;
for (let i: number = 0; i < n; i++) {
sm[64 + i] = m[i];
}
for (let i: number = 0; i < 32; i++) {
sm[32 + i] = d[32 + i];
}
crypto_hash(r, sm.subarray(32), n + 32);
reduce(r);
scalarbase(p, r);
pack(sm, p);
for (let i: number = 32; i < 64; i++) {
sm[i] = sk[i];
}
crypto_hash(h, sm, n + 64);
reduce(h);
for (let i: number = 0; i < 64; i++) {
x[i] = 0;
}
for (let i: number = 0; i < 32; i++) {
x[i] = r[i];
}
for (let i: number = 0; i < 32; i++) {
for (let j: number = 0; j < 32; j++) {
x[i + j] += h[i] * d[j];
}
}
modL(sm.subarray(32), x);
return smlen;
}
function unpackneg(r: Float64Array[], p: Uint8Array): -1 | 0 {
const chk: Float64Array = gf();
const den: Float64Array = gf();
const den2: Float64Array = gf();
const den4: Float64Array = gf();
const den6: Float64Array = gf();
const num: Float64Array = gf();
const t: Float64Array = gf();
set25519(r[2], gf1);
unpack25519(r[1], p);
S(num, r[1]);
M(den, num, D);
Z(num, num, r[2]);
A(den, r[2], 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(r[0], t, den);
S(chk, r[0]);
M(chk, chk, den);
if (neq25519(chk, num)) {
M(r[0], r[0], I);
}
S(chk, r[0]);
M(chk, chk, den);
if (neq25519(chk, num)) {
return -1;
}
if (par25519(r[0]) === (p[31] >> 7)) {
Z(r[0], gf0, r[0]);
}
M(r[3], r[0], r[1]);
return 0;
}
export function crypto_sign_open(m: Uint8Array, sm: Uint8Array, n: number, pk: Uint8Array): number {
const h: Uint8Array = new Uint8Array(64);
const p: Float64Array[] = [gf(), gf(), gf(), gf()];
const q: Float64Array[] = [gf(), gf(), gf(), gf()];
const t: Uint8Array = new Uint8Array(32);
if (n < 64) {
return -1;
}
if (unpackneg(q, pk)) {
return -1;
}
for (let i: number = 0; i < n; i++) {
m[i] = sm[i];
}
for (let i: number = 0; i < 32; i++) {
m[i + 32] = pk[i];
}
crypto_hash(h, m, n);
reduce(h);
scalarmult(p, q, h);
scalarbase(q, sm.subarray(32));
add(p, q);
pack(t, p);
n -= 64;
if (crypto_verify_32(sm, 0, t, 0)) {
for (let i: number = 0; i < n; i++) {
m[i] = 0;
}
return -1;
}
for (let i: number = 0; i < n; i++) {
m[i] = sm[i + 64];
}
return n;
}
export const crypto_box_BEFORENMBYTES = 32;
export const crypto_box_PUBLICKEYBYTES = 32;
export const crypto_box_SECRETKEYBYTES = 32;
export const crypto_hash_BYTES = 64;
export const crypto_scalarmult_BYTES = 32;
export const crypto_scalarmult_SCALARBYTES = 32;
export const crypto_secretbox_BOXZEROBYTES = 16;
export const crypto_secretbox_KEYBYTES = 32;
export const crypto_secretbox_NONCEBYTES = 24;
export const crypto_secretbox_ZEROBYTES = 32;
export const crypto_sign_BYTES = 64;
export const crypto_sign_PUBLICKEYBYTES = 32;
export const crypto_sign_SECRETKEYBYTES = 64;
export const crypto_sign_SEEDBYTES = 32;
export const crypto_box_BOXZEROBYTES = crypto_secretbox_BOXZEROBYTES;
export const crypto_box_NONCEBYTES = crypto_secretbox_NONCEBYTES;
export const crypto_box_ZEROBYTES = crypto_secretbox_ZEROBYTES;