1'use strict'; 2 3const { 4 ObjectSetPrototypeOf, 5 ReflectApply, 6 Symbol, 7} = primordials; 8 9const { 10 Hash: _Hash, 11 HashJob, 12 Hmac: _Hmac, 13 kCryptoJobAsync, 14} = internalBinding('crypto'); 15 16const { 17 getDefaultEncoding, 18 getStringOption, 19 jobPromise, 20 normalizeHashName, 21 validateMaxBufferLength, 22 kHandle, 23} = require('internal/crypto/util'); 24 25const { 26 prepareSecretKey, 27} = require('internal/crypto/keys'); 28 29const { 30 lazyDOMException, 31} = require('internal/util'); 32 33const { 34 Buffer, 35} = require('buffer'); 36 37const { 38 codes: { 39 ERR_CRYPTO_HASH_FINALIZED, 40 ERR_CRYPTO_HASH_UPDATE_FAILED, 41 ERR_INVALID_ARG_TYPE, 42 }, 43} = require('internal/errors'); 44 45const { 46 validateEncoding, 47 validateString, 48 validateUint32, 49} = require('internal/validators'); 50 51const { 52 isArrayBufferView, 53} = require('internal/util/types'); 54 55const LazyTransform = require('internal/streams/lazy_transform'); 56 57const kState = Symbol('kState'); 58const kFinalized = Symbol('kFinalized'); 59 60function Hash(algorithm, options) { 61 if (!(this instanceof Hash)) 62 return new Hash(algorithm, options); 63 if (!(algorithm instanceof _Hash)) 64 validateString(algorithm, 'algorithm'); 65 const xofLen = typeof options === 'object' && options !== null ? 66 options.outputLength : undefined; 67 if (xofLen !== undefined) 68 validateUint32(xofLen, 'options.outputLength'); 69 this[kHandle] = new _Hash(algorithm, xofLen); 70 this[kState] = { 71 [kFinalized]: false, 72 }; 73 ReflectApply(LazyTransform, this, [options]); 74} 75 76ObjectSetPrototypeOf(Hash.prototype, LazyTransform.prototype); 77ObjectSetPrototypeOf(Hash, LazyTransform); 78 79Hash.prototype.copy = function copy(options) { 80 const state = this[kState]; 81 if (state[kFinalized]) 82 throw new ERR_CRYPTO_HASH_FINALIZED(); 83 84 return new Hash(this[kHandle], options); 85}; 86 87Hash.prototype._transform = function _transform(chunk, encoding, callback) { 88 this[kHandle].update(chunk, encoding); 89 callback(); 90}; 91 92Hash.prototype._flush = function _flush(callback) { 93 this.push(this[kHandle].digest()); 94 callback(); 95}; 96 97Hash.prototype.update = function update(data, encoding) { 98 encoding = encoding || getDefaultEncoding(); 99 100 const state = this[kState]; 101 if (state[kFinalized]) 102 throw new ERR_CRYPTO_HASH_FINALIZED(); 103 104 if (typeof data === 'string') { 105 validateEncoding(data, encoding); 106 } else if (!isArrayBufferView(data)) { 107 throw new ERR_INVALID_ARG_TYPE( 108 'data', ['string', 'Buffer', 'TypedArray', 'DataView'], data); 109 } 110 111 if (!this[kHandle].update(data, encoding)) 112 throw new ERR_CRYPTO_HASH_UPDATE_FAILED(); 113 return this; 114}; 115 116 117Hash.prototype.digest = function digest(outputEncoding) { 118 const state = this[kState]; 119 if (state[kFinalized]) 120 throw new ERR_CRYPTO_HASH_FINALIZED(); 121 outputEncoding = outputEncoding || getDefaultEncoding(); 122 123 // Explicit conversion for backward compatibility. 124 const ret = this[kHandle].digest(`${outputEncoding}`); 125 state[kFinalized] = true; 126 return ret; 127}; 128 129function Hmac(hmac, key, options) { 130 if (!(this instanceof Hmac)) 131 return new Hmac(hmac, key, options); 132 validateString(hmac, 'hmac'); 133 const encoding = getStringOption(options, 'encoding'); 134 key = prepareSecretKey(key, encoding); 135 this[kHandle] = new _Hmac(); 136 this[kHandle].init(hmac, key); 137 this[kState] = { 138 [kFinalized]: false, 139 }; 140 ReflectApply(LazyTransform, this, [options]); 141} 142 143ObjectSetPrototypeOf(Hmac.prototype, LazyTransform.prototype); 144ObjectSetPrototypeOf(Hmac, LazyTransform); 145 146Hmac.prototype.update = Hash.prototype.update; 147 148Hmac.prototype.digest = function digest(outputEncoding) { 149 const state = this[kState]; 150 outputEncoding = outputEncoding || getDefaultEncoding(); 151 152 if (state[kFinalized]) { 153 const buf = Buffer.from(''); 154 return outputEncoding === 'buffer' ? buf : buf.toString(outputEncoding); 155 } 156 157 // Explicit conversion for backward compatibility. 158 const ret = this[kHandle].digest(`${outputEncoding}`); 159 state[kFinalized] = true; 160 return ret; 161}; 162 163Hmac.prototype._flush = Hash.prototype._flush; 164Hmac.prototype._transform = Hash.prototype._transform; 165 166// Implementation for WebCrypto subtle.digest() 167 168async function asyncDigest(algorithm, data) { 169 validateMaxBufferLength(data, 'data'); 170 171 switch (algorithm.name) { 172 case 'SHA-1': 173 // Fall through 174 case 'SHA-256': 175 // Fall through 176 case 'SHA-384': 177 // Fall through 178 case 'SHA-512': 179 return jobPromise(() => new HashJob( 180 kCryptoJobAsync, 181 normalizeHashName(algorithm.name), 182 data)); 183 } 184 185 throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError'); 186} 187 188module.exports = { 189 Hash, 190 Hmac, 191 asyncDigest, 192}; 193