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>
This commit is contained in:
James M Snell 2017-09-06 08:10:34 -07:00
parent 8fa5fcc0ba
commit c75f87cc4c
25 changed files with 1690 additions and 1008 deletions

View File

@ -48,7 +48,60 @@ The `crypto` module provides the `Certificate` class for working with SPKAC
data. The most common usage is handling output generated by the HTML5 data. The most common usage is handling output generated by the HTML5
`<keygen>` element. Node.js uses [OpenSSL's SPKAC implementation][] internally. `<keygen>` element. Node.js uses [OpenSSL's SPKAC implementation][] internally.
### new crypto.Certificate() ### Certificate.exportChallenge(spkac)
<!-- YAML
added: REPLACEME
-->
- `spkac` {string | Buffer | TypedArray | DataView}
- Returns {Buffer} The challenge component of the `spkac` data structure, which
includes a public key and a challenge.
```js
const { Certificate } = require('crypto');
const spkac = getSpkacSomehow();
const challenge = Certificate.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// Prints: the challenge as a UTF8 string
```
### Certificate.exportPublicKey(spkac)
<!-- YAML
added: REPLACEME
-->
- `spkac` {string | Buffer | TypedArray | DataView}
- Returns {Buffer} The public key component of the `spkac` data structure,
which includes a public key and a challenge.
```js
const { Certificate } = require('crypto');
const spkac = getSpkacSomehow();
const publicKey = Certificate.exportPublicKey(spkac);
console.log(publicKey);
// Prints: the public key as <Buffer ...>
```
### Certificate.verifySpkac(spkac)
<!-- YAML
added: REPLACEME
-->
- `spkac` {Buffer | TypedArray | DataView}
- Returns {boolean} `true` if the given `spkac` data structure is valid, `false`
otherwise.
```js
const { Certificate } = require('crypto');
const spkac = getSpkacSomehow();
console.log(Certificate.verifySpkac(Buffer.from(spkac)));
// Prints: true or false
```
### Legacy API
As a still supported legacy interface, it is possible (but not recommended) to
create new instances of the `crypto.Certificate` class as illustrated in the
examples below.
#### new crypto.Certificate()
Instances of the `Certificate` class can be created using the `new` keyword Instances of the `Certificate` class can be created using the `new` keyword
or by calling `crypto.Certificate()` as a function: or by calling `crypto.Certificate()` as a function:
@ -60,7 +113,7 @@ const cert1 = new crypto.Certificate();
const cert2 = crypto.Certificate(); const cert2 = crypto.Certificate();
``` ```
### certificate.exportChallenge(spkac) #### certificate.exportChallenge(spkac)
<!-- YAML <!-- YAML
added: v0.11.8 added: v0.11.8
--> -->
@ -76,7 +129,7 @@ console.log(challenge.toString('utf8'));
// Prints: the challenge as a UTF8 string // Prints: the challenge as a UTF8 string
``` ```
### certificate.exportPublicKey(spkac) #### certificate.exportPublicKey(spkac)
<!-- YAML <!-- YAML
added: v0.11.8 added: v0.11.8
--> -->
@ -92,7 +145,7 @@ console.log(publicKey);
// Prints: the public key as <Buffer ...> // Prints: the public key as <Buffer ...>
``` ```
### certificate.verifySpkac(spkac) #### certificate.verifySpkac(spkac)
<!-- YAML <!-- YAML
added: v0.11.8 added: v0.11.8
--> -->
@ -1747,9 +1800,13 @@ negative performance implications for some applications, see the
### crypto.randomFillSync(buffer[, offset][, size]) ### crypto.randomFillSync(buffer[, offset][, size])
<!-- YAML <!-- YAML
added: v7.10.0 added: v7.10.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/15231
description: The `buffer` argument may be any ArrayBufferView
--> -->
* `buffer` {Buffer|Uint8Array} Must be supplied. * `buffer` {Buffer|Uint8Array|ArrayBufferView} Must be supplied.
* `offset` {number} Defaults to `0`. * `offset` {number} Defaults to `0`.
* `size` {number} Defaults to `buffer.length - offset`. * `size` {number} Defaults to `buffer.length - offset`.
@ -1769,12 +1826,29 @@ crypto.randomFillSync(buf, 5, 5);
console.log(buf.toString('hex')); console.log(buf.toString('hex'));
``` ```
Any `TypedArray` or `DataView` instance may be passed as `buffer`.
```js
const a = new Uint32Array(10);
console.log(crypto.randomFillSync(a).toString('hex'));
const b = new Float64Array(10);
console.log(crypto.randomFillSync(a).toString('hex'));
const c = new DataView(new ArrayBuffer(10));
console.log(crypto.randomFillSync(a).toString('hex'));
```
### crypto.randomFill(buffer[, offset][, size], callback) ### crypto.randomFill(buffer[, offset][, size], callback)
<!-- YAML <!-- YAML
added: v7.10.0 added: v7.10.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/15231
description: The `buffer` argument may be any ArrayBufferView
--> -->
* `buffer` {Buffer|Uint8Array} Must be supplied. * `buffer` {Buffer|Uint8Array|ArrayBufferView} Must be supplied.
* `offset` {number} Defaults to `0`. * `offset` {number} Defaults to `0`.
* `size` {number} Defaults to `buffer.length - offset`. * `size` {number} Defaults to `buffer.length - offset`.
* `callback` {Function} `function(err, buf) {}`. * `callback` {Function} `function(err, buf) {}`.
@ -1804,6 +1878,28 @@ crypto.randomFill(buf, 5, 5, (err, buf) => {
}); });
``` ```
Any `TypedArray` or `DataView` instance may be passed as `buffer`.
```js
const a = new Uint32Array(10);
crypto.randomFill(a, (err, buf) => {
if (err) throw err;
console.log(buf.toString('hex'));
});
const b = new Float64Array(10);
crypto.randomFill(b, (err, buf) => {
if (err) throw err;
console.log(buf.toString('hex'));
});
const c = new DataView(new ArrayBuffer(10));
crypto.randomFill(c, (err, buf) => {
if (err) throw err;
console.log(buf.toString('hex'));
});
```
Note that this API uses libuv's threadpool, which can have surprising and Note that this API uses libuv's threadpool, which can have surprising and
negative performance implications for some applications, see the negative performance implications for some applications, see the
[`UV_THREADPOOL_SIZE`][] documentation for more information. [`UV_THREADPOOL_SIZE`][] documentation for more information.

View File

@ -621,6 +621,12 @@ Used when `Console` is instantiated without `stdout` stream or when `stdout` or
Used when the native call from `process.cpuUsage` cannot be processed properly. Used when the native call from `process.cpuUsage` cannot be processed properly.
<a id="ERR_CRYPTO_ECDH_INVALID_FORMAT"></a>
### ERR_CRYPTO_ECDH_INVALID_FORMAT
Used when an invalid value for the `format` argument has been passed to the
`crypto.ECDH()` class `getPublicKey()` method.
<a id="ERR_DNS_SET_SERVERS_FAILED"></a> <a id="ERR_DNS_SET_SERVERS_FAILED"></a>
### ERR_DNS_SET_SERVERS_FAILED ### ERR_DNS_SET_SERVERS_FAILED

View File

@ -24,786 +24,144 @@
'use strict'; 'use strict';
const internalUtil = require('internal/util'); const {
internalUtil.assertCrypto(); assertCrypto,
deprecate
exports.DEFAULT_ENCODING = 'buffer'; } = require('internal/util');
assertCrypto();
const constants = process.binding('constants').crypto; const constants = process.binding('constants').crypto;
const binding = process.binding('crypto'); const {
const randomBytes = binding.randomBytes; getFipsCrypto,
const getCiphers = binding.getCiphers; setFipsCrypto,
const getHashes = binding.getHashes; timingSafeEqual
const getCurves = binding.getCurves; } = process.binding('crypto');
const getFipsCrypto = binding.getFipsCrypto; const {
const setFipsCrypto = binding.setFipsCrypto; randomBytes,
const timingSafeEqual = binding.timingSafeEqual; randomFill,
randomFillSync
const Buffer = require('buffer').Buffer; } = require('internal/crypto/random');
const kBufferMaxLength = require('buffer').kMaxLength; const {
const stream = require('stream'); pbkdf2,
const util = require('util'); pbkdf2Sync
const { isUint8Array } = process.binding('util'); } = require('internal/crypto/pbkdf2');
const LazyTransform = require('internal/streams/lazy_transform'); const {
DiffieHellman,
const DH_GENERATOR = 2; DiffieHellmanGroup,
ECDH
Object.defineProperty(exports, 'constants', { } = require('internal/crypto/diffiehellman');
configurable: false, const {
enumerable: true, Cipher,
value: constants Cipheriv,
}); Decipher,
Decipheriv,
// This is here because many functions accepted binary strings without privateDecrypt,
// any explicit encoding in older versions of node, and we don't want privateEncrypt,
// to break them unnecessarily. publicDecrypt,
function toBuf(str, encoding) { publicEncrypt
if (typeof str === 'string') { } = require('internal/crypto/cipher');
if (encoding === 'buffer' || !encoding) const {
encoding = 'utf8'; Sign,
return Buffer.from(str, encoding); Verify
} } = require('internal/crypto/sig');
return str; const {
} Hash,
exports._toBuf = toBuf; Hmac
} = require('internal/crypto/hash');
const {
const assert = require('assert'); getCiphers,
const StringDecoder = require('string_decoder').StringDecoder; getCurves,
getDefaultEncoding,
getHashes,
exports.createHash = exports.Hash = Hash; setDefaultEncoding,
function Hash(algorithm, options) { setEngine,
if (!(this instanceof Hash)) toBuf
return new Hash(algorithm, options); } = require('internal/crypto/util');
this._handle = new binding.Hash(algorithm); const Certificate = require('internal/crypto/certificate');
LazyTransform.call(this, options);
} function createECDH(curve) {
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;
// Explicit conversion for backward compatibility.
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;
// Options specific to RSA
var rsaPadding = constants.RSA_PKCS1_PADDING;
if (options.hasOwnProperty('padding')) {
if (options.padding === options.padding >> 0) {
rsaPadding = options.padding;
} else {
throw new TypeError('padding must be an integer');
}
}
var pssSaltLength = constants.RSA_PSS_SALTLEN_AUTO;
if (options.hasOwnProperty('saltLength')) {
if (options.saltLength === options.saltLength >> 0) {
pssSaltLength = options.saltLength;
} else {
throw new TypeError('saltLength must be an integer');
}
}
var ret = this._handle.sign(toBuf(key), passphrase, rsaPadding,
pssSaltLength);
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(options, signature, sigEncoding) {
var key = options.key || options;
sigEncoding = sigEncoding || exports.DEFAULT_ENCODING;
// Options specific to RSA
var rsaPadding = constants.RSA_PKCS1_PADDING;
if (options.hasOwnProperty('padding')) {
if (options.padding === options.padding >> 0) {
rsaPadding = options.padding;
} else {
throw new TypeError('padding must be an integer');
}
}
var pssSaltLength = constants.RSA_PSS_SALTLEN_AUTO;
if (options.hasOwnProperty('saltLength')) {
if (options.saltLength === options.saltLength >> 0) {
pssSaltLength = options.saltLength;
} else {
throw new TypeError('saltLength must be an integer');
}
}
return this._handle.verify(toBuf(key), toBuf(signature, sigEncoding),
rsaPadding, pssSaltLength);
};
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);
};
}
function rsaPrivate(method, defaultPadding) {
return function(options, buffer) {
var key = options.key || options;
var passphrase = options.passphrase || null;
var padding = options.padding || defaultPadding;
return method(toBuf(key), buffer, padding, passphrase);
};
}
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 (typeof sizeOrKey !== 'number' &&
typeof sizeOrKey !== 'string' &&
!ArrayBuffer.isView(sizeOrKey)) {
throw new TypeError('First argument should be number, string, ' +
'Buffer, TypedArray, or DataView');
}
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 setPublicKey(key, encoding) {
encoding = encoding || exports.DEFAULT_ENCODING;
this._handle.setPublicKey(toBuf(key, encoding));
return this;
};
DiffieHellman.prototype.setPrivateKey = function setPrivateKey(key, encoding) {
encoding = encoding || exports.DEFAULT_ENCODING;
this._handle.setPrivateKey(toBuf(key, encoding));
return this;
};
function ECDH(curve) {
if (typeof curve !== 'string')
throw new TypeError('"curve" argument should be a string');
this._handle = new binding.ECDH(curve);
}
exports.createECDH = function createECDH(curve) {
return new ECDH(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) {
if (typeof digest === 'function') {
callback = digest;
digest = undefined;
}
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);
};
function pbkdf2(password, salt, iterations, keylen, digest, callback) {
if (digest === undefined) {
throw new TypeError(
'The "digest" argument is required and must not be undefined');
}
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);
}
} }
module.exports = exports = {
// Methods
_toBuf: toBuf,
createCipher: Cipher,
createCipheriv: Cipheriv,
createDecipher: Decipher,
createDecipheriv: Decipheriv,
createDiffieHellman: DiffieHellman,
createDiffieHellmanGroup: DiffieHellmanGroup,
createECDH,
createHash: Hash,
createHmac: Hmac,
createSign: Sign,
createVerify: Verify,
getCiphers,
getCurves,
getDiffieHellman: DiffieHellmanGroup,
getHashes,
pbkdf2,
pbkdf2Sync,
privateDecrypt,
privateEncrypt,
prng: randomBytes,
pseudoRandomBytes: randomBytes,
publicDecrypt,
publicEncrypt,
randomBytes,
randomFill,
randomFillSync,
rng: randomBytes,
setEngine,
timingSafeEqual,
exports.Certificate = Certificate; // Classes
Certificate,
function Certificate() { Cipher,
if (!(this instanceof Certificate)) Cipheriv,
return new Certificate(); Decipher,
} Decipheriv,
DiffieHellman,
DiffieHellmanGroup,
Certificate.prototype.verifySpkac = function verifySpkac(object) { Hash,
return binding.certVerifySpkac(object); Hmac,
Sign,
Verify
}; };
Object.defineProperties(exports, {
fips: {
get: getFipsCrypto,
set: setFipsCrypto
},
DEFAULT_ENCODING: {
enumerable: true,
configurable: true,
get: getDefaultEncoding,
set: setDefaultEncoding
},
constants: {
configurable: false,
enumerable: true,
value: constants
},
Certificate.prototype.exportPublicKey = // Legacy API
function exportPublicKey(object, encoding) { createCredentials: {
return binding.certExportPublicKey(toBuf(object, encoding)); configurable: true,
}; enumerable: true,
get: deprecate(() => {
return require('tls').createSecureContext;
Certificate.prototype.exportChallenge = }, 'crypto.createCredentials is deprecated. ' +
function exportChallenge(object, encoding) { 'Use tls.createSecureContext instead.', 'DEP0010')
return binding.certExportChallenge(toBuf(object, encoding)); },
}; Credentials: {
configurable: true,
enumerable: true,
exports.setEngine = function setEngine(id, flags) { get: deprecate(function() {
if (typeof id !== 'string') return require('tls').SecureContext;
throw new TypeError('"id" argument should be a string'); }, 'crypto.Credentials is deprecated. ' +
'Use tls.SecureContext instead.', 'DEP0011')
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);
};
const kMaxUint32 = Math.pow(2, 32) - 1;
function randomFillSync(buf, offset = 0, size) {
if (!isUint8Array(buf)) {
throw new TypeError('"buf" argument must be a Buffer or Uint8Array');
} }
assertOffset(offset, buf.length);
if (size === undefined) size = buf.length - offset;
assertSize(size, offset, buf.length);
return binding.randomFill(buf, offset, size);
}
exports.randomFillSync = randomFillSync;
function randomFill(buf, offset, size, cb) {
if (!isUint8Array(buf)) {
throw new TypeError('"buf" argument must be a Buffer or Uint8Array');
}
if (typeof offset === 'function') {
cb = offset;
offset = 0;
size = buf.length;
} else if (typeof size === 'function') {
cb = size;
size = buf.length - offset;
} else if (typeof cb !== 'function') {
throw new TypeError('"cb" argument must be a function');
}
assertOffset(offset, buf.length);
assertSize(size, offset, buf.length);
return binding.randomFill(buf, offset, size, cb);
}
exports.randomFill = randomFill;
function assertOffset(offset, length) {
if (typeof offset !== 'number' || offset !== offset) {
throw new TypeError('offset must be a number');
}
if (offset > kMaxUint32 || offset < 0) {
throw new TypeError('offset must be a uint32');
}
if (offset > kBufferMaxLength || offset > length) {
throw new RangeError('offset out of range');
}
}
function assertSize(size, offset, length) {
if (typeof size !== 'number' || size !== size) {
throw new TypeError('size must be a number');
}
if (size > kMaxUint32 || size < 0) {
throw new TypeError('size must be a uint32');
}
if (size + offset > length || size > kBufferMaxLength) {
throw new RangeError('buffer too small');
}
}
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')
}); });

View File

@ -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;

View File

@ -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,
};

View File

@ -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
};

125
lib/internal/crypto/hash.js Normal file
View File

@ -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
};

View File

@ -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
};

View File

@ -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
};

131
lib/internal/crypto/sig.js Normal file
View File

@ -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
};

View File

@ -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
};

View File

@ -125,6 +125,11 @@ E('ERR_CHILD_CLOSED_BEFORE_REPLY', 'Child closed before reply received');
E('ERR_CONSOLE_WRITABLE_STREAM', E('ERR_CONSOLE_WRITABLE_STREAM',
'Console expects a writable stream instance for %s'); 'Console expects a writable stream instance for %s');
E('ERR_CPU_USAGE', 'Unable to obtain cpu usage %s'); E('ERR_CPU_USAGE', 'Unable to obtain cpu usage %s');
E('ERR_CRYPTO_ECDH_INVALID_FORMAT', 'Invalid ECDH format: %s');
E('ERR_CRYPTO_HASH_DIGEST_NO_UTF16', 'hash.digest() does not support UTF-16');
E('ERR_CRYPTO_HASH_FINALIZED', 'Digest already called');
E('ERR_CRYPTO_HASH_UPDATE_FAILED', 'Hash update failed');
E('ERR_CRYPTO_SIGN_KEY_REQUIRED', 'No key provided to sign');
E('ERR_DNS_SET_SERVERS_FAILED', (err, servers) => E('ERR_DNS_SET_SERVERS_FAILED', (err, servers) =>
`c-ares failed to set servers: "${err}" [${servers}]`); `c-ares failed to set servers: "${err}" [${servers}]`);
E('ERR_ENCODING_INVALID_ENCODED_DATA', E('ERR_ENCODING_INVALID_ENCODED_DATA',
@ -253,6 +258,7 @@ E('ERR_NAPI_CONS_PROTOTYPE_OBJECT', 'Constructor.prototype must be an object');
E('ERR_NO_CRYPTO', 'Node.js is not compiled with OpenSSL crypto support'); E('ERR_NO_CRYPTO', 'Node.js is not compiled with OpenSSL crypto support');
E('ERR_NO_ICU', '%s is not supported on Node.js compiled without ICU'); E('ERR_NO_ICU', '%s is not supported on Node.js compiled without ICU');
E('ERR_NO_LONGER_SUPPORTED', '%s is no longer supported'); E('ERR_NO_LONGER_SUPPORTED', '%s is no longer supported');
E('ERR_OUT_OF_RANGE', 'The "%s" argument is out of range');
E('ERR_OUTOFMEMORY', 'Out of memory'); E('ERR_OUTOFMEMORY', 'Out of memory');
E('ERR_PARSE_HISTORY_DATA', 'Could not parse history data in %s'); E('ERR_PARSE_HISTORY_DATA', 'Could not parse history data in %s');
E('ERR_REQUIRE_ESM', 'Must use import to load ES Module: %s'); E('ERR_REQUIRE_ESM', 'Must use import to load ES Module: %s');

View File

@ -84,6 +84,14 @@
'lib/internal/cluster/shared_handle.js', 'lib/internal/cluster/shared_handle.js',
'lib/internal/cluster/utils.js', 'lib/internal/cluster/utils.js',
'lib/internal/cluster/worker.js', 'lib/internal/cluster/worker.js',
'lib/internal/crypto/certificate.js',
'lib/internal/crypto/cipher.js',
'lib/internal/crypto/diffiehellman.js',
'lib/internal/crypto/hash.js',
'lib/internal/crypto/pbkdf2.js',
'lib/internal/crypto/random.js',
'lib/internal/crypto/sig.js',
'lib/internal/crypto/util.js',
'lib/internal/encoding.js', 'lib/internal/encoding.js',
'lib/internal/errors.js', 'lib/internal/errors.js',
'lib/internal/freelist.js', 'lib/internal/freelist.js',

View File

@ -3699,7 +3699,6 @@ void Hmac::New(const FunctionCallbackInfo<Value>& args) {
void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) { void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) {
HandleScope scope(env()->isolate()); HandleScope scope(env()->isolate());
CHECK_EQ(initialised_, false);
const EVP_MD* md = EVP_get_digestbyname(hash_type); const EVP_MD* md = EVP_get_digestbyname(hash_type);
if (md == nullptr) { if (md == nullptr) {
return env()->ThrowError("Unknown message digest"); return env()->ThrowError("Unknown message digest");
@ -3720,13 +3719,6 @@ void Hmac::HmacInit(const FunctionCallbackInfo<Value>& args) {
ASSIGN_OR_RETURN_UNWRAP(&hmac, args.Holder()); ASSIGN_OR_RETURN_UNWRAP(&hmac, args.Holder());
Environment* env = hmac->env(); Environment* env = hmac->env();
if (args.Length() < 2) {
return env->ThrowError("Hash type and key arguments are mandatory");
}
THROW_AND_RETURN_IF_NOT_STRING(args[0], "Hash type");
THROW_AND_RETURN_IF_NOT_BUFFER(args[1], "Key");
const node::Utf8Value hash_type(env->isolate(), args[0]); const node::Utf8Value hash_type(env->isolate(), args[0]);
const char* buffer_data = Buffer::Data(args[1]); const char* buffer_data = Buffer::Data(args[1]);
size_t buffer_length = Buffer::Length(args[1]); size_t buffer_length = Buffer::Length(args[1]);
@ -3748,24 +3740,22 @@ void Hmac::HmacUpdate(const FunctionCallbackInfo<Value>& args) {
Hmac* hmac; Hmac* hmac;
ASSIGN_OR_RETURN_UNWRAP(&hmac, args.Holder()); ASSIGN_OR_RETURN_UNWRAP(&hmac, args.Holder());
THROW_AND_RETURN_IF_NOT_STRING_OR_BUFFER(args[0], "Data");
// Only copy the data if we have to, because it's a string // Only copy the data if we have to, because it's a string
bool r; bool r = true;
if (args[0]->IsString()) { if (args[0]->IsString()) {
StringBytes::InlineDecoder decoder; StringBytes::InlineDecoder decoder;
if (!decoder.Decode(env, args[0].As<String>(), args[1], UTF8)) if (!decoder.Decode(env, args[0].As<String>(), args[1], UTF8)) {
args.GetReturnValue().Set(false);
return; return;
}
r = hmac->HmacUpdate(decoder.out(), decoder.size()); r = hmac->HmacUpdate(decoder.out(), decoder.size());
} else { } else if (args[0]->IsArrayBufferView()) {
char* buf = Buffer::Data(args[0]); char* buf = Buffer::Data(args[0]);
size_t buflen = Buffer::Length(args[0]); size_t buflen = Buffer::Length(args[0]);
r = hmac->HmacUpdate(buf, buflen); r = hmac->HmacUpdate(buf, buflen);
} }
if (!r) { args.GetReturnValue().Set(r);
return env->ThrowTypeError("HmacUpdate fail");
}
} }
@ -3777,13 +3767,9 @@ void Hmac::HmacDigest(const FunctionCallbackInfo<Value>& args) {
enum encoding encoding = BUFFER; enum encoding encoding = BUFFER;
if (args.Length() >= 1) { if (args.Length() >= 1) {
CHECK(args[0]->IsString());
encoding = ParseEncoding(env->isolate(), args[0], BUFFER); encoding = ParseEncoding(env->isolate(), args[0], BUFFER);
} }
CHECK_NE(encoding, UCS2); // Digest does not support UTF-16
if (encoding == UCS2) {
return env->ThrowError("hmac.digest() does not support UTF-16");
}
unsigned char md_value[EVP_MAX_MD_SIZE]; unsigned char md_value[EVP_MAX_MD_SIZE];
unsigned int md_len = 0; unsigned int md_len = 0;
@ -3825,10 +3811,6 @@ void Hash::Initialize(Environment* env, v8::Local<v8::Object> target) {
void Hash::New(const FunctionCallbackInfo<Value>& args) { void Hash::New(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Environment* env = Environment::GetCurrent(args);
if (args.Length() == 0 || !args[0]->IsString()) {
return env->ThrowError("Must give hashtype string as argument");
}
const node::Utf8Value hash_type(env->isolate(), args[0]); const node::Utf8Value hash_type(env->isolate(), args[0]);
Hash* hash = new Hash(env, args.This()); Hash* hash = new Hash(env, args.This());
@ -3840,7 +3822,6 @@ void Hash::New(const FunctionCallbackInfo<Value>& args) {
bool Hash::HashInit(const char* hash_type) { bool Hash::HashInit(const char* hash_type) {
CHECK_EQ(initialised_, false);
const EVP_MD* md = EVP_get_digestbyname(hash_type); const EVP_MD* md = EVP_get_digestbyname(hash_type);
if (md == nullptr) if (md == nullptr)
return false; return false;
@ -3868,31 +3849,22 @@ void Hash::HashUpdate(const FunctionCallbackInfo<Value>& args) {
Hash* hash; Hash* hash;
ASSIGN_OR_RETURN_UNWRAP(&hash, args.Holder()); ASSIGN_OR_RETURN_UNWRAP(&hash, args.Holder());
THROW_AND_RETURN_IF_NOT_STRING_OR_BUFFER(args[0], "Data");
if (!hash->initialised_) {
return env->ThrowError("Not initialized");
}
if (hash->finalized_) {
return env->ThrowError("Digest already called");
}
// Only copy the data if we have to, because it's a string // Only copy the data if we have to, because it's a string
bool r; bool r = true;
if (args[0]->IsString()) { if (args[0]->IsString()) {
StringBytes::InlineDecoder decoder; StringBytes::InlineDecoder decoder;
if (!decoder.Decode(env, args[0].As<String>(), args[1], UTF8)) if (!decoder.Decode(env, args[0].As<String>(), args[1], UTF8)) {
args.GetReturnValue().Set(false);
return; return;
}
r = hash->HashUpdate(decoder.out(), decoder.size()); r = hash->HashUpdate(decoder.out(), decoder.size());
} else { } else if (args[0]->IsArrayBufferView()) {
char* buf = Buffer::Data(args[0]); char* buf = Buffer::Data(args[0]);
size_t buflen = Buffer::Length(args[0]); size_t buflen = Buffer::Length(args[0]);
r = hash->HashUpdate(buf, buflen); r = hash->HashUpdate(buf, buflen);
} }
if (!r) { args.GetReturnValue().Set(r);
return env->ThrowTypeError("HashUpdate fail");
}
} }
@ -3902,23 +3874,11 @@ void Hash::HashDigest(const FunctionCallbackInfo<Value>& args) {
Hash* hash; Hash* hash;
ASSIGN_OR_RETURN_UNWRAP(&hash, args.Holder()); ASSIGN_OR_RETURN_UNWRAP(&hash, args.Holder());
if (!hash->initialised_) {
return env->ThrowError("Not initialized");
}
if (hash->finalized_) {
return env->ThrowError("Digest already called");
}
enum encoding encoding = BUFFER; enum encoding encoding = BUFFER;
if (args.Length() >= 1) { if (args.Length() >= 1) {
CHECK(args[0]->IsString());
encoding = ParseEncoding(env->isolate(), args[0], BUFFER); encoding = ParseEncoding(env->isolate(), args[0], BUFFER);
} }
if (encoding == UCS2) {
return env->ThrowError("hash.digest() does not support UTF-16");
}
unsigned char md_value[EVP_MAX_MD_SIZE]; unsigned char md_value[EVP_MAX_MD_SIZE];
unsigned int md_len; unsigned int md_len;
@ -5562,13 +5522,14 @@ void RandomBytesCheck(RandomBytesRequest* req, Local<Value> (*argv)[2]) {
req->object()->Get(req->env()->context(), req->object()->Get(req->env()->context(),
req->env()->buffer_string()).ToLocalChecked(); req->env()->buffer_string()).ToLocalChecked();
if (buffer->IsUint8Array()) { if (buffer->IsArrayBufferView()) {
CHECK_LE(req->size(), Buffer::Length(buffer)); CHECK_LE(req->size(), Buffer::Length(buffer));
char* buf = Buffer::Data(buffer); char* buf = Buffer::Data(buffer);
memcpy(buf, data, req->size()); memcpy(buf, data, req->size());
(*argv)[1] = buffer; (*argv)[1] = buffer;
} else { } else {
(*argv)[1] = Buffer::New(req->env(), data, size).ToLocalChecked(); (*argv)[1] = Buffer::New(req->env(), data, size)
.ToLocalChecked();
} }
} }
} }
@ -5649,7 +5610,7 @@ void RandomBytes(const FunctionCallbackInfo<Value>& args) {
void RandomBytesBuffer(const FunctionCallbackInfo<Value>& args) { void RandomBytesBuffer(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Environment* env = Environment::GetCurrent(args);
CHECK(args[0]->IsUint8Array()); CHECK(args[0]->IsArrayBufferView());
CHECK(args[1]->IsUint32()); CHECK(args[1]->IsUint32());
CHECK(args[2]->IsUint32()); CHECK(args[2]->IsUint32());

View File

@ -19,6 +19,7 @@ using v8::Value;
#define VALUE_METHOD_MAP(V) \ #define VALUE_METHOD_MAP(V) \
V(isArrayBuffer, IsArrayBuffer) \ V(isArrayBuffer, IsArrayBuffer) \
V(isArrayBufferView, IsArrayBufferView) \
V(isAsyncFunction, IsAsyncFunction) \ V(isAsyncFunction, IsAsyncFunction) \
V(isDataView, IsDataView) \ V(isDataView, IsDataView) \
V(isDate, IsDate) \ V(isDate, IsDate) \

View File

@ -568,9 +568,12 @@ testCipher4(Buffer.from('0123456789abcd0123456789'), Buffer.from('12345678'));
// update() should only take buffers / strings // update() should only take buffers / strings
assert.throws(function() { common.expectsError(
crypto.createHash('sha1').update({ foo: 'bar' }); () => crypto.createHash('sha1').update({ foo: 'bar' }),
}, /^TypeError: Data must be a string or a buffer$/); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError
});
// Test Diffie-Hellman with two parties sharing a secret, // Test Diffie-Hellman with two parties sharing a secret,

View File

@ -26,6 +26,7 @@ if (!common.hasCrypto)
const assert = require('assert'); const assert = require('assert');
const crypto = require('crypto'); const crypto = require('crypto');
const { Certificate } = crypto;
const fixtures = require('../common/fixtures'); const fixtures = require('../common/fixtures');
crypto.DEFAULT_ENCODING = 'buffer'; crypto.DEFAULT_ENCODING = 'buffer';
@ -35,26 +36,47 @@ const spkacValid = fixtures.readSync('spkac.valid');
const spkacFail = fixtures.readSync('spkac.fail'); const spkacFail = fixtures.readSync('spkac.fail');
const spkacPem = fixtures.readSync('spkac.pem'); const spkacPem = fixtures.readSync('spkac.pem');
const certificate = new crypto.Certificate(); {
// Test instance methods
const certificate = new Certificate();
assert.strictEqual(certificate.verifySpkac(spkacValid), true); assert.strictEqual(certificate.verifySpkac(spkacValid), true);
assert.strictEqual(certificate.verifySpkac(spkacFail), false); assert.strictEqual(certificate.verifySpkac(spkacFail), false);
assert.strictEqual( assert.strictEqual(
stripLineEndings(certificate.exportPublicKey(spkacValid).toString('utf8')), stripLineEndings(certificate.exportPublicKey(spkacValid).toString('utf8')),
stripLineEndings(spkacPem.toString('utf8')) stripLineEndings(spkacPem.toString('utf8'))
); );
assert.strictEqual(certificate.exportPublicKey(spkacFail), ''); assert.strictEqual(certificate.exportPublicKey(spkacFail), '');
assert.strictEqual( assert.strictEqual(
certificate.exportChallenge(spkacValid).toString('utf8'), certificate.exportChallenge(spkacValid).toString('utf8'),
'fb9ab814-6677-42a4-a60c-f905d1a6924d' 'fb9ab814-6677-42a4-a60c-f905d1a6924d'
); );
assert.strictEqual(certificate.exportChallenge(spkacFail), ''); assert.strictEqual(certificate.exportChallenge(spkacFail), '');
}
{
// Test static methods
assert.strictEqual(Certificate.verifySpkac(spkacValid), true);
assert.strictEqual(Certificate.verifySpkac(spkacFail), false);
assert.strictEqual(
stripLineEndings(Certificate.exportPublicKey(spkacValid).toString('utf8')),
stripLineEndings(spkacPem.toString('utf8'))
);
assert.strictEqual(Certificate.exportPublicKey(spkacFail), '');
assert.strictEqual(
Certificate.exportChallenge(spkacValid).toString('utf8'),
'fb9ab814-6677-42a4-a60c-f905d1a6924d'
);
assert.strictEqual(Certificate.exportChallenge(spkacFail), '');
}
function stripLineEndings(obj) { function stripLineEndings(obj) {
return obj.replace(/\n/g, ''); return obj.replace(/\n/g, '');
} }
// direct call Certificate() should return instance // direct call Certificate() should return instance
assert(crypto.Certificate() instanceof crypto.Certificate); assert(Certificate() instanceof Certificate);

View File

@ -22,24 +22,22 @@ assert.strictEqual(secret2.toString('base64'), secret1);
assert.strictEqual(dh1.verifyError, 0); assert.strictEqual(dh1.verifyError, 0);
assert.strictEqual(dh2.verifyError, 0); assert.strictEqual(dh2.verifyError, 0);
const argumentsError = [
/^TypeError: First argument should be number, string, Buffer, TypedArray, or DataView$/; [0x1, 0x2],
() => { },
assert.throws(() => { /abc/,
crypto.createDiffieHellman([0x1, 0x2]); {}
}, argumentsError); ].forEach((i) => {
common.expectsError(
assert.throws(() => { () => crypto.createDiffieHellman(i),
crypto.createDiffieHellman(() => { }); {
}, argumentsError); code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
assert.throws(() => { message: 'The "sizeOrKey" argument must be one of type number, string, ' +
crypto.createDiffieHellman(/abc/); 'Buffer, TypedArray, or DataView'
}, argumentsError); }
);
assert.throws(() => { });
crypto.createDiffieHellman({});
}, argumentsError);
// Create "another dh1" using generated keys from dh1, // Create "another dh1" using generated keys from dh1,
// and compute secret again // and compute secret again
@ -198,9 +196,14 @@ if (availableCurves.has('prime256v1') && availableCurves.has('secp256k1')) {
firstByte = ecdh1.getPublicKey('buffer', 'hybrid')[0]; firstByte = ecdh1.getPublicKey('buffer', 'hybrid')[0];
assert(firstByte === 6 || firstByte === 7); assert(firstByte === 6 || firstByte === 7);
// format value should be string // format value should be string
assert.throws(() => {
ecdh1.getPublicKey('buffer', 10); common.expectsError(
}, /^TypeError: Bad format: 10$/); () => ecdh1.getPublicKey('buffer', 10),
{
code: 'ERR_CRYPTO_ECDH_INVALID_FORMAT',
type: TypeError,
message: 'Invalid ECDH format: 10'
});
// ECDH should check that point is on curve // ECDH should check that point is on curve
const ecdh3 = crypto.createECDH('secp256k1'); const ecdh3 = crypto.createECDH('secp256k1');
@ -331,6 +334,10 @@ if (availableCurves.has('prime256v1') && availableHashes.has('sha256')) {
} }
// invalid test: curve argument is undefined // invalid test: curve argument is undefined
assert.throws(() => { common.expectsError(
crypto.createECDH(); () => crypto.createECDH(),
}, /^TypeError: "curve" argument should be a string$/); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "curve" argument must be of type string'
});

View File

@ -4,13 +4,20 @@ const common = require('../common');
if (!common.hasCrypto) if (!common.hasCrypto)
common.skip('missing crypto'); common.skip('missing crypto');
const assert = require('assert');
const crypto = require('crypto'); const crypto = require('crypto');
assert.throws(function() { common.expectsError(
crypto.setEngine(true); () => crypto.setEngine(true),
}, /^TypeError: "id" argument should be a string$/); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "id" argument must be of type string'
});
assert.throws(function() { common.expectsError(
crypto.setEngine('/path/to/engine', 'notANumber'); () => crypto.setEngine('/path/to/engine', 'notANumber'),
}, /^TypeError: "flags" argument should be a number, if present$/); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "flags" argument must be of type number'
});

View File

@ -105,14 +105,34 @@ assert.notStrictEqual(
const h3 = crypto.createHash('sha256'); const h3 = crypto.createHash('sha256');
h3.digest(); h3.digest();
assert.throws(function() {
h3.digest();
}, /Digest already called/);
assert.throws(function() { common.expectsError(
h3.update('foo'); () => h3.digest(),
}, /Digest already called/); {
code: 'ERR_CRYPTO_HASH_FINALIZED',
type: Error
});
assert.throws(function() { common.expectsError(
crypto.createHash('sha256').digest('ucs2'); () => h3.update('foo'),
}, /^Error: hash\.digest\(\) does not support UTF-16$/); {
code: 'ERR_CRYPTO_HASH_FINALIZED',
type: Error
});
common.expectsError(
() => crypto.createHash('sha256').digest('ucs2'),
{
code: 'ERR_CRYPTO_HASH_DIGEST_NO_UTF16',
type: Error
}
);
common.expectsError(
() => crypto.createHash(),
{
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "algorithm" argument must be of type string'
}
);

View File

@ -6,19 +6,11 @@ if (!common.hasCrypto)
const assert = require('assert'); const assert = require('assert');
const crypto = require('crypto'); const crypto = require('crypto');
// Test for binding layer robustness
{
const binding = process.binding('crypto');
const h = new binding.Hmac();
// Fail to init the Hmac with an algorithm.
assert.throws(() => h.update('hello'), /^TypeError: HmacUpdate fail$/);
}
// Test HMAC // Test HMAC
const h1 = crypto.createHmac('sha1', 'Node') const h1 = crypto.createHmac('sha1', 'Node')
.update('some data') .update('some data')
.update('to hmac') .update('to hmac')
.digest('hex'); .digest('hex');
assert.strictEqual(h1, '19fd6e1ba73d9ed2224dd5094a71babe85d9a892', 'test HMAC'); assert.strictEqual(h1, '19fd6e1ba73d9ed2224dd5094a71babe85d9a892', 'test HMAC');
// Test HMAC (Wikipedia Test Cases) // Test HMAC (Wikipedia Test Cases)
@ -376,9 +368,12 @@ for (let i = 0, l = rfc2202_sha1.length; i < l; i++) {
); );
} }
assert.throws(function() { common.expectsError(
crypto.createHmac('sha256', 'w00t').digest('ucs2'); () => crypto.createHmac('sha256', 'w00t').digest('ucs2'),
}, /^Error: hmac\.digest\(\) does not support UTF-16$/); {
code: 'ERR_CRYPTO_HASH_DIGEST_NO_UTF16',
type: Error
});
// Check initialized -> uninitialized state transition after calling digest(). // Check initialized -> uninitialized state transition after calling digest().
{ {

View File

@ -55,9 +55,13 @@ function ondone(err, key) {
} }
// Error path should not leak memory (check with valgrind). // Error path should not leak memory (check with valgrind).
assert.throws(function() { common.expectsError(
crypto.pbkdf2('password', 'salt', 1, 20, null); () => crypto.pbkdf2('password', 'salt', 1, 20, null),
}, /^Error: No callback provided to pbkdf2$/); {
code: 'ERR_INVALID_CALLBACK',
type: TypeError
}
);
// Should not work with Infinity key length // Should not work with Infinity key length
assert.throws(function() { assert.throws(function() {
@ -95,10 +99,18 @@ assert.doesNotThrow(() => {
})); }));
}); });
assert.throws(() => { common.expectsError(
crypto.pbkdf2('password', 'salt', 8, 8, common.mustNotCall()); () => crypto.pbkdf2('password', 'salt', 8, 8, common.mustNotCall()),
}, /^TypeError: The "digest" argument is required and must not be undefined$/); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "digest" argument must be one of type string or null'
});
assert.throws(() => { common.expectsError(
crypto.pbkdf2Sync('password', 'salt', 8, 8); () => crypto.pbkdf2Sync('password', 'salt', 8, 8),
}, /^TypeError: The "digest" argument is required and must not be undefined$/); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "digest" argument must be one of type string or null'
});

View File

@ -33,15 +33,6 @@ crypto.DEFAULT_ENCODING = 'buffer';
// bump, we register a lot of exit listeners // bump, we register a lot of exit listeners
process.setMaxListeners(256); process.setMaxListeners(256);
const errMessages = {
offsetNotNumber: /^TypeError: offset must be a number$/,
offsetOutOfRange: /^RangeError: offset out of range$/,
offsetNotUInt32: /^TypeError: offset must be a uint32$/,
sizeNotNumber: /^TypeError: size must be a number$/,
sizeNotUInt32: /^TypeError: size must be a uint32$/,
bufferTooSmall: /^RangeError: buffer too small$/,
};
const expectedErrorRegexp = /^TypeError: size must be a number >= 0$/; const expectedErrorRegexp = /^TypeError: size must be a number >= 0$/;
[crypto.randomBytes, crypto.pseudoRandomBytes].forEach(function(f) { [crypto.randomBytes, crypto.pseudoRandomBytes].forEach(function(f) {
[-1, undefined, null, false, true, {}, []].forEach(function(value) { [-1, undefined, null, false, true, {}, []].forEach(function(value) {
@ -74,6 +65,46 @@ const expectedErrorRegexp = /^TypeError: size must be a number >= 0$/;
assert.notStrictEqual(before, after); assert.notStrictEqual(before, after);
} }
{
const buf = new Uint16Array(10);
const before = Buffer.from(buf.buffer).toString('hex');
crypto.randomFillSync(buf);
const after = Buffer.from(buf.buffer).toString('hex');
assert.notStrictEqual(before, after);
}
{
const buf = new Uint32Array(10);
const before = Buffer.from(buf.buffer).toString('hex');
crypto.randomFillSync(buf);
const after = Buffer.from(buf.buffer).toString('hex');
assert.notStrictEqual(before, after);
}
{
const buf = new Float32Array(10);
const before = Buffer.from(buf.buffer).toString('hex');
crypto.randomFillSync(buf);
const after = Buffer.from(buf.buffer).toString('hex');
assert.notStrictEqual(before, after);
}
{
const buf = new Float64Array(10);
const before = Buffer.from(buf.buffer).toString('hex');
crypto.randomFillSync(buf);
const after = Buffer.from(buf.buffer).toString('hex');
assert.notStrictEqual(before, after);
}
{
const buf = new DataView(new ArrayBuffer(10));
const before = Buffer.from(buf.buffer).toString('hex');
crypto.randomFillSync(buf);
const after = Buffer.from(buf.buffer).toString('hex');
assert.notStrictEqual(before, after);
}
{ {
const buf = Buffer.alloc(10); const buf = Buffer.alloc(10);
const before = buf.toString('hex'); const before = buf.toString('hex');
@ -94,6 +125,56 @@ const expectedErrorRegexp = /^TypeError: size must be a number >= 0$/;
})); }));
} }
{
const buf = new Uint16Array(10);
const before = Buffer.from(buf.buffer).toString('hex');
crypto.randomFill(buf, common.mustCall((err, buf) => {
assert.ifError(err);
const after = Buffer.from(buf.buffer).toString('hex');
assert.notStrictEqual(before, after);
}));
}
{
const buf = new Uint32Array(10);
const before = Buffer.from(buf.buffer).toString('hex');
crypto.randomFill(buf, common.mustCall((err, buf) => {
assert.ifError(err);
const after = Buffer.from(buf.buffer).toString('hex');
assert.notStrictEqual(before, after);
}));
}
{
const buf = new Float32Array(10);
const before = Buffer.from(buf.buffer).toString('hex');
crypto.randomFill(buf, common.mustCall((err, buf) => {
assert.ifError(err);
const after = Buffer.from(buf.buffer).toString('hex');
assert.notStrictEqual(before, after);
}));
}
{
const buf = new Float64Array(10);
const before = Buffer.from(buf.buffer).toString('hex');
crypto.randomFill(buf, common.mustCall((err, buf) => {
assert.ifError(err);
const after = Buffer.from(buf.buffer).toString('hex');
assert.notStrictEqual(before, after);
}));
}
{
const buf = new DataView(new ArrayBuffer(10));
const before = Buffer.from(buf.buffer).toString('hex');
crypto.randomFill(buf, common.mustCall((err, buf) => {
assert.ifError(err);
const after = Buffer.from(buf.buffer).toString('hex');
assert.notStrictEqual(before, after);
}));
}
{ {
const buf = Buffer.alloc(10); const buf = Buffer.alloc(10);
const before = buf.toString('hex'); const before = buf.toString('hex');
@ -155,108 +236,228 @@ const expectedErrorRegexp = /^TypeError: size must be a number >= 0$/;
const len = Buffer.byteLength(buf); const len = Buffer.byteLength(buf);
assert.strictEqual(len, 10, `Expected byteLength of 10, got ${len}`); assert.strictEqual(len, 10, `Expected byteLength of 10, got ${len}`);
assert.throws(() => { common.expectsError(
crypto.randomFillSync(buf, 'test'); () => crypto.randomFillSync(buf, 'test'),
}, errMessages.offsetNotNumber); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "offset" argument must be of type number'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFillSync(buf, NaN); () => crypto.randomFillSync(buf, NaN),
}, errMessages.offsetNotNumber); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "offset" argument must be of type number'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFill(buf, 'test', common.mustNotCall()); () => crypto.randomFill(buf, 'test', common.mustNotCall()),
}, errMessages.offsetNotNumber); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "offset" argument must be of type number'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFill(buf, NaN, common.mustNotCall()); () => crypto.randomFill(buf, NaN, common.mustNotCall()),
}, errMessages.offsetNotNumber); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "offset" argument must be of type number'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFillSync(buf, 11); () => crypto.randomFillSync(buf, 11),
}, errMessages.offsetOutOfRange); {
code: 'ERR_OUT_OF_RANGE',
type: RangeError,
message: 'The "offset" argument is out of range'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFillSync(buf, max); () => crypto.randomFillSync(buf, max),
}, errMessages.offsetOutOfRange); {
code: 'ERR_OUT_OF_RANGE',
type: RangeError,
message: 'The "offset" argument is out of range'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFill(buf, 11, common.mustNotCall()); () => crypto.randomFill(buf, 11, common.mustNotCall()),
}, errMessages.offsetOutOfRange); {
code: 'ERR_OUT_OF_RANGE',
type: RangeError,
message: 'The "offset" argument is out of range'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFill(buf, max, common.mustNotCall()); () => crypto.randomFill(buf, max, common.mustNotCall()),
}, errMessages.offsetOutOfRange); {
code: 'ERR_OUT_OF_RANGE',
type: RangeError,
message: 'The "offset" argument is out of range'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFillSync(buf, 0, 'test'); () => crypto.randomFillSync(buf, 0, 'test'),
}, errMessages.sizeNotNumber); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "size" argument must be of type number'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFillSync(buf, 0, NaN); () => crypto.randomFillSync(buf, 0, NaN),
}, errMessages.sizeNotNumber); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "size" argument must be of type number'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFill(buf, 0, 'test', common.mustNotCall()); () => crypto.randomFill(buf, 0, 'test', common.mustNotCall()),
}, errMessages.sizeNotNumber); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "size" argument must be of type number'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFill(buf, 0, NaN, common.mustNotCall()); () => crypto.randomFill(buf, 0, NaN, common.mustNotCall()),
}, errMessages.sizeNotNumber); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "size" argument must be of type number'
}
);
{ {
const size = (-1 >>> 0) + 1; const size = (-1 >>> 0) + 1;
assert.throws(() => { common.expectsError(
crypto.randomFillSync(buf, 0, -10); () => crypto.randomFillSync(buf, 0, -10),
}, errMessages.sizeNotUInt32); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "size" argument must be of type uint32'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFillSync(buf, 0, size); () => crypto.randomFillSync(buf, 0, size),
}, errMessages.sizeNotUInt32); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "size" argument must be of type uint32'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFill(buf, 0, -10, common.mustNotCall()); () => crypto.randomFill(buf, 0, -10, common.mustNotCall()),
}, errMessages.sizeNotUInt32); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "size" argument must be of type uint32'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFill(buf, 0, size, common.mustNotCall()); () => crypto.randomFill(buf, 0, size, common.mustNotCall()),
}, errMessages.sizeNotUInt32); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "size" argument must be of type uint32'
}
);
} }
assert.throws(() => { common.expectsError(
crypto.randomFillSync(buf, -10); () => crypto.randomFillSync(buf, -10),
}, errMessages.offsetNotUInt32); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "offset" argument must be of type uint32'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFill(buf, -10, common.mustNotCall()); () => crypto.randomFill(buf, -10, common.mustNotCall()),
}, errMessages.offsetNotUInt32); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "offset" argument must be of type uint32'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFillSync(buf, 1, 10); () => crypto.randomFillSync(buf, 1, 10),
}, errMessages.bufferTooSmall); {
code: 'ERR_OUT_OF_RANGE',
type: RangeError,
message: 'The "size" argument is out of range'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFill(buf, 1, 10, common.mustNotCall()); () => crypto.randomFill(buf, 1, 10, common.mustNotCall()),
}, errMessages.bufferTooSmall); {
code: 'ERR_OUT_OF_RANGE',
type: RangeError,
message: 'The "size" argument is out of range'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFillSync(buf, 0, 12); () => crypto.randomFillSync(buf, 0, 12),
}, errMessages.bufferTooSmall); {
code: 'ERR_OUT_OF_RANGE',
type: RangeError,
message: 'The "size" argument is out of range'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFill(buf, 0, 12, common.mustNotCall()); () => crypto.randomFill(buf, 0, 12, common.mustNotCall()),
}, errMessages.bufferTooSmall); {
code: 'ERR_OUT_OF_RANGE',
type: RangeError,
message: 'The "size" argument is out of range'
}
);
{ {
// Offset is too big // Offset is too big
const offset = (-1 >>> 0) + 1; const offset = (-1 >>> 0) + 1;
assert.throws(() => { common.expectsError(
crypto.randomFillSync(buf, offset, 10); () => crypto.randomFillSync(buf, offset, 10),
}, errMessages.offsetNotUInt32); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "offset" argument must be of type uint32'
}
);
assert.throws(() => { common.expectsError(
crypto.randomFill(buf, offset, 10, common.mustNotCall()); () => crypto.randomFill(buf, offset, 10, common.mustNotCall()),
}, errMessages.offsetNotUInt32); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "offset" argument must be of type uint32'
}
);
} }
} }
} }
@ -265,4 +466,21 @@ const expectedErrorRegexp = /^TypeError: size must be a number >= 0$/;
// length exceeds max acceptable value" // length exceeds max acceptable value"
assert.throws(function() { assert.throws(function() {
crypto.randomBytes((-1 >>> 0) + 1); crypto.randomBytes((-1 >>> 0) + 1);
}, errMessages.sizeNotUInt32); }, /^TypeError: size must be a uint32$/);
[1, true, NaN, null, undefined, {}, []].forEach((i) => {
common.expectsError(
() => crypto.randomFillSync(i),
{
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError
}
);
common.expectsError(
() => crypto.randomFill(i, common.mustNotCall()),
{
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError
}
);
});

View File

@ -197,21 +197,21 @@ const modSize = 1024;
// Test exceptions for invalid `padding` and `saltLength` values // Test exceptions for invalid `padding` and `saltLength` values
{ {
const paddingNotInteger = /^TypeError: padding must be an integer$/;
const saltLengthNotInteger = /^TypeError: saltLength must be an integer$/;
[null, undefined, NaN, 'boom', {}, [], true, false] [null, undefined, NaN, 'boom', {}, [], true, false]
.forEach((invalidValue) => { .forEach((invalidValue) => {
assert.throws(() => { common.expectsError(() => {
crypto.createSign('SHA256') crypto.createSign('SHA256')
.update('Test123') .update('Test123')
.sign({ .sign({
key: keyPem, key: keyPem,
padding: invalidValue padding: invalidValue
}); });
}, paddingNotInteger); }, {
code: 'ERR_INVALID_OPT_VALUE',
type: TypeError
});
assert.throws(() => { common.expectsError(() => {
crypto.createSign('SHA256') crypto.createSign('SHA256')
.update('Test123') .update('Test123')
.sign({ .sign({
@ -219,7 +219,10 @@ const modSize = 1024;
padding: crypto.constants.RSA_PKCS1_PSS_PADDING, padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: invalidValue saltLength: invalidValue
}); });
}, saltLengthNotInteger); }, {
code: 'ERR_INVALID_OPT_VALUE',
type: TypeError
});
}); });
assert.throws(() => { assert.throws(() => {
@ -234,9 +237,12 @@ const modSize = 1024;
// Test throws exception when key options is null // Test throws exception when key options is null
{ {
assert.throws(() => { common.expectsError(() => {
crypto.createSign('SHA1').update('Test123').sign(null, 'base64'); crypto.createSign('SHA1').update('Test123').sign(null, 'base64');
}, /^Error: No key provided to sign$/); }, {
code: 'ERR_CRYPTO_SIGN_KEY_REQUIRED',
type: Error
});
} }
// RSA-PSS Sign test by verifying with 'openssl dgst -verify' // RSA-PSS Sign test by verifying with 'openssl dgst -verify'

View File

@ -67,9 +67,12 @@ assert.throws(function() {
// update() should only take buffers / strings // update() should only take buffers / strings
assert.throws(function() { common.expectsError(
crypto.createHash('sha1').update({ foo: 'bar' }); () => crypto.createHash('sha1').update({ foo: 'bar' }),
}, /^TypeError: Data must be a string or a buffer$/); {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError
});
function validateList(list) { function validateList(list) {