• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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