Skip to content

Commit

Permalink
crypto: add internal error codes
Browse files Browse the repository at this point in the history
  • Loading branch information
RaisinTen committed Mar 9, 2021
1 parent 52ca115 commit 8bdf37e
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 54 deletions.
12 changes: 6 additions & 6 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,9 +248,9 @@ 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());
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
85 changes: 60 additions & 25 deletions src/crypto/crypto_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -187,51 +187,86 @@ 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();
}

template<typename... Args>
void CryptoErrorStore::Insert(const NodeCryptoError error, Args&&... args) {
const char* error_string;
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::NO_ERROR:
error_string = "No error";
break;
}
errors.emplace_back(SPrintF(error_string,
std::forward<Args>(args)...));
}

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::NO_ERROR);
}
// 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,
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()) {
CHECK(exception_v->IsObject());
Local<Object> exception = exception_v.As<Object>();
Local<Value> stack;
if (!ToV8Value(env->context(), *this).ToLocal(&stack) ||
exception->Set(env->context(), env->openssl_error_stack(), stack)
.IsNothing()) {
return MaybeLocal<Value>();
}
if (Empty()) {
return exception_v;
}

return exception_v;
CHECK(exception_v->IsObject());
Local<Object> exception = exception_v.As<Object>();
Local<Value> stack;
if (!ToV8Value(env->context(), *this).ToLocal(&stack) ||
exception->Set(env->context(), env->openssl_error_stack(), stack)
.IsNothing()) {
return MaybeLocal<Value>();
}
}

ByteSource::ByteSource(ByteSource&& other) noexcept
Expand Down Expand Up @@ -509,7 +544,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 +555,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 +574,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
32 changes: 25 additions & 7 deletions src/crypto/crypto_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,31 @@ 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,
NO_ERROR,
};

// Utility struct used to harvest error information from openssl's error stack
struct CryptoErrorVector : public std::vector<std::string> {
struct CryptoErrorStore {
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;

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

template <typename T>
Expand Down Expand Up @@ -320,7 +338,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 +382,7 @@ class CryptoJob : public AsyncWrap, public ThreadPoolWork {

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

Expand Down Expand Up @@ -412,7 +430,7 @@ 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");
Expand All @@ -425,7 +443,7 @@ 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());
*err = v8::Undefined(env->isolate());
Expand Down Expand Up @@ -505,12 +523,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

0 comments on commit 8bdf37e

Please sign in to comment.