UNPKG

@bsv/sdk

Version:

BSV Blockchain Software Development Kit

1,726 lines (1,596 loc) 56.4 kB
// @ts-nocheck /* eslint-disable @typescript-eslint/naming-convention */ import { assertValidHex, normalizeHex } from './hex.js' const assert = ( expression: unknown, message: string = 'Hash assertion failed' ): void => { if (!(expression as boolean)) { throw new Error(message) } } /** * The BaseHash class is an abstract base class for cryptographic hash functions. * It provides a common structure and functionality for hash function classes. * * @class BaseHash * * @property pending - Stores partially processed message segments. * @property pendingTotal - The total number of characters that are being stored in `pending` * @property blockSize - The size of each block to processed. * @property outSize - The size of the final hash output. * @property endian - The endianness used during processing, can either be 'big' or 'little'. * @property _delta8 - The block size divided by 8, useful in various computations. * @property _delta32 - The block size divided by 32, useful in various computations. * @property padLength - The length of padding to be added to finalize the computation. * @property hmacStrength - The HMAC strength value. * * @param blockSize - The size of the block to be hashed. * @param outSize - The size of the resulting hash. * @param hmacStrength - The strength of the HMAC. * @param padLength - The length of the padding to be added. * * @example * Sub-classes would extend this base BaseHash class like: * class RIPEMD160 extends BaseHash { * constructor () { * super(512, 160, 192, 64); * // ... * } * // ... * } */ abstract class BaseHash { pending: number[] | null pendingTotal: number blockSize: number outSize: number endian: 'big' | 'little' _delta8: number _delta32: number padLength: number hmacStrength: number constructor ( blockSize: number, outSize: number, hmacStrength: number, padLength: number ) { this.pending = null this.pendingTotal = 0 this.blockSize = blockSize this.outSize = outSize this.hmacStrength = hmacStrength this.padLength = padLength / 8 this.endian = 'big' this._delta8 = this.blockSize / 8 this._delta32 = this.blockSize / 32 } _update (msg: number[], start: number): void { throw new Error('Not implemented') } _digest (): number[] { throw new Error('Not implemented') } _digestHex (): string { throw new Error('Not implemented') } /** * Converts the input message into an array, pads it, and joins into 32bit blocks. * If there is enough data, it tries updating the hash computation. * * @method update * @param msg - The message segment to include in the hashing computation. * @param enc - The encoding of the message. If 'hex', the string will be treated as such, 'utf8' otherwise. * * @returns Returns the instance of the object for chaining. * * @example * sha256.update('Hello World', 'utf8'); */ update (msg: number[] | string, enc?: 'hex' | 'utf8'): this { // Convert message to array, pad it, and join into 32bit blocks msg = toArray(msg, enc) if (this.pending == null) { this.pending = msg } else { this.pending = this.pending.concat(msg) } this.pendingTotal += msg.length // Enough data, try updating if (this.pending.length >= this._delta8) { msg = this.pending // Process pending data in blocks const r = msg.length % this._delta8 this.pending = msg.slice(msg.length - r, msg.length) if (this.pending.length === 0) { this.pending = null } msg = join32(msg, 0, msg.length - r, this.endian) for (let i = 0; i < msg.length; i += this._delta32) { this._update(msg, i) } } return this } /** * Finalizes the hash computation and returns the hash value/result. * * @method digest * * @returns Returns the final hash value. * * @example * const hash = sha256.digest(); */ digest (): number[] { this.update(this._pad()) assert(this.pending === null) return this._digest() } /** * Finalizes the hash computation and returns the hash value/result as a hex string. * * @method digest * * @returns Returns the final hash value as a hex string. * * @example * const hash = sha256.digestHex(); */ digestHex (): string { this.update(this._pad()) assert(this.pending === null) return this._digestHex() } /** * [Private Method] Used internally to prepare the padding for the final stage of the hash computation. * * @method _pad * @private * * @returns Returns an array denoting the padding. */ private _pad (): number[] { const len = this.pendingTotal if (!Number.isSafeInteger(len) || len < 0) { throw new Error('Message too long for this hash function') } const bytes = this._delta8 const k = bytes - ((len + this.padLength) % bytes) const res = new Array(k + this.padLength) res[0] = 0x80 let i: number for (i = 1; i < k; i++) { res[i] = 0 } const lengthBytes = this.padLength const maxBits = 1n << BigInt(lengthBytes * 8) let totalBits = BigInt(len) * 8n if (totalBits >= maxBits) { throw new Error('Message too long for this hash function') } if (this.endian === 'big') { const lenArray = new Array<number>(lengthBytes) for (let b = lengthBytes - 1; b >= 0; b--) { lenArray[b] = Number(totalBits & 0xffn) totalBits >>= 8n } for (let b = 0; b < lengthBytes; b++) { res[i++] = lenArray[b] } } else { for (let b = 0; b < lengthBytes; b++) { res[i++] = Number(totalBits & 0xffn) totalBits >>= 8n } } return res } } function isSurrogatePair (msg: string, i: number): boolean { if ((msg.charCodeAt(i) & 0xfc00) !== 0xd800) { return false } if (i < 0 || i + 1 >= msg.length) { return false } return (msg.charCodeAt(i + 1) & 0xfc00) === 0xdc00 } /** * * @param msg * @param enc Optional. Encoding to use if msg is string. Default is 'utf8'. * @returns array of byte values from msg. If msg is an array, a copy is returned. */ export function toArray ( msg: number[] | string, enc?: 'hex' | 'utf8' ): number[] { if (Array.isArray(msg)) { return msg.slice() } if (!(msg as unknown as boolean)) { return [] } const res: number[] = [] if (typeof msg === 'string') { if (enc !== 'hex') { // Inspired by stringToUtf8ByteArray() in closure-library by Google // https://github.com/google/closure-library/blob/8598d87242af59aac233270742c8984e2b2bdbe0/closure/goog/crypt/crypt#L117-L143 // Apache License 2.0 // https://github.com/google/closure-library/blob/master/LICENSE let p = 0 for (let i = 0; i < msg.length; i++) { let c = msg.charCodeAt(i) if (c < 128) { res[p++] = c } else if (c < 2048) { res[p++] = (c >> 6) | 192 res[p++] = (c & 63) | 128 } else if (isSurrogatePair(msg, i)) { c = 0x10000 + ((c & 0x03ff) << 10) + (msg.charCodeAt(++i) & 0x03ff) res[p++] = (c >> 18) | 240 res[p++] = ((c >> 12) & 63) | 128 res[p++] = ((c >> 6) & 63) | 128 res[p++] = (c & 63) | 128 } else { res[p++] = (c >> 12) | 224 res[p++] = ((c >> 6) & 63) | 128 res[p++] = (c & 63) | 128 } } } else { assertValidHex(msg) msg = normalizeHex(msg) for (let i = 0; i < msg.length; i += 2) { res.push(parseInt(msg[i] + msg[i + 1], 16)) } } } else { msg = msg as number[] for (let i = 0; i < msg.length; i++) { res[i] = msg[i] | 0 } } return res } /** * @deprecated * This function behaves differently from the standard C `htonl()`. * It always performs an unconditional 32-bit byte swap. * Use `swapBytes32()` for explicit byte swapping, or `realHtonl()` for * standards-compliant host-to-network conversion. */ export function htonl (w: number): number { return swapBytes32(w) } function toHex32 (msg: number[], endian?: 'little' | 'big'): string { let res = '' for (let i = 0; i < msg.length; i++) { let w = msg[i] if (endian === 'little') { w = htonl(w) } res += zero8(w.toString(16)) } return res } function zero8 (word: string): string { if (word.length === 7) { return '0' + word } else if (word.length === 6) { return '00' + word } else if (word.length === 5) { return '000' + word } else if (word.length === 4) { return '0000' + word } else if (word.length === 3) { return '00000' + word } else if (word.length === 2) { return '000000' + word } else if (word.length === 1) { return '0000000' + word } else { return word } } function bytesToHex (data: Uint8Array): string { let res = '' for (const b of data) res += (b.toString(16).padStart(2, '0') as string) return res } function join32 (msg, start, end, endian): number[] { const len = end - start assert(len % 4 === 0) const res = new Array(len / 4) for (let i = 0, k: number = start; i < res.length; i++, k += 4) { let w if (endian === 'big') { w = (msg[k] << 24) | (msg[k + 1] << 16) | (msg[k + 2] << 8) | msg[k + 3] } else { w = (msg[k + 3] << 24) | (msg[k + 2] << 16) | (msg[k + 1] << 8) | msg[k] } res[i] = w >>> 0 } return res } function split32 (msg: number[], endian: 'big' | 'little'): number[] { const res = new Array(msg.length * 4) for (let i = 0, k = 0; i < msg.length; i++, k += 4) { const m = msg[i] if (endian === 'big') { res[k] = m >>> 24 res[k + 1] = (m >>> 16) & 0xff res[k + 2] = (m >>> 8) & 0xff res[k + 3] = m & 0xff } else { res[k + 3] = m >>> 24 res[k + 2] = (m >>> 16) & 0xff res[k + 1] = (m >>> 8) & 0xff res[k] = m & 0xff } } return res } function rotr32 (w: number, b: number): number { return (w >>> b) | (w << (32 - b)) } function rotl32 (w: number, b: number): number { return (w << b) | (w >>> (32 - b)) } function sum32 (a: number, b: number): number { return (a + b) >>> 0 } function SUM32_3 (a: number, b: number, c: number): number { return (a + b + c) >>> 0 } function SUM32_4 (a: number, b: number, c: number, d: number): number { return (a + b + c + d) >>> 0 } function SUM32_5 ( a: number, b: number, c: number, d: number, e: number ): number { return (a + b + c + d + e) >>> 0 } function FT_1 (s, x, y, z): number { if (s === 0) { return ch32(x, y, z) } if (s === 1 || s === 3) { return p32(x, y, z) } if (s === 2) { return maj32(x, y, z) } return 0 } function ch32 (x, y, z): number { return (x & y) ^ (~x & z) } function maj32 (x, y, z): number { return (x & y) ^ (x & z) ^ (y & z) } function p32 (x, y, z): number { return x ^ y ^ z } function S0_256 (x): number { return rotr32(x, 2) ^ rotr32(x, 13) ^ rotr32(x, 22) } function S1_256 (x): number { return rotr32(x, 6) ^ rotr32(x, 11) ^ rotr32(x, 25) } function G0_256 (x): number { return rotr32(x, 7) ^ rotr32(x, 18) ^ (x >>> 3) } function G1_256 (x): number { return rotr32(x, 17) ^ rotr32(x, 19) ^ (x >>> 10) } const r = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 ] const rh = [ 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 ] const s = [ 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 ] const sh = [ 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 ] function f (j, x, y, z): number { if (j <= 15) { return x ^ y ^ z } else if (j <= 31) { return (x & y) | (~x & z) } else if (j <= 47) { return (x | ~y) ^ z } else if (j <= 63) { return (x & z) | (y & ~z) } else { return x ^ (y | ~z) } } function K (j): number { if (j <= 15) { return 0x00000000 } else if (j <= 31) { return 0x5a827999 } else if (j <= 47) { return 0x6ed9eba1 } else if (j <= 63) { return 0x8f1bbcdc } else { return 0xa953fd4e } } function Kh (j): number { if (j <= 15) { return 0x50a28be6 } else if (j <= 31) { return 0x5c4dd124 } else if (j <= 47) { return 0x6d703ef3 } else if (j <= 63) { return 0x7a6d76e9 } else { return 0x00000000 } } /** * An implementation of RIPEMD160 cryptographic hash function. Extends the BaseHash class. * It provides a way to compute a 'digest' for any kind of input data; transforming the data * into a unique output of fixed size. The output is deterministic; it will always be * the same for the same input. * * @class RIPEMD160 * @param None * * @constructor * Use the RIPEMD160 constructor to create an instance of RIPEMD160 hash function. * * @example * const ripemd160 = new RIPEMD160(); * * @property h - Array that is updated iteratively as part of hashing computation. */ export class RIPEMD160 extends BaseHash { h: number[] constructor () { super(512, 160, 192, 64) this.endian = 'little' this.h = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0] this.endian = 'little' } _update (msg: number[], start: number): void { let A = this.h[0] let B = this.h[1] let C = this.h[2] let D = this.h[3] let E = this.h[4] let Ah = A let Bh = B let Ch = C let Dh = D let Eh = E let T for (let j = 0; j < 80; j++) { T = sum32( rotl32(SUM32_4(A, f(j, B, C, D), msg[r[j] + start], K(j)), s[j]), E ) A = E E = D D = rotl32(C, 10) C = B B = T T = sum32( rotl32( SUM32_4(Ah, f(79 - j, Bh, Ch, Dh), msg[rh[j] + start], Kh(j)), sh[j] ), Eh ) Ah = Eh Eh = Dh Dh = rotl32(Ch, 10) Ch = Bh Bh = T } T = SUM32_3(this.h[1], C, Dh) this.h[1] = SUM32_3(this.h[2], D, Eh) this.h[2] = SUM32_3(this.h[3], E, Ah) this.h[3] = SUM32_3(this.h[4], A, Bh) this.h[4] = SUM32_3(this.h[0], B, Ch) this.h[0] = T } _digest (): number[] { return split32(this.h, 'little') } _digestHex (): string { return toHex32(this.h, 'little') } } /** * An implementation of SHA256 cryptographic hash function. Extends the BaseHash class. * It provides a way to compute a 'digest' for any kind of input data; transforming the data * into a unique output of fixed size. The output is deterministic; it will always be * the same for the same input. * * @class SHA256 * @param None * * @constructor * Use the SHA256 constructor to create an instance of SHA256 hash function. * * @example * const sha256 = new SHA256(); * * @property h - The initial hash constants * @property W - Provides a way to recycle usage of the array memory. * @property k - The round constants used for each round of SHA-256 */ export class SHA256 { private readonly h: FastSHA256 constructor () { this.h = new FastSHA256() } update ( msg: Uint8Array | number[] | string, enc?: 'hex' | 'utf8' ): this { const data = msg instanceof Uint8Array ? msg : Uint8Array.from(toArray(msg, enc)) this.h.update(data) return this } digest (): number[] { return Array.from(this.h.digest()) } digestHex (): string { return bytesToHex(this.h.digest()) } } /** * An implementation of SHA1 cryptographic hash function. Extends the BaseHash class. * It provides a way to compute a 'digest' for any kind of input data; transforming the data * into a unique output of fixed size. The output is deterministic; it will always be * the same for the same input. * * @class SHA1 * @param None * * @constructor * Use the SHA1 constructor to create an instance of SHA1 hash function. * * @example * const sha1 = new SHA1(); * * @property h - The initial hash constants. * @property W - Provides a way to recycle usage of the array memory. * @property k - The round constants used for each round of SHA-1. */ export class SHA1 extends BaseHash { h: number[] W: number[] k: number[] constructor () { super(512, 160, 80, 64) this.k = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6] this.h = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0] this.W = new Array(80) } _update (msg: number[], start?: number): void { const W = this.W // Default start to 0 if (start === undefined) { start = 0 } let i: number for (i = 0; i < 16; i++) { W[i] = msg[start + i] } for (; i < W.length; i++) { W[i] = rotl32(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1) } let a = this.h[0] let b = this.h[1] let c = this.h[2] let d = this.h[3] let e = this.h[4] for (i = 0; i < W.length; i++) { const s = ~~(i / 20) const t = SUM32_5(rotl32(a, 5), FT_1(s, b, c, d), e, W[i], this.k[s]) e = d d = c c = rotl32(b, 30) b = a a = t } this.h[0] = sum32(this.h[0], a) this.h[1] = sum32(this.h[1], b) this.h[2] = sum32(this.h[2], c) this.h[3] = sum32(this.h[3], d) this.h[4] = sum32(this.h[4], e) } _digest (): number[] { return split32(this.h, 'big') } _digestHex (): string { return toHex32(this.h, 'big') } } /** * An implementation of SHA512 cryptographic hash function. Extends the BaseHash class. * It provides a way to compute a 'digest' for any kind of input data; transforming the data * into a unique output of fixed size. The output is deterministic; it will always be * the same for the same input. * * @class SHA512 * @param None * * @constructor * Use the SHA512 constructor to create an instance of SHA512 hash function. * * @example * const sha512 = new SHA512(); * * @property h - The initial hash constants. * @property W - Provides a way to recycle usage of the array memory. * @property k - The round constants used for each round of SHA-512. */ export class SHA512 { private readonly h: FastSHA512 constructor () { this.h = new FastSHA512() } update (msg: number[] | string, enc?: 'hex' | 'utf8'): this { const data = Uint8Array.from(toArray(msg, enc)) this.h.update(data) return this } digest (): number[] { return Array.from(this.h.digest()) } digestHex (): string { return bytesToHex(this.h.digest()) } } /** * The `SHA256HMAC` class is used to create Hash-based Message Authentication Code (HMAC) using the SHA-256 cryptographic hash function. * * HMAC is a specific type of MAC involving a cryptographic hash function and a secret cryptographic key. It may be used to simultaneously verify both the data integrity and the authenticity of a message. * * This class also uses the SHA-256 cryptographic hash algorithm that produces a 256-bit (32-byte) hash value. * * @property inner - Represents the inner hash of SHA-256. * @property outer - Represents the outer hash of SHA-256. * @property blockSize - The block size for the SHA-256 hash function, in bytes. It's set to 64 bytes. * @property outSize - The output size of the SHA-256 hash function, in bytes. It's set to 32 bytes. */ export class SHA256HMAC { private readonly h: HMAC<FastSHA256> blockSize = 64 outSize = 32 /** * The constructor for the `SHA256HMAC` class. * * It initializes the `SHA256HMAC` object and sets up the inner and outer padded keys. * If the key size is larger than the blockSize, it is digested using SHA-256. * If the key size is less than the blockSize, it is padded with zeroes. * * @constructor * @param key - The key to use to create the HMAC. Can be a number array or a string in hexadecimal format. * * @example * const myHMAC = new SHA256HMAC('deadbeef'); */ constructor (key: Uint8Array | number[] | string) { const k = key instanceof Uint8Array ? key : Uint8Array.from( toArray( key, typeof key === 'string' ? 'hex' : undefined ) ) this.h = new HMAC(sha256Fast, k) } /** * Updates the `SHA256HMAC` object with part of the message to be hashed. * * @method update * @param msg - Part of the message to hash. Can be a number array or a string. * @param enc - If 'hex', then the input is encoded as hexadecimal. If undefined or not 'hex', then no encoding is performed. * @returns Returns the instance of `SHA256HMAC` for chaining calls. * * @example * myHMAC.update('deadbeef', 'hex'); */ update (msg: Uint8Array | number[] | string, enc?: 'hex'): SHA256HMAC { const data = msg instanceof Uint8Array ? msg : Uint8Array.from(toArray(msg, enc)) this.h.update(data) return this } /** * Finalizes the HMAC computation and returns the resultant hash. * * @method digest * @returns Returns the digest of the hashed data. Can be a number array or a string. * * @example * let hashedMessage = myHMAC.digest(); */ digest (): number[] { return Array.from(this.h.digest()) } /** * Finalizes the HMAC computation and returns the resultant hash as a hex string. * * @method digest * @returns Returns the digest of the hashed data as a hex string * * @example * let hashedMessage = myHMAC.digestHex(); */ digestHex (): string { return bytesToHex(this.h.digest()) } } export class SHA1HMAC { inner: SHA1 outer: SHA1 blockSize = 64 constructor (key: number[] | string) { key = toArray(key, 'hex') // Shorten key, if needed if (key.length > this.blockSize) { key = new SHA1().update(key).digest() } // Keys shorter than block size are padded with zeros on the right let i for (i = key.length; i < this.blockSize; i++) { key.push(0) } for (i = 0; i < key.length; i++) { key[i] ^= 0x36 } this.inner = new SHA1().update(key) // 0x36 ^ 0x5c = 0x6a for (i = 0; i < key.length; i++) { key[i] ^= 0x6a } this.outer = new SHA1().update(key) } update (msg: number[] | string, enc?: 'hex'): SHA1HMAC { this.inner.update(msg, enc) return this } digest (): number[] { this.outer.update(this.inner.digest()) return this.outer.digest() } digestHex (): string { this.outer.update(this.inner.digest()) return this.outer.digestHex() } } /** * The `SHA512HMAC` class is used to create Hash-based Message Authentication Code (HMAC) using the SHA-512 cryptographic hash function. * * HMAC is a specific type of MAC involving a cryptographic hash function and a secret cryptographic key. It may be used to simultaneously verify both the data integrity and the authenticity of a message. * * This class also uses the SHA-512 cryptographic hash algorithm that produces a 512-bit (64-byte) hash value. * * @property inner - Represents the inner hash of SHA-512. * @property outer - Represents the outer hash of SHA-512. * @property blockSize - The block size for the SHA-512 hash function, in bytes. It's set to 128 bytes. * @property outSize - The output size of the SHA-512 hash function, in bytes. It's set to 64 bytes. */ export class SHA512HMAC { private readonly h: HMAC<FastSHA512> blockSize = 128 outSize = 32 /** * The constructor for the `SHA512HMAC` class. * * It initializes the `SHA512HMAC` object and sets up the inner and outer padded keys. * If the key size is larger than the blockSize, it is digested using SHA-512. * If the key size is less than the blockSize, it is padded with zeroes. * * @constructor * @param key - The key to use to create the HMAC. Can be a number array or a string in hexadecimal format. * * @example * const myHMAC = new SHA512HMAC('deadbeef'); */ constructor (key: Uint8Array | number[] | string) { const k = key instanceof Uint8Array ? key : Uint8Array.from( toArray( key, typeof key === 'string' ? 'hex' : undefined ) ) this.h = new HMAC(sha512Fast, k) } /** * Updates the `SHA512HMAC` object with part of the message to be hashed. * * @method update * @param msg - Part of the message to hash. Can be a number array or a string. * @param enc - If 'hex', then the input is encoded as hexadecimal. If undefined or not 'hex', then no encoding is performed. * @returns Returns the instance of `SHA512HMAC` for chaining calls. * * @example * myHMAC.update('deadbeef', 'hex'); */ update (msg: Uint8Array | number[] | string, enc?: 'hex' | 'utf8'): SHA512HMAC { const data = msg instanceof Uint8Array ? msg : Uint8Array.from(toArray(msg, enc)) this.h.update(data) return this } /** * Finalizes the HMAC computation and returns the resultant hash. * * @method digest * @returns Returns the digest of the hashed data as a number array. * * @example * let hashedMessage = myHMAC.digest(); */ digest (): number[] { return Array.from(this.h.digest()) } /** * Finalizes the HMAC computation and returns the resultant hash as a hex string. * * @method digest * @returns Returns the digest of the hashed data as a hex string * * @example * let hashedMessage = myHMAC.digestHex(); */ digestHex (): string { return bytesToHex(this.h.digest()) } } /** * Computes RIPEMD160 hash of a given message. * @function ripemd160 * @param msg - The message to compute the hash for. * @param enc - The encoding of msg if string. Default is 'utf8'. * * @returns the computed RIPEMD160 hash of the message. * * @example * const digest = ripemd160('Hello, world!'); */ export const ripemd160 = ( msg: number[] | string, enc?: 'hex' | 'utf8' ): number[] => { return new RIPEMD160().update(msg, enc).digest() } /** * Computes SHA1 hash of a given message. * @function sha1 * @param msg - The message to compute the hash for. * @param enc - The encoding of msg if string. Default is 'utf8'. * * @returns the computed SHA1 hash of the message. * * @example * const digest = sha1('Hello, world!'); */ export const sha1 = ( msg: number[] | string, enc?: 'hex' | 'utf8' ): number[] => { return new SHA1().update(msg, enc).digest() } /** * Computes SHA256 hash of a given message. * @function sha256 * @param msg - The message to compute the hash for. * @param enc - The encoding of msg if string. Default is 'utf8'. * * @returns the computed SHA256 hash of the message. * * @example * const digest = sha256('Hello, world!'); */ export const sha256 = ( msg: Uint8Array | number[] | string, enc?: 'hex' | 'utf8' ): number[] => { return new SHA256().update(msg, enc).digest() } /** * Computes SHA512 hash of a given message. * @function sha512 * @param msg - The message to compute the hash for. * @param enc - The encoding of msg if string. Default is 'utf8'. * * @returns the computed SHA512 hash of the message. * * @example * const digest = sha512('Hello, world!'); */ export const sha512 = ( msg: number[] | string, enc?: 'hex' | 'utf8' ): number[] => { return new SHA512().update(msg, enc).digest() } /** * Performs a 'double hash' using SHA256. This means the data is hashed twice * with SHA256. First, the SHA256 hash of the message is computed, then the * SHA256 hash of the resulting hash is computed. * @function hash256 * @param msg - The message to compute the hash for. * @param enc - The encoding of msg if string. Default is 'utf8'. * * @returns the double hashed SHA256 output. * * @example * const doubleHash = hash256('Hello, world!'); */ export const hash256 = ( msg: Uint8Array | number[] | string, enc?: 'hex' | 'utf8' ): number[] => { const first = new SHA256().update(msg, enc).digest() return new SHA256().update(first).digest() } /** * Computes SHA256 hash of a given message and then computes a RIPEMD160 hash of the result. * * @function hash160 * @param msg - The message to compute the hash for. * @param enc - The encoding of msg if string. Default is 'utf8'. * * @returns the RIPEMD160 hash of the SHA256 hash of the input message. * * @example * const hash = hash160('Hello, world!'); */ export const hash160 = ( msg: Uint8Array | number[] | string, enc?: 'hex' | 'utf8' ): number[] => { const first = new SHA256().update(msg, enc).digest() return new RIPEMD160().update(first).digest() } /** * Computes SHA256 HMAC of a given message with a given key. * @function sha256hmac * @param key - The key used to compute the HMAC * @param msg - The message to compute the hash for. * @param enc - The encoding of msg if string. Default is 'utf8'. * * @returns the computed HMAC of the message. * * @example * const digest = sha256hmac('deadbeef', 'ffff001d'); */ export const sha256hmac = ( key: Uint8Array | number[] | string, msg: Uint8Array | number[] | string, enc?: 'hex' ): number[] => { return new SHA256HMAC(key).update(msg, enc).digest() } /** * Computes SHA512 HMAC of a given message with a given key. * @function sha512hmac * @param key - The key used to compute the HMAC * @param msg - The message to compute the hash for. * @param enc - The encoding of msg if string. Default is 'utf8'. * * @returns the computed HMAC of the message. * * @example * const digest = sha512hmac('deadbeef', 'ffff001d'); */ export const sha512hmac = ( key: Uint8Array | number[] | string, msg: Uint8Array | number[] | string, enc?: 'hex' ): number[] => { return new SHA512HMAC(key).update(msg, enc).digest() } // BEGIN fast-pbkdf2 helpers // Utils function isBytes (a: unknown): a is Uint8Array { return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array') } function anumber (n: number): void { if (!Number.isSafeInteger(n) || n < 0) { throw new Error(`positive integer expected, got ${n}`) } } function abytes (b: Uint8Array | undefined, ...lengths: number[]): void { if (!isBytes(b)) throw new Error('Uint8Array expected') if (lengths.length > 0 && !lengths.includes(b.length)) { const lens = lengths.join(',') throw new Error(`Uint8Array expected of length ${lens}, got length=${b.length}`) } } function ahash (h: IHash): void { if (typeof h !== 'function' || typeof h.create !== 'function') { throw new Error('Hash should be wrapped by utils.createHasher') } anumber(h.outputLen) anumber(h.blockLen) } function aexists (instance: any, checkFinished = true): void { if (instance.destroyed === true) throw new Error('Hash instance has been destroyed') if (checkFinished && instance.finished === true) { throw new Error('Hash#digest() has already been called') } } function aoutput (out: any, instance: any): void { abytes(out) const min: number = instance.outputLen as number if (out.length < min) { throw new Error(`digestInto() expects output buffer of length at least ${min}`) } } type TypedArray = | Int8Array | Uint8ClampedArray | Uint8Array | Uint16Array | Int16Array | Uint32Array | Int32Array function clean (...arrays: TypedArray[]): void { for (let i = 0; i < arrays.length; i++) arrays[i].fill(0) } function createView (arr: TypedArray): DataView { return new DataView(arr.buffer, arr.byteOffset, arr.byteLength) } function toBytes (data: Input): Uint8Array { if (typeof data === 'string') data = utf8ToBytes(data) abytes(data) return data } function utf8ToBytes (str: string): Uint8Array { if (typeof str !== 'string') throw new Error('string expected') return new Uint8Array(new TextEncoder().encode(str)) } type Input = string | Uint8Array type KDFInput = string | Uint8Array function kdfInputToBytes (data: KDFInput): Uint8Array { if (typeof data === 'string') data = utf8ToBytes(data) abytes(data) return data } interface IHash { (data: Uint8Array): Uint8Array blockLen: number outputLen: number create: any } interface Hasher<T extends Hash<T>> { (msg: Input): Uint8Array blockLen: number outputLen: number create: () => Hash<T> } abstract class Hash<T extends Hash<T>> { abstract blockLen: number abstract outputLen: number abstract update (buf: Input): this abstract digestInto (buf: Uint8Array): void abstract digest (): Uint8Array abstract destroy (): void abstract _cloneInto (to?: T): T abstract clone (): T } function createHasher<T extends Hash<T>> (hashCons: () => Hash<T>): Hasher<T> { const hashC = (msg: Input): Uint8Array => hashCons().update(toBytes(msg)).digest() const tmp = hashCons() hashC.outputLen = tmp.outputLen hashC.blockLen = tmp.blockLen hashC.create = () => hashCons() return hashC } // u64 helpers const U32_MASK64 = BigInt(2 ** 32 - 1) const _32n = BigInt(32) function fromBig (n: bigint, le = false): { h: number, l: number } { if (le) return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) } return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 } } function split (lst: bigint[], le = false): Uint32Array[] { const len = lst.length const Ah = new Uint32Array(len) const Al = new Uint32Array(len) for (let i = 0; i < len; i++) { const { h, l } = fromBig(lst[i], le) Ah[i] = h Al[i] = l } return [Ah, Al] } const shrSH = (h: number, _l: number, s: number): number => h >>> s const shrSL = (h: number, l: number, s: number): number => (h << (32 - s)) | (l >>> s) const rotrSH = (h: number, l: number, s: number): number => (h >>> s) | (l << (32 - s)) const rotrSL = (h: number, l: number, s: number): number => (h << (32 - s)) | (l >>> s) const rotrBH = (h: number, l: number, s: number): number => (h << (64 - s)) | (l >>> (s - 32)) const rotrBL = (h: number, l: number, s: number): number => (h >>> (s - 32)) | (l << (64 - s)) function add (Ah: number, Al: number, Bh: number, Bl: number): { h: number, l: number } { const l = (Al >>> 0) + (Bl >>> 0) return { h: (Ah + Bh + ((l / 2 ** 32) | 0)) | 0, l: l | 0 } } const add3L = (Al: number, Bl: number, Cl: number): number => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) const add3H = (low: number, Ah: number, Bh: number, Ch: number): number => (Ah + Bh + Ch + ((low / 2 ** 32) | 0)) | 0 const add4L = (Al: number, Bl: number, Cl: number, Dl: number): number => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) const add4H = (low: number, Ah: number, Bh: number, Ch: number, Dh: number): number => (Ah + Bh + Ch + Dh + ((low / 2 ** 32) | 0)) | 0 const add5L = (Al: number, Bl: number, Cl: number, Dl: number, El: number): number => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0) const add5H = (low: number, Ah: number, Bh: number, Ch: number, Dh: number, Eh: number): number => (Ah + Bh + Ch + Dh + Eh + ((low / 2 ** 32) | 0)) | 0 // _md helpers abstract class HashMD<T extends HashMD<T>> extends Hash<T> { readonly blockLen: number readonly outputLen: number readonly padOffset: number readonly isLE: boolean protected buffer: Uint8Array protected view: DataView protected finished = false protected length = 0 protected pos = 0 protected destroyed = false constructor (blockLen: number, outputLen: number, padOffset: number, isLE: boolean) { super() this.blockLen = blockLen this.outputLen = outputLen this.padOffset = padOffset this.isLE = isLE this.buffer = new Uint8Array(blockLen) this.view = createView(this.buffer) } protected abstract process (buf: DataView, offset: number): void protected abstract get (): number[] protected abstract set (...args: number[]): void abstract destroy (): void protected abstract roundClean (): void update (data: Input): this { aexists(this) data = toBytes(data) abytes(data) const { view, buffer, blockLen } = this const len = data.length for (let pos = 0; pos < len;) { const take = Math.min(blockLen - this.pos, len - pos) 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: Uint8Array): void { aexists(this) aoutput(out, this) this.finished = true const { buffer, view, blockLen, isLE } = this let { pos } = this buffer[pos++] = 0b10000000 clean(this.buffer.subarray(pos)) if (this.padOffset > blockLen - pos) { this.process(view, 0) pos = 0 } for (let i = pos; i < blockLen; i++) buffer[i] = 0 setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE) this.process(view, 0) const oview = createView(out) const len = this.outputLen if (len % 4 !== 0) 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 (): Uint8Array { const { buffer, outputLen } = this this.digestInto(buffer) const res = buffer.slice(0, outputLen) this.destroy() return res } _cloneInto (to?: T): T { to ||= new (this.constructor as any)() as T to.set(...this.get()) const { blockLen, buffer, length, finished, destroyed, pos } = this to.destroyed = destroyed to.finished = finished to.length = length to.pos = pos if (length % blockLen !== 0) to.buffer.set(buffer) return to } clone (): T { return this._cloneInto() } } function setBigUint64 (view: DataView, byteOffset: number, value: bigint, isLE: boolean): void { 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) } // sha256 fast constants const SHA256_IV = Uint32Array.from([ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ]) const K256 = Uint32Array.from([ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 ]) const SHA256_W = new Uint32Array(64) class FastSHA256 extends HashMD<FastSHA256> { protected A = SHA256_IV[0] | 0 protected B = SHA256_IV[1] | 0 protected C = SHA256_IV[2] | 0 protected D = SHA256_IV[3] | 0 protected E = SHA256_IV[4] | 0 protected F = SHA256_IV[5] | 0 protected G = SHA256_IV[6] | 0 protected H = SHA256_IV[7] | 0 constructor (outputLen = 32) { super(64, outputLen, 8, false) } protected get (): number[] { const { A, B, C, D, E, F, G, H } = this return [A, B, C, D, E, F, G, H] } protected set ( A: number, B: number, C: number, D: number, E: number, F: number, G: number, H: number ): void { this.A = A | 0 this.B = B | 0 this.C = C | 0 this.D = D | 0 this.E = E | 0 this.F = F | 0 this.G = G | 0 this.H = H | 0 } protected process (view: DataView, offset: number): void { for (let i = 0; i < 16; i++, offset += 4) { SHA256_W[i] = view.getUint32(offset) } for (let i = 16; i < 64; i++) { const w15 = SHA256_W[i - 15] const w2 = SHA256_W[i - 2] const s0 = G0_256(w15) const s1 = G1_256(w2) SHA256_W[i] = sum32(sum32(s0, SHA256_W[i - 7]), sum32(s1, SHA256_W[i - 16])) } let { A, B, C, D, E, F, G, H } = this for (let i = 0; i < 64; i++) { const T1 = SUM32_5(H, S1_256(E), ch32(E, F, G), K256[i], SHA256_W[i]) const T2 = sum32(S0_256(A), maj32(A, B, C)) H = G G = F F = E E = sum32(D, T1) D = C C = B B = A A = sum32(T1, T2) } this.A = sum32(this.A, A) this.B = sum32(this.B, B) this.C = sum32(this.C, C) this.D = sum32(this.D, D) this.E = sum32(this.E, E) this.F = sum32(this.F, F) this.G = sum32(this.G, G) this.H = sum32(this.H, H) } protected roundClean (): void { clean(SHA256_W) } destroy (): void { clean(this.buffer) this.set(0, 0, 0, 0, 0, 0, 0, 0) } } const sha256Fast = createHasher(() => new FastSHA256()) // sha512 const SHA512_IV = Uint32Array.from([ 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1, 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179 ]) const K512 = (() => split([ '0x428a2f98d728ae22', '0x7137449123ef65cd', '0xb5c0fbcfec4d3b2f', '0xe9b5dba58189dbbc', '0x3956c25bf348b538', '0x59f111f1b605d019', '0x923f82a4af194f9b', '0xab1c5ed5da6d8118', '0xd807aa98a3030242', '0x12835b0145706fbe', '0x243185be4ee4b28c', '0x550c7dc3d5ffb4e2', '0x72be5d74f27b896f', '0x80deb1fe3b1696b1', '0x9bdc06a725c71235', '0xc19bf174cf692694', '0xe49b69c19ef14ad2', '0xefbe4786384f25e3', '0x0fc19dc68b8cd5b5', '0x240ca1cc77ac9c65', '0x2de92c6f592b0275', '0x4a7484aa6ea6e483', '0x5cb0a9dcbd41fbd4', '0x76f988da831153b5', '0x983e5152ee66dfab', '0xa831c66d2db43210', '0xb00327c898fb213f', '0xbf597fc7beef0ee4', '0xc6e00bf33da88fc2', '0xd5a79147930aa725', '0x06ca6351e003826f', '0x142929670a0e6e70', '0x27b70a8546d22ffc', '0x2e1b21385c26c926', '0x4d2c6dfc5ac42aed', '0x53380d139d95b3df', '0x650a73548baf63de', '0x766a0abb3c77b2a8', '0x81c2c92e47edaee6', '0x92722c851482353b', '0xa2bfe8a14cf10364', '0xa81a664bbc423001', '0xc24b8b70d0f89791', '0xc76c51a30654be30', '0xd192e819d6ef5218', '0xd69906245565a910', '0xf40e35855771202a', '0x106aa07032bbd1b8', '0x19a4c116b8d2d0c8', '0x1e376c085141ab53', '0x2748774cdf8eeb99', '0x34b0bcb5e19b48a8', '0x391c0cb3c5c95a63', '0x4ed8aa4ae3418acb', '0x5b9cca4f7763e373', '0x682e6ff3d6b2b8a3', '0x748f82ee5defb2fc', '0x78a5636f43172f60', '0x84c87814a1f0ab72', '0x8cc702081a6439ec', '0x90befffa23631e28', '0xa4506cebde82bde9', '0xbef9a3f7b2c67915', '0xc67178f2e372532b', '0xca273eceea26619c', '0xd186b8c721c0c207', '0xeada7dd6cde0eb1e', '0xf57d4f7fee6ed178', '0x06f067aa72176fba', '0x0a637dc5a2c898a6', '0x113f9804bef90dae', '0x1b710b35131c471b', '0x28db77f523047d84', '0x32caab7b40c72493', '0x3c9ebe0a15c9bebc', '0x431d67c49c100d4c', '0x4cc5d4becb3e42b6', '0x597f299cfc657e2a', '0x5fcb6fab3ad6faec', '0x6c44198c4a475817' ].map((n) => BigInt(n))) )() const SHA512_Kh = (() => K512[0])() const SHA512_Kl = (() => K512[1])() const SHA512_W_H = new Uint32Array(80) const SHA512_W_L = new Uint32Array(80) class FastSHA512 extends HashMD<FastSHA512> { protected Ah = SHA512_IV[0] | 0 protected Al = SHA512_IV[1] | 0 protected Bh = SHA512_IV[2] | 0 protected Bl = SHA512_IV[3] | 0 protected Ch = SHA512_IV[4] | 0 protected Cl = SHA512_IV[5] | 0 protected Dh = SHA512_IV[6] | 0 protected Dl = SHA512_IV[7] | 0 protected Eh = SHA512_IV[8] | 0 protected El = SHA512_IV[9] | 0 protected Fh = SHA512_IV[10] | 0 protected Fl = SHA512_IV[11] | 0 protected Gh = SHA512_IV[12] | 0 protected Gl = SHA512_IV[13] | 0 protected Hh = SHA512_IV[14] | 0 protected Hl = SHA512_IV[15] | 0 constructor (outputLen = 64) { super(128, outputLen, 16, false) } protected get (): number[] { const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl] } protected set ( Ah: number, Al: number, Bh: number, Bl: number, Ch: number, Cl: number, Dh: number, Dl: number, Eh: number, El: number, Fh: number, Fl: number, Gh: number, Gl: number, Hh: number, Hl: number ): void { this.Ah = Ah | 0 this.Al = Al | 0 this.Bh = Bh | 0 this.Bl = Bl | 0 this.Ch = Ch | 0 this.Cl = Cl | 0 this.Dh = Dh | 0 this.Dl = Dl | 0 this.Eh = Eh | 0 this.El = El | 0 this.Fh = Fh | 0 this.Fl = Fl | 0 this.Gh = Gh | 0 this.Gl = Gl | 0 this.Hh = Hh | 0 this.Hl = Hl | 0 } protected process (view: DataView, offset: number): void { for (let i = 0; i < 16; i++, offset += 4) { SHA512_W_H[i] = view.getUint32(offset) SHA512_W_L[i] = view.getUint32((offset += 4)) } for (let i = 16; i < 80; i++) { const W15h = SHA512_W_H[i - 15] | 0 const W15l = SHA512_W_L[i - 15] | 0 const s0h = rotrSH(W15h, W15l, 1) ^ rotrSH(W15h, W15l, 8) ^ shrSH(W15h, W15l, 7) const s0l = rotrSL(W15h, W15l, 1) ^ rotrSL(W15h, W15l, 8) ^ shrSL(W15h, W15l, 7) const W2h = SHA512_W_H[i - 2] | 0 const W2l = SHA512_W_L[i - 2] | 0 const s1h = rotrSH(W2h, W2l, 19) ^ rotrBH(W2h, W2l, 61) ^ shrSH(W2h, W2l, 6) const s1l = rotrSL(W2h, W2l, 19) ^ rotrBL(W2h, W2l, 61) ^ shrSL(W2h, W2l, 6) const SUMl = add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]) const SUMh = add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]) SHA512_W_H[i] = SUMh | 0 SHA512_W_L[i] = SUMl | 0 } let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this for (let i = 0; i < 80; i++) { const sigma1h = rotrSH(Eh, El, 14) ^ rotrSH(Eh, El, 18) ^ rotrBH(Eh, El, 41) const sigma1l = rotrSL(Eh, El, 14) ^ rotrSL(Eh, El, 18) ^ rotrBL(Eh, El, 41) const CHIh = (Eh & Fh) ^ (~Eh & Gh) const CHIl = (El & Fl) ^ (~El & Gl) const T1ll = add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]) const T1h = add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]) const T1l = T1ll | 0 const sigma0h = rotrSH(Ah, Al, 28) ^ rotrBH(Ah, Al, 34) ^ rotrBH(Ah, Al, 39) const sigma0l = rotrSL(Ah, Al, 28) ^ rotrBL(Ah, Al, 34) ^ rotrBL(Ah, Al, 39) const MAJh = (Ah & Bh) ^ (Ah & Ch) ^ (Bh & Ch) const MAJl = (Al & Bl) ^ (Al & Cl) ^ (Bl & Cl) Hh = Gh | 0 Hl = Gl | 0 Gh = Fh | 0 Gl = Fl | 0 Fh = Eh | 0 Fl = El | 0 ;({ h: Eh, l: El } = add(Dh | 0, Dl | 0, T1h | 0, T1l | 0)) Dh = Ch | 0 Dl = Cl | 0 Ch = Bh | 0 Cl = Bl | 0 Bh = Ah | 0 Bl = Al | 0 const T2l = add3L(sigma0l, MAJl, T1l) Ah = add3H(T2l, sigma0h, MAJh, T1h) Al = T2l | 0 } ;({ h: Ah, l: Al } = add(Ah, Al, this.Ah, this.Al)) ;({ h: Bh, l: Bl } = add(Bh, Bl, this.Bh, this.Bl)) ;({ h: Ch, l: Cl } = add(Ch, Cl, this.Ch, this.Cl)) ;({ h: Dh, l: Dl } = add(Dh, Dl, this.Dh, this.Dl)) ;({ h: Eh, l: El } = add(Eh, El, this.Eh, this.El)) ;({ h: Fh, l: Fl } = add(Fh, Fl, this.Fh, this.Fl)) ;({ h: Gh, l: Gl } = add(Gh, Gl, this.Gh, this.Gl)) ;({ h: Hh, l: Hl } = add(Hh, Hl, this.Hh, this.Hl)) this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl) } protected roundClean (): void { clean(SHA512_W_H, SHA512_W_L) } destroy (): void { clean(this.buffer) this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) } } const sha512Fast = createHasher(() => new FastSHA512()) class HMAC<T extends Hash<T>> extends Hash<HMAC<T>> { oHash: T iHash: T blockLen: number outputLen: number private finished = false private destroyed = false constructor (hash: (msg: Input) => Uint8Array & { create: () => T, blockLen: number, outputLen: number }, _key: Input) { super() ahash(hash) const key = toBytes(_key) this.iHash = hash.create() as T if (typeof (this.iHash as any).update !== 'function') { throw new Error('Expected instance of class which extends utils.Hash') } this.blockLen = this.iHash.blockLen this.outputLen = this.iHash.outputLen const blockLen = this.blockLen const pad = new Uint8Array(blockLen) pad.set(key.length > blockLen ? hash.create().update(key).digest() : key) for (let