summaryrefslogtreecommitdiff
path: root/lib/internal/crypto/hash.js
blob: d172a6a3c8e90e10cb496f48663f0e768be76cab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
'use strict';

const {
  Hash: _Hash,
  Hmac: _Hmac
} = process.binding('crypto');

const {
  getDefaultEncoding,
  toBuf
} = require('internal/crypto/util');

const { Buffer } = require('buffer');

const {
  ERR_CRYPTO_HASH_DIGEST_NO_UTF16,
  ERR_CRYPTO_HASH_FINALIZED,
  ERR_CRYPTO_HASH_UPDATE_FAILED,
  ERR_INVALID_ARG_TYPE
} = require('internal/errors').codes;
const { inherits } = require('util');
const { normalizeEncoding } = require('internal/util');
const { isArrayBufferView } = require('internal/util/types');
const LazyTransform = require('internal/streams/lazy_transform');
const kState = Symbol('state');
const kFinalized = Symbol('finalized');

function Hash(algorithm, options) {
  if (!(this instanceof Hash))
    return new Hash(algorithm, options);
  if (typeof algorithm !== 'string')
    throw new ERR_INVALID_ARG_TYPE('algorithm', 'string', algorithm);
  this._handle = new _Hash(algorithm);
  this[kState] = {
    [kFinalized]: false
  };
  LazyTransform.call(this, options);
}

inherits(Hash, LazyTransform);

Hash.prototype._transform = function _transform(chunk, encoding, callback) {
  this._handle.update(chunk, encoding);
  callback();
};

Hash.prototype._flush = function _flush(callback) {
  this.push(this._handle.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' && !isArrayBufferView(data)) {
    throw new ERR_INVALID_ARG_TYPE('data',
                                   ['string', 'TypedArray', 'DataView'], data);
  }

  if (!this._handle.update(data, encoding || getDefaultEncoding()))
    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();
  outputEncoding = outputEncoding || getDefaultEncoding();
  if (normalizeEncoding(outputEncoding) === 'utf16le')
    throw new ERR_CRYPTO_HASH_DIGEST_NO_UTF16();

  // Explicit conversion for backward compatibility.
  const ret = this._handle.digest(`${outputEncoding}`);
  state[kFinalized] = true;
  return ret;
};


function Hmac(hmac, key, options) {
  if (!(this instanceof Hmac))
    return new Hmac(hmac, key, options);
  if (typeof hmac !== 'string')
    throw new ERR_INVALID_ARG_TYPE('hmac', 'string', hmac);
  if (typeof key !== 'string' && !isArrayBufferView(key)) {
    throw new ERR_INVALID_ARG_TYPE('key',
                                   ['string', 'TypedArray', 'DataView'], key);
  }
  this._handle = new _Hmac();
  this._handle.init(hmac, toBuf(key));
  this[kState] = {
    [kFinalized]: false
  };
  LazyTransform.call(this, options);
}

inherits(Hmac, LazyTransform);

Hmac.prototype.update = Hash.prototype.update;

Hmac.prototype.digest = function digest(outputEncoding) {
  const state = this[kState];
  outputEncoding = outputEncoding || getDefaultEncoding();
  if (normalizeEncoding(outputEncoding) === 'utf16le')
    throw new ERR_CRYPTO_HASH_DIGEST_NO_UTF16();

  if (state[kFinalized]) {
    const buf = Buffer.from('');
    return outputEncoding === 'buffer' ? buf : buf.toString(outputEncoding);
  }

  // Explicit conversion for backward compatibility.
  const ret = this._handle.digest(`${outputEncoding}`);
  state[kFinalized] = true;
  return ret;
};

Hmac.prototype._flush = Hash.prototype._flush;
Hmac.prototype._transform = Hash.prototype._transform;

module.exports = {
  Hash,
  Hmac
};