From 10cd47726266d2acc1cf4307c7fa71014abb425a Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 2 Mar 2025 16:43:43 -0800 Subject: [PATCH 1/4] src: refine ncrypto more An eventual goal for ncrypto is to completely abstract away details of working directly with openssl in order to make it easier to work with multiple different openssl/boringssl versions. As part of that we want to move away from direct reliance on specific openssl APIs in the runtime and instead go through the ncrypto abstractions. Not only does this help other runtimes trying to be compatible with Node.js, but it helps Node.js also by reducing the complexity of the crypto code in Node.js itself. --- deps/ncrypto/ncrypto.cc | 48 +++++++++++++++++++++++++++++++++++-- deps/ncrypto/ncrypto.h | 29 +++++++++++++++++++++- src/crypto/crypto_aes.cc | 27 ++++++++++----------- src/crypto/crypto_aes.h | 24 +++++++++---------- src/crypto/crypto_cipher.cc | 38 +++++++++++++---------------- 5 files changed, 114 insertions(+), 52 deletions(-) diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index 0a0da0263676d9..5dca9f389faea8 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -2742,6 +2742,50 @@ const Cipher Cipher::FromCtx(const CipherCtxPointer& ctx) { return Cipher(EVP_CIPHER_CTX_cipher(ctx.get())); } +const Cipher Cipher::EMPTY = Cipher(); +const Cipher Cipher::AES_128_CBC = Cipher::FromNid(NID_aes_128_cbc); +const Cipher Cipher::AES_192_CBC = Cipher::FromNid(NID_aes_192_cbc); +const Cipher Cipher::AES_256_CBC = Cipher::FromNid(NID_aes_256_cbc); +const Cipher Cipher::AES_128_CTR = Cipher::FromNid(NID_aes_128_ctr); +const Cipher Cipher::AES_192_CTR = Cipher::FromNid(NID_aes_192_ctr); +const Cipher Cipher::AES_256_CTR = Cipher::FromNid(NID_aes_256_ctr); +const Cipher Cipher::AES_128_GCM = Cipher::FromNid(NID_aes_128_gcm); +const Cipher Cipher::AES_192_GCM = Cipher::FromNid(NID_aes_192_gcm); +const Cipher Cipher::AES_256_GCM = Cipher::FromNid(NID_aes_256_gcm); +const Cipher Cipher::AES_128_KW = Cipher::FromNid(NID_id_aes128_wrap); +const Cipher Cipher::AES_192_KW = Cipher::FromNid(NID_id_aes192_wrap); +const Cipher Cipher::AES_256_KW = Cipher::FromNid(NID_id_aes256_wrap); + +bool Cipher::isGcmMode() const { + if (!cipher_) return false; + return getMode() == EVP_CIPH_GCM_MODE; +} + +bool Cipher::isWrapMode() const { + if (!cipher_) return false; + return getMode() == EVP_CIPH_WRAP_MODE; +} + +bool Cipher::isCtrMode() const { + if (!cipher_) return false; + return getMode() == EVP_CIPH_CTR_MODE; +} + +bool Cipher::isCcmMode() const { + if (!cipher_) return false; + return getMode() == EVP_CIPH_CCM_MODE; +} + +bool Cipher::isOcbMode() const { + if (!cipher_) return false; + return getMode() == EVP_CIPH_OCB_MODE; +} + +bool Cipher::isStreamMode() const { + if (!cipher_) return false; + return getMode() == EVP_CIPH_STREAM_CIPHER; +} + int Cipher::getMode() const { if (!cipher_) return 0; return EVP_CIPHER_mode(cipher_); @@ -2851,9 +2895,9 @@ EVP_CIPHER_CTX* CipherCtxPointer::release() { return ctx_.release(); } -void CipherCtxPointer::setFlags(int flags) { +void CipherCtxPointer::setAllowWrap() { if (!ctx_) return; - EVP_CIPHER_CTX_set_flags(ctx_.get(), flags); + EVP_CIPHER_CTX_set_flags(ctx_.get(), EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); } bool CipherCtxPointer::setKeyLength(size_t length) { diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index f73c84edce20bf..6b85a92cd7d1ea 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -274,12 +274,38 @@ class Cipher final { std::string_view getModeLabel() const; std::string_view getName() const; + bool isGcmMode() const; + bool isWrapMode() const; + bool isCtrMode() const; + bool isCcmMode() const; + bool isOcbMode() const; + bool isStreamMode() const; + bool isSupportedAuthenticatedMode() const; static const Cipher FromName(std::string_view name); static const Cipher FromNid(int nid); static const Cipher FromCtx(const CipherCtxPointer& ctx); + // Utilities to get various ciphers by type. If the underlying + // implementation does not support the requested cipher, then + // the result will be an empty Cipher object whose bool operator + // will return false. + + static const Cipher EMPTY; + static const Cipher AES_128_CBC; + static const Cipher AES_192_CBC; + static const Cipher AES_256_CBC; + static const Cipher AES_128_CTR; + static const Cipher AES_192_CTR; + static const Cipher AES_256_CTR; + static const Cipher AES_128_GCM; + static const Cipher AES_192_GCM; + static const Cipher AES_256_GCM; + static const Cipher AES_128_KW; + static const Cipher AES_192_KW; + static const Cipher AES_256_KW; + struct CipherParams { int padding; const EVP_MD* digest; @@ -596,7 +622,8 @@ class CipherCtxPointer final { void reset(EVP_CIPHER_CTX* ctx = nullptr); EVP_CIPHER_CTX* release(); - void setFlags(int flags); + void setAllowWrap(); + bool setKeyLength(size_t length); bool setIvLength(size_t length); bool setAeadTag(const Buffer& tag); diff --git a/src/crypto/crypto_aes.cc b/src/crypto/crypto_aes.cc index 3f4e3e6672ae91..d723ea54e3afb8 100644 --- a/src/crypto/crypto_aes.cc +++ b/src/crypto/crypto_aes.cc @@ -47,11 +47,11 @@ WebCryptoCipherStatus AES_Cipher(Environment* env, ByteSource* out) { CHECK_EQ(key_data.GetKeyType(), kKeyTypeSecret); - const int mode = params.cipher.getMode(); - auto ctx = CipherCtxPointer::New(); - if (mode == EVP_CIPH_WRAP_MODE) { - ctx.setFlags(EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + CHECK(ctx); + + if (params.cipher.isWrapMode()) { + ctx.setAllowWrap(); } const bool encrypt = cipher_mode == kWebCryptoCipherEncrypt; @@ -61,7 +61,7 @@ WebCryptoCipherStatus AES_Cipher(Environment* env, return WebCryptoCipherStatus::FAILED; } - if (mode == EVP_CIPH_GCM_MODE && !ctx.setIvLength(params.iv.size())) { + if (params.cipher.isGcmMode() && !ctx.setIvLength(params.iv.size())) { return WebCryptoCipherStatus::FAILED; } @@ -76,7 +76,7 @@ WebCryptoCipherStatus AES_Cipher(Environment* env, size_t tag_len = 0; - if (mode == EVP_CIPH_GCM_MODE) { + if (params.cipher.isGcmMode()) { switch (cipher_mode) { case kWebCryptoCipherDecrypt: { // If in decrypt mode, the auth tag must be set in the params.tag. @@ -112,7 +112,7 @@ WebCryptoCipherStatus AES_Cipher(Environment* env, .data = params.additional_data.data(), .len = params.additional_data.size(), }; - if (mode == EVP_CIPH_GCM_MODE && params.additional_data.size() && + if (params.cipher.isGcmMode() && params.additional_data.size() && !ctx.update(buffer, nullptr, &out_len)) { return WebCryptoCipherStatus::FAILED; } @@ -149,7 +149,7 @@ WebCryptoCipherStatus AES_Cipher(Environment* env, // If using AES_GCM, grab the generated auth tag and append // it to the end of the ciphertext. - if (cipher_mode == kWebCryptoCipherEncrypt && mode == EVP_CIPH_GCM_MODE) { + if (encrypt && params.cipher.isGcmMode()) { if (!ctx.getAeadTag(tag_len, ptr + total)) { return WebCryptoCipherStatus::FAILED; } @@ -467,10 +467,9 @@ Maybe AESCipherTraits::AdditionalConfig( params->variant = static_cast(args[offset].As()->Value()); - int cipher_nid; #define V(name, _, nid) \ case AESKeyVariant::name: { \ - cipher_nid = nid; \ + params->cipher = nid; \ break; \ } switch (params->variant) { @@ -480,22 +479,20 @@ Maybe AESCipherTraits::AdditionalConfig( } #undef V - params->cipher = Cipher::FromNid(cipher_nid); if (!params->cipher) { THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env); return Nothing(); } - int cipher_op_mode = params->cipher.getMode(); - if (cipher_op_mode != EVP_CIPH_WRAP_MODE) { + if (params->cipher.isWrapMode()) { if (!ValidateIV(env, mode, args[offset + 1], params)) { return Nothing(); } - if (cipher_op_mode == EVP_CIPH_CTR_MODE) { + if (params->cipher.isCtrMode()) { if (!ValidateCounter(env, args[offset + 2], params)) { return Nothing(); } - } else if (cipher_op_mode == EVP_CIPH_GCM_MODE) { + } else if (params->cipher.isGcmMode()) { if (!ValidateAuthTag(env, mode, cipher_mode, args[offset + 2], params) || !ValidateAdditionalData(env, mode, args[offset + 3], params)) { return Nothing(); diff --git a/src/crypto/crypto_aes.h b/src/crypto/crypto_aes.h index b5c371e5a6e7ad..74cfdb80818287 100644 --- a/src/crypto/crypto_aes.h +++ b/src/crypto/crypto_aes.h @@ -13,18 +13,18 @@ namespace node::crypto { constexpr unsigned kNoAuthTagLength = static_cast(-1); #define VARIANTS(V) \ - V(CTR_128, AES_CTR_Cipher, NID_aes_128_ctr) \ - V(CTR_192, AES_CTR_Cipher, NID_aes_192_ctr) \ - V(CTR_256, AES_CTR_Cipher, NID_aes_256_ctr) \ - V(CBC_128, AES_Cipher, NID_aes_128_cbc) \ - V(CBC_192, AES_Cipher, NID_aes_192_cbc) \ - V(CBC_256, AES_Cipher, NID_aes_256_cbc) \ - V(GCM_128, AES_Cipher, NID_aes_128_gcm) \ - V(GCM_192, AES_Cipher, NID_aes_192_gcm) \ - V(GCM_256, AES_Cipher, NID_aes_256_gcm) \ - V(KW_128, AES_Cipher, NID_id_aes128_wrap) \ - V(KW_192, AES_Cipher, NID_id_aes192_wrap) \ - V(KW_256, AES_Cipher, NID_id_aes256_wrap) + V(CTR_128, AES_CTR_Cipher, ncrypto::Cipher::AES_128_CTR) \ + V(CTR_192, AES_CTR_Cipher, ncrypto::Cipher::AES_192_CTR) \ + V(CTR_256, AES_CTR_Cipher, ncrypto::Cipher::AES_256_CTR) \ + V(CBC_128, AES_Cipher, ncrypto::Cipher::AES_128_CBC) \ + V(CBC_192, AES_Cipher, ncrypto::Cipher::AES_192_CBC) \ + V(CBC_256, AES_Cipher, ncrypto::Cipher::AES_256_CBC) \ + V(GCM_128, AES_Cipher, ncrypto::Cipher::AES_128_GCM) \ + V(GCM_192, AES_Cipher, ncrypto::Cipher::AES_192_GCM) \ + V(GCM_256, AES_Cipher, ncrypto::Cipher::AES_256_GCM) \ + V(KW_128, AES_Cipher, ncrypto::Cipher::AES_128_KW) \ + V(KW_192, AES_Cipher, ncrypto::Cipher::AES_192_KW) \ + V(KW_256, AES_Cipher, ncrypto::Cipher::AES_256_KW) enum class AESKeyVariant { #define V(name, _, __) name, diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc index 1bf91b37e8b3a6..446def939ee535 100644 --- a/src/crypto/crypto_cipher.cc +++ b/src/crypto/crypto_cipher.cc @@ -89,22 +89,17 @@ void GetCipherInfo(const FunctionCallbackInfo& args) { // For GCM and OCB modes, we'll check by attempting to // set the value. For everything else, just check that // check_len == iv_length. - switch (cipher.getMode()) { - case EVP_CIPH_CCM_MODE: - if (check_len < 7 || check_len > 13) - return; - break; - case EVP_CIPH_GCM_MODE: - // Fall through - case EVP_CIPH_OCB_MODE: - if (!ctx.setIvLength(check_len)) { - return; - } - break; - default: - if (check_len != iv_length) - return; + + if (cipher.isCcmMode()) { + if (check_len < 7 || check_len > 13) return; + } else if (cipher.isGcmMode()) { + // Nothing to do. + } else if (cipher.isOcbMode()) { + if (!ctx.setIvLength(check_len)) return; + } else { + if (check_len != iv_length) return; } + iv_length = check_len; } } @@ -133,7 +128,7 @@ void GetCipherInfo(const FunctionCallbackInfo& args) { } // Stream ciphers do not have a meaningful block size - if (cipher.getMode() != EVP_CIPH_STREAM_CIPHER && + if (!cipher.isStreamMode() && info->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "blockSize"), Int32::New(env->isolate(), block_length)) @@ -313,8 +308,8 @@ void CipherBase::CommonInit(const char* cipher_type, ctx_ = CipherCtxPointer::New(); CHECK(ctx_); - if (cipher.getMode() == EVP_CIPH_WRAP_MODE) { - ctx_.setFlags(EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + if (cipher.isWrapMode()) { + ctx_.setAllowWrap(); } const bool encrypt = (kind_ == kCipher); @@ -366,10 +361,9 @@ void CipherBase::Init(const char* cipher_type, iv); CHECK_NE(key_len, 0); - const int mode = cipher.getMode(); - if (kind_ == kCipher && (mode == EVP_CIPH_CTR_MODE || - mode == EVP_CIPH_GCM_MODE || - mode == EVP_CIPH_CCM_MODE)) { + if (kind_ == kCipher && (cipher.isCtrMode() || + cipher.isGcmMode() || + cipher.isCcmMode())) { // Ignore the return value (i.e. possible exception) because we are // not calling back into JS anyway. ProcessEmitWarning(env(), From 73562bdbe97177131eb8084494b25eda09ac79a9 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 2 Mar 2025 17:54:31 -0800 Subject: [PATCH 2/4] src: move cipher list iteration to ncrypto --- deps/ncrypto/ncrypto.cc | 70 +++++++++++++++++++++++++++++++++++++ deps/ncrypto/ncrypto.h | 6 ++++ src/crypto/crypto_cipher.cc | 36 +++++++++++-------- src/crypto/crypto_util.h | 59 ------------------------------- 4 files changed, 98 insertions(+), 73 deletions(-) diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index 5dca9f389faea8..69f7920c1cbca3 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -3717,6 +3717,76 @@ DataPointer Cipher::recover(const EVPKeyPointer& key, key, params, in); } +namespace { +struct CipherCallbackContext { + Cipher::CipherNameCallback cb; + void operator()(std::string_view name) { + cb(name); + } +}; + +#if OPENSSL_VERSION_MAJOR >= 3 +template +void array_push_back(const TypeName* evp_ref, + const char* from, + const char* to, + void* arg) { + if (from == nullptr) return; + + const TypeName* real_instance = getbyname(from); + if (!real_instance) return; + + const char* real_name = getname(real_instance); + if (!real_name) return; + + // EVP_*_fetch() does not support alias names, so we need to pass it the + // real/original algorithm name. + // We use EVP_*_fetch() as a filter here because it will only return an + // instance if the algorithm is supported by the public OpenSSL APIs (some + // algorithms are used internally by OpenSSL and are also passed to this + // callback). + TypeName* fetched = fetch_type(nullptr, real_name, nullptr); + if (fetched == nullptr) return; + + free_type(fetched); + auto& cb = *(static_cast(arg)); + cb(from); +} +#else +template +void array_push_back(const TypeName* evp_ref, + const char* from, + const char* to, + void* arg) { + if (!from) return; + auto& cb = *(static_cast(arg)); + cb(from); +} +#endif +} // namespace + +void Cipher::ForEach(Cipher::CipherNameCallback callback) { + ClearErrorOnReturn clearErrorOnReturn; + CipherCallbackContext context; + context.cb = std::move(callback); + + EVP_CIPHER_do_all_sorted( +#if OPENSSL_VERSION_MAJOR >= 3 + array_push_back, +#else + array_push_back, +#endif + &context); +} + // ============================================================================ Ec::Ec() : ec_(nullptr) {} diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index 6b85a92cd7d1ea..12fbe964fbf517 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -287,6 +287,12 @@ class Cipher final { static const Cipher FromNid(int nid); static const Cipher FromCtx(const CipherCtxPointer& ctx); + using CipherNameCallback = std::function; + + // Iterates the known ciphers if the underlying implementation + // is able to do so. + static void ForEach(CipherNameCallback callback); + // Utilities to get various ciphers by type. If the underlying // implementation does not support the requested cipher, then // the result will be an empty Cipher object whose bool operator diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc index 446def939ee535..0e71890564bc4a 100644 --- a/src/crypto/crypto_cipher.cc +++ b/src/crypto/crypto_cipher.cc @@ -182,20 +182,28 @@ void CipherBase::GetSSLCiphers(const FunctionCallbackInfo& args) { void CipherBase::GetCiphers(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - MarkPopErrorOnReturn mark_pop_error_on_return; - CipherPushContext ctx(env); - EVP_CIPHER_do_all_sorted( -#if OPENSSL_VERSION_MAJOR >= 3 - array_push_back, -#else - array_push_back, -#endif - &ctx); - args.GetReturnValue().Set(ctx.ToJSArray()); + LocalVector ciphers(env->isolate()); + bool errored = false; + Cipher::ForEach([&](std::string_view name) { + // If a prior iteration errored, do nothing further. We apparently + // can't actually stop openssl from stopping its iteration here. + // But why does it matter? Good question. + if (errored) return; + Local val; + if (!ToV8Value(env->context(), name, env->isolate()).ToLocal(&val)) { + errored = true; + return; + } + ciphers.push_back(val); + }); + + // If errored is true here, then we encountered a JavaScript error + // while trying to create the V8 String from the std::string_view + // in the iteration callback. That means we need to throw. + if (!errored) { + args.GetReturnValue().Set( + Array::New(env->isolate(), ciphers.data(), ciphers.size())); + } } CipherBase::CipherBase(Environment* env, diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h index 8015e1ab00dab6..d663db59793ed2 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h @@ -479,65 +479,6 @@ void ThrowCryptoError(Environment* env, unsigned long err, // NOLINT(runtime/int) const char* message = nullptr); -class CipherPushContext final { - public: - inline explicit CipherPushContext(Environment* env) - : list_(env->isolate()), env_(env) {} - - inline void push_back(const char* str) { - list_.emplace_back(OneByteString(env_->isolate(), str)); - } - - inline v8::Local ToJSArray() { - return v8::Array::New(env_->isolate(), list_.data(), list_.size()); - } - - private: - v8::LocalVector list_; - Environment* env_; -}; - -#if OPENSSL_VERSION_MAJOR >= 3 -template -void array_push_back(const TypeName* evp_ref, - const char* from, - const char* to, - void* arg) { - if (!from) return; - - const TypeName* real_instance = getbyname(from); - if (!real_instance) return; - - const char* real_name = getname(real_instance); - if (!real_name) return; - - // EVP_*_fetch() does not support alias names, so we need to pass it the - // real/original algorithm name. - // We use EVP_*_fetch() as a filter here because it will only return an - // instance if the algorithm is supported by the public OpenSSL APIs (some - // algorithms are used internally by OpenSSL and are also passed to this - // callback). - TypeName* fetched = fetch_type(nullptr, real_name, nullptr); - if (!fetched) return; - - free_type(fetched); - static_cast(arg)->push_back(from); -} -#else -template -void array_push_back(const TypeName* evp_ref, - const char* from, - const char* to, - void* arg) { - if (!from) return; - static_cast(arg)->push_back(from); -} -#endif - // WebIDL AllowSharedBufferSource. inline bool IsAnyBufferSource(v8::Local arg) { return arg->IsArrayBufferView() || From a34cb1b071f8caf9515259dec825856789081571 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 2 Mar 2025 18:45:50 -0800 Subject: [PATCH 3/4] src: move more crypto impl details into ncrypto --- deps/ncrypto/ncrypto.cc | 82 ++++++++++++++++++++------ deps/ncrypto/ncrypto.h | 48 ++++++++++++++- src/crypto/crypto_aes.cc | 2 +- src/crypto/crypto_cipher.cc | 114 +++++++++++++++--------------------- src/crypto/crypto_cipher.h | 17 +++--- src/crypto/crypto_hkdf.cc | 5 +- src/crypto/crypto_hkdf.h | 2 +- 7 files changed, 172 insertions(+), 98 deletions(-) diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index 69f7920c1cbca3..220e0f253715fe 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -1669,17 +1669,17 @@ const EVP_CIPHER* getCipherByName(const std::string_view name) { return EVP_get_cipherbyname(name.data()); } -bool checkHkdfLength(const EVP_MD* md, size_t length) { +bool checkHkdfLength(const Digest& md, size_t length) { // HKDF-Expand computes up to 255 HMAC blocks, each having as many bits as // the output of the hash function. 255 is a hard limit because HKDF appends // an 8-bit counter to each HMAC'd message, starting at 1. static constexpr size_t kMaxDigestMultiplier = 255; - size_t max_length = EVP_MD_size(md) * kMaxDigestMultiplier; + size_t max_length = md.size() * kMaxDigestMultiplier; if (length > max_length) return false; return true; } -DataPointer hkdf(const EVP_MD* md, +DataPointer hkdf(const Digest& md, const Buffer& key, const Buffer& info, const Buffer& salt, @@ -1703,7 +1703,7 @@ DataPointer hkdf(const EVP_MD* md, if (salt.len > 0) { actual_salt = {reinterpret_cast(salt.data), salt.len}; } else { - actual_salt = {default_salt, static_cast(EVP_MD_size(md))}; + actual_salt = {default_salt, static_cast(md.size())}; } // We do not use EVP_PKEY_HKDF_MODE_EXTRACT_AND_EXPAND because and instead @@ -2786,6 +2786,11 @@ bool Cipher::isStreamMode() const { return getMode() == EVP_CIPH_STREAM_CIPHER; } +bool Cipher::isChaCha20Poly1305() const { + if (!cipher_) return false; + return getNid() == NID_chacha20_poly1305; +} + int Cipher::getMode() const { if (!cipher_) return 0; return EVP_CIPHER_mode(cipher_); @@ -2862,6 +2867,14 @@ bool Cipher::isSupportedAuthenticatedMode() const { } } +int Cipher::bytesToKey(const Digest& digest, + const Buffer& input, + unsigned char* key, + unsigned char* iv) const { + return EVP_BytesToKey( + *this, Digest::MD5, nullptr, input.data, input.len, 1, key, iv); +} + // ============================================================================ CipherCtxPointer CipherCtxPointer::New() { @@ -2938,6 +2951,26 @@ int CipherCtxPointer::getMode() const { return EVP_CIPHER_CTX_mode(ctx_.get()); } +bool CipherCtxPointer::isGcmMode() const { + if (!ctx_) return false; + return getMode() == EVP_CIPH_GCM_MODE; +} + +bool CipherCtxPointer::isCcmMode() const { + if (!ctx_) return false; + return getMode() == EVP_CIPH_CCM_MODE; +} + +bool CipherCtxPointer::isWrapMode() const { + if (!ctx_) return false; + return getMode() == EVP_CIPH_WRAP_MODE; +} + +bool CipherCtxPointer::isChaCha20Poly1305() const { + if (!ctx_) return false; + return getNid() == NID_chacha20_poly1305; +} + int CipherCtxPointer::getNid() const { if (!ctx_) return 0; return EVP_CIPHER_CTX_nid(ctx_.get()); @@ -3720,9 +3753,7 @@ DataPointer Cipher::recover(const EVPKeyPointer& key, namespace { struct CipherCallbackContext { Cipher::CipherNameCallback cb; - void operator()(std::string_view name) { - cb(name); - } + void operator()(std::string_view name) { cb(name); } }; #if OPENSSL_VERSION_MAJOR >= 3 @@ -3759,9 +3790,9 @@ void array_push_back(const TypeName* evp_ref, #else template void array_push_back(const TypeName* evp_ref, - const char* from, - const char* to, - void* arg) { + const char* from, + const char* to, + void* arg) { if (!from) return; auto& cb = *(static_cast(arg)); cb(from); @@ -3776,15 +3807,15 @@ void Cipher::ForEach(Cipher::CipherNameCallback callback) { EVP_CIPHER_do_all_sorted( #if OPENSSL_VERSION_MAJOR >= 3 - array_push_back, + array_push_back, #else - array_push_back, + array_push_back, #endif - &context); + &context); } // ============================================================================ @@ -4143,4 +4174,21 @@ size_t Dsa::getDivisorLength() const { return BignumPointer::GetBitCount(getQ()); } +// ============================================================================ + +size_t Digest::size() const { + if (md_ == nullptr) return 0; + return EVP_MD_size(md_); +} + +const Digest Digest::MD5 = Digest(EVP_md5()); +const Digest Digest::SHA1 = Digest(EVP_sha1()); +const Digest Digest::SHA256 = Digest(EVP_sha256()); +const Digest Digest::SHA384 = Digest(EVP_sha384()); +const Digest Digest::SHA512 = Digest(EVP_sha512()); + +const Digest Digest::FromName(std::string_view name) { + return ncrypto::getDigestByName(name); +} + } // namespace ncrypto diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index 12fbe964fbf517..ad86ea58e028df 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -247,11 +247,44 @@ struct Buffer { size_t len = 0; }; +class Digest final { + public: + Digest() = default; + Digest(const EVP_MD* md) : md_(md) {} + Digest(const Digest&) = default; + Digest& operator=(const Digest&) = default; + inline Digest& operator=(const EVP_MD* md) { + md_ = md; + return *this; + } + NCRYPTO_DISALLOW_MOVE(Digest) + + size_t size() const; + + inline const EVP_MD* get() const { return md_; } + inline operator const EVP_MD*() const { return md_; } + inline operator bool() const { return md_ != nullptr; } + + static const Digest MD5; + static const Digest SHA1; + static const Digest SHA256; + static const Digest SHA384; + static const Digest SHA512; + + static const Digest FromName(std::string_view name); + + private: + const EVP_MD* md_ = nullptr; +}; + DataPointer hashDigest(const Buffer& data, const EVP_MD* md); class Cipher final { public: + static constexpr size_t MAX_KEY_LENGTH = EVP_MAX_KEY_LENGTH; + static constexpr size_t MAX_IV_LENGTH = EVP_MAX_IV_LENGTH; + Cipher() = default; Cipher(const EVP_CIPHER* cipher) : cipher_(cipher) {} Cipher(const Cipher&) = default; @@ -280,9 +313,15 @@ class Cipher final { bool isCcmMode() const; bool isOcbMode() const; bool isStreamMode() const; + bool isChaCha20Poly1305() const; bool isSupportedAuthenticatedMode() const; + int bytesToKey(const Digest& digest, + const Buffer& input, + unsigned char* key, + unsigned char* iv) const; + static const Cipher FromName(std::string_view name); static const Cipher FromNid(int nid); static const Cipher FromCtx(const CipherCtxPointer& ctx); @@ -644,6 +683,11 @@ class CipherCtxPointer final { int getMode() const; int getNid() const; + bool isGcmMode() const; + bool isCcmMode() const; + bool isWrapMode() const; + bool isChaCha20Poly1305() const; + bool update(const Buffer& in, unsigned char* out, int* out_len, @@ -1420,13 +1464,13 @@ const EVP_CIPHER* getCipherByName(const std::string_view name); // Verify that the specified HKDF output length is valid for the given digest. // The maximum length for HKDF output for a given digest is 255 times the // hash size for the given digest algorithm. -bool checkHkdfLength(const EVP_MD* md, size_t length); +bool checkHkdfLength(const Digest& digest, size_t length); bool extractP1363(const Buffer& buf, unsigned char* dest, size_t n); -DataPointer hkdf(const EVP_MD* md, +DataPointer hkdf(const Digest& md, const Buffer& key, const Buffer& info, const Buffer& salt, diff --git a/src/crypto/crypto_aes.cc b/src/crypto/crypto_aes.cc index d723ea54e3afb8..48c2fbde65dc31 100644 --- a/src/crypto/crypto_aes.cc +++ b/src/crypto/crypto_aes.cc @@ -484,7 +484,7 @@ Maybe AESCipherTraits::AdditionalConfig( return Nothing(); } - if (params->cipher.isWrapMode()) { + if (!params->cipher.isWrapMode()) { if (!ValidateIV(env, mode, args[offset + 1], params)) { return Nothing(); } diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc index 0e71890564bc4a..7d7f78d329c074 100644 --- a/src/crypto/crypto_cipher.cc +++ b/src/crypto/crypto_cipher.cc @@ -12,6 +12,8 @@ namespace node { using ncrypto::Cipher; using ncrypto::CipherCtxPointer; +using ncrypto::ClearErrorOnReturn; +using ncrypto::Digest; using ncrypto::EVPKeyCtxPointer; using ncrypto::EVPKeyPointer; using ncrypto::MarkPopErrorOnReturn; @@ -157,19 +159,18 @@ void GetCipherInfo(const FunctionCallbackInfo& args) { } // namespace void CipherBase::GetSSLCiphers(const FunctionCallbackInfo& args) { - MarkPopErrorOnReturn mark_pop_error_on_return; + ClearErrorOnReturn clear_error_on_return; Environment* env = Environment::GetCurrent(args); auto ctx = SSLCtxPointer::New(); if (!ctx) { return ThrowCryptoError( - env, mark_pop_error_on_return.peekError(), "SSL_CTX_new"); + env, clear_error_on_return.peekError(), "SSL_CTX_new"); } auto ssl = SSLPointer::New(ctx); if (!ssl) { - return ThrowCryptoError( - env, mark_pop_error_on_return.peekError(), "SSL_new"); + return ThrowCryptoError(env, clear_error_on_return.peekError(), "SSL_new"); } LocalVector arr(env->isolate()); @@ -304,7 +305,7 @@ void CipherBase::New(const FunctionCallbackInfo& args) { new CipherBase(env, args.This(), args[0]->IsTrue() ? kCipher : kDecipher); } -void CipherBase::CommonInit(const char* cipher_type, +void CipherBase::CommonInit(std::string_view cipher_type, const ncrypto::Cipher& cipher, const unsigned char* key, int key_len, @@ -346,32 +347,28 @@ void CipherBase::CommonInit(const char* cipher_type, } } -void CipherBase::Init(const char* cipher_type, +void CipherBase::Init(std::string_view cipher_type, const ArrayBufferOrViewContents& key_buf, unsigned int auth_tag_len) { HandleScope scope(env()->isolate()); MarkPopErrorOnReturn mark_pop_error_on_return; - auto cipher = Cipher::FromName(cipher_type); + const auto cipher = Cipher::FromName(cipher_type); if (!cipher) { return THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env()); } - unsigned char key[EVP_MAX_KEY_LENGTH]; - unsigned char iv[EVP_MAX_IV_LENGTH]; - - int key_len = EVP_BytesToKey(cipher, - EVP_md5(), - nullptr, - key_buf.data(), - key_buf.size(), - 1, - key, - iv); + unsigned char key[Cipher::MAX_KEY_LENGTH]; + unsigned char iv[Cipher::MAX_IV_LENGTH]; + + ncrypto::Buffer keyBuf{ + .data = key_buf.data(), + .len = key_buf.size(), + }; + int key_len = cipher.bytesToKey(Digest::MD5, keyBuf, key, iv); CHECK_NE(key_len, 0); - if (kind_ == kCipher && (cipher.isCtrMode() || - cipher.isGcmMode() || - cipher.isCcmMode())) { + if (kind_ == kCipher && + (cipher.isCtrMode() || cipher.isGcmMode() || cipher.isCcmMode())) { // Ignore the return value (i.e. possible exception) because we are // not calling back into JS anyway. ProcessEmitWarning(env(), @@ -410,10 +407,10 @@ void CipherBase::Init(const FunctionCallbackInfo& args) { auth_tag_len = kNoAuthTagLength; } - cipher->Init(*cipher_type, key_buf, auth_tag_len); + cipher->Init(cipher_type.ToStringView(), key_buf, auth_tag_len); } -void CipherBase::InitIv(const char* cipher_type, +void CipherBase::InitIv(std::string_view cipher_type, const ByteSource& key_buf, const ArrayBufferOrViewContents& iv_buf, unsigned int auth_tag_len) { @@ -439,7 +436,7 @@ void CipherBase::InitIv(const char* cipher_type, return THROW_ERR_CRYPTO_INVALID_IV(env()); } - if (cipher.getNid() == NID_chacha20_poly1305) { + if (cipher.isChaCha20Poly1305()) { CHECK(has_iv); // Check for invalid IV lengths, since OpenSSL does not under some // conditions: @@ -493,13 +490,12 @@ void CipherBase::InitIv(const FunctionCallbackInfo& args) { auth_tag_len = kNoAuthTagLength; } - cipher->InitIv(*cipher_type, key_buf, iv_buf, auth_tag_len); + cipher->InitIv(cipher_type.ToStringView(), key_buf, iv_buf, auth_tag_len); } -bool CipherBase::InitAuthenticated( - const char* cipher_type, - int iv_len, - unsigned int auth_tag_len) { +bool CipherBase::InitAuthenticated(std::string_view cipher_type, + int iv_len, + unsigned int auth_tag_len) { CHECK(IsAuthenticatedMode()); MarkPopErrorOnReturn mark_pop_error_on_return; @@ -508,8 +504,7 @@ bool CipherBase::InitAuthenticated( return false; } - const int mode = ctx_.getMode(); - if (mode == EVP_CIPH_GCM_MODE) { + if (ctx_.isGcmMode()) { if (auth_tag_len != kNoAuthTagLength) { if (!Cipher::IsValidGCMTagLength(auth_tag_len)) { THROW_ERR_CRYPTO_INVALID_AUTH_TAG( @@ -528,7 +523,7 @@ bool CipherBase::InitAuthenticated( // length defaults to 16 bytes when encrypting. Unlike GCM, the // authentication tag length also defaults to 16 bytes when decrypting, // whereas GCM would accept any valid authentication tag length. - if (ctx_.getNid() == NID_chacha20_poly1305) { + if (ctx_.isChaCha20Poly1305()) { auth_tag_len = 16; } else { THROW_ERR_CRYPTO_INVALID_AUTH_TAG( @@ -539,12 +534,7 @@ bool CipherBase::InitAuthenticated( // TODO(tniessen) Support CCM decryption in FIPS mode -#if OPENSSL_VERSION_MAJOR >= 3 - if (mode == EVP_CIPH_CCM_MODE && kind_ == kDecipher && - EVP_default_properties_is_fips_enabled(nullptr)) { -#else - if (mode == EVP_CIPH_CCM_MODE && kind_ == kDecipher && FIPS_mode()) { -#endif + if (ctx_.isCcmMode() && kind_ == kDecipher && ncrypto::isFipsEnabled()) { THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env(), "CCM encryption not supported in FIPS mode"); return false; @@ -560,7 +550,7 @@ bool CipherBase::InitAuthenticated( // Remember the given authentication tag length for later. auth_tag_len_ = auth_tag_len; - if (mode == EVP_CIPH_CCM_MODE) { + if (ctx_.isCcmMode()) { // Restrict the message length to min(INT_MAX, 2^(8*(15-iv_len))-1) bytes. CHECK(iv_len >= 7 && iv_len <= 13); max_message_size_ = INT_MAX; @@ -574,7 +564,7 @@ bool CipherBase::InitAuthenticated( bool CipherBase::CheckCCMMessageLength(int message_len) { CHECK(ctx_); - CHECK(ctx_.getMode() == EVP_CIPH_CCM_MODE); + CHECK(ctx_.isCcmMode()); if (message_len > max_message_size_) { THROW_ERR_CRYPTO_INVALID_MESSAGELEN(env()); @@ -627,9 +617,8 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo& args) { } unsigned int tag_len = auth_tag.size(); - const int mode = cipher->ctx_.getMode(); bool is_valid; - if (mode == EVP_CIPH_GCM_MODE) { + if (cipher->ctx_.isGcmMode()) { // Restrict GCM tag lengths according to NIST 800-38d, page 9. is_valid = (cipher->auth_tag_len_ == kNoAuthTagLength || cipher->auth_tag_len_ == tag_len) && @@ -647,7 +636,7 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo& args) { env, "Invalid authentication tag length: %u", tag_len); } - if (mode == EVP_CIPH_GCM_MODE && cipher->auth_tag_len_ == kNoAuthTagLength && + if (cipher->ctx_.isGcmMode() && cipher->auth_tag_len_ == kNoAuthTagLength && tag_len != 16 && env->EmitProcessEnvWarning()) { if (ProcessEmitDeprecationWarning( env, @@ -691,11 +680,10 @@ bool CipherBase::SetAAD( MarkPopErrorOnReturn mark_pop_error_on_return; int outlen; - const int mode = ctx_.getMode(); // When in CCM mode, we need to set the authentication tag and the plaintext // length in advance. - if (mode == EVP_CIPH_CCM_MODE) { + if (ctx_.isCcmMode()) { if (plaintext_len < 0) { THROW_ERR_MISSING_ARGS(env(), "options.plaintextLength required for CCM mode with AAD"); @@ -750,9 +738,7 @@ CipherBase::UpdateResult CipherBase::Update( if (!ctx_ || len > INT_MAX) return kErrorState; MarkPopErrorOnReturn mark_pop_error_on_return; - const int mode = ctx_.getMode(); - - if (mode == EVP_CIPH_CCM_MODE && !CheckCCMMessageLength(len)) { + if (ctx_.isCcmMode() && !CheckCCMMessageLength(len)) { return kErrorMessageSize; } @@ -771,7 +757,7 @@ CipherBase::UpdateResult CipherBase::Update( .data = reinterpret_cast(data), .len = len, }; - if (kind_ == kCipher && mode == EVP_CIPH_WRAP_MODE && + if (kind_ == kCipher && ctx_.isWrapMode() && !ctx_.update(buffer, nullptr, &buf_len)) { return kErrorState; } @@ -802,7 +788,7 @@ CipherBase::UpdateResult CipherBase::Update( // When in CCM mode, EVP_CipherUpdate will fail if the authentication tag is // invalid. In that case, remember the error and throw in final(). - if (!r && kind_ == kDecipher && mode == EVP_CIPH_CCM_MODE) { + if (!r && kind_ == kDecipher && ctx_.isCcmMode()) { pending_auth_failed_ = true; return kSuccess; } @@ -857,8 +843,6 @@ void CipherBase::SetAutoPadding(const FunctionCallbackInfo& args) { bool CipherBase::Final(std::unique_ptr* out) { if (!ctx_) return false; - const int mode = ctx_.getMode(); - *out = ArrayBuffer::NewBackingStore( env()->isolate(), static_cast(ctx_.getBlockSize()), @@ -869,18 +853,19 @@ bool CipherBase::Final(std::unique_ptr* out) { MaybePassAuthTagToOpenSSL(); } +#if (OPENSSL_VERSION_NUMBER < 0x30000000L) // OpenSSL v1.x doesn't verify the presence of the auth tag so do // it ourselves, see https://github.com/nodejs/node/issues/45874. - if (OPENSSL_VERSION_NUMBER < 0x30000000L && kind_ == kDecipher && - NID_chacha20_poly1305 == ctx_.getNid() && + if (kind_ == kDecipher && ctx_.isChaCha20Poly1305() && auth_tag_state_ != kAuthTagPassedToOpenSSL) { return false; } +#endif // In CCM mode, final() only checks whether authentication failed in update(). // EVP_CipherFinal_ex must not be called and will fail. bool ok; - if (kind_ == kDecipher && mode == EVP_CIPH_CCM_MODE) { + if (kind_ == kDecipher && ctx_.isCcmMode()) { ok = !pending_auth_failed_; *out = ArrayBuffer::NewBackingStore(env()->isolate(), 0); } else { @@ -904,7 +889,7 @@ bool CipherBase::Final(std::unique_ptr* out) { // but defaults to 16 bytes when encrypting. In CCM and OCB mode, it must // always be given by the user. if (auth_tag_len_ == kNoAuthTagLength) { - CHECK(mode == EVP_CIPH_GCM_MODE); + CHECK(ctx_.isGcmMode()); auth_tag_len_ = sizeof(auth_tag_); } ok = ctx_.getAeadTag(auth_tag_len_, @@ -951,7 +936,7 @@ bool PublicKeyCipher::Cipher( Environment* env, const EVPKeyPointer& pkey, int padding, - const EVP_MD* digest, + const Digest& digest, const ArrayBufferOrViewContents& oaep_label, const ArrayBufferOrViewContents& data, std::unique_ptr* out) { @@ -965,6 +950,7 @@ bool PublicKeyCipher::Cipher( }; auto buf = cipher(pkey, params, in); + if (!buf) return false; if (buf.size() == 0) { @@ -1007,23 +993,19 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { return ThrowCryptoError(env, ERR_get_error()); } -#ifndef OPENSSL_IS_BORINGSSL // RSA implicit rejection here is not supported by BoringSSL. - // Skip this check when boring is used. - if (!ctx.setRsaImplicitRejection()) { + if (!ctx.setRsaImplicitRejection()) [[unlikely]] { return THROW_ERR_INVALID_ARG_VALUE( env, "RSA_PKCS1_PADDING is no longer supported for private decryption"); } -#endif } - const EVP_MD* digest = nullptr; + Digest digest; if (args[offset + 2]->IsString()) { - const Utf8Value oaep_str(env->isolate(), args[offset + 2]); - digest = ncrypto::getDigestByName(oaep_str.ToStringView()); - if (digest == nullptr) - return THROW_ERR_OSSL_EVP_INVALID_DIGEST(env); + Utf8Value oaep_str(env->isolate(), args[offset + 2]); + digest = Digest::FromName(oaep_str.ToStringView()); + if (!digest) return THROW_ERR_OSSL_EVP_INVALID_DIGEST(env); } ArrayBufferOrViewContents oaep_label( diff --git a/src/crypto/crypto_cipher.h b/src/crypto/crypto_cipher.h index 950acfa2521ede..fa26f5aa864760 100644 --- a/src/crypto/crypto_cipher.h +++ b/src/crypto/crypto_cipher.h @@ -43,21 +43,22 @@ class CipherBase : public BaseObject { }; static const unsigned kNoAuthTagLength = static_cast(-1); - void CommonInit(const char* cipher_type, + void CommonInit(std::string_view cipher_type, const ncrypto::Cipher& cipher, const unsigned char* key, int key_len, const unsigned char* iv, int iv_len, unsigned int auth_tag_len); - void Init(const char* cipher_type, + void Init(std::string_view cipher_type, const ArrayBufferOrViewContents& key_buf, unsigned int auth_tag_len); - void InitIv(const char* cipher_type, + void InitIv(std::string_view cipher_type, const ByteSource& key_buf, const ArrayBufferOrViewContents& iv_buf, unsigned int auth_tag_len); - bool InitAuthenticated(const char* cipher_type, int iv_len, + bool InitAuthenticated(std::string_view cipher_type, + int iv_len, unsigned int auth_tag_len); bool CheckCCMMessageLength(int message_len); UpdateResult Update(const char* data, size_t len, @@ -110,7 +111,7 @@ class PublicKeyCipher { static bool Cipher(Environment* env, const ncrypto::EVPKeyPointer& pkey, int padding, - const EVP_MD* digest, + const ncrypto::Digest& digest, const ArrayBufferOrViewContents& oaep_label, const ArrayBufferOrViewContents& data, std::unique_ptr* out); @@ -148,10 +149,8 @@ class CipherJob final : public CryptoJob { CryptoJobMode mode = GetCryptoJobMode(args[0]); CHECK(args[1]->IsUint32()); // Cipher Mode - - uint32_t cmode = args[1].As()->Value(); - CHECK_LE(cmode, WebCryptoCipherMode::kWebCryptoCipherDecrypt); - WebCryptoCipherMode cipher_mode = static_cast(cmode); + auto cipher_mode = + static_cast(args[1].As()->Value()); CHECK(args[2]->IsObject()); // KeyObject KeyObjectHandle* key; diff --git a/src/crypto/crypto_hkdf.cc b/src/crypto/crypto_hkdf.cc index 10bb8e4258bf63..418aa0bb23aa42 100644 --- a/src/crypto/crypto_hkdf.cc +++ b/src/crypto/crypto_hkdf.cc @@ -9,6 +9,7 @@ namespace node { +using ncrypto::Digest; using v8::FunctionCallbackInfo; using v8::JustVoid; using v8::Maybe; @@ -54,8 +55,8 @@ Maybe HKDFTraits::AdditionalConfig( CHECK(args[offset + 4]->IsUint32()); // Length Utf8Value hash(env->isolate(), args[offset]); - params->digest = ncrypto::getDigestByName(hash.ToStringView()); - if (params->digest == nullptr) [[unlikely]] { + params->digest = Digest::FromName(hash.ToStringView()); + if (!params->digest) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *hash); return Nothing(); } diff --git a/src/crypto/crypto_hkdf.h b/src/crypto/crypto_hkdf.h index be6e823269d8ab..67484e4711a96e 100644 --- a/src/crypto/crypto_hkdf.h +++ b/src/crypto/crypto_hkdf.h @@ -14,7 +14,7 @@ namespace crypto { struct HKDFConfig final : public MemoryRetainer { CryptoJobMode mode; size_t length; - const EVP_MD* digest; + ncrypto::Digest digest; KeyObjectData key; ByteSource salt; ByteSource info; From d111ff9dfcb4c132644c58bc391a94075d72b575 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 3 Mar 2025 09:20:47 -0800 Subject: [PATCH 4/4] src: move more openssl api detail behind ncrypto --- deps/ncrypto/ncrypto.cc | 63 ++++++++++++++++++++++++------------ deps/ncrypto/ncrypto.h | 25 +++++++------- src/crypto/crypto_context.cc | 42 ++++++++++-------------- src/crypto/crypto_hmac.cc | 11 ++++--- src/crypto/crypto_hmac.h | 2 +- src/crypto/crypto_pbkdf2.cc | 5 +-- src/crypto/crypto_pbkdf2.h | 2 +- src/crypto/crypto_rsa.cc | 27 ++++++++-------- src/crypto/crypto_rsa.h | 6 ++-- src/crypto/crypto_sig.cc | 9 +++--- src/crypto/crypto_sig.h | 2 +- src/crypto/crypto_x509.cc | 25 +++++++------- 12 files changed, 119 insertions(+), 100 deletions(-) diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index 220e0f253715fe..9ea5b541ec6bda 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -1164,7 +1164,7 @@ X509View X509View::From(const SSLCtxPointer& ctx) { } std::optional X509View::getFingerprint( - const EVP_MD* method) const { + const Digest& method) const { unsigned int md_size; unsigned char md[EVP_MAX_MD_SIZE]; static constexpr char hex[] = "0123456789ABCDEF"; @@ -1692,8 +1692,11 @@ DataPointer hkdf(const Digest& md, } auto ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_HKDF); + // OpenSSL < 3.0.0 accepted only a void* as the argument of + // EVP_PKEY_CTX_set_hkdf_md. + const EVP_MD* md_ptr = md; if (!ctx || !EVP_PKEY_derive_init(ctx.get()) || - !EVP_PKEY_CTX_set_hkdf_md(ctx.get(), md) || + !EVP_PKEY_CTX_set_hkdf_md(ctx.get(), md_ptr) || !EVP_PKEY_CTX_add1_hkdf_info(ctx.get(), info.data, info.len)) { return {}; } @@ -1776,7 +1779,7 @@ DataPointer scrypt(const Buffer& pass, return {}; } -DataPointer pbkdf2(const EVP_MD* md, +DataPointer pbkdf2(const Digest& md, const Buffer& pass, const Buffer& salt, uint32_t iterations, @@ -1788,12 +1791,13 @@ DataPointer pbkdf2(const EVP_MD* md, } auto dp = DataPointer::Alloc(length); + const EVP_MD* md_ptr = md; if (dp && PKCS5_PBKDF2_HMAC(pass.data, pass.len, salt.data, salt.len, iterations, - md, + md_ptr, length, reinterpret_cast(dp.get()))) { return dp; @@ -2728,6 +2732,17 @@ bool SSLCtxPointer::setGroups(const char* groups) { return SSL_CTX_set1_groups_list(get(), groups) == 1; } +bool SSLCtxPointer::setCipherSuites(std::string_view ciphers) { +#ifndef OPENSSL_IS_BORINGSSL + if (!ctx_) return false; + return SSL_CTX_set_ciphersuites(ctx_.get(), ciphers.data()); +#else + // BoringSSL does not allow API config of TLS 1.3 cipher suites. + // We treat this as a non-op. + return true; +#endif +} + // ============================================================================ const Cipher Cipher::FromName(std::string_view name) { @@ -3335,14 +3350,16 @@ bool EVPKeyCtxPointer::setEcParameters(int curve, int encoding) { EVP_PKEY_CTX_set_ec_param_enc(ctx_.get(), encoding) == 1; } -bool EVPKeyCtxPointer::setRsaOaepMd(const EVP_MD* md) { - if (md == nullptr || !ctx_) return false; - return EVP_PKEY_CTX_set_rsa_oaep_md(ctx_.get(), md) > 0; +bool EVPKeyCtxPointer::setRsaOaepMd(const Digest& md) { + if (!md || !ctx_) return false; + const EVP_MD* md_ptr = md; + return EVP_PKEY_CTX_set_rsa_oaep_md(ctx_.get(), md_ptr) > 0; } -bool EVPKeyCtxPointer::setRsaMgf1Md(const EVP_MD* md) { - if (md == nullptr || !ctx_) return false; - return EVP_PKEY_CTX_set_rsa_mgf1_md(ctx_.get(), md) > 0; +bool EVPKeyCtxPointer::setRsaMgf1Md(const Digest& md) { + if (!md || !ctx_) return false; + const EVP_MD* md_ptr = md; + return EVP_PKEY_CTX_set_rsa_mgf1_md(ctx_.get(), md_ptr) > 0; } bool EVPKeyCtxPointer::setRsaPadding(int padding) { @@ -3377,14 +3394,17 @@ bool EVPKeyCtxPointer::setRsaKeygenPubExp(BignumPointer&& e) { return false; } -bool EVPKeyCtxPointer::setRsaPssKeygenMd(const EVP_MD* md) { - if (md == nullptr || !ctx_) return false; - return EVP_PKEY_CTX_set_rsa_pss_keygen_md(ctx_.get(), md) > 0; +bool EVPKeyCtxPointer::setRsaPssKeygenMd(const Digest& md) { + if (!md || !ctx_) return false; + // OpenSSL < 3 accepts a void* for the md parameter. + const EVP_MD* md_ptr = md; + return EVP_PKEY_CTX_set_rsa_pss_keygen_md(ctx_.get(), md_ptr) > 0; } -bool EVPKeyCtxPointer::setRsaPssKeygenMgf1Md(const EVP_MD* md) { - if (md == nullptr || !ctx_) return false; - return EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md(ctx_.get(), md) > 0; +bool EVPKeyCtxPointer::setRsaPssKeygenMgf1Md(const Digest& md) { + if (!md || !ctx_) return false; + const EVP_MD* md_ptr = md; + return EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md(ctx_.get(), md_ptr) > 0; } bool EVPKeyCtxPointer::setRsaPssSaltlen(int salt_len) { @@ -3858,7 +3878,7 @@ EVP_MD_CTX* EVPMDCtxPointer::release() { return ctx_.release(); } -bool EVPMDCtxPointer::digestInit(const EVP_MD* digest) { +bool EVPMDCtxPointer::digestInit(const Digest& digest) { if (!ctx_) return false; return EVP_DigestInit_ex(ctx_.get(), digest, nullptr) > 0; } @@ -3924,7 +3944,7 @@ bool EVPMDCtxPointer::copyTo(const EVPMDCtxPointer& other) const { } std::optional EVPMDCtxPointer::signInit(const EVPKeyPointer& key, - const EVP_MD* digest) { + const Digest& digest) { EVP_PKEY_CTX* ctx = nullptr; if (!EVP_DigestSignInit(ctx_.get(), &ctx, digest, nullptr, key.get())) { return std::nullopt; @@ -3933,7 +3953,7 @@ std::optional EVPMDCtxPointer::signInit(const EVPKeyPointer& key, } std::optional EVPMDCtxPointer::verifyInit( - const EVPKeyPointer& key, const EVP_MD* digest) { + const EVPKeyPointer& key, const Digest& digest) { EVP_PKEY_CTX* ctx = nullptr; if (!EVP_DigestVerifyInit(ctx_.get(), &ctx, digest, nullptr, key.get())) { return std::nullopt; @@ -4030,9 +4050,10 @@ HMAC_CTX* HMACCtxPointer::release() { return ctx_.release(); } -bool HMACCtxPointer::init(const Buffer& buf, const EVP_MD* md) { +bool HMACCtxPointer::init(const Buffer& buf, const Digest& md) { if (!ctx_) return false; - return HMAC_Init_ex(ctx_.get(), buf.data, buf.len, md, nullptr) == 1; + const EVP_MD* md_ptr = md; + return HMAC_Init_ex(ctx_.get(), buf.data, buf.len, md_ptr, nullptr) == 1; } bool HMACCtxPointer::update(const Buffer& buf) { diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index ad86ea58e028df..9fdf0e1d22e819 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -249,6 +249,7 @@ struct Buffer { class Digest final { public: + static constexpr size_t MAX_SIZE = EVP_MAX_MD_SIZE; Digest() = default; Digest(const EVP_MD* md) : md_(md) {} Digest(const Digest&) = default; @@ -353,7 +354,7 @@ class Cipher final { struct CipherParams { int padding; - const EVP_MD* digest; + Digest digest; const Buffer label; }; @@ -723,13 +724,13 @@ class EVPKeyCtxPointer final { bool setDsaParameters(uint32_t bits, std::optional q_bits); bool setEcParameters(int curve, int encoding); - bool setRsaOaepMd(const EVP_MD* md); - bool setRsaMgf1Md(const EVP_MD* md); + bool setRsaOaepMd(const Digest& md); + bool setRsaMgf1Md(const Digest& md); bool setRsaPadding(int padding); bool setRsaKeygenPubExp(BignumPointer&& e); bool setRsaKeygenBits(int bits); - bool setRsaPssKeygenMd(const EVP_MD* md); - bool setRsaPssKeygenMgf1Md(const EVP_MD* md); + bool setRsaPssKeygenMd(const Digest& md); + bool setRsaPssKeygenMgf1Md(const Digest& md); bool setRsaPssSaltlen(int salt_len); bool setRsaImplicitRejection(); bool setRsaOaepLabel(DataPointer&& data); @@ -1003,6 +1004,8 @@ class SSLCtxPointer final { SSL_CTX_set_tlsext_status_arg(get(), nullptr); } + bool setCipherSuites(std::string_view ciphers); + static SSLCtxPointer NewServer(); static SSLCtxPointer NewClient(); static SSLCtxPointer New(const SSL_METHOD* method = TLS_method()); @@ -1131,7 +1134,7 @@ class X509View final { bool checkPrivateKey(const EVPKeyPointer& pkey) const; bool checkPublicKey(const EVPKeyPointer& pkey) const; - std::optional getFingerprint(const EVP_MD* method) const; + std::optional getFingerprint(const Digest& method) const; X509Pointer clone() const; @@ -1327,16 +1330,16 @@ class EVPMDCtxPointer final { void reset(EVP_MD_CTX* ctx = nullptr); EVP_MD_CTX* release(); - bool digestInit(const EVP_MD* digest); + bool digestInit(const Digest& digest); bool digestUpdate(const Buffer& in); DataPointer digestFinal(size_t length); bool digestFinalInto(Buffer* buf); size_t getExpectedSize(); std::optional signInit(const EVPKeyPointer& key, - const EVP_MD* digest); + const Digest& digest); std::optional verifyInit(const EVPKeyPointer& key, - const EVP_MD* digest); + const Digest& digest); DataPointer signOneShot(const Buffer& buf) const; DataPointer sign(const Buffer& buf) const; @@ -1371,7 +1374,7 @@ class HMACCtxPointer final { void reset(HMAC_CTX* ctx = nullptr); HMAC_CTX* release(); - bool init(const Buffer& buf, const EVP_MD* md); + bool init(const Buffer& buf, const Digest& md); bool update(const Buffer& buf); DataPointer digest(); bool digestInto(Buffer* buf); @@ -1486,7 +1489,7 @@ DataPointer scrypt(const Buffer& pass, uint64_t maxmem, size_t length); -DataPointer pbkdf2(const EVP_MD* md, +DataPointer pbkdf2(const Digest& md, const Buffer& pass, const Buffer& salt, uint32_t iterations, diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index 3e4b517fa462ef..bb549475b4997b 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -31,9 +31,11 @@ namespace node { using ncrypto::BignumPointer; using ncrypto::BIOPointer; +using ncrypto::Cipher; using ncrypto::ClearErrorOnReturn; using ncrypto::CryptoErrorList; using ncrypto::DHPointer; +using ncrypto::Digest; #ifndef OPENSSL_NO_ENGINE using ncrypto::EnginePointer; #endif // !OPENSSL_NO_ENGINE @@ -1440,8 +1442,6 @@ void SecureContext::AddRootCerts(const FunctionCallbackInfo& args) { } void SecureContext::SetCipherSuites(const FunctionCallbackInfo& args) { - // BoringSSL doesn't allow API config of TLS1.3 cipher suites. -#ifndef OPENSSL_IS_BORINGSSL SecureContext* sc; ASSIGN_OR_RETURN_UNWRAP(&sc, args.This()); Environment* env = sc->env(); @@ -1451,9 +1451,9 @@ void SecureContext::SetCipherSuites(const FunctionCallbackInfo& args) { CHECK(args[0]->IsString()); const Utf8Value ciphers(env->isolate(), args[0]); - if (!SSL_CTX_set_ciphersuites(sc->ctx_.get(), *ciphers)) + if (!sc->ctx_.setCipherSuites(ciphers.ToStringView())) { return ThrowCryptoError(env, ERR_get_error(), "Failed to set ciphers"); -#endif + } } void SecureContext::SetCiphers(const FunctionCallbackInfo& args) { @@ -1932,25 +1932,14 @@ int SecureContext::TicketKeyCallback(SSL* ssl, } ArrayBufferViewContents hmac_buf(hmac); - HMAC_Init_ex(hctx, - hmac_buf.data(), - hmac_buf.length(), - EVP_sha256(), - nullptr); + HMAC_Init_ex( + hctx, hmac_buf.data(), hmac_buf.length(), Digest::SHA256, nullptr); ArrayBufferViewContents aes_key(aes.As()); if (enc) { - EVP_EncryptInit_ex(ectx, - EVP_aes_128_cbc(), - nullptr, - aes_key.data(), - iv); + EVP_EncryptInit_ex(ectx, Cipher::AES_128_CBC, nullptr, aes_key.data(), iv); } else { - EVP_DecryptInit_ex(ectx, - EVP_aes_128_cbc(), - nullptr, - aes_key.data(), - iv); + EVP_DecryptInit_ex(ectx, Cipher::AES_128_CBC, nullptr, aes_key.data(), iv); } return r; @@ -1969,11 +1958,11 @@ int SecureContext::TicketCompatibilityCallback(SSL* ssl, memcpy(name, sc->ticket_key_name_, sizeof(sc->ticket_key_name_)); if (!ncrypto::CSPRNG(iv, 16) || EVP_EncryptInit_ex( - ectx, EVP_aes_128_cbc(), nullptr, sc->ticket_key_aes_, iv) <= 0 || + ectx, Cipher::AES_128_CBC, nullptr, sc->ticket_key_aes_, iv) <= 0 || HMAC_Init_ex(hctx, sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_), - EVP_sha256(), + Digest::SHA256, nullptr) <= 0) { return -1; } @@ -1985,10 +1974,13 @@ int SecureContext::TicketCompatibilityCallback(SSL* ssl, return 0; } - if (EVP_DecryptInit_ex(ectx, EVP_aes_128_cbc(), nullptr, sc->ticket_key_aes_, - iv) <= 0 || - HMAC_Init_ex(hctx, sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_), - EVP_sha256(), nullptr) <= 0) { + if (EVP_DecryptInit_ex( + ectx, Cipher::AES_128_CBC, nullptr, sc->ticket_key_aes_, iv) <= 0 || + HMAC_Init_ex(hctx, + sc->ticket_key_hmac_, + sizeof(sc->ticket_key_hmac_), + Digest::SHA256, + nullptr) <= 0) { return -1; } return 1; diff --git a/src/crypto/crypto_hmac.cc b/src/crypto/crypto_hmac.cc index 56a09e1a2d9b0f..d08c54ef36ba6f 100644 --- a/src/crypto/crypto_hmac.cc +++ b/src/crypto/crypto_hmac.cc @@ -13,6 +13,7 @@ namespace node { +using ncrypto::Digest; using ncrypto::HMACCtxPointer; using v8::Boolean; using v8::FunctionCallbackInfo; @@ -70,8 +71,8 @@ void Hmac::New(const FunctionCallbackInfo& args) { void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) { HandleScope scope(env()->isolate()); - const EVP_MD* md = ncrypto::getDigestByName(hash_type); - if (md == nullptr) [[unlikely]] { + Digest md = Digest::FromName(hash_type); + if (!md) [[unlikely]] { return THROW_ERR_CRYPTO_INVALID_DIGEST( env(), "Invalid digest: %s", hash_type); } @@ -130,7 +131,7 @@ void Hmac::HmacDigest(const FunctionCallbackInfo& args) { encoding = ParseEncoding(env->isolate(), args[0], BUFFER); } - unsigned char md_value[EVP_MAX_MD_SIZE]; + unsigned char md_value[Digest::MAX_SIZE]; ncrypto::Buffer buf{ .data = md_value, .len = sizeof(md_value), @@ -199,8 +200,8 @@ Maybe HmacTraits::AdditionalConfig( CHECK(args[offset + 2]->IsObject()); // Key Utf8Value digest(env->isolate(), args[offset + 1]); - params->digest = ncrypto::getDigestByName(digest.ToStringView()); - if (params->digest == nullptr) [[unlikely]] { + params->digest = Digest::FromName(digest.ToStringView()); + if (!params->digest) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); } diff --git a/src/crypto/crypto_hmac.h b/src/crypto/crypto_hmac.h index e29ec231a1b7d4..728b315ec4e214 100644 --- a/src/crypto/crypto_hmac.h +++ b/src/crypto/crypto_hmac.h @@ -45,7 +45,7 @@ struct HmacConfig final : public MemoryRetainer { KeyObjectData key; ByteSource data; ByteSource signature; - const EVP_MD* digest; + ncrypto::Digest digest; HmacConfig() = default; diff --git a/src/crypto/crypto_pbkdf2.cc b/src/crypto/crypto_pbkdf2.cc index 1a0dff8238d938..0838944c52fd5d 100644 --- a/src/crypto/crypto_pbkdf2.cc +++ b/src/crypto/crypto_pbkdf2.cc @@ -9,6 +9,7 @@ namespace node { +using ncrypto::Digest; using v8::FunctionCallbackInfo; using v8::Int32; using v8::JustVoid; @@ -100,8 +101,8 @@ Maybe PBKDF2Traits::AdditionalConfig( } Utf8Value name(args.GetIsolate(), args[offset + 4]); - params->digest = ncrypto::getDigestByName(name.ToStringView()); - if (params->digest == nullptr) [[unlikely]] { + params->digest = Digest::FromName(name.ToStringView()); + if (!params->digest) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *name); return Nothing(); } diff --git a/src/crypto/crypto_pbkdf2.h b/src/crypto/crypto_pbkdf2.h index 604736f308b7d6..c56544b5b1068b 100644 --- a/src/crypto/crypto_pbkdf2.h +++ b/src/crypto/crypto_pbkdf2.h @@ -30,7 +30,7 @@ struct PBKDF2Config final : public MemoryRetainer { ByteSource salt; int32_t iterations; int32_t length; - const EVP_MD* digest = nullptr; + ncrypto::Digest digest; PBKDF2Config() = default; diff --git a/src/crypto/crypto_rsa.cc b/src/crypto/crypto_rsa.cc index 7742cf204df6d8..c01482b2931c73 100644 --- a/src/crypto/crypto_rsa.cc +++ b/src/crypto/crypto_rsa.cc @@ -16,6 +16,7 @@ namespace node { using ncrypto::BignumPointer; using ncrypto::DataPointer; +using ncrypto::Digest; using ncrypto::EVPKeyCtxPointer; using ncrypto::EVPKeyPointer; using ncrypto::RSAPointer; @@ -55,8 +56,7 @@ EVPKeyCtxPointer RsaKeyGenTraits::Setup(RsaKeyPairGenConfig* params) { } if (params->params.variant == kKeyVariantRSA_PSS) { - if (params->params.md != nullptr && - !ctx.setRsaPssKeygenMd(params->params.md)) { + if (params->params.md && !ctx.setRsaPssKeygenMd(params->params.md)) { return {}; } @@ -64,18 +64,18 @@ EVPKeyCtxPointer RsaKeyGenTraits::Setup(RsaKeyPairGenConfig* params) { // OpenSSL 1.1.1 behaves as recommended by RFC 8017 and defaults the MGF1 // hash algorithm to the RSA-PSS hashAlgorithm. Remove this code if the // behavior of OpenSSL 3 changes. - const EVP_MD* mgf1_md = params->params.mgf1_md; - if (mgf1_md == nullptr && params->params.md != nullptr) { + auto& mgf1_md = params->params.mgf1_md; + if (!mgf1_md && params->params.md) { mgf1_md = params->params.md; } - if (mgf1_md != nullptr && !ctx.setRsaPssKeygenMgf1Md(mgf1_md)) { + if (mgf1_md && !ctx.setRsaPssKeygenMgf1Md(mgf1_md)) { return {}; } int saltlen = params->params.saltlen; - if (saltlen < 0 && params->params.md != nullptr) { - saltlen = EVP_MD_size(params->params.md); + if (saltlen < 0 && params->params.md) { + saltlen = params->params.md.size(); } if (saltlen >= 0 && !ctx.setRsaPssSaltlen(saltlen)) { @@ -141,8 +141,8 @@ Maybe RsaKeyGenTraits::AdditionalConfig( if (!args[*offset]->IsUndefined()) { CHECK(args[*offset]->IsString()); Utf8Value digest(env->isolate(), args[*offset]); - params->params.md = ncrypto::getDigestByName(digest.ToStringView()); - if (params->params.md == nullptr) { + params->params.md = Digest::FromName(digest.ToStringView()); + if (!params->params.md) { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); } @@ -151,8 +151,8 @@ Maybe RsaKeyGenTraits::AdditionalConfig( if (!args[*offset + 1]->IsUndefined()) { CHECK(args[*offset + 1]->IsString()); Utf8Value digest(env->isolate(), args[*offset + 1]); - params->params.mgf1_md = ncrypto::getDigestByName(digest.ToStringView()); - if (params->params.mgf1_md == nullptr) { + params->params.mgf1_md = Digest::FromName(digest.ToStringView()); + if (!params->params.mgf1_md) { THROW_ERR_CRYPTO_INVALID_DIGEST( env, "Invalid MGF1 digest: %s", *digest); return Nothing(); @@ -276,9 +276,8 @@ Maybe RSACipherTraits::AdditionalConfig( case kKeyVariantRSA_OAEP: { CHECK(args[offset + 1]->IsString()); // digest Utf8Value digest(env->isolate(), args[offset + 1]); - - params->digest = ncrypto::getDigestByName(digest.ToStringView()); - if (params->digest == nullptr) { + params->digest = Digest::FromName(digest.ToStringView()); + if (!params->digest) { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); } diff --git a/src/crypto/crypto_rsa.h b/src/crypto/crypto_rsa.h index db5ba492faa398..a9912d6f43674b 100644 --- a/src/crypto/crypto_rsa.h +++ b/src/crypto/crypto_rsa.h @@ -26,8 +26,8 @@ struct RsaKeyPairParams final : public MemoryRetainer { // The following options are used for RSA-PSS. If any of them are set, a // RSASSA-PSS-params sequence will be added to the key. - const EVP_MD* md = nullptr; - const EVP_MD* mgf1_md = nullptr; + ncrypto::Digest md = nullptr; + ncrypto::Digest mgf1_md = nullptr; int saltlen = -1; SET_NO_MEMORY_INFO() @@ -80,7 +80,7 @@ struct RSACipherConfig final : public MemoryRetainer { CryptoJobMode mode = kCryptoJobAsync; ByteSource label; int padding = 0; - const EVP_MD* digest = nullptr; + ncrypto::Digest digest; RSACipherConfig() = default; diff --git a/src/crypto/crypto_sig.cc b/src/crypto/crypto_sig.cc index a28f12237d0883..3b9a638f1773f5 100644 --- a/src/crypto/crypto_sig.cc +++ b/src/crypto/crypto_sig.cc @@ -15,6 +15,7 @@ namespace node { using ncrypto::BignumPointer; using ncrypto::ClearErrorOnReturn; using ncrypto::DataPointer; +using ncrypto::Digest; using ncrypto::ECDSASigPointer; using ncrypto::EVPKeyCtxPointer; using ncrypto::EVPKeyPointer; @@ -233,8 +234,8 @@ bool UseP1363Encoding(const EVPKeyPointer& key, const DSASigEnc dsa_encoding) { SignBase::Error SignBase::Init(std::string_view digest) { CHECK_NULL(mdctx_); - auto md = ncrypto::getDigestByName(digest); - if (md == nullptr) [[unlikely]] + auto md = Digest::FromName(digest); + if (!md) [[unlikely]] return Error::UnknownDigest; mdctx_ = EVPMDCtxPointer::New(); @@ -587,8 +588,8 @@ Maybe SignTraits::AdditionalConfig( if (args[offset + 6]->IsString()) { Utf8Value digest(env->isolate(), args[offset + 6]); - params->digest = ncrypto::getDigestByName(digest.ToStringView()); - if (params->digest == nullptr) [[unlikely]] { + params->digest = Digest::FromName(digest.ToStringView()); + if (!params->digest) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); } diff --git a/src/crypto/crypto_sig.h b/src/crypto/crypto_sig.h index 36c51b07bb5692..eee61759a3840a 100644 --- a/src/crypto/crypto_sig.h +++ b/src/crypto/crypto_sig.h @@ -107,7 +107,7 @@ struct SignConfiguration final : public MemoryRetainer { KeyObjectData key; ByteSource data; ByteSource signature; - const EVP_MD* digest = nullptr; + ncrypto::Digest digest; int flags = SignConfiguration::kHasNone; int padding = 0; int salt_length = 0; diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc index b974667a4cacb9..dec0940e8d75d7 100644 --- a/src/crypto/crypto_x509.cc +++ b/src/crypto/crypto_x509.cc @@ -19,6 +19,7 @@ using ncrypto::BignumPointer; using ncrypto::BIOPointer; using ncrypto::ClearErrorOnReturn; using ncrypto::DataPointer; +using ncrypto::Digest; using ncrypto::ECKeyPointer; using ncrypto::SSLPointer; using ncrypto::X509Name; @@ -70,7 +71,7 @@ void ManagedX509::MemoryInfo(MemoryTracker* tracker) const { namespace { MaybeLocal GetFingerprintDigest(Environment* env, - const EVP_MD* method, + const Digest& method, const X509View& cert) { auto fingerprint = cert.getFingerprint(method); // Returning an empty string indicates that the digest failed for @@ -82,13 +83,13 @@ MaybeLocal GetFingerprintDigest(Environment* env, return OneByteString(env->isolate(), fp.data(), fp.length()); } -template +template void Fingerprint(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.This()); Local ret; - if (GetFingerprintDigest(env, algo(), cert->view()).ToLocal(&ret)) { + if (GetFingerprintDigest(env, algo, cert->view()).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } @@ -764,15 +765,15 @@ MaybeLocal X509ToObject(Environment* env, const X509View& cert) { !Set(env, info, env->fingerprint_string(), - GetFingerprintDigest(env, EVP_sha1(), cert)) || + GetFingerprintDigest(env, Digest::SHA1, cert)) || !Set(env, info, env->fingerprint256_string(), - GetFingerprintDigest(env, EVP_sha256(), cert)) || + GetFingerprintDigest(env, Digest::SHA256, cert)) || !Set(env, info, env->fingerprint512_string(), - GetFingerprintDigest(env, EVP_sha512(), cert)) || + GetFingerprintDigest(env, Digest::SHA512, cert)) || !Set( env, info, env->ext_key_usage_string(), GetKeyUsage(env, cert)) || !Set( @@ -805,11 +806,11 @@ Local X509Certificate::GetConstructorTemplate( SetProtoMethodNoSideEffect(isolate, tmpl, "validToDate", ValidToDate); SetProtoMethodNoSideEffect(isolate, tmpl, "validFromDate", ValidFromDate); SetProtoMethodNoSideEffect( - isolate, tmpl, "fingerprint", Fingerprint); + isolate, tmpl, "fingerprint", Fingerprint); SetProtoMethodNoSideEffect( - isolate, tmpl, "fingerprint256", Fingerprint); + isolate, tmpl, "fingerprint256", Fingerprint); SetProtoMethodNoSideEffect( - isolate, tmpl, "fingerprint512", Fingerprint); + isolate, tmpl, "fingerprint512", Fingerprint); SetProtoMethodNoSideEffect(isolate, tmpl, "keyUsage", KeyUsage); SetProtoMethodNoSideEffect(isolate, tmpl, "serialNumber", SerialNumber); SetProtoMethodNoSideEffect(isolate, tmpl, "pem", Pem); @@ -975,9 +976,9 @@ void X509Certificate::RegisterExternalReferences( registry->Register(ValidFrom); registry->Register(ValidToDate); registry->Register(ValidFromDate); - registry->Register(Fingerprint); - registry->Register(Fingerprint); - registry->Register(Fingerprint); + registry->Register(Fingerprint); + registry->Register(Fingerprint); + registry->Register(Fingerprint); registry->Register(KeyUsage); registry->Register(SerialNumber); registry->Register(Pem);