diff --git a/doc/api/errors.md b/doc/api/errors.md
index 6d8eed9492..8805e5ec03 100644
--- a/doc/api/errors.md
+++ b/doc/api/errors.md
@@ -679,6 +679,13 @@ ever, happen.
Used when an invalid [crypto digest algorithm][] is specified.
+
+### ERR_CRYPTO_INVALID_STATE
+
+Used generically when a crypto method is used on an object that is in an
+invalid state. For instance, calling [`cipher.getAuthTag()`][] before calling
+`cipher.final()`.
+
### ERR_CRYPTO_SIGN_KEY_REQUIRED
@@ -1498,6 +1505,7 @@ closed.
Used when creation of a [`zlib`][] object fails due to incorrect configuration.
[`--force-fips`]: cli.html#cli_force_fips
+[`cipher.getAuthTag()`]: crypto.html#crypto_cipher_getauthtag
[`crypto.timingSafeEqual()`]: crypto.html#crypto_crypto_timingsafeequal_a_b
[`dgram.createSocket()`]: dgram.html#dgram_dgram_createsocket_options_callback
[`ERR_INVALID_ARG_TYPE`]: #ERR_INVALID_ARG_TYPE
diff --git a/lib/internal/crypto/cipher.js b/lib/internal/crypto/cipher.js
index d9b31674c1..cd2f3960e5 100644
--- a/lib/internal/crypto/cipher.js
+++ b/lib/internal/crypto/cipher.js
@@ -5,11 +5,15 @@ const {
RSA_PKCS1_PADDING
} = process.binding('constants').crypto;
+const errors = require('internal/errors');
+
const {
getDefaultEncoding,
toBuf
} = require('internal/crypto/util');
+const { isArrayBufferView } = require('internal/util/types');
+
const {
CipherBase,
privateDecrypt: _privateDecrypt,
@@ -58,9 +62,19 @@ function getDecoder(decoder, encoding) {
function Cipher(cipher, password, options) {
if (!(this instanceof Cipher))
return new Cipher(cipher, password, options);
+
+ if (typeof cipher !== 'string')
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'cipher', 'string');
+
+ password = toBuf(password);
+ if (!isArrayBufferView(password)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'password',
+ ['string', 'Buffer', 'TypedArray', 'DataView']);
+ }
+
this._handle = new CipherBase(true);
- this._handle.init(cipher, toBuf(password));
+ this._handle.init(cipher, password);
this._decoder = null;
LazyTransform.call(this, options);
@@ -88,11 +102,16 @@ Cipher.prototype.update = function update(data, inputEncoding, outputEncoding) {
inputEncoding = inputEncoding || encoding;
outputEncoding = outputEncoding || encoding;
- var ret = this._handle.update(data, inputEncoding);
+ if (typeof data !== 'string' && !isArrayBufferView(data)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'data',
+ ['string', 'Buffer', 'TypedArray', 'DataView']);
+ }
+
+ const ret = this._handle.update(data, inputEncoding);
if (outputEncoding && outputEncoding !== 'buffer') {
this._decoder = getDecoder(this._decoder, outputEncoding);
- ret = this._decoder.write(ret);
+ return this._decoder.write(ret);
}
return ret;
@@ -101,11 +120,11 @@ Cipher.prototype.update = function update(data, inputEncoding, outputEncoding) {
Cipher.prototype.final = function final(outputEncoding) {
outputEncoding = outputEncoding || getDefaultEncoding();
- var ret = this._handle.final();
+ const ret = this._handle.final();
if (outputEncoding && outputEncoding !== 'buffer') {
this._decoder = getDecoder(this._decoder, outputEncoding);
- ret = this._decoder.end(ret);
+ return this._decoder.end(ret);
}
return ret;
@@ -113,30 +132,63 @@ Cipher.prototype.final = function final(outputEncoding) {
Cipher.prototype.setAutoPadding = function setAutoPadding(ap) {
- this._handle.setAutoPadding(ap);
+ if (this._handle.setAutoPadding(ap) === false)
+ throw new errors.Error('ERR_CRYPTO_INVALID_STATE', 'setAutoPadding');
return this;
};
Cipher.prototype.getAuthTag = function getAuthTag() {
- return this._handle.getAuthTag();
+ const ret = this._handle.getAuthTag();
+ if (ret === undefined)
+ throw new errors.Error('ERR_CRYPTO_INVALID_STATE', 'getAuthTag');
+ return ret;
};
Cipher.prototype.setAuthTag = function setAuthTag(tagbuf) {
- this._handle.setAuthTag(tagbuf);
+ if (!isArrayBufferView(tagbuf)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'buffer',
+ ['Buffer', 'TypedArray', 'DataView']);
+ }
+ // Do not do a normal falsy check because the method returns
+ // undefined if it succeeds. Returns false specifically if it
+ // errored
+ if (this._handle.setAuthTag(tagbuf) === false)
+ throw new errors.Error('ERR_CRYPTO_INVALID_STATE', 'setAuthTag');
return this;
};
Cipher.prototype.setAAD = function setAAD(aadbuf) {
- this._handle.setAAD(aadbuf);
+ if (!isArrayBufferView(aadbuf)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'buffer',
+ ['Buffer', 'TypedArray', 'DataView']);
+ }
+ if (this._handle.setAAD(aadbuf) === false)
+ throw new errors.Error('ERR_CRYPTO_INVALID_STATE', 'setAAD');
return this;
};
function Cipheriv(cipher, key, iv, options) {
if (!(this instanceof Cipheriv))
return new Cipheriv(cipher, key, iv, options);
+
+ if (typeof cipher !== 'string')
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'cipher', 'string');
+
+ key = toBuf(key);
+ if (!isArrayBufferView(key)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'key',
+ ['string', 'Buffer', 'TypedArray', 'DataView']);
+ }
+
+ iv = toBuf(iv);
+ if (!isArrayBufferView(iv)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'iv',
+ ['string', 'Buffer', 'TypedArray', 'DataView']);
+ }
+
this._handle = new CipherBase(true);
- this._handle.initiv(cipher, toBuf(key), toBuf(iv));
+ this._handle.initiv(cipher, key, iv);
this._decoder = null;
LazyTransform.call(this, options);
@@ -158,8 +210,17 @@ function Decipher(cipher, password, options) {
if (!(this instanceof Decipher))
return new Decipher(cipher, password, options);
+ if (typeof cipher !== 'string')
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'cipher', 'string');
+
+ password = toBuf(password);
+ if (!isArrayBufferView(password)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'password',
+ ['string', 'Buffer', 'TypedArray', 'DataView']);
+ }
+
this._handle = new CipherBase(false);
- this._handle.init(cipher, toBuf(password));
+ this._handle.init(cipher, password);
this._decoder = null;
LazyTransform.call(this, options);
@@ -182,8 +243,23 @@ function Decipheriv(cipher, key, iv, options) {
if (!(this instanceof Decipheriv))
return new Decipheriv(cipher, key, iv, options);
+ if (typeof cipher !== 'string')
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'cipher', 'string');
+
+ key = toBuf(key);
+ if (!isArrayBufferView(key)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'key',
+ ['string', 'Buffer', 'TypedArray', 'DataView']);
+ }
+
+ iv = toBuf(iv);
+ if (!isArrayBufferView(iv)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'iv',
+ ['string', 'Buffer', 'TypedArray', 'DataView']);
+ }
+
this._handle = new CipherBase(false);
- this._handle.initiv(cipher, toBuf(key), toBuf(iv));
+ this._handle.initiv(cipher, key, iv);
this._decoder = null;
LazyTransform.call(this, options);
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index 4e6a2c4a5c..8862ca390d 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -164,6 +164,7 @@ 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_INVALID_DIGEST', 'Invalid digest: %s');
+E('ERR_CRYPTO_INVALID_STATE', 'Invalid state for operation %s');
E('ERR_CRYPTO_SIGN_KEY_REQUIRED', 'No key provided to sign');
E('ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH',
'Input buffers must have the same length');
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index 5b4ce7c059..b6e7d54ea0 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -48,13 +48,6 @@
#include
#include
-#define THROW_AND_RETURN_IF_NOT_STRING_OR_BUFFER(val, prefix) \
- do { \
- if (!Buffer::HasInstance(val) && !val->IsString()) { \
- return env->ThrowTypeError(prefix " must be a string or a buffer"); \
- } \
- } while (0)
-
#define THROW_AND_RETURN_IF_NOT_BUFFER(val, prefix) \
do { \
if (!Buffer::HasInstance(val)) { \
@@ -3410,14 +3403,8 @@ void CipherBase::Init(const char* cipher_type,
void CipherBase::Init(const FunctionCallbackInfo& args) {
CipherBase* cipher;
ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder());
- Environment* env = cipher->env();
-
- if (args.Length() < 2) {
- return env->ThrowError("Cipher type and key arguments are mandatory");
- }
- THROW_AND_RETURN_IF_NOT_STRING(args[0], "Cipher type");
- THROW_AND_RETURN_IF_NOT_BUFFER(args[1], "Key");
+ CHECK_GE(args.Length(), 2);
const node::Utf8Value cipher_type(args.GetIsolate(), args[0]);
const char* key_buf = Buffer::Data(args[1]);
@@ -3480,13 +3467,7 @@ void CipherBase::InitIv(const FunctionCallbackInfo& args) {
ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder());
Environment* env = cipher->env();
- if (args.Length() < 3) {
- return env->ThrowError("Cipher type, key, and IV arguments are mandatory");
- }
-
- THROW_AND_RETURN_IF_NOT_STRING(args[0], "Cipher type");
- THROW_AND_RETURN_IF_NOT_BUFFER(args[1], "Key");
- THROW_AND_RETURN_IF_NOT_BUFFER(args[2], "IV");
+ CHECK_GE(args.Length(), 3);
const node::Utf8Value cipher_type(env->isolate(), args[0]);
ssize_t key_len = Buffer::Length(args[1]);
@@ -3515,7 +3496,7 @@ void CipherBase::GetAuthTag(const FunctionCallbackInfo& args) {
if (cipher->initialised_ ||
cipher->kind_ != kCipher ||
cipher->auth_tag_len_ == 0) {
- return env->ThrowError("Attempting to get auth tag in unsupported state");
+ return args.GetReturnValue().SetUndefined();
}
Local