UNPKG

sha256-uint8array

Version:

Fast SHA-256 digest hash based on Uint8Array, pure JavaScript.

246 lines (245 loc) 9.53 kB
/** * sha256-uint8array.ts */ // first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311 const K = [ 0x428a2f98 | 0, 0x71374491 | 0, 0xb5c0fbcf | 0, 0xe9b5dba5 | 0, 0x3956c25b | 0, 0x59f111f1 | 0, 0x923f82a4 | 0, 0xab1c5ed5 | 0, 0xd807aa98 | 0, 0x12835b01 | 0, 0x243185be | 0, 0x550c7dc3 | 0, 0x72be5d74 | 0, 0x80deb1fe | 0, 0x9bdc06a7 | 0, 0xc19bf174 | 0, 0xe49b69c1 | 0, 0xefbe4786 | 0, 0x0fc19dc6 | 0, 0x240ca1cc | 0, 0x2de92c6f | 0, 0x4a7484aa | 0, 0x5cb0a9dc | 0, 0x76f988da | 0, 0x983e5152 | 0, 0xa831c66d | 0, 0xb00327c8 | 0, 0xbf597fc7 | 0, 0xc6e00bf3 | 0, 0xd5a79147 | 0, 0x06ca6351 | 0, 0x14292967 | 0, 0x27b70a85 | 0, 0x2e1b2138 | 0, 0x4d2c6dfc | 0, 0x53380d13 | 0, 0x650a7354 | 0, 0x766a0abb | 0, 0x81c2c92e | 0, 0x92722c85 | 0, 0xa2bfe8a1 | 0, 0xa81a664b | 0, 0xc24b8b70 | 0, 0xc76c51a3 | 0, 0xd192e819 | 0, 0xd6990624 | 0, 0xf40e3585 | 0, 0x106aa070 | 0, 0x19a4c116 | 0, 0x1e376c08 | 0, 0x2748774c | 0, 0x34b0bcb5 | 0, 0x391c0cb3 | 0, 0x4ed8aa4a | 0, 0x5b9cca4f | 0, 0x682e6ff3 | 0, 0x748f82ee | 0, 0x78a5636f | 0, 0x84c87814 | 0, 0x8cc70208 | 0, 0x90befffa | 0, 0xa4506ceb | 0, 0xbef9a3f7 | 0, 0xc67178f2 | 0, ]; const algorithms = { sha256: 1, }; export function createHash(algorithm) { if (algorithm && !algorithms[algorithm] && !algorithms[algorithm.toLowerCase()]) { throw new Error("Digest method not supported"); } return new Hash(); } export class Hash { constructor() { // first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19 this.A = 0x6a09e667 | 0; this.B = 0xbb67ae85 | 0; this.C = 0x3c6ef372 | 0; this.D = 0xa54ff53a | 0; this.E = 0x510e527f | 0; this.F = 0x9b05688c | 0; this.G = 0x1f83d9ab | 0; this.H = 0x5be0cd19 | 0; this._size = 0; this._sp = 0; // surrogate pair if (!sharedBuffer || sharedOffset >= 8000 /* N.allocTotal */) { sharedBuffer = new ArrayBuffer(8000 /* N.allocTotal */); sharedOffset = 0; } this._byte = new Uint8Array(sharedBuffer, sharedOffset, 80 /* N.allocBytes */); this._word = new Int32Array(sharedBuffer, sharedOffset, 20 /* N.allocWords */); sharedOffset += 80 /* N.allocBytes */; } update(data) { // data: string if ("string" === typeof data) { return this._utf8(data); } // data: undefined if (data == null) { throw new TypeError("Invalid type: " + typeof data); } const byteOffset = data.byteOffset; const length = data.byteLength; let blocks = (length / 64 /* N.inputBytes */) | 0; let offset = 0; // longer than 1 block if (blocks && !(byteOffset & 3) && !(this._size % 64 /* N.inputBytes */)) { const block = new Int32Array(data.buffer, byteOffset, blocks * 16 /* N.inputWords */); while (blocks--) { this._int32(block, offset >> 2); offset += 64 /* N.inputBytes */; } this._size += offset; } // data: TypedArray | DataView const BYTES_PER_ELEMENT = data.BYTES_PER_ELEMENT; if (BYTES_PER_ELEMENT !== 1 && data.buffer) { const rest = new Uint8Array(data.buffer, byteOffset + offset, length - offset); return this._uint8(rest); } // no more bytes if (offset === length) return this; // data: Uint8Array | Int8Array return this._uint8(data, offset); } _uint8(data, offset) { const { _byte, _word } = this; const length = data.length; offset = offset | 0; while (offset < length) { const start = this._size % 64 /* N.inputBytes */; let index = start; while (offset < length && index < 64 /* N.inputBytes */) { _byte[index++] = data[offset++]; } if (index >= 64 /* N.inputBytes */) { this._int32(_word); } this._size += index - start; } return this; } _utf8(text) { const { _byte, _word } = this; const length = text.length; let surrogate = this._sp; for (let offset = 0; offset < length;) { const start = this._size % 64 /* N.inputBytes */; let index = start; while (offset < length && index < 64 /* N.inputBytes */) { let code = text.charCodeAt(offset++) | 0; if (code < 0x80) { // ASCII characters _byte[index++] = code; } else if (code < 0x800) { // 2 bytes _byte[index++] = 0xC0 | (code >>> 6); _byte[index++] = 0x80 | (code & 0x3F); } else if (code < 0xD800 || code > 0xDFFF) { // 3 bytes _byte[index++] = 0xE0 | (code >>> 12); _byte[index++] = 0x80 | ((code >>> 6) & 0x3F); _byte[index++] = 0x80 | (code & 0x3F); } else if (surrogate) { // 4 bytes - surrogate pair code = ((surrogate & 0x3FF) << 10) + (code & 0x3FF) + 0x10000; _byte[index++] = 0xF0 | (code >>> 18); _byte[index++] = 0x80 | ((code >>> 12) & 0x3F); _byte[index++] = 0x80 | ((code >>> 6) & 0x3F); _byte[index++] = 0x80 | (code & 0x3F); surrogate = 0; } else { surrogate = code; } } if (index >= 64 /* N.inputBytes */) { this._int32(_word); _word[0] = _word[16 /* N.inputWords */]; } this._size += index - start; } this._sp = surrogate; return this; } _int32(data, offset) { let { A, B, C, D, E, F, G, H } = this; let i = 0; offset = offset | 0; while (i < 16 /* N.inputWords */) { W[i++] = swap32(data[offset++]); } for (i = 16 /* N.inputWords */; i < 64 /* N.workWords */; i++) { W[i] = (gamma1(W[i - 2]) + W[i - 7] + gamma0(W[i - 15]) + W[i - 16]) | 0; } for (i = 0; i < 64 /* N.workWords */; i++) { const T1 = (H + sigma1(E) + ch(E, F, G) + K[i] + W[i]) | 0; const T2 = (sigma0(A) + maj(A, B, C)) | 0; H = G; G = F; F = E; E = (D + T1) | 0; D = C; C = B; B = A; A = (T1 + T2) | 0; } this.A = (A + this.A) | 0; this.B = (B + this.B) | 0; this.C = (C + this.C) | 0; this.D = (D + this.D) | 0; this.E = (E + this.E) | 0; this.F = (F + this.F) | 0; this.G = (G + this.G) | 0; this.H = (H + this.H) | 0; } digest(encoding) { const { _byte, _word } = this; let i = (this._size % 64 /* N.inputBytes */) | 0; _byte[i++] = 0x80; // pad 0 for current word while (i & 3) { _byte[i++] = 0; } i >>= 2; if (i > 14 /* N.highIndex */) { while (i < 16 /* N.inputWords */) { _word[i++] = 0; } i = 0; this._int32(_word); } // pad 0 for rest words while (i < 16 /* N.inputWords */) { _word[i++] = 0; } // input size const bits64 = this._size * 8; const low32 = (bits64 & 0xffffffff) >>> 0; const high32 = (bits64 - low32) / 0x100000000; if (high32) _word[14 /* N.highIndex */] = swap32(high32); if (low32) _word[15 /* N.lowIndex */] = swap32(low32); this._int32(_word); return (encoding === "hex") ? this._hex() : this._bin(); } _hex() { const { A, B, C, D, E, F, G, H } = this; return hex32(A) + hex32(B) + hex32(C) + hex32(D) + hex32(E) + hex32(F) + hex32(G) + hex32(H); } _bin() { const { A, B, C, D, E, F, G, H, _byte, _word } = this; _word[0] = swap32(A); _word[1] = swap32(B); _word[2] = swap32(C); _word[3] = swap32(D); _word[4] = swap32(E); _word[5] = swap32(F); _word[6] = swap32(G); _word[7] = swap32(H); return _byte.slice(0, 32); } } const W = new Int32Array(64 /* N.workWords */); let sharedBuffer; let sharedOffset = 0; const hex32 = num => (num + 0x100000000).toString(16).substr(-8); const swapLE = (c => (((c << 24) & 0xff000000) | ((c << 8) & 0xff0000) | ((c >> 8) & 0xff00) | ((c >> 24) & 0xff))); const swapBE = (c => c); const swap32 = isBE() ? swapBE : swapLE; const ch = (x, y, z) => (z ^ (x & (y ^ z))); const maj = (x, y, z) => ((x & y) | (z & (x | y))); const sigma0 = x => ((x >>> 2 | x << 30) ^ (x >>> 13 | x << 19) ^ (x >>> 22 | x << 10)); const sigma1 = x => ((x >>> 6 | x << 26) ^ (x >>> 11 | x << 21) ^ (x >>> 25 | x << 7)); const gamma0 = x => ((x >>> 7 | x << 25) ^ (x >>> 18 | x << 14) ^ (x >>> 3)); const gamma1 = x => ((x >>> 17 | x << 15) ^ (x >>> 19 | x << 13) ^ (x >>> 10)); function isBE() { const buf = new Uint8Array(new Uint16Array([0xFEFF]).buffer); // BOM return (buf[0] === 0xFE); }