src: add error codes to errors thrown in C++
PR-URL: https://github.com/nodejs/node/pull/27700 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
parent
c065773cc5
commit
a0e2c6d284
@ -12,7 +12,7 @@ const {
|
|||||||
ERR_INVALID_ARG_TYPE,
|
ERR_INVALID_ARG_TYPE,
|
||||||
ERR_INVALID_OPT_VALUE
|
ERR_INVALID_OPT_VALUE
|
||||||
} = require('internal/errors').codes;
|
} = require('internal/errors').codes;
|
||||||
const { validateString } = require('internal/validators');
|
const { validateEncoding, validateString } = require('internal/validators');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
preparePrivateKey,
|
preparePrivateKey,
|
||||||
@ -161,6 +161,8 @@ Cipher.prototype.update = function update(data, inputEncoding, outputEncoding) {
|
|||||||
throw invalidArrayBufferView('data', data);
|
throw invalidArrayBufferView('data', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateEncoding(data, inputEncoding);
|
||||||
|
|
||||||
const ret = this[kHandle].update(data, inputEncoding);
|
const ret = this[kHandle].update(data, inputEncoding);
|
||||||
|
|
||||||
if (outputEncoding && outputEncoding !== 'buffer') {
|
if (outputEncoding && outputEncoding !== 'buffer') {
|
||||||
|
@ -25,7 +25,8 @@ const {
|
|||||||
ERR_CRYPTO_HASH_UPDATE_FAILED,
|
ERR_CRYPTO_HASH_UPDATE_FAILED,
|
||||||
ERR_INVALID_ARG_TYPE
|
ERR_INVALID_ARG_TYPE
|
||||||
} = require('internal/errors').codes;
|
} = require('internal/errors').codes;
|
||||||
const { validateString, validateUint32 } = require('internal/validators');
|
const { validateEncoding, validateString, validateUint32 } =
|
||||||
|
require('internal/validators');
|
||||||
const { normalizeEncoding } = require('internal/util');
|
const { normalizeEncoding } = require('internal/util');
|
||||||
const { isArrayBufferView } = require('internal/util/types');
|
const { isArrayBufferView } = require('internal/util/types');
|
||||||
const LazyTransform = require('internal/streams/lazy_transform');
|
const LazyTransform = require('internal/streams/lazy_transform');
|
||||||
@ -61,6 +62,8 @@ Hash.prototype._flush = function _flush(callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Hash.prototype.update = function update(data, encoding) {
|
Hash.prototype.update = function update(data, encoding) {
|
||||||
|
encoding = encoding || getDefaultEncoding();
|
||||||
|
|
||||||
const state = this[kState];
|
const state = this[kState];
|
||||||
if (state[kFinalized])
|
if (state[kFinalized])
|
||||||
throw new ERR_CRYPTO_HASH_FINALIZED();
|
throw new ERR_CRYPTO_HASH_FINALIZED();
|
||||||
@ -74,7 +77,9 @@ Hash.prototype.update = function update(data, encoding) {
|
|||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this[kHandle].update(data, encoding || getDefaultEncoding()))
|
validateEncoding(data, encoding);
|
||||||
|
|
||||||
|
if (!this[kHandle].update(data, encoding))
|
||||||
throw new ERR_CRYPTO_HASH_UPDATE_FAILED();
|
throw new ERR_CRYPTO_HASH_UPDATE_FAILED();
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
@ -9,6 +9,7 @@ const {
|
|||||||
ERR_UNKNOWN_SIGNAL
|
ERR_UNKNOWN_SIGNAL
|
||||||
}
|
}
|
||||||
} = require('internal/errors');
|
} = require('internal/errors');
|
||||||
|
const { normalizeEncoding } = require('internal/util');
|
||||||
const {
|
const {
|
||||||
isArrayBufferView
|
isArrayBufferView
|
||||||
} = require('internal/util/types');
|
} = require('internal/util/types');
|
||||||
@ -142,11 +143,24 @@ const validateBuffer = hideStackFrames((buffer, name = 'buffer') => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function validateEncoding(data, encoding) {
|
||||||
|
const normalizedEncoding = normalizeEncoding(encoding);
|
||||||
|
const length = data.length;
|
||||||
|
|
||||||
|
if (normalizedEncoding === 'hex' && length % 2 !== 0) {
|
||||||
|
throw new ERR_INVALID_ARG_VALUE('encoding', encoding,
|
||||||
|
`is invalid for data of length ${length}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bnoordhuis) Add BASE64 check?
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
isInt32,
|
isInt32,
|
||||||
isUint32,
|
isUint32,
|
||||||
parseMode,
|
parseMode,
|
||||||
validateBuffer,
|
validateBuffer,
|
||||||
|
validateEncoding,
|
||||||
validateInteger,
|
validateInteger,
|
||||||
validateInt32,
|
validateInt32,
|
||||||
validateUint32,
|
validateUint32,
|
||||||
|
@ -843,7 +843,7 @@ void IndexOfString(const FunctionCallbackInfo<Value>& args) {
|
|||||||
|
|
||||||
if (IsBigEndian()) {
|
if (IsBigEndian()) {
|
||||||
StringBytes::InlineDecoder decoder;
|
StringBytes::InlineDecoder decoder;
|
||||||
if (decoder.Decode(env, needle, args[3], UCS2).IsNothing()) return;
|
if (decoder.Decode(env, needle, enc).IsNothing()) return;
|
||||||
const uint16_t* decoded_string =
|
const uint16_t* decoded_string =
|
||||||
reinterpret_cast<const uint16_t*>(decoder.out());
|
reinterpret_cast<const uint16_t*>(decoder.out());
|
||||||
|
|
||||||
|
@ -4317,8 +4317,9 @@ void CipherBase::Update(const FunctionCallbackInfo<Value>& args) {
|
|||||||
// 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
|
||||||
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)
|
enum encoding enc = ParseEncoding(env->isolate(), args[1], UTF8);
|
||||||
.FromMaybe(false))
|
|
||||||
|
if (decoder.Decode(env, args[0].As<String>(), enc).IsNothing())
|
||||||
return;
|
return;
|
||||||
r = cipher->Update(decoder.out(), decoder.size(), &out);
|
r = cipher->Update(decoder.out(), decoder.size(), &out);
|
||||||
} else {
|
} else {
|
||||||
@ -4501,8 +4502,9 @@ void Hmac::HmacUpdate(const FunctionCallbackInfo<Value>& args) {
|
|||||||
bool r = false;
|
bool r = false;
|
||||||
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)
|
enum encoding enc = ParseEncoding(env->isolate(), args[1], UTF8);
|
||||||
.FromMaybe(false)) {
|
|
||||||
|
if (!decoder.Decode(env, args[0].As<String>(), enc).IsNothing()) {
|
||||||
r = hmac->HmacUpdate(decoder.out(), decoder.size());
|
r = hmac->HmacUpdate(decoder.out(), decoder.size());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -4626,8 +4628,9 @@ void Hash::HashUpdate(const FunctionCallbackInfo<Value>& args) {
|
|||||||
bool r = true;
|
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)
|
enum encoding enc = ParseEncoding(env->isolate(), args[1], UTF8);
|
||||||
.FromMaybe(false)) {
|
|
||||||
|
if (decoder.Decode(env, args[0].As<String>(), enc).IsNothing()) {
|
||||||
args.GetReturnValue().Set(false);
|
args.GetReturnValue().Set(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -392,15 +392,6 @@ size_t StringBytes::Write(Isolate* isolate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool StringBytes::IsValidString(Local<String> string,
|
|
||||||
enum encoding enc) {
|
|
||||||
if (enc == HEX && string->Length() % 2 != 0)
|
|
||||||
return false;
|
|
||||||
// TODO(bnoordhuis) Add BASE64 check?
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Quick and dirty size calculation
|
// Quick and dirty size calculation
|
||||||
// Will always be at least big enough, but may have some extra
|
// Will always be at least big enough, but may have some extra
|
||||||
// UTF8 can be as much as 3x the size, Base64 can have 1-2 extra bytes
|
// UTF8 can be as much as 3x the size, Base64 can have 1-2 extra bytes
|
||||||
|
@ -37,14 +37,7 @@ class StringBytes {
|
|||||||
public:
|
public:
|
||||||
inline v8::Maybe<bool> Decode(Environment* env,
|
inline v8::Maybe<bool> Decode(Environment* env,
|
||||||
v8::Local<v8::String> string,
|
v8::Local<v8::String> string,
|
||||||
v8::Local<v8::Value> encoding,
|
enum encoding enc) {
|
||||||
enum encoding _default) {
|
|
||||||
enum encoding enc = ParseEncoding(env->isolate(), encoding, _default);
|
|
||||||
if (!StringBytes::IsValidString(string, enc)) {
|
|
||||||
env->ThrowTypeError("Bad input string");
|
|
||||||
return v8::Nothing<bool>();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t storage;
|
size_t storage;
|
||||||
if (!StringBytes::StorageSize(env->isolate(), string, enc).To(&storage))
|
if (!StringBytes::StorageSize(env->isolate(), string, enc).To(&storage))
|
||||||
return v8::Nothing<bool>();
|
return v8::Nothing<bool>();
|
||||||
@ -60,12 +53,6 @@ class StringBytes {
|
|||||||
inline size_t size() const { return length(); }
|
inline size_t size() const { return length(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Does the string match the encoding? Quick but non-exhaustive.
|
|
||||||
// Example: a HEX string must have a length that's a multiple of two.
|
|
||||||
// FIXME(bnoordhuis) IsMaybeValidString()? Naming things is hard...
|
|
||||||
static bool IsValidString(v8::Local<v8::String> string,
|
|
||||||
enum encoding enc);
|
|
||||||
|
|
||||||
// Fast, but can be 2 bytes oversized for Base64, and
|
// Fast, but can be 2 bytes oversized for Base64, and
|
||||||
// as much as triple UTF-8 strings <= 65536 chars in length
|
// as much as triple UTF-8 strings <= 65536 chars in length
|
||||||
static v8::Maybe<size_t> StorageSize(v8::Isolate* isolate,
|
static v8::Maybe<size_t> StorageSize(v8::Isolate* isolate,
|
||||||
|
@ -167,42 +167,66 @@ testImmutability(crypto.getCurves);
|
|||||||
|
|
||||||
// Regression tests for https://github.com/nodejs/node-v0.x-archive/pull/5725:
|
// Regression tests for https://github.com/nodejs/node-v0.x-archive/pull/5725:
|
||||||
// hex input that's not a power of two should throw, not assert in C++ land.
|
// hex input that's not a power of two should throw, not assert in C++ land.
|
||||||
assert.throws(function() {
|
|
||||||
crypto.createCipher('aes192', 'test').update('0', 'hex');
|
|
||||||
}, (err) => {
|
|
||||||
const errorMessage =
|
|
||||||
common.hasFipsCrypto ? /not supported in FIPS mode/ : /Bad input string/;
|
|
||||||
// Throws general Error, so there is no opensslErrorStack property.
|
|
||||||
if ((err instanceof Error) &&
|
|
||||||
errorMessage.test(err) &&
|
|
||||||
err.opensslErrorStack === undefined) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.throws(function() {
|
common.expectsError(
|
||||||
crypto.createDecipher('aes192', 'test').update('0', 'hex');
|
() => crypto.createCipher('aes192', 'test').update('0', 'hex'),
|
||||||
}, (err) => {
|
Object.assign(
|
||||||
const errorMessage =
|
common.hasFipsCrypto ?
|
||||||
common.hasFipsCrypto ? /not supported in FIPS mode/ : /Bad input string/;
|
{
|
||||||
// Throws general Error, so there is no opensslErrorStack property.
|
code: undefined,
|
||||||
if ((err instanceof Error) &&
|
type: Error,
|
||||||
errorMessage.test(err) &&
|
message: /not supported in FIPS mode/,
|
||||||
err.opensslErrorStack === undefined) {
|
} :
|
||||||
return true;
|
{
|
||||||
}
|
code: 'ERR_INVALID_ARG_VALUE',
|
||||||
});
|
type: TypeError,
|
||||||
|
message: "The argument 'encoding' is invalid for data of length 1." +
|
||||||
|
" Received 'hex'",
|
||||||
|
},
|
||||||
|
{ opensslErrorStack: undefined }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
assert.throws(function() {
|
common.expectsError(
|
||||||
crypto.createHash('sha1').update('0', 'hex');
|
() => crypto.createDecipher('aes192', 'test').update('0', 'hex'),
|
||||||
}, (err) => {
|
Object.assign(
|
||||||
// Throws TypeError, so there is no opensslErrorStack property.
|
common.hasFipsCrypto ?
|
||||||
if ((err instanceof Error) &&
|
{
|
||||||
/^TypeError: Bad input string$/.test(err) &&
|
code: undefined,
|
||||||
err.opensslErrorStack === undefined) {
|
type: Error,
|
||||||
return true;
|
message: /not supported in FIPS mode/,
|
||||||
|
} :
|
||||||
|
{
|
||||||
|
code: 'ERR_INVALID_ARG_VALUE',
|
||||||
|
type: TypeError,
|
||||||
|
message: "The argument 'encoding' is invalid for data of length 1." +
|
||||||
|
" Received 'hex'",
|
||||||
|
},
|
||||||
|
{ opensslErrorStack: undefined }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
common.expectsError(
|
||||||
|
() => crypto.createHash('sha1').update('0', 'hex'),
|
||||||
|
{
|
||||||
|
code: 'ERR_INVALID_ARG_VALUE',
|
||||||
|
type: TypeError,
|
||||||
|
message: "The argument 'encoding' is invalid for data of length 1." +
|
||||||
|
" Received 'hex'",
|
||||||
|
opensslErrorStack: undefined
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
|
common.expectsError(
|
||||||
|
() => crypto.createHmac('sha256', 'a secret').update('0', 'hex'),
|
||||||
|
{
|
||||||
|
code: 'ERR_INVALID_ARG_VALUE',
|
||||||
|
type: TypeError,
|
||||||
|
message: "The argument 'encoding' is invalid for data of length 1." +
|
||||||
|
" Received 'hex'",
|
||||||
|
opensslErrorStack: undefined
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
assert.throws(function() {
|
assert.throws(function() {
|
||||||
const priv = [
|
const priv = [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user