diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index 0a0da0263676d9..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"; @@ -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, @@ -1692,8 +1692,11 @@ DataPointer hkdf(const EVP_MD* 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 {}; } @@ -1703,7 +1706,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 @@ -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) { @@ -2742,6 +2757,55 @@ 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; +} + +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_); @@ -2818,6 +2882,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() { @@ -2851,9 +2923,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) { @@ -2894,6 +2966,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()); @@ -3258,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) { @@ -3300,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) { @@ -3673,6 +3770,74 @@ 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) {} @@ -3713,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; } @@ -3779,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; @@ -3788,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; @@ -3885,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) { @@ -4029,4 +4195,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 f73c84edce20bf..9fdf0e1d22e819 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -247,11 +247,45 @@ struct Buffer { size_t len = 0; }; +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; + 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; @@ -274,15 +308,53 @@ 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 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); + 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 + // 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; + Digest digest; const Buffer label; }; @@ -596,7 +668,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); @@ -611,6 +684,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, @@ -646,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); @@ -926,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()); @@ -1054,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; @@ -1250,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; @@ -1294,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); @@ -1387,13 +1467,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, @@ -1409,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_aes.cc b/src/crypto/crypto_aes.cc index 3f4e3e6672ae91..48c2fbde65dc31 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..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; @@ -89,22 +91,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 +130,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)) @@ -162,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()); @@ -187,20 +183,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, @@ -301,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, @@ -313,8 +317,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); @@ -343,33 +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); - 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(), @@ -408,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) { @@ -437,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: @@ -491,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; @@ -506,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( @@ -526,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( @@ -537,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; @@ -558,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; @@ -572,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()); @@ -625,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) && @@ -645,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, @@ -689,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"); @@ -748,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; } @@ -769,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; } @@ -800,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; } @@ -855,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()), @@ -867,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 { @@ -902,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_, @@ -949,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) { @@ -963,6 +950,7 @@ bool PublicKeyCipher::Cipher( }; auto buf = cipher(pkey, params, in); + if (!buf) return false; if (buf.size() == 0) { @@ -1005,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_context.cc b/src/crypto/crypto_context.cc index b7103be97bfed8..266797bb54b626 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 @@ -1518,8 +1520,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(); @@ -1529,9 +1529,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) { @@ -2010,25 +2010,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; @@ -2047,11 +2036,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; } @@ -2063,10 +2052,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_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; 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_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() || 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);