Upload via GUI (7543 Dateien)
This commit is contained in:
+497
@@ -0,0 +1,497 @@
|
||||
/**
|
||||
* Argon2 KDF from RFC 9106. Can be used to create a key from password and salt.
|
||||
* We suggest to use Scrypt. JS Argon is 2-10x slower than native code because of 64-bitness:
|
||||
* * argon uses uint64, but JS doesn't have fast uint64array
|
||||
* * uint64 multiplication is 1/3 of time
|
||||
* * `P` function would be very nice with u64, because most of value will be in registers,
|
||||
* hovewer with u32 it will require 32 registers, which is too much.
|
||||
* * JS arrays do slow bound checks, so reading from `A2_BUF` slows it down
|
||||
* @module
|
||||
*/
|
||||
import { add3H, add3L, rotr32H, rotr32L, rotrBH, rotrBL, rotrSH, rotrSL } from './_u64.ts';
|
||||
import { blake2b } from './blake2.ts';
|
||||
import { abytes, clean, kdfInputToBytes, nextTick, u32, u8, type KDFInput } from './utils.ts';
|
||||
|
||||
const AT = { Argond2d: 0, Argon2i: 1, Argon2id: 2 } as const;
|
||||
type Types = (typeof AT)[keyof typeof AT];
|
||||
|
||||
const ARGON2_SYNC_POINTS = 4;
|
||||
const abytesOrZero = (buf?: KDFInput) => {
|
||||
if (buf === undefined) return Uint8Array.of();
|
||||
return kdfInputToBytes(buf);
|
||||
};
|
||||
|
||||
// u32 * u32 = u64
|
||||
function mul(a: number, b: number) {
|
||||
const aL = a & 0xffff;
|
||||
const aH = a >>> 16;
|
||||
const bL = b & 0xffff;
|
||||
const bH = b >>> 16;
|
||||
const ll = Math.imul(aL, bL);
|
||||
const hl = Math.imul(aH, bL);
|
||||
const lh = Math.imul(aL, bH);
|
||||
const hh = Math.imul(aH, bH);
|
||||
const carry = (ll >>> 16) + (hl & 0xffff) + lh;
|
||||
const high = (hh + (hl >>> 16) + (carry >>> 16)) | 0;
|
||||
const low = (carry << 16) | (ll & 0xffff);
|
||||
return { h: high, l: low };
|
||||
}
|
||||
|
||||
function mul2(a: number, b: number) {
|
||||
// 2 * a * b (via shifts)
|
||||
const { h, l } = mul(a, b);
|
||||
return { h: ((h << 1) | (l >>> 31)) & 0xffff_ffff, l: (l << 1) & 0xffff_ffff };
|
||||
}
|
||||
|
||||
// BlaMka permutation for Argon2
|
||||
// A + B + (2 * u32(A) * u32(B))
|
||||
function blamka(Ah: number, Al: number, Bh: number, Bl: number) {
|
||||
const { h: Ch, l: Cl } = mul2(Al, Bl);
|
||||
// A + B + (2 * A * B)
|
||||
const Rll = add3L(Al, Bl, Cl);
|
||||
return { h: add3H(Rll, Ah, Bh, Ch), l: Rll | 0 };
|
||||
}
|
||||
|
||||
// Temporary block buffer
|
||||
const A2_BUF = new Uint32Array(256); // 1024 bytes (matrix 16x16)
|
||||
|
||||
function G(a: number, b: number, c: number, d: number) {
|
||||
let Al = A2_BUF[2*a], Ah = A2_BUF[2*a + 1]; // prettier-ignore
|
||||
let Bl = A2_BUF[2*b], Bh = A2_BUF[2*b + 1]; // prettier-ignore
|
||||
let Cl = A2_BUF[2*c], Ch = A2_BUF[2*c + 1]; // prettier-ignore
|
||||
let Dl = A2_BUF[2*d], Dh = A2_BUF[2*d + 1]; // prettier-ignore
|
||||
|
||||
({ h: Ah, l: Al } = blamka(Ah, Al, Bh, Bl));
|
||||
({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
|
||||
({ Dh, Dl } = { Dh: rotr32H(Dh, Dl), Dl: rotr32L(Dh, Dl) });
|
||||
|
||||
({ h: Ch, l: Cl } = blamka(Ch, Cl, Dh, Dl));
|
||||
({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
|
||||
({ Bh, Bl } = { Bh: rotrSH(Bh, Bl, 24), Bl: rotrSL(Bh, Bl, 24) });
|
||||
|
||||
({ h: Ah, l: Al } = blamka(Ah, Al, Bh, Bl));
|
||||
({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
|
||||
({ Dh, Dl } = { Dh: rotrSH(Dh, Dl, 16), Dl: rotrSL(Dh, Dl, 16) });
|
||||
|
||||
({ h: Ch, l: Cl } = blamka(Ch, Cl, Dh, Dl));
|
||||
({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
|
||||
({ Bh, Bl } = { Bh: rotrBH(Bh, Bl, 63), Bl: rotrBL(Bh, Bl, 63) });
|
||||
|
||||
(A2_BUF[2 * a] = Al), (A2_BUF[2 * a + 1] = Ah);
|
||||
(A2_BUF[2 * b] = Bl), (A2_BUF[2 * b + 1] = Bh);
|
||||
(A2_BUF[2 * c] = Cl), (A2_BUF[2 * c + 1] = Ch);
|
||||
(A2_BUF[2 * d] = Dl), (A2_BUF[2 * d + 1] = Dh);
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
function P(
|
||||
v00: number, v01: number, v02: number, v03: number, v04: number, v05: number, v06: number, v07: number,
|
||||
v08: number, v09: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number,
|
||||
) {
|
||||
G(v00, v04, v08, v12);
|
||||
G(v01, v05, v09, v13);
|
||||
G(v02, v06, v10, v14);
|
||||
G(v03, v07, v11, v15);
|
||||
G(v00, v05, v10, v15);
|
||||
G(v01, v06, v11, v12);
|
||||
G(v02, v07, v08, v13);
|
||||
G(v03, v04, v09, v14);
|
||||
}
|
||||
|
||||
function block(x: Uint32Array, xPos: number, yPos: number, outPos: number, needXor: boolean) {
|
||||
for (let i = 0; i < 256; i++) A2_BUF[i] = x[xPos + i] ^ x[yPos + i];
|
||||
// columns (8)
|
||||
for (let i = 0; i < 128; i += 16) {
|
||||
// prettier-ignore
|
||||
P(
|
||||
i, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, i + 7,
|
||||
i + 8, i + 9, i + 10, i + 11, i + 12, i + 13, i + 14, i + 15
|
||||
);
|
||||
}
|
||||
// rows (8)
|
||||
for (let i = 0; i < 16; i += 2) {
|
||||
// prettier-ignore
|
||||
P(
|
||||
i, i + 1, i + 16, i + 17, i + 32, i + 33, i + 48, i + 49,
|
||||
i + 64, i + 65, i + 80, i + 81, i + 96, i + 97, i + 112, i + 113
|
||||
);
|
||||
}
|
||||
|
||||
if (needXor) for (let i = 0; i < 256; i++) x[outPos + i] ^= A2_BUF[i] ^ x[xPos + i] ^ x[yPos + i];
|
||||
else for (let i = 0; i < 256; i++) x[outPos + i] = A2_BUF[i] ^ x[xPos + i] ^ x[yPos + i];
|
||||
clean(A2_BUF);
|
||||
}
|
||||
|
||||
// Variable-Length Hash Function H'
|
||||
function Hp(A: Uint32Array, dkLen: number) {
|
||||
const A8 = u8(A);
|
||||
const T = new Uint32Array(1);
|
||||
const T8 = u8(T);
|
||||
T[0] = dkLen;
|
||||
// Fast path
|
||||
if (dkLen <= 64) return blake2b.create({ dkLen }).update(T8).update(A8).digest();
|
||||
const out = new Uint8Array(dkLen);
|
||||
let V = blake2b.create({}).update(T8).update(A8).digest();
|
||||
let pos = 0;
|
||||
// First block
|
||||
out.set(V.subarray(0, 32));
|
||||
pos += 32;
|
||||
// Rest blocks
|
||||
for (; dkLen - pos > 64; pos += 32) {
|
||||
const Vh = blake2b.create({}).update(V);
|
||||
Vh.digestInto(V);
|
||||
Vh.destroy();
|
||||
out.set(V.subarray(0, 32), pos);
|
||||
}
|
||||
// Last block
|
||||
out.set(blake2b(V, { dkLen: dkLen - pos }), pos);
|
||||
clean(V, T);
|
||||
return u32(out);
|
||||
}
|
||||
|
||||
// Used only inside process block!
|
||||
function indexAlpha(
|
||||
r: number,
|
||||
s: number,
|
||||
laneLen: number,
|
||||
segmentLen: number,
|
||||
index: number,
|
||||
randL: number,
|
||||
sameLane: boolean = false
|
||||
) {
|
||||
// This is ugly, but close enough to reference implementation.
|
||||
let area: number;
|
||||
if (r === 0) {
|
||||
if (s === 0) area = index - 1;
|
||||
else if (sameLane) area = s * segmentLen + index - 1;
|
||||
else area = s * segmentLen + (index == 0 ? -1 : 0);
|
||||
} else if (sameLane) area = laneLen - segmentLen + index - 1;
|
||||
else area = laneLen - segmentLen + (index == 0 ? -1 : 0);
|
||||
const startPos = r !== 0 && s !== ARGON2_SYNC_POINTS - 1 ? (s + 1) * segmentLen : 0;
|
||||
const rel = area - 1 - mul(area, mul(randL, randL).h).h;
|
||||
return (startPos + rel) % laneLen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Argon2 options.
|
||||
* * t: time cost, m: mem cost in kb, p: parallelization.
|
||||
* * key: optional key. personalization: arbitrary extra data.
|
||||
* * dkLen: desired number of output bytes.
|
||||
*/
|
||||
export type ArgonOpts = {
|
||||
t: number; // Time cost, iterations count
|
||||
m: number; // Memory cost (in KB)
|
||||
p: number; // Parallelization parameter
|
||||
version?: number; // Default: 0x13 (19)
|
||||
key?: KDFInput; // Optional key
|
||||
personalization?: KDFInput; // Optional arbitrary extra data
|
||||
dkLen?: number; // Desired number of returned bytes
|
||||
asyncTick?: number; // Maximum time in ms for which async function can block execution
|
||||
maxmem?: number;
|
||||
onProgress?: (progress: number) => void;
|
||||
};
|
||||
|
||||
const maxUint32 = Math.pow(2, 32);
|
||||
function isU32(num: number) {
|
||||
return Number.isSafeInteger(num) && num >= 0 && num < maxUint32;
|
||||
}
|
||||
|
||||
function argon2Opts(opts: ArgonOpts) {
|
||||
const merged: any = {
|
||||
version: 0x13,
|
||||
dkLen: 32,
|
||||
maxmem: maxUint32 - 1,
|
||||
asyncTick: 10,
|
||||
};
|
||||
for (let [k, v] of Object.entries(opts)) if (v != null) merged[k] = v;
|
||||
|
||||
const { dkLen, p, m, t, version, onProgress } = merged;
|
||||
if (!isU32(dkLen) || dkLen < 4) throw new Error('dkLen should be at least 4 bytes');
|
||||
if (!isU32(p) || p < 1 || p >= Math.pow(2, 24)) throw new Error('p should be 1 <= p < 2^24');
|
||||
if (!isU32(m)) throw new Error('m should be 0 <= m < 2^32');
|
||||
if (!isU32(t) || t < 1) throw new Error('t (iterations) should be 1 <= t < 2^32');
|
||||
if (onProgress !== undefined && typeof onProgress !== 'function')
|
||||
throw new Error('progressCb should be function');
|
||||
/*
|
||||
Memory size m MUST be an integer number of kibibytes from 8*p to 2^(32)-1. The actual number of blocks is m', which is m rounded down to the nearest multiple of 4*p.
|
||||
*/
|
||||
if (!isU32(m) || m < 8 * p) throw new Error('memory should be at least 8*p bytes');
|
||||
if (version !== 0x10 && version !== 0x13) throw new Error('unknown version=' + version);
|
||||
return merged;
|
||||
}
|
||||
|
||||
function argon2Init(password: KDFInput, salt: KDFInput, type: Types, opts: ArgonOpts) {
|
||||
password = kdfInputToBytes(password);
|
||||
salt = kdfInputToBytes(salt);
|
||||
abytes(password);
|
||||
abytes(salt);
|
||||
if (!isU32(password.length)) throw new Error('password should be less than 4 GB');
|
||||
if (!isU32(salt.length) || salt.length < 8)
|
||||
throw new Error('salt should be at least 8 bytes and less than 4 GB');
|
||||
if (!Object.values(AT).includes(type)) throw new Error('invalid type');
|
||||
let { p, dkLen, m, t, version, key, personalization, maxmem, onProgress, asyncTick } =
|
||||
argon2Opts(opts);
|
||||
|
||||
// Validation
|
||||
key = abytesOrZero(key);
|
||||
personalization = abytesOrZero(personalization);
|
||||
// H_0 = H^(64)(LE32(p) || LE32(T) || LE32(m) || LE32(t) ||
|
||||
// LE32(v) || LE32(y) || LE32(length(P)) || P ||
|
||||
// LE32(length(S)) || S || LE32(length(K)) || K ||
|
||||
// LE32(length(X)) || X)
|
||||
const h = blake2b.create({});
|
||||
const BUF = new Uint32Array(1);
|
||||
const BUF8 = u8(BUF);
|
||||
for (let item of [p, dkLen, m, t, version, type]) {
|
||||
BUF[0] = item;
|
||||
h.update(BUF8);
|
||||
}
|
||||
for (let i of [password, salt, key, personalization]) {
|
||||
BUF[0] = i.length; // BUF is u32 array, this is valid
|
||||
h.update(BUF8).update(i);
|
||||
}
|
||||
const H0 = new Uint32Array(18);
|
||||
const H0_8 = u8(H0);
|
||||
h.digestInto(H0_8);
|
||||
// 256 u32 = 1024 (BLOCK_SIZE), fills A2_BUF on processing
|
||||
|
||||
// Params
|
||||
const lanes = p;
|
||||
// m' = 4 * p * floor (m / 4p)
|
||||
const mP = 4 * p * Math.floor(m / (ARGON2_SYNC_POINTS * p));
|
||||
//q = m' / p columns
|
||||
const laneLen = Math.floor(mP / p);
|
||||
const segmentLen = Math.floor(laneLen / ARGON2_SYNC_POINTS);
|
||||
const memUsed = mP * 256;
|
||||
if (!isU32(maxmem) || memUsed > maxmem)
|
||||
throw new Error(
|
||||
'mem should be less than 2**32, got: maxmem=' + maxmem + ', memused=' + memUsed
|
||||
);
|
||||
const B = new Uint32Array(memUsed);
|
||||
// Fill first blocks
|
||||
for (let l = 0; l < p; l++) {
|
||||
const i = 256 * laneLen * l;
|
||||
// B[i][0] = H'^(1024)(H_0 || LE32(0) || LE32(i))
|
||||
H0[17] = l;
|
||||
H0[16] = 0;
|
||||
B.set(Hp(H0, 1024), i);
|
||||
// B[i][1] = H'^(1024)(H_0 || LE32(1) || LE32(i))
|
||||
H0[16] = 1;
|
||||
B.set(Hp(H0, 1024), i + 256);
|
||||
}
|
||||
let perBlock = () => {};
|
||||
if (onProgress) {
|
||||
const totalBlock = t * ARGON2_SYNC_POINTS * p * segmentLen;
|
||||
// Invoke callback if progress changes from 10.01 to 10.02
|
||||
// Allows to draw smooth progress bar on up to 8K screen
|
||||
const callbackPer = Math.max(Math.floor(totalBlock / 10000), 1);
|
||||
let blockCnt = 0;
|
||||
perBlock = () => {
|
||||
blockCnt++;
|
||||
if (onProgress && (!(blockCnt % callbackPer) || blockCnt === totalBlock))
|
||||
onProgress(blockCnt / totalBlock);
|
||||
};
|
||||
}
|
||||
clean(BUF, H0);
|
||||
return { type, mP, p, t, version, B, laneLen, lanes, segmentLen, dkLen, perBlock, asyncTick };
|
||||
}
|
||||
|
||||
function argon2Output(B: Uint32Array, p: number, laneLen: number, dkLen: number) {
|
||||
const B_final = new Uint32Array(256);
|
||||
for (let l = 0; l < p; l++)
|
||||
for (let j = 0; j < 256; j++) B_final[j] ^= B[256 * (laneLen * l + laneLen - 1) + j];
|
||||
const res = u8(Hp(B_final, dkLen));
|
||||
clean(B_final);
|
||||
return res;
|
||||
}
|
||||
|
||||
function processBlock(
|
||||
B: Uint32Array,
|
||||
address: Uint32Array,
|
||||
l: number,
|
||||
r: number,
|
||||
s: number,
|
||||
index: number,
|
||||
laneLen: number,
|
||||
segmentLen: number,
|
||||
lanes: number,
|
||||
offset: number,
|
||||
prev: number,
|
||||
dataIndependent: boolean,
|
||||
needXor: boolean
|
||||
) {
|
||||
if (offset % laneLen) prev = offset - 1;
|
||||
let randL, randH;
|
||||
if (dataIndependent) {
|
||||
let i128 = index % 128;
|
||||
if (i128 === 0) {
|
||||
address[256 + 12]++;
|
||||
block(address, 256, 2 * 256, 0, false);
|
||||
block(address, 0, 2 * 256, 0, false);
|
||||
}
|
||||
randL = address[2 * i128];
|
||||
randH = address[2 * i128 + 1];
|
||||
} else {
|
||||
const T = 256 * prev;
|
||||
randL = B[T];
|
||||
randH = B[T + 1];
|
||||
}
|
||||
// address block
|
||||
const refLane = r === 0 && s === 0 ? l : randH % lanes;
|
||||
const refPos = indexAlpha(r, s, laneLen, segmentLen, index, randL, refLane == l);
|
||||
const refBlock = laneLen * refLane + refPos;
|
||||
// B[i][j] = G(B[i][j-1], B[l][z])
|
||||
block(B, 256 * prev, 256 * refBlock, offset * 256, needXor);
|
||||
}
|
||||
|
||||
function argon2(type: Types, password: KDFInput, salt: KDFInput, opts: ArgonOpts) {
|
||||
const { mP, p, t, version, B, laneLen, lanes, segmentLen, dkLen, perBlock } = argon2Init(
|
||||
password,
|
||||
salt,
|
||||
type,
|
||||
opts
|
||||
);
|
||||
// Pre-loop setup
|
||||
// [address, input, zero_block] format so we can pass single U32 to block function
|
||||
const address = new Uint32Array(3 * 256);
|
||||
address[256 + 6] = mP;
|
||||
address[256 + 8] = t;
|
||||
address[256 + 10] = type;
|
||||
for (let r = 0; r < t; r++) {
|
||||
const needXor = r !== 0 && version === 0x13;
|
||||
address[256 + 0] = r;
|
||||
for (let s = 0; s < ARGON2_SYNC_POINTS; s++) {
|
||||
address[256 + 4] = s;
|
||||
const dataIndependent = type == AT.Argon2i || (type == AT.Argon2id && r === 0 && s < 2);
|
||||
for (let l = 0; l < p; l++) {
|
||||
address[256 + 2] = l;
|
||||
address[256 + 12] = 0;
|
||||
let startPos = 0;
|
||||
if (r === 0 && s === 0) {
|
||||
startPos = 2;
|
||||
if (dataIndependent) {
|
||||
address[256 + 12]++;
|
||||
block(address, 256, 2 * 256, 0, false);
|
||||
block(address, 0, 2 * 256, 0, false);
|
||||
}
|
||||
}
|
||||
// current block postion
|
||||
let offset = l * laneLen + s * segmentLen + startPos;
|
||||
// previous block position
|
||||
let prev = offset % laneLen ? offset - 1 : offset + laneLen - 1;
|
||||
for (let index = startPos; index < segmentLen; index++, offset++, prev++) {
|
||||
perBlock();
|
||||
processBlock(
|
||||
B,
|
||||
address,
|
||||
l,
|
||||
r,
|
||||
s,
|
||||
index,
|
||||
laneLen,
|
||||
segmentLen,
|
||||
lanes,
|
||||
offset,
|
||||
prev,
|
||||
dataIndependent,
|
||||
needXor
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
clean(address);
|
||||
return argon2Output(B, p, laneLen, dkLen);
|
||||
}
|
||||
|
||||
/** argon2d GPU-resistant version. */
|
||||
export const argon2d = (password: KDFInput, salt: KDFInput, opts: ArgonOpts): Uint8Array =>
|
||||
argon2(AT.Argond2d, password, salt, opts);
|
||||
/** argon2i side-channel-resistant version. */
|
||||
export const argon2i = (password: KDFInput, salt: KDFInput, opts: ArgonOpts): Uint8Array =>
|
||||
argon2(AT.Argon2i, password, salt, opts);
|
||||
/** argon2id, combining i+d, the most popular version from RFC 9106 */
|
||||
export const argon2id = (password: KDFInput, salt: KDFInput, opts: ArgonOpts): Uint8Array =>
|
||||
argon2(AT.Argon2id, password, salt, opts);
|
||||
|
||||
async function argon2Async(type: Types, password: KDFInput, salt: KDFInput, opts: ArgonOpts) {
|
||||
const { mP, p, t, version, B, laneLen, lanes, segmentLen, dkLen, perBlock, asyncTick } =
|
||||
argon2Init(password, salt, type, opts);
|
||||
// Pre-loop setup
|
||||
// [address, input, zero_block] format so we can pass single U32 to block function
|
||||
const address = new Uint32Array(3 * 256);
|
||||
address[256 + 6] = mP;
|
||||
address[256 + 8] = t;
|
||||
address[256 + 10] = type;
|
||||
let ts = Date.now();
|
||||
for (let r = 0; r < t; r++) {
|
||||
const needXor = r !== 0 && version === 0x13;
|
||||
address[256 + 0] = r;
|
||||
for (let s = 0; s < ARGON2_SYNC_POINTS; s++) {
|
||||
address[256 + 4] = s;
|
||||
const dataIndependent = type == AT.Argon2i || (type == AT.Argon2id && r === 0 && s < 2);
|
||||
for (let l = 0; l < p; l++) {
|
||||
address[256 + 2] = l;
|
||||
address[256 + 12] = 0;
|
||||
let startPos = 0;
|
||||
if (r === 0 && s === 0) {
|
||||
startPos = 2;
|
||||
if (dataIndependent) {
|
||||
address[256 + 12]++;
|
||||
block(address, 256, 2 * 256, 0, false);
|
||||
block(address, 0, 2 * 256, 0, false);
|
||||
}
|
||||
}
|
||||
// current block postion
|
||||
let offset = l * laneLen + s * segmentLen + startPos;
|
||||
// previous block position
|
||||
let prev = offset % laneLen ? offset - 1 : offset + laneLen - 1;
|
||||
for (let index = startPos; index < segmentLen; index++, offset++, prev++) {
|
||||
perBlock();
|
||||
processBlock(
|
||||
B,
|
||||
address,
|
||||
l,
|
||||
r,
|
||||
s,
|
||||
index,
|
||||
laneLen,
|
||||
segmentLen,
|
||||
lanes,
|
||||
offset,
|
||||
prev,
|
||||
dataIndependent,
|
||||
needXor
|
||||
);
|
||||
// Date.now() is not monotonic, so in case if clock goes backwards we return return control too
|
||||
const diff = Date.now() - ts;
|
||||
if (!(diff >= 0 && diff < asyncTick)) {
|
||||
await nextTick();
|
||||
ts += diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
clean(address);
|
||||
return argon2Output(B, p, laneLen, dkLen);
|
||||
}
|
||||
|
||||
/** argon2d async GPU-resistant version. */
|
||||
export const argon2dAsync = (
|
||||
password: KDFInput,
|
||||
salt: KDFInput,
|
||||
opts: ArgonOpts
|
||||
): Promise<Uint8Array> => argon2Async(AT.Argond2d, password, salt, opts);
|
||||
/** argon2i async side-channel-resistant version. */
|
||||
export const argon2iAsync = (
|
||||
password: KDFInput,
|
||||
salt: KDFInput,
|
||||
opts: ArgonOpts
|
||||
): Promise<Uint8Array> => argon2Async(AT.Argon2i, password, salt, opts);
|
||||
/** argon2id async, combining i+d, the most popular version from RFC 9106 */
|
||||
export const argon2idAsync = (
|
||||
password: KDFInput,
|
||||
salt: KDFInput,
|
||||
opts: ArgonOpts
|
||||
): Promise<Uint8Array> => argon2Async(AT.Argon2id, password, salt, opts);
|
||||
+534
@@ -0,0 +1,534 @@
|
||||
/**
|
||||
* Blake1 legacy hash function, one of SHA3 proposals.
|
||||
* Rarely used. Check out blake2 or blake3 instead.
|
||||
* https://www.aumasson.jp/blake/blake.pdf
|
||||
*
|
||||
* In the best case, there are 0 allocations.
|
||||
*
|
||||
* Differences from blake2:
|
||||
*
|
||||
* - BE instead of LE
|
||||
* - Paddings, similar to MD5, RIPEMD, SHA1, SHA2, but:
|
||||
* - length flag is located before actual length
|
||||
* - padding block is compressed differently (no lengths)
|
||||
* Instead of msg[sigma[k]], we have `msg[sigma[k]] ^ constants[sigma[k-1]]`
|
||||
* (-1 for g1, g2 without -1)
|
||||
* - Salt is XOR-ed into constants instead of state
|
||||
* - Salt is XOR-ed with output in `compress`
|
||||
* - Additional rows (+64 bytes) in SIGMA for new rounds
|
||||
* - Different round count:
|
||||
* - 14 / 10 rounds in blake256 / blake2s
|
||||
* - 16 / 12 rounds in blake512 / blake2b
|
||||
* - blake512: G1b: rotr 24 -> 25, G2b: rotr 63 -> 11
|
||||
* @module
|
||||
*/
|
||||
import { BSIGMA, G1s, G2s } from './_blake.ts';
|
||||
import { setBigUint64, SHA224_IV, SHA256_IV, SHA384_IV, SHA512_IV } from './_md.ts';
|
||||
import * as u64 from './_u64.ts';
|
||||
// prettier-ignore
|
||||
import {
|
||||
abytes, aexists, aoutput,
|
||||
clean, createOptHasher,
|
||||
createView, Hash, toBytes,
|
||||
type CHashO, type Input,
|
||||
} from './utils.ts';
|
||||
|
||||
/** Blake1 options. Basically just "salt" */
|
||||
export type BlakeOpts = {
|
||||
salt?: Uint8Array;
|
||||
};
|
||||
|
||||
// Empty zero-filled salt
|
||||
const EMPTY_SALT = /* @__PURE__ */ new Uint32Array(8);
|
||||
|
||||
abstract class BLAKE1<T extends BLAKE1<T>> extends Hash<T> {
|
||||
protected finished = false;
|
||||
protected length = 0;
|
||||
protected pos = 0;
|
||||
protected destroyed = false;
|
||||
// For partial updates less than block size
|
||||
protected buffer: Uint8Array;
|
||||
protected view: DataView;
|
||||
protected salt: Uint32Array;
|
||||
abstract compress(view: DataView, offset: number, withLength?: boolean): void;
|
||||
protected abstract get(): number[];
|
||||
protected abstract set(...args: number[]): void;
|
||||
|
||||
readonly blockLen: number;
|
||||
readonly outputLen: number;
|
||||
private lengthFlag: number;
|
||||
private counterLen: number;
|
||||
protected constants: Uint32Array;
|
||||
|
||||
constructor(
|
||||
blockLen: number,
|
||||
outputLen: number,
|
||||
lengthFlag: number,
|
||||
counterLen: number,
|
||||
saltLen: number,
|
||||
constants: Uint32Array,
|
||||
opts: BlakeOpts = {}
|
||||
) {
|
||||
super();
|
||||
const { salt } = opts;
|
||||
this.blockLen = blockLen;
|
||||
this.outputLen = outputLen;
|
||||
this.lengthFlag = lengthFlag;
|
||||
this.counterLen = counterLen;
|
||||
this.buffer = new Uint8Array(blockLen);
|
||||
this.view = createView(this.buffer);
|
||||
if (salt) {
|
||||
let slt = salt;
|
||||
slt = toBytes(slt);
|
||||
abytes(slt);
|
||||
if (slt.length !== 4 * saltLen) throw new Error('wrong salt length');
|
||||
const salt32 = (this.salt = new Uint32Array(saltLen));
|
||||
const sv = createView(slt);
|
||||
this.constants = constants.slice();
|
||||
for (let i = 0, offset = 0; i < salt32.length; i++, offset += 4) {
|
||||
salt32[i] = sv.getUint32(offset, false);
|
||||
this.constants[i] ^= salt32[i];
|
||||
}
|
||||
} else {
|
||||
this.salt = EMPTY_SALT;
|
||||
this.constants = constants;
|
||||
}
|
||||
}
|
||||
update(data: Input): this {
|
||||
aexists(this);
|
||||
data = toBytes(data);
|
||||
abytes(data);
|
||||
// From _md, but update length before each compress
|
||||
const { view, buffer, blockLen } = this;
|
||||
const len = data.length;
|
||||
let dataView;
|
||||
for (let pos = 0; pos < len; ) {
|
||||
const take = Math.min(blockLen - this.pos, len - pos);
|
||||
// Fast path: we have at least one block in input, cast it to view and process
|
||||
if (take === blockLen) {
|
||||
if (!dataView) dataView = createView(data);
|
||||
for (; blockLen <= len - pos; pos += blockLen) {
|
||||
this.length += blockLen;
|
||||
this.compress(dataView, pos);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
buffer.set(data.subarray(pos, pos + take), this.pos);
|
||||
this.pos += take;
|
||||
pos += take;
|
||||
if (this.pos === blockLen) {
|
||||
this.length += blockLen;
|
||||
this.compress(view, 0, true);
|
||||
this.pos = 0;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
destroy(): void {
|
||||
this.destroyed = true;
|
||||
if (this.salt !== EMPTY_SALT) {
|
||||
clean(this.salt, this.constants);
|
||||
}
|
||||
}
|
||||
_cloneInto(to?: T): T {
|
||||
to ||= new (this.constructor as any)() as T;
|
||||
to.set(...this.get());
|
||||
const { buffer, length, finished, destroyed, constants, salt, pos } = this;
|
||||
to.buffer.set(buffer);
|
||||
to.constants = constants.slice();
|
||||
to.destroyed = destroyed;
|
||||
to.finished = finished;
|
||||
to.length = length;
|
||||
to.pos = pos;
|
||||
to.salt = salt.slice();
|
||||
return to;
|
||||
}
|
||||
clone(): T {
|
||||
return this._cloneInto();
|
||||
}
|
||||
digestInto(out: Uint8Array): void {
|
||||
aexists(this);
|
||||
aoutput(out, this);
|
||||
this.finished = true;
|
||||
// Padding
|
||||
const { buffer, blockLen, counterLen, lengthFlag, view } = this;
|
||||
clean(buffer.subarray(this.pos)); // clean buf
|
||||
const counter = BigInt((this.length + this.pos) * 8);
|
||||
const counterPos = blockLen - counterLen - 1;
|
||||
buffer[this.pos] |= 0b1000_0000; // End block flag
|
||||
this.length += this.pos; // add unwritten length
|
||||
// Not enough in buffer for length: write what we have.
|
||||
if (this.pos > counterPos) {
|
||||
this.compress(view, 0);
|
||||
clean(buffer);
|
||||
this.pos = 0;
|
||||
}
|
||||
// Difference with md: here we have lengthFlag!
|
||||
buffer[counterPos] |= lengthFlag; // Length flag
|
||||
// We always set 8 byte length flag. Because length will overflow significantly sooner.
|
||||
setBigUint64(view, blockLen - 8, counter, false);
|
||||
this.compress(view, 0, this.pos !== 0); // don't add length if length is not empty block?
|
||||
// Write output
|
||||
clean(buffer);
|
||||
const v = createView(out);
|
||||
const state = this.get();
|
||||
for (let i = 0; i < this.outputLen / 4; ++i) v.setUint32(i * 4, state[i]);
|
||||
}
|
||||
digest(): Uint8Array {
|
||||
const { buffer, outputLen } = this;
|
||||
this.digestInto(buffer);
|
||||
const res = buffer.slice(0, outputLen);
|
||||
this.destroy();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// Constants
|
||||
const B64C = /* @__PURE__ */ Uint32Array.from([
|
||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
|
||||
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
|
||||
0x9216d5d9, 0x8979fb1b, 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,
|
||||
0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69,
|
||||
]);
|
||||
// first half of C512
|
||||
const B32C = B64C.slice(0, 16);
|
||||
|
||||
const B256_IV = SHA256_IV.slice();
|
||||
const B224_IV = SHA224_IV.slice();
|
||||
const B384_IV = SHA384_IV.slice();
|
||||
const B512_IV = SHA512_IV.slice();
|
||||
|
||||
function generateTBL256() {
|
||||
const TBL = [];
|
||||
for (let i = 0, j = 0; i < 14; i++, j += 16) {
|
||||
for (let offset = 1; offset < 16; offset += 2) {
|
||||
TBL.push(B32C[BSIGMA[j + offset]]);
|
||||
TBL.push(B32C[BSIGMA[j + offset - 1]]);
|
||||
}
|
||||
}
|
||||
return new Uint32Array(TBL);
|
||||
}
|
||||
const TBL256 = /* @__PURE__ */ generateTBL256(); // C256[SIGMA[X]] precompute
|
||||
|
||||
// Reusable temporary buffer
|
||||
const BLAKE256_W = /* @__PURE__ */ new Uint32Array(16);
|
||||
|
||||
class Blake1_32 extends BLAKE1<Blake1_32> {
|
||||
private v0: number;
|
||||
private v1: number;
|
||||
private v2: number;
|
||||
private v3: number;
|
||||
private v4: number;
|
||||
private v5: number;
|
||||
private v6: number;
|
||||
private v7: number;
|
||||
constructor(outputLen: number, IV: Uint32Array, lengthFlag: number, opts: BlakeOpts = {}) {
|
||||
super(64, outputLen, lengthFlag, 8, 4, B32C, opts);
|
||||
this.v0 = IV[0] | 0;
|
||||
this.v1 = IV[1] | 0;
|
||||
this.v2 = IV[2] | 0;
|
||||
this.v3 = IV[3] | 0;
|
||||
this.v4 = IV[4] | 0;
|
||||
this.v5 = IV[5] | 0;
|
||||
this.v6 = IV[6] | 0;
|
||||
this.v7 = IV[7] | 0;
|
||||
}
|
||||
protected get(): [number, number, number, number, number, number, number, number] {
|
||||
const { v0, v1, v2, v3, v4, v5, v6, v7 } = this;
|
||||
return [v0, v1, v2, v3, v4, v5, v6, v7];
|
||||
}
|
||||
// prettier-ignore
|
||||
protected set(
|
||||
v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number
|
||||
): void {
|
||||
this.v0 = v0 | 0;
|
||||
this.v1 = v1 | 0;
|
||||
this.v2 = v2 | 0;
|
||||
this.v3 = v3 | 0;
|
||||
this.v4 = v4 | 0;
|
||||
this.v5 = v5 | 0;
|
||||
this.v6 = v6 | 0;
|
||||
this.v7 = v7 | 0;
|
||||
}
|
||||
destroy(): void {
|
||||
super.destroy();
|
||||
this.set(0, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
compress(view: DataView, offset: number, withLength = true): void {
|
||||
for (let i = 0; i < 16; i++, offset += 4) BLAKE256_W[i] = view.getUint32(offset, false);
|
||||
// NOTE: we cannot re-use compress from blake2s, since there is additional xor over u256[SIGMA[e]]
|
||||
let v00 = this.v0 | 0;
|
||||
let v01 = this.v1 | 0;
|
||||
let v02 = this.v2 | 0;
|
||||
let v03 = this.v3 | 0;
|
||||
let v04 = this.v4 | 0;
|
||||
let v05 = this.v5 | 0;
|
||||
let v06 = this.v6 | 0;
|
||||
let v07 = this.v7 | 0;
|
||||
let v08 = this.constants[0] | 0;
|
||||
let v09 = this.constants[1] | 0;
|
||||
let v10 = this.constants[2] | 0;
|
||||
let v11 = this.constants[3] | 0;
|
||||
const { h, l } = u64.fromBig(BigInt(withLength ? this.length * 8 : 0));
|
||||
let v12 = (this.constants[4] ^ l) >>> 0;
|
||||
let v13 = (this.constants[5] ^ l) >>> 0;
|
||||
let v14 = (this.constants[6] ^ h) >>> 0;
|
||||
let v15 = (this.constants[7] ^ h) >>> 0;
|
||||
// prettier-ignore
|
||||
for (let i = 0, k = 0, j = 0; i < 14; i++) {
|
||||
({ a: v00, b: v04, c: v08, d: v12 } = G1s(v00, v04, v08, v12, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
({ a: v00, b: v04, c: v08, d: v12 } = G2s(v00, v04, v08, v12, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
({ a: v01, b: v05, c: v09, d: v13 } = G1s(v01, v05, v09, v13, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
({ a: v01, b: v05, c: v09, d: v13 } = G2s(v01, v05, v09, v13, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
({ a: v02, b: v06, c: v10, d: v14 } = G1s(v02, v06, v10, v14, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
({ a: v02, b: v06, c: v10, d: v14 } = G2s(v02, v06, v10, v14, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
({ a: v03, b: v07, c: v11, d: v15 } = G1s(v03, v07, v11, v15, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
({ a: v03, b: v07, c: v11, d: v15 } = G2s(v03, v07, v11, v15, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
({ a: v00, b: v05, c: v10, d: v15 } = G1s(v00, v05, v10, v15, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
({ a: v00, b: v05, c: v10, d: v15 } = G2s(v00, v05, v10, v15, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
({ a: v01, b: v06, c: v11, d: v12 } = G1s(v01, v06, v11, v12, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
({ a: v01, b: v06, c: v11, d: v12 } = G2s(v01, v06, v11, v12, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
({ a: v02, b: v07, c: v08, d: v13 } = G1s(v02, v07, v08, v13, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
({ a: v02, b: v07, c: v08, d: v13 } = G2s(v02, v07, v08, v13, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
({ a: v03, b: v04, c: v09, d: v14 } = G1s(v03, v04, v09, v14, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
({ a: v03, b: v04, c: v09, d: v14 } = G2s(v03, v04, v09, v14, BLAKE256_W[BSIGMA[k++]] ^ TBL256[j++]));
|
||||
}
|
||||
this.v0 = (this.v0 ^ v00 ^ v08 ^ this.salt[0]) >>> 0;
|
||||
this.v1 = (this.v1 ^ v01 ^ v09 ^ this.salt[1]) >>> 0;
|
||||
this.v2 = (this.v2 ^ v02 ^ v10 ^ this.salt[2]) >>> 0;
|
||||
this.v3 = (this.v3 ^ v03 ^ v11 ^ this.salt[3]) >>> 0;
|
||||
this.v4 = (this.v4 ^ v04 ^ v12 ^ this.salt[0]) >>> 0;
|
||||
this.v5 = (this.v5 ^ v05 ^ v13 ^ this.salt[1]) >>> 0;
|
||||
this.v6 = (this.v6 ^ v06 ^ v14 ^ this.salt[2]) >>> 0;
|
||||
this.v7 = (this.v7 ^ v07 ^ v15 ^ this.salt[3]) >>> 0;
|
||||
clean(BLAKE256_W);
|
||||
}
|
||||
}
|
||||
|
||||
const BBUF = /* @__PURE__ */ new Uint32Array(32);
|
||||
const BLAKE512_W = /* @__PURE__ */ new Uint32Array(32);
|
||||
|
||||
function generateTBL512() {
|
||||
const TBL = [];
|
||||
for (let r = 0, k = 0; r < 16; r++, k += 16) {
|
||||
for (let offset = 1; offset < 16; offset += 2) {
|
||||
TBL.push(B64C[BSIGMA[k + offset] * 2 + 0]);
|
||||
TBL.push(B64C[BSIGMA[k + offset] * 2 + 1]);
|
||||
TBL.push(B64C[BSIGMA[k + offset - 1] * 2 + 0]);
|
||||
TBL.push(B64C[BSIGMA[k + offset - 1] * 2 + 1]);
|
||||
}
|
||||
}
|
||||
return new Uint32Array(TBL);
|
||||
}
|
||||
const TBL512 = /* @__PURE__ */ generateTBL512(); // C512[SIGMA[X]] precompute
|
||||
|
||||
// Mixing function G splitted in two halfs
|
||||
function G1b(a: number, b: number, c: number, d: number, msg: Uint32Array, k: number) {
|
||||
const Xpos = 2 * BSIGMA[k];
|
||||
const Xl = msg[Xpos + 1] ^ TBL512[k * 2 + 1], Xh = msg[Xpos] ^ TBL512[k * 2]; // prettier-ignore
|
||||
let Al = BBUF[2 * a + 1], Ah = BBUF[2 * a]; // prettier-ignore
|
||||
let Bl = BBUF[2 * b + 1], Bh = BBUF[2 * b]; // prettier-ignore
|
||||
let Cl = BBUF[2 * c + 1], Ch = BBUF[2 * c]; // prettier-ignore
|
||||
let Dl = BBUF[2 * d + 1], Dh = BBUF[2 * d]; // prettier-ignore
|
||||
// v[a] = (v[a] + v[b] + x) | 0;
|
||||
let ll = u64.add3L(Al, Bl, Xl);
|
||||
Ah = u64.add3H(ll, Ah, Bh, Xh) >>> 0;
|
||||
Al = (ll | 0) >>> 0;
|
||||
// v[d] = rotr(v[d] ^ v[a], 32)
|
||||
({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
|
||||
({ Dh, Dl } = { Dh: u64.rotr32H(Dh, Dl), Dl: u64.rotr32L(Dh, Dl) });
|
||||
// v[c] = (v[c] + v[d]) | 0;
|
||||
({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl));
|
||||
// v[b] = rotr(v[b] ^ v[c], 25)
|
||||
({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
|
||||
({ Bh, Bl } = { Bh: u64.rotrSH(Bh, Bl, 25), Bl: u64.rotrSL(Bh, Bl, 25) });
|
||||
(BBUF[2 * a + 1] = Al), (BBUF[2 * a] = Ah);
|
||||
(BBUF[2 * b + 1] = Bl), (BBUF[2 * b] = Bh);
|
||||
(BBUF[2 * c + 1] = Cl), (BBUF[2 * c] = Ch);
|
||||
(BBUF[2 * d + 1] = Dl), (BBUF[2 * d] = Dh);
|
||||
}
|
||||
|
||||
function G2b(a: number, b: number, c: number, d: number, msg: Uint32Array, k: number) {
|
||||
const Xpos = 2 * BSIGMA[k];
|
||||
const Xl = msg[Xpos + 1] ^ TBL512[k * 2 + 1], Xh = msg[Xpos] ^ TBL512[k * 2]; // prettier-ignore
|
||||
let Al = BBUF[2 * a + 1], Ah = BBUF[2 * a]; // prettier-ignore
|
||||
let Bl = BBUF[2 * b + 1], Bh = BBUF[2 * b]; // prettier-ignore
|
||||
let Cl = BBUF[2 * c + 1], Ch = BBUF[2 * c]; // prettier-ignore
|
||||
let Dl = BBUF[2 * d + 1], Dh = BBUF[2 * d]; // prettier-ignore
|
||||
// v[a] = (v[a] + v[b] + x) | 0;
|
||||
let ll = u64.add3L(Al, Bl, Xl);
|
||||
Ah = u64.add3H(ll, Ah, Bh, Xh);
|
||||
Al = ll | 0;
|
||||
// v[d] = rotr(v[d] ^ v[a], 16)
|
||||
({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
|
||||
({ Dh, Dl } = { Dh: u64.rotrSH(Dh, Dl, 16), Dl: u64.rotrSL(Dh, Dl, 16) });
|
||||
// v[c] = (v[c] + v[d]) | 0;
|
||||
({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl));
|
||||
// v[b] = rotr(v[b] ^ v[c], 11)
|
||||
({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
|
||||
({ Bh, Bl } = { Bh: u64.rotrSH(Bh, Bl, 11), Bl: u64.rotrSL(Bh, Bl, 11) });
|
||||
(BBUF[2 * a + 1] = Al), (BBUF[2 * a] = Ah);
|
||||
(BBUF[2 * b + 1] = Bl), (BBUF[2 * b] = Bh);
|
||||
(BBUF[2 * c + 1] = Cl), (BBUF[2 * c] = Ch);
|
||||
(BBUF[2 * d + 1] = Dl), (BBUF[2 * d] = Dh);
|
||||
}
|
||||
|
||||
class Blake1_64 extends BLAKE1<Blake1_64> {
|
||||
private v0l: number;
|
||||
private v0h: number;
|
||||
private v1l: number;
|
||||
private v1h: number;
|
||||
private v2l: number;
|
||||
private v2h: number;
|
||||
private v3l: number;
|
||||
private v3h: number;
|
||||
private v4l: number;
|
||||
private v4h: number;
|
||||
private v5l: number;
|
||||
private v5h: number;
|
||||
private v6l: number;
|
||||
private v6h: number;
|
||||
private v7l: number;
|
||||
private v7h: number;
|
||||
constructor(outputLen: number, IV: Uint32Array, lengthFlag: number, opts: BlakeOpts = {}) {
|
||||
super(128, outputLen, lengthFlag, 16, 8, B64C, opts);
|
||||
this.v0l = IV[0] | 0;
|
||||
this.v0h = IV[1] | 0;
|
||||
this.v1l = IV[2] | 0;
|
||||
this.v1h = IV[3] | 0;
|
||||
this.v2l = IV[4] | 0;
|
||||
this.v2h = IV[5] | 0;
|
||||
this.v3l = IV[6] | 0;
|
||||
this.v3h = IV[7] | 0;
|
||||
this.v4l = IV[8] | 0;
|
||||
this.v4h = IV[9] | 0;
|
||||
this.v5l = IV[10] | 0;
|
||||
this.v5h = IV[11] | 0;
|
||||
this.v6l = IV[12] | 0;
|
||||
this.v6h = IV[13] | 0;
|
||||
this.v7l = IV[14] | 0;
|
||||
this.v7h = IV[15] | 0;
|
||||
}
|
||||
// prettier-ignore
|
||||
protected get(): [
|
||||
number, number, number, number, number, number, number, number,
|
||||
number, number, number, number, number, number, number, number
|
||||
] {
|
||||
let { v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h } = this;
|
||||
return [v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h];
|
||||
}
|
||||
// prettier-ignore
|
||||
protected set(
|
||||
v0l: number, v0h: number, v1l: number, v1h: number,
|
||||
v2l: number, v2h: number, v3l: number, v3h: number,
|
||||
v4l: number, v4h: number, v5l: number, v5h: number,
|
||||
v6l: number, v6h: number, v7l: number, v7h: number
|
||||
): void {
|
||||
this.v0l = v0l | 0;
|
||||
this.v0h = v0h | 0;
|
||||
this.v1l = v1l | 0;
|
||||
this.v1h = v1h | 0;
|
||||
this.v2l = v2l | 0;
|
||||
this.v2h = v2h | 0;
|
||||
this.v3l = v3l | 0;
|
||||
this.v3h = v3h | 0;
|
||||
this.v4l = v4l | 0;
|
||||
this.v4h = v4h | 0;
|
||||
this.v5l = v5l | 0;
|
||||
this.v5h = v5h | 0;
|
||||
this.v6l = v6l | 0;
|
||||
this.v6h = v6h | 0;
|
||||
this.v7l = v7l | 0;
|
||||
this.v7h = v7h | 0;
|
||||
}
|
||||
destroy(): void {
|
||||
super.destroy();
|
||||
this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
compress(view: DataView, offset: number, withLength = true): void {
|
||||
for (let i = 0; i < 32; i++, offset += 4) BLAKE512_W[i] = view.getUint32(offset, false);
|
||||
|
||||
this.get().forEach((v, i) => (BBUF[i] = v)); // First half from state.
|
||||
BBUF.set(this.constants.subarray(0, 16), 16);
|
||||
if (withLength) {
|
||||
const { h, l } = u64.fromBig(BigInt(this.length * 8));
|
||||
BBUF[24] = (BBUF[24] ^ h) >>> 0;
|
||||
BBUF[25] = (BBUF[25] ^ l) >>> 0;
|
||||
BBUF[26] = (BBUF[26] ^ h) >>> 0;
|
||||
BBUF[27] = (BBUF[27] ^ l) >>> 0;
|
||||
}
|
||||
for (let i = 0, k = 0; i < 16; i++) {
|
||||
G1b(0, 4, 8, 12, BLAKE512_W, k++);
|
||||
G2b(0, 4, 8, 12, BLAKE512_W, k++);
|
||||
G1b(1, 5, 9, 13, BLAKE512_W, k++);
|
||||
G2b(1, 5, 9, 13, BLAKE512_W, k++);
|
||||
G1b(2, 6, 10, 14, BLAKE512_W, k++);
|
||||
G2b(2, 6, 10, 14, BLAKE512_W, k++);
|
||||
G1b(3, 7, 11, 15, BLAKE512_W, k++);
|
||||
G2b(3, 7, 11, 15, BLAKE512_W, k++);
|
||||
|
||||
G1b(0, 5, 10, 15, BLAKE512_W, k++);
|
||||
G2b(0, 5, 10, 15, BLAKE512_W, k++);
|
||||
G1b(1, 6, 11, 12, BLAKE512_W, k++);
|
||||
G2b(1, 6, 11, 12, BLAKE512_W, k++);
|
||||
G1b(2, 7, 8, 13, BLAKE512_W, k++);
|
||||
G2b(2, 7, 8, 13, BLAKE512_W, k++);
|
||||
G1b(3, 4, 9, 14, BLAKE512_W, k++);
|
||||
G2b(3, 4, 9, 14, BLAKE512_W, k++);
|
||||
}
|
||||
this.v0l ^= BBUF[0] ^ BBUF[16] ^ this.salt[0];
|
||||
this.v0h ^= BBUF[1] ^ BBUF[17] ^ this.salt[1];
|
||||
this.v1l ^= BBUF[2] ^ BBUF[18] ^ this.salt[2];
|
||||
this.v1h ^= BBUF[3] ^ BBUF[19] ^ this.salt[3];
|
||||
this.v2l ^= BBUF[4] ^ BBUF[20] ^ this.salt[4];
|
||||
this.v2h ^= BBUF[5] ^ BBUF[21] ^ this.salt[5];
|
||||
this.v3l ^= BBUF[6] ^ BBUF[22] ^ this.salt[6];
|
||||
this.v3h ^= BBUF[7] ^ BBUF[23] ^ this.salt[7];
|
||||
this.v4l ^= BBUF[8] ^ BBUF[24] ^ this.salt[0];
|
||||
this.v4h ^= BBUF[9] ^ BBUF[25] ^ this.salt[1];
|
||||
this.v5l ^= BBUF[10] ^ BBUF[26] ^ this.salt[2];
|
||||
this.v5h ^= BBUF[11] ^ BBUF[27] ^ this.salt[3];
|
||||
this.v6l ^= BBUF[12] ^ BBUF[28] ^ this.salt[4];
|
||||
this.v6h ^= BBUF[13] ^ BBUF[29] ^ this.salt[5];
|
||||
this.v7l ^= BBUF[14] ^ BBUF[30] ^ this.salt[6];
|
||||
this.v7h ^= BBUF[15] ^ BBUF[31] ^ this.salt[7];
|
||||
clean(BBUF, BLAKE512_W);
|
||||
}
|
||||
}
|
||||
|
||||
export class BLAKE224 extends Blake1_32 {
|
||||
constructor(opts: BlakeOpts = {}) {
|
||||
super(28, B224_IV, 0b0000_0000, opts);
|
||||
}
|
||||
}
|
||||
export class BLAKE256 extends Blake1_32 {
|
||||
constructor(opts: BlakeOpts = {}) {
|
||||
super(32, B256_IV, 0b0000_0001, opts);
|
||||
}
|
||||
}
|
||||
export class BLAKE384 extends Blake1_64 {
|
||||
constructor(opts: BlakeOpts = {}) {
|
||||
super(48, B384_IV, 0b0000_0000, opts);
|
||||
}
|
||||
}
|
||||
export class BLAKE512 extends Blake1_64 {
|
||||
constructor(opts: BlakeOpts = {}) {
|
||||
super(64, B512_IV, 0b0000_0001, opts);
|
||||
}
|
||||
}
|
||||
/** blake1-224 hash function */
|
||||
export const blake224: CHashO = /* @__PURE__ */ createOptHasher<BLAKE224, BlakeOpts>(
|
||||
(opts) => new BLAKE224(opts)
|
||||
);
|
||||
/** blake1-256 hash function */
|
||||
export const blake256: CHashO = /* @__PURE__ */ createOptHasher<BLAKE256, BlakeOpts>(
|
||||
(opts) => new BLAKE256(opts)
|
||||
);
|
||||
/** blake1-384 hash function */
|
||||
export const blake384: CHashO = /* @__PURE__ */ createOptHasher<BLAKE512, BlakeOpts>(
|
||||
(opts) => new BLAKE384(opts)
|
||||
);
|
||||
/** blake1-512 hash function */
|
||||
export const blake512: CHashO = /* @__PURE__ */ createOptHasher<BLAKE512, BlakeOpts>(
|
||||
(opts) => new BLAKE512(opts)
|
||||
);
|
||||
Reference in New Issue
Block a user