// https://tools.ietf.org/html/rfc7693 import {MASK, int32s2bytes, bytes2int32s, zeroPad} from "./util.js"; const BLOCK_LEN = 64; const IV = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19]; function rrot(x, shift) { return ((x >>> shift) | (x << (32-shift))) & MASK; } /** * @param {number} outputLen output length in bytes, 1<=x<=32 * @param {Array} key byte array, 0<=key.length<=32 * @returns {BLAKE2S} */ export function BLAKE2S(outputLen = 32, key = []) { this._buffer = []; this._dataLen = [0, 0]; // low, high this._outputLen = outputLen; this._state = IV.slice(); this._state[0] ^= 0x01010000 ^ (key.length << 8) ^ this._outputLen; if (key.length > 0) {this.update(zeroPad(key, BLOCK_LEN));} } BLAKE2S.prototype.update = function (data) { for (let i = 0; i < data.length; i++) { if (this._buffer.length == BLOCK_LEN) { this._compress(false); this._buffer = []; } this._buffer.push(data[i]); this._dataLen[0] = (this._dataLen[0]+1) & MASK; if (this._dataLen[0] < this._buffer.length) {this._dataLen[1]++;} } }; BLAKE2S.prototype.digest = function () { this._buffer = zeroPad(this._buffer, BLOCK_LEN); this._compress(true); return int32s2bytes(this._state).slice(0, this._outputLen); }; BLAKE2S.prototype._compress = function (last) { const SIGMA = [ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0] ]; let v = this._state.concat(IV); v[12] ^= this._dataLen[0]; v[13] ^= this._dataLen[1]; if (last) {v[14] ^= MASK;} let data = bytes2int32s(this._buffer); for (let i = 0; i < 10; i++) { let perm = SIGMA[i % 10]; this._mix(v, 0, 4, 8, 12, data[perm[0]], data[perm[1]]); this._mix(v, 1, 5, 9, 13, data[perm[2]], data[perm[3]]); this._mix(v, 2, 6, 10, 14, data[perm[4]], data[perm[5]]); this._mix(v, 3, 7, 11, 15, data[perm[6]], data[perm[7]]); this._mix(v, 0, 5, 10, 15, data[perm[8]], data[perm[9]]); this._mix(v, 1, 6, 11, 12, data[perm[10]], data[perm[11]]); this._mix(v, 2, 7, 8, 13, data[perm[12]], data[perm[13]]); this._mix(v, 3, 4, 9, 14, data[perm[14]], data[perm[15]]); } this._state = this._state.map((x, i) => x ^ v[i] ^ v[i+8]); }; BLAKE2S.prototype._mix = function (arr, ia, ib, ic, id, x, y) { let a = arr[ia], b = arr[ib], c = arr[ic], d = arr[id]; a = (a + b + x) & MASK; d = rrot(d ^ a, 16); c = (c + d) & MASK; b = rrot(b ^ c, 12); a = (a + b + y) & MASK; d = rrot(d ^ a, 8); c = (c + d) & MASK; b = rrot(b ^ c, 7); arr[ia] = a; arr[ib] = b; arr[ic] = c; arr[id] = d; }; export function blake2s(data, outputLen = 32, key = []) { let h = new BLAKE2S(outputLen, key); for (let i = 0; i < data.length; i += BLOCK_LEN) { h.update(data.slice(i, i + BLOCK_LEN)); } return h.digest(); }