UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

210 lines (182 loc) 6.04 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/crypto/hash.js import { Hash as _Hash, HashJob, Hmac as _Hmac, kCryptoJobAsync, oneShotDigest, } from "nstdlib/stub/binding/crypto"; import { getStringOption, jobPromise, normalizeHashName, validateMaxBufferLength, kHandle, getCachedHashId, getHashCache, } from "nstdlib/lib/internal/crypto/util"; import { prepareSecretKey } from "nstdlib/lib/internal/crypto/keys"; import { lazyDOMException, normalizeEncoding, encodingsMap, } from "nstdlib/lib/internal/util"; import { Buffer } from "nstdlib/lib/buffer"; import { codes as __codes__ } from "nstdlib/lib/internal/errors"; import { validateEncoding, validateString, validateUint32, } from "nstdlib/lib/internal/validators"; import { isArrayBufferView } from "nstdlib/lib/internal/util/types"; import * as LazyTransform from "nstdlib/lib/internal/streams/lazy_transform"; const { ERR_CRYPTO_HASH_FINALIZED, ERR_CRYPTO_HASH_UPDATE_FAILED, ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, } = __codes__; const kState = Symbol("kState"); const kFinalized = Symbol("kFinalized"); function Hash(algorithm, options) { if (!new.target) return new Hash(algorithm, options); const isCopy = algorithm instanceof _Hash; if (!isCopy) validateString(algorithm, "algorithm"); const xofLen = typeof options === "object" && options !== null ? options.outputLength : undefined; if (xofLen !== undefined) validateUint32(xofLen, "options.outputLength"); // Lookup the cached ID from JS land because it's faster than decoding // the string in C++ land. const algorithmId = isCopy ? -1 : getCachedHashId(algorithm); this[kHandle] = new _Hash(algorithm, xofLen, algorithmId, getHashCache()); this[kState] = { [kFinalized]: false, }; ReflectApply(LazyTransform, this, [options]); } Object.setPrototypeOf(Hash.prototype, LazyTransform.prototype); Object.setPrototypeOf(Hash, LazyTransform); Hash.prototype.copy = function copy(options) { const state = this[kState]; if (state[kFinalized]) throw new ERR_CRYPTO_HASH_FINALIZED(); return new Hash(this[kHandle], options); }; Hash.prototype._transform = function _transform(chunk, encoding, callback) { this[kHandle].update(chunk, encoding); callback(); }; Hash.prototype._flush = function _flush(callback) { this.push(this[kHandle].digest()); callback(); }; Hash.prototype.update = function update(data, encoding) { const state = this[kState]; if (state[kFinalized]) throw new ERR_CRYPTO_HASH_FINALIZED(); if (typeof data === "string") { validateEncoding(data, encoding); } else if (!isArrayBufferView(data)) { throw new ERR_INVALID_ARG_TYPE( "data", ["string", "Buffer", "TypedArray", "DataView"], data, ); } if (!this[kHandle].update(data, encoding)) throw new ERR_CRYPTO_HASH_UPDATE_FAILED(); return this; }; Hash.prototype.digest = function digest(outputEncoding) { const state = this[kState]; if (state[kFinalized]) throw new ERR_CRYPTO_HASH_FINALIZED(); // Explicit conversion of truthy values for backward compatibility. const ret = this[kHandle].digest(outputEncoding && `${outputEncoding}`); state[kFinalized] = true; return ret; }; function Hmac(hmac, key, options) { if (!(this instanceof Hmac)) return new Hmac(hmac, key, options); validateString(hmac, "hmac"); const encoding = getStringOption(options, "encoding"); key = prepareSecretKey(key, encoding); this[kHandle] = new _Hmac(); this[kHandle].init(hmac, key); this[kState] = { [kFinalized]: false, }; ReflectApply(LazyTransform, this, [options]); } Object.setPrototypeOf(Hmac.prototype, LazyTransform.prototype); Object.setPrototypeOf(Hmac, LazyTransform); Hmac.prototype.update = Hash.prototype.update; Hmac.prototype.digest = function digest(outputEncoding) { const state = this[kState]; if (state[kFinalized]) { const buf = Buffer.from(""); if (outputEncoding && outputEncoding !== "buffer") return buf.toString(outputEncoding); return buf; } // Explicit conversion of truthy values for backward compatibility. const ret = this[kHandle].digest(outputEncoding && `${outputEncoding}`); state[kFinalized] = true; return ret; }; Hmac.prototype._flush = Hash.prototype._flush; Hmac.prototype._transform = Hash.prototype._transform; // Implementation for WebCrypto subtle.digest() async function asyncDigest(algorithm, data) { validateMaxBufferLength(data, "data"); switch (algorithm.name) { case "SHA-1": // Fall through case "SHA-256": // Fall through case "SHA-384": // Fall through case "SHA-512": return jobPromise( () => new HashJob(kCryptoJobAsync, normalizeHashName(algorithm.name), data), ); } throw lazyDOMException("Unrecognized algorithm name", "NotSupportedError"); } function hash(algorithm, input, outputEncoding = "hex") { validateString(algorithm, "algorithm"); if (typeof input !== "string" && !isArrayBufferView(input)) { throw new ERR_INVALID_ARG_TYPE( "input", ["Buffer", "TypedArray", "DataView", "string"], input, ); } let normalized = outputEncoding; // Fast case: if it's 'hex', we don't need to validate it further. if (outputEncoding !== "hex") { validateString(outputEncoding, "outputEncoding"); normalized = normalizeEncoding(outputEncoding); // If the encoding is invalid, normalizeEncoding() returns undefined. if (normalized === undefined) { // normalizeEncoding() doesn't handle 'buffer'. if (String.prototype.toLowerCase.call(outputEncoding) === "buffer") { normalized = "buffer"; } else { throw new ERR_INVALID_ARG_VALUE("outputEncoding", outputEncoding); } } } return oneShotDigest( algorithm, getCachedHashId(algorithm), getHashCache(), input, normalized, encodingsMap[normalized], ); } export { Hash }; export { Hmac }; export { asyncDigest }; export { hash };