Skip to content

Commit

Permalink
crypto: use EVP_MD_fetch and cache EVP_MD for hashes
Browse files Browse the repository at this point in the history
On OpenSSL 3, migrate from EVP_get_digestbyname() to EVP_MD_fetch()
to get the implementation and use a per-Environment cache for it.
The EVP_MDs are freed during Environment cleanup.

Drive-by: declare the smart pointer for EVP_MD_CTX as EVPMDCtxPointer
instead of EVPMDPointer to avoid confusion with EVP_MD pointers.
  • Loading branch information
joyeecheung committed Dec 3, 2023
1 parent 78855fd commit b9bcd67
Show file tree
Hide file tree
Showing 7 changed files with 35 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/crypto/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ using SSLPointer = DeleteFnPtr<SSL, SSL_free>;
using PKCS8Pointer = DeleteFnPtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>;
using EVPKeyPointer = DeleteFnPtr<EVP_PKEY, EVP_PKEY_free>;
using EVPKeyCtxPointer = DeleteFnPtr<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
using EVPMDPointer = DeleteFnPtr<EVP_MD_CTX, EVP_MD_CTX_free>;
using EVPMDCtxPointer = DeleteFnPtr<EVP_MD_CTX, EVP_MD_CTX_free>;
using RSAPointer = DeleteFnPtr<RSA, RSA_free>;
using ECPointer = DeleteFnPtr<EC_KEY, EC_KEY_free>;
using BignumPointer = DeleteFnPtr<BIGNUM, BN_free>;
Expand Down
18 changes: 17 additions & 1 deletion src/crypto/crypto_hash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,23 @@ void Hash::New(const FunctionCallbackInfo<Value>& args) {
md = EVP_MD_CTX_md(orig->mdctx_.get());
} else {
const Utf8Value hash_type(env->isolate(), args[0]);
#if OPENSSL_VERSION_MAJOR >= 3
std::string hash_type_str = hash_type.ToString();
auto it = env->evp_md_cache.find(hash_type_str);
if (it == env->evp_md_cache.end()) {
EVP_MD* maybe_md = EVP_MD_fetch(nullptr, hash_type_str.c_str(), nullptr);
if (maybe_md == nullptr) {
return ThrowCryptoError(
env, ERR_get_error(), "Digest method not supported");
}
md = maybe_md;
env->evp_md_cache.emplace(hash_type_str, maybe_md);
} else {
md = it->second.get();
}
#else
md = EVP_get_digestbyname(*hash_type);
#endif // OPENSSL_VERSION_MAJOR >= 3
}

Maybe<unsigned int> xof_md_len = Nothing<unsigned int>();
Expand Down Expand Up @@ -284,7 +300,7 @@ bool HashTraits::DeriveBits(
Environment* env,
const HashConfig& params,
ByteSource* out) {
EVPMDPointer ctx(EVP_MD_CTX_new());
EVPMDCtxPointer ctx(EVP_MD_CTX_new());

if (UNLIKELY(!ctx ||
EVP_DigestInit_ex(ctx.get(), params.digest, nullptr) <= 0 ||
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/crypto_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class Hash final : public BaseObject {
Hash(Environment* env, v8::Local<v8::Object> wrap);

private:
EVPMDPointer mdctx_ {};
EVPMDCtxPointer mdctx_{};
unsigned int md_len_ = 0;
ByteSource digest_;
};
Expand Down
8 changes: 4 additions & 4 deletions src/crypto/crypto_sig.cc
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ bool ApplyRSAOptions(const ManagedEVPPKey& pkey,
}

std::unique_ptr<BackingStore> Node_SignFinal(Environment* env,
EVPMDPointer&& mdctx,
EVPMDCtxPointer&& mdctx,
const ManagedEVPPKey& pkey,
int padding,
Maybe<int> pss_salt_len) {
Expand Down Expand Up @@ -391,7 +391,7 @@ Sign::SignResult Sign::SignFinal(
if (!mdctx_)
return SignResult(kSignNotInitialised);

EVPMDPointer mdctx = std::move(mdctx_);
EVPMDCtxPointer mdctx = std::move(mdctx_);

if (!ValidateDSAParameters(pkey.get()))
return SignResult(kSignPrivateKey);
Expand Down Expand Up @@ -511,7 +511,7 @@ SignBase::Error Verify::VerifyFinal(const ManagedEVPPKey& pkey,
unsigned char m[EVP_MAX_MD_SIZE];
unsigned int m_len;
*verify_result = false;
EVPMDPointer mdctx = std::move(mdctx_);
EVPMDCtxPointer mdctx = std::move(mdctx_);

if (!EVP_DigestFinal_ex(mdctx.get(), m, &m_len))
return kSignPublicKey;
Expand Down Expand Up @@ -696,7 +696,7 @@ bool SignTraits::DeriveBits(
const SignConfiguration& params,
ByteSource* out) {
ClearErrorOnReturn clear_error_on_return;
EVPMDPointer context(EVP_MD_CTX_new());
EVPMDCtxPointer context(EVP_MD_CTX_new());
EVP_PKEY_CTX* ctx = nullptr;

switch (params.mode) {
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/crypto_sig.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class SignBase : public BaseObject {
SET_SELF_SIZE(SignBase)

protected:
EVPMDPointer mdctx_;
EVPMDCtxPointer mdctx_;
};

class Sign : public SignBase {
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/crypto_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ using SSLPointer = DeleteFnPtr<SSL, SSL_free>;
using PKCS8Pointer = DeleteFnPtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>;
using EVPKeyPointer = DeleteFnPtr<EVP_PKEY, EVP_PKEY_free>;
using EVPKeyCtxPointer = DeleteFnPtr<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
using EVPMDPointer = DeleteFnPtr<EVP_MD_CTX, EVP_MD_CTX_free>;
using EVPMDCtxPointer = DeleteFnPtr<EVP_MD_CTX, EVP_MD_CTX_free>;
using RSAPointer = DeleteFnPtr<RSA, RSA_free>;
using ECPointer = DeleteFnPtr<EC_KEY, EC_KEY_free>;
using BignumPointer = DeleteFnPtr<BIGNUM, BN_free>;
Expand Down
10 changes: 10 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
#include "uv.h"
#include "v8.h"

#if HAVE_OPENSSL
#include <openssl/evp.h>
#endif

#include <array>
#include <atomic>
#include <cstdint>
Expand Down Expand Up @@ -1015,6 +1019,12 @@ class Environment : public MemoryRetainer {
kExitInfoFieldCount
};

#if OPENSSL_VERSION_MAJOR >= 3
// We declare another alias here to avoid having to include crypto_util.h
using EVPMDPointer = DeleteFnPtr<EVP_MD, EVP_MD_free>;
std::unordered_map<std::string, EVPMDPointer> evp_md_cache;
#endif // OPENSSL_VERSION_MAJOR

private:
// V8 has changed the constructor of exceptions, support both APIs before Node
// updates to V8 12.1.
Expand Down

0 comments on commit b9bcd67

Please sign in to comment.