-
Notifications
You must be signed in to change notification settings - Fork 30.5k
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: handle OpenSSL error queue in CipherBase #21288
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2566,6 +2566,7 @@ void CipherBase::Init(const char* cipher_type, | |
int key_buf_len, | ||
unsigned int auth_tag_len) { | ||
HandleScope scope(env()->isolate()); | ||
MarkPopErrorOnReturn mark_pop_error_on_return; | ||
|
||
#ifdef NODE_FIPS_MODE | ||
if (FIPS_mode()) { | ||
|
@@ -2590,10 +2591,12 @@ void CipherBase::Init(const char* cipher_type, | |
1, | ||
key, | ||
iv); | ||
CHECK_NE(key_len, 0); | ||
|
||
ctx_.reset(EVP_CIPHER_CTX_new()); | ||
const bool encrypt = (kind_ == kCipher); | ||
EVP_CipherInit_ex(ctx_.get(), cipher, nullptr, nullptr, nullptr, encrypt); | ||
CHECK(EVP_CipherInit_ex(ctx_.get(), cipher, nullptr, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume the old behavior (= no error checking) isn't appropriate either? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, errors should be reported. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would make this semver-major I guess, so I'll probably just leave the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mwah, I don't think better error checking constitutes semver-major. The kind of bugs it'd flush out are programmer bugs and environmental issues. |
||
nullptr, nullptr, encrypt)); | ||
|
||
int mode = EVP_CIPHER_CTX_mode(ctx_.get()); | ||
if (encrypt && (mode == EVP_CIPH_CTR_MODE || mode == EVP_CIPH_GCM_MODE || | ||
|
@@ -2616,12 +2619,12 @@ void CipherBase::Init(const char* cipher_type, | |
|
||
CHECK_EQ(1, EVP_CIPHER_CTX_set_key_length(ctx_.get(), key_len)); | ||
|
||
EVP_CipherInit_ex(ctx_.get(), | ||
nullptr, | ||
nullptr, | ||
reinterpret_cast<unsigned char*>(key), | ||
reinterpret_cast<unsigned char*>(iv), | ||
encrypt); | ||
CHECK(EVP_CipherInit_ex(ctx_.get(), | ||
nullptr, | ||
nullptr, | ||
reinterpret_cast<unsigned char*>(key), | ||
reinterpret_cast<unsigned char*>(iv), | ||
encrypt)); | ||
} | ||
|
||
|
||
|
@@ -2656,6 +2659,7 @@ void CipherBase::InitIv(const char* cipher_type, | |
int iv_len, | ||
unsigned int auth_tag_len) { | ||
HandleScope scope(env()->isolate()); | ||
MarkPopErrorOnReturn mark_pop_error_on_return; | ||
|
||
const EVP_CIPHER* const cipher = EVP_get_cipherbyname(cipher_type); | ||
if (cipher == nullptr) { | ||
|
@@ -2686,7 +2690,8 @@ void CipherBase::InitIv(const char* cipher_type, | |
EVP_CIPHER_CTX_set_flags(ctx_.get(), EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); | ||
|
||
const bool encrypt = (kind_ == kCipher); | ||
EVP_CipherInit_ex(ctx_.get(), cipher, nullptr, nullptr, nullptr, encrypt); | ||
CHECK(EVP_CipherInit_ex(ctx_.get(), cipher, nullptr, | ||
nullptr, nullptr, encrypt)); | ||
|
||
if (IsAuthenticatedMode()) { | ||
CHECK(has_iv); | ||
|
@@ -2699,12 +2704,12 @@ void CipherBase::InitIv(const char* cipher_type, | |
return env()->ThrowError("Invalid key length"); | ||
} | ||
|
||
EVP_CipherInit_ex(ctx_.get(), | ||
nullptr, | ||
nullptr, | ||
reinterpret_cast<const unsigned char*>(key), | ||
reinterpret_cast<const unsigned char*>(iv), | ||
encrypt); | ||
CHECK(EVP_CipherInit_ex(ctx_.get(), | ||
nullptr, | ||
nullptr, | ||
reinterpret_cast<const unsigned char*>(key), | ||
reinterpret_cast<const unsigned char*>(iv), | ||
encrypt)); | ||
} | ||
|
||
|
||
|
@@ -2749,6 +2754,7 @@ static bool IsValidGCMTagLength(unsigned int tag_len) { | |
bool CipherBase::InitAuthenticated(const char* cipher_type, int iv_len, | ||
unsigned int auth_tag_len) { | ||
CHECK(IsAuthenticatedMode()); | ||
MarkPopErrorOnReturn mark_pop_error_on_return; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For my own understanding: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exactly, all errors that occur in this scope are dropped once the destructor is called (unless they have been handled specifically). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And now that we're handling the errors manually, Node will be throwing an error instead of just crashing, I suppose? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And because we're manually handling the errors now, it's better because we'll throw an error that you could catch and what not instead of just crashing, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It won't throw more errors than before (except for |
||
|
||
if (!EVP_CIPHER_CTX_ctrl(ctx_.get(), | ||
EVP_CTRL_AEAD_SET_IVLEN, | ||
|
@@ -2893,6 +2899,7 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) { | |
bool CipherBase::SetAAD(const char* data, unsigned int len, int plaintext_len) { | ||
if (!ctx_ || !IsAuthenticatedMode()) | ||
return false; | ||
MarkPopErrorOnReturn mark_pop_error_on_return; | ||
|
||
int outlen; | ||
const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); | ||
|
@@ -2952,6 +2959,7 @@ CipherBase::UpdateResult CipherBase::Update(const char* data, | |
int* out_len) { | ||
if (!ctx_) | ||
return kErrorState; | ||
MarkPopErrorOnReturn mark_pop_error_on_return; | ||
|
||
const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); | ||
|
||
|
@@ -2963,10 +2971,10 @@ CipherBase::UpdateResult CipherBase::Update(const char* data, | |
// on first update: | ||
if (kind_ == kDecipher && IsAuthenticatedMode() && auth_tag_len_ > 0 && | ||
auth_tag_len_ != kNoAuthTagLength && !auth_tag_set_) { | ||
EVP_CIPHER_CTX_ctrl(ctx_.get(), | ||
EVP_CTRL_GCM_SET_TAG, | ||
auth_tag_len_, | ||
reinterpret_cast<unsigned char*>(auth_tag_)); | ||
CHECK(EVP_CIPHER_CTX_ctrl(ctx_.get(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit intrigued. https://www.openssl.org/docs/man1.0.2/crypto/EVP_EncryptInit.html doesn't show If it doesn't return There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only reasons I can think of right now are a bug (e.g. because we permitted the call in an invalid state) or a memory allocation failure, and those are generally not handled within our APIs. |
||
EVP_CTRL_GCM_SET_TAG, | ||
auth_tag_len_, | ||
reinterpret_cast<unsigned char*>(auth_tag_))); | ||
auth_tag_set_ = true; | ||
} | ||
|
||
|
@@ -3044,6 +3052,7 @@ void CipherBase::Update(const FunctionCallbackInfo<Value>& args) { | |
bool CipherBase::SetAutoPadding(bool auto_padding) { | ||
if (!ctx_) | ||
return false; | ||
MarkPopErrorOnReturn mark_pop_error_on_return; | ||
return EVP_CIPHER_CTX_set_padding(ctx_.get(), auto_padding); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to https://www.openssl.org/docs/man1.0.2/crypto/EVP_BytesToKey.html, this method returns 0 when an error is encountered. Don't you think we should instead use an
if
statement here and throw something ifkey_len
is 0?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mhh yeah, in theory, yes. Not sure which error conditions could cause that, so maybe I will defer that to another PR. This change won't make the situation any worse :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Totally fine by me, just wanted to make sure.