| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533 |
- const {
- bytesToBase64,
- bytesToBin,
- bytesToHex,
- stringToUtf8Bytes,
- toByteArray
- } = require('../../utils/binary-utils.js')
- const MASK_64 = 0xFFFFFFFFFFFFFFFFn
- const HASH_ALGORITHM_PRESETS = [
- { key: 'md5', label: 'MD5', kind: 'hash', hash: 'md5' },
- { key: 'sha1', label: 'SHA1', kind: 'hash', hash: 'sha1' },
- { key: 'sha224', label: 'SHA224', kind: 'hash', hash: 'sha224' },
- { key: 'sha256', label: 'SHA256', kind: 'hash', hash: 'sha256' },
- { key: 'sha384', label: 'SHA384', kind: 'hash', hash: 'sha384' },
- { key: 'sha512', label: 'SHA512', kind: 'hash', hash: 'sha512' },
- { key: 'hmac-md5', label: 'HMAC-MD5', kind: 'hmac', hash: 'md5' },
- { key: 'hmac-sha1', label: 'HMAC-SHA1', kind: 'hmac', hash: 'sha1' },
- { key: 'hmac-sha224', label: 'HMAC-SHA224', kind: 'hmac', hash: 'sha224' },
- { key: 'hmac-sha256', label: 'HMAC-SHA256', kind: 'hmac', hash: 'sha256' },
- { key: 'hmac-sha384', label: 'HMAC-SHA384', kind: 'hmac', hash: 'sha384' },
- { key: 'hmac-sha512', label: 'HMAC-SHA512', kind: 'hmac', hash: 'sha512' },
- { key: 'pbkdf2', label: 'PBKDF2', kind: 'pbkdf2', hash: 'sha256' }
- ]
- const SHA256_K = [
- 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 SHA512_K = [
- 0x428A2F98D728AE22n, 0x7137449123EF65CDn, 0xB5C0FBCFEC4D3B2Fn, 0xE9B5DBA58189DBBCn,
- 0x3956C25BF348B538n, 0x59F111F1B605D019n, 0x923F82A4AF194F9Bn, 0xAB1C5ED5DA6D8118n,
- 0xD807AA98A3030242n, 0x12835B0145706FBEn, 0x243185BE4EE4B28Cn, 0x550C7DC3D5FFB4E2n,
- 0x72BE5D74F27B896Fn, 0x80DEB1FE3B1696B1n, 0x9BDC06A725C71235n, 0xC19BF174CF692694n,
- 0xE49B69C19EF14AD2n, 0xEFBE4786384F25E3n, 0x0FC19DC68B8CD5B5n, 0x240CA1CC77AC9C65n,
- 0x2DE92C6F592B0275n, 0x4A7484AA6EA6E483n, 0x5CB0A9DCBD41FBD4n, 0x76F988DA831153B5n,
- 0x983E5152EE66DFABn, 0xA831C66D2DB43210n, 0xB00327C898FB213Fn, 0xBF597FC7BEEF0EE4n,
- 0xC6E00BF33DA88FC2n, 0xD5A79147930AA725n, 0x06CA6351E003826Fn, 0x142929670A0E6E70n,
- 0x27B70A8546D22FFCn, 0x2E1B21385C26C926n, 0x4D2C6DFC5AC42AEDn, 0x53380D139D95B3DFn,
- 0x650A73548BAF63DEn, 0x766A0ABB3C77B2A8n, 0x81C2C92E47EDAEE6n, 0x92722C851482353Bn,
- 0xA2BFE8A14CF10364n, 0xA81A664BBC423001n, 0xC24B8B70D0F89791n, 0xC76C51A30654BE30n,
- 0xD192E819D6EF5218n, 0xD69906245565A910n, 0xF40E35855771202An, 0x106AA07032BBD1B8n,
- 0x19A4C116B8D2D0C8n, 0x1E376C085141AB53n, 0x2748774CDF8EEB99n, 0x34B0BCB5E19B48A8n,
- 0x391C0CB3C5C95A63n, 0x4ED8AA4AE3418ACBn, 0x5B9CCA4F7763E373n, 0x682E6FF3D6B2B8A3n,
- 0x748F82EE5DEFB2FCn, 0x78A5636F43172F60n, 0x84C87814A1F0AB72n, 0x8CC702081A6439ECn,
- 0x90BEFFFA23631E28n, 0xA4506CEBDE82BDE9n, 0xBEF9A3F7B2C67915n, 0xC67178F2E372532Bn,
- 0xCA273ECEEA26619Cn, 0xD186B8C721C0C207n, 0xEADA7DD6CDE0EB1En, 0xF57D4F7FEE6ED178n,
- 0x06F067AA72176FBAn, 0x0A637DC5A2C898A6n, 0x113F9804BEF90DAEn, 0x1B710B35131C471Bn,
- 0x28DB77F523047D84n, 0x32CAAB7B40C72493n, 0x3C9EBE0A15C9BEBCn, 0x431D67C49C100D4Cn,
- 0x4CC5D4BECB3E42B6n, 0x597F299CFC657E2An, 0x5FCB6FAB3AD6FAECn, 0x6C44198C4A475817n
- ]
- function add32(...values) {
- return values.reduce((sum, value) => (sum + (value >>> 0)) >>> 0, 0)
- }
- function rotl32(value, bits) {
- return ((value << bits) | (value >>> (32 - bits))) >>> 0
- }
- function rotr32(value, bits) {
- return ((value >>> bits) | (value << (32 - bits))) >>> 0
- }
- function writeWord32BE(value, output) {
- output.push((value >>> 24) & 0xFF, (value >>> 16) & 0xFF, (value >>> 8) & 0xFF, value & 0xFF)
- }
- function writeWord32LE(value, output) {
- output.push(value & 0xFF, (value >>> 8) & 0xFF, (value >>> 16) & 0xFF, (value >>> 24) & 0xFF)
- }
- function padBlock64BE(bytes) {
- const message = toByteArray(bytes)
- const bitLength = BigInt(message.length) * 8n
- const padded = message.slice()
- padded.push(0x80)
- while (padded.length % 64 !== 56) padded.push(0)
- for (let shift = 56; shift >= 0; shift -= 8) {
- padded.push(Number((bitLength >> BigInt(shift)) & 0xFFn))
- }
- return padded
- }
- function md5(bytes) {
- const shifts = [
- 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
- 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
- 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
- 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
- ]
- const constants = []
- const message = toByteArray(bytes)
- const bitLength = BigInt(message.length) * 8n
- const padded = message.slice()
- for (let index = 0; index < 64; index += 1) {
- constants[index] = Math.floor(Math.abs(Math.sin(index + 1)) * 0x100000000) >>> 0
- }
- padded.push(0x80)
- while (padded.length % 64 !== 56) padded.push(0)
- for (let shift = 0; shift < 64; shift += 8) {
- padded.push(Number((bitLength >> BigInt(shift)) & 0xFFn))
- }
- let a0 = 0x67452301
- let b0 = 0xEFCDAB89
- let c0 = 0x98BADCFE
- let d0 = 0x10325476
- for (let offset = 0; offset < padded.length; offset += 64) {
- const words = []
- for (let index = 0; index < 16; index += 1) {
- const base = offset + index * 4
- words[index] = (
- (padded[base] & 0xFF) |
- ((padded[base + 1] & 0xFF) << 8) |
- ((padded[base + 2] & 0xFF) << 16) |
- ((padded[base + 3] & 0xFF) << 24)
- ) >>> 0
- }
- let a = a0
- let b = b0
- let c = c0
- let d = d0
- for (let index = 0; index < 64; index += 1) {
- let f
- let g
- if (index < 16) {
- f = (b & c) | ((~b) & d)
- g = index
- } else if (index < 32) {
- f = (d & b) | ((~d) & c)
- g = (5 * index + 1) % 16
- } else if (index < 48) {
- f = b ^ c ^ d
- g = (3 * index + 5) % 16
- } else {
- f = c ^ (b | (~d))
- g = (7 * index) % 16
- }
- const next = d
- d = c
- c = b
- b = add32(b, rotl32(add32(a, f, constants[index], words[g]), shifts[index]))
- a = next
- }
- a0 = add32(a0, a)
- b0 = add32(b0, b)
- c0 = add32(c0, c)
- d0 = add32(d0, d)
- }
- const output = []
- ;[a0, b0, c0, d0].forEach((word) => writeWord32LE(word, output))
- return output
- }
- function sha1(bytes) {
- const padded = padBlock64BE(bytes)
- const words = []
- let h0 = 0x67452301
- let h1 = 0xEFCDAB89
- let h2 = 0x98BADCFE
- let h3 = 0x10325476
- let h4 = 0xC3D2E1F0
- for (let offset = 0; offset < padded.length; offset += 64) {
- for (let index = 0; index < 16; index += 1) {
- const base = offset + index * 4
- words[index] = (
- ((padded[base] & 0xFF) << 24) |
- ((padded[base + 1] & 0xFF) << 16) |
- ((padded[base + 2] & 0xFF) << 8) |
- (padded[base + 3] & 0xFF)
- ) >>> 0
- }
- for (let index = 16; index < 80; index += 1) {
- words[index] = rotl32(words[index - 3] ^ words[index - 8] ^ words[index - 14] ^ words[index - 16], 1)
- }
- let a = h0
- let b = h1
- let c = h2
- let d = h3
- let e = h4
- for (let index = 0; index < 80; index += 1) {
- let f
- let k
- if (index < 20) {
- f = (b & c) | ((~b) & d)
- k = 0x5A827999
- } else if (index < 40) {
- f = b ^ c ^ d
- k = 0x6ED9EBA1
- } else if (index < 60) {
- f = (b & c) | (b & d) | (c & d)
- k = 0x8F1BBCDC
- } else {
- f = b ^ c ^ d
- k = 0xCA62C1D6
- }
- const temp = add32(rotl32(a, 5), f, e, k, words[index])
- e = d
- d = c
- c = rotl32(b, 30)
- b = a
- a = temp
- }
- h0 = add32(h0, a)
- h1 = add32(h1, b)
- h2 = add32(h2, c)
- h3 = add32(h3, d)
- h4 = add32(h4, e)
- }
- const output = []
- ;[h0, h1, h2, h3, h4].forEach((word) => writeWord32BE(word, output))
- return output
- }
- function sha256Family(bytes, mode) {
- const padded = padBlock64BE(bytes)
- const words = []
- const hash = mode === 'sha224'
- ? [0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939, 0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4]
- : [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19]
- for (let offset = 0; offset < padded.length; offset += 64) {
- for (let index = 0; index < 16; index += 1) {
- const base = offset + index * 4
- words[index] = (
- ((padded[base] & 0xFF) << 24) |
- ((padded[base + 1] & 0xFF) << 16) |
- ((padded[base + 2] & 0xFF) << 8) |
- (padded[base + 3] & 0xFF)
- ) >>> 0
- }
- for (let index = 16; index < 64; index += 1) {
- const s0 = rotr32(words[index - 15], 7) ^ rotr32(words[index - 15], 18) ^ (words[index - 15] >>> 3)
- const s1 = rotr32(words[index - 2], 17) ^ rotr32(words[index - 2], 19) ^ (words[index - 2] >>> 10)
- words[index] = add32(words[index - 16], s0, words[index - 7], s1)
- }
- let a = hash[0]
- let b = hash[1]
- let c = hash[2]
- let d = hash[3]
- let e = hash[4]
- let f = hash[5]
- let g = hash[6]
- let h = hash[7]
- for (let index = 0; index < 64; index += 1) {
- const s1 = rotr32(e, 6) ^ rotr32(e, 11) ^ rotr32(e, 25)
- const ch = (e & f) ^ ((~e) & g)
- const temp1 = add32(h, s1, ch, SHA256_K[index], words[index])
- const s0 = rotr32(a, 2) ^ rotr32(a, 13) ^ rotr32(a, 22)
- const maj = (a & b) ^ (a & c) ^ (b & c)
- const temp2 = add32(s0, maj)
- h = g
- g = f
- f = e
- e = add32(d, temp1)
- d = c
- c = b
- b = a
- a = add32(temp1, temp2)
- }
- hash[0] = add32(hash[0], a)
- hash[1] = add32(hash[1], b)
- hash[2] = add32(hash[2], c)
- hash[3] = add32(hash[3], d)
- hash[4] = add32(hash[4], e)
- hash[5] = add32(hash[5], f)
- hash[6] = add32(hash[6], g)
- hash[7] = add32(hash[7], h)
- }
- const output = []
- hash.slice(0, mode === 'sha224' ? 7 : 8).forEach((word) => writeWord32BE(word, output))
- return output
- }
- function rotr64(value, bits) {
- const shift = BigInt(bits)
- return ((value >> shift) | (value << (64n - shift))) & MASK_64
- }
- function writeWord64BE(value, output) {
- for (let shift = 56; shift >= 0; shift -= 8) {
- output.push(Number((value >> BigInt(shift)) & 0xFFn))
- }
- }
- function readWord64BE(bytes, offset) {
- let value = 0n
- for (let index = 0; index < 8; index += 1) {
- value = (value << 8n) | BigInt(bytes[offset + index] & 0xFF)
- }
- return value
- }
- function padBlock128BE(bytes) {
- const message = toByteArray(bytes)
- const bitLength = BigInt(message.length) * 8n
- const padded = message.slice()
- padded.push(0x80)
- while (padded.length % 128 !== 112) padded.push(0)
- for (let shift = 120; shift >= 0; shift -= 8) {
- padded.push(Number((bitLength >> BigInt(shift)) & 0xFFn))
- }
- return padded
- }
- function sha512Family(bytes, mode) {
- const padded = padBlock128BE(bytes)
- const words = []
- const hash = mode === 'sha384'
- ? [
- 0xCBBB9D5DC1059ED8n, 0x629A292A367CD507n, 0x9159015A3070DD17n, 0x152FECD8F70E5939n,
- 0x67332667FFC00B31n, 0x8EB44A8768581511n, 0xDB0C2E0D64F98FA7n, 0x47B5481DBEFA4FA4n
- ]
- : [
- 0x6A09E667F3BCC908n, 0xBB67AE8584CAA73Bn, 0x3C6EF372FE94F82Bn, 0xA54FF53A5F1D36F1n,
- 0x510E527FADE682D1n, 0x9B05688C2B3E6C1Fn, 0x1F83D9ABFB41BD6Bn, 0x5BE0CD19137E2179n
- ]
- for (let offset = 0; offset < padded.length; offset += 128) {
- for (let index = 0; index < 16; index += 1) {
- words[index] = readWord64BE(padded, offset + index * 8)
- }
- for (let index = 16; index < 80; index += 1) {
- const s0 = rotr64(words[index - 15], 1) ^ rotr64(words[index - 15], 8) ^ (words[index - 15] >> 7n)
- const s1 = rotr64(words[index - 2], 19) ^ rotr64(words[index - 2], 61) ^ (words[index - 2] >> 6n)
- words[index] = (words[index - 16] + s0 + words[index - 7] + s1) & MASK_64
- }
- let a = hash[0]
- let b = hash[1]
- let c = hash[2]
- let d = hash[3]
- let e = hash[4]
- let f = hash[5]
- let g = hash[6]
- let h = hash[7]
- for (let index = 0; index < 80; index += 1) {
- const s1 = rotr64(e, 14) ^ rotr64(e, 18) ^ rotr64(e, 41)
- const ch = (e & f) ^ ((MASK_64 ^ e) & g)
- const temp1 = (h + s1 + ch + SHA512_K[index] + words[index]) & MASK_64
- const s0 = rotr64(a, 28) ^ rotr64(a, 34) ^ rotr64(a, 39)
- const maj = (a & b) ^ (a & c) ^ (b & c)
- const temp2 = (s0 + maj) & MASK_64
- h = g
- g = f
- f = e
- e = (d + temp1) & MASK_64
- d = c
- c = b
- b = a
- a = (temp1 + temp2) & MASK_64
- }
- hash[0] = (hash[0] + a) & MASK_64
- hash[1] = (hash[1] + b) & MASK_64
- hash[2] = (hash[2] + c) & MASK_64
- hash[3] = (hash[3] + d) & MASK_64
- hash[4] = (hash[4] + e) & MASK_64
- hash[5] = (hash[5] + f) & MASK_64
- hash[6] = (hash[6] + g) & MASK_64
- hash[7] = (hash[7] + h) & MASK_64
- }
- const output = []
- hash.slice(0, mode === 'sha384' ? 6 : 8).forEach((word) => writeWord64BE(word, output))
- return output
- }
- function digestBytes(hash, bytes) {
- if (hash === 'md5') return md5(bytes)
- if (hash === 'sha1') return sha1(bytes)
- if (hash === 'sha224') return sha256Family(bytes, 'sha224')
- if (hash === 'sha256') return sha256Family(bytes, 'sha256')
- if (hash === 'sha384') return sha512Family(bytes, 'sha384')
- if (hash === 'sha512') return sha512Family(bytes, 'sha512')
- throw new Error('不支持的哈希算法')
- }
- function getBlockSize(hash) {
- return hash === 'sha384' || hash === 'sha512' ? 128 : 64
- }
- function hmacBytes(hash, keyBytes, dataBytes) {
- const blockSize = getBlockSize(hash)
- let key = toByteArray(keyBytes)
- if (key.length > blockSize) {
- key = digestBytes(hash, key)
- }
- while (key.length < blockSize) key.push(0)
- const innerPad = []
- const outerPad = []
- for (let index = 0; index < blockSize; index += 1) {
- innerPad[index] = key[index] ^ 0x36
- outerPad[index] = key[index] ^ 0x5C
- }
- return digestBytes(hash, outerPad.concat(digestBytes(hash, innerPad.concat(toByteArray(dataBytes)))))
- }
- function parsePositiveInteger(value, label, fallback, maxValue) {
- const numberValue = Number(value || fallback)
- if (!Number.isInteger(numberValue) || numberValue <= 0) {
- throw new Error(`${label}需为正整数`)
- }
- if (numberValue > maxValue) {
- throw new Error(`${label}不能超过 ${maxValue}`)
- }
- return numberValue
- }
- function pbkdf2Bytes(hash, passwordBytes, saltBytes, iterations, outputLength) {
- const rounds = parsePositiveInteger(iterations, '迭代次数', 1000, 100000)
- const length = parsePositiveInteger(outputLength, '输出长度', 32, 4096)
- const hashLength = digestBytes(hash, []).length
- const blockCount = Math.ceil(length / hashLength)
- const output = []
- for (let blockIndex = 1; blockIndex <= blockCount; blockIndex += 1) {
- const blockIndexBytes = [
- (blockIndex >>> 24) & 0xFF,
- (blockIndex >>> 16) & 0xFF,
- (blockIndex >>> 8) & 0xFF,
- blockIndex & 0xFF
- ]
- let previous = hmacBytes(hash, passwordBytes, toByteArray(saltBytes).concat(blockIndexBytes))
- const block = previous.slice()
- for (let round = 1; round < rounds; round += 1) {
- previous = hmacBytes(hash, passwordBytes, previous)
- for (let index = 0; index < block.length; index += 1) {
- block[index] ^= previous[index]
- }
- }
- output.push(...block)
- }
- return output.slice(0, length)
- }
- function getPreset(key) {
- return HASH_ALGORITHM_PRESETS.find((preset) => preset.key === key) || HASH_ALGORITHM_PRESETS[0]
- }
- function calculateHash(bytes, config = {}) {
- const preset = getPreset(config.key)
- const dataBytes = toByteArray(bytes)
- let resultBytes
- if (preset.kind === 'hmac') {
- resultBytes = hmacBytes(preset.hash, stringToUtf8Bytes(config.hmacKey || ''), dataBytes)
- } else if (preset.kind === 'pbkdf2') {
- resultBytes = pbkdf2Bytes(
- preset.hash,
- dataBytes,
- stringToUtf8Bytes(config.pbkdf2Salt || ''),
- config.pbkdf2Iterations,
- config.pbkdf2Length
- )
- } else {
- resultBytes = digestBytes(preset.hash, dataBytes)
- }
- return {
- base64: bytesToBase64(resultBytes),
- bin: bytesToBin(resultBytes),
- bytes: resultBytes,
- hex: bytesToHex(resultBytes),
- width: resultBytes.length * 8
- }
- }
- module.exports = {
- HASH_ALGORITHM_PRESETS,
- bytesToBase64,
- bytesToBin,
- bytesToHex,
- calculateHash,
- digestBytes,
- hmacBytes,
- pbkdf2Bytes,
- stringToUtf8Bytes,
- toByteArray
- }
|