diff --git a/common.gypi b/common.gypi index bae09d8a5d6300..90530c62269030 100644 --- a/common.gypi +++ b/common.gypi @@ -24,6 +24,7 @@ 'node_module_version%': '', 'node_with_ltcg%': '', 'node_shared_openssl%': 'false', + 'openssl_is_boringssl%': 'false', 'node_tag%': '', 'uv_library%': 'static_library', diff --git a/configure.py b/configure.py index a5b57064d1fa4a..dc7722e0994e02 100755 --- a/configure.py +++ b/configure.py @@ -1440,6 +1440,48 @@ def get_gas_version(cc): warn(f'Could not recognize `gas`: {gas_ret}') return '0.0' +def get_openssl_macros(o): + """Extract OpenSSL preprocessor macros from the configured headers.""" + + # Use the C compiler to extract preprocessor macros from OpenSSL headers. + # crypto.h is included because BoringSSL declares OPENSSL_IS_BORINGSSL there. + args = ['-E', '-dM', + '-include', 'openssl/opensslv.h', + '-include', 'openssl/crypto.h', + '-'] + if not options.shared_openssl: + args = ['-I', 'deps/openssl/openssl/include'] + args + elif options.shared_openssl_includes: + args = ['-I', options.shared_openssl_includes] + args + else: + for dir in o['include_dirs']: + args = ['-I', dir] + args + + proc = subprocess.Popen( + shlex.split(CC) + args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + with proc: + proc.stdin.write(b'\n') + out = to_utf8(proc.communicate()[0]) + + if proc.returncode != 0: + warn('Failed to extract OpenSSL macros from headers') + return {} + + macros = {} + for line in out.split('\n'): + if line.startswith('#define OPENSSL_'): + parts = line.split() + if len(parts) >= 2: + macro_name = parts[1] + macro_value = parts[2] if len(parts) >= 3 else '1' + macros[macro_name] = macro_value + + return macros + def get_openssl_version(o): """Parse OpenSSL version from opensslv.h header file. @@ -1449,39 +1491,7 @@ def get_openssl_version(o): """ try: - # Use the C compiler to extract preprocessor macros from opensslv.h - args = ['-E', '-dM', '-include', 'openssl/opensslv.h', '-'] - if not options.shared_openssl: - args = ['-I', 'deps/openssl/openssl/include'] + args - elif options.shared_openssl_includes: - args = ['-I', options.shared_openssl_includes] + args - else: - for dir in o['include_dirs']: - args = ['-I', dir] + args - - proc = subprocess.Popen( - shlex.split(CC) + args, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) - with proc: - proc.stdin.write(b'\n') - out = to_utf8(proc.communicate()[0]) - - if proc.returncode != 0: - warn('Failed to extract OpenSSL version from opensslv.h header') - return 0 - - # Parse the macro definitions - macros = {} - for line in out.split('\n'): - if line.startswith('#define OPENSSL_VERSION_'): - parts = line.split() - if len(parts) >= 3: - macro_name = parts[1] - macro_value = parts[2] - macros[macro_name] = macro_value + macros = get_openssl_macros(o) # Extract version components major = int(macros.get('OPENSSL_VERSION_MAJOR', '0')) @@ -1511,6 +1521,13 @@ def get_openssl_version(o): warn(f'Failed to determine OpenSSL version from header: {e}') return 0 +def get_openssl_is_boringssl(o): + try: + return b('OPENSSL_IS_BORINGSSL' in get_openssl_macros(o)) + except (OSError, ValueError, subprocess.SubprocessError) as e: + warn(f'Failed to determine whether OpenSSL headers are BoringSSL: {e}') + return 'false' + def get_cargo_version(cargo): try: proc = subprocess.Popen(shlex.split(cargo) + ['--version'], @@ -2315,6 +2332,7 @@ def without_ssl_error(option): configure_library('openssl', o) o['variables']['openssl_version'] = get_openssl_version(o) + o['variables']['openssl_is_boringssl'] = get_openssl_is_boringssl(o) def configure_lief(o): if options.without_lief: diff --git a/deps/ncrypto/engine.cc b/deps/ncrypto/engine.cc index 1845cfcca47aa4..d81b68aa0075db 100644 --- a/deps/ncrypto/engine.cc +++ b/deps/ncrypto/engine.cc @@ -1,12 +1,17 @@ #include "ncrypto.h" +#if !defined(OPENSSL_NO_ENGINE) && \ + (NCRYPTO_ENGINE_COMPAT || NCRYPTO_USE_LEGACY_OPENSSL) +#include +#endif + namespace ncrypto { // ============================================================================ // Engine #ifndef OPENSSL_NO_ENGINE -EnginePointer::EnginePointer(ENGINE* engine_, bool finish_on_exit_) +EnginePointer::EnginePointer(void* engine_, bool finish_on_exit_) : engine(engine_), finish_on_exit(finish_on_exit_) {} EnginePointer::EnginePointer(EnginePointer&& other) noexcept @@ -24,21 +29,22 @@ EnginePointer& EnginePointer::operator=(EnginePointer&& other) noexcept { return *new (this) EnginePointer(std::move(other)); } -void EnginePointer::reset(ENGINE* engine_, bool finish_on_exit_) { +void EnginePointer::reset(void* engine_, bool finish_on_exit_) { if (engine != nullptr) { + ENGINE* current = static_cast(engine); if (finish_on_exit) { // This also does the equivalent of ENGINE_free. - ENGINE_finish(engine); + ENGINE_finish(current); } else { - ENGINE_free(engine); + ENGINE_free(current); } } engine = engine_; finish_on_exit = finish_on_exit_; } -ENGINE* EnginePointer::release() { - ENGINE* ret = engine; +void* EnginePointer::release() { + void* ret = engine; engine = nullptr; finish_on_exit = false; return ret; @@ -52,8 +58,9 @@ EnginePointer EnginePointer::getEngineByName(const char* name, // Engine not found, try loading dynamically. engine = EnginePointer(ENGINE_by_id("dynamic")); if (engine) { - if (!ENGINE_ctrl_cmd_string(engine.get(), "SO_PATH", name, 0) || - !ENGINE_ctrl_cmd_string(engine.get(), "LOAD", nullptr, 0)) { + ENGINE* current = static_cast(engine.engine); + if (!ENGINE_ctrl_cmd_string(current, "SO_PATH", name, 0) || + !ENGINE_ctrl_cmd_string(current, "LOAD", nullptr, 0)) { engine.reset(); } } @@ -64,19 +71,24 @@ EnginePointer EnginePointer::getEngineByName(const char* name, bool EnginePointer::setAsDefault(uint32_t flags, CryptoErrorList* errors) { if (engine == nullptr) return false; ClearErrorOnReturn clear_error_on_return(errors); - return ENGINE_set_default(engine, flags) != 0; + return ENGINE_set_default(static_cast(engine), flags) != 0; } bool EnginePointer::init(bool finish_on_exit) { if (engine == nullptr) return false; if (finish_on_exit) setFinishOnExit(); - return ENGINE_init(engine) == 1; + return ENGINE_init(static_cast(engine)) == 1; } EVPKeyPointer EnginePointer::loadPrivateKey(const char* key_name) { if (engine == nullptr) return EVPKeyPointer(); - return EVPKeyPointer( - ENGINE_load_private_key(engine, key_name, nullptr, nullptr)); + return EVPKeyPointer(ENGINE_load_private_key( + static_cast(engine), key_name, nullptr, nullptr)); +} + +bool EnginePointer::setClientCertEngine(SSL_CTX* ctx) { + if (engine == nullptr || ctx == nullptr) return false; + return SSL_CTX_set_client_cert_engine(ctx, static_cast(engine)) == 1; } void EnginePointer::initEnginesOnce() { diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index 81af3ded563777..bb6360e89a2cd0 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -14,6 +14,7 @@ #endif #include #include +#include #include #include #if OPENSSL_VERSION_MAJOR >= 3 @@ -75,9 +76,209 @@ using BignumCtxPointer = DeleteFnPtr; using BignumGenCallbackPointer = DeleteFnPtr; using NetscapeSPKIPointer = DeleteFnPtr; +const EVP_CIPHER* GetCipherCtxCipher(const EVP_CIPHER_CTX* ctx) { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + return EVP_CIPHER_CTX_get0_cipher(ctx); +#else + return EVP_CIPHER_CTX_cipher(ctx); +#endif +} + +const EVP_MD* GetDigestCtxMd(const EVP_MD_CTX* ctx) { +#if NCRYPTO_USE_OPENSSL3_PROVIDER || NCRYPTO_USE_BORINGSSL + return EVP_MD_CTX_get0_md(ctx); +#else + return EVP_MD_CTX_md(ctx); +#endif +} + +#if NCRYPTO_USE_OPENSSL3_PROVIDER +using OSSLParamBldPointer = DeleteFnPtr; +using OSSLParamPointer = DeleteFnPtr; +struct OpenSSLBufferDeleter { + void operator()(unsigned char* pointer) const { OPENSSL_free(pointer); } +}; +using OpenSSLBufferPointer = + std::unique_ptr; +#endif + static constexpr int kX509NameFlagsRFC2253WithinUtf8JSON = XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB & ~ASN1_STRFLGS_ESC_CTRL; +#if NCRYPTO_USE_OPENSSL3_PROVIDER +bool GetPKeyBnParam(const EVP_PKEY* pkey, + const char* name, + DeleteFnPtr* out) { + BIGNUM* bn = nullptr; + if (pkey == nullptr) return false; + if (EVP_PKEY_get_bn_param(pkey, name, &bn) == 1) { + out->reset(bn); + return true; + } + + size_t len = 0; + if (EVP_PKEY_get_octet_string_param(pkey, name, nullptr, 0, &len) != 1) { + return false; + } + auto data = DataPointer::Alloc(len); + if (!data || + EVP_PKEY_get_octet_string_param(pkey, + name, + static_cast(data.get()), + data.size(), + &len) != 1) { + return false; + } + bn = BN_bin2bn(static_cast(data.get()), len, nullptr); + if (bn == nullptr) return false; + out->reset(bn); + return true; +} + +bool GetOptionalPKeyBnParam(const EVP_PKEY* pkey, + const char* name, + DeleteFnPtr* out) { + BIGNUM* bn = nullptr; + if (pkey == nullptr) { + out->reset(); + return true; + } + if (EVP_PKEY_get_bn_param(pkey, name, &bn) == 1) { + out->reset(bn); + return true; + } + + size_t len = 0; + if (EVP_PKEY_get_octet_string_param(pkey, name, nullptr, 0, &len) == 1) { + auto data = DataPointer::Alloc(len); + if (!data || + EVP_PKEY_get_octet_string_param(pkey, + name, + static_cast(data.get()), + data.size(), + &len) != 1) { + return false; + } + bn = BN_bin2bn(static_cast(data.get()), len, nullptr); + if (bn == nullptr) return false; + out->reset(bn); + return true; + } + + out->reset(); + return true; +} + +EVPKeyPointer NewPKeyFromData(int id, int selection, OSSL_PARAM* params) { + auto ctx = EVPKeyCtxPointer::NewFromID(id); + if (!ctx || EVP_PKEY_fromdata_init(ctx.get()) != 1) return {}; + + EVP_PKEY* pkey = nullptr; + if (EVP_PKEY_fromdata(ctx.get(), &pkey, selection, params) != 1) { + return {}; + } + return EVPKeyPointer(pkey); +} + +EVPKeyPointer NewDhPKey(const BIGNUM* p, + const BIGNUM* g, + const BIGNUM* pub = nullptr, + const BIGNUM* priv = nullptr) { + if (p == nullptr || g == nullptr) return {}; + + OSSLParamBldPointer bld(OSSL_PARAM_BLD_new()); + if (!bld || + OSSL_PARAM_BLD_push_BN(bld.get(), OSSL_PKEY_PARAM_FFC_P, p) != 1 || + OSSL_PARAM_BLD_push_BN(bld.get(), OSSL_PKEY_PARAM_FFC_G, g) != 1) { + return {}; + } + + int selection = EVP_PKEY_KEY_PARAMETERS; + if (pub != nullptr) { + if (OSSL_PARAM_BLD_push_BN(bld.get(), OSSL_PKEY_PARAM_PUB_KEY, pub) != 1) { + return {}; + } + selection |= EVP_PKEY_PUBLIC_KEY; + } + if (priv != nullptr) { + if (OSSL_PARAM_BLD_push_BN(bld.get(), OSSL_PKEY_PARAM_PRIV_KEY, priv) != + 1) { + return {}; + } + selection |= EVP_PKEY_PRIVATE_KEY; + } + + OSSLParamPointer params(OSSL_PARAM_BLD_to_param(bld.get())); + if (!params) return {}; + return NewPKeyFromData(EVP_PKEY_DH, selection, params.get()); +} + +EVPKeyPointer NewDhPKey(const char* group_name, + const BIGNUM* pub = nullptr, + const BIGNUM* priv = nullptr) { + if (group_name == nullptr) return {}; + + if (pub == nullptr && priv == nullptr) { + EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new_from_name(nullptr, "DH", nullptr)); + OSSL_PARAM params[] = { + OSSL_PARAM_construct_utf8_string( + OSSL_PKEY_PARAM_GROUP_NAME, const_cast(group_name), 0), + OSSL_PARAM_END, + }; + if (!ctx || !ctx.initForParamgen() || + EVP_PKEY_CTX_set_params(ctx.get(), params) != 1) { + return {}; + } + return ctx.paramgen(); + } + + OSSLParamBldPointer bld(OSSL_PARAM_BLD_new()); + if (!bld || OSSL_PARAM_BLD_push_utf8_string( + bld.get(), OSSL_PKEY_PARAM_GROUP_NAME, group_name, 0) != 1) { + return {}; + } + + int selection = EVP_PKEY_KEY_PARAMETERS; + if (pub != nullptr) { + if (OSSL_PARAM_BLD_push_BN(bld.get(), OSSL_PKEY_PARAM_PUB_KEY, pub) != 1) { + return {}; + } + selection |= EVP_PKEY_PUBLIC_KEY; + } + if (priv != nullptr) { + if (OSSL_PARAM_BLD_push_BN(bld.get(), OSSL_PKEY_PARAM_PRIV_KEY, priv) != + 1) { + return {}; + } + selection |= EVP_PKEY_PRIVATE_KEY; + } + + OSSLParamPointer params(OSSL_PARAM_BLD_to_param(bld.get())); + if (!params) return {}; + return NewPKeyFromData(EVP_PKEY_DH, selection, params.get()); +} + +bool GetDhParams(const EVP_PKEY* pkey, + DeleteFnPtr* p, + DeleteFnPtr* g, + DeleteFnPtr* q = nullptr, + DeleteFnPtr* j = nullptr) { + return GetPKeyBnParam(pkey, OSSL_PKEY_PARAM_FFC_P, p) && + GetPKeyBnParam(pkey, OSSL_PKEY_PARAM_FFC_G, g) && + (q == nullptr || + GetOptionalPKeyBnParam(pkey, OSSL_PKEY_PARAM_FFC_Q, q)) && + (j == nullptr || + GetOptionalPKeyBnParam(pkey, OSSL_PKEY_PARAM_FFC_COFACTOR, j)); +} + +bool GetDhKeys(const EVP_PKEY* pkey, + DeleteFnPtr* pub, + DeleteFnPtr* priv) { + return GetOptionalPKeyBnParam(pkey, OSSL_PKEY_PARAM_PUB_KEY, pub) && + GetOptionalPKeyBnParam(pkey, OSSL_PKEY_PARAM_PRIV_KEY, priv); +} +#endif + #if NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK struct BoringSSLCipher { const EVP_CIPHER* (*get)(); @@ -500,7 +701,18 @@ int BignumPointer::isPrime(int nchecks, }, &innerCb); } +#if NCRYPTO_USE_OPENSSL3_PROVIDER + return BN_check_prime(get(), ctx.get(), cb.get()); +#elif NCRYPTO_USE_BORINGSSL + int is_probably_prime = 0; + if (BN_primality_test( + &is_probably_prime, get(), nchecks, ctx.get(), 0, cb.get()) != 1) { + return -1; + } + return is_probably_prime; +#else return BN_is_prime_ex(get(), nchecks, ctx.get(), cb.get()); +#endif } BignumPointer BignumPointer::NewPrime(const PrimeConfig& params, @@ -1389,7 +1601,11 @@ bool X509View::ifRsa(KeyCallback callback) const { OSSL3_CONST EVP_PKEY* pkey = X509_get0_pubkey(cert_); auto id = EVP_PKEY_id(pkey); if (id == EVP_PKEY_RSA || id == EVP_PKEY_RSA2 || id == EVP_PKEY_RSA_PSS) { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + Rsa rsa(pkey); +#else Rsa rsa(EVP_PKEY_get0_RSA(pkey)); +#endif if (!rsa) [[unlikely]] return true; return callback(rsa); @@ -1402,7 +1618,11 @@ bool X509View::ifEc(KeyCallback callback) const { OSSL3_CONST EVP_PKEY* pkey = X509_get0_pubkey(cert_); auto id = EVP_PKEY_id(pkey); if (id == EVP_PKEY_EC) { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + Ec ec(pkey); +#else Ec ec(EVP_PKEY_get0_EC_KEY(pkey)); +#endif if (!ec) [[unlikely]] return true; return callback(ec); @@ -1430,7 +1650,11 @@ X509Pointer X509Pointer::IssuerFrom(const SSL_CTX* ctx, const X509View& cert) { } X509Pointer X509Pointer::PeerFrom(const SSLPointer& ssl) { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + return X509Pointer(SSL_get1_peer_certificate(ssl.get())); +#else return X509Pointer(SSL_get_peer_certificate(ssl.get())); +#endif } // When adding or removing errors below, please also update the list in the API @@ -1560,11 +1784,194 @@ bool EqualNoCase(const std::string_view a, const std::string_view b) { return std::tolower(a) == std::tolower(b); }); } + +#if NCRYPTO_USE_OPENSSL3_PROVIDER +const char* GetOpenSSLDhGroupName(const std::string_view name, + DHPointer::FindGroupOption option) { + if (option != DHPointer::FindGroupOption::NO_SMALL_PRIMES && + EqualNoCase(name, "modp5")) { + return "modp_1536"; + } + if (EqualNoCase(name, "modp14")) return "modp_2048"; + if (EqualNoCase(name, "modp15")) return "modp_3072"; + if (EqualNoCase(name, "modp16")) return "modp_4096"; + if (EqualNoCase(name, "modp17")) return "modp_6144"; + if (EqualNoCase(name, "modp18")) return "modp_8192"; + return nullptr; +} + +int GetDhGroupPrivateBits(const char* group_name) { + if (group_name == nullptr) return 0; + if (strcmp(group_name, "modp_1536") == 0) return 200; + if (strcmp(group_name, "modp_2048") == 0) return 225; + if (strcmp(group_name, "modp_3072") == 0) return 275; + if (strcmp(group_name, "modp_4096") == 0) return 325; + if (strcmp(group_name, "modp_6144") == 0) return 375; + if (strcmp(group_name, "modp_8192") == 0) return 400; + return 0; +} + +bool GenerateDhPrivateKey(BignumPointer* out, + const BIGNUM* p, + const char* group_name) { + if (out == nullptr || p == nullptr) return false; + auto priv = BignumPointer::NewSecure(); + if (!priv) return false; + + const int bits = GetDhGroupPrivateBits(group_name); + if (bits > 0) { + if (BN_priv_rand(priv.get(), bits, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY) != + 1) { + return false; + } + } else { + auto range = BignumPointer(BN_dup(p)); + if (!range || BN_sub_word(range.get(), 3) != 1 || + BN_priv_rand_range(priv.get(), range.get()) != 1 || + BN_add_word(priv.get(), 2) != 1) { + return false; + } + } + + *out = std::move(priv); + return true; +} + +bool GenerateDhPublicKey(BignumPointer* out, + const BIGNUM* p, + const BIGNUM* g, + const BIGNUM* priv) { + if (out == nullptr || p == nullptr || g == nullptr || priv == nullptr) { + return false; + } + auto pub = BignumPointer::New(); + BignumCtxPointer ctx(BN_CTX_new()); + if (!pub || !ctx || + BN_mod_exp_mont_consttime(pub.get(), g, priv, p, ctx.get(), nullptr) != + 1) { + return false; + } + + *out = std::move(pub); + return true; +} + +std::optional CheckDhParams(const BIGNUM* p, + const BIGNUM* g, + const BIGNUM* q, + const BIGNUM* j) { + if (p == nullptr || g == nullptr) return std::nullopt; + + const int p_bits = BN_num_bits(p); + if (p_bits > OPENSSL_DH_CHECK_MAX_MODULUS_BITS) return std::nullopt; + + int codes = 0; + if (!BN_is_odd(p)) { + codes |= static_cast(DHPointer::CheckResult::P_NOT_PRIME); + } + if (BN_is_negative(g) || BN_is_zero(g) || BN_is_one(g)) { + codes |= static_cast(DHPointer::CheckResult::NOT_SUITABLE_GENERATOR); + } + if (p_bits < 512) { + codes |= static_cast(DHPointer::CheckResult::MODULUS_TOO_SMALL); + } + if (p_bits > OPENSSL_DH_MAX_MODULUS_BITS) { + codes |= static_cast(DHPointer::CheckResult::MODULUS_TOO_LARGE); + } + + BignumCtxPointer ctx(BN_CTX_new()); + if (!ctx) return std::nullopt; + + auto tmp1 = BignumPointer::New(); + auto tmp2 = BignumPointer::New(); + if (!tmp1 || !tmp2) return std::nullopt; + + if (BN_copy(tmp1.get(), p) == nullptr || BN_sub_word(tmp1.get(), 1) != 1) { + return std::nullopt; + } + if (BN_cmp(g, tmp1.get()) >= 0) { + codes |= static_cast(DHPointer::CheckResult::NOT_SUITABLE_GENERATOR); + } + + bool q_good = false; + if (q != nullptr) { + if (BN_ucmp(p, q) > 0) { + q_good = true; + } else { + codes |= static_cast(DHPointer::CheckResult::INVALID_Q); + } + } + + if (q_good) { + if (BN_cmp(g, BN_value_one()) <= 0 || BN_cmp(g, p) >= 0) { + codes |= static_cast(DHPointer::CheckResult::NOT_SUITABLE_GENERATOR); + } else if (BN_mod_exp(tmp1.get(), g, q, p, ctx.get()) != 1) { + return std::nullopt; + } else if (!BN_is_one(tmp1.get())) { + codes |= static_cast(DHPointer::CheckResult::NOT_SUITABLE_GENERATOR); + } + + const int q_is_prime = BN_check_prime(q, ctx.get(), nullptr); + if (q_is_prime < 0) return std::nullopt; + if (q_is_prime == 0) { + codes |= static_cast(DHPointer::CheckResult::Q_NOT_PRIME); + } + + if (BN_div(tmp1.get(), tmp2.get(), p, q, ctx.get()) != 1) { + return std::nullopt; + } + if (!BN_is_one(tmp2.get())) { + codes |= static_cast(DHPointer::CheckResult::INVALID_Q); + } + if (j != nullptr && BN_cmp(j, tmp1.get()) != 0) { + codes |= static_cast(DHPointer::CheckResult::INVALID_J); + } + } + + const int p_is_prime = BN_check_prime(p, ctx.get(), nullptr); + if (p_is_prime < 0) return std::nullopt; + if (p_is_prime == 0) { + codes |= static_cast(DHPointer::CheckResult::P_NOT_PRIME); + } else if (q == nullptr) { + if (BN_rshift1(tmp1.get(), p) != 1) return std::nullopt; + const int q_is_prime = BN_check_prime(tmp1.get(), ctx.get(), nullptr); + if (q_is_prime < 0) return std::nullopt; + if (q_is_prime == 0) { + codes |= static_cast(DHPointer::CheckResult::P_NOT_SAFE_PRIME); + } + } + + return codes; +} +#endif } // namespace +#if NCRYPTO_USE_OPENSSL3_PROVIDER +DHPointer::DHPointer(EVPKeyPointer&& key, const char* group_name) + : dh_(key.release()), group_name_(group_name) {} + +DHPointer::DHPointer(BignumPointer&& p, + BignumPointer&& g, + const char* group_name) + : p_(std::move(p)), g_(std::move(g)), group_name_(group_name) {} +#else DHPointer::DHPointer(DH* dh) : dh_(dh) {} +#endif -DHPointer::DHPointer(DHPointer&& other) noexcept : dh_(other.release()) {} +DHPointer::DHPointer(DHPointer&& other) noexcept +#if NCRYPTO_USE_OPENSSL3_PROVIDER + : dh_(other.dh_.release()), + p_(std::move(other.p_)), + g_(std::move(other.g_)), + pub_key_(std::move(other.pub_key_)), + pvt_key_(std::move(other.pvt_key_)), + group_name_(other.group_name_) { + other.group_name_ = nullptr; +} +#else + : dh_(other.release()) { +} +#endif DHPointer& DHPointer::operator=(DHPointer&& other) noexcept { if (this == &other) return *this; @@ -1576,13 +1983,45 @@ DHPointer::~DHPointer() { reset(); } -void DHPointer::reset(DH* dh) { +void DHPointer::reset( +#if NCRYPTO_USE_OPENSSL3_PROVIDER + EVP_PKEY* dh +#else + DH* dh +#endif +) { dh_.reset(dh); +#if NCRYPTO_USE_OPENSSL3_PROVIDER + p_.reset(); + g_.reset(); + pub_key_.reset(); + pvt_key_.reset(); + group_name_ = nullptr; +#endif } +#if NCRYPTO_USE_OPENSSL3_PROVIDER +EVP_PKEY* DHPointer::release() { + if (!dh_ && p_ && g_) { + auto pkey = + group_name_ != nullptr + ? NewDhPKey(group_name_, pub_key_.get(), pvt_key_.get()) + : NewDhPKey(p_.get(), g_.get(), pub_key_.get(), pvt_key_.get()); + if (!pkey) return nullptr; + dh_.reset(pkey.release()); + } + p_.reset(); + g_.reset(); + pub_key_.reset(); + pvt_key_.reset(); + group_name_ = nullptr; + return dh_.release(); +} +#else DH* DHPointer::release() { return dh_.release(); } +#endif BignumPointer DHPointer::FindGroup(const std::string_view name, FindGroupOption option) { @@ -1608,7 +2047,7 @@ BignumPointer DHPointer::FindGroup(const std::string_view name, BignumPointer DHPointer::GetStandardGenerator() { auto bn = BignumPointer::New(); if (!bn) return {}; - if (!bn.setWord(DH_GENERATOR_2)) return {}; + if (!bn.setWord(2)) return {}; return bn; } @@ -1620,12 +2059,22 @@ DHPointer DHPointer::FromGroup(const std::string_view name, auto generator = GetStandardGenerator(); if (!generator) return {}; // Unable to create the generator. +#if NCRYPTO_USE_OPENSSL3_PROVIDER + const char* group_name = GetOpenSSLDhGroupName(name, option); + return DHPointer(std::move(group), std::move(generator), group_name); +#else return New(std::move(group), std::move(generator)); +#endif } DHPointer DHPointer::New(BignumPointer&& p, BignumPointer&& g) { if (!p || !g) return {}; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + auto pkey = NewDhPKey(p.get(), g.get()); + if (!pkey) return {}; + return DHPointer(std::move(pkey)); +#else DHPointer dh(DH_new()); if (!dh) return {}; @@ -1640,9 +2089,21 @@ DHPointer DHPointer::New(BignumPointer&& p, BignumPointer&& g) { g.release(); return dh; +#endif } DHPointer DHPointer::New(size_t bits, unsigned int generator) { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + auto param_ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_DH); + if (!param_ctx.initForParamgen() || + !param_ctx.setDhParameters(bits, generator)) { + return {}; + } + + auto key_params = param_ctx.paramgen(); + if (!key_params) return {}; + return DHPointer(std::move(key_params)); +#else DHPointer dh(DH_new()); if (!dh) return {}; @@ -1651,23 +2112,101 @@ DHPointer DHPointer::New(size_t bits, unsigned int generator) { } return dh; +#endif } DHPointer::CheckResult DHPointer::check() { ClearErrorOnReturn clearErrorOnReturn; - if (!dh_) return DHPointer::CheckResult::NONE; + if (!*this) return DHPointer::CheckResult::NONE; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (group_name_ != nullptr) return CheckResult::NONE; + + DeleteFnPtr p; + DeleteFnPtr g; + DeleteFnPtr q; + DeleteFnPtr j; + const BIGNUM* p_bn = p_.get(); + const BIGNUM* g_bn = g_.get(); + const BIGNUM* q_bn = nullptr; + const BIGNUM* j_bn = nullptr; + if ((p_bn == nullptr || g_bn == nullptr) && + !GetDhParams(dh_.get(), &p, &g, &q, &j)) { + return DHPointer::CheckResult::CHECK_FAILED; + } + if (p_bn == nullptr) p_bn = p.get(); + if (g_bn == nullptr) g_bn = g.get(); + q_bn = q.get(); + j_bn = j.get(); + if (p_bn == nullptr || g_bn == nullptr) { + return DHPointer::CheckResult::CHECK_FAILED; + } + + auto codes = CheckDhParams(p_bn, g_bn, q_bn, j_bn); + if (!codes) return DHPointer::CheckResult::CHECK_FAILED; + return static_cast(*codes); +#else int codes = 0; if (DH_check(dh_.get(), &codes) != 1) return DHPointer::CheckResult::CHECK_FAILED; return static_cast(codes); +#endif } DHPointer::CheckPublicKeyResult DHPointer::checkPublicKey( const BignumPointer& pub_key) { ClearErrorOnReturn clearErrorOnReturn; - if (!pub_key || !dh_) { + if (!pub_key || !*this) { + return DHPointer::CheckPublicKeyResult::CHECK_FAILED; + } +#if NCRYPTO_USE_OPENSSL3_PROVIDER + DeleteFnPtr p; + DeleteFnPtr g; + const BIGNUM* p_bn = p_.get(); + const BIGNUM* g_bn = g_.get(); + if ((p_bn == nullptr || g_bn == nullptr) && !GetDhParams(dh_.get(), &p, &g)) { return DHPointer::CheckPublicKeyResult::CHECK_FAILED; } + if (p_bn == nullptr) p_bn = p.get(); + if (g_bn == nullptr) g_bn = g.get(); + if (p_bn == nullptr || g_bn == nullptr) { + return DHPointer::CheckPublicKeyResult::CHECK_FAILED; + } + + if (BN_cmp(pub_key.get(), BN_value_one()) <= 0) { + return DHPointer::CheckPublicKeyResult::TOO_SMALL; + } + + DeleteFnPtr p_minus_one(BN_dup(p_bn)); + if (!p_minus_one || BN_sub_word(p_minus_one.get(), 1) != 1) { + return DHPointer::CheckPublicKeyResult::CHECK_FAILED; + } + + if (BN_cmp(pub_key.get(), p_minus_one.get()) >= 0) { + return DHPointer::CheckPublicKeyResult::TOO_LARGE; + } + + if (p_) { + if (group_name_ == nullptr) return CheckPublicKeyResult::NONE; + + auto peer = NewDhPKey(group_name_, pub_key.get()); + if (!peer) return DHPointer::CheckPublicKeyResult::CHECK_FAILED; + EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(peer.get(), nullptr)); + if (!ctx) return DHPointer::CheckPublicKeyResult::CHECK_FAILED; + if (EVP_PKEY_public_check(ctx.get()) != 1) { + return DHPointer::CheckPublicKeyResult::INVALID; + } + return CheckPublicKeyResult::NONE; + } + + auto peer = NewDhPKey(p_bn, g_bn, pub_key.get()); + if (!peer) return DHPointer::CheckPublicKeyResult::CHECK_FAILED; + EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(peer.get(), nullptr)); + if (!ctx) return DHPointer::CheckPublicKeyResult::CHECK_FAILED; + if (EVP_PKEY_public_check(ctx.get()) != 1) { + return DHPointer::CheckPublicKeyResult::INVALID; + } + return CheckPublicKeyResult::NONE; +#else int codes = 0; if (DH_check_pub_key(dh_.get(), pub_key.get(), &codes) != 1) { return DHPointer::CheckPublicKeyResult::CHECK_FAILED; @@ -1684,65 +2223,242 @@ DHPointer::CheckPublicKeyResult DHPointer::checkPublicKey( return DHPointer::CheckPublicKeyResult::INVALID; } return CheckPublicKeyResult::NONE; +#endif } DataPointer DHPointer::getPrime() const { - if (!dh_) return {}; + if (!*this) return {}; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (p_) return p_.encode(); + + DeleteFnPtr p; + DeleteFnPtr g; + if (!GetDhParams(dh_.get(), &p, &g)) return {}; + return BignumPointer::Encode(p.get()); +#else const BIGNUM* p; DH_get0_pqg(dh_.get(), &p, nullptr, nullptr); return BignumPointer::Encode(p); +#endif +} + +size_t DHPointer::getPrimeBits() const { + if (!*this) return 0; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (p_) return BignumPointer::GetBitCount(p_.get()); + + DeleteFnPtr p; + DeleteFnPtr g; + if (!GetDhParams(dh_.get(), &p, &g)) return 0; + return BignumPointer::GetBitCount(p.get()); +#else + const BIGNUM* p; + DH_get0_pqg(dh_.get(), &p, nullptr, nullptr); + return BignumPointer::GetBitCount(p); +#endif } DataPointer DHPointer::getGenerator() const { - if (!dh_) return {}; + if (!*this) return {}; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (g_) return g_.encode(); + + DeleteFnPtr p; + DeleteFnPtr g; + if (!GetDhParams(dh_.get(), &p, &g)) return {}; + return BignumPointer::Encode(g.get()); +#else const BIGNUM* g; DH_get0_pqg(dh_.get(), nullptr, nullptr, &g); return BignumPointer::Encode(g); +#endif } DataPointer DHPointer::getPublicKey() const { + if (!*this) return {}; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (pub_key_) return pub_key_.encode(); if (!dh_) return {}; + + DeleteFnPtr pub_key; + DeleteFnPtr pvt_key; + if (!GetDhKeys(dh_.get(), &pub_key, &pvt_key)) return {}; + return BignumPointer::Encode(pub_key.get()); +#else const BIGNUM* pub_key; DH_get0_key(dh_.get(), &pub_key, nullptr); return BignumPointer::Encode(pub_key); +#endif } DataPointer DHPointer::getPrivateKey() const { + if (!*this) return {}; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (pvt_key_) return pvt_key_.encode(); if (!dh_) return {}; + + DeleteFnPtr pub_key; + DeleteFnPtr pvt_key; + if (!GetDhKeys(dh_.get(), &pub_key, &pvt_key)) return {}; + return BignumPointer::Encode(pvt_key.get()); +#else const BIGNUM* pvt_key; DH_get0_key(dh_.get(), nullptr, &pvt_key); return BignumPointer::Encode(pvt_key); +#endif } bool DHPointer::hasPrivateKey() const { + if (!*this) return false; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (pvt_key_) return true; if (!dh_) return false; + + DeleteFnPtr pub_key; + DeleteFnPtr pvt_key; + if (!GetDhKeys(dh_.get(), &pub_key, &pvt_key)) return false; + return pvt_key != nullptr; +#else const BIGNUM* pvt_key = nullptr; DH_get0_key(dh_.get(), nullptr, &pvt_key); return pvt_key != nullptr; +#endif } -DataPointer DHPointer::generateKeys() const { +DataPointer DHPointer::generateKeys() { ClearErrorOnReturn clearErrorOnReturn; - if (!dh_) return {}; + if (!*this) return {}; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (p_ && g_) { + if (!pvt_key_ && !GenerateDhPrivateKey(&pvt_key_, p_.get(), group_name_)) { + return {}; + } + + BignumPointer generated_pub_key; + if (!GenerateDhPublicKey( + &generated_pub_key, p_.get(), g_.get(), pvt_key_.get())) { + return {}; + } + + if (pub_key_ && BN_cmp(pub_key_.get(), generated_pub_key.get()) == 0) { + return getPublicKey(); + } + + pub_key_ = std::move(generated_pub_key); + return getPublicKey(); + } + + DeleteFnPtr p; + DeleteFnPtr g; + DeleteFnPtr pub_key; + DeleteFnPtr pvt_key; + if (!GetDhParams(dh_.get(), &p, &g) || + !GetDhKeys(dh_.get(), &pub_key, &pvt_key)) { + return {}; + } + + if (pvt_key != nullptr) { + BignumPointer generated_pub_key; + if (!GenerateDhPublicKey( + &generated_pub_key, p.get(), g.get(), pvt_key.get())) { + return {}; + } + + if (pub_key != nullptr && + BN_cmp(pub_key.get(), generated_pub_key.get()) == 0) { + return getPublicKey(); + } + + auto replacement = + group_name_ != nullptr + ? NewDhPKey(group_name_, generated_pub_key.get(), pvt_key.get()) + : NewDhPKey( + p.get(), g.get(), generated_pub_key.get(), pvt_key.get()); + if (!replacement) return {}; + dh_.reset(replacement.release()); + return getPublicKey(); + } + + EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(dh_.get(), nullptr)); + if (!ctx || !ctx.initForKeygen()) return {}; + EVP_PKEY* generated = nullptr; + if (EVP_PKEY_keygen(ctx.get(), &generated) != 1) return {}; + dh_.reset(generated); + return getPublicKey(); +#else // Key generation failed if (!DH_generate_key(dh_.get())) return {}; return getPublicKey(); +#endif } size_t DHPointer::size() const { - if (!dh_) return 0; + if (!*this) return 0; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (p_) return BignumPointer::GetByteCount(p_.get()); + + const int bits = EVP_PKEY_get_bits(dh_.get()); + return bits > 0 ? (static_cast(bits) + 7) / 8 : 0; +#else int ret = DH_size(dh_.get()); // DH_size can return a -1 on error but we just want to return a 0 // in that case so we don't wrap around when returning the size_t. return ret >= 0 ? static_cast(ret) : 0; +#endif } DataPointer DHPointer::computeSecret(const BignumPointer& peer) const { ClearErrorOnReturn clearErrorOnReturn; - if (!dh_ || !peer) return {}; + if (!*this || !peer) return {}; + +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (p_ && pvt_key_) { + auto secret = BignumPointer::NewSecure(); + BignumCtxPointer ctx(BN_CTX_new()); + if (!secret || !ctx || + BN_mod_exp_mont_consttime(secret.get(), + peer.get(), + pvt_key_.get(), + p_.get(), + ctx.get(), + nullptr) != 1) { + return {}; + } + return secret.encodePadded(size()); + } + + EVPKeyPointer peer_key; + if (group_name_ != nullptr) { + peer_key = NewDhPKey(group_name_, peer.get()); + } else { + DeleteFnPtr p; + DeleteFnPtr g; + if (!GetDhParams(dh_.get(), &p, &g)) return {}; + peer_key = NewDhPKey(p.get(), g.get(), peer.get()); + } + if (!peer_key) return {}; + + EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(dh_.get(), nullptr)); + size_t out_size = size(); + if (!ctx || EVP_PKEY_derive_init(ctx.get()) != 1 || + EVP_PKEY_CTX_set_dh_pad(ctx.get(), 1) != 1 || + EVP_PKEY_derive_set_peer(ctx.get(), peer_key.get()) != 1 || + EVP_PKEY_derive(ctx.get(), nullptr, &out_size) != 1) { + return {}; + } + if (out_size == 0) return {}; + + auto dp = DataPointer::Alloc(out_size); + if (!dp) return {}; + if (EVP_PKEY_derive( + ctx.get(), static_cast(dp.get()), &out_size) != 1) { + return {}; + } + return dp.resize(out_size); +#else auto dp = DataPointer::Alloc(size()); if (!dp) return {}; @@ -1760,10 +2476,35 @@ DataPointer DHPointer::computeSecret(const BignumPointer& peer) const { } return dp; +#endif } bool DHPointer::setPublicKey(BignumPointer&& key) { - if (!dh_) return false; + if (!*this) return false; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (p_ && g_) { + pub_key_ = std::move(key); + return true; + } + + DeleteFnPtr pub_key; + DeleteFnPtr pvt_key; + if (!GetDhKeys(dh_.get(), &pub_key, &pvt_key)) { + return false; + } + EVPKeyPointer pkey; + if (group_name_ != nullptr) { + pkey = NewDhPKey(group_name_, key.get(), pvt_key.get()); + } else { + DeleteFnPtr p; + DeleteFnPtr g; + if (!GetDhParams(dh_.get(), &p, &g)) return false; + pkey = NewDhPKey(p.get(), g.get(), key.get(), pvt_key.get()); + } + if (!pkey) return false; + dh_.reset(pkey.release()); + return true; +#else if (DH_set0_key(dh_.get(), key.get(), nullptr) == 1) { // If DH_set0_key returns successfully, then dh_ takes ownership of the // BIGNUM, so we must release it here. Unfortunately coverity does not @@ -1773,10 +2514,35 @@ bool DHPointer::setPublicKey(BignumPointer&& key) { return true; } return false; +#endif } bool DHPointer::setPrivateKey(BignumPointer&& key) { - if (!dh_) return false; + if (!*this) return false; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (p_ && g_) { + pvt_key_ = std::move(key); + return true; + } + + DeleteFnPtr pub_key; + DeleteFnPtr pvt_key; + if (!GetDhKeys(dh_.get(), &pub_key, &pvt_key)) { + return false; + } + EVPKeyPointer pkey; + if (group_name_ != nullptr) { + pkey = NewDhPKey(group_name_, pub_key.get(), key.get()); + } else { + DeleteFnPtr p; + DeleteFnPtr g; + if (!GetDhParams(dh_.get(), &p, &g)) return false; + pkey = NewDhPKey(p.get(), g.get(), pub_key.get(), key.get()); + } + if (!pkey) return false; + dh_.reset(pkey.release()); + return true; +#else if (DH_set0_key(dh_.get(), nullptr, key.get()) == 1) { // If DH_set0_key returns successfully, then dh_ takes ownership of the // BIGNUM, so we must release it here. Unfortunately coverity does not @@ -1786,6 +2552,7 @@ bool DHPointer::setPrivateKey(BignumPointer&& key) { return true; } return false; +#endif } DataPointer DHPointer::stateless(const EVPKeyPointer& ourKey, @@ -1794,8 +2561,16 @@ DataPointer DHPointer::stateless(const EVPKeyPointer& ourKey, if (!ourKey || !theirKey) return {}; auto ctx = EVPKeyCtxPointer::New(ourKey); - if (!ctx || EVP_PKEY_derive_init(ctx.get()) <= 0 || - EVP_PKEY_derive_set_peer(ctx.get(), theirKey.get()) <= 0 || + if (!ctx || EVP_PKEY_derive_init(ctx.get()) <= 0) { + return {}; + } +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (ourKey.id() == EVP_PKEY_DH && + EVP_PKEY_CTX_set_dh_pad(ctx.get(), 1) <= 0) { + return {}; + } +#endif + if (EVP_PKEY_derive_set_peer(ctx.get(), theirKey.get()) <= 0 || EVP_PKEY_derive(ctx.get(), nullptr, &out_size) <= 0) { return {}; } @@ -2209,14 +2984,60 @@ EVPKeyPointer EVPKeyPointer::NewRawSeed( EVPKeyPointer EVPKeyPointer::NewDH(DHPointer&& dh) { if (!dh) return {}; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + return EVPKeyPointer(dh.release()); +#else auto key = New(); if (!key) return {}; if (EVP_PKEY_assign_DH(key.get(), dh.get())) { dh.release(); } return key; +#endif } +#if NCRYPTO_USE_OPENSSL3_PROVIDER +EVPKeyPointer EVPKeyPointer::NewRSA(const Rsa& rsa) { + const auto public_key = rsa.getPublicKey(); + if (public_key.n == nullptr || public_key.e == nullptr) return {}; + + OSSLParamBldPointer bld(OSSL_PARAM_BLD_new()); + if (!bld || + OSSL_PARAM_BLD_push_BN(bld.get(), OSSL_PKEY_PARAM_RSA_N, public_key.n) != + 1 || + OSSL_PARAM_BLD_push_BN(bld.get(), OSSL_PKEY_PARAM_RSA_E, public_key.e) != + 1) { + return {}; + } + + int selection = EVP_PKEY_PUBLIC_KEY; + if (public_key.d != nullptr) { + const auto private_key = rsa.getPrivateKey(); + if (private_key.p == nullptr || private_key.q == nullptr || + private_key.dp == nullptr || private_key.dq == nullptr || + private_key.qi == nullptr || + OSSL_PARAM_BLD_push_BN( + bld.get(), OSSL_PKEY_PARAM_RSA_D, public_key.d) != 1 || + OSSL_PARAM_BLD_push_BN( + bld.get(), OSSL_PKEY_PARAM_RSA_FACTOR1, private_key.p) != 1 || + OSSL_PARAM_BLD_push_BN( + bld.get(), OSSL_PKEY_PARAM_RSA_FACTOR2, private_key.q) != 1 || + OSSL_PARAM_BLD_push_BN( + bld.get(), OSSL_PKEY_PARAM_RSA_EXPONENT1, private_key.dp) != 1 || + OSSL_PARAM_BLD_push_BN( + bld.get(), OSSL_PKEY_PARAM_RSA_EXPONENT2, private_key.dq) != 1 || + OSSL_PARAM_BLD_push_BN( + bld.get(), OSSL_PKEY_PARAM_RSA_COEFFICIENT1, private_key.qi) != 1) { + return {}; + } + selection = EVP_PKEY_KEYPAIR; + } + + OSSLParamPointer params(OSSL_PARAM_BLD_to_param(bld.get())); + if (!params) return {}; + return NewPKeyFromData(EVP_PKEY_RSA, selection, params.get()); +} +#else EVPKeyPointer EVPKeyPointer::NewRSA(RSAPointer&& rsa) { if (!rsa) return {}; auto key = New(); @@ -2226,6 +3047,7 @@ EVPKeyPointer EVPKeyPointer::NewRSA(RSAPointer&& rsa) { } return key; } +#endif // NCRYPTO_USE_OPENSSL3_PROVIDER EVPKeyPointer::EVPKeyPointer(EVP_PKEY* pkey) : pkey_(pkey) {} @@ -2363,18 +3185,83 @@ BIOPointer EVPKeyPointer::derPublicKey() const { bool EVPKeyPointer::assign(const ECKeyPointer& eckey) { if (!pkey_ || !eckey) return {}; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + return set(eckey); +#else return EVP_PKEY_assign_EC_KEY(pkey_.get(), eckey.get()); +#endif } bool EVPKeyPointer::set(const ECKeyPointer& eckey) { if (!pkey_ || !eckey) return false; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + const int nid = EC_GROUP_get_curve_name(eckey.group_.get()); + const char* group_name = OBJ_nid2sn(nid); + if (group_name == nullptr) return false; + + OSSLParamBldPointer bld(OSSL_PARAM_BLD_new()); + if (!bld || OSSL_PARAM_BLD_push_utf8_string( + bld.get(), OSSL_PKEY_PARAM_GROUP_NAME, group_name, 0) != 1) { + return false; + } + + int selection = EVP_PKEY_KEY_PARAMETERS; + OpenSSLBufferPointer encoded_public_key; + ECPointPointer generated_public_key; + const EC_POINT* public_key = eckey.pub_.get(); + if (public_key == nullptr && eckey.priv_ != nullptr) { + generated_public_key = ECPointPointer::New(eckey.group_.get()); + if (!generated_public_key || + !generated_public_key.mul(eckey.group_.get(), eckey.priv_.get())) { + return false; + } + public_key = generated_public_key.get(); + } + + if (public_key != nullptr) { + unsigned char* encoded_public_key_raw = nullptr; + const size_t encoded_public_key_len = + EC_POINT_point2buf(eckey.group_.get(), + public_key, + POINT_CONVERSION_UNCOMPRESSED, + &encoded_public_key_raw, + nullptr); + if (encoded_public_key_len == 0) return false; + encoded_public_key.reset(encoded_public_key_raw); + if (OSSL_PARAM_BLD_push_octet_string(bld.get(), + OSSL_PKEY_PARAM_PUB_KEY, + encoded_public_key.get(), + encoded_public_key_len) != 1) { + return false; + } + selection |= EVP_PKEY_PUBLIC_KEY; + } + + if (eckey.priv_ != nullptr) { + if (OSSL_PARAM_BLD_push_BN( + bld.get(), OSSL_PKEY_PARAM_PRIV_KEY, eckey.priv_.get()) != 1) { + return false; + } + selection |= EVP_PKEY_PRIVATE_KEY; + } + + OSSLParamPointer params(OSSL_PARAM_BLD_to_param(bld.get())); + if (!params) return false; + auto pkey = NewPKeyFromData(EVP_PKEY_EC, selection, params.get()); + if (!pkey) return false; + reset(pkey.release()); + return true; +#else return EVP_PKEY_set1_EC_KEY(pkey_.get(), eckey); +#endif } +#if NCRYPTO_USE_LEGACY_KEY_TYPES EVPKeyPointer::operator const EC_KEY*() const { if (!pkey_) return nullptr; return EVP_PKEY_get0_EC_KEY(pkey_.get()); } +#endif // NCRYPTO_USE_LEGACY_KEY_TYPES namespace { @@ -2429,6 +3316,34 @@ constexpr bool IsASN1Sequence(const unsigned char* data, return true; } +constexpr bool ReadASN1Element(const unsigned char* data, + size_t size, + unsigned char tag, + size_t* header_size, + size_t* content_size, + size_t* total_size) { + if (size < 2 || data[0] != tag) return false; + + size_t offset; + size_t length; + if (data[1] & 0x80) { + size_t n_bytes = data[1] & ~0x80; + if (n_bytes + 2 > size || n_bytes > sizeof(size_t)) return false; + length = 0; + for (size_t i = 0; i < n_bytes; i++) length = (length << 8) | data[i + 2]; + offset = 2 + n_bytes; + } else { + offset = 2; + length = data[1]; + } + + if (offset > size || length > size - offset) return false; + *header_size = offset; + *content_size = length; + *total_size = offset + length; + return true; +} + constexpr bool IsEncryptedPrivateKeyInfo( const Buffer& buffer) { // Both PrivateKeyInfo and EncryptedPrivateKeyInfo start with a SEQUENCE. @@ -2546,6 +3461,103 @@ Buffer GetPassphrase( } return pass; } + +#if NCRYPTO_USE_OPENSSL3_PROVIDER +using OSSLEncoderCtxPointer = + DeleteFnPtr; + +bool WriteEncodedPKey(BIO* bio, + const EVP_PKEY* pkey, + int selection, + EVPKeyPointer::PKFormatType format, + const char* structure, + const EVP_CIPHER* cipher = nullptr, + Buffer passphrase = {}) { + const char* output_type = + format == EVPKeyPointer::PKFormatType::PEM ? "PEM" : "DER"; + OSSLEncoderCtxPointer ctx(OSSL_ENCODER_CTX_new_for_pkey( + pkey, selection, output_type, structure, nullptr)); + if (!ctx || OSSL_ENCODER_CTX_get_num_encoders(ctx.get()) == 0) { + return false; + } + + if (cipher != nullptr) { + if (OSSL_ENCODER_CTX_set_cipher( + ctx.get(), EVP_CIPHER_get0_name(cipher), nullptr) != 1 || + OSSL_ENCODER_CTX_set_passphrase( + ctx.get(), + reinterpret_cast(passphrase.data), + passphrase.len) != 1) { + return false; + } + } + + return OSSL_ENCODER_to_bio(ctx.get(), bio) == 1; +} + +struct DERView { + const unsigned char* data = nullptr; + size_t len = 0; +}; + +int WriteDERView(const void* x, unsigned char** out) { + const auto* der = static_cast(x); + if (der == nullptr || der->data == nullptr || + der->len > static_cast(INT_MAX)) { + return -1; + } + if (out != nullptr) { + memcpy(*out, der->data, der->len); + *out += der->len; + } + return static_cast(der->len); +} + +bool WriteEncryptedTraditionalPEM(BIO* bio, + const EVP_PKEY* pkey, + const EVP_CIPHER* cipher, + Buffer passphrase) { + if (passphrase.len > static_cast(INT_MAX)) return false; + + unsigned char* der = nullptr; + size_t der_len = 0; + OSSLEncoderCtxPointer ctx(OSSL_ENCODER_CTX_new_for_pkey( + pkey, OSSL_KEYMGMT_SELECT_KEYPAIR, "DER", "pkcs1", nullptr)); + if (!ctx || OSSL_ENCODER_to_data(ctx.get(), &der, &der_len) != 1) { + return false; + } + + OpenSSLBufferPointer der_storage(der); + DERView der_view{der_storage.get(), der_len}; + return PEM_ASN1_write_bio( + WriteDERView, + PEM_STRING_RSA, + bio, + &der_view, + cipher, + reinterpret_cast(passphrase.data), + static_cast(passphrase.len), + nullptr, + nullptr) == 1; +} + +bool ECKeyHasMissingOid(const EVPKeyPointer& key) { + if (key.id() != EVP_PKEY_EC) return false; + + const Ec ec(key.get()); + const EC_GROUP* group = ec.getGroup(); + if (group == nullptr || + EC_GROUP_get_asn1_flag(group) != OPENSSL_EC_NAMED_CURVE) { + return false; + } + + const int nid = EC_GROUP_get_curve_name(group); + if (nid == NID_undef) return true; + + const ASN1_OBJECT* asn1 = OBJ_nid2obj(nid); + return asn1 == nullptr || OBJ_length(asn1) == 0; +} +#endif } // namespace EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey( @@ -2633,6 +3645,22 @@ Result EVPKeyPointer::writePrivateKey( // PKCS1 is only permitted for RSA keys. if (id() != EVP_PKEY_RSA) return Result(false); +#if NCRYPTO_USE_OPENSSL3_PROVIDER + const EVP_CIPHER* cipher = + config.format == PKFormatType::PEM ? config.cipher : nullptr; + if (cipher != nullptr && passphrase.len == 0) { + err = + !WriteEncryptedTraditionalPEM(bio.get(), get(), cipher, passphrase); + } else { + err = !WriteEncodedPKey(bio.get(), + get(), + OSSL_KEYMGMT_SELECT_ALL, + config.format, + "pkcs1", + cipher, + passphrase); + } +#else #if OPENSSL_VERSION_MAJOR >= 3 const RSA* rsa = EVP_PKEY_get0_RSA(get()); #else @@ -2660,6 +3688,7 @@ Result EVPKeyPointer::writePrivateKey( return Result(false); } } +#endif break; } case PKEncodingType::PKCS8: { @@ -2696,6 +3725,17 @@ Result EVPKeyPointer::writePrivateKey( // SEC1 is only permitted for EC keys if (id() != EVP_PKEY_EC) return Result(false); +#if NCRYPTO_USE_OPENSSL3_PROVIDER + const EVP_CIPHER* cipher = + config.format == PKFormatType::PEM ? config.cipher : nullptr; + err = !WriteEncodedPKey(bio.get(), + get(), + OSSL_KEYMGMT_SELECT_ALL, + config.format, + "type-specific", + cipher, + passphrase); +#else #if OPENSSL_VERSION_MAJOR >= 3 const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(get()); #else @@ -2723,6 +3763,7 @@ Result EVPKeyPointer::writePrivateKey( return Result(false); } } +#endif break; } default: { @@ -2749,6 +3790,18 @@ Result EVPKeyPointer::writePublicKey( if (config.type == ncrypto::EVPKeyPointer::PKEncodingType::PKCS1) { // PKCS#1 is only valid for RSA keys. +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (id() != EVP_PKEY_RSA) return Result(false); + if (!WriteEncodedPKey(bio.get(), + get(), + OSSL_KEYMGMT_SELECT_PUBLIC_KEY, + config.format, + "pkcs1")) { + return Result(false, + mark_pop_error_on_return.peekError()); + } + return bio; +#else #if OPENSSL_VERSION_MAJOR >= 3 const RSA* rsa = EVP_PKEY_get0_RSA(get()); #else @@ -2769,7 +3822,16 @@ Result EVPKeyPointer::writePublicKey( mark_pop_error_on_return.peekError()); } return bio; +#endif + } + +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (ECKeyHasMissingOid(*this)) { + ERR_raise(ERR_LIB_EC, EC_R_MISSING_OID); + return Result(false, + mark_pop_error_on_return.peekError()); } +#endif if (config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM) { // Encode SPKI as PEM. @@ -2841,11 +3903,23 @@ std::optional EVPKeyPointer::getBytesOfRS() const { int bits, id = base_id(); if (id == EVP_PKEY_DSA) { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + DeleteFnPtr q; + if (!GetPKeyBnParam(get(), OSSL_PKEY_PARAM_FFC_Q, &q)) return std::nullopt; + bits = BignumPointer::GetBitCount(q.get()); +#else const DSA* dsa_key = EVP_PKEY_get0_DSA(get()); // Both r and s are computed mod q, so their width is limited by that of q. bits = BignumPointer::GetBitCount(DSA_get0_q(dsa_key)); +#endif } else if (id == EVP_PKEY_EC) { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + Ec ec(get()); + if (!ec) return std::nullopt; + bits = EC_GROUP_order_bits(ec.getGroup()); +#else bits = EC_GROUP_order_bits(ECKeyPointer::GetGroup(*this)); +#endif } else { return std::nullopt; } @@ -2857,6 +3931,9 @@ EVPKeyPointer::operator Rsa() const { int type = id(); if (type != EVP_PKEY_RSA && type != EVP_PKEY_RSA_PSS) return {}; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + return Rsa(get()); +#else // TODO(tniessen): Remove the "else" branch once we drop support for OpenSSL // versions older than 1.1.1e via FIPS / dynamic linking. OSSL3_CONST RSA* rsa; @@ -2867,15 +3944,20 @@ EVPKeyPointer::operator Rsa() const { } if (rsa == nullptr) return {}; return Rsa(rsa); +#endif } EVPKeyPointer::operator Dsa() const { int type = id(); if (type != EVP_PKEY_DSA) return {}; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + return Dsa(get()); +#else OSSL3_CONST DSA* dsa = EVP_PKEY_get0_DSA(get()); if (dsa == nullptr) return {}; return Dsa(dsa); +#endif } bool EVPKeyPointer::validateDsaParameters() const { @@ -2886,12 +3968,25 @@ bool EVPKeyPointer::validateDsaParameters() const { #else if (FIPS_mode() && EVP_PKEY_DSA == id()) { #endif +#if NCRYPTO_USE_OPENSSL3_PROVIDER + DeleteFnPtr p; + DeleteFnPtr q; + if (!GetPKeyBnParam(pkey_.get(), OSSL_PKEY_PARAM_FFC_P, &p) || + !GetPKeyBnParam(pkey_.get(), OSSL_PKEY_PARAM_FFC_Q, &q)) { + return false; + } + const BIGNUM* p_value = p.get(); + const BIGNUM* q_value = q.get(); +#else const DSA* dsa = EVP_PKEY_get0_DSA(pkey_.get()); const BIGNUM* p; const BIGNUM* q; DSA_get0_pqg(dsa, &p, &q, nullptr); - int L = BignumPointer::GetBitCount(p); - int N = BignumPointer::GetBitCount(q); + const BIGNUM* p_value = p; + const BIGNUM* q_value = q; +#endif + int L = BignumPointer::GetBitCount(p_value); + int N = BignumPointer::GetBitCount(q_value); return (L == 1024 && N == 160) || (L == 2048 && N == 224) || (L == 2048 && N == 256) || (L == 3072 && N == 256); @@ -3185,7 +4280,7 @@ const Cipher Cipher::FromNid(int nid) { } const Cipher Cipher::FromCtx(const CipherCtxPointer& ctx) { - return Cipher(EVP_CIPHER_CTX_cipher(ctx.get())); + return Cipher(GetCipherCtxCipher(ctx.get())); } const Cipher Cipher::EMPTY = Cipher(); @@ -3334,7 +4429,7 @@ int Cipher::bytesToKey(const Digest& digest, CipherCtxPointer CipherCtxPointer::New() { auto ret = CipherCtxPointer(EVP_CIPHER_CTX_new()); if (!ret) return {}; - EVP_CIPHER_CTX_init(ret.get()); + EVP_CIPHER_CTX_reset(ret.get()); return ret; } @@ -3598,8 +4693,15 @@ bool ECPointPointer::mul(const EC_GROUP* group, const BIGNUM* priv_key) { // ============================================================================ +#if NCRYPTO_USE_LEGACY_KEY_TYPES ECKeyPointer::ECKeyPointer() : key_(nullptr) {} +ECKeyPointer::ECKeyPointer(const EVPKeyPointer& key) : key_(nullptr) { + if (key.id() != EVP_PKEY_EC) return; + const EC_KEY* ec = key; + if (ec != nullptr) key_.reset(EC_KEY_dup(ec)); +} + ECKeyPointer::ECKeyPointer(EC_KEY* key) : key_(key) {} ECKeyPointer::ECKeyPointer(ECKeyPointer&& other) noexcept @@ -3720,6 +4822,20 @@ bool ECKeyPointer::checkKey() const { return Check(key_.get()); } +DataPointer ECKeyPointer::computeSecret(const ECPointPointer& peer) const { + if (!key_ || !peer) return {}; + const EC_GROUP* group = getGroup(); + const int field_size = EC_GROUP_get_degree(group); + const size_t out_len = (field_size + 7) / 8; + auto out = DataPointer::Alloc(out_len); + if (!out) return {}; + if (ECDH_compute_key( + out.get(), out.size(), peer.get(), key_.get(), nullptr) == 0) { + return {}; + } + return out; +} + ECKeyPointer ECKeyPointer::NewByCurveName(int nid) { return ECKeyPointer(EC_KEY_new_by_curve_name(nid)); } @@ -3730,6 +4846,262 @@ ECKeyPointer ECKeyPointer::New(const EC_GROUP* group) { if (!EC_KEY_set_group(ptr.get(), group)) return {}; return ptr; } +#else +ECKeyPointer::ECKeyPointer() : group_(nullptr), pub_(nullptr), priv_(nullptr) {} + +ECKeyPointer::ECKeyPointer(const EVPKeyPointer& key) : ECKeyPointer() { + if (key.id() != EVP_PKEY_EC) return; + char group_name[80]; + size_t group_name_len = 0; + if (EVP_PKEY_get_utf8_string_param(key.get(), + OSSL_PKEY_PARAM_GROUP_NAME, + group_name, + sizeof(group_name), + &group_name_len) != 1) { + return; + } + + const int nid = Ec::GetCurveIdFromName(group_name); + if (nid == NID_undef) return; + group_.reset(EC_GROUP_new_by_curve_name(nid)); + if (!group_) return; + + GetOptionalPKeyBnParam(key.get(), OSSL_PKEY_PARAM_PRIV_KEY, &priv_); + + size_t public_key_len = 0; + if (EVP_PKEY_get_octet_string_param( + key.get(), OSSL_PKEY_PARAM_PUB_KEY, nullptr, 0, &public_key_len) == + 1) { + auto public_key = DataPointer::Alloc(public_key_len); + if (!public_key || EVP_PKEY_get_octet_string_param( + key.get(), + OSSL_PKEY_PARAM_PUB_KEY, + static_cast(public_key.get()), + public_key.size(), + &public_key_len) != 1) { + reset(); + return; + } + + auto point = ECPointPointer::New(group_.get()); + if (!point || + !point.setFromBuffer( + {static_cast(public_key.get()), public_key_len}, + group_.get())) { + reset(); + return; + } + pub_.reset(point.release()); + } +} + +ECKeyPointer::ECKeyPointer(ECKeyPointer&& other) noexcept + : group_(std::move(other.group_)), + pub_(std::move(other.pub_)), + priv_(std::move(other.priv_)) {} + +ECKeyPointer& ECKeyPointer::operator=(ECKeyPointer&& other) noexcept { + group_ = std::move(other.group_); + pub_ = std::move(other.pub_); + priv_ = std::move(other.priv_); + return *this; +} + +ECKeyPointer::~ECKeyPointer() { + reset(); +} + +void ECKeyPointer::reset() { + group_.reset(); + pub_.reset(); + priv_.reset(); +} + +ECKeyPointer ECKeyPointer::clone() const { + if (!group_) return {}; + ECKeyPointer ret; + ret.group_.reset(EC_GROUP_dup(group_.get())); + if (!ret.group_) return {}; + if (pub_ != nullptr) { + ret.pub_.reset(EC_POINT_dup(pub_.get(), ret.group_.get())); + if (!ret.pub_) return {}; + } + if (priv_ != nullptr) { + ret.priv_.reset(BN_dup(priv_.get())); + if (!ret.priv_) return {}; + } + return ret; +} + +bool ECKeyPointer::generate() { + if (!group_) return false; + const int nid = EC_GROUP_get_curve_name(group_.get()); + auto ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_EC); + if (!ctx || !ctx.initForKeygen() || + !ctx.setEcParameters(nid, OPENSSL_EC_NAMED_CURVE)) { + return false; + } + + EVP_PKEY* raw = nullptr; + if (EVP_PKEY_keygen(ctx.get(), &raw) != 1) return false; + EVPKeyPointer pkey(raw); + + DeleteFnPtr priv; + if (!GetPKeyBnParam(pkey.get(), OSSL_PKEY_PARAM_PRIV_KEY, &priv)) { + return false; + } + + size_t public_key_len = 0; + if (EVP_PKEY_get_octet_string_param( + pkey.get(), OSSL_PKEY_PARAM_PUB_KEY, nullptr, 0, &public_key_len) != + 1) { + return false; + } + + auto public_key = DataPointer::Alloc(public_key_len); + if (!public_key || EVP_PKEY_get_octet_string_param( + pkey.get(), + OSSL_PKEY_PARAM_PUB_KEY, + static_cast(public_key.get()), + public_key.size(), + &public_key_len) != 1) { + return false; + } + + auto point = ECPointPointer::New(group_.get()); + if (!point || + !point.setFromBuffer( + {static_cast(public_key.get()), public_key_len}, + group_.get())) { + return false; + } + + priv_ = std::move(priv); + pub_.reset(point.release()); + return true; +} + +bool ECKeyPointer::setPublicKey(const ECPointPointer& pub) { + if (!group_ || !pub) return false; + pub_.reset(EC_POINT_dup(pub.get(), group_.get())); + return pub_ != nullptr; +} + +bool ECKeyPointer::setPublicKeyRaw(const BignumPointer& x, + const BignumPointer& y) { + if (!group_ || !x || !y) return false; + const size_t field_len = (EC_GROUP_get_degree(group_.get()) + 7) / 8; + const size_t uncompressed_len = 1 + 2 * field_len; + auto buf = DataPointer::Alloc(uncompressed_len); + if (!buf) return false; + unsigned char* ptr = static_cast(buf.get()); + ptr[0] = POINT_CONVERSION_UNCOMPRESSED; + x.encodePaddedInto(ptr + 1, field_len); + y.encodePaddedInto(ptr + 1 + field_len, field_len); + + auto point = ECPointPointer::New(group_.get()); + if (!point || !point.setFromBuffer({ptr, uncompressed_len}, group_.get())) { + return false; + } + pub_.reset(point.release()); + return true; +} + +bool ECKeyPointer::setPrivateKey(const BignumPointer& priv) { + if (!group_ || !priv) return false; + priv_.reset(BN_dup(priv.get())); + return priv_ != nullptr; +} + +const BIGNUM* ECKeyPointer::getPrivateKey() const { + return priv_.get(); +} + +const EC_POINT* ECKeyPointer::getPublicKey() const { + return pub_.get(); +} + +const EC_GROUP* ECKeyPointer::getGroup() const { + return group_.get(); +} + +bool ECKeyPointer::checkKey() const { + if (!group_) return false; + + if (priv_ != nullptr) { + auto order = BignumPointer::New(); + if (!order || !EC_GROUP_get_order(group_.get(), order.get(), nullptr)) { + return false; + } + if (BN_is_zero(priv_.get()) || BN_is_negative(priv_.get()) || + BN_cmp(priv_.get(), order.get()) >= 0) { + return false; + } + } + + if (pub_ != nullptr && + EC_POINT_is_on_curve(group_.get(), pub_.get(), nullptr) != 1) { + return false; + } + + if (priv_ != nullptr && pub_ != nullptr) { + auto expected = ECPointPointer::New(group_.get()); + if (!expected || !expected.mul(group_.get(), priv_.get()) || + EC_POINT_cmp(group_.get(), expected.get(), pub_.get(), nullptr) != 0) { + return false; + } + } + + auto pkey = EVPKeyPointer::New(); + if (!pkey || !pkey.set(*this)) return false; + EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(pkey.get(), nullptr)); + if (!ctx) return false; + if (pub_ != nullptr && EVP_PKEY_public_check(ctx.get()) != 1) return false; + if (priv_ != nullptr && EVP_PKEY_private_check(ctx.get()) != 1) return false; + return true; +} + +DataPointer ECKeyPointer::computeSecret(const ECPointPointer& peer) const { + if (!group_ || !priv_ || !peer) return {}; + auto our_key = EVPKeyPointer::New(); + auto their_key = EVPKeyPointer::New(); + auto their_ec = ECKeyPointer::New(group_.get()); + if (!our_key || !their_key || !our_key.set(*this) || + !their_ec.setPublicKey(peer) || !their_key.set(their_ec)) { + return {}; + } + + EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(our_key.get(), nullptr)); + size_t out_len = 0; + if (!ctx || EVP_PKEY_derive_init(ctx.get()) != 1 || + EVP_PKEY_derive_set_peer(ctx.get(), their_key.get()) != 1 || + EVP_PKEY_derive(ctx.get(), nullptr, &out_len) != 1) { + return {}; + } + + auto out = DataPointer::Alloc(out_len); + if (!out) return {}; + if (EVP_PKEY_derive( + ctx.get(), static_cast(out.get()), &out_len) != 1) { + return {}; + } + return out.resize(out_len); +} + +ECKeyPointer ECKeyPointer::NewByCurveName(int nid) { + ECKeyPointer ret; + ret.group_.reset(EC_GROUP_new_by_curve_name(nid)); + return ret; +} + +ECKeyPointer ECKeyPointer::New(const EC_GROUP* group) { + ECKeyPointer ret; + if (group != nullptr) { + ret.group_.reset(EC_GROUP_dup(group)); + } + return ret; +} +#endif // NCRYPTO_USE_LEGACY_KEY_TYPES // ============================================================================ @@ -3824,8 +5196,33 @@ bool EVPKeyCtxPointer::setDsaParameters(uint32_t bits, bool EVPKeyCtxPointer::setEcParameters(int curve, int encoding) { if (!ctx_) return false; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + const char* group_name = OBJ_nid2sn(curve); + if (group_name == nullptr) return false; + + const char* encoding_name = nullptr; + switch (encoding) { + case OPENSSL_EC_EXPLICIT_CURVE: + encoding_name = OSSL_PKEY_EC_ENCODING_EXPLICIT; + break; + case OPENSSL_EC_NAMED_CURVE: + encoding_name = OSSL_PKEY_EC_ENCODING_GROUP; + break; + default: + return false; + } + OSSL_PARAM params[] = { + OSSL_PARAM_construct_utf8_string( + OSSL_PKEY_PARAM_GROUP_NAME, const_cast(group_name), 0), + OSSL_PARAM_construct_utf8_string( + OSSL_PKEY_PARAM_EC_ENCODING, const_cast(encoding_name), 0), + OSSL_PARAM_END, + }; + return EVP_PKEY_CTX_set_params(ctx_.get(), params) == 1; +#else return EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx_.get(), curve) == 1 && EVP_PKEY_CTX_set_ec_param_enc(ctx_.get(), encoding) == 1; +#endif } bool EVPKeyCtxPointer::setRsaOaepMd(const Digest& md) { @@ -3864,12 +5261,16 @@ bool EVPKeyCtxPointer::setRsaKeygenBits(int bits) { bool EVPKeyCtxPointer::setRsaKeygenPubExp(BignumPointer&& e) { if (!ctx_) return false; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + return EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx_.get(), e.get()) == 1; +#else if (EVP_PKEY_CTX_set_rsa_keygen_pubexp(ctx_.get(), e.get()) == 1) { // The ctx_ takes ownership of e on success. e.release(); return true; } return false; +#endif } bool EVPKeyCtxPointer::setRsaPssKeygenMd(const Digest& md) { @@ -3923,7 +5324,7 @@ bool EVPKeyCtxPointer::setRsaOaepLabel(DataPointer&& data) { bool EVPKeyCtxPointer::setSignatureMd(const EVPMDCtxPointer& md) { if (!ctx_) return false; - return EVP_PKEY_CTX_set_signature_md(ctx_.get(), EVP_MD_CTX_md(md.get())) == + return EVP_PKEY_CTX_set_signature_md(ctx_.get(), GetDigestCtxMd(md.get())) == 1; } @@ -4113,26 +5514,206 @@ DataPointer CipherImpl(const EVPKeyPointer& key, } } // namespace -Rsa::Rsa() : rsa_(nullptr) {} +#if NCRYPTO_USE_OPENSSL3_PROVIDER +namespace { +int DigestAlgorithmIdentifierToNid(const unsigned char* data, size_t size) { + size_t sequence_header; + size_t sequence_len; + size_t sequence_total; + if (!ReadASN1Element( + data, size, 0x30, &sequence_header, &sequence_len, &sequence_total)) { + return NID_undef; + } + + size_t oid_header; + size_t oid_len; + size_t oid_total; + const unsigned char* oid = data + sequence_header; + if (!ReadASN1Element( + oid, sequence_len, 0x06, &oid_header, &oid_len, &oid_total)) { + return NID_undef; + } + + const unsigned char* oid_data = oid; + DeleteFnPtr obj( + d2i_ASN1_OBJECT(nullptr, &oid_data, oid_total)); + if (!obj) return NID_undef; + return OBJ_obj2nid(obj.get()); +} + +bool ReadRsaPssParams(const EVP_PKEY* pkey, Rsa::PssParams* params) { + const int der_len = i2d_PUBKEY(pkey, nullptr); + if (der_len <= 0) return false; + + auto der = DataPointer::Alloc(der_len); + if (!der) return false; + + auto serialized = static_cast(der.get()); + if (i2d_PUBKEY(pkey, &serialized) != der_len) return false; + + size_t outer_header; + size_t outer_len; + size_t outer_total; + const auto* data = static_cast(der.get()); + if (!ReadASN1Element( + data, der.size(), 0x30, &outer_header, &outer_len, &outer_total)) { + return false; + } + + size_t alg_header; + size_t alg_len; + size_t alg_total; + const unsigned char* alg = data + outer_header; + if (!ReadASN1Element( + alg, outer_len, 0x30, &alg_header, &alg_len, &alg_total)) { + return false; + } + + size_t oid_header; + size_t oid_len; + size_t oid_total; + const unsigned char* oid = alg + alg_header; + if (!ReadASN1Element(oid, alg_len, 0x06, &oid_header, &oid_len, &oid_total) || + oid_total == alg_len) { + return false; + } + + size_t pss_header; + size_t pss_len; + size_t pss_total; + const unsigned char* pss = oid + oid_total; + if (!ReadASN1Element( + pss, alg_len - oid_total, 0x30, &pss_header, &pss_len, &pss_total)) { + return false; + } + + const unsigned char* cursor = pss + pss_header; + size_t remaining = pss_len; + while (remaining > 0) { + const unsigned char tag = cursor[0]; + size_t item_header; + size_t item_len; + size_t item_total; + if (!ReadASN1Element( + cursor, remaining, tag, &item_header, &item_len, &item_total)) { + return false; + } + + const unsigned char* item = cursor + item_header; + switch (tag) { + case 0xa0: { + const int nid = DigestAlgorithmIdentifierToNid(item, item_len); + if (nid != NID_undef) params->digest = OBJ_nid2ln(nid); + break; + } + case 0xa1: { + size_t mgf_header; + size_t mgf_len; + size_t mgf_total; + if (!ReadASN1Element( + item, item_len, 0x30, &mgf_header, &mgf_len, &mgf_total)) { + return false; + } + const unsigned char* mgf = item + mgf_header; + size_t mgf_oid_header; + size_t mgf_oid_len; + size_t mgf_oid_total; + if (!ReadASN1Element(mgf, + mgf_len, + 0x06, + &mgf_oid_header, + &mgf_oid_len, + &mgf_oid_total) || + mgf_oid_total == mgf_len) { + return false; + } + const int nid = DigestAlgorithmIdentifierToNid(mgf + mgf_oid_total, + mgf_len - mgf_oid_total); + if (nid != NID_undef) params->mgf1_digest = OBJ_nid2ln(nid); + break; + } + case 0xa2: { + size_t int_header; + size_t int_len; + size_t int_total; + if (!ReadASN1Element( + item, item_len, 0x02, &int_header, &int_len, &int_total)) { + return false; + } + int64_t salt_length = 0; + for (size_t n = 0; n < int_len; n++) { + salt_length = (salt_length << 8) | item[int_header + n]; + } + params->salt_length = salt_length; + break; + } + } + + cursor += item_total; + remaining -= item_total; + } + + return true; +} +} // namespace + +Rsa::Rsa() : rsa_(false) {} + +Rsa::Rsa(const EVP_PKEY* pkey) : Rsa() { + const int type = EVPKeyPointer::id(pkey); + if (type != EVP_PKEY_RSA && type != EVP_PKEY_RSA_PSS) return; + if (!GetPKeyBnParam(pkey, OSSL_PKEY_PARAM_RSA_N, &n_) || + !GetPKeyBnParam(pkey, OSSL_PKEY_PARAM_RSA_E, &e_)) { + return; + } + GetOptionalPKeyBnParam(pkey, OSSL_PKEY_PARAM_RSA_D, &d_); + GetOptionalPKeyBnParam(pkey, OSSL_PKEY_PARAM_RSA_FACTOR1, &p_); + GetOptionalPKeyBnParam(pkey, OSSL_PKEY_PARAM_RSA_FACTOR2, &q_); + GetOptionalPKeyBnParam(pkey, OSSL_PKEY_PARAM_RSA_EXPONENT1, &dp_); + GetOptionalPKeyBnParam(pkey, OSSL_PKEY_PARAM_RSA_EXPONENT2, &dq_); + GetOptionalPKeyBnParam(pkey, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, &qi_); + + if (type == EVP_PKEY_RSA_PSS) { + PssParams params; + if (ReadRsaPssParams(pkey, ¶ms)) pss_params_ = params; + } + rsa_ = true; +} +#else +Rsa::Rsa() : rsa_(nullptr) {} Rsa::Rsa(OSSL3_CONST RSA* ptr) : rsa_(ptr) {} +#endif const Rsa::PublicKey Rsa::getPublicKey() const { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (!rsa_) return {}; + return PublicKey{n_.get(), e_.get(), d_.get()}; +#else if (rsa_ == nullptr) return {}; PublicKey key; RSA_get0_key(rsa_, &key.n, &key.e, &key.d); return key; +#endif } const Rsa::PrivateKey Rsa::getPrivateKey() const { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (!rsa_) return {}; + return PrivateKey{p_.get(), q_.get(), dp_.get(), dq_.get(), qi_.get()}; +#else if (rsa_ == nullptr) return {}; PrivateKey key; RSA_get0_factors(rsa_, &key.p, &key.q); RSA_get0_crt_params(rsa_, &key.dp, &key.dq, &key.qi); return key; +#endif } const std::optional Rsa::getPssParams() const { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + return pss_params_; +#else if (rsa_ == nullptr) return std::nullopt; const RSA_PSS_PARAMS* params = RSA_get0_pss_params(rsa_); if (params == nullptr) return std::nullopt; @@ -4165,16 +5746,36 @@ const std::optional Rsa::getPssParams() const { } } return ret; +#endif +} + +BIOPointer Rsa::derPublicKey() const { + auto bio = BIOPointer::NewMem(); + if (!bio) return {}; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + auto pkey = EVPKeyPointer::NewRSA(*this); + if (!pkey || i2d_PUBKEY_bio(bio.get(), pkey.get()) != 1) return {}; +#else + if (rsa_ == nullptr || i2d_RSA_PUBKEY_bio(bio.get(), rsa_) != 1) return {}; +#endif + return bio; } bool Rsa::setPublicKey(BignumPointer&& n, BignumPointer&& e) { if (!n || !e) return false; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + n_.reset(n.release()); + e_.reset(e.release()); + rsa_ = true; + return true; +#else if (RSA_set0_key(const_cast(rsa_), n.get(), e.get(), nullptr) == 1) { n.release(); e.release(); return true; } return false; +#endif } bool Rsa::setPrivateKey(BignumPointer&& d, @@ -4183,6 +5784,17 @@ bool Rsa::setPrivateKey(BignumPointer&& d, BignumPointer&& dp, BignumPointer&& dq, BignumPointer&& qi) { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (!d || !q || !p || !dp || !dq || !qi) return false; + d_.reset(d.release()); + q_.reset(q.release()); + p_.reset(p.release()); + dp_.reset(dp.release()); + dq_.reset(dq.release()); + qi_.reset(qi.release()); + rsa_ = n_ != nullptr && e_ != nullptr; + return rsa_; +#else if (!RSA_set0_key(const_cast(rsa_), nullptr, nullptr, d.get())) { return false; } @@ -4202,6 +5814,7 @@ bool Rsa::setPrivateKey(BignumPointer&& d, dq.release(); qi.release(); return true; +#endif } DataPointer Rsa::encrypt(const EVPKeyPointer& key, @@ -4324,12 +5937,99 @@ void Cipher::ForEach(Cipher::CipherNameCallback callback) { // ============================================================================ -Ec::Ec() : ec_(nullptr) {} +#if NCRYPTO_USE_OPENSSL3_PROVIDER +Ec::Ec() : ec_(nullptr), pub_(nullptr) {} + +Ec::Ec(const EVP_PKEY* pkey) : Ec() { + if (EVPKeyPointer::id(pkey) != EVP_PKEY_EC) return; + char group_name[80]; + size_t group_name_len = 0; + if (EVP_PKEY_get_utf8_string_param(pkey, + OSSL_PKEY_PARAM_GROUP_NAME, + group_name, + sizeof(group_name), + &group_name_len) != 1) { + return; + } + + const int nid = GetCurveIdFromName(group_name); + if (nid == NID_undef) return; + ec_.reset(EC_GROUP_new_by_curve_name(nid)); + if (!ec_) return; + + size_t public_key_len = 0; + if (EVP_PKEY_get_octet_string_param( + pkey, OSSL_PKEY_PARAM_PUB_KEY, nullptr, 0, &public_key_len) != 1) { + return; + } + + auto public_key = DataPointer::Alloc(public_key_len); + if (!public_key || + EVP_PKEY_get_octet_string_param( + pkey, + OSSL_PKEY_PARAM_PUB_KEY, + static_cast(public_key.get()), + public_key.size(), + &public_key_len) != 1 || + public_key_len == 0) { + ec_.reset(); + return; + } + + const auto* public_key_data = + static_cast(public_key.get()); + switch (public_key_data[0]) { + case POINT_CONVERSION_COMPRESSED: + case POINT_CONVERSION_COMPRESSED + 1: + form_ = POINT_CONVERSION_COMPRESSED; + break; + case POINT_CONVERSION_UNCOMPRESSED: + form_ = POINT_CONVERSION_UNCOMPRESSED; + break; + case POINT_CONVERSION_HYBRID: + case POINT_CONVERSION_HYBRID + 1: + form_ = POINT_CONVERSION_HYBRID; + break; + default: + ec_.reset(); + return; + } + auto point = ECPointPointer::New(ec_.get()); + if (!point || + !point.setFromBuffer({public_key_data, public_key_len}, ec_.get())) { + ec_.reset(); + return; + } + pub_.reset(point.release()); +} +#else +Ec::Ec() : ec_(nullptr) {} Ec::Ec(OSSL3_CONST EC_KEY* key) : ec_(key) {} +#endif const EC_GROUP* Ec::getGroup() const { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + return ec_.get(); +#else return ECKeyPointer::GetGroup(ec_); +#endif +} + +const EC_POINT* Ec::getPublicKey() const { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + return pub_.get(); +#else + return ECKeyPointer::GetPublicKey(ec_); +#endif +} + +point_conversion_form_t Ec::getPointConversionForm() const { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + return form_; +#else + return EC_KEY_get_conv_form(ec_); +#endif } int Ec::getCurve() const { @@ -4433,7 +6133,7 @@ size_t EVPMDCtxPointer::getDigestSize() const { const EVP_MD* EVPMDCtxPointer::getDigest() const { if (!ctx_) return nullptr; - return EVP_MD_CTX_md(ctx_.get()); + return GetDigestCtxMd(ctx_.get()); } bool EVPMDCtxPointer::hasXofFlag() const { @@ -4975,31 +6675,61 @@ std::pair X509Name::Iterator::operator*() const { // ============================================================================ -Dsa::Dsa() : dsa_(nullptr) {} +#if NCRYPTO_USE_OPENSSL3_PROVIDER +Dsa::Dsa() : dsa_(false) {} +Dsa::Dsa(const EVP_PKEY* pkey) : Dsa() { + if (EVPKeyPointer::id(pkey) != EVP_PKEY_DSA) return; + if (!GetPKeyBnParam(pkey, OSSL_PKEY_PARAM_FFC_P, &p_) || + !GetPKeyBnParam(pkey, OSSL_PKEY_PARAM_FFC_Q, &q_)) { + return; + } + dsa_ = true; +} +#else +Dsa::Dsa() : dsa_(nullptr) {} Dsa::Dsa(OSSL3_CONST DSA* dsa) : dsa_(dsa) {} +#endif const BIGNUM* Dsa::getP() const { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (!dsa_) return nullptr; + return p_.get(); +#else if (dsa_ == nullptr) return nullptr; const BIGNUM* p; DSA_get0_pqg(dsa_, &p, nullptr, nullptr); return p; +#endif } const BIGNUM* Dsa::getQ() const { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (!dsa_) return nullptr; + return q_.get(); +#else if (dsa_ == nullptr) return nullptr; const BIGNUM* q; DSA_get0_pqg(dsa_, nullptr, &q, nullptr); return q; +#endif } size_t Dsa::getModulusLength() const { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (!dsa_) return 0; +#else if (dsa_ == nullptr) return 0; +#endif return BignumPointer::GetBitCount(getP()); } size_t Dsa::getDivisorLength() const { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + if (!dsa_) return 0; +#else if (dsa_ == nullptr) return 0; +#endif return BignumPointer::GetBitCount(getQ()); } diff --git a/deps/ncrypto/ncrypto.gyp b/deps/ncrypto/ncrypto.gyp index 1747f3ea0149b9..804a664fa0a2a1 100644 --- a/deps/ncrypto/ncrypto.gyp +++ b/deps/ncrypto/ncrypto.gyp @@ -2,10 +2,25 @@ 'variables': { 'ncrypto_bssl_libdecrepit_missing%': 1, 'ncrypto_sources': [ - 'engine.cc', 'ncrypto.cc', 'ncrypto.h', ], + 'ncrypto_engine_sources': [ + 'engine.cc', + 'ncrypto.h', + ], + 'ncrypto_strict_defines': [ + 'OPENSSL_API_COMPAT=30000', + 'OPENSSL_NO_DEPRECATED', + ], + 'ncrypto_legacy_openssl_defines': [ + 'OPENSSL_API_COMPAT=0x10100000L', + ], + 'ncrypto_engine_defines': [ + 'OPENSSL_API_COMPAT=30000', + 'OPENSSL_SUPPRESS_DEPRECATED', + 'NCRYPTO_ENGINE_COMPAT=1', + ], }, 'targets': [ { @@ -20,9 +35,25 @@ 'defines': [ 'NCRYPTO_BSSL_LIBDECREPIT_MISSING=<(ncrypto_bssl_libdecrepit_missing)', ], + 'conditions': [ + ['openssl_is_boringssl=="false" and openssl_version >= 0x3000000f', { + 'defines!': [ '<@(ncrypto_legacy_openssl_defines)' ], + 'defines': [ '<@(ncrypto_strict_defines)' ], + }], + ], }, 'sources': [ '<@(ncrypto_sources)' ], 'conditions': [ + ['openssl_is_boringssl=="false" and openssl_version >= 0x3000000f', { + 'defines!': [ '<@(ncrypto_legacy_openssl_defines)' ], + 'defines': [ '<@(ncrypto_strict_defines)' ], + 'dependencies': [ + 'ncrypto_engine', + ], + }], + ['openssl_is_boringssl=="false" and openssl_version < 0x3000000f', { + 'sources': [ '<@(ncrypto_engine_sources)' ], + }], ['node_shared_openssl=="false"', { 'dependencies': [ '../openssl/openssl.gyp:openssl' @@ -30,5 +61,28 @@ }], ] }, - ] + ], + 'conditions': [ + ['openssl_is_boringssl=="false" and openssl_version >= 0x3000000f', { + 'targets': [ + { + 'target_name': 'ncrypto_engine', + 'type': 'static_library', + 'include_dirs': ['.'], + 'defines': [ + 'NCRYPTO_BSSL_LIBDECREPIT_MISSING=<(ncrypto_bssl_libdecrepit_missing)', + '<@(ncrypto_engine_defines)', + ], + 'sources': [ '<@(ncrypto_engine_sources)' ], + 'conditions': [ + ['node_shared_openssl=="false"', { + 'dependencies': [ + '../openssl/openssl.gyp:openssl' + ] + }], + ] + }, + ], + }], + ], } diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index a6befbf7cf6794..acfdd45f0e49d2 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -19,9 +19,9 @@ #include #include #include -#ifndef OPENSSL_NO_ENGINE +#if NCRYPTO_ENGINE_COMPAT && !defined(OPENSSL_NO_ENGINE) #include -#endif // !OPENSSL_NO_ENGINE +#endif // NCRYPTO_ENGINE_COMPAT && !OPENSSL_NO_ENGINE #ifndef OPENSSL_VERSION_PREREQ #define OPENSSL_VERSION_PREREQ(maj, min) \ @@ -40,6 +40,40 @@ #define NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK 0 #endif +// Backend split: +// - OpenSSL >= 3 uses provider APIs and hides deprecated low-level objects. +// - BoringSSL has its own API-compatible branch. +// - OpenSSL < 3 remains the legacy fallback branch. +#if !defined(OPENSSL_IS_BORINGSSL) && OPENSSL_VERSION_PREREQ(3, 0) +#define NCRYPTO_USE_OPENSSL3_PROVIDER 1 +#else +#define NCRYPTO_USE_OPENSSL3_PROVIDER 0 +#endif + +#ifdef OPENSSL_IS_BORINGSSL +#define NCRYPTO_USE_BORINGSSL 1 +#else +#define NCRYPTO_USE_BORINGSSL 0 +#endif + +#if !NCRYPTO_USE_OPENSSL3_PROVIDER && !NCRYPTO_USE_BORINGSSL +#define NCRYPTO_USE_LEGACY_OPENSSL 1 +#else +#define NCRYPTO_USE_LEGACY_OPENSSL 0 +#endif + +#if NCRYPTO_USE_BORINGSSL || NCRYPTO_USE_LEGACY_OPENSSL +#define NCRYPTO_USE_LEGACY_KEY_TYPES 1 +#else +#define NCRYPTO_USE_LEGACY_KEY_TYPES 0 +#endif + +#if NCRYPTO_USE_OPENSSL3_PROVIDER +#include +#include +#include +#endif + // The FIPS-related functions are only available // when the OpenSSL itself was compiled with FIPS support. #if defined(OPENSSL_FIPS) && !OPENSSL_VERSION_PREREQ(3, 0) @@ -112,7 +146,6 @@ #define EVP_PKEY_ML_KEM_512 NID_ML_KEM_512 #define EVP_PKEY_ML_KEM_768 NID_ML_KEM_768 #define EVP_PKEY_ML_KEM_1024 NID_ML_KEM_1024 -#include #elif OPENSSL_WITH_BORINGSSL_PQC #define EVP_PKEY_ML_KEM_768 NID_ML_KEM_768 #define EVP_PKEY_ML_KEM_1024 NID_ML_KEM_1024 @@ -303,7 +336,9 @@ template using DeleteFnPtr = typename FunctionDeleter::Pointer; using PKCS8Pointer = DeleteFnPtr; +#if NCRYPTO_USE_LEGACY_KEY_TYPES using RSAPointer = DeleteFnPtr; +#endif using SSLSessionPointer = DeleteFnPtr; class BIOPointer; @@ -502,11 +537,23 @@ class Cipher final { class Dsa final { public: Dsa(); +#if NCRYPTO_USE_OPENSSL3_PROVIDER + explicit Dsa(const EVP_PKEY* pkey); +#else Dsa(OSSL3_CONST DSA* dsa); +#endif NCRYPTO_DISALLOW_COPY_AND_MOVE(Dsa) +#if NCRYPTO_USE_OPENSSL3_PROVIDER + inline operator bool() const { + return dsa_; + } +#else inline operator bool() const { return dsa_ != nullptr; } +#endif +#if NCRYPTO_USE_LEGACY_KEY_TYPES inline operator OSSL3_CONST DSA*() const { return dsa_; } +#endif const BIGNUM* getP() const; const BIGNUM* getQ() const; @@ -514,7 +561,13 @@ class Dsa final { size_t getDivisorLength() const; private: +#if NCRYPTO_USE_OPENSSL3_PROVIDER + bool dsa_ = false; + DeleteFnPtr p_; + DeleteFnPtr q_; +#else OSSL3_CONST DSA* dsa_; +#endif }; // ============================================================================ @@ -523,11 +576,23 @@ class Dsa final { class Rsa final { public: Rsa(); +#if NCRYPTO_USE_OPENSSL3_PROVIDER + explicit Rsa(const EVP_PKEY* pkey); +#else Rsa(OSSL3_CONST RSA* rsa); +#endif NCRYPTO_DISALLOW_COPY_AND_MOVE(Rsa) +#if NCRYPTO_USE_OPENSSL3_PROVIDER + inline operator bool() const { + return rsa_; + } +#else inline operator bool() const { return rsa_ != nullptr; } +#endif +#if NCRYPTO_USE_LEGACY_KEY_TYPES inline operator OSSL3_CONST RSA*() const { return rsa_; } +#endif struct PublicKey { const BIGNUM* n; @@ -561,6 +626,8 @@ class Rsa final { using CipherParams = Cipher::CipherParams; + BIOPointer derPublicKey() const; + static DataPointer encrypt(const EVPKeyPointer& key, const CipherParams& params, const Buffer in); @@ -569,20 +636,41 @@ class Rsa final { const Buffer in); private: +#if NCRYPTO_USE_OPENSSL3_PROVIDER + bool rsa_ = false; + DeleteFnPtr n_; + DeleteFnPtr e_; + DeleteFnPtr d_; + DeleteFnPtr p_; + DeleteFnPtr q_; + DeleteFnPtr dp_; + DeleteFnPtr dq_; + DeleteFnPtr qi_; + std::optional pss_params_; +#else OSSL3_CONST RSA* rsa_; +#endif }; class Ec final { public: Ec(); +#if NCRYPTO_USE_OPENSSL3_PROVIDER + explicit Ec(const EVP_PKEY* pkey); +#else Ec(OSSL3_CONST EC_KEY* key); +#endif NCRYPTO_DISALLOW_COPY_AND_MOVE(Ec) const EC_GROUP* getGroup() const; + const EC_POINT* getPublicKey() const; + point_conversion_form_t getPointConversionForm() const; int getCurve() const; inline operator bool() const { return ec_ != nullptr; } +#if NCRYPTO_USE_LEGACY_KEY_TYPES inline operator OSSL3_CONST EC_KEY*() const { return ec_; } +#endif static int GetCurveIdFromName(const char* name); @@ -590,7 +678,13 @@ class Ec final { static bool GetCurves(GetCurveCallback callback); private: +#if NCRYPTO_USE_OPENSSL3_PROVIDER + DeleteFnPtr ec_; + DeleteFnPtr pub_; + point_conversion_form_t form_ = POINT_CONVERSION_UNCOMPRESSED; +#else OSSL3_CONST EC_KEY* ec_ = nullptr; +#endif }; // A managed pointer to a buffer of data. When destroyed the underlying @@ -926,7 +1020,11 @@ class EVPKeyPointer final { const Buffer& data); #endif static EVPKeyPointer NewDH(DHPointer&& dh); +#if NCRYPTO_USE_OPENSSL3_PROVIDER + static EVPKeyPointer NewRSA(const Rsa& rsa); +#else static EVPKeyPointer NewRSA(RSAPointer&& rsa); +#endif enum class PKEncodingType { // RSAPublicKey / RSAPrivateKey according to PKCS#1. @@ -998,7 +1096,9 @@ class EVPKeyPointer final { bool assign(const ECKeyPointer& eckey); bool set(const ECKeyPointer& eckey); +#if NCRYPTO_USE_LEGACY_KEY_TYPES operator const EC_KEY*() const; +#endif inline bool operator==(std::nullptr_t) const noexcept { return pkey_ == nullptr; @@ -1070,29 +1170,53 @@ class DHPointer final { static DHPointer New(size_t bits, unsigned int generator); DHPointer() = default; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + explicit DHPointer(EVPKeyPointer&& key, const char* group_name = nullptr); + DHPointer(BignumPointer&& p, BignumPointer&& g, const char* group_name); +#else explicit DHPointer(DH* dh); +#endif DHPointer(DHPointer&& other) noexcept; DHPointer& operator=(DHPointer&& other) noexcept; NCRYPTO_DISALLOW_COPY(DHPointer) ~DHPointer(); +#if NCRYPTO_USE_OPENSSL3_PROVIDER + inline bool operator==(std::nullptr_t) noexcept { + return !operator bool(); + } + inline operator bool() const { + return dh_ != nullptr || (p_ && g_); + } +#else inline bool operator==(std::nullptr_t) noexcept { return dh_ == nullptr; } inline operator bool() const { return dh_ != nullptr; } +#endif +#if NCRYPTO_USE_LEGACY_KEY_TYPES inline DH* get() const { return dh_.get(); } void reset(DH* dh = nullptr); DH* release(); +#else + inline EVP_PKEY* get() const { + return dh_.get(); + } + void reset(EVP_PKEY* dh = nullptr); + EVP_PKEY* release(); +#endif enum class CheckResult { NONE, - P_NOT_PRIME = DH_CHECK_P_NOT_PRIME, - P_NOT_SAFE_PRIME = DH_CHECK_P_NOT_SAFE_PRIME, - UNABLE_TO_CHECK_GENERATOR = DH_UNABLE_TO_CHECK_GENERATOR, - NOT_SUITABLE_GENERATOR = DH_NOT_SUITABLE_GENERATOR, - Q_NOT_PRIME = DH_CHECK_Q_NOT_PRIME, + P_NOT_PRIME = 0x01, + P_NOT_SAFE_PRIME = 0x02, + UNABLE_TO_CHECK_GENERATOR = 0x04, + NOT_SUITABLE_GENERATOR = 0x08, + Q_NOT_PRIME = 0x10, #ifndef OPENSSL_IS_BORINGSSL // Boringssl does not define the DH_CHECK_INVALID_[Q or J]_VALUE - INVALID_Q = DH_CHECK_INVALID_Q_VALUE, - INVALID_J = DH_CHECK_INVALID_J_VALUE, + INVALID_Q = 0x20, + INVALID_J = 0x40, + MODULUS_TOO_SMALL = 0x80, + MODULUS_TOO_LARGE = 0x100, #endif CHECK_FAILED = 512, }; @@ -1114,11 +1238,12 @@ class DHPointer final { CheckPublicKeyResult checkPublicKey(const BignumPointer& pub_key); DataPointer getPrime() const; + size_t getPrimeBits() const; DataPointer getGenerator() const; DataPointer getPublicKey() const; DataPointer getPrivateKey() const; bool hasPrivateKey() const; - DataPointer generateKeys() const; + DataPointer generateKeys(); DataPointer computeSecret(const BignumPointer& peer) const; bool setPublicKey(BignumPointer&& key); @@ -1130,7 +1255,16 @@ class DHPointer final { const EVPKeyPointer& theirKey); private: +#if NCRYPTO_USE_OPENSSL3_PROVIDER + DeleteFnPtr dh_; + BignumPointer p_; + BignumPointer g_; + BignumPointer pub_key_; + BignumPointer pvt_key_; + const char* group_name_ = nullptr; +#else DeleteFnPtr dh_; +#endif }; struct StackOfX509Deleter { @@ -1438,20 +1572,38 @@ class ECPointPointer final { }; class ECKeyPointer final { + friend class EVPKeyPointer; + public: ECKeyPointer(); + explicit ECKeyPointer(const EVPKeyPointer& key); +#if NCRYPTO_USE_LEGACY_KEY_TYPES explicit ECKeyPointer(EC_KEY* key); +#endif ECKeyPointer(ECKeyPointer&& other) noexcept; ECKeyPointer& operator=(ECKeyPointer&& other) noexcept; NCRYPTO_DISALLOW_COPY(ECKeyPointer) ~ECKeyPointer(); +#if NCRYPTO_USE_OPENSSL3_PROVIDER + inline bool operator==(std::nullptr_t) noexcept { + return group_ == nullptr; + } + inline operator bool() const { + return group_ != nullptr; + } +#else inline bool operator==(std::nullptr_t) noexcept { return key_ == nullptr; } inline operator bool() const { return key_ != nullptr; } +#endif +#if NCRYPTO_USE_LEGACY_KEY_TYPES inline EC_KEY* get() const { return key_.get(); } inline operator EC_KEY*() const { return key_.get(); } void reset(EC_KEY* key = nullptr); EC_KEY* release(); +#else + void reset(); +#endif ECKeyPointer clone() const; bool setPrivateKey(const BignumPointer& priv); @@ -1459,6 +1611,7 @@ class ECKeyPointer final { bool setPublicKeyRaw(const BignumPointer& x, const BignumPointer& y); bool generate(); bool checkKey() const; + DataPointer computeSecret(const ECPointPointer& peer) const; const EC_GROUP* getGroup() const; const BIGNUM* getPrivateKey() const; @@ -1467,14 +1620,22 @@ class ECKeyPointer final { static ECKeyPointer New(const EC_GROUP* group); static ECKeyPointer NewByCurveName(int nid); +#if NCRYPTO_USE_LEGACY_KEY_TYPES static const EC_POINT* GetPublicKey(const EC_KEY* key); static const BIGNUM* GetPrivateKey(const EC_KEY* key); static const EC_GROUP* GetGroup(const EC_KEY* key); static int GetGroupName(const EC_KEY* key); static bool Check(const EC_KEY* key); +#endif private: +#if NCRYPTO_USE_OPENSSL3_PROVIDER + DeleteFnPtr group_; + DeleteFnPtr pub_; + DeleteFnPtr priv_; +#else DeleteFnPtr key_; +#endif }; class EVPMDCtxPointer final { @@ -1641,24 +1802,23 @@ class EnginePointer final { public: EnginePointer() = default; - explicit EnginePointer(ENGINE* engine_, bool finish_on_exit = false); + explicit EnginePointer(void* engine_, bool finish_on_exit = false); EnginePointer(EnginePointer&& other) noexcept; EnginePointer& operator=(EnginePointer&& other) noexcept; NCRYPTO_DISALLOW_COPY(EnginePointer) ~EnginePointer(); inline operator bool() const { return engine != nullptr; } - inline ENGINE* get() { return engine; } inline void setFinishOnExit() { finish_on_exit = true; } - void reset(ENGINE* engine_ = nullptr, bool finish_on_exit_ = false); + void reset(void* engine_ = nullptr, bool finish_on_exit_ = false); bool setAsDefault(uint32_t flags, CryptoErrorList* errors = nullptr); bool init(bool finish_on_exit = false); EVPKeyPointer loadPrivateKey(const char* key_name); + bool setClientCertEngine(SSL_CTX* ctx); - // Release ownership of the ENGINE* pointer. - ENGINE* release(); + void* release(); // Retrieve an OpenSSL Engine instance by name. If the name does not // identify a valid named engine, the returned EnginePointer will be @@ -1670,7 +1830,7 @@ class EnginePointer final { static void initEnginesOnce(); private: - ENGINE* engine = nullptr; + void* engine = nullptr; bool finish_on_exit = false; }; #endif // !OPENSSL_NO_ENGINE diff --git a/src/crypto/crypto_common.cc b/src/crypto/crypto_common.cc index 4f117f160a9673..f6b828edf868e0 100644 --- a/src/crypto/crypto_common.cc +++ b/src/crypto/crypto_common.cc @@ -229,7 +229,8 @@ MaybeLocal GetEphemeralKey(Environment* env, const SSLPointer& ssl) { case EVP_PKEY_X448: { const char* curve_name; if (kid == EVP_PKEY_EC) { - int nid = ECKeyPointer::GetGroupName(key); + ECKeyPointer ec(key); + int nid = EC_GROUP_get_curve_name(ec.getGroup()); curve_name = OBJ_nid2sn(nid); } else { curve_name = OBJ_nid2sn(kid); @@ -282,7 +283,7 @@ MaybeLocal GetPeerCert( // NOTE: This is because of the odd OpenSSL behavior. On client `cert_chain` // contains the `peer_certificate`, but on server it doesn't. - X509Pointer cert(is_server ? SSL_get_peer_certificate(ssl.get()) : nullptr); + X509Pointer cert(is_server ? X509Pointer::PeerFrom(ssl) : X509Pointer()); STACK_OF(X509)* ssl_certs = SSL_get_peer_cert_chain(ssl.get()); if (!cert && (ssl_certs == nullptr || sk_X509_num(ssl_certs) == 0)) return Undefined(env->isolate()); diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index 01d8a17d8e2f53..5819b3e3416066 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -18,9 +18,6 @@ #include #include #include -#ifndef OPENSSL_NO_ENGINE -#include -#endif // !OPENSSL_NO_ENGINE #ifdef __APPLE__ #include #endif @@ -34,7 +31,6 @@ namespace node { -using ncrypto::BignumPointer; using ncrypto::BIOPointer; using ncrypto::Cipher; using ncrypto::ClearErrorOnReturn; @@ -549,7 +545,7 @@ static bool IsCertificateExpired(X509* cert) { // -1 if the time is in the past (expired) // 0 if there was an error // 1 if the time is in the future (not yet expired) - ASN1_TIME* not_after = X509_get_notAfter(cert); + const ASN1_TIME* not_after = X509_get0_notAfter(cert); if (not_after == nullptr) { return false; } @@ -1677,7 +1673,12 @@ void SecureContext::Init(const FunctionCallbackInfo& args) { return THROW_ERR_CRYPTO_OPERATION_FAILED( env, "Error generating ticket keys"); } +#if NCRYPTO_USE_OPENSSL3_PROVIDER + SSL_CTX_set_tlsext_ticket_key_evp_cb(sc->ctx_.get(), + TicketCompatibilityCallback); +#else SSL_CTX_set_tlsext_ticket_key_cb(sc->ctx_.get(), TicketCompatibilityCallback); +#endif } SSLPointer SecureContext::CreateSSL() { @@ -2021,7 +2022,11 @@ void SecureContext::SetDHParam(const FunctionCallbackInfo& args) { if (!bio) return; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + dh.reset(PEM_read_bio_Parameters(bio.get(), nullptr)); +#else dh.reset(PEM_read_bio_DHparams(bio.get(), nullptr, nullptr, nullptr)); +#endif } // Invalid dhparam is silently discarded and DHE is no longer used. @@ -2029,9 +2034,7 @@ void SecureContext::SetDHParam(const FunctionCallbackInfo& args) { if (!dh) return; - const BIGNUM* p; - DH_get0_pqg(dh.get(), &p, nullptr, nullptr); - const int size = BignumPointer::GetBitCount(p); + const int size = dh.getPrimeBits(); if (size < 1024) { return THROW_ERR_INVALID_ARG_VALUE( env, "DH parameter is less than 1024 bits"); @@ -2040,10 +2043,18 @@ void SecureContext::SetDHParam(const FunctionCallbackInfo& args) { env->isolate(), "DH parameter is less than 2048 bits")); } +#if NCRYPTO_USE_OPENSSL3_PROVIDER + EVPKeyPointer dh_pkey(dh.release()); + if (!SSL_CTX_set0_tmp_dh_pkey(sc->ctx_.get(), dh_pkey.get())) { +#else if (!SSL_CTX_set_tmp_dh(sc->ctx_.get(), dh.get())) { +#endif return THROW_ERR_CRYPTO_OPERATION_FAILED( env, "Error setting temp DH parameter"); } +#if NCRYPTO_USE_OPENSSL3_PROVIDER + dh_pkey.release(); +#endif } void SecureContext::SetMinProto(const FunctionCallbackInfo& args) { @@ -2404,7 +2415,7 @@ void SecureContext::SetClientCertEngine( } // Note that this takes another reference to `engine`. - if (!SSL_CTX_set_client_cert_engine(sc->ctx_.get(), engine.get())) + if (!engine.setClientCertEngine(sc->ctx_.get())) return ThrowCryptoError(env, ERR_get_error()); sc->client_cert_engine_provided_ = true; } @@ -2449,14 +2460,43 @@ void SecureContext::EnableTicketKeyCallback( SecureContext* wrap; ASSIGN_OR_RETURN_UNWRAP(&wrap, args.This()); +#if NCRYPTO_USE_OPENSSL3_PROVIDER + SSL_CTX_set_tlsext_ticket_key_evp_cb(wrap->ctx_.get(), TicketKeyCallback); +#else SSL_CTX_set_tlsext_ticket_key_cb(wrap->ctx_.get(), TicketKeyCallback); +#endif +} + +namespace { +#if NCRYPTO_USE_OPENSSL3_PROVIDER +bool InitTicketHmac(EVP_MAC_CTX* hctx, + const unsigned char* key, + size_t key_len) { + const char* md_name = EVP_MD_get0_name(Digest::SHA256); + if (md_name == nullptr) return false; + OSSL_PARAM params[] = { + OSSL_PARAM_construct_utf8_string( + OSSL_MAC_PARAM_DIGEST, const_cast(md_name), 0), + OSSL_PARAM_construct_end(), + }; + return EVP_MAC_init(hctx, key, key_len, params) == 1; +} +#else +bool InitTicketHmac(HMAC_CTX* hctx, const unsigned char* key, size_t key_len) { + return HMAC_Init_ex(hctx, key, key_len, Digest::SHA256, nullptr) == 1; } +#endif +} // namespace int SecureContext::TicketKeyCallback(SSL* ssl, unsigned char* name, unsigned char* iv, EVP_CIPHER_CTX* ectx, +#if NCRYPTO_USE_OPENSSL3_PROVIDER + EVP_MAC_CTX* hctx, +#else HMAC_CTX* hctx, +#endif int enc) { static const int kTicketPartSize = 16; @@ -2529,8 +2569,9 @@ int SecureContext::TicketKeyCallback(SSL* ssl, } ArrayBufferViewContents hmac_buf(hmac); - HMAC_Init_ex( - hctx, hmac_buf.data(), hmac_buf.length(), Digest::SHA256, nullptr); + if (!InitTicketHmac(hctx, hmac_buf.data(), hmac_buf.length())) { + return -1; + } ArrayBufferViewContents aes_key(aes.As()); if (enc) { @@ -2546,7 +2587,11 @@ int SecureContext::TicketCompatibilityCallback(SSL* ssl, unsigned char* name, unsigned char* iv, EVP_CIPHER_CTX* ectx, +#if NCRYPTO_USE_OPENSSL3_PROVIDER + EVP_MAC_CTX* hctx, +#else HMAC_CTX* hctx, +#endif int enc) { SecureContext* sc = static_cast( SSL_CTX_get_app_data(SSL_get_SSL_CTX(ssl))); @@ -2556,11 +2601,8 @@ int SecureContext::TicketCompatibilityCallback(SSL* ssl, if (!ncrypto::CSPRNG(iv, 16) || EVP_EncryptInit_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) { + !InitTicketHmac( + hctx, sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_))) { return -1; } return 1; @@ -2573,11 +2615,8 @@ int SecureContext::TicketCompatibilityCallback(SSL* ssl, 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) { + !InitTicketHmac( + hctx, sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_))) { return -1; } return 1; diff --git a/src/crypto/crypto_context.h b/src/crypto/crypto_context.h index ffb0b88816c092..95ddea4c262dd5 100644 --- a/src/crypto/crypto_context.h +++ b/src/crypto/crypto_context.h @@ -158,14 +158,22 @@ class SecureContext final : public BaseObject { unsigned char* name, unsigned char* iv, EVP_CIPHER_CTX* ectx, +#if NCRYPTO_USE_OPENSSL3_PROVIDER + EVP_MAC_CTX* hctx, +#else HMAC_CTX* hctx, +#endif int enc); static int TicketCompatibilityCallback(SSL* ssl, unsigned char* name, unsigned char* iv, EVP_CIPHER_CTX* ectx, +#if NCRYPTO_USE_OPENSSL3_PROVIDER + EVP_MAC_CTX* hctx, +#else HMAC_CTX* hctx, +#endif int enc); SecureContext(Environment* env, v8::Local wrap); diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc index 10dfd055021737..63bf1aab48921e 100644 --- a/src/crypto/crypto_dh.cc +++ b/src/crypto/crypto_dh.cc @@ -89,6 +89,26 @@ MaybeLocal DataPointerToBuffer(Environment* env, DataPointer&& data) { return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local()); } +void PutDhError(int reason) { +#ifdef OPENSSL_IS_BORINGSSL + OPENSSL_PUT_ERROR(DH, reason); +#elif NCRYPTO_USE_OPENSSL3_PROVIDER + ERR_raise(ERR_LIB_DH, reason); +#else + ERR_put_error(ERR_LIB_DH, 0, reason, __FILE__, __LINE__); +#endif +} + +#if defined(OPENSSL_IS_BORINGSSL) || !NCRYPTO_USE_OPENSSL3_PROVIDER +void PutBnError(int reason) { +#ifdef OPENSSL_IS_BORINGSSL + OPENSSL_PUT_ERROR(BN, reason); +#else + ERR_put_error(ERR_LIB_BN, 0, reason, __FILE__, __LINE__); +#endif +} +#endif + void DiffieHellmanGroup(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK_EQ(args.Length(), 1); @@ -115,12 +135,12 @@ void New(const FunctionCallbackInfo& args) { if (bits < 2) { #ifndef OPENSSL_IS_BORINGSSL #if OPENSSL_VERSION_MAJOR >= 3 - ERR_put_error(ERR_LIB_DH, 0, DH_R_MODULUS_TOO_SMALL, __FILE__, __LINE__); + PutDhError(DH_R_MODULUS_TOO_SMALL); #else - ERR_put_error(ERR_LIB_BN, 0, BN_R_BITS_TOO_SMALL, __FILE__, __LINE__); + PutBnError(BN_R_BITS_TOO_SMALL); #endif // OPENSSL_VERSION_MAJOR >= 3 #else // OPENSSL_IS_BORINGSSL - OPENSSL_PUT_ERROR(BN, BN_R_BITS_TOO_SMALL); + PutBnError(BN_R_BITS_TOO_SMALL); #endif // OPENSSL_IS_BORINGSSL return ThrowCryptoError(env, ERR_get_error(), "Invalid prime length"); } @@ -134,11 +154,7 @@ void New(const FunctionCallbackInfo& args) { } int32_t generator = args[1].As()->Value(); if (generator < 2) { -#ifndef OPENSSL_IS_BORINGSSL - ERR_put_error(ERR_LIB_DH, 0, DH_R_BAD_GENERATOR, __FILE__, __LINE__); -#else - OPENSSL_PUT_ERROR(DH, DH_R_BAD_GENERATOR); -#endif + PutDhError(DH_R_BAD_GENERATOR); return ThrowCryptoError(env, ERR_get_error(), "Invalid generator"); } @@ -167,20 +183,12 @@ void New(const FunctionCallbackInfo& args) { if (args[1]->IsInt32()) { int32_t generator = args[1].As()->Value(); if (generator < 2) { -#ifndef OPENSSL_IS_BORINGSSL - ERR_put_error(ERR_LIB_DH, 0, DH_R_BAD_GENERATOR, __FILE__, __LINE__); -#else - OPENSSL_PUT_ERROR(DH, DH_R_BAD_GENERATOR); -#endif + PutDhError(DH_R_BAD_GENERATOR); return ThrowCryptoError(env, ERR_get_error(), "Invalid generator"); } bn_g = BignumPointer::New(); if (!bn_g.setWord(generator)) { -#ifndef OPENSSL_IS_BORINGSSL - ERR_put_error(ERR_LIB_DH, 0, DH_R_BAD_GENERATOR, __FILE__, __LINE__); -#else - OPENSSL_PUT_ERROR(DH, DH_R_BAD_GENERATOR); -#endif + PutDhError(DH_R_BAD_GENERATOR); return ThrowCryptoError(env, ERR_get_error(), "Invalid generator"); } } else { @@ -189,19 +197,11 @@ void New(const FunctionCallbackInfo& args) { return THROW_ERR_OUT_OF_RANGE(env, "generator is too big"); bn_g = BignumPointer(reinterpret_cast(arg1.data()), arg1.size()); if (!bn_g) { -#ifndef OPENSSL_IS_BORINGSSL - ERR_put_error(ERR_LIB_DH, 0, DH_R_BAD_GENERATOR, __FILE__, __LINE__); -#else - OPENSSL_PUT_ERROR(DH, DH_R_BAD_GENERATOR); -#endif + PutDhError(DH_R_BAD_GENERATOR); return ThrowCryptoError(env, ERR_get_error(), "Invalid generator"); } if (bn_g.getWord().has_value() && bn_g.getWord().value() < 2) { -#ifndef OPENSSL_IS_BORINGSSL - ERR_put_error(ERR_LIB_DH, 0, DH_R_BAD_GENERATOR, __FILE__, __LINE__); -#else - OPENSSL_PUT_ERROR(DH, DH_R_BAD_GENERATOR); -#endif + PutDhError(DH_R_BAD_GENERATOR); return ThrowCryptoError(env, ERR_get_error(), "Invalid generator"); } } diff --git a/src/crypto/crypto_ec.cc b/src/crypto/crypto_ec.cc index a89e3391dbf896..3d38f253419fe2 100644 --- a/src/crypto/crypto_ec.cc +++ b/src/crypto/crypto_ec.cc @@ -187,15 +187,16 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo& args) { return; } - int field_size = EC_GROUP_get_degree(ecdh->group_); - size_t out_len = (field_size + 7) / 8; - auto bs = ArrayBuffer::NewBackingStore( - env->isolate(), out_len, BackingStoreInitializationMode::kUninitialized); - - if (!ECDH_compute_key( - bs->Data(), bs->ByteLength(), pub, ecdh->key_.get(), nullptr)) + auto secret = ecdh->key_.computeSecret(pub); + if (!secret) return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to compute ECDH key"); + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), + secret.size(), + BackingStoreInitializationMode::kUninitialized); + memcpy(bs->Data(), secret.get(), secret.size()); + Local ab = ArrayBuffer::New(env->isolate(), std::move(bs)); Local buffer; if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return; @@ -469,11 +470,11 @@ bool ExportJWKEcKey(Environment* env, const auto& m_pkey = key.GetAsymmetricKey(); CHECK_EQ(m_pkey.id(), EVP_PKEY_EC); - const EC_KEY* ec = m_pkey; - CHECK_NOT_NULL(ec); + ECKeyPointer ec(m_pkey); + CHECK(ec); - const auto pub = ECKeyPointer::GetPublicKey(ec); - const auto group = ECKeyPointer::GetGroup(ec); + const auto pub = ec.getPublicKey(); + const auto group = ec.getGroup(); int degree_bits = EC_GROUP_get_degree(group); int degree_bytes = @@ -538,7 +539,7 @@ bool ExportJWKEcKey(Environment* env, } if (key.GetKeyType() == kKeyTypePrivate) { - auto pvt = ECKeyPointer::GetPrivateKey(ec); + auto pvt = ec.getPrivateKey(); return SetEncodedValue(env, target, env->jwk_d_string(), pvt, degree_bytes) .IsJust(); } @@ -751,10 +752,10 @@ bool GetEcKeyDetail(Environment* env, const auto& m_pkey = key.GetAsymmetricKey(); CHECK_EQ(m_pkey.id(), EVP_PKEY_EC); - const EC_KEY* ec = m_pkey; - CHECK_NOT_NULL(ec); + ECKeyPointer ec(m_pkey); + CHECK(ec); - const auto group = ECKeyPointer::GetGroup(ec); + const auto group = ec.getGroup(); int nid = EC_GROUP_get_curve_name(group); return target @@ -770,11 +771,11 @@ bool GetEcKeyDetail(Environment* env, // https://github.com/chromium/chromium/blob/7af6cfd/components/webcrypto/algorithms/ecdsa.cc size_t GroupOrderSize(const EVPKeyPointer& key) { - const EC_KEY* ec = key; - CHECK_NOT_NULL(ec); + ECKeyPointer ec(key); + CHECK(ec); auto order = BignumPointer::New(); CHECK(order); - CHECK(EC_GROUP_get_order(ECKeyPointer::GetGroup(ec), order.get(), nullptr)); + CHECK(EC_GROUP_get_order(ec.getGroup(), order.get(), nullptr)); return order.byteLength(); } } // namespace crypto diff --git a/src/crypto/crypto_hash.cc b/src/crypto/crypto_hash.cc index 989f81302d198d..1a98b7b5b0c9ca 100644 --- a/src/crypto/crypto_hash.cc +++ b/src/crypto/crypto_hash.cc @@ -247,7 +247,11 @@ const EVP_MD* GetDigestImplementation(Environment* env, } void MarkInvalidXofLength() { +#if NCRYPTO_USE_OPENSSL3_PROVIDER + ERR_raise(ERR_LIB_EVP, EVP_R_NOT_XOF_OR_INVALID_LENGTH); +#else EVPerr(EVP_F_EVP_DIGESTFINALXOF, EVP_R_NOT_XOF_OR_INVALID_LENGTH); +#endif } // DEP0198 EOL requires XOFs without an OpenSSL-defined default output length @@ -459,7 +463,7 @@ bool Hash::HashInit(const EVP_MD* md, Maybe xof_md_len) { // This is a little hack to cause createHash to fail when an incorrect // hashSize option was passed for a non-XOF hash function. if (!mdctx_.hasXofFlag()) [[unlikely]] { - EVPerr(EVP_F_EVP_DIGESTFINALXOF, EVP_R_NOT_XOF_OR_INVALID_LENGTH); + MarkInvalidXofLength(); mdctx_.reset(); return false; } diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc index 40b5ae9563ee7b..7066c556e87a54 100644 --- a/src/crypto/crypto_keys.cc +++ b/src/crypto/crypto_keys.cc @@ -385,11 +385,11 @@ bool KeyObjectData::ToEncodedPublicKey( Mutex::ScopedLock lock(mutex()); const auto& pkey = GetAsymmetricKey(); if (pkey.id() == EVP_PKEY_EC) { - const EC_KEY* ec_key = pkey; - CHECK_NOT_NULL(ec_key); + ECKeyPointer ec_key(pkey); + CHECK(ec_key); auto form = static_cast(config.ec_point_form); - const auto group = ECKeyPointer::GetGroup(ec_key); - const auto point = ECKeyPointer::GetPublicKey(ec_key); + const auto group = ec_key.getGroup(); + const auto point = ec_key.getPublicKey(); return ECPointToBuffer(env, group, point, form).ToLocal(out); } const int id = pkey.id(); @@ -431,11 +431,11 @@ bool KeyObjectData::ToEncodedPrivateKey( Mutex::ScopedLock lock(mutex()); const auto& pkey = GetAsymmetricKey(); if (pkey.id() == EVP_PKEY_EC) { - const EC_KEY* ec_key = pkey; - CHECK_NOT_NULL(ec_key); - const BIGNUM* private_key = ECKeyPointer::GetPrivateKey(ec_key); + ECKeyPointer ec_key(pkey); + CHECK(ec_key); + const BIGNUM* private_key = ec_key.getPrivateKey(); CHECK_NOT_NULL(private_key); - const auto group = ECKeyPointer::GetGroup(ec_key); + const auto group = ec_key.getGroup(); auto order = BignumPointer::New(); CHECK(order); CHECK(EC_GROUP_get_order(group, order.get(), nullptr)); @@ -629,7 +629,9 @@ static KeyObjectData ImportRawKey(Environment* env, throw_invalid(); return {}; } +#if NCRYPTO_USE_LEGACY_KEY_TYPES eckey.release(); +#endif return KeyObjectData::CreateAsymmetric(target_type, std::move(pkey)); } @@ -1460,15 +1462,15 @@ void KeyObjectHandle::ExportECPublicRaw( return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); } - const EC_KEY* ec_key = m_pkey; - CHECK_NOT_NULL(ec_key); + ECKeyPointer ec_key(m_pkey); + CHECK(ec_key); CHECK(args[0]->IsInt32()); auto form = static_cast(args[0].As()->Value()); - const auto group = ECKeyPointer::GetGroup(ec_key); - const auto point = ECKeyPointer::GetPublicKey(ec_key); + const auto group = ec_key.getGroup(); + const auto point = ec_key.getPublicKey(); Local buf; if (!ECPointToBuffer(env, group, point, form).ToLocal(&buf)) return; @@ -1491,13 +1493,13 @@ void KeyObjectHandle::ExportECPrivateRaw( return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); } - const EC_KEY* ec_key = m_pkey; - CHECK_NOT_NULL(ec_key); + ECKeyPointer ec_key(m_pkey); + CHECK(ec_key); - const BIGNUM* private_key = ECKeyPointer::GetPrivateKey(ec_key); + const BIGNUM* private_key = ec_key.getPrivateKey(); CHECK_NOT_NULL(private_key); - const auto group = ECKeyPointer::GetGroup(ec_key); + const auto group = ec_key.getGroup(); auto order = BignumPointer::New(); CHECK(order); CHECK(EC_GROUP_get_order(group, order.get(), nullptr)); diff --git a/src/crypto/crypto_rsa.cc b/src/crypto/crypto_rsa.cc index acec4b993613cd..ad27108418353f 100644 --- a/src/crypto/crypto_rsa.cc +++ b/src/crypto/crypto_rsa.cc @@ -19,7 +19,9 @@ using ncrypto::DataPointer; using ncrypto::Digest; using ncrypto::EVPKeyCtxPointer; using ncrypto::EVPKeyPointer; +#if NCRYPTO_USE_LEGACY_KEY_TYPES using ncrypto::RSAPointer; +#endif using v8::ArrayBuffer; using v8::BackingStoreInitializationMode; using v8::FunctionCallbackInfo; @@ -353,6 +355,9 @@ KeyObjectData ImportJWKRsaKey(Environment* env, Local jwk) { KeyType type = d_value->IsString() ? kKeyTypePrivate : kKeyTypePublic; +#if NCRYPTO_USE_OPENSSL3_PROVIDER + ncrypto::Rsa rsa_view; +#else RSAPointer rsa(RSA_new()); if (!rsa) { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Unable to create RSA pointer"); @@ -360,6 +365,7 @@ KeyObjectData ImportJWKRsaKey(Environment* env, Local jwk) { } ncrypto::Rsa rsa_view(rsa.get()); +#endif ByteSource n = ByteSource::FromEncodedString(env, n_value.As()); ByteSource e = ByteSource::FromEncodedString(env, e_value.As()); @@ -421,7 +427,11 @@ KeyObjectData ImportJWKRsaKey(Environment* env, Local jwk) { } } +#if NCRYPTO_USE_OPENSSL3_PROVIDER + auto pkey = EVPKeyPointer::NewRSA(rsa_view); +#else auto pkey = EVPKeyPointer::NewRSA(std::move(rsa)); +#endif if (!pkey) { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Unable to create key pointer"); return {}; diff --git a/src/crypto/crypto_tls.cc b/src/crypto/crypto_tls.cc index 28eb760dbb4cd6..ce44ad15fa79d3 100644 --- a/src/crypto/crypto_tls.cc +++ b/src/crypto/crypto_tls.cc @@ -874,7 +874,11 @@ void TLSWrap::ClearOut() { return; const char* ls = ERR_lib_error_string(ssl_err); +#if NCRYPTO_USE_OPENSSL3_PROVIDER + const char* fs = nullptr; +#else const char* fs = ERR_func_error_string(ssl_err); +#endif const char* rs = ERR_reason_error_string(ssl_err); if (!Set(env(), obj, env()->library_string(), ls) || !Set(env(), obj, env()->function_string(), fs) || diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc index 8d22350adffaac..711984e5e4f23f 100644 --- a/src/crypto/crypto_util.cc +++ b/src/crypto/crypto_util.cc @@ -12,10 +12,6 @@ #include "util-inl.h" #include "v8.h" -#ifndef OPENSSL_NO_ENGINE -#include -#endif // !OPENSSL_NO_ENGINE - #include "math.h" #if OPENSSL_VERSION_MAJOR >= 3 @@ -544,7 +540,11 @@ Maybe Decorate(Environment* env, if (err == 0) return JustVoid(); // No decoration necessary. const char* ls = ERR_lib_error_string(err); +#if NCRYPTO_USE_OPENSSL3_PROVIDER + const char* fs = nullptr; +#else const char* fs = ERR_func_error_string(err); +#endif const char* rs = ERR_reason_error_string(err); Isolate* isolate = env->isolate(); diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc index 7c680dc2c5eae9..6ed7d7d25db02e 100644 --- a/src/crypto/crypto_x509.cc +++ b/src/crypto/crypto_x509.cc @@ -20,7 +20,6 @@ using ncrypto::BIOPointer; using ncrypto::ClearErrorOnReturn; using ncrypto::DataPointer; using ncrypto::Digest; -using ncrypto::ECKeyPointer; using ncrypto::SSLPointer; using ncrypto::X509Name; using ncrypto::X509Pointer; @@ -668,17 +667,10 @@ static MaybeLocal GetX509NameObject(Environment* env, } MaybeLocal GetPubKey(Environment* env, const ncrypto::Rsa& rsa) { - int size = i2d_RSA_PUBKEY(rsa, nullptr); - CHECK_GE(size, 0); - - auto bs = ArrayBuffer::NewBackingStore( - env->isolate(), size, BackingStoreInitializationMode::kUninitialized); - - auto serialized = reinterpret_cast(bs->Data()); - CHECK_GE(i2d_RSA_PUBKEY(rsa, &serialized), 0); - - auto ab = ArrayBuffer::New(env->isolate(), std::move(bs)); - return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local()); + auto bio = rsa.derPublicKey(); + Local ret; + if (!ToBuffer(env, &bio).ToLocal(&ret)) return {}; + return ret.As(); } MaybeLocal GetModulusString(Environment* env, const BIGNUM* n) { @@ -698,14 +690,13 @@ MaybeLocal GetExponentString(Environment* env, const BIGNUM* e) { return ToV8Value(env->context(), bio); } -MaybeLocal GetECPubKey(Environment* env, - const EC_GROUP* group, - OSSL3_CONST EC_KEY* ec) { - const auto pubkey = ECKeyPointer::GetPublicKey(ec); +MaybeLocal GetECPubKey(Environment* env, const ncrypto::Ec& ec) { + const auto group = ec.getGroup(); + const auto pubkey = ec.getPublicKey(); if (pubkey == nullptr) [[unlikely]] return Undefined(env->isolate()); - return ECPointToBuffer(env, group, pubkey, EC_KEY_get_conv_form(ec)) + return ECPointToBuffer(env, group, pubkey, ec.getPointConversionForm()) .FromMaybe(Local()); } @@ -792,7 +783,7 @@ MaybeLocal X509ToObject(Environment* env, const X509View& cert) { cert.ifEc([&](const ncrypto::Ec& ec) { const auto group = ec.getGroup(); - values[7] = GetECPubKey(env, group, ec); // pubkey + values[7] = GetECPubKey(env, ec); // pubkey values[8] = GetECGroupBits(env, group); // bits const int nid = ec.getCurve(); if (nid != 0) { diff --git a/src/node.cc b/src/node.cc index 4ba019ddca05f4..b368c58734345f 100644 --- a/src/node.cc +++ b/src/node.cc @@ -50,6 +50,11 @@ #if HAVE_OPENSSL #include "ncrypto.h" #include "node_crypto.h" +#if OPENSSL_VERSION_MAJOR >= 3 && !defined(CONF_MFLAGS_IGNORE_MISSING_FILE) +// OpenSSL hides this deprecated macro under OPENSSL_NO_DEPRECATED, but the +// non-deprecated OPENSSL_INIT settings API still accepts the flag value. +#define CONF_MFLAGS_IGNORE_MISSING_FILE 0x10 +#endif #endif #if defined(NODE_HAVE_I18N_SUPPORT) diff --git a/src/node_constants.cc b/src/node_constants.cc index 50ece35429e932..c44886896dc35c 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -39,8 +39,42 @@ #if HAVE_OPENSSL #include #include +#if !defined(RSA_PKCS1_PADDING) +#define RSA_PKCS1_PADDING 1 +#endif +#if !defined(RSA_SSLV23_PADDING) +#define RSA_SSLV23_PADDING 2 +#endif +#if !defined(RSA_NO_PADDING) +#define RSA_NO_PADDING 3 +#endif +#if !defined(RSA_PKCS1_OAEP_PADDING) +#define RSA_PKCS1_OAEP_PADDING 4 +#endif +#if !defined(RSA_X931_PADDING) +#define RSA_X931_PADDING 5 +#endif +#if !defined(RSA_PKCS1_PSS_PADDING) +#define RSA_PKCS1_PSS_PADDING 6 +#endif #ifndef OPENSSL_NO_ENGINE +#if !defined(OPENSSL_IS_BORINGSSL) && OPENSSL_VERSION_MAJOR >= 3 +// Engine constants remain public API while engine implementation lives in the +// dedicated compatibility target. +#define ENGINE_METHOD_RSA (unsigned int)0x0001 +#define ENGINE_METHOD_DSA (unsigned int)0x0002 +#define ENGINE_METHOD_DH (unsigned int)0x0004 +#define ENGINE_METHOD_RAND (unsigned int)0x0008 +#define ENGINE_METHOD_CIPHERS (unsigned int)0x0040 +#define ENGINE_METHOD_DIGESTS (unsigned int)0x0080 +#define ENGINE_METHOD_PKEY_METHS (unsigned int)0x0200 +#define ENGINE_METHOD_PKEY_ASN1_METHS (unsigned int)0x0400 +#define ENGINE_METHOD_EC (unsigned int)0x0800 +#define ENGINE_METHOD_ALL (unsigned int)0xFFFF +#define ENGINE_METHOD_NONE (unsigned int)0x0000 +#else #include +#endif #endif // !OPENSSL_NO_ENGINE #endif // HAVE_OPENSSL diff --git a/test/parallel/test-crypto-dh-curves.js b/test/parallel/test-crypto-dh-curves.js index ddd5ea9377e63f..d17d9d5af11220 100644 --- a/test/parallel/test-crypto-dh-curves.js +++ b/test/parallel/test-crypto-dh-curves.js @@ -5,6 +5,7 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); // Second OAKLEY group, see // https://github.com/nodejs/node-v0.x-archive/issues/2338 and @@ -21,6 +22,12 @@ const bad_dh = process.features.openssl_is_boringssl ? crypto.createDiffieHellman('02', 'hex'); assert.notStrictEqual(bad_dh.verifyError, 0); +if (hasOpenSSL3) { + const smallSafePrime = crypto.createDiffieHellman( + Buffer.from([23]), Buffer.from([2])); + assert.notStrictEqual(smallSafePrime.verifyError, 0); +} + const availableCurves = new Set(crypto.getCurves()); const availableHashes = new Set(crypto.getHashes());