diff --git a/src/crypto/crypto_cipher.h b/src/crypto/crypto_cipher.h index 70dea9ff3c5531..c8dd3e48f718fd 100644 --- a/src/crypto/crypto_cipher.h +++ b/src/crypto/crypto_cipher.h @@ -227,18 +227,18 @@ class CipherJob final : public CryptoJob { // Success! return; } - CryptoErrorVector* errors = CryptoJob::errors(); + CryptoErrorStore* errors = CryptoJob::errors(); errors->Capture(); - if (errors->empty()) { + if (errors->Empty()) { switch (status) { case WebCryptoCipherStatus::OK: UNREACHABLE(); break; case WebCryptoCipherStatus::INVALID_KEY_TYPE: - errors->emplace_back("Invalid key type."); + errors->Insert(NodeCryptoError::INVALID_KEY_TYPE); break; case WebCryptoCipherStatus::FAILED: - errors->emplace_back("Cipher job failed."); + errors->Insert(NodeCryptoError::CIPHER_JOB_FAILED); break; } } @@ -248,17 +248,17 @@ class CipherJob final : public CryptoJob { v8::Local* err, v8::Local* result) override { Environment* env = AsyncWrap::env(); - CryptoErrorVector* errors = CryptoJob::errors(); + CryptoErrorStore* errors = CryptoJob::errors(); if (out_.size() > 0) { - CHECK(errors->empty()); + CHECK(errors->Empty()); *err = v8::Undefined(env->isolate()); *result = out_.ToArrayBuffer(env); return v8::Just(!result->IsEmpty()); } - if (errors->empty()) + if (errors->Empty()) errors->Capture(); - CHECK(!errors->empty()); + CHECK(!errors->Empty()); *result = v8::Undefined(env->isolate()); return v8::Just(errors->ToException(env).ToLocal(err)); } diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index b63ae15ab4577f..80f04efbed0f17 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -551,7 +551,7 @@ void SecureContext::SetEngineKey(const FunctionCallbackInfo& args) { CHECK_EQ(args.Length(), 2); - CryptoErrorVector errors; + CryptoErrorStore errors; Utf8Value engine_id(env->isolate(), args[1]); EnginePointer engine = LoadEngineById(*engine_id, &errors); if (!engine) { @@ -987,7 +987,7 @@ void SecureContext::SetClientCertEngine( // support multiple calls to SetClientCertEngine. CHECK(!sc->client_cert_engine_provided_); - CryptoErrorVector errors; + CryptoErrorStore errors; const Utf8Value engine_id(env->isolate(), args[0]); EnginePointer engine = LoadEngineById(*engine_id, &errors); if (!engine) { diff --git a/src/crypto/crypto_keygen.h b/src/crypto/crypto_keygen.h index 58e3b8211d6bd7..6231eb7eef8320 100644 --- a/src/crypto/crypto_keygen.h +++ b/src/crypto/crypto_keygen.h @@ -82,10 +82,10 @@ class KeyGenJob final : public CryptoJob { // Success! break; case KeyGenJobStatus::FAILED: { - CryptoErrorVector* errors = CryptoJob::errors(); + CryptoErrorStore* errors = CryptoJob::errors(); errors->Capture(); - if (errors->empty()) - errors->push_back(std::string("Key generation job failed")); + if (errors->Empty()) + errors->Insert(NodeCryptoError::KEY_GENERATION_JOB_FAILED); } } } @@ -94,7 +94,7 @@ class KeyGenJob final : public CryptoJob { v8::Local* err, v8::Local* result) override { Environment* env = AsyncWrap::env(); - CryptoErrorVector* errors = CryptoJob::errors(); + CryptoErrorStore* errors = CryptoJob::errors(); AdditionalParams* params = CryptoJob::params(); if (status_ == KeyGenJobStatus::OK && LIKELY(!KeyGenTraits::EncodeKey(env, params, result).IsNothing())) { @@ -102,9 +102,9 @@ class KeyGenJob final : public CryptoJob { return v8::Just(true); } - if (errors->empty()) + if (errors->Empty()) errors->Capture(); - CHECK(!errors->empty()); + CHECK(!errors->Empty()); *result = Undefined(env->isolate()); return v8::Just(errors->ToException(env).ToLocal(err)); } diff --git a/src/crypto/crypto_keys.h b/src/crypto/crypto_keys.h index 03d1f9a33b1b61..21ccb73e330aa8 100644 --- a/src/crypto/crypto_keys.h +++ b/src/crypto/crypto_keys.h @@ -347,18 +347,18 @@ class KeyExportJob final : public CryptoJob { // Success! return; } - CryptoErrorVector* errors = CryptoJob::errors(); + CryptoErrorStore* errors = CryptoJob::errors(); errors->Capture(); - if (errors->empty()) { + if (errors->Empty()) { switch (status) { case WebCryptoKeyExportStatus::OK: UNREACHABLE(); break; case WebCryptoKeyExportStatus::INVALID_KEY_TYPE: - errors->emplace_back("Invalid key type."); + errors->Insert(NodeCryptoError::INVALID_KEY_TYPE); break; case WebCryptoKeyExportStatus::FAILED: - errors->emplace_back("Cipher job failed."); + errors->Insert(NodeCryptoError::CIPHER_JOB_FAILED); break; } } @@ -368,17 +368,17 @@ class KeyExportJob final : public CryptoJob { v8::Local* err, v8::Local* result) override { Environment* env = AsyncWrap::env(); - CryptoErrorVector* errors = CryptoJob::errors(); + CryptoErrorStore* errors = CryptoJob::errors(); if (out_.size() > 0) { - CHECK(errors->empty()); + CHECK(errors->Empty()); *err = v8::Undefined(env->isolate()); *result = out_.ToArrayBuffer(env); return v8::Just(!result->IsEmpty()); } - if (errors->empty()) + if (errors->Empty()) errors->Capture(); - CHECK(!errors->empty()); + CHECK(!errors->Empty()); *result = v8::Undefined(env->isolate()); return v8::Just(errors->ToException(env).ToLocal(err)); } diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc index 0d44d0bb4e16f7..9644dc4dc7f9aa 100644 --- a/src/crypto/crypto_util.cc +++ b/src/crypto/crypto_util.cc @@ -187,44 +187,52 @@ void TestFipsCrypto(const v8::FunctionCallbackInfo& args) { args.GetReturnValue().Set(enabled); } -void CryptoErrorVector::Capture() { - clear(); - while (auto err = ERR_get_error()) { +void CryptoErrorStore::Capture() { + errors_.clear(); + while (const uint32_t err = ERR_get_error()) { char buf[256]; ERR_error_string_n(err, buf, sizeof(buf)); - push_back(buf); + errors_.emplace_back(buf); } - std::reverse(begin(), end()); + std::reverse(std::begin(errors_), std::end(errors_)); } -MaybeLocal CryptoErrorVector::ToException( +bool CryptoErrorStore::Empty() const { + return errors_.empty(); +} + +MaybeLocal CryptoErrorStore::ToException( Environment* env, Local exception_string) const { if (exception_string.IsEmpty()) { - CryptoErrorVector copy(*this); - if (copy.empty()) copy.push_back("no error"); // But possibly a bug... + CryptoErrorStore copy(*this); + if (copy.Empty()) { + // But possibly a bug... + copy.Insert(NodeCryptoError::OK); + } // Use last element as the error message, everything else goes // into the .opensslErrorStack property on the exception object. + const std::string& last_error_string = copy.errors_.back(); Local exception_string; if (!String::NewFromUtf8( env->isolate(), - copy.back().data(), + last_error_string.data(), NewStringType::kNormal, - copy.back().size()).ToLocal(&exception_string)) { + last_error_string.size()).ToLocal(&exception_string)) { return MaybeLocal(); } - copy.pop_back(); + copy.errors_.pop_back(); return copy.ToException(env, exception_string); } Local exception_v = Exception::Error(exception_string); CHECK(!exception_v.IsEmpty()); - if (!empty()) { + if (!Empty()) { CHECK(exception_v->IsObject()); Local exception = exception_v.As(); Local stack; - if (!ToV8Value(env->context(), *this).ToLocal(&stack) || + if (!ToV8Value(env->context(), errors_).ToLocal(&stack) || exception->Set(env->context(), env->openssl_error_stack(), stack) .IsNothing()) { return MaybeLocal(); @@ -509,7 +517,7 @@ void ThrowCryptoError(Environment* env, Local obj; if (!String::NewFromUtf8(env->isolate(), message).ToLocal(&exception_string)) return; - CryptoErrorVector errors; + CryptoErrorStore errors; errors.Capture(); if (!errors.ToException(env, exception_string).ToLocal(&exception) || !exception->ToObject(env->context()).ToLocal(&obj) || @@ -520,7 +528,7 @@ void ThrowCryptoError(Environment* env, } #ifndef OPENSSL_NO_ENGINE -EnginePointer LoadEngineById(const char* id, CryptoErrorVector* errors) { +EnginePointer LoadEngineById(const char* id, CryptoErrorStore* errors) { MarkPopErrorOnReturn mark_pop_error_on_return; EnginePointer engine(ENGINE_by_id(id)); @@ -539,14 +547,14 @@ EnginePointer LoadEngineById(const char* id, CryptoErrorVector* errors) { if (ERR_get_error() != 0) { errors->Capture(); } else { - errors->push_back(std::string("Engine \"") + id + "\" was not found"); + errors->Insert(NodeCryptoError::ENGINE_NOT_FOUND, id); } } return engine; } -bool SetEngine(const char* id, uint32_t flags, CryptoErrorVector* errors) { +bool SetEngine(const char* id, uint32_t flags, CryptoErrorStore* errors) { ClearErrorOnReturn clear_error_on_return; EnginePointer engine = LoadEngineById(id, errors); if (!engine) diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h index c04b68efbf8c71..1a7bef77a40930 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h @@ -159,15 +159,64 @@ void Decode(const v8::FunctionCallbackInfo& args, } } +enum class NodeCryptoError { + CIPHER_JOB_FAILED, + DERIVING_BITS_FAILED, + ENGINE_NOT_FOUND, + INVALID_KEY_TYPE, + KEY_GENERATION_JOB_FAILED, + OK +}; + // Utility struct used to harvest error information from openssl's error stack -struct CryptoErrorVector : public std::vector { +struct CryptoErrorStore final : public MemoryRetainer { + public: void Capture(); + bool Empty() const; + + template + void Insert(const NodeCryptoError error, Args&&... args); + v8::MaybeLocal ToException( Environment* env, v8::Local exception_string = v8::Local()) const; + + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(CryptoErrorStore); + SET_SELF_SIZE(CryptoErrorStore); + + private: + std::vector errors_; }; +template +void CryptoErrorStore::Insert(const NodeCryptoError error, Args&&... args) { + const char* error_string = nullptr; + switch (error) { + case NodeCryptoError::CIPHER_JOB_FAILED: + error_string = "Cipher job failed"; + break; + case NodeCryptoError::DERIVING_BITS_FAILED: + error_string = "Deriving bits failed"; + break; + case NodeCryptoError::ENGINE_NOT_FOUND: + error_string = "Engine \"%s\" was not found"; + break; + case NodeCryptoError::INVALID_KEY_TYPE: + error_string = "Invalid key type"; + break; + case NodeCryptoError::KEY_GENERATION_JOB_FAILED: + error_string = "Key generation failed"; + break; + case NodeCryptoError::OK: + error_string = "Ok"; + break; + } + errors_.emplace_back(SPrintF(error_string, + std::forward(args)...)); +} + template T* MallocOpenSSL(size_t count) { void* mem = OPENSSL_malloc(MultiplyWithOverflowCheck(count, sizeof(T))); @@ -320,7 +369,7 @@ class CryptoJob : public AsyncWrap, public ThreadPoolWork { CryptoJobMode mode() const { return mode_; } - CryptoErrorVector* errors() { return &errors_; } + CryptoErrorStore* errors() { return &errors_; } AdditionalParams* params() { return ¶ms_; } @@ -364,7 +413,7 @@ class CryptoJob : public AsyncWrap, public ThreadPoolWork { private: const CryptoJobMode mode_; - CryptoErrorVector errors_; + CryptoErrorStore errors_; AdditionalParams params_; }; @@ -412,10 +461,10 @@ class DeriveBitsJob final : public CryptoJob { if (!DeriveBitsTraits::DeriveBits( AsyncWrap::env(), *CryptoJob::params(), &out_)) { - CryptoErrorVector* errors = CryptoJob::errors(); + CryptoErrorStore* errors = CryptoJob::errors(); errors->Capture(); - if (errors->empty()) - errors->push_back("Deriving bits failed"); + if (errors->Empty()) + errors->Insert(NodeCryptoError::DERIVING_BITS_FAILED); return; } success_ = true; @@ -425,9 +474,9 @@ class DeriveBitsJob final : public CryptoJob { v8::Local* err, v8::Local* result) override { Environment* env = AsyncWrap::env(); - CryptoErrorVector* errors = CryptoJob::errors(); + CryptoErrorStore* errors = CryptoJob::errors(); if (success_) { - CHECK(errors->empty()); + CHECK(errors->Empty()); *err = v8::Undefined(env->isolate()); return DeriveBitsTraits::EncodeOutput( env, @@ -436,9 +485,9 @@ class DeriveBitsJob final : public CryptoJob { result); } - if (errors->empty()) + if (errors->Empty()) errors->Capture(); - CHECK(!errors->empty()); + CHECK(!errors->Empty()); *result = v8::Undefined(env->isolate()); return v8::Just(errors->ToException(env).ToLocal(err)); } @@ -505,12 +554,12 @@ struct EnginePointer { } }; -EnginePointer LoadEngineById(const char* id, CryptoErrorVector* errors); +EnginePointer LoadEngineById(const char* id, CryptoErrorStore* errors); bool SetEngine( const char* id, uint32_t flags, - CryptoErrorVector* errors = nullptr); + CryptoErrorStore* errors = nullptr); void SetEngine(const v8::FunctionCallbackInfo& args); #endif // !OPENSSL_NO_ENGINE