summaryrefslogtreecommitdiff
path: root/lib/internal/crypto
diff options
context:
space:
mode:
authorJames M Snell <jasnell@gmail.com>2017-09-06 08:10:34 -0700
committerJames M Snell <jasnell@gmail.com>2017-09-18 08:10:59 -0700
commitc75f87cc4c8d3699e081d37bb5bf47a70d830fdb (patch)
tree9d79319f568ff43e36e05a8d2634130adfacfb74 /lib/internal/crypto
parent8fa5fcc0ba74c23490c34da1a6c6e9a454280740 (diff)
downloadandroid-node-v8-c75f87cc4c8d3699e081d37bb5bf47a70d830fdb.tar.gz
android-node-v8-c75f87cc4c8d3699e081d37bb5bf47a70d830fdb.tar.bz2
android-node-v8-c75f87cc4c8d3699e081d37bb5bf47a70d830fdb.zip
crypto: refactor the crypto module
* Split single monolithic file into multiple * Make Certificate methods static * Allow randomFill(Sync) to use any ArrayBufferView * Use internal/errors throughout * Improve arg validation in Hash/Hmac * Doc updates PR-URL: https://github.com/nodejs/node/pull/15231 Reviewed-By: Michaƫl Zasso <targos@protonmail.com> Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
Diffstat (limited to 'lib/internal/crypto')
-rw-r--r--lib/internal/crypto/certificate.js40
-rw-r--r--lib/internal/crypto/cipher.js214
-rw-r--r--lib/internal/crypto/diffiehellman.js216
-rw-r--r--lib/internal/crypto/hash.js125
-rw-r--r--lib/internal/crypto/pbkdf2.js59
-rw-r--r--lib/internal/crypto/random.js98
-rw-r--r--lib/internal/crypto/sig.js131
-rw-r--r--lib/internal/crypto/util.js70
8 files changed, 953 insertions, 0 deletions
diff --git a/lib/internal/crypto/certificate.js b/lib/internal/crypto/certificate.js
new file mode 100644
index 0000000000..e37bedd2f9
--- /dev/null
+++ b/lib/internal/crypto/certificate.js
@@ -0,0 +1,40 @@
+'use strict';
+
+const {
+ certExportChallenge,
+ certExportPublicKey,
+ certVerifySpkac
+} = process.binding('crypto');
+
+const {
+ toBuf
+} = require('internal/crypto/util');
+
+function verifySpkac(object) {
+ return certVerifySpkac(object);
+}
+
+function exportPublicKey(object, encoding) {
+ return certExportPublicKey(toBuf(object, encoding));
+}
+
+function exportChallenge(object, encoding) {
+ return certExportChallenge(toBuf(object, encoding));
+}
+
+// For backwards compatibility reasons, this cannot be converted into a
+// ES6 Class.
+function Certificate() {
+ if (!(this instanceof Certificate))
+ return new Certificate();
+}
+
+Certificate.prototype.verifySpkac = verifySpkac;
+Certificate.prototype.exportPublicKey = exportPublicKey;
+Certificate.prototype.exportChallenge = exportChallenge;
+
+Certificate.exportChallenge = exportChallenge;
+Certificate.exportPublicKey = exportPublicKey;
+Certificate.verifySpkac = verifySpkac;
+
+module.exports = Certificate;
diff --git a/lib/internal/crypto/cipher.js b/lib/internal/crypto/cipher.js
new file mode 100644
index 0000000000..d9b31674c1
--- /dev/null
+++ b/lib/internal/crypto/cipher.js
@@ -0,0 +1,214 @@
+'use strict';
+
+const {
+ RSA_PKCS1_OAEP_PADDING,
+ RSA_PKCS1_PADDING
+} = process.binding('constants').crypto;
+
+const {
+ getDefaultEncoding,
+ toBuf
+} = require('internal/crypto/util');
+
+const {
+ CipherBase,
+ privateDecrypt: _privateDecrypt,
+ privateEncrypt: _privateEncrypt,
+ publicDecrypt: _publicDecrypt,
+ publicEncrypt: _publicEncrypt
+} = process.binding('crypto');
+
+const assert = require('assert');
+const LazyTransform = require('internal/streams/lazy_transform');
+const { StringDecoder } = require('string_decoder');
+
+const { inherits } = require('util');
+const { normalizeEncoding } = require('internal/util');
+
+function rsaPublic(method, defaultPadding) {
+ return function(options, buffer) {
+ const key = options.key || options;
+ const padding = options.padding || defaultPadding;
+ const passphrase = options.passphrase || null;
+ return method(toBuf(key), buffer, padding, passphrase);
+ };
+}
+
+function rsaPrivate(method, defaultPadding) {
+ return function(options, buffer) {
+ const key = options.key || options;
+ const passphrase = options.passphrase || null;
+ const padding = options.padding || defaultPadding;
+ return method(toBuf(key), buffer, padding, passphrase);
+ };
+}
+
+const publicEncrypt = rsaPublic(_publicEncrypt, RSA_PKCS1_OAEP_PADDING);
+const publicDecrypt = rsaPublic(_publicDecrypt, RSA_PKCS1_PADDING);
+const privateEncrypt = rsaPrivate(_privateEncrypt, RSA_PKCS1_PADDING);
+const privateDecrypt = rsaPrivate(_privateDecrypt, RSA_PKCS1_OAEP_PADDING);
+
+function getDecoder(decoder, encoding) {
+ encoding = normalizeEncoding(encoding);
+ decoder = decoder || new StringDecoder(encoding);
+ assert(decoder.encoding === encoding, 'Cannot change encoding');
+ return decoder;
+}
+
+function Cipher(cipher, password, options) {
+ if (!(this instanceof Cipher))
+ return new Cipher(cipher, password, options);
+ this._handle = new CipherBase(true);
+
+ this._handle.init(cipher, toBuf(password));
+ this._decoder = null;
+
+ LazyTransform.call(this, options);
+}
+
+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) {
+ const encoding = getDefaultEncoding();
+ inputEncoding = inputEncoding || encoding;
+ outputEncoding = outputEncoding || 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 || getDefaultEncoding();
+ 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;
+};
+
+function Cipheriv(cipher, key, iv, options) {
+ if (!(this instanceof Cipheriv))
+ return new Cipheriv(cipher, key, iv, options);
+ this._handle = new CipherBase(true);
+ this._handle.initiv(cipher, toBuf(key), toBuf(iv));
+ this._decoder = null;
+
+ LazyTransform.call(this, options);
+}
+
+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;
+
+
+function Decipher(cipher, password, options) {
+ if (!(this instanceof Decipher))
+ return new Decipher(cipher, password, options);
+
+ this._handle = new CipherBase(false);
+ this._handle.init(cipher, toBuf(password));
+ this._decoder = null;
+
+ LazyTransform.call(this, options);
+}
+
+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;
+
+
+function Decipheriv(cipher, key, iv, options) {
+ if (!(this instanceof Decipheriv))
+ return new Decipheriv(cipher, key, iv, options);
+
+ this._handle = new CipherBase(false);
+ this._handle.initiv(cipher, toBuf(key), toBuf(iv));
+ this._decoder = null;
+
+ LazyTransform.call(this, options);
+}
+
+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;
+
+
+module.exports = {
+ Cipher,
+ Cipheriv,
+ Decipher,
+ Decipheriv,
+ privateDecrypt,
+ privateEncrypt,
+ publicDecrypt,
+ publicEncrypt,
+};
diff --git a/lib/internal/crypto/diffiehellman.js b/lib/internal/crypto/diffiehellman.js
new file mode 100644
index 0000000000..b891a0b354
--- /dev/null
+++ b/lib/internal/crypto/diffiehellman.js
@@ -0,0 +1,216 @@
+'use strict';
+
+const { Buffer } = require('buffer');
+const errors = require('internal/errors');
+const {
+ getDefaultEncoding,
+ toBuf
+} = require('internal/crypto/util');
+const {
+ DiffieHellman: _DiffieHellman,
+ DiffieHellmanGroup: _DiffieHellmanGroup,
+ ECDH: _ECDH
+} = process.binding('crypto');
+const {
+ POINT_CONVERSION_COMPRESSED,
+ POINT_CONVERSION_HYBRID,
+ POINT_CONVERSION_UNCOMPRESSED
+} = process.binding('constants').crypto;
+
+const DH_GENERATOR = 2;
+
+function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) {
+ if (!(this instanceof DiffieHellman))
+ return new DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding);
+
+ if (typeof sizeOrKey !== 'number' &&
+ typeof sizeOrKey !== 'string' &&
+ !ArrayBuffer.isView(sizeOrKey)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'sizeOrKey',
+ ['number', 'string', 'Buffer', 'TypedArray',
+ 'DataView']);
+ }
+
+ if (keyEncoding) {
+ if (typeof keyEncoding !== 'string' ||
+ (!Buffer.isEncoding(keyEncoding) && keyEncoding !== 'buffer')) {
+ genEncoding = generator;
+ generator = keyEncoding;
+ keyEncoding = false;
+ }
+ }
+
+ const encoding = getDefaultEncoding();
+ keyEncoding = keyEncoding || encoding;
+ genEncoding = genEncoding || 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 _DiffieHellman(sizeOrKey, generator);
+ Object.defineProperty(this, 'verifyError', {
+ enumerable: true,
+ value: this._handle.verifyError,
+ writable: false
+ });
+}
+
+
+function DiffieHellmanGroup(name) {
+ if (!(this instanceof DiffieHellmanGroup))
+ return new DiffieHellmanGroup(name);
+ this._handle = new _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 || getDefaultEncoding();
+ if (encoding && encoding !== 'buffer')
+ keys = keys.toString(encoding);
+ return keys;
+}
+
+
+DiffieHellmanGroup.prototype.computeSecret =
+ DiffieHellman.prototype.computeSecret =
+ dhComputeSecret;
+
+function dhComputeSecret(key, inEnc, outEnc) {
+ const encoding = getDefaultEncoding();
+ inEnc = inEnc || encoding;
+ outEnc = outEnc || 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 || getDefaultEncoding();
+ 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 || getDefaultEncoding();
+ 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 || getDefaultEncoding();
+ 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 || getDefaultEncoding();
+ if (encoding && encoding !== 'buffer')
+ key = key.toString(encoding);
+ return key;
+}
+
+
+DiffieHellman.prototype.setPublicKey = function setPublicKey(key, encoding) {
+ encoding = encoding || getDefaultEncoding();
+ this._handle.setPublicKey(toBuf(key, encoding));
+ return this;
+};
+
+
+DiffieHellman.prototype.setPrivateKey = function setPrivateKey(key, encoding) {
+ encoding = encoding || getDefaultEncoding();
+ this._handle.setPrivateKey(toBuf(key, encoding));
+ return this;
+};
+
+
+function ECDH(curve) {
+ if (typeof curve !== 'string')
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'curve', 'string');
+
+ this._handle = 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 = POINT_CONVERSION_COMPRESSED;
+ else if (format === 'hybrid')
+ f = POINT_CONVERSION_HYBRID;
+ // Default
+ else if (format === 'uncompressed')
+ f = POINT_CONVERSION_UNCOMPRESSED;
+ else
+ throw new errors.TypeError('ERR_CRYPTO_ECDH_INVALID_FORMAT', format);
+ } else {
+ f = POINT_CONVERSION_UNCOMPRESSED;
+ }
+ var key = this._handle.getPublicKey(f);
+ encoding = encoding || getDefaultEncoding();
+ if (encoding && encoding !== 'buffer')
+ key = key.toString(encoding);
+ return key;
+};
+
+module.exports = {
+ DiffieHellman,
+ DiffieHellmanGroup,
+ ECDH
+};
diff --git a/lib/internal/crypto/hash.js b/lib/internal/crypto/hash.js
new file mode 100644
index 0000000000..12b3e1e78e
--- /dev/null
+++ b/lib/internal/crypto/hash.js
@@ -0,0 +1,125 @@
+'use strict';
+
+const {
+ Hash: _Hash,
+ Hmac: _Hmac
+} = process.binding('crypto');
+
+const {
+ getDefaultEncoding,
+ toBuf
+} = require('internal/crypto/util');
+
+const {
+ isArrayBufferView
+} = process.binding('util');
+
+const { Buffer } = require('buffer');
+
+const errors = require('internal/errors');
+const { inherits } = require('util');
+const { normalizeEncoding } = require('internal/util');
+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 errors.TypeError('ERR_INVALID_ARG_TYPE', 'algorithm', 'string');
+ 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 errors.Error('ERR_CRYPTO_HASH_FINALIZED');
+
+ if (typeof data !== 'string' && !isArrayBufferView(data)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'data',
+ ['string', 'TypedArray', 'DataView']);
+ }
+
+ if (!this._handle.update(data, encoding || getDefaultEncoding()))
+ throw new errors.Error('ERR_CRYPTO_HASH_UPDATE_FAILED');
+ return this;
+};
+
+
+Hash.prototype.digest = function digest(outputEncoding) {
+ const state = this[kState];
+ if (state[kFinalized])
+ throw new errors.Error('ERR_CRYPTO_HASH_FINALIZED');
+ outputEncoding = outputEncoding || getDefaultEncoding();
+ if (normalizeEncoding(outputEncoding) === 'utf16le')
+ throw new errors.Error('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 errors.TypeError('ERR_INVALID_ARG_TYPE', 'hmac', 'string');
+ if (typeof key !== 'string' && !isArrayBufferView(key)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'key',
+ ['string', 'TypedArray', 'DataView']);
+ }
+ 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 errors.Error('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
+};
diff --git a/lib/internal/crypto/pbkdf2.js b/lib/internal/crypto/pbkdf2.js
new file mode 100644
index 0000000000..5398321ece
--- /dev/null
+++ b/lib/internal/crypto/pbkdf2.js
@@ -0,0 +1,59 @@
+'use strict';
+
+const errors = require('internal/errors');
+const {
+ getDefaultEncoding,
+ toBuf
+} = require('internal/crypto/util');
+const {
+ PBKDF2
+} = process.binding('crypto');
+
+function pbkdf2(password, salt, iterations, keylen, digest, callback) {
+ if (typeof digest === 'function') {
+ callback = digest;
+ digest = undefined;
+ }
+
+ if (typeof callback !== 'function')
+ throw new errors.TypeError('ERR_INVALID_CALLBACK');
+
+ 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) {
+
+ if (digest !== null && typeof digest !== 'string')
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'digest',
+ ['string', 'null']);
+
+ password = toBuf(password);
+ salt = toBuf(salt);
+
+ const encoding = getDefaultEncoding();
+
+ if (encoding === 'buffer')
+ return PBKDF2(password, salt, iterations, keylen, digest, callback);
+
+ // at this point, we need to handle encodings.
+ if (callback) {
+ function next(er, ret) {
+ if (ret)
+ ret = ret.toString(encoding);
+ callback(er, ret);
+ }
+ PBKDF2(password, salt, iterations, keylen, digest, next);
+ } else {
+ var ret = PBKDF2(password, salt, iterations, keylen, digest);
+ return ret.toString(encoding);
+ }
+}
+
+module.exports = {
+ pbkdf2,
+ pbkdf2Sync
+};
diff --git a/lib/internal/crypto/random.js b/lib/internal/crypto/random.js
new file mode 100644
index 0000000000..81025289d5
--- /dev/null
+++ b/lib/internal/crypto/random.js
@@ -0,0 +1,98 @@
+'use strict';
+
+const errors = require('internal/errors');
+const { isArrayBufferView } = process.binding('util');
+const {
+ randomBytes,
+ randomFill: _randomFill
+} = process.binding('crypto');
+
+const { kMaxLength } = require('buffer');
+const kMaxUint32 = Math.pow(2, 32) - 1;
+
+function assertOffset(offset, length) {
+ if (typeof offset !== 'number' || offset !== offset) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'offset', 'number');
+ }
+
+ if (offset > kMaxUint32 || offset < 0) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'offset', 'uint32');
+ }
+
+ if (offset > kMaxLength || offset > length) {
+ throw new errors.RangeError('ERR_OUT_OF_RANGE', 'offset');
+ }
+}
+
+function assertSize(size, offset, length) {
+ if (typeof size !== 'number' || size !== size) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'size', 'number');
+ }
+
+ if (size > kMaxUint32 || size < 0) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'size', 'uint32');
+ }
+
+ if (size + offset > length || size > kMaxLength) {
+ throw new errors.RangeError('ERR_OUT_OF_RANGE', 'size');
+ }
+}
+
+function randomFillSync(buf, offset = 0, size) {
+ if (!isArrayBufferView(buf)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
+ 'buf', 'ArrayBufferView');
+ }
+
+ const elementSize = buf.BYTES_PER_ELEMENT || 1;
+
+ offset *= elementSize;
+ assertOffset(offset, buf.byteLength);
+
+ if (size === undefined) {
+ size = buf.byteLength - offset;
+ } else {
+ size *= elementSize;
+ }
+
+ assertSize(size, offset, buf.byteLength);
+
+ return _randomFill(buf, offset, size);
+}
+
+function randomFill(buf, offset, size, cb) {
+ if (!isArrayBufferView(buf)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
+ 'buf', 'ArrayBufferView');
+ }
+
+ const elementSize = buf.BYTES_PER_ELEMENT || 1;
+
+ if (typeof offset === 'function') {
+ cb = offset;
+ offset = 0;
+ size = buf.bytesLength;
+ } else if (typeof size === 'function') {
+ cb = size;
+ offset *= elementSize;
+ size = buf.byteLength - offset;
+ } else if (typeof cb !== 'function') {
+ throw new errors.TypeError('ERR_INVALID_CALLBACK');
+ }
+ if (size === undefined) {
+ size = buf.byteLength - offset;
+ } else {
+ size *= elementSize;
+ }
+
+ assertOffset(offset, buf.byteLength);
+ assertSize(size, offset, buf.byteLength);
+
+ return _randomFill(buf, offset, size, cb);
+}
+
+module.exports = {
+ randomBytes,
+ randomFill,
+ randomFillSync
+};
diff --git a/lib/internal/crypto/sig.js b/lib/internal/crypto/sig.js
new file mode 100644
index 0000000000..52827c9c4b
--- /dev/null
+++ b/lib/internal/crypto/sig.js
@@ -0,0 +1,131 @@
+'use strict';
+
+const errors = require('internal/errors');
+const {
+ Sign: _Sign,
+ Verify: _Verify
+} = process.binding('crypto');
+const {
+ RSA_PSS_SALTLEN_AUTO,
+ RSA_PKCS1_PADDING
+} = process.binding('constants').crypto;
+const {
+ getDefaultEncoding,
+ toBuf
+} = require('internal/crypto/util');
+const { Writable } = require('stream');
+const { inherits } = require('util');
+
+function Sign(algorithm, options) {
+ if (!(this instanceof Sign))
+ return new Sign(algorithm, options);
+ this._handle = new _Sign();
+ this._handle.init(algorithm);
+
+ Writable.call(this, options);
+}
+
+inherits(Sign, Writable);
+
+Sign.prototype._write = function _write(chunk, encoding, callback) {
+ this._handle.update(chunk, encoding);
+ callback();
+};
+
+Sign.prototype.update = function update(data, encoding) {
+ encoding = encoding || getDefaultEncoding();
+ this._handle.update(data, encoding);
+ return this;
+};
+
+Sign.prototype.sign = function sign(options, encoding) {
+ if (!options)
+ throw new errors.Error('ERR_CRYPTO_SIGN_KEY_REQUIRED');
+
+ var key = options.key || options;
+ var passphrase = options.passphrase || null;
+
+ // Options specific to RSA
+ var rsaPadding = RSA_PKCS1_PADDING;
+ if (options.hasOwnProperty('padding')) {
+ if (options.padding === options.padding >> 0) {
+ rsaPadding = options.padding;
+ } else {
+ throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
+ 'padding',
+ options.padding);
+ }
+ }
+
+ var pssSaltLength = RSA_PSS_SALTLEN_AUTO;
+ if (options.hasOwnProperty('saltLength')) {
+ if (options.saltLength === options.saltLength >> 0) {
+ pssSaltLength = options.saltLength;
+ } else {
+ throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
+ 'saltLength',
+ options.saltLength);
+ }
+ }
+
+ var ret = this._handle.sign(toBuf(key), passphrase, rsaPadding,
+ pssSaltLength);
+
+ encoding = encoding || getDefaultEncoding();
+ if (encoding && encoding !== 'buffer')
+ ret = ret.toString(encoding);
+
+ return ret;
+};
+
+
+function Verify(algorithm, options) {
+ if (!(this instanceof Verify))
+ return new Verify(algorithm, options);
+
+ this._handle = new _Verify();
+ this._handle.init(algorithm);
+
+ Writable.call(this, options);
+}
+
+inherits(Verify, Writable);
+
+Verify.prototype._write = Sign.prototype._write;
+Verify.prototype.update = Sign.prototype.update;
+
+Verify.prototype.verify = function verify(options, signature, sigEncoding) {
+ var key = options.key || options;
+ sigEncoding = sigEncoding || getDefaultEncoding();
+
+ // Options specific to RSA
+ var rsaPadding = RSA_PKCS1_PADDING;
+ if (options.hasOwnProperty('padding')) {
+ if (options.padding === options.padding >> 0) {
+ rsaPadding = options.padding;
+ } else {
+ throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
+ 'padding',
+ options.padding);
+ }
+ }
+
+ var pssSaltLength = RSA_PSS_SALTLEN_AUTO;
+ if (options.hasOwnProperty('saltLength')) {
+ if (options.saltLength === options.saltLength >> 0) {
+ pssSaltLength = options.saltLength;
+ } else {
+ throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
+ 'saltLength',
+ options.saltLength);
+ }
+ }
+
+ return this._handle.verify(toBuf(key), toBuf(signature, sigEncoding),
+ rsaPadding, pssSaltLength);
+};
+
+module.exports = {
+ Sign,
+ Verify
+};
diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js
new file mode 100644
index 0000000000..9e242dc917
--- /dev/null
+++ b/lib/internal/crypto/util.js
@@ -0,0 +1,70 @@
+'use strict';
+
+const {
+ getCiphers: _getCiphers,
+ getCurves: _getCurves,
+ getHashes: _getHashes,
+ setEngine: _setEngine
+} = process.binding('crypto');
+
+const {
+ ENGINE_METHOD_ALL
+} = process.binding('constants').crypto;
+
+const errors = require('internal/errors');
+const { Buffer } = require('buffer');
+const {
+ cachedResult,
+ filterDuplicateStrings
+} = require('internal/util');
+
+var defaultEncoding = 'buffer';
+
+function setDefaultEncoding(val) {
+ defaultEncoding = val;
+}
+
+function getDefaultEncoding() {
+ return defaultEncoding;
+}
+
+// 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;
+}
+
+const getCiphers = cachedResult(() => filterDuplicateStrings(_getCiphers()));
+const getHashes = cachedResult(() => filterDuplicateStrings(_getHashes()));
+const getCurves = cachedResult(() => filterDuplicateStrings(_getCurves()));
+
+function setEngine(id, flags) {
+ if (typeof id !== 'string')
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'id', 'string');
+
+ if (flags && typeof flags !== 'number')
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'flags', 'number');
+ flags = flags >>> 0;
+
+ // Use provided engine for everything by default
+ if (flags === 0)
+ flags = ENGINE_METHOD_ALL;
+
+ return _setEngine(id, flags);
+}
+
+module.exports = {
+ getCiphers,
+ getCurves,
+ getDefaultEncoding,
+ getHashes,
+ setDefaultEncoding,
+ setEngine,
+ toBuf
+};