diff --git a/lib/crypto.js b/lib/crypto.js index da381463fd4d..24a4e09fa03e 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -6,323 +6,27 @@ const internalUtil = require('internal/util'); internalUtil.assertCrypto(); -exports.DEFAULT_ENCODING = 'buffer'; - -const constants = process.binding('constants').crypto; const binding = process.binding('crypto'); -const randomBytes = binding.randomBytes; +const constants = process.binding('constants').crypto; +const cryptoUtil = require('internal/crypto/util'); +const internalCert = require('internal/crypto/certificate'); +const internalCipher = require('internal/crypto/cipher'); +const internalDh = require('internal/crypto/diffiehellman'); +const internalHash = require('internal/crypto/hash'); +const internalSign = require('internal/crypto/sign'); const getCiphers = binding.getCiphers; const getHashes = binding.getHashes; const getCurves = binding.getCurves; const getFipsCrypto = binding.getFipsCrypto; +const randomBytes = binding.randomBytes; const setFipsCrypto = binding.setFipsCrypto; const timingSafeEqual = binding.timingSafeEqual; -const Buffer = require('buffer').Buffer; -const stream = require('stream'); -const util = require('util'); -const LazyTransform = require('internal/streams/lazy_transform'); - -const DH_GENERATOR = 2; - -Object.defineProperty(exports, 'constants', { - configurable: false, - enumerable: true, - value: constants -}); - -// 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) { - if (typeof str === 'string') { - if (encoding === 'buffer' || !encoding) - encoding = 'utf8'; - return Buffer.from(str, encoding); - } - return str; -} -exports._toBuf = toBuf; - - -const assert = require('assert'); -const StringDecoder = require('string_decoder').StringDecoder; - - -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 _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) { - encoding = encoding || exports.DEFAULT_ENCODING; - this._handle.update(data, encoding); - return this; -}; - - -Hash.prototype.digest = function digest(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) { - encoding = internalUtil.normalizeEncoding(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 _transform(chunk, encoding, callback) { - this.push(this._handle.update(chunk, encoding)); - callback(); -}; - -Cipher.prototype._flush = function _flush(callback) { - try { - this.push(this._handle.final()); - } catch (e) { - callback(e); - return; - } - callback(); -}; - -Cipher.prototype.update = function update(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 final(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 setAutoPadding(ap) { - this._handle.setAutoPadding(ap); - return this; -}; - -Cipher.prototype.getAuthTag = function getAuthTag() { - return this._handle.getAuthTag(); -}; - - -Cipher.prototype.setAuthTag = function setAuthTag(tagbuf) { - this._handle.setAuthTag(tagbuf); - return this; -}; - -Cipher.prototype.setAAD = function setAAD(aadbuf) { - this._handle.setAAD(aadbuf); - 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 = Cipher.prototype.getAuthTag; -Cipheriv.prototype.setAuthTag = Cipher.prototype.setAuthTag; -Cipheriv.prototype.setAAD = Cipher.prototype.setAAD; - -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; -Decipher.prototype.getAuthTag = Cipher.prototype.getAuthTag; -Decipher.prototype.setAuthTag = Cipher.prototype.setAuthTag; -Decipher.prototype.setAAD = Cipher.prototype.setAAD; - - -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 = Cipher.prototype.getAuthTag; -Decipheriv.prototype.setAuthTag = Cipher.prototype.setAuthTag; -Decipheriv.prototype.setAAD = Cipher.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 _write(chunk, encoding, callback) { - this._handle.update(chunk, encoding); - callback(); -}; - -Sign.prototype.update = Hash.prototype.update; - -Sign.prototype.sign = function sign(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 verify(object, signature, sigEncoding) { - sigEncoding = sigEncoding || exports.DEFAULT_ENCODING; - return this._handle.verify(toBuf(object), toBuf(signature, sigEncoding)); -}; - -function rsaPublic(method, defaultPadding) { - return function(options, buffer) { - var key = options.key || options; - var padding = options.padding || defaultPadding; - var passphrase = options.passphrase || null; - return method(toBuf(key), buffer, padding, passphrase); - }; -} +const toBuf = cryptoUtil.toBuf; +const filterDuplicateStrings = internalUtil.filterDuplicateStrings; +const cachedResult = internalUtil.cachedResult; -function rsaPrivate(method, defaultPadding) { +function rsa(method, defaultPadding) { return function(options, buffer) { var key = options.key || options; var passphrase = options.passphrase || null; @@ -331,218 +35,106 @@ function rsaPrivate(method, defaultPadding) { }; } -exports.publicEncrypt = rsaPublic(binding.publicEncrypt, - constants.RSA_PKCS1_OAEP_PADDING); -exports.publicDecrypt = rsaPublic(binding.publicDecrypt, - constants.RSA_PKCS1_PADDING); -exports.privateEncrypt = rsaPrivate(binding.privateEncrypt, - constants.RSA_PKCS1_PADDING); -exports.privateDecrypt = rsaPrivate(binding.privateDecrypt, - constants.RSA_PKCS1_OAEP_PADDING); - - -exports.createDiffieHellman = exports.DiffieHellman = DiffieHellman; - -function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) { - if (!(this instanceof DiffieHellman)) - return new DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding); - - if (!(sizeOrKey instanceof Buffer) && - 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; +module.exports = exports = { + createCipher: internalCipher.createCipher, + createCipheriv: internalCipher.createCipheriv, + createDecipher: internalCipher.createDecipher, + createDecipheriv: internalCipher.createDecipheriv, + createDiffieHellman: internalDh.createDiffieHellman, + createDiffieHellmanGroup: internalDh.createDiffieHellmanGroup, + createECDH: internalDh.createECDH, + createHash: internalHash.createHash, + createHmac: internalHash.createHmac, + createSign: internalSign.createSign, + createVerify: internalSign.createVerify, + getDiffieHellman: internalDh.createDiffieHellmanGroup, + getCiphers: cachedResult(() => filterDuplicateStrings(getCiphers())), + getCurves: cachedResult(() => filterDuplicateStrings(getCurves())), + getHashes: cachedResult(() => filterDuplicateStrings(getHashes())), + pbkdf2: pbkdf2, + pbkdf2Sync: pbkdf2Sync, + publicEncrypt: rsa(binding.publicEncrypt, constants.RSA_PKCS1_OAEP_PADDING), + publicDecrypt: rsa(binding.publicDecrypt, constants.RSA_PKCS1_PADDING), + privateEncrypt: rsa(binding.privateEncrypt, constants.RSA_PKCS1_PADDING), + privateDecrypt: rsa(binding.privateDecrypt, constants.RSA_PKCS1_OAEP_PADDING), + prng: randomBytes, + pseudoRandomBytes: randomBytes, + randomBytes: randomBytes, + rng: randomBytes, + setEngine: setEngine, + timingSafeEqual: timingSafeEqual, + Certificate: internalCert.createCertificate, + Cipher: internalCipher.createCipher, + Cipheriv: internalCipher.createCipheriv, + Decipher: internalCipher.createDecipher, + Decipheriv: internalCipher.createDecipheriv, + DiffieHellman: internalDh.createDiffieHellman, + DiffieHellmanGroup: internalDh.createDiffieHellmanGroup, + Hash: internalHash.createHash, + Hmac: internalHash.createHmac, + Sign: internalSign.createSign, + Verify: internalSign.createVerify, + + _toBuf: toBuf +}; + +Object.defineProperties(exports, { + 'constants': { + configurable: false, + enumerable: true, + value: constants + }, + 'DEFAULT_ENCODING': { + configurable: true, + enumerable: true, + get: function() { + return cryptoUtil.DEFAULT_ENCODING; + }, + set: function(encoding) { + encoding = internalUtil.normalizeEncoding(encoding); + cryptoUtil.DEFAULT_ENCODING = encoding; } - } - - 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', { + }, + 'fips': { + get: getFipsCrypto, + set: setFipsCrypto + }, + + // Legacy API + 'createCredentials': { + configurable: true, 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', { + get: internalUtil.deprecate(function() { + return require('tls').createSecureContext; + }, 'crypto.createCredentials is deprecated. ' + + 'Use tls.createSecureContext instead.', 'DEP0010') + }, + 'Credentials': { + configurable: true, 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 setPublicKey(key, encoding) { - encoding = encoding || exports.DEFAULT_ENCODING; - this._handle.setPublicKey(toBuf(key, encoding)); - return this; -}; - + get: internalUtil.deprecate(function() { + return require('tls').SecureContext; + }, 'crypto.Credentials is deprecated. ' + + 'Use tls.SecureContext instead.', 'DEP0011') + } +}); -DiffieHellman.prototype.setPrivateKey = function setPrivateKey(key, encoding) { - encoding = encoding || exports.DEFAULT_ENCODING; - this._handle.setPrivateKey(toBuf(key, encoding)); - return this; -}; +function setEngine(id, flags) { + if (typeof id !== 'string') + throw new TypeError('"id" argument should be a string'); + if (flags && typeof flags !== 'number') + throw new TypeError('"flags" argument should be a number, if present'); + flags = flags >>> 0; -function ECDH(curve) { - if (typeof curve !== 'string') - throw new TypeError('"curve" argument should be a string'); + // Use provided engine for everything by default + if (flags === 0) + flags = constants.ENGINE_METHOD_ALL; - this._handle = new binding.ECDH(curve); + return binding.setEngine(id, flags); } -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 new 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) { +function pbkdf2(password, salt, iterations, keylen, digest, callback) { if (typeof digest === 'function') { callback = digest; digest = undefined; @@ -551,16 +143,14 @@ exports.pbkdf2 = function(password, if (typeof callback !== 'function') 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); -}; + return _pbkdf2(password, salt, iterations, keylen, digest, callback); +} +function pbkdf2Sync(password, salt, iterations, keylen, digest) { + return _pbkdf2(password, salt, iterations, keylen, digest); +} -function pbkdf2(password, salt, iterations, keylen, digest, callback) { +function _pbkdf2(password, salt, iterations, keylen, digest, callback) { if (digest === undefined) { throw new TypeError( @@ -570,11 +160,11 @@ function pbkdf2(password, salt, iterations, keylen, digest, callback) { password = toBuf(password); salt = toBuf(salt); - if (exports.DEFAULT_ENCODING === 'buffer') + if (cryptoUtil.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; + var encoding = cryptoUtil.DEFAULT_ENCODING; if (callback) { var next = function(er, ret) { if (ret) @@ -587,86 +177,3 @@ function pbkdf2(password, salt, iterations, keylen, digest, callback) { return ret.toString(encoding); } } - - -exports.Certificate = Certificate; - -function Certificate() { - if (!(this instanceof Certificate)) - return new Certificate(); -} - - -Certificate.prototype.verifySpkac = function verifySpkac(object) { - return binding.certVerifySpkac(object); -}; - - -Certificate.prototype.exportPublicKey = - function exportPublicKey(object, encoding) { - return binding.certExportPublicKey(toBuf(object, encoding)); - }; - - -Certificate.prototype.exportChallenge = - function exportChallenge(object, encoding) { - return binding.certExportChallenge(toBuf(object, encoding)); - }; - - -exports.setEngine = function setEngine(id, flags) { - if (typeof id !== 'string') - throw new TypeError('"id" argument should be a string'); - - if (flags && typeof flags !== 'number') - throw new TypeError('"flags" argument 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 = exports.pseudoRandomBytes = randomBytes; - -exports.rng = exports.prng = randomBytes; - -exports.getCiphers = internalUtil.cachedResult( - () => internalUtil.filterDuplicateStrings(getCiphers()) -); - -exports.getHashes = internalUtil.cachedResult( - () => internalUtil.filterDuplicateStrings(getHashes()) -); - -exports.getCurves = internalUtil.cachedResult( - () => internalUtil.filterDuplicateStrings(getCurves()) -); - -Object.defineProperty(exports, 'fips', { - get: getFipsCrypto, - set: setFipsCrypto -}); - -exports.timingSafeEqual = timingSafeEqual; - -// Legacy API -Object.defineProperty(exports, 'createCredentials', { - configurable: true, - enumerable: true, - get: internalUtil.deprecate(function() { - return require('tls').createSecureContext; - }, 'crypto.createCredentials is deprecated. ' + - 'Use tls.createSecureContext instead.', 'DEP0010') -}); - -Object.defineProperty(exports, 'Credentials', { - configurable: true, - enumerable: true, - get: internalUtil.deprecate(function() { - return require('tls').SecureContext; - }, 'crypto.Credentials is deprecated. ' + - 'Use tls.SecureContext instead.', 'DEP0011') -}); diff --git a/lib/internal/crypto/certificate.js b/lib/internal/crypto/certificate.js new file mode 100644 index 000000000000..037d4ab80812 --- /dev/null +++ b/lib/internal/crypto/certificate.js @@ -0,0 +1,25 @@ +'use strict'; + +const binding = process.binding('crypto'); +const cryptoUtil = require('internal/crypto/util'); +const internalUtil = require('internal/util'); +const toBuf = cryptoUtil.toBuf; + +class Certificate { + verifySpkac(object) { + return binding.certVerifySpkac(object); + } + + exportPublicKey(object, encoding) { + return binding.certExportPublicKey(toBuf(object, encoding)); + } + + exportChallenge(object, encoding) { + return binding.certExportChallenge(toBuf(object, encoding)); + } +} + +module.exports = { + Certificate, + createCertificate: internalUtil.createClassWrapper(Certificate) +}; diff --git a/lib/internal/crypto/cipher.js b/lib/internal/crypto/cipher.js new file mode 100644 index 000000000000..cad2c94f76dc --- /dev/null +++ b/lib/internal/crypto/cipher.js @@ -0,0 +1,170 @@ +'use strict'; + +const assert = require('assert'); +const binding = process.binding('crypto'); +const cryptoUtil = require('internal/crypto/util'); +const internalUtil = require('internal/util'); +const LazyTransform = require('internal/streams/lazy_transform'); +const StringDecoder = require('string_decoder').StringDecoder; +const util = require('util'); + +const inspect = internalUtil.customInspectSymbol; +const toBuf = cryptoUtil.toBuf; +const kAlgorithm = cryptoUtil.kAlgorithm; + +function getDecoder(decoder, encoding) { + encoding = internalUtil.normalizeEncoding(encoding); + decoder = decoder || new StringDecoder(encoding); + assert(decoder.encoding === encoding, 'Cannot change encoding'); + return decoder; +} + +class CipherBase extends LazyTransform { + constructor(handle, algorithm, decoder, options) { + super(options); + this._handle = handle; + this._decoder = decoder; + this[kAlgorithm] = algorithm; + } + + _transform(chunk, encoding, callback) { + this.push(this._handle.update(chunk, encoding)); + callback(); + } + + _flush(callback) { + try { + this.push(this._handle.final()); + } catch (e) { + callback(e); + return; + } + callback(); + } + + update(data, inputEncoding, outputEncoding) { + inputEncoding = inputEncoding || cryptoUtil.DEFAULT_ENCODING; + outputEncoding = outputEncoding || cryptoUtil.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; + } + + final(outputEncoding) { + outputEncoding = outputEncoding || cryptoUtil.DEFAULT_ENCODING; + var ret = this._handle.final(); + + if (outputEncoding && outputEncoding !== 'buffer') { + this._decoder = getDecoder(this._decoder, outputEncoding); + ret = this._decoder.end(ret); + } + + return ret; + } + + setAutoPadding(ap) { + this._handle.setAutoPadding(ap); + return this; + } + + getAuthTag() { + return this._handle.getAuthTag(); + } + + setAuthTag(tagbuf) { + this._handle.setAuthTag(tagbuf); + return this; + } + + setAAD(aadbuf) { + this._handle.setAAD(aadbuf); + return this; + } +} + +class Cipher extends CipherBase { + constructor(cipher, password, options) { + const handle = new binding.CipherBase(true); + handle.init(cipher, toBuf(password)); + super(handle, cipher, null, options); + } + + [inspect](depth, options) { + var obj = { + cipher: this[kAlgorithm] + }; + if (this._options) + obj.options = this._options; + return `Cipher ${util.inspect(obj, options)}`; + } +} + +class Cipheriv extends CipherBase { + constructor(cipher, key, iv, options) { + const handle = new binding.CipherBase(true); + handle.initiv(cipher, toBuf(key), toBuf(iv)); + super(handle, cipher, null, options); + } + + [inspect](depth, options) { + var obj = { + cipher: this[kAlgorithm] + }; + if (this._options) + obj.options = this._options; + return `Cipheriv ${util.inspect(obj, options)}`; + } +} + +class Decipher extends CipherBase { + constructor(cipher, password, options) { + const handle = new binding.CipherBase(false); + handle.init(cipher, toBuf(password)); + super(handle, cipher, null, options); + } + + [inspect](depth, options) { + var obj = { + cipher: this[kAlgorithm] + }; + if (this._options) + obj.options = this._options; + return `Decipher ${util.inspect(obj, options)}`; + } +} +Decipher.prototype.finaltol = Decipher.prototype.final; + +class Decipheriv extends CipherBase { + constructor(cipher, key, iv, options) { + const handle = new binding.CipherBase(false); + handle.initiv(cipher, toBuf(key), toBuf(iv)); + super(handle, cipher, null, options); + } + + [inspect](depth, options) { + var obj = { + cipher: this[kAlgorithm] + }; + if (this._options) + obj.options = this._options; + return `Decipheriv ${util.inspect(obj, options)}`; + } +} +Decipheriv.prototype.finaltol = Decipheriv.prototype.final; + +module.exports = { + Cipher, + Decipher, + Cipheriv, + Decipheriv, + createCipher: internalUtil.createClassWrapper(Cipher), + createCipheriv: internalUtil.createClassWrapper(Cipheriv), + createDecipher: internalUtil.createClassWrapper(Decipher), + createDecipheriv: internalUtil.createClassWrapper(Decipheriv) +}; diff --git a/lib/internal/crypto/diffiehellman.js b/lib/internal/crypto/diffiehellman.js new file mode 100644 index 000000000000..78af967e0032 --- /dev/null +++ b/lib/internal/crypto/diffiehellman.js @@ -0,0 +1,207 @@ +'use strict'; + +const binding = process.binding('crypto'); +const Buffer = require('buffer').Buffer; +const constants = process.binding('constants').crypto; +const cryptoUtil = require('internal/crypto/util'); +const internalUtil = require('internal/util'); +const util = require('util'); + +const inspect = internalUtil.customInspectSymbol; +const toBuf = cryptoUtil.toBuf; +const kAlgorithm = cryptoUtil.kAlgorithm; + +const kGenerator = 2; + +class DiffieHellmanBase { + constructor(handle) { + this._handle = handle; + Object.defineProperty(this, 'verifyError', { + enumerable: true, + value: this._handle.verifyError, + writable: false + }); + } + + generateKeys(encoding) { + var keys = this._handle.generateKeys(); + encoding = encoding || cryptoUtil.DEFAULT_ENCODING; + if (encoding && encoding !== 'buffer') + keys = keys.toString(encoding); + return keys; + } + + computeSecret(key, inEnc, outEnc) { + inEnc = inEnc || cryptoUtil.DEFAULT_ENCODING; + outEnc = outEnc || cryptoUtil.DEFAULT_ENCODING; + var ret = this._handle.computeSecret(toBuf(key, inEnc)); + if (outEnc && outEnc !== 'buffer') + ret = ret.toString(outEnc); + return ret; + } + + getPrime(encoding) { + var prime = this._handle.getPrime(); + encoding = encoding || cryptoUtil.DEFAULT_ENCODING; + if (encoding && encoding !== 'buffer') + prime = prime.toString(encoding); + return prime; + } + + getGenerator(encoding) { + var generator = this._handle.getGenerator(); + encoding = encoding || cryptoUtil.DEFAULT_ENCODING; + if (encoding && encoding !== 'buffer') + generator = generator.toString(encoding); + return generator; + } + + getPublicKey(encoding) { + var key = this._handle.getPublicKey(); + encoding = encoding || cryptoUtil.DEFAULT_ENCODING; + if (encoding && encoding !== 'buffer') + key = key.toString(encoding); + return key; + } + + getPrivateKey(encoding) { + var key = this._handle.getPrivateKey(); + encoding = encoding || cryptoUtil.DEFAULT_ENCODING; + if (encoding && encoding !== 'buffer') + key = key.toString(encoding); + return key; + } +} + +class DiffieHellman extends DiffieHellmanBase { + constructor(sizeOrKey, keyEncoding, generator, genEncoding) { + if (!(sizeOrKey instanceof Buffer) && + 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 || cryptoUtil.DEFAULT_ENCODING; + genEncoding = genEncoding || cryptoUtil.DEFAULT_ENCODING; + + if (typeof sizeOrKey !== 'number') + sizeOrKey = toBuf(sizeOrKey, keyEncoding); + + if (!generator) + generator = kGenerator; + else if (typeof generator !== 'number') + generator = toBuf(generator, genEncoding); + + super(new binding.DiffieHellman(sizeOrKey, generator)); + } + + setPublicKey(key, encoding) { + encoding = encoding || cryptoUtil.DEFAULT_ENCODING; + this._handle.setPublicKey(toBuf(key, encoding)); + return this; + } + + setPrivateKey(key, encoding) { + encoding = encoding || cryptoUtil.DEFAULT_ENCODING; + this._handle.setPrivateKey(toBuf(key, encoding)); + return this; + } + + [inspect](depth, options) { + var obj = { + generator: this.getGenerator('hex'), + prime: this.getPrime('hex') + }; + if (this._options) + obj.options = this._options; + return `DiffieHellman ${util.inspect(obj, options)}`; + } +} + +class DiffieHellmanGroup extends DiffieHellmanBase { + constructor(name) { + super(new binding.DiffieHellmanGroup(name)); + this[kAlgorithm] = name; + } + + [inspect](depth, options) { + var obj = { + name: this[kAlgorithm], + generator: this.getGenerator('hex'), + prime: this.getPrime('hex') + }; + if (this._options) + obj.options = this._options; + return `DiffieHellmanGroup ${util.inspect(obj, options)}`; + } +} + +class ECDH { + constructor(curve) { + this._handle = new binding.ECDH(curve); + this[kAlgorithm] = curve; + } + + generateKeys(encoding, format) { + this._handle.generateKeys(); + return this.getPublicKey(encoding, format); + } + + getPublicKey(encoding, format) { + var f = constants.POINT_CONVERSION_UNCOMPRESSED; + if (format !== undefined) { + switch (format) { + case 'compressed': + f = constants.POINT_CONVERSION_COMPRESSED; + break; + case 'hybrid': + f = constants.POINT_CONVERSION_HYBRID; + break; + case 'uncompressed': + f = constants.POINT_CONVERSION_UNCOMPRESSED; + break; + default: + throw new TypeError(`Bad format: ${format}`); + } + } + var key = this._handle.getPublicKey(f); + encoding = encoding || cryptoUtil.DEFAULT_ENCODING; + if (encoding && encoding !== 'buffer') + key = key.toString(encoding); + return key; + } + + [inspect](depth, options) { + var obj = { + curve: this[kAlgorithm] + }; + if (this._options) + obj.options = this._options; + return `ECDH ${util.inspect(obj, options)}`; + } +} +ECDH.prototype.computeSecret = DiffieHellman.prototype.computeSecret; +ECDH.prototype.setPrivateKey = DiffieHellman.prototype.setPrivateKey; +ECDH.prototype.setPublicKey = DiffieHellman.prototype.setPublicKey; +ECDH.prototype.getPrivateKey = DiffieHellman.prototype.getPrivateKey; + +function createECDH(curve) { + return new ECDH(curve); +} + +module.exports = { + DiffieHellman, + DiffieHellmanGroup, + ECDH, + createDiffieHellman: internalUtil.createClassWrapper(DiffieHellman), + createDiffieHellmanGroup: internalUtil.createClassWrapper(DiffieHellmanGroup), + createECDH +}; diff --git a/lib/internal/crypto/hash.js b/lib/internal/crypto/hash.js new file mode 100644 index 000000000000..432adec28c85 --- /dev/null +++ b/lib/internal/crypto/hash.js @@ -0,0 +1,79 @@ +'use strict'; + +const binding = process.binding('crypto'); +const cryptoUtil = require('internal/crypto/util'); +const internalUtil = require('internal/util'); +const LazyTransform = require('internal/streams/lazy_transform'); +const util = require('util'); + +const inspect = internalUtil.customInspectSymbol; +const toBuf = cryptoUtil.toBuf; +const kAlgorithm = cryptoUtil.kAlgorithm; + +class HashBase extends LazyTransform { + constructor(handle, algorithm, options) { + super(options); + this._handle = handle; + this[kAlgorithm] = algorithm; + } + + _transform(chunk, encoding, callback) { + this._handle.update(chunk, encoding); + callback(); + } + + _flush(callback) { + this.push(this._handle.digest()); + callback(); + } + + update(data, encoding) { + encoding = encoding || cryptoUtil.DEFAULT_ENCODING; + this._handle.update(data, encoding); + return this; + } + + digest(outputEncoding) { + outputEncoding = outputEncoding || cryptoUtil.DEFAULT_ENCODING; + return this._handle.digest(outputEncoding); + } +} + +class Hash extends HashBase { + constructor(algorithm, options) { + super(new binding.Hash(algorithm), algorithm, options); + } + + [inspect](depth, options) { + var obj = { + algorithm: this[kAlgorithm] + }; + if (this._options) + obj.options = this._options; + return `Hash ${util.inspect(obj, options)}`; + } +} + +class Hmac extends HashBase { + constructor(hmac, key, options) { + const handle = new binding.Hmac(); + handle.init(hmac, toBuf(key)); + super(handle, hmac, options); + } + + [inspect](depth, options) { + var obj = { + hmac: this[kAlgorithm] + }; + if (this._options) + obj.options = this._options; + return `Hmac ${util.inspect(obj, options)}`; + } +} + +module.exports = { + Hash, + Hmac, + createHash: internalUtil.createClassWrapper(Hash), + createHmac: internalUtil.createClassWrapper(Hmac) +}; diff --git a/lib/internal/crypto/sign.js b/lib/internal/crypto/sign.js new file mode 100644 index 000000000000..aa35507b7b71 --- /dev/null +++ b/lib/internal/crypto/sign.js @@ -0,0 +1,104 @@ +'use strict'; + +const binding = process.binding('crypto'); +const cryptoUtil = require('internal/crypto/util'); +const internalUtil = require('internal/util'); +const Writable = require('stream').Writable; +const util = require('util'); + +const inspect = internalUtil.customInspectSymbol; +const toBuf = cryptoUtil.toBuf; +const kAlgorithm = cryptoUtil.kAlgorithm; + +class SignBase extends Writable { + constructor(handle, options) { + super(options); + this._handle = handle; + } + + _write(chunk, encoding, callback) { + this._handle.update(chunk, encoding); + callback(); + } + + update(data, encoding) { + encoding = encoding || cryptoUtil.DEFAULT_ENCODING; + this._handle.update(data, encoding); + return this; + } +} + +class Sign extends SignBase { + constructor(algorithm, options) { + const handle = new binding.Sign(); + handle.init(algorithm); + super(handle, options); + this[kAlgorithm] = algorithm; + } + + sign(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 || cryptoUtil.DEFAULT_ENCODING; + if (encoding && encoding !== 'buffer') + ret = ret.toString(encoding); + + return ret; + } + + [inspect](depth, options) { + var obj = { + algorithm: this[kAlgorithm], + _writableState: this._writableState, + writable: this.writable, + domain: this.domain, + _events: this._events, + _eventsCount: this._eventsCount, + _maxListeners: this._maxListeners + }; + if (this._options) + obj.options = this._options; + return `Sign ${util.inspect(obj, options)}`; + } +} + +class Verify extends SignBase { + constructor(algorithm, options) { + const handle = new binding.Verify(); + handle.init(algorithm); + super(handle, options); + this[kAlgorithm] = algorithm; + } + + verify(object, signature, sigEncoding) { + sigEncoding = sigEncoding || cryptoUtil.DEFAULT_ENCODING; + return this._handle.verify(toBuf(object), toBuf(signature, sigEncoding)); + } + + [inspect](depth, options) { + var obj = { + algorithm: this[kAlgorithm], + _writableState: this._writableState, + writable: this.writable, + domain: this.domain, + _events: this._events, + _eventsCount: this._eventsCount, + _maxListeners: this._maxListeners + }; + if (this._options) + obj.options = this._options; + return `Verify ${util.inspect(obj, options)}`; + } +} + +module.exports = { + Sign, + Verify, + createSign: internalUtil.createClassWrapper(Sign), + createVerify: internalUtil.createClassWrapper(Verify) +}; diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js new file mode 100644 index 000000000000..0cab62965760 --- /dev/null +++ b/lib/internal/crypto/util.js @@ -0,0 +1,18 @@ +'use strict'; + +const Buffer = require('buffer').Buffer; + +function toBuf(str, encoding) { + if (typeof str === 'string') { + if (encoding === 'buffer' || !encoding) + encoding = 'utf8'; + return Buffer.from(str, encoding); + } + return str; +} + +module.exports = { + toBuf, + DEFAULT_ENCODING: 'buffer', + kAlgorithm: Symbol('algorithm') +}; diff --git a/node.gyp b/node.gyp index 0d5b6c27ef60..cb51b6476041 100644 --- a/node.gyp +++ b/node.gyp @@ -83,6 +83,12 @@ 'lib/internal/cluster/utils.js', 'lib/internal/cluster/worker.js', 'lib/internal/errors.js', + 'lib/internal/crypto/certificate.js', + 'lib/internal/crypto/cipher.js', + 'lib/internal/crypto/diffiehellman.js', + 'lib/internal/crypto/hash.js', + 'lib/internal/crypto/sign.js', + 'lib/internal/crypto/util.js', 'lib/internal/freelist.js', 'lib/internal/fs.js', 'lib/internal/linkedlist.js', diff --git a/test/parallel/test-crypto-dh.js b/test/parallel/test-crypto-dh.js index b95863a13ff9..1c0d546249a9 100644 --- a/test/parallel/test-crypto-dh.js +++ b/test/parallel/test-crypto-dh.js @@ -285,4 +285,4 @@ ecdh5.setPrivateKey(cafebabeKey, 'hex'); // invalid test: curve argument is undefined assert.throws(() => { crypto.createECDH(); -}, /^TypeError: "curve" argument should be a string$/); +}, /^TypeError: ECDH curve name must be a string$/);