| 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 | |
| 10 | const MASK = (1n << 64n) - 1n; |
| 11 | |
| 12 | const IV = [ |
| 13 | 0x6a09e667f3bcc908n, 0xbb67ae8584caa73bn, 0x3c6ef372fe94f82bn, 0xa54ff53a5f1d36f1n, |
| 14 | 0x510e527fade682d1n, 0x9b05688c2b3e6c1fn, 0x1f83d9abfb41bd6bn, 0x5be0cd19137e2179n, |
| 15 | ]; |
| 16 | |
| 17 | const 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 | |
| 32 | const rotr = (x, n) => ((x >> n) | (x << (64n - n))) & MASK; |
| 33 | |
| 34 | function 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 | |
| 45 | function 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. |
| 74 | export 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 | |