UNPKG

@li0ard/kupyna

Version:

Kupyna (DSTU 7564:2014) hash function in pure TypeScript

161 lines (160 loc) 5.45 kB
import { T0, T1, T2, T3, T4, T5, T6, T7 } from "../const"; import { bytesToNumberLE, bytesToUint64s, concatBytes, numberToBytesLE, uint64sToBytes } from "../utils"; const T = [T0, T1, T2, T3, T4, T5, T6, T7]; const r = 0x00f0f0f0f0f0f0f3n; /** Abstract class for 256/512 bit */ export class KupynaBase { blockLen; /** State size */ stSize; /** Threshold for padding */ threshold; s; x; nx; len; constructor(blockLen) { this.blockLen = blockLen; this.stSize = (blockLen / 2) / 4; this.threshold = blockLen - 12; this.s = new BigUint64Array(this.stSize); this.x = new Uint8Array(blockLen); this.nx = 0; this.len = 0n; let s1 = new Uint8Array(8); s1[0] = blockLen; this.s[0] = bytesToNumberLE(s1); } /** Update hash buffer */ update(data) { const nn = data.length; this.len += BigInt(nn); if (this.nx > 0) { const available = this.blockLen - this.nx; const n = Math.min(available, data.length); this.x.set(data.slice(0, n), this.nx); this.nx += n; if (this.nx === this.blockLen) { this.block(this.x); this.nx = 0; } data = data.slice(n); } while (data.length >= this.blockLen) { this.block(data.slice(0, this.blockLen)); this.nx = 0; data = data.slice(this.blockLen); } if (data.length > 0) { this.x.set(data, 0); this.nx = data.length; } return this; } digest() { return this.clone().final(); } final() { this.x[this.nx] = 0x80; this.nx++; const fillBytes = (start) => { const available = this.x.length - start; if (available > 0) this.x.fill(0, start, start + available); }; if (this.nx > this.threshold) { fillBytes(this.nx); this.block(this.x); this.nx = 0; } fillBytes(this.nx); this.x.set(numberToBytesLE(this.len * 8n, 12), this.threshold); this.block(this.x); this.outputTransform(); return uint64sToBytes(this.s).slice(this.outputLen); } byte(a) { return Number(a & 0xffn); } G(x, y) { for (let i = 0; i < this.stSize; i++) { let result = 0n; for (let j = 0; j < 8; j++) { const index = (i - this.offsets[j] + this.stSize) % this.stSize; result ^= T[j][this.byte(x[index] >> (BigInt(j) * 8n))]; } y[i] = result; } } G1(x, y, round) { for (let i = 0; i < this.stSize; i++) { let result = 0n; for (let j = 0; j < 8; j++) { const index = (i - this.offsets[j] + this.stSize) % this.stSize; result ^= T[j][this.byte(x[index] >> (BigInt(j) * 8n))]; } y[i] = result ^ BigInt(i << 4) ^ round; } } G2(x, y, round) { for (let i = 0; i < this.stSize; i++) { let result = 0n; for (let j = 0; j < 8; j++) { const index = (i - this.offsets[j] + this.stSize) % this.stSize; result ^= T[j][this.byte(x[index] >> (BigInt(j) * 8n))]; } y[i] = result + (r ^ ((BigInt((this.stSize - 1 - i) * 16) ^ round) << 56n)); } } P(x, y, round) { for (let idx = 0n; idx < BigInt(this.stSize); idx++) x[Number(idx)] ^= (idx << 4n) ^ round; this.G1(x, y, round + 1n); this.G(y, x); } Q(x, y, round) { for (let j = 0n; j < BigInt(this.stSize); j++) x[Number(j)] += (r ^ ((((BigInt(this.stSize - 1) - j) * 0x10n) ^ round) << 56n)); this.G2(x, y, round + 1n); this.G(y, x); } outputTransform() { let t1 = new BigUint64Array(this.s), t2 = new BigUint64Array(this.stSize); for (let r = 0n; r < BigInt(this.rounds); r += 2n) this.P(t1, t2, r); for (let column = 0; column < this.stSize; column++) this.s[column] ^= t1[column]; } transform(b) { let AQ1 = new BigUint64Array(this.stSize); let AP1 = new BigUint64Array(this.stSize); let tmp = new BigUint64Array(this.stSize); for (let column = 0; column < this.stSize; column++) { AP1[column] = this.s[column] ^ b[column]; AQ1[column] = b[column]; } for (let r = 0n; r < BigInt(this.rounds); r += 2n) { this.P(AP1, tmp, r); this.Q(AQ1, tmp, r); } for (let column = 0; column < this.stSize; column++) this.s[column] ^= AP1[column] ^ AQ1[column]; } block(b) { return this.transform(bytesToUint64s(b)); } } /** Abstract class for derived versions (48/304/384 bit) */ export class KupynaDerived { hash; slice; outputLen; blockLen; buffer = new Uint8Array(); constructor(hash, slice) { this.hash = hash; this.slice = slice; this.outputLen = Math.abs(slice); this.blockLen = hash().blockLen; } update(data) { this.buffer = concatBytes(this.buffer, data); return this; } digest() { return this.clone().final(); } final() { return this.hash().update(this.buffer).digest().slice(this.slice); } }