// Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. // Note: In 0.8 and before, crypto functions all defaulted to using // binary-encoded strings rather than buffers. exports.DEFAULT_ENCODING = 'buffer'; try { var binding = process.binding('crypto'); var randomBytes = binding.randomBytes; var pseudoRandomBytes = binding.pseudoRandomBytes; var getCiphers = binding.getCiphers; var getHashes = binding.getHashes; } catch (e) { throw new Error('node.js not compiled with openssl crypto support.'); } var constants = require('constants'); var stream = require('stream'); var util = require('util'); var DH_GENERATOR = 2; // This is here because many functions accepted binary strings without // any explicit encoding in older versions of node, and we don't want // to break them unnecessarily. function toBuf(str, encoding) { encoding = encoding || 'binary'; if (util.isString(str)) { if (encoding === 'buffer') encoding = 'binary'; str = new Buffer(str, encoding); } return str; } exports._toBuf = toBuf; var assert = require('assert'); var StringDecoder = require('string_decoder').StringDecoder; function LazyTransform(options) { this._options = options; } util.inherits(LazyTransform, stream.Transform); [ '_readableState', '_writableState', '_transformState' ].forEach(function(prop, i, props) { Object.defineProperty(LazyTransform.prototype, prop, { get: function() { stream.Transform.call(this, this._options); this._writableState.decodeStrings = false; this._writableState.defaultEncoding = 'binary'; return this[prop]; }, set: function(val) { Object.defineProperty(this, prop, { value: val, enumerable: true, configurable: true, writable: true }); }, configurable: true, enumerable: true }); }); exports.createHash = exports.Hash = Hash; function Hash(algorithm, options) { if (!(this instanceof Hash)) return new Hash(algorithm, options); this._handle = new binding.Hash(algorithm); LazyTransform.call(this, options); } util.inherits(Hash, LazyTransform); Hash.prototype._transform = function(chunk, encoding, callback) { this._handle.update(chunk, encoding); callback(); }; Hash.prototype._flush = function(callback) { var encoding = this._readableState.encoding || 'buffer'; this.push(this._handle.digest(encoding), encoding); callback(); }; Hash.prototype.update = function(data, encoding) { encoding = encoding || exports.DEFAULT_ENCODING; if (encoding === 'buffer' && util.isString(data)) encoding = 'binary'; this._handle.update(data, encoding); return this; }; Hash.prototype.digest = function(outputEncoding) { outputEncoding = outputEncoding || exports.DEFAULT_ENCODING; return this._handle.digest(outputEncoding); }; exports.createHmac = exports.Hmac = Hmac; function Hmac(hmac, key, options) { if (!(this instanceof Hmac)) return new Hmac(hmac, key, options); this._handle = new binding.Hmac(); this._handle.init(hmac, toBuf(key)); LazyTransform.call(this, options); } util.inherits(Hmac, LazyTransform); Hmac.prototype.update = Hash.prototype.update; Hmac.prototype.digest = Hash.prototype.digest; Hmac.prototype._flush = Hash.prototype._flush; Hmac.prototype._transform = Hash.prototype._transform; function getDecoder(decoder, encoding) { if (encoding === 'utf-8') encoding = 'utf8'; // Normalize encoding. decoder = decoder || new StringDecoder(encoding); assert(decoder.encoding === encoding, 'Cannot change encoding'); return decoder; } exports.createCipher = exports.Cipher = Cipher; function Cipher(cipher, password, options) { if (!(this instanceof Cipher)) return new Cipher(cipher, password, options); this._handle = new binding.CipherBase(true); this._handle.init(cipher, toBuf(password)); this._decoder = null; LazyTransform.call(this, options); } util.inherits(Cipher, LazyTransform); Cipher.prototype._transform = function(chunk, encoding, callback) { this.push(this._handle.update(chunk, encoding)); callback(); }; Cipher.prototype._flush = function(callback) { try { this.push(this._handle.final()); } catch (e) { callback(e); return; } callback(); }; Cipher.prototype.update = function(data, inputEncoding, outputEncoding) { inputEncoding = inputEncoding || exports.DEFAULT_ENCODING; outputEncoding = outputEncoding || exports.DEFAULT_ENCODING; var ret = this._handle.update(data, inputEncoding); if (outputEncoding && outputEncoding !== 'buffer') { this._decoder = getDecoder(this._decoder, outputEncoding); ret = this._decoder.write(ret); } return ret; }; Cipher.prototype.final = function(outputEncoding) { outputEncoding = outputEncoding || exports.DEFAULT_ENCODING; var ret = this._handle.final(); if (outputEncoding && outputEncoding !== 'buffer') { this._decoder = getDecoder(this._decoder, outputEncoding); ret = this._decoder.end(ret); } return ret; }; Cipher.prototype.setAutoPadding = function(ap) { this._handle.setAutoPadding(ap); return this; }; exports.createCipheriv = exports.Cipheriv = Cipheriv; function Cipheriv(cipher, key, iv, options) { if (!(this instanceof Cipheriv)) return new Cipheriv(cipher, key, iv, options); this._handle = new binding.CipherBase(true); this._handle.initiv(cipher, toBuf(key), toBuf(iv)); this._decoder = null; LazyTransform.call(this, options); } util.inherits(Cipheriv, LazyTransform); Cipheriv.prototype._transform = Cipher.prototype._transform; Cipheriv.prototype._flush = Cipher.prototype._flush; Cipheriv.prototype.update = Cipher.prototype.update; Cipheriv.prototype.final = Cipher.prototype.final; Cipheriv.prototype.setAutoPadding = Cipher.prototype.setAutoPadding; Cipheriv.prototype.getAuthTag = function() { return this._handle.getAuthTag(); }; Cipheriv.prototype.setAuthTag = function(tagbuf) { this._handle.setAuthTag(tagbuf); }; Cipheriv.prototype.setAAD = function(aadbuf) { this._handle.setAAD(aadbuf); }; exports.createDecipher = exports.Decipher = Decipher; function Decipher(cipher, password, options) { if (!(this instanceof Decipher)) return new Decipher(cipher, password, options); this._handle = new binding.CipherBase(false); this._handle.init(cipher, toBuf(password)); this._decoder = null; LazyTransform.call(this, options); } util.inherits(Decipher, LazyTransform); Decipher.prototype._transform = Cipher.prototype._transform; Decipher.prototype._flush = Cipher.prototype._flush; Decipher.prototype.update = Cipher.prototype.update; Decipher.prototype.final = Cipher.prototype.final; Decipher.prototype.finaltol = Cipher.prototype.final; Decipher.prototype.setAutoPadding = Cipher.prototype.setAutoPadding; exports.createDecipheriv = exports.Decipheriv = Decipheriv; function Decipheriv(cipher, key, iv, options) { if (!(this instanceof Decipheriv)) return new Decipheriv(cipher, key, iv, options); this._handle = new binding.CipherBase(false); this._handle.initiv(cipher, toBuf(key), toBuf(iv)); this._decoder = null; LazyTransform.call(this, options); } util.inherits(Decipheriv, LazyTransform); Decipheriv.prototype._transform = Cipher.prototype._transform; Decipheriv.prototype._flush = Cipher.prototype._flush; Decipheriv.prototype.update = Cipher.prototype.update; Decipheriv.prototype.final = Cipher.prototype.final; Decipheriv.prototype.finaltol = Cipher.prototype.final; Decipheriv.prototype.setAutoPadding = Cipher.prototype.setAutoPadding; Decipheriv.prototype.getAuthTag = Cipheriv.prototype.getAuthTag; Decipheriv.prototype.setAuthTag = Cipheriv.prototype.setAuthTag; Decipheriv.prototype.setAAD = Cipheriv.prototype.setAAD; exports.createSign = exports.Sign = Sign; function Sign(algorithm, options) { if (!(this instanceof Sign)) return new Sign(algorithm, options); this._handle = new binding.Sign(); this._handle.init(algorithm); stream.Writable.call(this, options); } util.inherits(Sign, stream.Writable); Sign.prototype._write = function(chunk, encoding, callback) { this._handle.update(chunk, encoding); callback(); }; Sign.prototype.update = Hash.prototype.update; Sign.prototype.sign = function(options, encoding) { if (!options) throw new Error('No key provided to sign'); var key = options.key || options; var passphrase = options.passphrase || null; var ret = this._handle.sign(toBuf(key), null, passphrase); encoding = encoding || exports.DEFAULT_ENCODING; if (encoding && encoding !== 'buffer') ret = ret.toString(encoding); return ret; }; exports.createVerify = exports.Verify = Verify; function Verify(algorithm, options) { if (!(this instanceof Verify)) return new Verify(algorithm, options); this._handle = new binding.Verify; this._handle.init(algorithm); stream.Writable.call(this, options); } util.inherits(Verify, stream.Writable); Verify.prototype._write = Sign.prototype._write; Verify.prototype.update = Sign.prototype.update; Verify.prototype.verify = function(object, signature, sigEncoding) { sigEncoding = sigEncoding || exports.DEFAULT_ENCODING; return this._handle.verify(toBuf(object), toBuf(signature, sigEncoding)); }; exports.publicEncrypt = function(options, buffer) { var key = options.key || options; var padding = options.padding || constants.RSA_PKCS1_OAEP_PADDING; return binding.publicEncrypt(toBuf(key), buffer, padding); }; exports.privateDecrypt = function(options, buffer) { var key = options.key || options; var passphrase = options.passphrase || null; var padding = options.padding || constants.RSA_PKCS1_OAEP_PADDING; return binding.privateDecrypt(toBuf(key), buffer, padding, passphrase); }; exports.createDiffieHellman = exports.DiffieHellman = DiffieHellman; function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) { if (!(this instanceof DiffieHellman)) return new DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding); if (!util.isBuffer(sizeOrKey) && typeof sizeOrKey !== 'number' && typeof sizeOrKey !== 'string') throw new TypeError('First argument should be number, string or Buffer'); if (keyEncoding) { if (typeof keyEncoding !== 'string' || (!Buffer.isEncoding(keyEncoding) && keyEncoding !== 'buffer')) { genEncoding = generator; generator = keyEncoding; keyEncoding = false; } } keyEncoding = keyEncoding || exports.DEFAULT_ENCODING; genEncoding = genEncoding || exports.DEFAULT_ENCODING; if (typeof sizeOrKey !== 'number') sizeOrKey = toBuf(sizeOrKey, keyEncoding); if (!generator) generator = DH_GENERATOR; else if (typeof generator !== 'number') generator = toBuf(generator, genEncoding); this._handle = new binding.DiffieHellman(sizeOrKey, generator); Object.defineProperty(this, 'verifyError', { enumerable: true, value: this._handle.verifyError, writable: false }); } exports.DiffieHellmanGroup = exports.createDiffieHellmanGroup = exports.getDiffieHellman = DiffieHellmanGroup; function DiffieHellmanGroup(name) { if (!(this instanceof DiffieHellmanGroup)) return new DiffieHellmanGroup(name); this._handle = new binding.DiffieHellmanGroup(name); Object.defineProperty(this, 'verifyError', { enumerable: true, value: this._handle.verifyError, writable: false }); } DiffieHellmanGroup.prototype.generateKeys = DiffieHellman.prototype.generateKeys = dhGenerateKeys; function dhGenerateKeys(encoding) { var keys = this._handle.generateKeys(); encoding = encoding || exports.DEFAULT_ENCODING; if (encoding && encoding !== 'buffer') keys = keys.toString(encoding); return keys; } DiffieHellmanGroup.prototype.computeSecret = DiffieHellman.prototype.computeSecret = dhComputeSecret; function dhComputeSecret(key, inEnc, outEnc) { inEnc = inEnc || exports.DEFAULT_ENCODING; outEnc = outEnc || exports.DEFAULT_ENCODING; var ret = this._handle.computeSecret(toBuf(key, inEnc)); if (outEnc && outEnc !== 'buffer') ret = ret.toString(outEnc); return ret; } DiffieHellmanGroup.prototype.getPrime = DiffieHellman.prototype.getPrime = dhGetPrime; function dhGetPrime(encoding) { var prime = this._handle.getPrime(); encoding = encoding || exports.DEFAULT_ENCODING; if (encoding && encoding !== 'buffer') prime = prime.toString(encoding); return prime; } DiffieHellmanGroup.prototype.getGenerator = DiffieHellman.prototype.getGenerator = dhGetGenerator; function dhGetGenerator(encoding) { var generator = this._handle.getGenerator(); encoding = encoding || exports.DEFAULT_ENCODING; if (encoding && encoding !== 'buffer') generator = generator.toString(encoding); return generator; } DiffieHellmanGroup.prototype.getPublicKey = DiffieHellman.prototype.getPublicKey = dhGetPublicKey; function dhGetPublicKey(encoding) { var key = this._handle.getPublicKey(); encoding = encoding || exports.DEFAULT_ENCODING; if (encoding && encoding !== 'buffer') key = key.toString(encoding); return key; } DiffieHellmanGroup.prototype.getPrivateKey = DiffieHellman.prototype.getPrivateKey = dhGetPrivateKey; function dhGetPrivateKey(encoding) { var key = this._handle.getPrivateKey(); encoding = encoding || exports.DEFAULT_ENCODING; if (encoding && encoding !== 'buffer') key = key.toString(encoding); return key; } DiffieHellman.prototype.setPublicKey = function(key, encoding) { encoding = encoding || exports.DEFAULT_ENCODING; this._handle.setPublicKey(toBuf(key, encoding)); return this; }; DiffieHellman.prototype.setPrivateKey = function(key, encoding) { encoding = encoding || exports.DEFAULT_ENCODING; this._handle.setPrivateKey(toBuf(key, encoding)); return this; }; function ECDH(curve) { if (!util.isString(curve)) throw new TypeError('curve should be a string'); this._handle = new binding.ECDH(curve); } exports.createECDH = function createECDH(curve) { return new ECDH(curve); }; ECDH.prototype.computeSecret = DiffieHellman.prototype.computeSecret; ECDH.prototype.setPrivateKey = DiffieHellman.prototype.setPrivateKey; ECDH.prototype.setPublicKey = DiffieHellman.prototype.setPublicKey; ECDH.prototype.getPrivateKey = DiffieHellman.prototype.getPrivateKey; ECDH.prototype.generateKeys = function generateKeys(encoding, format) { this._handle.generateKeys(); return this.getPublicKey(encoding, format); }; ECDH.prototype.getPublicKey = function getPublicKey(encoding, format) { var f; if (format) { if (typeof format === 'number') f = format; if (format === 'compressed') f = constants.POINT_CONVERSION_COMPRESSED; else if (format === 'hybrid') f = constants.POINT_CONVERSION_HYBRID; // Default else if (format === 'uncompressed') f = constants.POINT_CONVERSION_UNCOMPRESSED; else throw TypeError('Bad format: ' + format); } else { f = constants.POINT_CONVERSION_UNCOMPRESSED; } var key = this._handle.getPublicKey(f); encoding = encoding || exports.DEFAULT_ENCODING; if (encoding && encoding !== 'buffer') key = key.toString(encoding); return key; }; exports.pbkdf2 = function(password, salt, iterations, keylen, digest, callback) { if (util.isFunction(digest)) { callback = digest; digest = undefined; } if (!util.isFunction(callback)) throw new Error('No callback provided to pbkdf2'); return pbkdf2(password, salt, iterations, keylen, digest, callback); }; exports.pbkdf2Sync = function(password, salt, iterations, keylen, digest) { return pbkdf2(password, salt, iterations, keylen, digest); }; function pbkdf2(password, salt, iterations, keylen, digest, callback) { password = toBuf(password); salt = toBuf(salt); if (exports.DEFAULT_ENCODING === 'buffer') return binding.PBKDF2(password, salt, iterations, keylen, digest, callback); // at this point, we need to handle encodings. var encoding = exports.DEFAULT_ENCODING; if (callback) { function next(er, ret) { if (ret) ret = ret.toString(encoding); callback(er, ret); } binding.PBKDF2(password, salt, iterations, keylen, digest, next); } else { var ret = binding.PBKDF2(password, salt, iterations, keylen, digest); return ret.toString(encoding); } } exports.Certificate = Certificate; function Certificate() { if (!(this instanceof Certificate)) return new Certificate(); this._handle = new binding.Certificate(); } Certificate.prototype.verifySpkac = function(object) { return this._handle.verifySpkac(object); }; Certificate.prototype.exportPublicKey = function(object, encoding) { return this._handle.exportPublicKey(toBuf(object, encoding)); }; Certificate.prototype.exportChallenge = function(object, encoding) { return this._handle.exportChallenge(toBuf(object, encoding)); }; exports.setEngine = function setEngine(id, flags) { if (!util.isString(id)) throw new TypeError('id should be a string'); if (flags && !util.isNumber(flags)) throw new TypeError('flags should be a number, if present'); flags = flags >>> 0; // Use provided engine for everything by default if (flags === 0) flags = constants.ENGINE_METHOD_ALL; return binding.setEngine(id, flags); }; exports.randomBytes = randomBytes; exports.pseudoRandomBytes = pseudoRandomBytes; exports.rng = randomBytes; exports.prng = pseudoRandomBytes; exports.getCiphers = function() { return filterDuplicates(getCiphers.call(null, arguments)); }; exports.getHashes = function() { return filterDuplicates(getHashes.call(null, arguments)); }; function filterDuplicates(names) { // Drop all-caps names in favor of their lowercase aliases, // for example, 'sha1' instead of 'SHA1'. var ctx = {}; names.forEach(function(name) { var key = name; if (/^[0-9A-Z\-]+$/.test(key)) key = key.toLowerCase(); if (!ctx.hasOwnProperty(key) || ctx[key] < name) ctx[key] = name; }); return Object.getOwnPropertyNames(ctx).map(function(key) { return ctx[key]; }).sort(); } // Legacy API exports.__defineGetter__('createCredentials', util.deprecate(function() { return require('tls').createSecureContext; }, 'createCredentials() is deprecated, use tls.createSecureContext instead')); exports.__defineGetter__('Credentials', util.deprecate(function() { return require('tls').SecureContext; }, 'Credentials is deprecated, use tls.createSecureContext instead'));