Skip to content

Commit a0e2c6d

Browse files
yanivfriedensohnTrott
authored andcommitted
src: add error codes to errors thrown in C++
PR-URL: #27700 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent c065773 commit a0e2c6d

File tree

8 files changed

+91
-65
lines changed

8 files changed

+91
-65
lines changed

lib/internal/crypto/cipher.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const {
1212
ERR_INVALID_ARG_TYPE,
1313
ERR_INVALID_OPT_VALUE
1414
} = require('internal/errors').codes;
15-
const { validateString } = require('internal/validators');
15+
const { validateEncoding, validateString } = require('internal/validators');
1616

1717
const {
1818
preparePrivateKey,
@@ -161,6 +161,8 @@ Cipher.prototype.update = function update(data, inputEncoding, outputEncoding) {
161161
throw invalidArrayBufferView('data', data);
162162
}
163163

164+
validateEncoding(data, inputEncoding);
165+
164166
const ret = this[kHandle].update(data, inputEncoding);
165167

166168
if (outputEncoding && outputEncoding !== 'buffer') {

lib/internal/crypto/hash.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ const {
2525
ERR_CRYPTO_HASH_UPDATE_FAILED,
2626
ERR_INVALID_ARG_TYPE
2727
} = require('internal/errors').codes;
28-
const { validateString, validateUint32 } = require('internal/validators');
28+
const { validateEncoding, validateString, validateUint32 } =
29+
require('internal/validators');
2930
const { normalizeEncoding } = require('internal/util');
3031
const { isArrayBufferView } = require('internal/util/types');
3132
const LazyTransform = require('internal/streams/lazy_transform');
@@ -61,6 +62,8 @@ Hash.prototype._flush = function _flush(callback) {
6162
};
6263

6364
Hash.prototype.update = function update(data, encoding) {
65+
encoding = encoding || getDefaultEncoding();
66+
6467
const state = this[kState];
6568
if (state[kFinalized])
6669
throw new ERR_CRYPTO_HASH_FINALIZED();
@@ -74,7 +77,9 @@ Hash.prototype.update = function update(data, encoding) {
7477
data);
7578
}
7679

77-
if (!this[kHandle].update(data, encoding || getDefaultEncoding()))
80+
validateEncoding(data, encoding);
81+
82+
if (!this[kHandle].update(data, encoding))
7883
throw new ERR_CRYPTO_HASH_UPDATE_FAILED();
7984
return this;
8085
};

lib/internal/validators.js

+14
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const {
99
ERR_UNKNOWN_SIGNAL
1010
}
1111
} = require('internal/errors');
12+
const { normalizeEncoding } = require('internal/util');
1213
const {
1314
isArrayBufferView
1415
} = require('internal/util/types');
@@ -142,11 +143,24 @@ const validateBuffer = hideStackFrames((buffer, name = 'buffer') => {
142143
}
143144
});
144145

146+
function validateEncoding(data, encoding) {
147+
const normalizedEncoding = normalizeEncoding(encoding);
148+
const length = data.length;
149+
150+
if (normalizedEncoding === 'hex' && length % 2 !== 0) {
151+
throw new ERR_INVALID_ARG_VALUE('encoding', encoding,
152+
`is invalid for data of length ${length}`);
153+
}
154+
155+
// TODO(bnoordhuis) Add BASE64 check?
156+
}
157+
145158
module.exports = {
146159
isInt32,
147160
isUint32,
148161
parseMode,
149162
validateBuffer,
163+
validateEncoding,
150164
validateInteger,
151165
validateInt32,
152166
validateUint32,

src/node_buffer.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -843,7 +843,7 @@ void IndexOfString(const FunctionCallbackInfo<Value>& args) {
843843

844844
if (IsBigEndian()) {
845845
StringBytes::InlineDecoder decoder;
846-
if (decoder.Decode(env, needle, args[3], UCS2).IsNothing()) return;
846+
if (decoder.Decode(env, needle, enc).IsNothing()) return;
847847
const uint16_t* decoded_string =
848848
reinterpret_cast<const uint16_t*>(decoder.out());
849849

src/node_crypto.cc

+9-6
Original file line numberDiff line numberDiff line change
@@ -4317,8 +4317,9 @@ void CipherBase::Update(const FunctionCallbackInfo<Value>& args) {
43174317
// Only copy the data if we have to, because it's a string
43184318
if (args[0]->IsString()) {
43194319
StringBytes::InlineDecoder decoder;
4320-
if (!decoder.Decode(env, args[0].As<String>(), args[1], UTF8)
4321-
.FromMaybe(false))
4320+
enum encoding enc = ParseEncoding(env->isolate(), args[1], UTF8);
4321+
4322+
if (decoder.Decode(env, args[0].As<String>(), enc).IsNothing())
43224323
return;
43234324
r = cipher->Update(decoder.out(), decoder.size(), &out);
43244325
} else {
@@ -4501,8 +4502,9 @@ void Hmac::HmacUpdate(const FunctionCallbackInfo<Value>& args) {
45014502
bool r = false;
45024503
if (args[0]->IsString()) {
45034504
StringBytes::InlineDecoder decoder;
4504-
if (decoder.Decode(env, args[0].As<String>(), args[1], UTF8)
4505-
.FromMaybe(false)) {
4505+
enum encoding enc = ParseEncoding(env->isolate(), args[1], UTF8);
4506+
4507+
if (!decoder.Decode(env, args[0].As<String>(), enc).IsNothing()) {
45064508
r = hmac->HmacUpdate(decoder.out(), decoder.size());
45074509
}
45084510
} else {
@@ -4626,8 +4628,9 @@ void Hash::HashUpdate(const FunctionCallbackInfo<Value>& args) {
46264628
bool r = true;
46274629
if (args[0]->IsString()) {
46284630
StringBytes::InlineDecoder decoder;
4629-
if (!decoder.Decode(env, args[0].As<String>(), args[1], UTF8)
4630-
.FromMaybe(false)) {
4631+
enum encoding enc = ParseEncoding(env->isolate(), args[1], UTF8);
4632+
4633+
if (decoder.Decode(env, args[0].As<String>(), enc).IsNothing()) {
46314634
args.GetReturnValue().Set(false);
46324635
return;
46334636
}

src/string_bytes.cc

-9
Original file line numberDiff line numberDiff line change
@@ -392,15 +392,6 @@ size_t StringBytes::Write(Isolate* isolate,
392392
}
393393

394394

395-
bool StringBytes::IsValidString(Local<String> string,
396-
enum encoding enc) {
397-
if (enc == HEX && string->Length() % 2 != 0)
398-
return false;
399-
// TODO(bnoordhuis) Add BASE64 check?
400-
return true;
401-
}
402-
403-
404395
// Quick and dirty size calculation
405396
// Will always be at least big enough, but may have some extra
406397
// UTF8 can be as much as 3x the size, Base64 can have 1-2 extra bytes

src/string_bytes.h

+1-14
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,7 @@ class StringBytes {
3737
public:
3838
inline v8::Maybe<bool> Decode(Environment* env,
3939
v8::Local<v8::String> string,
40-
v8::Local<v8::Value> encoding,
41-
enum encoding _default) {
42-
enum encoding enc = ParseEncoding(env->isolate(), encoding, _default);
43-
if (!StringBytes::IsValidString(string, enc)) {
44-
env->ThrowTypeError("Bad input string");
45-
return v8::Nothing<bool>();
46-
}
47-
40+
enum encoding enc) {
4841
size_t storage;
4942
if (!StringBytes::StorageSize(env->isolate(), string, enc).To(&storage))
5043
return v8::Nothing<bool>();
@@ -60,12 +53,6 @@ class StringBytes {
6053
inline size_t size() const { return length(); }
6154
};
6255

63-
// Does the string match the encoding? Quick but non-exhaustive.
64-
// Example: a HEX string must have a length that's a multiple of two.
65-
// FIXME(bnoordhuis) IsMaybeValidString()? Naming things is hard...
66-
static bool IsValidString(v8::Local<v8::String> string,
67-
enum encoding enc);
68-
6956
// Fast, but can be 2 bytes oversized for Base64, and
7057
// as much as triple UTF-8 strings <= 65536 chars in length
7158
static v8::Maybe<size_t> StorageSize(v8::Isolate* isolate,

test/parallel/test-crypto.js

+56-32
Original file line numberDiff line numberDiff line change
@@ -167,42 +167,66 @@ testImmutability(crypto.getCurves);
167167

168168
// Regression tests for https://github.com/nodejs/node-v0.x-archive/pull/5725:
169169
// hex input that's not a power of two should throw, not assert in C++ land.
170-
assert.throws(function() {
171-
crypto.createCipher('aes192', 'test').update('0', 'hex');
172-
}, (err) => {
173-
const errorMessage =
174-
common.hasFipsCrypto ? /not supported in FIPS mode/ : /Bad input string/;
175-
// Throws general Error, so there is no opensslErrorStack property.
176-
if ((err instanceof Error) &&
177-
errorMessage.test(err) &&
178-
err.opensslErrorStack === undefined) {
179-
return true;
180-
}
181-
});
182170

183-
assert.throws(function() {
184-
crypto.createDecipher('aes192', 'test').update('0', 'hex');
185-
}, (err) => {
186-
const errorMessage =
187-
common.hasFipsCrypto ? /not supported in FIPS mode/ : /Bad input string/;
188-
// Throws general Error, so there is no opensslErrorStack property.
189-
if ((err instanceof Error) &&
190-
errorMessage.test(err) &&
191-
err.opensslErrorStack === undefined) {
192-
return true;
171+
common.expectsError(
172+
() => crypto.createCipher('aes192', 'test').update('0', 'hex'),
173+
Object.assign(
174+
common.hasFipsCrypto ?
175+
{
176+
code: undefined,
177+
type: Error,
178+
message: /not supported in FIPS mode/,
179+
} :
180+
{
181+
code: 'ERR_INVALID_ARG_VALUE',
182+
type: TypeError,
183+
message: "The argument 'encoding' is invalid for data of length 1." +
184+
" Received 'hex'",
185+
},
186+
{ opensslErrorStack: undefined }
187+
)
188+
);
189+
190+
common.expectsError(
191+
() => crypto.createDecipher('aes192', 'test').update('0', 'hex'),
192+
Object.assign(
193+
common.hasFipsCrypto ?
194+
{
195+
code: undefined,
196+
type: Error,
197+
message: /not supported in FIPS mode/,
198+
} :
199+
{
200+
code: 'ERR_INVALID_ARG_VALUE',
201+
type: TypeError,
202+
message: "The argument 'encoding' is invalid for data of length 1." +
203+
" Received 'hex'",
204+
},
205+
{ opensslErrorStack: undefined }
206+
)
207+
);
208+
209+
common.expectsError(
210+
() => crypto.createHash('sha1').update('0', 'hex'),
211+
{
212+
code: 'ERR_INVALID_ARG_VALUE',
213+
type: TypeError,
214+
message: "The argument 'encoding' is invalid for data of length 1." +
215+
" Received 'hex'",
216+
opensslErrorStack: undefined
193217
}
194-
});
218+
);
195219

196-
assert.throws(function() {
197-
crypto.createHash('sha1').update('0', 'hex');
198-
}, (err) => {
199-
// Throws TypeError, so there is no opensslErrorStack property.
200-
if ((err instanceof Error) &&
201-
/^TypeError: Bad input string$/.test(err) &&
202-
err.opensslErrorStack === undefined) {
203-
return true;
220+
common.expectsError(
221+
() => crypto.createHmac('sha256', 'a secret').update('0', 'hex'),
222+
{
223+
code: 'ERR_INVALID_ARG_VALUE',
224+
type: TypeError,
225+
message: "The argument 'encoding' is invalid for data of length 1." +
226+
" Received 'hex'",
227+
opensslErrorStack: undefined
204228
}
205-
});
229+
);
206230

207231
assert.throws(function() {
208232
const priv = [

0 commit comments

Comments
 (0)