carlos / contra Public

Clone
git clone https://amadan.net/carlos/contra

Plain git — no account needed to clone.

Download

Download this file

1// BLAKE2b, as Argon2id needs it.
2//
3// Written with BigInt on purpose. Argon2 calls BLAKE2b only for H0, the two
4// initial blocks per lane, and the final tag — about sixty short calls in a
5// whole derivation. The 64 MiB of work lives in argon2id.js's compression
6// function, which is where the awkward 32-bit arithmetic goes and where it
7// earns its keep. Clarity is worth more than speed here, and being able to
8// read this against RFC 7693 is worth more than either.
9
10const MASK = (1n << 64n) - 1n;
11
12const IV = [
13 0x6a09e667f3bcc908n, 0xbb67ae8584caa73bn, 0x3c6ef372fe94f82bn, 0xa54ff53a5f1d36f1n,
14 0x510e527fade682d1n, 0x9b05688c2b3e6c1fn, 0x1f83d9abfb41bd6bn, 0x5be0cd19137e2179n,
15];
16
17const SIGMA = [
18 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
19 [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
20 [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
21 [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
22 [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
23 [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
24 [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
25 [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
26 [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
27 [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
28 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
29 [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
30];
31
32const rotr = (x, n) => ((x >> n) | (x << (64n - n))) & MASK;
33
34function mix(v, a, b, c, d, x, y) {
35 v[a] = (v[a] + v[b] + x) & MASK;
36 v[d] = rotr(v[d] ^ v[a], 32n);
37 v[c] = (v[c] + v[d]) & MASK;
38 v[b] = rotr(v[b] ^ v[c], 24n);
39 v[a] = (v[a] + v[b] + y) & MASK;
40 v[d] = rotr(v[d] ^ v[a], 16n);
41 v[c] = (v[c] + v[d]) & MASK;
42 v[b] = rotr(v[b] ^ v[c], 63n);
43}
44
45function compress(h, block, counter, last) {
46 const v = [...h, ...IV];
47 v[12] ^= counter & MASK;
48 v[13] ^= (counter >> 64n) & MASK;
49 if (last) v[14] ^= MASK;
50
51 const m = new Array(16);
52 for (let i = 0; i < 16; i++) {
53 let w = 0n;
54 for (let j = 7; j >= 0; j--) w = (w << 8n) | BigInt(block[i * 8 + j]);
55 m[i] = w;
56 }
57
58 for (let r = 0; r < 12; r++) {
59 const s = SIGMA[r];
60 mix(v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
61 mix(v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
62 mix(v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
63 mix(v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
64 mix(v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
65 mix(v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
66 mix(v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
67 mix(v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
68 }
69 for (let i = 0; i < 8; i++) h[i] = h[i] ^ v[i] ^ v[i + 8];
70}
71
72// blake2b hashes input to outLen bytes (1..64), unkeyed — Argon2 never uses
73// the key parameter.
74export function blake2b(outLen, input) {
75 if (outLen < 1 || outLen > 64) throw new Error("contra: blake2b output length");
76
77 const h = IV.slice();
78 h[0] ^= 0x01010000n ^ BigInt(outLen);
79
80 let counter = 0n;
81 let off = 0;
82 // Every full block except the last is compressed as non-final; the tail
83 // (or a single empty block, for empty input) is zero-padded and final.
84 while (input.length - off > 128) {
85 counter += 128n;
86 compress(h, input.subarray(off, off + 128), counter, false);
87 off += 128;
88 }
89 const tail = new Uint8Array(128);
90 tail.set(input.subarray(off));
91 counter += BigInt(input.length - off);
92 compress(h, tail, counter, true);
93
94 const out = new Uint8Array(outLen);
95 for (let i = 0; i < outLen; i++) {
96 out[i] = Number((h[i >> 3] >> BigInt(8 * (i & 7))) & 0xffn);
97 }
98 return out;
99}
100