crypto: allow padding in RSA methods
[platform/upstream/nodejs.git] / lib / crypto.js
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 // Note: In 0.8 and before, crypto functions all defaulted to using
23 // binary-encoded strings rather than buffers.
24
25 exports.DEFAULT_ENCODING = 'buffer';
26
27 try {
28   var binding = process.binding('crypto');
29   var randomBytes = binding.randomBytes;
30   var pseudoRandomBytes = binding.pseudoRandomBytes;
31   var getCiphers = binding.getCiphers;
32   var getHashes = binding.getHashes;
33 } catch (e) {
34   throw new Error('node.js not compiled with openssl crypto support.');
35 }
36
37 var constants = require('constants');
38 var stream = require('stream');
39 var util = require('util');
40
41 var DH_GENERATOR = 2;
42
43 // This is here because many functions accepted binary strings without
44 // any explicit encoding in older versions of node, and we don't want
45 // to break them unnecessarily.
46 function toBuf(str, encoding) {
47   encoding = encoding || 'binary';
48   if (util.isString(str)) {
49     if (encoding === 'buffer')
50       encoding = 'binary';
51     str = new Buffer(str, encoding);
52   }
53   return str;
54 }
55 exports._toBuf = toBuf;
56
57
58 var assert = require('assert');
59 var StringDecoder = require('string_decoder').StringDecoder;
60
61
62 function LazyTransform(options) {
63   this._options = options;
64 }
65 util.inherits(LazyTransform, stream.Transform);
66
67 [
68   '_readableState',
69   '_writableState',
70   '_transformState'
71 ].forEach(function(prop, i, props) {
72   Object.defineProperty(LazyTransform.prototype, prop, {
73     get: function() {
74       stream.Transform.call(this, this._options);
75       this._writableState.decodeStrings = false;
76       this._writableState.defaultEncoding = 'binary';
77       return this[prop];
78     },
79     set: function(val) {
80       Object.defineProperty(this, prop, {
81         value: val,
82         enumerable: true,
83         configurable: true,
84         writable: true
85       });
86     },
87     configurable: true,
88     enumerable: true
89   });
90 });
91
92
93 exports.createHash = exports.Hash = Hash;
94 function Hash(algorithm, options) {
95   if (!(this instanceof Hash))
96     return new Hash(algorithm, options);
97   this._handle = new binding.Hash(algorithm);
98   LazyTransform.call(this, options);
99 }
100
101 util.inherits(Hash, LazyTransform);
102
103 Hash.prototype._transform = function(chunk, encoding, callback) {
104   this._handle.update(chunk, encoding);
105   callback();
106 };
107
108 Hash.prototype._flush = function(callback) {
109   var encoding = this._readableState.encoding || 'buffer';
110   this.push(this._handle.digest(encoding), encoding);
111   callback();
112 };
113
114 Hash.prototype.update = function(data, encoding) {
115   encoding = encoding || exports.DEFAULT_ENCODING;
116   if (encoding === 'buffer' && util.isString(data))
117     encoding = 'binary';
118   this._handle.update(data, encoding);
119   return this;
120 };
121
122
123 Hash.prototype.digest = function(outputEncoding) {
124   outputEncoding = outputEncoding || exports.DEFAULT_ENCODING;
125   return this._handle.digest(outputEncoding);
126 };
127
128
129 exports.createHmac = exports.Hmac = Hmac;
130
131 function Hmac(hmac, key, options) {
132   if (!(this instanceof Hmac))
133     return new Hmac(hmac, key, options);
134   this._handle = new binding.Hmac();
135   this._handle.init(hmac, toBuf(key));
136   LazyTransform.call(this, options);
137 }
138
139 util.inherits(Hmac, LazyTransform);
140
141 Hmac.prototype.update = Hash.prototype.update;
142 Hmac.prototype.digest = Hash.prototype.digest;
143 Hmac.prototype._flush = Hash.prototype._flush;
144 Hmac.prototype._transform = Hash.prototype._transform;
145
146
147 function getDecoder(decoder, encoding) {
148   if (encoding === 'utf-8') encoding = 'utf8';  // Normalize encoding.
149   decoder = decoder || new StringDecoder(encoding);
150   assert(decoder.encoding === encoding, 'Cannot change encoding');
151   return decoder;
152 }
153
154
155 exports.createCipher = exports.Cipher = Cipher;
156 function Cipher(cipher, password, options) {
157   if (!(this instanceof Cipher))
158     return new Cipher(cipher, password, options);
159   this._handle = new binding.CipherBase(true);
160
161   this._handle.init(cipher, toBuf(password));
162   this._decoder = null;
163
164   LazyTransform.call(this, options);
165 }
166
167 util.inherits(Cipher, LazyTransform);
168
169 Cipher.prototype._transform = function(chunk, encoding, callback) {
170   this.push(this._handle.update(chunk, encoding));
171   callback();
172 };
173
174 Cipher.prototype._flush = function(callback) {
175   try {
176     this.push(this._handle.final());
177   } catch (e) {
178     callback(e);
179     return;
180   }
181   callback();
182 };
183
184 Cipher.prototype.update = function(data, inputEncoding, outputEncoding) {
185   inputEncoding = inputEncoding || exports.DEFAULT_ENCODING;
186   outputEncoding = outputEncoding || exports.DEFAULT_ENCODING;
187
188   var ret = this._handle.update(data, inputEncoding);
189
190   if (outputEncoding && outputEncoding !== 'buffer') {
191     this._decoder = getDecoder(this._decoder, outputEncoding);
192     ret = this._decoder.write(ret);
193   }
194
195   return ret;
196 };
197
198
199 Cipher.prototype.final = function(outputEncoding) {
200   outputEncoding = outputEncoding || exports.DEFAULT_ENCODING;
201   var ret = this._handle.final();
202
203   if (outputEncoding && outputEncoding !== 'buffer') {
204     this._decoder = getDecoder(this._decoder, outputEncoding);
205     ret = this._decoder.end(ret);
206   }
207
208   return ret;
209 };
210
211
212 Cipher.prototype.setAutoPadding = function(ap) {
213   this._handle.setAutoPadding(ap);
214   return this;
215 };
216
217
218
219 exports.createCipheriv = exports.Cipheriv = Cipheriv;
220 function Cipheriv(cipher, key, iv, options) {
221   if (!(this instanceof Cipheriv))
222     return new Cipheriv(cipher, key, iv, options);
223   this._handle = new binding.CipherBase(true);
224   this._handle.initiv(cipher, toBuf(key), toBuf(iv));
225   this._decoder = null;
226
227   LazyTransform.call(this, options);
228 }
229
230 util.inherits(Cipheriv, LazyTransform);
231
232 Cipheriv.prototype._transform = Cipher.prototype._transform;
233 Cipheriv.prototype._flush = Cipher.prototype._flush;
234 Cipheriv.prototype.update = Cipher.prototype.update;
235 Cipheriv.prototype.final = Cipher.prototype.final;
236 Cipheriv.prototype.setAutoPadding = Cipher.prototype.setAutoPadding;
237
238 Cipheriv.prototype.getAuthTag = function() {
239   return this._handle.getAuthTag();
240 };
241
242
243 Cipheriv.prototype.setAuthTag = function(tagbuf) {
244   this._handle.setAuthTag(tagbuf);
245 };
246
247 Cipheriv.prototype.setAAD = function(aadbuf) {
248   this._handle.setAAD(aadbuf);
249 };
250
251
252 exports.createDecipher = exports.Decipher = Decipher;
253 function Decipher(cipher, password, options) {
254   if (!(this instanceof Decipher))
255     return new Decipher(cipher, password, options);
256
257   this._handle = new binding.CipherBase(false);
258   this._handle.init(cipher, toBuf(password));
259   this._decoder = null;
260
261   LazyTransform.call(this, options);
262 }
263
264 util.inherits(Decipher, LazyTransform);
265
266 Decipher.prototype._transform = Cipher.prototype._transform;
267 Decipher.prototype._flush = Cipher.prototype._flush;
268 Decipher.prototype.update = Cipher.prototype.update;
269 Decipher.prototype.final = Cipher.prototype.final;
270 Decipher.prototype.finaltol = Cipher.prototype.final;
271 Decipher.prototype.setAutoPadding = Cipher.prototype.setAutoPadding;
272
273
274
275 exports.createDecipheriv = exports.Decipheriv = Decipheriv;
276 function Decipheriv(cipher, key, iv, options) {
277   if (!(this instanceof Decipheriv))
278     return new Decipheriv(cipher, key, iv, options);
279
280   this._handle = new binding.CipherBase(false);
281   this._handle.initiv(cipher, toBuf(key), toBuf(iv));
282   this._decoder = null;
283
284   LazyTransform.call(this, options);
285 }
286
287 util.inherits(Decipheriv, LazyTransform);
288
289 Decipheriv.prototype._transform = Cipher.prototype._transform;
290 Decipheriv.prototype._flush = Cipher.prototype._flush;
291 Decipheriv.prototype.update = Cipher.prototype.update;
292 Decipheriv.prototype.final = Cipher.prototype.final;
293 Decipheriv.prototype.finaltol = Cipher.prototype.final;
294 Decipheriv.prototype.setAutoPadding = Cipher.prototype.setAutoPadding;
295 Decipheriv.prototype.getAuthTag = Cipheriv.prototype.getAuthTag;
296 Decipheriv.prototype.setAuthTag = Cipheriv.prototype.setAuthTag;
297 Decipheriv.prototype.setAAD = Cipheriv.prototype.setAAD;
298
299
300
301 exports.createSign = exports.Sign = Sign;
302 function Sign(algorithm, options) {
303   if (!(this instanceof Sign))
304     return new Sign(algorithm, options);
305   this._handle = new binding.Sign();
306   this._handle.init(algorithm);
307
308   stream.Writable.call(this, options);
309 }
310
311 util.inherits(Sign, stream.Writable);
312
313 Sign.prototype._write = function(chunk, encoding, callback) {
314   this._handle.update(chunk, encoding);
315   callback();
316 };
317
318 Sign.prototype.update = Hash.prototype.update;
319
320 Sign.prototype.sign = function(options, encoding) {
321   if (!options)
322     throw new Error('No key provided to sign');
323
324   var key = options.key || options;
325   var passphrase = options.passphrase || null;
326   var ret = this._handle.sign(toBuf(key), null, passphrase);
327
328   encoding = encoding || exports.DEFAULT_ENCODING;
329   if (encoding && encoding !== 'buffer')
330     ret = ret.toString(encoding);
331
332   return ret;
333 };
334
335
336
337 exports.createVerify = exports.Verify = Verify;
338 function Verify(algorithm, options) {
339   if (!(this instanceof Verify))
340     return new Verify(algorithm, options);
341
342   this._handle = new binding.Verify;
343   this._handle.init(algorithm);
344
345   stream.Writable.call(this, options);
346 }
347
348 util.inherits(Verify, stream.Writable);
349
350 Verify.prototype._write = Sign.prototype._write;
351 Verify.prototype.update = Sign.prototype.update;
352
353 Verify.prototype.verify = function(object, signature, sigEncoding) {
354   sigEncoding = sigEncoding || exports.DEFAULT_ENCODING;
355   return this._handle.verify(toBuf(object), toBuf(signature, sigEncoding));
356 };
357
358 exports.publicEncrypt = function(options, buffer) {
359   var key = options.key || options;
360   var padding = options.padding || constants.RSA_PKCS1_OAEP_PADDING;
361   return binding.publicEncrypt(toBuf(key), buffer, padding);
362 };
363
364 exports.privateDecrypt = function(options, buffer) {
365   var key = options.key || options;
366   var passphrase = options.passphrase || null;
367   var padding = options.padding || constants.RSA_PKCS1_OAEP_PADDING;
368   return binding.privateDecrypt(toBuf(key), buffer, padding, passphrase);
369 };
370
371
372
373 exports.createDiffieHellman = exports.DiffieHellman = DiffieHellman;
374
375 function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) {
376   if (!(this instanceof DiffieHellman))
377     return new DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding);
378
379   if (keyEncoding) {
380     if (typeof keyEncoding !== 'string' ||
381         (!Buffer.isEncoding(keyEncoding) && keyEncoding !== 'buffer')) {
382       genEncoding = generator;
383       generator = keyEncoding;
384       keyEncoding = false;
385     }
386   }
387
388   keyEncoding = keyEncoding || exports.DEFAULT_ENCODING;
389   genEncoding = genEncoding || exports.DEFAULT_ENCODING;
390
391   if (typeof sizeOrKey !== 'number')
392     sizeOrKey = toBuf(sizeOrKey, keyEncoding);
393
394   if (!generator)
395     generator = DH_GENERATOR;
396   else if (typeof generator !== 'number')
397     generator = toBuf(generator, genEncoding);
398
399   this._handle = new binding.DiffieHellman(sizeOrKey, generator);
400   Object.defineProperty(this, 'verifyError', {
401     enumerable: true,
402     value: this._handle.verifyError,
403     writable: false
404   });
405 }
406
407
408 exports.DiffieHellmanGroup =
409     exports.createDiffieHellmanGroup =
410     exports.getDiffieHellman = DiffieHellmanGroup;
411
412 function DiffieHellmanGroup(name) {
413   if (!(this instanceof DiffieHellmanGroup))
414     return new DiffieHellmanGroup(name);
415   this._handle = new binding.DiffieHellmanGroup(name);
416   Object.defineProperty(this, 'verifyError', {
417     enumerable: true,
418     value: this._handle.verifyError,
419     writable: false
420   });
421 }
422
423
424 DiffieHellmanGroup.prototype.generateKeys =
425     DiffieHellman.prototype.generateKeys =
426     dhGenerateKeys;
427
428 function dhGenerateKeys(encoding) {
429   var keys = this._handle.generateKeys();
430   encoding = encoding || exports.DEFAULT_ENCODING;
431   if (encoding && encoding !== 'buffer')
432     keys = keys.toString(encoding);
433   return keys;
434 }
435
436
437 DiffieHellmanGroup.prototype.computeSecret =
438     DiffieHellman.prototype.computeSecret =
439     dhComputeSecret;
440
441 function dhComputeSecret(key, inEnc, outEnc) {
442   inEnc = inEnc || exports.DEFAULT_ENCODING;
443   outEnc = outEnc || exports.DEFAULT_ENCODING;
444   var ret = this._handle.computeSecret(toBuf(key, inEnc));
445   if (outEnc && outEnc !== 'buffer')
446     ret = ret.toString(outEnc);
447   return ret;
448 }
449
450
451 DiffieHellmanGroup.prototype.getPrime =
452     DiffieHellman.prototype.getPrime =
453     dhGetPrime;
454
455 function dhGetPrime(encoding) {
456   var prime = this._handle.getPrime();
457   encoding = encoding || exports.DEFAULT_ENCODING;
458   if (encoding && encoding !== 'buffer')
459     prime = prime.toString(encoding);
460   return prime;
461 }
462
463
464 DiffieHellmanGroup.prototype.getGenerator =
465     DiffieHellman.prototype.getGenerator =
466     dhGetGenerator;
467
468 function dhGetGenerator(encoding) {
469   var generator = this._handle.getGenerator();
470   encoding = encoding || exports.DEFAULT_ENCODING;
471   if (encoding && encoding !== 'buffer')
472     generator = generator.toString(encoding);
473   return generator;
474 }
475
476
477 DiffieHellmanGroup.prototype.getPublicKey =
478     DiffieHellman.prototype.getPublicKey =
479     dhGetPublicKey;
480
481 function dhGetPublicKey(encoding) {
482   var key = this._handle.getPublicKey();
483   encoding = encoding || exports.DEFAULT_ENCODING;
484   if (encoding && encoding !== 'buffer')
485     key = key.toString(encoding);
486   return key;
487 }
488
489
490 DiffieHellmanGroup.prototype.getPrivateKey =
491     DiffieHellman.prototype.getPrivateKey =
492     dhGetPrivateKey;
493
494 function dhGetPrivateKey(encoding) {
495   var key = this._handle.getPrivateKey();
496   encoding = encoding || exports.DEFAULT_ENCODING;
497   if (encoding && encoding !== 'buffer')
498     key = key.toString(encoding);
499   return key;
500 }
501
502
503 DiffieHellman.prototype.setPublicKey = function(key, encoding) {
504   encoding = encoding || exports.DEFAULT_ENCODING;
505   this._handle.setPublicKey(toBuf(key, encoding));
506   return this;
507 };
508
509
510 DiffieHellman.prototype.setPrivateKey = function(key, encoding) {
511   encoding = encoding || exports.DEFAULT_ENCODING;
512   this._handle.setPrivateKey(toBuf(key, encoding));
513   return this;
514 };
515
516
517
518 exports.pbkdf2 = function(password,
519                           salt,
520                           iterations,
521                           keylen,
522                           digest,
523                           callback) {
524   if (util.isFunction(digest)) {
525     callback = digest;
526     digest = undefined;
527   }
528
529   if (!util.isFunction(callback))
530     throw new Error('No callback provided to pbkdf2');
531
532   return pbkdf2(password, salt, iterations, keylen, digest, callback);
533 };
534
535
536 exports.pbkdf2Sync = function(password, salt, iterations, keylen, digest) {
537   return pbkdf2(password, salt, iterations, keylen, digest);
538 };
539
540
541 function pbkdf2(password, salt, iterations, keylen, digest, callback) {
542   password = toBuf(password);
543   salt = toBuf(salt);
544
545   if (exports.DEFAULT_ENCODING === 'buffer')
546     return binding.PBKDF2(password, salt, iterations, keylen, digest, callback);
547
548   // at this point, we need to handle encodings.
549   var encoding = exports.DEFAULT_ENCODING;
550   if (callback) {
551     function next(er, ret) {
552       if (ret)
553         ret = ret.toString(encoding);
554       callback(er, ret);
555     }
556     binding.PBKDF2(password, salt, iterations, keylen, digest, next);
557   } else {
558     var ret = binding.PBKDF2(password, salt, iterations, keylen, digest);
559     return ret.toString(encoding);
560   }
561 }
562
563
564 exports.Certificate = Certificate;
565
566 function Certificate() {
567   if (!(this instanceof Certificate))
568     return new Certificate();
569
570   this._handle = new binding.Certificate();
571 }
572
573
574 Certificate.prototype.verifySpkac = function(object) {
575   return this._handle.verifySpkac(object);
576 };
577
578
579 Certificate.prototype.exportPublicKey = function(object, encoding) {
580   return this._handle.exportPublicKey(toBuf(object, encoding));
581 };
582
583
584 Certificate.prototype.exportChallenge = function(object, encoding) {
585   return this._handle.exportChallenge(toBuf(object, encoding));
586 };
587
588
589 exports.setEngine = function setEngine(id, flags) {
590   if (!util.isString(id))
591     throw new TypeError('id should be a string');
592
593   if (flags && !util.isNumber(flags))
594     throw new TypeError('flags should be a number, if present');
595   flags = flags >>> 0;
596
597   // Use provided engine for everything by default
598   if (flags === 0)
599     flags = constants.ENGINE_METHOD_ALL;
600
601   return binding.setEngine(id, flags);
602 };
603
604 exports.randomBytes = randomBytes;
605 exports.pseudoRandomBytes = pseudoRandomBytes;
606
607 exports.rng = randomBytes;
608 exports.prng = pseudoRandomBytes;
609
610
611 exports.getCiphers = function() {
612   return filterDuplicates(getCiphers.call(null, arguments));
613 };
614
615
616 exports.getHashes = function() {
617   return filterDuplicates(getHashes.call(null, arguments));
618
619 };
620
621
622 function filterDuplicates(names) {
623   // Drop all-caps names in favor of their lowercase aliases,
624   // for example, 'sha1' instead of 'SHA1'.
625   var ctx = {};
626   names.forEach(function(name) {
627     var key = name;
628     if (/^[0-9A-Z\-]+$/.test(key)) key = key.toLowerCase();
629     if (!ctx.hasOwnProperty(key) || ctx[key] < name)
630       ctx[key] = name;
631   });
632
633   return Object.getOwnPropertyNames(ctx).map(function(key) {
634     return ctx[key];
635   }).sort();
636 }
637
638 // Legacy API
639 exports.__defineGetter__('createCredentials', util.deprecate(function() {
640   return require('tls').createSecureContext;
641 }, 'createCredentials() is deprecated, use tls.createSecureContext instead'));
642
643 exports.__defineGetter__('Credentials', util.deprecate(function() {
644   return require('tls').SecureContext;
645 }, 'Credentials is deprecated, use tls.createSecureContext instead'));