Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto: add internal error codes #37650

Merged
merged 1 commit into from
Mar 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/crypto/crypto_cipher.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,18 +227,18 @@ class CipherJob final : public CryptoJob<CipherTraits> {
// Success!
return;
}
CryptoErrorVector* errors = CryptoJob<CipherTraits>::errors();
CryptoErrorStore* errors = CryptoJob<CipherTraits>::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;
}
}
Expand All @@ -248,17 +248,17 @@ class CipherJob final : public CryptoJob<CipherTraits> {
v8::Local<v8::Value>* err,
v8::Local<v8::Value>* result) override {
Environment* env = AsyncWrap::env();
CryptoErrorVector* errors = CryptoJob<CipherTraits>::errors();
CryptoErrorStore* errors = CryptoJob<CipherTraits>::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));
}
Expand Down
4 changes: 2 additions & 2 deletions src/crypto/crypto_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ void SecureContext::SetEngineKey(const FunctionCallbackInfo<Value>& 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) {
Expand Down Expand Up @@ -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) {
Expand Down
12 changes: 6 additions & 6 deletions src/crypto/crypto_keygen.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ class KeyGenJob final : public CryptoJob<KeyGenTraits> {
// Success!
break;
case KeyGenJobStatus::FAILED: {
CryptoErrorVector* errors = CryptoJob<KeyGenTraits>::errors();
CryptoErrorStore* errors = CryptoJob<KeyGenTraits>::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);
}
}
}
Expand All @@ -94,17 +94,17 @@ class KeyGenJob final : public CryptoJob<KeyGenTraits> {
v8::Local<v8::Value>* err,
v8::Local<v8::Value>* result) override {
Environment* env = AsyncWrap::env();
CryptoErrorVector* errors = CryptoJob<KeyGenTraits>::errors();
CryptoErrorStore* errors = CryptoJob<KeyGenTraits>::errors();
AdditionalParams* params = CryptoJob<KeyGenTraits>::params();
if (status_ == KeyGenJobStatus::OK &&
LIKELY(!KeyGenTraits::EncodeKey(env, params, result).IsNothing())) {
*err = Undefined(env->isolate());
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));
}
Expand Down
16 changes: 8 additions & 8 deletions src/crypto/crypto_keys.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,18 +347,18 @@ class KeyExportJob final : public CryptoJob<KeyExportTraits> {
// Success!
return;
}
CryptoErrorVector* errors = CryptoJob<KeyExportTraits>::errors();
CryptoErrorStore* errors = CryptoJob<KeyExportTraits>::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;
}
}
Expand All @@ -368,17 +368,17 @@ class KeyExportJob final : public CryptoJob<KeyExportTraits> {
v8::Local<v8::Value>* err,
v8::Local<v8::Value>* result) override {
Environment* env = AsyncWrap::env();
CryptoErrorVector* errors = CryptoJob<KeyExportTraits>::errors();
CryptoErrorStore* errors = CryptoJob<KeyExportTraits>::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));
}
Expand Down
42 changes: 25 additions & 17 deletions src/crypto/crypto_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -187,44 +187,52 @@ void TestFipsCrypto(const v8::FunctionCallbackInfo<v8::Value>& 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<Value> CryptoErrorVector::ToException(
bool CryptoErrorStore::Empty() const {
return errors_.empty();
}

MaybeLocal<Value> CryptoErrorStore::ToException(
Environment* env,
Local<String> 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<String> 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<Value>();
}
copy.pop_back();
copy.errors_.pop_back();
return copy.ToException(env, exception_string);
}

Local<Value> exception_v = Exception::Error(exception_string);
CHECK(!exception_v.IsEmpty());

if (!empty()) {
if (!Empty()) {
CHECK(exception_v->IsObject());
Local<Object> exception = exception_v.As<Object>();
Local<Value> 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<Value>();
Expand Down Expand Up @@ -509,7 +517,7 @@ void ThrowCryptoError(Environment* env,
Local<Object> 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) ||
Expand All @@ -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));
Expand All @@ -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)
Expand Down
73 changes: 61 additions & 12 deletions src/crypto/crypto_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,15 +159,64 @@ void Decode(const v8::FunctionCallbackInfo<v8::Value>& 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<std::string> {
struct CryptoErrorStore final : public MemoryRetainer {
public:
void Capture();

bool Empty() const;

template <typename... Args>
void Insert(const NodeCryptoError error, Args&&... args);

v8::MaybeLocal<v8::Value> ToException(
Environment* env,
v8::Local<v8::String> exception_string = v8::Local<v8::String>()) const;

SET_NO_MEMORY_INFO()
SET_MEMORY_INFO_NAME(CryptoErrorStore);
SET_SELF_SIZE(CryptoErrorStore);

private:
std::vector<std::string> errors_;
};

template <typename... Args>
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>(args)...));
}

template <typename T>
T* MallocOpenSSL(size_t count) {
void* mem = OPENSSL_malloc(MultiplyWithOverflowCheck(count, sizeof(T)));
Expand Down Expand Up @@ -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 &params_; }

Expand Down Expand Up @@ -364,7 +413,7 @@ class CryptoJob : public AsyncWrap, public ThreadPoolWork {

private:
const CryptoJobMode mode_;
CryptoErrorVector errors_;
CryptoErrorStore errors_;
AdditionalParams params_;
};

Expand Down Expand Up @@ -412,10 +461,10 @@ class DeriveBitsJob final : public CryptoJob<DeriveBitsTraits> {
if (!DeriveBitsTraits::DeriveBits(
AsyncWrap::env(),
*CryptoJob<DeriveBitsTraits>::params(), &out_)) {
CryptoErrorVector* errors = CryptoJob<DeriveBitsTraits>::errors();
CryptoErrorStore* errors = CryptoJob<DeriveBitsTraits>::errors();
errors->Capture();
if (errors->empty())
errors->push_back("Deriving bits failed");
if (errors->Empty())
errors->Insert(NodeCryptoError::DERIVING_BITS_FAILED);
return;
}
success_ = true;
Expand All @@ -425,9 +474,9 @@ class DeriveBitsJob final : public CryptoJob<DeriveBitsTraits> {
v8::Local<v8::Value>* err,
v8::Local<v8::Value>* result) override {
Environment* env = AsyncWrap::env();
CryptoErrorVector* errors = CryptoJob<DeriveBitsTraits>::errors();
CryptoErrorStore* errors = CryptoJob<DeriveBitsTraits>::errors();
if (success_) {
CHECK(errors->empty());
CHECK(errors->Empty());
*err = v8::Undefined(env->isolate());
return DeriveBitsTraits::EncodeOutput(
env,
Expand All @@ -436,9 +485,9 @@ class DeriveBitsJob final : public CryptoJob<DeriveBitsTraits> {
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));
}
Expand Down Expand Up @@ -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<v8::Value>& args);
#endif // !OPENSSL_NO_ENGINE
Expand Down