Skip to content

Commit e567402

Browse files
committed
crypto: migrate CipherBase to internal/errors
Migrates most of CipherBase errors to use internal/errors. There are still a handful remaining that need to be handled separately PR-URL: #16527 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
1 parent ab2c351 commit e567402

File tree

5 files changed

+135
-63
lines changed

5 files changed

+135
-63
lines changed

doc/api/errors.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,13 @@ ever, happen.
679679

680680
Used when an invalid [crypto digest algorithm][] is specified.
681681

682+
<a id="ERR_CRYPTO_INVALID_STATE"></a>
683+
### ERR_CRYPTO_INVALID_STATE
684+
685+
Used generically when a crypto method is used on an object that is in an
686+
invalid state. For instance, calling [`cipher.getAuthTag()`][] before calling
687+
`cipher.final()`.
688+
682689
<a id="ERR_CRYPTO_SIGN_KEY_REQUIRED"></a>
683690
### ERR_CRYPTO_SIGN_KEY_REQUIRED
684691

@@ -1498,6 +1505,7 @@ closed.
14981505
Used when creation of a [`zlib`][] object fails due to incorrect configuration.
14991506

15001507
[`--force-fips`]: cli.html#cli_force_fips
1508+
[`cipher.getAuthTag()`]: crypto.html#crypto_cipher_getauthtag
15011509
[`crypto.timingSafeEqual()`]: crypto.html#crypto_crypto_timingsafeequal_a_b
15021510
[`dgram.createSocket()`]: dgram.html#dgram_dgram_createsocket_options_callback
15031511
[`ERR_INVALID_ARG_TYPE`]: #ERR_INVALID_ARG_TYPE

lib/internal/crypto/cipher.js

Lines changed: 88 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ const {
55
RSA_PKCS1_PADDING
66
} = process.binding('constants').crypto;
77

8+
const errors = require('internal/errors');
9+
810
const {
911
getDefaultEncoding,
1012
toBuf
1113
} = require('internal/crypto/util');
1214

15+
const { isArrayBufferView } = require('internal/util/types');
16+
1317
const {
1418
CipherBase,
1519
privateDecrypt: _privateDecrypt,
@@ -58,9 +62,19 @@ function getDecoder(decoder, encoding) {
5862
function Cipher(cipher, password, options) {
5963
if (!(this instanceof Cipher))
6064
return new Cipher(cipher, password, options);
65+
66+
if (typeof cipher !== 'string')
67+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'cipher', 'string');
68+
69+
password = toBuf(password);
70+
if (!isArrayBufferView(password)) {
71+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'password',
72+
['string', 'Buffer', 'TypedArray', 'DataView']);
73+
}
74+
6175
this._handle = new CipherBase(true);
6276

63-
this._handle.init(cipher, toBuf(password));
77+
this._handle.init(cipher, password);
6478
this._decoder = null;
6579

6680
LazyTransform.call(this, options);
@@ -88,11 +102,16 @@ Cipher.prototype.update = function update(data, inputEncoding, outputEncoding) {
88102
inputEncoding = inputEncoding || encoding;
89103
outputEncoding = outputEncoding || encoding;
90104

91-
var ret = this._handle.update(data, inputEncoding);
105+
if (typeof data !== 'string' && !isArrayBufferView(data)) {
106+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'data',
107+
['string', 'Buffer', 'TypedArray', 'DataView']);
108+
}
109+
110+
const ret = this._handle.update(data, inputEncoding);
92111

93112
if (outputEncoding && outputEncoding !== 'buffer') {
94113
this._decoder = getDecoder(this._decoder, outputEncoding);
95-
ret = this._decoder.write(ret);
114+
return this._decoder.write(ret);
96115
}
97116

98117
return ret;
@@ -101,42 +120,75 @@ Cipher.prototype.update = function update(data, inputEncoding, outputEncoding) {
101120

102121
Cipher.prototype.final = function final(outputEncoding) {
103122
outputEncoding = outputEncoding || getDefaultEncoding();
104-
var ret = this._handle.final();
123+
const ret = this._handle.final();
105124

106125
if (outputEncoding && outputEncoding !== 'buffer') {
107126
this._decoder = getDecoder(this._decoder, outputEncoding);
108-
ret = this._decoder.end(ret);
127+
return this._decoder.end(ret);
109128
}
110129

111130
return ret;
112131
};
113132

114133

115134
Cipher.prototype.setAutoPadding = function setAutoPadding(ap) {
116-
this._handle.setAutoPadding(ap);
135+
if (this._handle.setAutoPadding(ap) === false)
136+
throw new errors.Error('ERR_CRYPTO_INVALID_STATE', 'setAutoPadding');
117137
return this;
118138
};
119139

120140
Cipher.prototype.getAuthTag = function getAuthTag() {
121-
return this._handle.getAuthTag();
141+
const ret = this._handle.getAuthTag();
142+
if (ret === undefined)
143+
throw new errors.Error('ERR_CRYPTO_INVALID_STATE', 'getAuthTag');
144+
return ret;
122145
};
123146

124147

125148
Cipher.prototype.setAuthTag = function setAuthTag(tagbuf) {
126-
this._handle.setAuthTag(tagbuf);
149+
if (!isArrayBufferView(tagbuf)) {
150+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'buffer',
151+
['Buffer', 'TypedArray', 'DataView']);
152+
}
153+
// Do not do a normal falsy check because the method returns
154+
// undefined if it succeeds. Returns false specifically if it
155+
// errored
156+
if (this._handle.setAuthTag(tagbuf) === false)
157+
throw new errors.Error('ERR_CRYPTO_INVALID_STATE', 'setAuthTag');
127158
return this;
128159
};
129160

130161
Cipher.prototype.setAAD = function setAAD(aadbuf) {
131-
this._handle.setAAD(aadbuf);
162+
if (!isArrayBufferView(aadbuf)) {
163+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'buffer',
164+
['Buffer', 'TypedArray', 'DataView']);
165+
}
166+
if (this._handle.setAAD(aadbuf) === false)
167+
throw new errors.Error('ERR_CRYPTO_INVALID_STATE', 'setAAD');
132168
return this;
133169
};
134170

135171
function Cipheriv(cipher, key, iv, options) {
136172
if (!(this instanceof Cipheriv))
137173
return new Cipheriv(cipher, key, iv, options);
174+
175+
if (typeof cipher !== 'string')
176+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'cipher', 'string');
177+
178+
key = toBuf(key);
179+
if (!isArrayBufferView(key)) {
180+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'key',
181+
['string', 'Buffer', 'TypedArray', 'DataView']);
182+
}
183+
184+
iv = toBuf(iv);
185+
if (!isArrayBufferView(iv)) {
186+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'iv',
187+
['string', 'Buffer', 'TypedArray', 'DataView']);
188+
}
189+
138190
this._handle = new CipherBase(true);
139-
this._handle.initiv(cipher, toBuf(key), toBuf(iv));
191+
this._handle.initiv(cipher, key, iv);
140192
this._decoder = null;
141193

142194
LazyTransform.call(this, options);
@@ -158,8 +210,17 @@ function Decipher(cipher, password, options) {
158210
if (!(this instanceof Decipher))
159211
return new Decipher(cipher, password, options);
160212

213+
if (typeof cipher !== 'string')
214+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'cipher', 'string');
215+
216+
password = toBuf(password);
217+
if (!isArrayBufferView(password)) {
218+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'password',
219+
['string', 'Buffer', 'TypedArray', 'DataView']);
220+
}
221+
161222
this._handle = new CipherBase(false);
162-
this._handle.init(cipher, toBuf(password));
223+
this._handle.init(cipher, password);
163224
this._decoder = null;
164225

165226
LazyTransform.call(this, options);
@@ -182,8 +243,23 @@ function Decipheriv(cipher, key, iv, options) {
182243
if (!(this instanceof Decipheriv))
183244
return new Decipheriv(cipher, key, iv, options);
184245

246+
if (typeof cipher !== 'string')
247+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'cipher', 'string');
248+
249+
key = toBuf(key);
250+
if (!isArrayBufferView(key)) {
251+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'key',
252+
['string', 'Buffer', 'TypedArray', 'DataView']);
253+
}
254+
255+
iv = toBuf(iv);
256+
if (!isArrayBufferView(iv)) {
257+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'iv',
258+
['string', 'Buffer', 'TypedArray', 'DataView']);
259+
}
260+
185261
this._handle = new CipherBase(false);
186-
this._handle.initiv(cipher, toBuf(key), toBuf(iv));
262+
this._handle.initiv(cipher, key, iv);
187263
this._decoder = null;
188264

189265
LazyTransform.call(this, options);

lib/internal/errors.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ E('ERR_CRYPTO_HASH_DIGEST_NO_UTF16', 'hash.digest() does not support UTF-16');
164164
E('ERR_CRYPTO_HASH_FINALIZED', 'Digest already called');
165165
E('ERR_CRYPTO_HASH_UPDATE_FAILED', 'Hash update failed');
166166
E('ERR_CRYPTO_INVALID_DIGEST', 'Invalid digest: %s');
167+
E('ERR_CRYPTO_INVALID_STATE', 'Invalid state for operation %s');
167168
E('ERR_CRYPTO_SIGN_KEY_REQUIRED', 'No key provided to sign');
168169
E('ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH',
169170
'Input buffers must have the same length');

src/node_crypto.cc

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,6 @@
4848
#include <stdlib.h>
4949
#include <string.h>
5050

51-
#define THROW_AND_RETURN_IF_NOT_STRING_OR_BUFFER(val, prefix) \
52-
do { \
53-
if (!Buffer::HasInstance(val) && !val->IsString()) { \
54-
return env->ThrowTypeError(prefix " must be a string or a buffer"); \
55-
} \
56-
} while (0)
57-
5851
#define THROW_AND_RETURN_IF_NOT_BUFFER(val, prefix) \
5952
do { \
6053
if (!Buffer::HasInstance(val)) { \
@@ -3407,14 +3400,8 @@ void CipherBase::Init(const char* cipher_type,
34073400
void CipherBase::Init(const FunctionCallbackInfo<Value>& args) {
34083401
CipherBase* cipher;
34093402
ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder());
3410-
Environment* env = cipher->env();
3411-
3412-
if (args.Length() < 2) {
3413-
return env->ThrowError("Cipher type and key arguments are mandatory");
3414-
}
34153403

3416-
THROW_AND_RETURN_IF_NOT_STRING(args[0], "Cipher type");
3417-
THROW_AND_RETURN_IF_NOT_BUFFER(args[1], "Key");
3404+
CHECK_GE(args.Length(), 2);
34183405

34193406
const node::Utf8Value cipher_type(args.GetIsolate(), args[0]);
34203407
const char* key_buf = Buffer::Data(args[1]);
@@ -3477,13 +3464,7 @@ void CipherBase::InitIv(const FunctionCallbackInfo<Value>& args) {
34773464
ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder());
34783465
Environment* env = cipher->env();
34793466

3480-
if (args.Length() < 3) {
3481-
return env->ThrowError("Cipher type, key, and IV arguments are mandatory");
3482-
}
3483-
3484-
THROW_AND_RETURN_IF_NOT_STRING(args[0], "Cipher type");
3485-
THROW_AND_RETURN_IF_NOT_BUFFER(args[1], "Key");
3486-
THROW_AND_RETURN_IF_NOT_BUFFER(args[2], "IV");
3467+
CHECK_GE(args.Length(), 3);
34873468

34883469
const node::Utf8Value cipher_type(env->isolate(), args[0]);
34893470
ssize_t key_len = Buffer::Length(args[1]);
@@ -3512,7 +3493,7 @@ void CipherBase::GetAuthTag(const FunctionCallbackInfo<Value>& args) {
35123493
if (cipher->initialised_ ||
35133494
cipher->kind_ != kCipher ||
35143495
cipher->auth_tag_len_ == 0) {
3515-
return env->ThrowError("Attempting to get auth tag in unsupported state");
3496+
return args.GetReturnValue().SetUndefined();
35163497
}
35173498

35183499
Local<Object> buf =
@@ -3523,17 +3504,13 @@ void CipherBase::GetAuthTag(const FunctionCallbackInfo<Value>& args) {
35233504

35243505

35253506
void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) {
3526-
Environment* env = Environment::GetCurrent(args);
3527-
3528-
THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "Auth tag");
3529-
35303507
CipherBase* cipher;
35313508
ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder());
35323509

35333510
if (!cipher->initialised_ ||
35343511
!cipher->IsAuthenticatedMode() ||
35353512
cipher->kind_ != kDecipher) {
3536-
return env->ThrowError("Attempting to set auth tag in unsupported state");
3513+
return args.GetReturnValue().Set(false);
35373514
}
35383515

35393516
// FIXME(bnoordhuis) Throw when buffer length is not a valid tag size.
@@ -3563,15 +3540,11 @@ bool CipherBase::SetAAD(const char* data, unsigned int len) {
35633540

35643541

35653542
void CipherBase::SetAAD(const FunctionCallbackInfo<Value>& args) {
3566-
Environment* env = Environment::GetCurrent(args);
3567-
3568-
THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "AAD");
3569-
35703543
CipherBase* cipher;
35713544
ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder());
35723545

35733546
if (!cipher->SetAAD(Buffer::Data(args[0]), Buffer::Length(args[0])))
3574-
env->ThrowError("Attempting to set AAD in unsupported state");
3547+
args.GetReturnValue().Set(false); // Report invalid state failure
35753548
}
35763549

35773550

@@ -3607,8 +3580,6 @@ void CipherBase::Update(const FunctionCallbackInfo<Value>& args) {
36073580
CipherBase* cipher;
36083581
ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder());
36093582

3610-
THROW_AND_RETURN_IF_NOT_STRING_OR_BUFFER(args[0], "Cipher data");
3611-
36123583
unsigned char* out = nullptr;
36133584
bool r;
36143585
int out_len = 0;
@@ -3648,13 +3619,11 @@ bool CipherBase::SetAutoPadding(bool auto_padding) {
36483619

36493620

36503621
void CipherBase::SetAutoPadding(const FunctionCallbackInfo<Value>& args) {
3651-
Environment* env = Environment::GetCurrent(args);
3652-
36533622
CipherBase* cipher;
36543623
ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder());
36553624

36563625
if (!cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue()))
3657-
env->ThrowError("Attempting to set auto padding in unsupported state");
3626+
args.GetReturnValue().Set(false); // Report invalid state failure
36583627
}
36593628

36603629

test/parallel/test-crypto-cipher-decipher.js

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,14 @@ testCipher2(Buffer.from('0123456789abcdef'));
162162
cipher.setAAD(aadbuf);
163163
cipher.setAutoPadding();
164164

165-
assert.throws(() => {
166-
cipher.getAuthTag();
167-
}, /^Error: Attempting to get auth tag in unsupported state$/);
165+
common.expectsError(
166+
() => cipher.getAuthTag(),
167+
{
168+
code: 'ERR_CRYPTO_INVALID_STATE',
169+
type: Error,
170+
message: 'Invalid state for operation getAuthTag'
171+
}
172+
);
168173

169174
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
170175

@@ -175,15 +180,28 @@ testCipher2(Buffer.from('0123456789abcdef'));
175180
decipher.update(encrypted);
176181
decipher.final();
177182

178-
assert.throws(() => {
179-
decipher.setAAD(aadbuf);
180-
}, /^Error: Attempting to set AAD in unsupported state$/);
181-
182-
assert.throws(() => {
183-
decipher.setAuthTag(cipher.getAuthTag());
184-
}, /^Error: Attempting to set auth tag in unsupported state$/);
185-
186-
assert.throws(() => {
187-
decipher.setAutoPadding();
188-
}, /^Error: Attempting to set auto padding in unsupported state$/);
183+
common.expectsError(
184+
() => decipher.setAAD(aadbuf),
185+
{
186+
code: 'ERR_CRYPTO_INVALID_STATE',
187+
type: Error,
188+
message: 'Invalid state for operation setAAD'
189+
});
190+
191+
common.expectsError(
192+
() => decipher.setAuthTag(cipher.getAuthTag()),
193+
{
194+
code: 'ERR_CRYPTO_INVALID_STATE',
195+
type: Error,
196+
message: 'Invalid state for operation setAuthTag'
197+
});
198+
199+
common.expectsError(
200+
() => decipher.setAutoPadding(),
201+
{
202+
code: 'ERR_CRYPTO_INVALID_STATE',
203+
type: Error,
204+
message: 'Invalid state for operation setAutoPadding'
205+
}
206+
);
189207
}

0 commit comments

Comments
 (0)