diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 815968cbd2c14a..70d873eeb63a21 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -226,9 +226,9 @@ static int NoPasswordCallback(char* buf, int size, int rwflag, void* u) { // namespace node::crypto::error namespace error { -void Decorate(Environment* env, Local obj, +Maybe Decorate(Environment* env, Local obj, unsigned long err) { // NOLINT(runtime/int) - if (err == 0) return; // No decoration possible. + if (err == 0) return Just(true); // No decoration necessary. const char* ls = ERR_lib_error_string(err); const char* fs = ERR_func_error_string(err); @@ -240,19 +240,19 @@ void Decorate(Environment* env, Local obj, if (ls != nullptr) { if (obj->Set(context, env->library_string(), OneByteString(isolate, ls)).IsNothing()) { - return; + return Nothing(); } } if (fs != nullptr) { if (obj->Set(context, env->function_string(), OneByteString(isolate, fs)).IsNothing()) { - return; + return Nothing(); } } if (rs != nullptr) { if (obj->Set(context, env->reason_string(), OneByteString(isolate, rs)).IsNothing()) { - return; + return Nothing(); } // SSL has no API to recover the error name from the number, so we @@ -325,8 +325,10 @@ void Decorate(Environment* env, Local obj, if (obj->Set(env->isolate()->GetCurrentContext(), env->code_string(), OneByteString(env->isolate(), code)).IsNothing()) - return; + return Nothing(); } + + return Just(true); } } // namespace error @@ -342,7 +344,7 @@ struct CryptoErrorVector : public std::vector { std::reverse(begin(), end()); } - inline Local ToException( + inline MaybeLocal ToException( Environment* env, Local exception_string = Local()) const { if (exception_string.IsEmpty()) { @@ -364,10 +366,11 @@ struct CryptoErrorVector : public std::vector { if (!empty()) { CHECK(exception_v->IsObject()); Local exception = exception_v.As(); - exception->Set(env->context(), + Maybe ok = exception->Set(env->context(), env->openssl_error_stack(), - ToV8Value(env->context(), *this).ToLocalChecked()) - .Check(); + ToV8Value(env->context(), *this).ToLocalChecked()); + if (ok.IsNothing()) + return MaybeLocal(); } return exception_v; @@ -386,16 +389,19 @@ void ThrowCryptoError(Environment* env, message = message_buffer; } HandleScope scope(env->isolate()); - auto exception_string = + Local exception_string = String::NewFromUtf8(env->isolate(), message, NewStringType::kNormal) .ToLocalChecked(); CryptoErrorVector errors; errors.Capture(); - Local exception = errors.ToException(env, exception_string); + Local exception; + if (!errors.ToException(env, exception_string).ToLocal(&exception)) + return; Local obj; if (!exception->ToObject(env->context()).ToLocal(&obj)) return; - error::Decorate(env, obj, err); + if (error::Decorate(env, obj, err).IsNothing()) + return; env->isolate()->ThrowException(exception); } @@ -5872,7 +5878,7 @@ struct RandomBytesJob : public CryptoJob { inline Local ToResult() const { if (errors.empty()) return Undefined(env->isolate()); - return errors.ToException(env); + return errors.ToException(env).ToLocalChecked(); } }; @@ -6009,7 +6015,7 @@ struct ScryptJob : public CryptoJob { inline Local ToResult() const { if (errors.empty()) return Undefined(env->isolate()); - return errors.ToException(env); + return errors.ToException(env).ToLocalChecked(); } inline void Cleanse() { @@ -6285,7 +6291,7 @@ class GenerateKeyPairJob : public CryptoJob { if (errors_.empty()) errors_.Capture(); CHECK(!errors_.empty()); - *err = errors_.ToException(env); + *err = errors_.ToException(env).ToLocalChecked(); *pubkey = Undefined(env->isolate()); *privkey = Undefined(env->isolate()); } diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js index 74de785c6453e0..062c2d08377cfe 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -29,6 +29,45 @@ const modSize = 1024; 'instance when called without `new`'); } +// Test handling of exceptional conditions +{ + const library = { + configurable: true, + set() { + throw new Error('bye, bye, library'); + } + }; + Object.defineProperty(Object.prototype, 'library', library); + + assert.throws(() => { + crypto.createSign('sha1').sign( + `-----BEGIN RSA PRIVATE KEY----- + AAAAAAAAAAAA + -----END RSA PRIVATE KEY-----`); + }, { message: 'bye, bye, library' }); + + delete Object.prototype.library; + + const errorStack = { + configurable: true, + set() { + throw new Error('bye, bye, error stack'); + } + }; + Object.defineProperty(Object.prototype, 'opensslErrorStack', errorStack); + + assert.throws(() => { + crypto.createSign('SHA1') + .update('Test123') + .sign({ + key: keyPem, + padding: crypto.constants.RSA_PKCS1_OAEP_PADDING + }); + }, { message: 'bye, bye, error stack' }); + + delete Object.prototype.opensslErrorStack; +} + common.expectsError( () => crypto.createVerify('SHA256').verify({ key: certPem, @@ -300,7 +339,14 @@ common.expectsError( key: keyPem, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING }); - }, /^Error:.*illegal or unsupported padding mode$/); + }, { + code: 'ERR_OSSL_RSA_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE', + message: /illegal or unsupported padding mode/, + opensslErrorStack: [ + 'error:06089093:digital envelope routines:EVP_PKEY_CTX_ctrl:' + + 'command not supported', + ], + }); } // Test throws exception when key options is null