import { exists, output } from './_assert.js'; import { Hash, createView, toBytes } from './utils.js'; // Polyfill for Safari 14 function setBigUint64(view, byteOffset, value, isLE) { if (typeof view.setBigUint64 === 'function') return view.setBigUint64(byteOffset, value, isLE); const _32n = BigInt(32); const _u32_max = BigInt(0xffffffff); const wh = Number((value >> _32n) & _u32_max); const wl = Number(value & _u32_max); const h = isLE ? 4 : 0; const l = isLE ? 0 : 4; view.setUint32(byteOffset + h, wh, isLE); view.setUint32(byteOffset + l, wl, isLE); } // Choice: a ? b : c export const Chi = (a, b, c) => (a & b) ^ (~a & c); // Majority function, true if any two inpust is true export const Maj = (a, b, c) => (a & b) ^ (a & c) ^ (b & c); /** * Merkle-Damgard hash construction base class. * Could be used to create MD5, RIPEMD, SHA1, SHA2. */ export class HashMD extends Hash { constructor(blockLen, outputLen, padOffset, isLE) { super(); this.blockLen = blockLen; this.outputLen = outputLen; this.padOffset = padOffset; this.isLE = isLE; this.finished = false; this.length = 0; this.pos = 0; this.destroyed = false; this.buffer = new Uint8Array(blockLen); this.view = createView(this.buffer); } update(data) { exists(this); const { view, buffer, blockLen } = this; data = toBytes(data); const len = data.length; 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) { const dataView = createView(data); for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos); continue; } buffer.set(data.subarray(pos, pos + take), this.pos); this.pos += take; pos += take; if (this.pos === blockLen) { this.process(view, 0); this.pos = 0; } } this.length += data.length; this.roundClean(); return this; } digestInto(out) { exists(this); output(out, this); this.finished = true; // Padding // We can avoid allocation of buffer for padding completely if it // was previously not allocated here. But it won't change performance. const { buffer, view, blockLen, isLE } = this; let { pos } = this; // append the bit '1' to the message buffer[pos++] = 0b10000000; this.buffer.subarray(pos).fill(0); // we have less than padOffset left in buffer, so we cannot put length in // current block, need process it and pad again if (this.padOffset > blockLen - pos) { this.process(view, 0); pos = 0; } // Pad until full block byte with zeros for (let i = pos; i < blockLen; i++) buffer[i] = 0; // Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that // You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen. // So we just write lowest 64 bits of that value. setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE); this.process(view, 0); const oview = createView(out); const len = this.outputLen; // NOTE: we do division by 4 later, which should be fused in single op with modulo by JIT if (len % 4) throw new Error('_sha2: outputLen should be aligned to 32bit'); const outLen = len / 4; const state = this.get(); if (outLen > state.length) throw new Error('_sha2: outputLen bigger than state'); for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE); } digest() { const { buffer, outputLen } = this; this.digestInto(buffer); const res = buffer.slice(0, outputLen); this.destroy(); return res; } _cloneInto(to) { to || (to = new this.constructor()); to.set(...this.get()); const { blockLen, buffer, length, finished, destroyed, pos } = this; to.length = length; to.pos = pos; to.finished = finished; to.destroyed = destroyed; if (length % blockLen) to.buffer.set(buffer); return to; } } //# sourceMappingURL=_md.js.map