xref: /third_party/node/lib/internal/crypto/sig.js (revision 1cb0ef41)
1'use strict';
2
3const {
4  FunctionPrototypeCall,
5  ObjectSetPrototypeOf,
6  ReflectApply,
7} = primordials;
8
9const {
10  codes: {
11    ERR_CRYPTO_SIGN_KEY_REQUIRED,
12    ERR_INVALID_ARG_TYPE,
13    ERR_INVALID_ARG_VALUE,
14  },
15} = require('internal/errors');
16
17const {
18  validateFunction,
19  validateEncoding,
20  validateString,
21} = require('internal/validators');
22
23const {
24  Sign: _Sign,
25  SignJob,
26  Verify: _Verify,
27  kCryptoJobAsync,
28  kCryptoJobSync,
29  kSigEncDER,
30  kSigEncP1363,
31  kSignJobModeSign,
32  kSignJobModeVerify,
33} = internalBinding('crypto');
34
35const {
36  getArrayBufferOrView,
37  getDefaultEncoding,
38  kHandle,
39} = require('internal/crypto/util');
40
41const {
42  preparePrivateKey,
43  preparePublicOrPrivateKey,
44} = require('internal/crypto/keys');
45
46const { Writable } = require('stream');
47
48const { Buffer } = require('buffer');
49
50const {
51  isArrayBufferView,
52} = require('internal/util/types');
53
54function Sign(algorithm, options) {
55  if (!(this instanceof Sign))
56    return new Sign(algorithm, options);
57  validateString(algorithm, 'algorithm');
58  this[kHandle] = new _Sign();
59  this[kHandle].init(algorithm);
60
61  ReflectApply(Writable, this, [options]);
62}
63
64ObjectSetPrototypeOf(Sign.prototype, Writable.prototype);
65ObjectSetPrototypeOf(Sign, Writable);
66
67Sign.prototype._write = function _write(chunk, encoding, callback) {
68  this.update(chunk, encoding);
69  callback();
70};
71
72Sign.prototype.update = function update(data, encoding) {
73  encoding = encoding || getDefaultEncoding();
74
75  if (typeof data === 'string') {
76    validateEncoding(data, encoding);
77  } else if (!isArrayBufferView(data)) {
78    throw new ERR_INVALID_ARG_TYPE(
79      'data', ['string', 'Buffer', 'TypedArray', 'DataView'], data);
80  }
81
82  this[kHandle].update(data, encoding);
83  return this;
84};
85
86function getPadding(options) {
87  return getIntOption('padding', options);
88}
89
90function getSaltLength(options) {
91  return getIntOption('saltLength', options);
92}
93
94function getDSASignatureEncoding(options) {
95  if (typeof options === 'object') {
96    const { dsaEncoding = 'der' } = options;
97    if (dsaEncoding === 'der')
98      return kSigEncDER;
99    else if (dsaEncoding === 'ieee-p1363')
100      return kSigEncP1363;
101    throw new ERR_INVALID_ARG_VALUE('options.dsaEncoding', dsaEncoding);
102  }
103
104  return kSigEncDER;
105}
106
107function getIntOption(name, options) {
108  const value = options[name];
109  if (value !== undefined) {
110    if (value === value >> 0) {
111      return value;
112    }
113    throw new ERR_INVALID_ARG_VALUE(`options.${name}`, value);
114  }
115  return undefined;
116}
117
118Sign.prototype.sign = function sign(options, encoding) {
119  if (!options)
120    throw new ERR_CRYPTO_SIGN_KEY_REQUIRED();
121
122  const { data, format, type, passphrase } = preparePrivateKey(options, true);
123
124  // Options specific to RSA
125  const rsaPadding = getPadding(options);
126  const pssSaltLength = getSaltLength(options);
127
128  // Options specific to (EC)DSA
129  const dsaSigEnc = getDSASignatureEncoding(options);
130
131  const ret = this[kHandle].sign(data, format, type, passphrase, rsaPadding,
132                                 pssSaltLength, dsaSigEnc);
133
134  encoding = encoding || getDefaultEncoding();
135  if (encoding && encoding !== 'buffer')
136    return ret.toString(encoding);
137
138  return ret;
139};
140
141function signOneShot(algorithm, data, key, callback) {
142  if (algorithm != null)
143    validateString(algorithm, 'algorithm');
144
145  if (callback !== undefined)
146    validateFunction(callback, 'callback');
147
148  data = getArrayBufferOrView(data, 'data');
149
150  if (!key)
151    throw new ERR_CRYPTO_SIGN_KEY_REQUIRED();
152
153  // Options specific to RSA
154  const rsaPadding = getPadding(key);
155  const pssSaltLength = getSaltLength(key);
156
157  // Options specific to (EC)DSA
158  const dsaSigEnc = getDSASignatureEncoding(key);
159
160  const {
161    data: keyData,
162    format: keyFormat,
163    type: keyType,
164    passphrase: keyPassphrase,
165  } = preparePrivateKey(key);
166
167  const job = new SignJob(
168    callback ? kCryptoJobAsync : kCryptoJobSync,
169    kSignJobModeSign,
170    keyData,
171    keyFormat,
172    keyType,
173    keyPassphrase,
174    data,
175    algorithm,
176    pssSaltLength,
177    rsaPadding,
178    dsaSigEnc);
179
180  if (!callback) {
181    const { 0: err, 1: signature } = job.run();
182    if (err !== undefined)
183      throw err;
184
185    return Buffer.from(signature);
186  }
187
188  job.ondone = (error, signature) => {
189    if (error) return FunctionPrototypeCall(callback, job, error);
190    FunctionPrototypeCall(callback, job, null, Buffer.from(signature));
191  };
192  job.run();
193}
194
195function Verify(algorithm, options) {
196  if (!(this instanceof Verify))
197    return new Verify(algorithm, options);
198  validateString(algorithm, 'algorithm');
199  this[kHandle] = new _Verify();
200  this[kHandle].init(algorithm);
201
202  ReflectApply(Writable, this, [options]);
203}
204
205ObjectSetPrototypeOf(Verify.prototype, Writable.prototype);
206ObjectSetPrototypeOf(Verify, Writable);
207
208Verify.prototype._write = Sign.prototype._write;
209Verify.prototype.update = Sign.prototype.update;
210
211Verify.prototype.verify = function verify(options, signature, sigEncoding) {
212  const {
213    data,
214    format,
215    type,
216    passphrase,
217  } = preparePublicOrPrivateKey(options, true);
218
219  sigEncoding = sigEncoding || getDefaultEncoding();
220
221  // Options specific to RSA
222  const rsaPadding = getPadding(options);
223  const pssSaltLength = getSaltLength(options);
224
225  // Options specific to (EC)DSA
226  const dsaSigEnc = getDSASignatureEncoding(options);
227
228  signature = getArrayBufferOrView(signature, 'signature', sigEncoding);
229
230  return this[kHandle].verify(data, format, type, passphrase, signature,
231                              rsaPadding, pssSaltLength, dsaSigEnc);
232};
233
234function verifyOneShot(algorithm, data, key, signature, callback) {
235  if (algorithm != null)
236    validateString(algorithm, 'algorithm');
237
238  if (callback !== undefined)
239    validateFunction(callback, 'callback');
240
241  data = getArrayBufferOrView(data, 'data');
242
243  if (!isArrayBufferView(data)) {
244    throw new ERR_INVALID_ARG_TYPE(
245      'data',
246      ['Buffer', 'TypedArray', 'DataView'],
247      data,
248    );
249  }
250
251  // Options specific to RSA
252  const rsaPadding = getPadding(key);
253  const pssSaltLength = getSaltLength(key);
254
255  // Options specific to (EC)DSA
256  const dsaSigEnc = getDSASignatureEncoding(key);
257
258  if (!isArrayBufferView(signature)) {
259    throw new ERR_INVALID_ARG_TYPE(
260      'signature',
261      ['Buffer', 'TypedArray', 'DataView'],
262      signature,
263    );
264  }
265
266  const {
267    data: keyData,
268    format: keyFormat,
269    type: keyType,
270    passphrase: keyPassphrase,
271  } = preparePublicOrPrivateKey(key);
272
273  const job = new SignJob(
274    callback ? kCryptoJobAsync : kCryptoJobSync,
275    kSignJobModeVerify,
276    keyData,
277    keyFormat,
278    keyType,
279    keyPassphrase,
280    data,
281    algorithm,
282    pssSaltLength,
283    rsaPadding,
284    dsaSigEnc,
285    signature);
286
287  if (!callback) {
288    const { 0: err, 1: result } = job.run();
289    if (err !== undefined)
290      throw err;
291
292    return result;
293  }
294
295  job.ondone = (error, result) => {
296    if (error) return FunctionPrototypeCall(callback, job, error);
297    FunctionPrototypeCall(callback, job, null, result);
298  };
299  job.run();
300}
301
302module.exports = {
303  Sign,
304  signOneShot,
305  Verify,
306  verifyOneShot,
307};
308