diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index ce2e7b384eb198..0c82efda89ee84 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -15,8 +15,23 @@ #include "dh-primes.h" #endif // OPENSSL_IS_BORINGSSL +// EVP_PKEY_CTX_set_dsa_paramgen_q_bits was added in OpenSSL 1.1.1e. +#if OPENSSL_VERSION_NUMBER < 0x1010105fL +#define EVP_PKEY_CTX_set_dsa_paramgen_q_bits(ctx, qbits) \ + EVP_PKEY_CTX_ctrl((ctx), \ + EVP_PKEY_DSA, \ + EVP_PKEY_OP_PARAMGEN, \ + EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS, \ + (qbits), \ + nullptr) +#endif + namespace ncrypto { namespace { +using BignumCtxPointer = DeleteFnPtr; +using BignumGenCallbackPointer = DeleteFnPtr; +using NetscapeSPKIPointer = DeleteFnPtr; + static constexpr int kX509NameFlagsRFC2253WithinUtf8JSON = XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB & ~ASN1_STRFLGS_ESC_CTRL; } // namespace @@ -87,6 +102,10 @@ DataPointer DataPointer::Alloc(size_t len) { return DataPointer(OPENSSL_zalloc(len), len); } +DataPointer DataPointer::Copy(const Buffer& buffer) { + return DataPointer(OPENSSL_memdup(buffer.data, buffer.len), buffer.len); +} + DataPointer::DataPointer(void* data, size_t length) : data_(data), len_(length) {} @@ -109,6 +128,11 @@ DataPointer::~DataPointer() { reset(); } +void DataPointer::zero() { + if (!data_) return; + OPENSSL_cleanse(data_, len_); +} + void DataPointer::reset(void* data, size_t length) { if (data_ != nullptr) { OPENSSL_clear_free(data_, len_); @@ -131,6 +155,15 @@ Buffer DataPointer::release() { return buf; } +DataPointer DataPointer::resize(size_t len) { + size_t actual_len = std::min(len_, len); + auto buf = release(); + if (actual_len == len_) return DataPointer(buf); + buf.data = OPENSSL_realloc(buf.data, actual_len); + buf.len = actual_len; + return DataPointer(buf); +} + // ============================================================================ bool isFipsEnabled() { #if OPENSSL_VERSION_MAJOR >= 3 @@ -1132,6 +1165,52 @@ Result X509Pointer::Parse( return Result(ERR_get_error()); } +bool X509View::enumUsages(UsageCallback callback) const { + if (cert_ == nullptr) return false; + StackOfASN1 eku(static_cast( + X509_get_ext_d2i(cert_, NID_ext_key_usage, nullptr, nullptr))); + if (!eku) return false; + const int count = sk_ASN1_OBJECT_num(eku.get()); + char buf[256]{}; + + int j = 0; + for (int i = 0; i < count; i++) { + if (OBJ_obj2txt(buf, sizeof(buf), sk_ASN1_OBJECT_value(eku.get(), i), 1) >= + 0) { + callback(buf); + } + } + return true; +} + +bool X509View::ifRsa(KeyCallback callback) const { + if (cert_ == nullptr) return true; + OSSL3_CONST EVP_PKEY* pkey = X509_get0_pubkey(cert_); + OSSL3_CONST RSA* rsa = nullptr; + auto id = EVP_PKEY_id(pkey); + if (id == EVP_PKEY_RSA || id == EVP_PKEY_RSA2 || id == EVP_PKEY_RSA_PSS) { + Rsa rsa(EVP_PKEY_get0_RSA(pkey)); + if (!rsa) [[unlikely]] + return true; + return callback(rsa); + } + return true; +} + +bool X509View::ifEc(KeyCallback callback) const { + if (cert_ == nullptr) return true; + OSSL3_CONST EVP_PKEY* pkey = X509_get0_pubkey(cert_); + OSSL3_CONST EC_KEY* ec = nullptr; + auto id = EVP_PKEY_id(pkey); + if (id == EVP_PKEY_EC) { + Ec ec(EVP_PKEY_get0_EC_KEY(pkey)); + if (!ec) [[unlikely]] + return true; + return callback(ec); + } + return true; +} + X509Pointer X509Pointer::IssuerFrom(const SSLPointer& ssl, const X509View& view) { return IssuerFrom(SSL_get_SSL_CTX(ssl.get()), view); @@ -1493,7 +1572,7 @@ DataPointer DHPointer::stateless(const EVPKeyPointer& ourKey, size_t out_size; if (!ourKey || !theirKey) return {}; - EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(ourKey.get(), nullptr)); + auto ctx = EVPKeyCtxPointer::New(ourKey); if (!ctx || EVP_PKEY_derive_init(ctx.get()) <= 0 || EVP_PKEY_derive_set_peer(ctx.get(), theirKey.get()) <= 0 || EVP_PKEY_derive(ctx.get(), nullptr, &out_size) <= 0) { @@ -1522,9 +1601,18 @@ DataPointer DHPointer::stateless(const EVPKeyPointer& ourKey, // KDF const EVP_MD* getDigestByName(const std::string_view name) { + // Historically, "dss1" and "DSS1" were DSA aliases for SHA-1 + // exposed through the public API. + if (name == "dss1" || name == "DSS1") [[unlikely]] { + return EVP_sha1(); + } return EVP_get_digestbyname(name.data()); } +const EVP_CIPHER* getCipherByName(const std::string_view name) { + return EVP_get_cipherbyname(name.data()); +} + bool checkHkdfLength(const EVP_MD* md, size_t length) { // HKDF-Expand computes up to 255 HMAC blocks, each having as many bits as // the output of the hash function. 255 is a hard limit because HKDF appends @@ -1547,8 +1635,7 @@ DataPointer hkdf(const EVP_MD* md, return {}; } - EVPKeyCtxPointer ctx = - EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr)); + auto ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_HKDF); if (!ctx || !EVP_PKEY_derive_init(ctx.get()) || !EVP_PKEY_CTX_set_hkdf_md(ctx.get(), md) || !EVP_PKEY_CTX_add1_hkdf_info(ctx.get(), info.data, info.len)) { @@ -1704,6 +1791,26 @@ EVPKeyPointer EVPKeyPointer::NewRawPrivate( EVP_PKEY_new_raw_private_key(id, nullptr, data.data, data.len)); } +EVPKeyPointer EVPKeyPointer::NewDH(DHPointer&& dh) { + if (!dh) return {}; + auto key = New(); + if (!key) return {}; + if (EVP_PKEY_assign_DH(key.get(), dh.get())) { + dh.release(); + } + return key; +} + +EVPKeyPointer EVPKeyPointer::NewRSA(RSAPointer&& rsa) { + if (!rsa) return {}; + auto key = New(); + if (!key) return {}; + if (EVP_PKEY_assign_RSA(key.get(), rsa.get())) { + rsa.release(); + } + return key; +} + EVPKeyPointer::EVPKeyPointer(EVP_PKEY* pkey) : pkey_(pkey) {} EVPKeyPointer::EVPKeyPointer(EVPKeyPointer&& other) noexcept @@ -1757,7 +1864,7 @@ size_t EVPKeyPointer::size() const { EVPKeyCtxPointer EVPKeyPointer::newCtx() const { if (!pkey_) return {}; - return EVPKeyCtxPointer(EVP_PKEY_CTX_new(get(), nullptr)); + return EVPKeyCtxPointer::New(*this); } size_t EVPKeyPointer::rawPublicKeySize() const { @@ -2230,6 +2337,84 @@ Result EVPKeyPointer::writePublicKey( return bio; } +bool EVPKeyPointer::isRsaVariant() const { + if (!pkey_) return false; + int type = id(); + return type == EVP_PKEY_RSA || type == EVP_PKEY_RSA2 || + type == EVP_PKEY_RSA_PSS; +} + +bool EVPKeyPointer::isOneShotVariant() const { + if (!pkey_) return false; + int type = id(); + return type == EVP_PKEY_ED25519 || type == EVP_PKEY_ED448; +} + +bool EVPKeyPointer::isSigVariant() const { + if (!pkey_) return false; + int type = id(); + return type == EVP_PKEY_EC || type == EVP_PKEY_DSA; +} + +int EVPKeyPointer::getDefaultSignPadding() const { + return id() == EVP_PKEY_RSA_PSS ? RSA_PKCS1_PSS_PADDING : RSA_PKCS1_PADDING; +} + +std::optional EVPKeyPointer::getBytesOfRS() const { + if (!pkey_) return std::nullopt; + int bits, id = base_id(); + + if (id == EVP_PKEY_DSA) { + 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)); + } else if (id == EVP_PKEY_EC) { + bits = EC_GROUP_order_bits(ECKeyPointer::GetGroup(*this)); + } else { + return std::nullopt; + } + + return (bits + 7) / 8; +} + +EVPKeyPointer::operator Rsa() const { + int type = id(); + if (type != EVP_PKEY_RSA && type != EVP_PKEY_RSA_PSS) return {}; + + // TODO(tniessen): Remove the "else" branch once we drop support for OpenSSL + // versions older than 1.1.1e via FIPS / dynamic linking. + const RSA* rsa; + if (OPENSSL_VERSION_NUMBER >= 0x1010105fL) { + rsa = EVP_PKEY_get0_RSA(get()); + } else { + rsa = static_cast(EVP_PKEY_get0(get())); + } + if (rsa == nullptr) return {}; + return Rsa(rsa); +} + +bool EVPKeyPointer::validateDsaParameters() const { + if (!pkey_) return false; + /* Validate DSA2 parameters from FIPS 186-4 */ +#if OPENSSL_VERSION_MAJOR >= 3 + if (EVP_default_properties_is_fips_enabled(nullptr) && EVP_PKEY_DSA == id()) { +#else + if (FIPS_mode() && EVP_PKEY_DSA == id()) { +#endif + 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); + + return (L == 1024 && N == 160) || (L == 2048 && N == 224) || + (L == 2048 && N == 256) || (L == 3072 && N == 256); + } + + return true; +} + // ============================================================================ SSLPointer::SSLPointer(SSL* ssl) : ssl_(ssl) {} @@ -2883,4 +3068,752 @@ ECKeyPointer ECKeyPointer::New(const EC_GROUP* group) { return ptr; } +// ============================================================================ + +EVPKeyCtxPointer::EVPKeyCtxPointer() : ctx_(nullptr) {} + +EVPKeyCtxPointer::EVPKeyCtxPointer(EVP_PKEY_CTX* ctx) : ctx_(ctx) {} + +EVPKeyCtxPointer::EVPKeyCtxPointer(EVPKeyCtxPointer&& other) noexcept + : ctx_(other.release()) {} + +EVPKeyCtxPointer& EVPKeyCtxPointer::operator=( + EVPKeyCtxPointer&& other) noexcept { + ctx_.reset(other.release()); + return *this; +} + +EVPKeyCtxPointer::~EVPKeyCtxPointer() { + reset(); +} + +void EVPKeyCtxPointer::reset(EVP_PKEY_CTX* ctx) { + ctx_.reset(ctx); +} + +EVP_PKEY_CTX* EVPKeyCtxPointer::release() { + return ctx_.release(); +} + +EVPKeyCtxPointer EVPKeyCtxPointer::New(const EVPKeyPointer& key) { + if (!key) return {}; + return EVPKeyCtxPointer(EVP_PKEY_CTX_new(key.get(), nullptr)); +} + +EVPKeyCtxPointer EVPKeyCtxPointer::NewFromID(int id) { + return EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(id, nullptr)); +} + +bool EVPKeyCtxPointer::initForDerive(const EVPKeyPointer& peer) { + if (!ctx_) return false; + if (EVP_PKEY_derive_init(ctx_.get()) != 1) return false; + return EVP_PKEY_derive_set_peer(ctx_.get(), peer.get()) == 1; +} + +bool EVPKeyCtxPointer::initForKeygen() { + if (!ctx_) return false; + return EVP_PKEY_keygen_init(ctx_.get()) == 1; +} + +bool EVPKeyCtxPointer::initForParamgen() { + if (!ctx_) return false; + return EVP_PKEY_paramgen_init(ctx_.get()) == 1; +} + +int EVPKeyCtxPointer::initForVerify() { + if (!ctx_) return 0; + return EVP_PKEY_verify_init(ctx_.get()); +} + +int EVPKeyCtxPointer::initForSign() { + if (!ctx_) return 0; + return EVP_PKEY_sign_init(ctx_.get()); +} + +bool EVPKeyCtxPointer::setDhParameters(int prime_size, uint32_t generator) { + if (!ctx_) return false; + return EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx_.get(), prime_size) == 1 && + EVP_PKEY_CTX_set_dh_paramgen_generator(ctx_.get(), generator) == 1; +} + +bool EVPKeyCtxPointer::setDsaParameters(uint32_t bits, + std::optional q_bits) { + if (!ctx_) return false; + if (EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx_.get(), bits) != 1) { + return false; + } + if (q_bits.has_value() && + EVP_PKEY_CTX_set_dsa_paramgen_q_bits(ctx_.get(), q_bits.value()) != 1) { + return false; + } + return true; +} + +bool EVPKeyCtxPointer::setEcParameters(int curve, int encoding) { + if (!ctx_) return false; + return EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx_.get(), curve) == 1 && + EVP_PKEY_CTX_set_ec_param_enc(ctx_.get(), encoding) == 1; +} + +bool EVPKeyCtxPointer::setRsaOaepMd(const EVP_MD* md) { + if (md == nullptr || !ctx_) return false; + return EVP_PKEY_CTX_set_rsa_oaep_md(ctx_.get(), md) > 0; +} + +bool EVPKeyCtxPointer::setRsaMgf1Md(const EVP_MD* md) { + if (md == nullptr || !ctx_) return false; + return EVP_PKEY_CTX_set_rsa_mgf1_md(ctx_.get(), md) > 0; +} + +bool EVPKeyCtxPointer::setRsaPadding(int padding) { + return setRsaPadding(ctx_.get(), padding, std::nullopt); +} + +bool EVPKeyCtxPointer::setRsaPadding(EVP_PKEY_CTX* ctx, + int padding, + std::optional salt_len) { + if (ctx == nullptr) return false; + if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0) { + return false; + } + if (padding == RSA_PKCS1_PSS_PADDING && salt_len.has_value()) { + return EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, salt_len.value()) > 0; + } + return true; +} + +bool EVPKeyCtxPointer::setRsaKeygenBits(int bits) { + if (!ctx_) return false; + return EVP_PKEY_CTX_set_rsa_keygen_bits(ctx_.get(), bits) == 1; +} + +bool EVPKeyCtxPointer::setRsaKeygenPubExp(BignumPointer&& e) { + if (!ctx_) return false; + 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; +} + +bool EVPKeyCtxPointer::setRsaPssKeygenMd(const EVP_MD* md) { + if (md == nullptr || !ctx_) return false; + return EVP_PKEY_CTX_set_rsa_pss_keygen_md(ctx_.get(), md) > 0; +} + +bool EVPKeyCtxPointer::setRsaPssKeygenMgf1Md(const EVP_MD* md) { + if (md == nullptr || !ctx_) return false; + return EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md(ctx_.get(), md) > 0; +} + +bool EVPKeyCtxPointer::setRsaPssSaltlen(int salt_len) { + if (!ctx_) return false; + return EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen(ctx_.get(), salt_len) > 0; +} + +bool EVPKeyCtxPointer::setRsaImplicitRejection() { + if (!ctx_) return false; + return EVP_PKEY_CTX_ctrl_str( + ctx_.get(), "rsa_pkcs1_implicit_rejection", "1") > 0; + // From the doc -2 means that the option is not supported. + // The default for the option is enabled and if it has been + // specifically disabled we want to respect that so we will + // not throw an error if the option is supported regardless + // of how it is set. The call to set the value + // will not affect what is used since a different context is + // used in the call if the option is supported +} + +bool EVPKeyCtxPointer::setRsaOaepLabel(DataPointer&& data) { + if (!ctx_) return false; + if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx_.get(), + static_cast(data.get()), + data.size()) > 0) { + // The ctx_ takes ownership of data on success. + data.release(); + return true; + } + return false; +} + +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())) == + 1; +} + +bool EVPKeyCtxPointer::initForEncrypt() { + if (!ctx_) return false; + return EVP_PKEY_encrypt_init(ctx_.get()) == 1; +} + +bool EVPKeyCtxPointer::initForDecrypt() { + if (!ctx_) return false; + return EVP_PKEY_decrypt_init(ctx_.get()) == 1; +} + +DataPointer EVPKeyCtxPointer::derive() const { + if (!ctx_) return {}; + size_t len = 0; + if (EVP_PKEY_derive(ctx_.get(), nullptr, &len) != 1) return {}; + auto data = DataPointer::Alloc(len); + if (!data) return {}; + if (EVP_PKEY_derive( + ctx_.get(), static_cast(data.get()), &len) != 1) { + return {}; + } + return data; +} + +EVPKeyPointer EVPKeyCtxPointer::paramgen() const { + if (!ctx_) return {}; + EVP_PKEY* key = nullptr; + if (EVP_PKEY_paramgen(ctx_.get(), &key) != 1) return {}; + return EVPKeyPointer(key); +} + +bool EVPKeyCtxPointer::publicCheck() const { + if (!ctx_) return false; +#if OPENSSL_VERSION_MAJOR >= 3 + return EVP_PKEY_public_check_quick(ctx_.get()) == 1; +#else + return EVP_PKEY_public_check(ctx_.get()) == 1; +#endif +} + +bool EVPKeyCtxPointer::privateCheck() const { + if (!ctx_) return false; + return EVP_PKEY_check(ctx_.get()) == 1; +} + +bool EVPKeyCtxPointer::verify(const Buffer& sig, + const Buffer& data) { + if (!ctx_) return false; + return EVP_PKEY_verify(ctx_.get(), sig.data, sig.len, data.data, data.len) == + 1; +} + +DataPointer EVPKeyCtxPointer::sign(const Buffer& data) { + if (!ctx_) return {}; + size_t len = 0; + if (EVP_PKEY_sign(ctx_.get(), nullptr, &len, data.data, data.len) != 1) { + return {}; + } + auto buf = DataPointer::Alloc(len); + if (!buf) return {}; + if (EVP_PKEY_sign(ctx_.get(), + static_cast(buf.get()), + &len, + data.data, + data.len) != 1) { + return {}; + } + return buf.resize(len); +} + +bool EVPKeyCtxPointer::signInto(const Buffer& data, + Buffer* sig) { + if (!ctx_) return false; + size_t len = sig->len; + if (EVP_PKEY_sign(ctx_.get(), sig->data, &len, data.data, data.len) != 1) { + return false; + } + sig->len = len; + return true; +} + +// ============================================================================ + +namespace { + +using EVP_PKEY_cipher_init_t = int(EVP_PKEY_CTX* ctx); +using EVP_PKEY_cipher_t = int(EVP_PKEY_CTX* ctx, + unsigned char* out, + size_t* outlen, + const unsigned char* in, + size_t inlen); + +template +DataPointer RSA_Cipher(const EVPKeyPointer& key, + const Rsa::CipherParams& params, + const Buffer in) { + if (!key) return {}; + EVPKeyCtxPointer ctx = key.newCtx(); + + if (!ctx || init(ctx.get()) <= 0 || !ctx.setRsaPadding(params.padding) || + (params.digest != nullptr && (!ctx.setRsaOaepMd(params.digest) || + !ctx.setRsaMgf1Md(params.digest)))) { + return {}; + } + + if (params.label.len != 0 && params.label.data != nullptr && + !ctx.setRsaOaepLabel(DataPointer::Copy(params.label))) { + return {}; + } + + size_t out_len = 0; + if (cipher(ctx.get(), + nullptr, + &out_len, + reinterpret_cast(in.data), + in.len) <= 0) { + return {}; + } + + auto buf = DataPointer::Alloc(out_len); + if (!buf) return {}; + + if (cipher(ctx.get(), + static_cast(buf.get()), + &out_len, + static_cast(in.data), + in.len) <= 0) { + return {}; + } + + return buf.resize(out_len); +} + +template +DataPointer CipherImpl(const EVPKeyPointer& key, + const Rsa::CipherParams& params, + const Buffer in) { + if (!key) return {}; + EVPKeyCtxPointer ctx = key.newCtx(); + if (!ctx || init(ctx.get()) <= 0 || !ctx.setRsaPadding(params.padding) || + (params.digest != nullptr && !ctx.setRsaOaepMd(params.digest))) { + return {}; + } + + if (params.label.len != 0 && params.label.data != nullptr && + !ctx.setRsaOaepLabel(DataPointer::Copy(params.label))) { + return {}; + } + + size_t out_len = 0; + if (cipher(ctx.get(), + nullptr, + &out_len, + static_cast(in.data), + in.len) <= 0) { + return {}; + } + + auto buf = DataPointer::Alloc(out_len); + if (!buf) return {}; + + if (cipher(ctx.get(), + static_cast(buf.get()), + &out_len, + static_cast(in.data), + in.len) <= 0) { + return {}; + } + + return buf.resize(out_len); +} +} // namespace + +Rsa::Rsa() : rsa_(nullptr) {} + +Rsa::Rsa(const RSA* ptr) : rsa_(ptr) {} + +const Rsa::PublicKey Rsa::getPublicKey() const { + if (rsa_ == nullptr) return {}; + PublicKey key; + RSA_get0_key(rsa_, &key.n, &key.e, &key.d); + return key; +} + +const Rsa::PrivateKey Rsa::getPrivateKey() const { + 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; +} + +const std::optional Rsa::getPssParams() const { + if (rsa_ == nullptr) return std::nullopt; + const RSA_PSS_PARAMS* params = RSA_get0_pss_params(rsa_); + if (params == nullptr) return std::nullopt; + Rsa::PssParams ret{ + .digest = OBJ_nid2ln(NID_sha1), + .mgf1_digest = OBJ_nid2ln(NID_sha1), + .salt_length = 20, + }; + + if (params->hashAlgorithm != nullptr) { + const ASN1_OBJECT* hash_obj; + X509_ALGOR_get0(&hash_obj, nullptr, nullptr, params->hashAlgorithm); + ret.digest = OBJ_nid2ln(OBJ_obj2nid(hash_obj)); + } + + if (params->maskGenAlgorithm != nullptr) { + const ASN1_OBJECT* mgf_obj; + X509_ALGOR_get0(&mgf_obj, nullptr, nullptr, params->maskGenAlgorithm); + int mgf_nid = OBJ_obj2nid(mgf_obj); + if (mgf_nid == NID_mgf1) { + const ASN1_OBJECT* mgf1_hash_obj; + X509_ALGOR_get0(&mgf1_hash_obj, nullptr, nullptr, params->maskHash); + ret.mgf1_digest = OBJ_nid2ln(OBJ_obj2nid(mgf1_hash_obj)); + } + } + + if (params->saltLength != nullptr) { + if (ASN1_INTEGER_get_int64(&ret.salt_length, params->saltLength) != 1) { + return std::nullopt; + } + } + return ret; +} + +bool Rsa::setPublicKey(BignumPointer&& n, BignumPointer&& e) { + if (!n || !e) return false; + if (RSA_set0_key(const_cast(rsa_), n.get(), e.get(), nullptr) == 1) { + n.release(); + e.release(); + return true; + } + return false; +} + +bool Rsa::setPrivateKey(BignumPointer&& d, + BignumPointer&& q, + BignumPointer&& p, + BignumPointer&& dp, + BignumPointer&& dq, + BignumPointer&& qi) { + if (!RSA_set0_key(const_cast(rsa_), nullptr, nullptr, d.get())) { + return false; + } + d.release(); + + if (!RSA_set0_factors(const_cast(rsa_), p.get(), q.get())) { + return false; + } + p.release(); + q.release(); + + if (!RSA_set0_crt_params( + const_cast(rsa_), dp.get(), dq.get(), qi.get())) { + return false; + } + dp.release(); + dq.release(); + qi.release(); + return true; +} + +DataPointer Rsa::encrypt(const EVPKeyPointer& key, + const Rsa::CipherParams& params, + const Buffer in) { + if (!key) return {}; + return RSA_Cipher(key, params, in); +} + +DataPointer Rsa::decrypt(const EVPKeyPointer& key, + const Rsa::CipherParams& params, + const Buffer in) { + if (!key) return {}; + return RSA_Cipher(key, params, in); +} + +DataPointer Cipher::encrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in) { + // public operation + return CipherImpl(key, params, in); +} + +DataPointer Cipher::decrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in) { + // private operation + return CipherImpl(key, params, in); +} + +DataPointer Cipher::sign(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in) { + // private operation + return CipherImpl(key, params, in); +} + +DataPointer Cipher::recover(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in) { + // public operation + return CipherImpl( + key, params, in); +} + +// ============================================================================ + +Ec::Ec() : ec_(nullptr) {} + +Ec::Ec(const EC_KEY* key) : ec_(key) {} + +const EC_GROUP* Ec::getGroup() const { + return ECKeyPointer::GetGroup(ec_); +} + +int Ec::getCurve() const { + return EC_GROUP_get_curve_name(getGroup()); +} + +// ============================================================================ + +EVPMDCtxPointer::EVPMDCtxPointer() : ctx_(nullptr) {} + +EVPMDCtxPointer::EVPMDCtxPointer(EVP_MD_CTX* ctx) : ctx_(ctx) {} + +EVPMDCtxPointer::EVPMDCtxPointer(EVPMDCtxPointer&& other) noexcept + : ctx_(other.release()) {} + +EVPMDCtxPointer& EVPMDCtxPointer::operator=(EVPMDCtxPointer&& other) noexcept { + ctx_.reset(other.release()); + return *this; +} + +EVPMDCtxPointer::~EVPMDCtxPointer() { + reset(); +} + +void EVPMDCtxPointer::reset(EVP_MD_CTX* ctx) { + ctx_.reset(ctx); +} + +EVP_MD_CTX* EVPMDCtxPointer::release() { + return ctx_.release(); +} + +bool EVPMDCtxPointer::digestInit(const EVP_MD* digest) { + if (!ctx_) return false; + return EVP_DigestInit_ex(ctx_.get(), digest, nullptr) > 0; +} + +bool EVPMDCtxPointer::digestUpdate(const Buffer& in) { + if (!ctx_) return false; + return EVP_DigestUpdate(ctx_.get(), in.data, in.len) > 0; +} + +DataPointer EVPMDCtxPointer::digestFinal(size_t length) { + if (!ctx_) return {}; + + auto buf = DataPointer::Alloc(length); + if (!buf) return {}; + + Buffer buffer = buf; + + if (!digestFinalInto(&buffer)) [[unlikely]] { + return {}; + } + + return buf; +} + +bool EVPMDCtxPointer::digestFinalInto(Buffer* buf) { + if (!ctx_) false; + + auto ptr = static_cast(buf->data); + + int ret = (buf->len == getExpectedSize()) + ? EVP_DigestFinal_ex(ctx_.get(), ptr, nullptr) + : EVP_DigestFinalXOF(ctx_.get(), ptr, buf->len); + + if (ret != 1) [[unlikely]] + return false; + + return true; +} + +size_t EVPMDCtxPointer::getExpectedSize() { + if (!ctx_) return 0; + return EVP_MD_CTX_size(ctx_.get()); +} + +size_t EVPMDCtxPointer::getDigestSize() const { + return EVP_MD_size(getDigest()); +} + +const EVP_MD* EVPMDCtxPointer::getDigest() const { + if (!ctx_) return nullptr; + return EVP_MD_CTX_md(ctx_.get()); +} + +bool EVPMDCtxPointer::hasXofFlag() const { + if (!ctx_) return false; + return (EVP_MD_flags(getDigest()) & EVP_MD_FLAG_XOF) == EVP_MD_FLAG_XOF; +} + +bool EVPMDCtxPointer::copyTo(const EVPMDCtxPointer& other) const { + if (!ctx_ || !other) return {}; + if (EVP_MD_CTX_copy(other.get(), ctx_.get()) != 1) return false; + return true; +} + +std::optional EVPMDCtxPointer::signInit(const EVPKeyPointer& key, + const EVP_MD* digest) { + EVP_PKEY_CTX* ctx = nullptr; + if (!EVP_DigestSignInit(ctx_.get(), &ctx, digest, nullptr, key.get())) { + return std::nullopt; + } + return ctx; +} + +std::optional EVPMDCtxPointer::verifyInit( + const EVPKeyPointer& key, const EVP_MD* digest) { + EVP_PKEY_CTX* ctx = nullptr; + if (!EVP_DigestVerifyInit(ctx_.get(), &ctx, digest, nullptr, key.get())) { + return std::nullopt; + } + return ctx; +} + +DataPointer EVPMDCtxPointer::signOneShot( + const Buffer& buf) const { + if (!ctx_) return {}; + size_t len; + if (!EVP_DigestSign(ctx_.get(), nullptr, &len, buf.data, buf.len)) { + return {}; + } + auto data = DataPointer::Alloc(len); + if (!data) [[unlikely]] + return {}; + + if (!EVP_DigestSign(ctx_.get(), + static_cast(data.get()), + &len, + buf.data, + buf.len)) { + return {}; + } + return data; +} + +DataPointer EVPMDCtxPointer::sign( + const Buffer& buf) const { + if (!ctx_) [[unlikely]] + return {}; + size_t len; + if (!EVP_DigestSignUpdate(ctx_.get(), buf.data, buf.len) || + !EVP_DigestSignFinal(ctx_.get(), nullptr, &len)) { + return {}; + } + auto data = DataPointer::Alloc(len); + if (!data) [[unlikely]] + return {}; + if (!EVP_DigestSignFinal( + ctx_.get(), static_cast(data.get()), &len)) { + return {}; + } + return data.resize(len); +} + +bool EVPMDCtxPointer::verify(const Buffer& buf, + const Buffer& sig) const { + if (!ctx_) return false; + int ret = EVP_DigestVerify(ctx_.get(), sig.data, sig.len, buf.data, buf.len); + return ret == 1; +} + +EVPMDCtxPointer EVPMDCtxPointer::New() { + return EVPMDCtxPointer(EVP_MD_CTX_new()); +} + +// ============================================================================ + +bool extractP1363(const Buffer& buf, + unsigned char* dest, + size_t n) { + auto asn1_sig = ECDSASigPointer::Parse(buf); + if (!asn1_sig) return false; + + return BignumPointer::EncodePaddedInto(asn1_sig.r(), dest, n) > 0 && + BignumPointer::EncodePaddedInto(asn1_sig.s(), dest + n, n) > 0; +} + +// ============================================================================ + +HMACCtxPointer::HMACCtxPointer() : ctx_(nullptr) {} + +HMACCtxPointer::HMACCtxPointer(HMAC_CTX* ctx) : ctx_(ctx) {} + +HMACCtxPointer::HMACCtxPointer(HMACCtxPointer&& other) noexcept + : ctx_(other.release()) {} + +HMACCtxPointer& HMACCtxPointer::operator=(HMACCtxPointer&& other) noexcept { + ctx_.reset(other.release()); + return *this; +} + +HMACCtxPointer::~HMACCtxPointer() { + reset(); +} + +void HMACCtxPointer::reset(HMAC_CTX* ctx) { + ctx_.reset(ctx); +} + +HMAC_CTX* HMACCtxPointer::release() { + return ctx_.release(); +} + +bool HMACCtxPointer::init(const Buffer& buf, const EVP_MD* md) { + if (!ctx_) return false; + return HMAC_Init_ex(ctx_.get(), buf.data, buf.len, md, nullptr) == 1; +} + +bool HMACCtxPointer::update(const Buffer& buf) { + if (!ctx_) return false; + return HMAC_Update(ctx_.get(), + static_cast(buf.data), + buf.len) == 1; +} + +DataPointer HMACCtxPointer::digest() { + auto data = DataPointer::Alloc(EVP_MAX_MD_SIZE); + if (!data) return {}; + Buffer buf = data; + if (!digestInto(&buf)) return {}; + return data.resize(buf.len); +} + +bool HMACCtxPointer::digestInto(Buffer* buf) { + if (!ctx_) return false; + + unsigned int len = buf->len; + if (!HMAC_Final(ctx_.get(), static_cast(buf->data), &len)) { + return false; + } + buf->len = len; + return true; +} + +HMACCtxPointer HMACCtxPointer::New() { + return HMACCtxPointer(HMAC_CTX_new()); +} + +DataPointer hashDigest(const Buffer& buf, + const EVP_MD* md) { + if (md == nullptr) return {}; + size_t md_len = EVP_MD_size(md); + unsigned int result_size; + auto data = DataPointer::Alloc(md_len); + if (!data) return {}; + + if (!EVP_Digest(buf.data, + buf.len, + reinterpret_cast(data.get()), + &result_size, + md, + nullptr)) { + return {}; + } + + return data.resize(result_size); +} + } // namespace ncrypto diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index e5bf2b529bf239..3b853c8c17c855 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -201,18 +201,28 @@ struct FunctionDeleter { template using DeleteFnPtr = typename FunctionDeleter::Pointer; -using BignumCtxPointer = DeleteFnPtr; -using BignumGenCallbackPointer = DeleteFnPtr; -using EVPKeyCtxPointer = DeleteFnPtr; -using EVPMDCtxPointer = DeleteFnPtr; -using HMACCtxPointer = DeleteFnPtr; -using NetscapeSPKIPointer = DeleteFnPtr; using PKCS8Pointer = DeleteFnPtr; using RSAPointer = DeleteFnPtr; using SSLSessionPointer = DeleteFnPtr; +class BIOPointer; +class BignumPointer; class CipherCtxPointer; +class DataPointer; +class DHPointer; class ECKeyPointer; +class EVPKeyPointer; +class EVPMDCtxPointer; +class SSLCtxPointer; +class SSLPointer; +class X509View; +class X509Pointer; +class ECDSASigPointer; +class ECGroupPointer; +class ECPointPointer; +class ECKeyPointer; +class Rsa; +class Ec; struct StackOfXASN1Deleter { void operator()(STACK_OF(ASN1_OBJECT) * p) const { @@ -228,6 +238,9 @@ struct Buffer { size_t len = 0; }; +DataPointer hashDigest(const Buffer& data, + const EVP_MD* md); + class Cipher final { public: Cipher() = default; @@ -258,15 +271,108 @@ class Cipher final { static const Cipher FromNid(int nid); static const Cipher FromCtx(const CipherCtxPointer& ctx); + struct CipherParams { + int padding; + const EVP_MD* digest; + const Buffer label; + }; + + static DataPointer encrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + static DataPointer decrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + + static DataPointer sign(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + + static DataPointer recover(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + private: const EVP_CIPHER* cipher_ = nullptr; }; +// ============================================================================ +// RSA + +class Rsa final { + public: + Rsa(); + Rsa(const RSA* rsa); + NCRYPTO_DISALLOW_COPY_AND_MOVE(Rsa) + + inline operator bool() const { return rsa_ != nullptr; } + inline operator const RSA*() const { return rsa_; } + + struct PublicKey { + const BIGNUM* n; + const BIGNUM* e; + const BIGNUM* d; + }; + struct PrivateKey { + const BIGNUM* p; + const BIGNUM* q; + const BIGNUM* dp; + const BIGNUM* dq; + const BIGNUM* qi; + }; + struct PssParams { + std::string_view digest = "sha1"; + std::optional mgf1_digest = "sha1"; + int64_t salt_length = 20; + }; + + const PublicKey getPublicKey() const; + const PrivateKey getPrivateKey() const; + const std::optional getPssParams() const; + + bool setPublicKey(BignumPointer&& n, BignumPointer&& e); + bool setPrivateKey(BignumPointer&& d, + BignumPointer&& q, + BignumPointer&& p, + BignumPointer&& dp, + BignumPointer&& dq, + BignumPointer&& qi); + + using CipherParams = Cipher::CipherParams; + + static DataPointer encrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + static DataPointer decrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + + private: + const RSA* rsa_; +}; + +class Ec final { + public: + Ec(); + Ec(const EC_KEY* key); + NCRYPTO_DISALLOW_COPY_AND_MOVE(Ec) + + const EC_GROUP* getGroup() const; + int getCurve() const; + + inline operator bool() const { return ec_ != nullptr; } + inline operator const EC_KEY*() const { return ec_; } + + private: + const EC_KEY* ec_ = nullptr; +}; + // A managed pointer to a buffer of data. When destroyed the underlying // buffer will be freed. class DataPointer final { public: static DataPointer Alloc(size_t len); + static DataPointer Copy(const Buffer& buffer); DataPointer() = default; explicit DataPointer(void* data, size_t len); @@ -283,6 +389,11 @@ class DataPointer final { void reset(void* data = nullptr, size_t len = 0); void reset(const Buffer& buffer); + // Sets the underlying data buffer to all zeros. + void zero(); + + DataPointer resize(size_t len); + // Releases ownership of the underlying data buffer. It is the caller's // responsibility to ensure the buffer is appropriately freed. Buffer release(); @@ -471,6 +582,74 @@ class CipherCtxPointer final { DeleteFnPtr ctx_; }; +class EVPKeyCtxPointer final { + public: + EVPKeyCtxPointer(); + explicit EVPKeyCtxPointer(EVP_PKEY_CTX* ctx); + EVPKeyCtxPointer(EVPKeyCtxPointer&& other) noexcept; + EVPKeyCtxPointer& operator=(EVPKeyCtxPointer&& other) noexcept; + NCRYPTO_DISALLOW_COPY(EVPKeyCtxPointer) + ~EVPKeyCtxPointer(); + + inline bool operator==(std::nullptr_t) const noexcept { + return ctx_ == nullptr; + } + inline operator bool() const { return ctx_ != nullptr; } + inline EVP_PKEY_CTX* get() const { return ctx_.get(); } + void reset(EVP_PKEY_CTX* ctx = nullptr); + EVP_PKEY_CTX* release(); + + bool initForDerive(const EVPKeyPointer& peer); + DataPointer derive() const; + + bool initForParamgen(); + bool setDhParameters(int prime_size, uint32_t generator); + bool setDsaParameters(uint32_t bits, std::optional q_bits); + bool setEcParameters(int curve, int encoding); + + bool setRsaOaepMd(const EVP_MD* md); + bool setRsaMgf1Md(const EVP_MD* md); + bool setRsaPadding(int padding); + bool setRsaKeygenPubExp(BignumPointer&& e); + bool setRsaKeygenBits(int bits); + bool setRsaPssKeygenMd(const EVP_MD* md); + bool setRsaPssKeygenMgf1Md(const EVP_MD* md); + bool setRsaPssSaltlen(int salt_len); + bool setRsaImplicitRejection(); + bool setRsaOaepLabel(DataPointer&& data); + + bool setSignatureMd(const EVPMDCtxPointer& md); + + bool publicCheck() const; + bool privateCheck() const; + + bool verify(const Buffer& sig, + const Buffer& data); + DataPointer sign(const Buffer& data); + bool signInto(const Buffer& data, + Buffer* sig); + + static constexpr int kDefaultRsaExponent = 0x10001; + + static bool setRsaPadding(EVP_PKEY_CTX* ctx, + int padding, + std::optional salt_len = std::nullopt); + + EVPKeyPointer paramgen() const; + + bool initForEncrypt(); + bool initForDecrypt(); + bool initForKeygen(); + int initForVerify(); + int initForSign(); + + static EVPKeyCtxPointer New(const EVPKeyPointer& key); + static EVPKeyCtxPointer NewFromID(int id); + + private: + DeleteFnPtr ctx_; +}; + class EVPKeyPointer final { public: static EVPKeyPointer New(); @@ -478,6 +657,8 @@ class EVPKeyPointer final { const Buffer& data); static EVPKeyPointer NewRawPrivate(int id, const Buffer& data); + static EVPKeyPointer NewDH(DHPointer&& dh); + static EVPKeyPointer NewRSA(RSAPointer&& rsa); enum class PKEncodingType { // RSAPublicKey / RSAPrivateKey according to PKCS#1. @@ -578,6 +759,15 @@ class EVPKeyPointer final { static bool IsRSAPrivateKey(const Buffer& buffer); + std::optional getBytesOfRS() const; + int getDefaultSignPadding() const; + operator Rsa() const; + + bool isRsaVariant() const; + bool isOneShotVariant() const; + bool isSigVariant() const; + bool validateDsaParameters() const; + private: DeleteFnPtr pkey_; }; @@ -663,9 +853,6 @@ struct StackOfX509Deleter { }; using StackOfX509 = std::unique_ptr; -class X509Pointer; -class X509View; - class SSLCtxPointer final { public: SSLCtxPointer() = default; @@ -792,6 +979,14 @@ class X509View final { CheckMatch checkEmail(const std::string_view email, int flags) const; CheckMatch checkIp(const std::string_view ip, int flags) const; + using UsageCallback = std::function; + bool enumUsages(UsageCallback callback) const; + + template + using KeyCallback = std::function; + bool ifRsa(KeyCallback callback) const; + bool ifEc(KeyCallback callback) const; + private: const X509* cert_ = nullptr; }; @@ -948,6 +1143,77 @@ class ECKeyPointer final { DeleteFnPtr key_; }; +class EVPMDCtxPointer final { + public: + EVPMDCtxPointer(); + explicit EVPMDCtxPointer(EVP_MD_CTX* ctx); + EVPMDCtxPointer(EVPMDCtxPointer&& other) noexcept; + EVPMDCtxPointer& operator=(EVPMDCtxPointer&& other) noexcept; + NCRYPTO_DISALLOW_COPY(EVPMDCtxPointer) + ~EVPMDCtxPointer(); + + inline bool operator==(std::nullptr_t) noexcept { return ctx_ == nullptr; } + inline operator bool() const { return ctx_ != nullptr; } + inline EVP_MD_CTX* get() const { return ctx_.get(); } + inline operator EVP_MD_CTX*() const { return ctx_.get(); } + void reset(EVP_MD_CTX* ctx = nullptr); + EVP_MD_CTX* release(); + + bool digestInit(const EVP_MD* digest); + bool digestUpdate(const Buffer& in); + DataPointer digestFinal(size_t length); + bool digestFinalInto(Buffer* buf); + size_t getExpectedSize(); + + std::optional signInit(const EVPKeyPointer& key, + const EVP_MD* digest); + std::optional verifyInit(const EVPKeyPointer& key, + const EVP_MD* digest); + + DataPointer signOneShot(const Buffer& buf) const; + DataPointer sign(const Buffer& buf) const; + bool verify(const Buffer& buf, + const Buffer& sig) const; + + const EVP_MD* getDigest() const; + size_t getDigestSize() const; + bool hasXofFlag() const; + + bool copyTo(const EVPMDCtxPointer& other) const; + + static EVPMDCtxPointer New(); + + private: + DeleteFnPtr ctx_; +}; + +class HMACCtxPointer final { + public: + HMACCtxPointer(); + explicit HMACCtxPointer(HMAC_CTX* ctx); + HMACCtxPointer(HMACCtxPointer&& other) noexcept; + HMACCtxPointer& operator=(HMACCtxPointer&& other) noexcept; + NCRYPTO_DISALLOW_COPY(HMACCtxPointer) + ~HMACCtxPointer(); + + inline bool operator==(std::nullptr_t) noexcept { return ctx_ == nullptr; } + inline operator bool() const { return ctx_ != nullptr; } + inline HMAC_CTX* get() const { return ctx_.get(); } + inline operator HMAC_CTX*() const { return ctx_.get(); } + void reset(HMAC_CTX* ctx = nullptr); + HMAC_CTX* release(); + + bool init(const Buffer& buf, const EVP_MD* md); + bool update(const Buffer& buf); + DataPointer digest(); + bool digestInto(Buffer* buf); + + static HMACCtxPointer New(); + + private: + DeleteFnPtr ctx_; +}; + #ifndef OPENSSL_NO_ENGINE class EnginePointer final { public: @@ -1025,12 +1291,17 @@ Buffer ExportChallenge(const char* input, size_t length); // KDF const EVP_MD* getDigestByName(const std::string_view name); +const EVP_CIPHER* getCipherByName(const std::string_view name); // Verify that the specified HKDF output length is valid for the given digest. // The maximum length for HKDF output for a given digest is 255 times the // hash size for the given digest algorithm. bool checkHkdfLength(const EVP_MD* md, size_t length); +bool extractP1363(const Buffer& buf, + unsigned char* dest, + size_t n); + DataPointer hkdf(const EVP_MD* md, const Buffer& key, const Buffer& info, diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc index 61dd1e97d9672a..dca59f16723ef8 100644 --- a/src/crypto/crypto_cipher.cc +++ b/src/crypto/crypto_cipher.cc @@ -20,6 +20,7 @@ using ncrypto::SSLPointer; using v8::Array; using v8::ArrayBuffer; using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -244,26 +245,22 @@ void CipherBase::Initialize(Environment* env, Local target) { target, "publicEncrypt", PublicKeyCipher::Cipher); + ncrypto::Cipher::encrypt>); SetMethod(context, target, "privateDecrypt", PublicKeyCipher::Cipher); + ncrypto::Cipher::decrypt>); SetMethod(context, target, "privateEncrypt", PublicKeyCipher::Cipher); + ncrypto::Cipher::sign>); SetMethod(context, target, "publicDecrypt", PublicKeyCipher::Cipher); + ncrypto::Cipher::recover>); SetMethodNoSideEffect(context, target, "getCipherInfo", GetCipherInfo); @@ -288,17 +285,13 @@ void CipherBase::RegisterExternalReferences( registry->Register(GetCiphers); registry->Register(PublicKeyCipher::Cipher); + ncrypto::Cipher::encrypt>); registry->Register(PublicKeyCipher::Cipher); + ncrypto::Cipher::decrypt>); registry->Register(PublicKeyCipher::Cipher); + ncrypto::Cipher::sign>); registry->Register(PublicKeyCipher::Cipher); + ncrypto::Cipher::recover>); registry->Register(GetCipherInfo); } @@ -773,10 +766,10 @@ CipherBase::UpdateResult CipherBase::Update( return kErrorState; } - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); - *out = ArrayBuffer::NewBackingStore(env()->isolate(), buf_len); - } + *out = ArrayBuffer::NewBackingStore( + env()->isolate(), + buf_len, + BackingStoreInitializationMode::kUninitialized); buffer = { .data = reinterpret_cast(data), @@ -853,11 +846,10 @@ bool CipherBase::Final(std::unique_ptr* out) { const int mode = ctx_.getMode(); - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); - *out = ArrayBuffer::NewBackingStore( - env()->isolate(), static_cast(ctx_.getBlockSize())); - } + *out = ArrayBuffer::NewBackingStore( + env()->isolate(), + static_cast(ctx_.getBlockSize()), + BackingStoreInitializationMode::kUninitialized); if (kind_ == kDecipher && Cipher::FromCtx(ctx_).isSupportedAuthenticatedMode()) { @@ -939,9 +931,7 @@ void CipherBase::Final(const FunctionCallbackInfo& args) { Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local())); } -template +template bool PublicKeyCipher::Cipher( Environment* env, const EVPKeyPointer& pkey, @@ -950,62 +940,32 @@ bool PublicKeyCipher::Cipher( const ArrayBufferOrViewContents& oaep_label, const ArrayBufferOrViewContents& data, std::unique_ptr* out) { - EVPKeyCtxPointer ctx = pkey.newCtx(); - if (!ctx) - return false; - if (EVP_PKEY_cipher_init(ctx.get()) <= 0) - return false; - if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), padding) <= 0) - return false; - - if (digest != nullptr) { - if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), digest) <= 0) - return false; - } - - if (!SetRsaOaepLabel(ctx, oaep_label.ToByteSource())) return false; + auto label = oaep_label.ToByteSource(); + auto in = data.ToByteSource(); - size_t out_len = 0; - if (EVP_PKEY_cipher( - ctx.get(), - nullptr, - &out_len, - data.data(), - data.size()) <= 0) { - return false; - } - - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - *out = ArrayBuffer::NewBackingStore(env->isolate(), out_len); - } + const ncrypto::Cipher::CipherParams params{ + .padding = padding, + .digest = digest, + .label = label, + }; - if (EVP_PKEY_cipher( - ctx.get(), - static_cast((*out)->Data()), - &out_len, - data.data(), - data.size()) <= 0) { - return false; - } + auto buf = cipher(pkey, params, in); + if (!buf) return false; - CHECK_LE(out_len, (*out)->ByteLength()); - if (out_len == 0) { + if (buf.size() == 0) { *out = ArrayBuffer::NewBackingStore(env->isolate(), 0); - } else if (out_len != (*out)->ByteLength()) { - std::unique_ptr old_out = std::move(*out); - *out = ArrayBuffer::NewBackingStore(env->isolate(), out_len); + } else { + *out = ArrayBuffer::NewBackingStore(env->isolate(), buf.size()); memcpy(static_cast((*out)->Data()), - static_cast(old_out->Data()), - out_len); + static_cast(buf.get()), + buf.size()); } return true; } template + PublicKeyCipher::Cipher_t cipher> void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { MarkPopErrorOnReturn mark_pop_error_on_return; Environment* env = Environment::GetCurrent(args); @@ -1024,25 +984,16 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { uint32_t padding; if (!args[offset + 1]->Uint32Value(env->context()).To(&padding)) return; - if (EVP_PKEY_cipher == EVP_PKEY_decrypt && + if (cipher == ncrypto::Cipher::decrypt && operation == PublicKeyCipher::kPrivate && padding == RSA_PKCS1_PADDING) { EVPKeyCtxPointer ctx = pkey.newCtx(); CHECK(ctx); - if (EVP_PKEY_decrypt_init(ctx.get()) <= 0) { + if (!ctx.initForDecrypt()) { return ThrowCryptoError(env, ERR_get_error()); } - int rsa_pkcs1_implicit_rejection = - EVP_PKEY_CTX_ctrl_str(ctx.get(), "rsa_pkcs1_implicit_rejection", "1"); - // From the doc -2 means that the option is not supported. - // The default for the option is enabled and if it has been - // specifically disabled we want to respect that so we will - // not throw an error if the option is supported regardless - // of how it is set. The call to set the value - // will not affect what is used since a different context is - // used in the call if the option is supported - if (rsa_pkcs1_implicit_rejection <= 0) { + if (!ctx.setRsaImplicitRejection()) { return THROW_ERR_INVALID_ARG_VALUE( env, "RSA_PKCS1_PADDING is no longer supported for private decryption"); @@ -1052,7 +1003,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { const EVP_MD* digest = nullptr; if (args[offset + 2]->IsString()) { const Utf8Value oaep_str(env->isolate(), args[offset + 2]); - digest = EVP_get_digestbyname(*oaep_str); + digest = ncrypto::getDigestByName(oaep_str.ToStringView()); if (digest == nullptr) return THROW_ERR_OSSL_EVP_INVALID_DIGEST(env); } @@ -1063,8 +1014,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { return THROW_ERR_OUT_OF_RANGE(env, "oaepLabel is too big"); } std::unique_ptr out; - if (!Cipher( - env, pkey, padding, digest, oaep_label, buf, &out)) { + if (!Cipher(env, pkey, padding, digest, oaep_label, buf, &out)) { return ThrowCryptoError(env, ERR_get_error()); } diff --git a/src/crypto/crypto_cipher.h b/src/crypto/crypto_cipher.h index 57c424e7509fa2..950acfa2521ede 100644 --- a/src/crypto/crypto_cipher.h +++ b/src/crypto/crypto_cipher.h @@ -96,19 +96,17 @@ class CipherBase : public BaseObject { class PublicKeyCipher { public: - typedef int (*EVP_PKEY_cipher_init_t)(EVP_PKEY_CTX* ctx); - typedef int (*EVP_PKEY_cipher_t)(EVP_PKEY_CTX* ctx, - unsigned char* out, size_t* outlen, - const unsigned char* in, size_t inlen); + using Cipher_t = + ncrypto::DataPointer(const ncrypto::EVPKeyPointer&, + const ncrypto::Cipher::CipherParams& params, + const ncrypto::Buffer); enum Operation { kPublic, kPrivate }; - template + template static bool Cipher(Environment* env, const ncrypto::EVPKeyPointer& pkey, int padding, @@ -117,9 +115,7 @@ class PublicKeyCipher { const ArrayBufferOrViewContents& data, std::unique_ptr* out); - template + template static void Cipher(const v8::FunctionCallbackInfo& args); }; diff --git a/src/crypto/crypto_common.cc b/src/crypto/crypto_common.cc index d94f6e1c82c4a6..591509e735b943 100644 --- a/src/crypto/crypto_common.cc +++ b/src/crypto/crypto_common.cc @@ -36,7 +36,7 @@ using ncrypto::StackOfX509; using ncrypto::X509Pointer; using ncrypto::X509View; using v8::ArrayBuffer; -using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Context; using v8::EscapableHandleScope; using v8::Integer; @@ -307,11 +307,8 @@ MaybeLocal ECPointToBuffer(Environment* env, return MaybeLocal(); } - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), len); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), len, BackingStoreInitializationMode::kUninitialized); len = EC_POINT_point2oct(group, point, diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index c7574e67f03f03..da5cebb87a3d51 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -1451,7 +1451,7 @@ void SecureContext::GetCertificate(const FunctionCallbackInfo& args) { } // UseExtraCaCerts is called only once at the start of the Node.js process. -void UseExtraCaCerts(const std::string& file) { +void UseExtraCaCerts(std::string_view file) { extra_root_certs_file = file; } diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc index 7041eb985d9f6d..0e25a937e175fa 100644 --- a/src/crypto/crypto_dh.cc +++ b/src/crypto/crypto_dh.cc @@ -397,31 +397,23 @@ EVPKeyCtxPointer DhKeyGenTraits::Setup(DhKeyPairGenConfig* params) { auto dh = DHPointer::New(std::move(prime), std::move(bn_g)); if (!dh) return {}; - key_params = EVPKeyPointer::New(); - CHECK(key_params); - CHECK_EQ(EVP_PKEY_assign_DH(key_params.get(), dh.release()), 1); + key_params = EVPKeyPointer::NewDH(std::move(dh)); } else if (int* prime_size = std::get_if(¶ms->params.prime)) { - EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr)); - EVP_PKEY* raw_params = nullptr; - if (!param_ctx || - EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 || - EVP_PKEY_CTX_set_dh_paramgen_prime_len( - param_ctx.get(), - *prime_size) <= 0 || - EVP_PKEY_CTX_set_dh_paramgen_generator( - param_ctx.get(), - params->params.generator) <= 0 || - EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) { + auto param_ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_DH); + if (!param_ctx.initForParamgen() || + !param_ctx.setDhParameters(*prime_size, params->params.generator)) { return {}; } - key_params = EVPKeyPointer(raw_params); + key_params = param_ctx.paramgen(); } else { UNREACHABLE(); } + if (!key_params) return {}; + EVPKeyCtxPointer ctx = key_params.newCtx(); - if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0) return {}; + if (!ctx.initForKeygen()) return {}; return ctx; } diff --git a/src/crypto/crypto_dsa.cc b/src/crypto/crypto_dsa.cc index 471fee77531139..cac05aa55f8dd2 100644 --- a/src/crypto/crypto_dsa.cc +++ b/src/crypto/crypto_dsa.cc @@ -12,22 +12,10 @@ #include -// EVP_PKEY_CTX_set_dsa_paramgen_q_bits was added in OpenSSL 1.1.1e. -#if OPENSSL_VERSION_NUMBER < 0x1010105fL -#define EVP_PKEY_CTX_set_dsa_paramgen_q_bits(ctx, qbits) \ - EVP_PKEY_CTX_ctrl((ctx), \ - EVP_PKEY_DSA, \ - EVP_PKEY_OP_PARAMGEN, \ - EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS, \ - (qbits), \ - nullptr) -#endif - namespace node { using ncrypto::BignumPointer; using ncrypto::EVPKeyCtxPointer; -using ncrypto::EVPKeyPointer; using v8::FunctionCallbackInfo; using v8::Int32; using v8::JustVoid; @@ -41,33 +29,22 @@ using v8::Value; namespace crypto { EVPKeyCtxPointer DsaKeyGenTraits::Setup(DsaKeyPairGenConfig* params) { - EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, nullptr)); - EVP_PKEY* raw_params = nullptr; - - if (!param_ctx || - EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 || - EVP_PKEY_CTX_set_dsa_paramgen_bits( - param_ctx.get(), - params->params.modulus_bits) <= 0) { - return EVPKeyCtxPointer(); - } - - if (params->params.divisor_bits != -1) { - if (EVP_PKEY_CTX_set_dsa_paramgen_q_bits( - param_ctx.get(), params->params.divisor_bits) <= 0) { - return EVPKeyCtxPointer(); - } + auto param_ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_DSA); + + if (!param_ctx.initForParamgen() || + !param_ctx.setDsaParameters( + params->params.modulus_bits, + params->params.divisor_bits != -1 + ? std::optional(params->params.divisor_bits) + : std::nullopt)) { + return {}; } - if (EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) - return EVPKeyCtxPointer(); + auto key_params = param_ctx.paramgen(); + if (!key_params) return {}; - EVPKeyPointer key_params(raw_params); EVPKeyCtxPointer key_ctx = key_params.newCtx(); - - if (!key_ctx || EVP_PKEY_keygen_init(key_ctx.get()) <= 0) - return EVPKeyCtxPointer(); - + if (!key_ctx.initForKeygen()) return {}; return key_ctx; } diff --git a/src/crypto/crypto_ec.cc b/src/crypto/crypto_ec.cc index 5ccda6f0768873..98f1e1312769ca 100644 --- a/src/crypto/crypto_ec.cc +++ b/src/crypto/crypto_ec.cc @@ -28,7 +28,7 @@ using ncrypto::EVPKeyPointer; using ncrypto::MarkPopErrorOnReturn; using v8::Array; using v8::ArrayBuffer; -using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -201,14 +201,10 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo& args) { return; } - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - // NOTE: field_size is in bits - int field_size = EC_GROUP_get_degree(ecdh->group_); - size_t out_len = (field_size + 7) / 8; - bs = ArrayBuffer::NewBackingStore(env->isolate(), out_len); - } + 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)) @@ -257,12 +253,11 @@ void ECDH::GetPrivateKey(const FunctionCallbackInfo& args) { return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get ECDH private key"); - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), - BignumPointer::GetByteCount(b)); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), + BignumPointer::GetByteCount(b), + BackingStoreInitializationMode::kUninitialized); + CHECK_EQ(bs->ByteLength(), BignumPointer::EncodePaddedInto( b, static_cast(bs->Data()), bs->ByteLength())); @@ -459,24 +454,14 @@ bool ECDHBitsTraits::DeriveBits(Environment* env, case EVP_PKEY_X25519: // Fall through case EVP_PKEY_X448: { - EVPKeyCtxPointer ctx = m_privkey.newCtx(); Mutex::ScopedLock pub_lock(params.public_.mutex()); - if (EVP_PKEY_derive_init(ctx.get()) <= 0 || - EVP_PKEY_derive_set_peer( - ctx.get(), - m_pubkey.get()) <= 0 || - EVP_PKEY_derive(ctx.get(), nullptr, &len) <= 0) { - return false; - } - - ByteSource::Builder buf(len); - - if (EVP_PKEY_derive(ctx.get(), buf.data(), &len) <= 0) { - return false; - } + EVPKeyCtxPointer ctx = m_privkey.newCtx(); + if (!ctx.initForDerive(m_pubkey)) return false; - *out = std::move(buf).release(len); + auto data = ctx.derive(); + if (!data) return false; + *out = ByteSource::Allocated(data.release()); break; } default: { @@ -523,28 +508,24 @@ EVPKeyCtxPointer EcKeyGenTraits::Setup(EcKeyPairGenConfig* params) { case EVP_PKEY_X25519: // Fall through case EVP_PKEY_X448: - key_ctx.reset(EVP_PKEY_CTX_new_id(params->params.curve_nid, nullptr)); + key_ctx = EVPKeyCtxPointer::NewFromID(params->params.curve_nid); break; default: { - EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr)); - EVP_PKEY* raw_params = nullptr; - if (!param_ctx || - EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 || - EVP_PKEY_CTX_set_ec_paramgen_curve_nid( - param_ctx.get(), params->params.curve_nid) <= 0 || - EVP_PKEY_CTX_set_ec_param_enc( - param_ctx.get(), params->params.param_encoding) <= 0 || - EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) { - return EVPKeyCtxPointer(); + auto param_ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_EC); + if (!param_ctx.initForParamgen() || + !param_ctx.setEcParameters(params->params.curve_nid, + params->params.param_encoding)) { + return {}; } - EVPKeyPointer key_params(raw_params); + + auto key_params = param_ctx.paramgen(); + if (!key_params) return {}; + key_ctx = key_params.newCtx(); } } - if (key_ctx && EVP_PKEY_keygen_init(key_ctx.get()) <= 0) - key_ctx.reset(); - + if (!key_ctx.initForKeygen()) return {}; return key_ctx; } diff --git a/src/crypto/crypto_hash.cc b/src/crypto/crypto_hash.cc index bcd4c533b07ceb..851847483327c1 100644 --- a/src/crypto/crypto_hash.cc +++ b/src/crypto/crypto_hash.cc @@ -11,6 +11,7 @@ namespace node { +using ncrypto::DataPointer; using ncrypto::EVPMDCtxPointer; using ncrypto::MarkPopErrorOnReturn; using v8::Context; @@ -59,7 +60,7 @@ struct MaybeCachedMD { }; MaybeCachedMD FetchAndMaybeCacheMD(Environment* env, const char* search_name) { - const EVP_MD* implicit_md = EVP_get_digestbyname(search_name); + const EVP_MD* implicit_md = ncrypto::getDigestByName(search_name); if (!implicit_md) return {nullptr, nullptr, -1}; const char* real_name = EVP_MD_get0_name(implicit_md); @@ -202,7 +203,7 @@ const EVP_MD* GetDigestImplementation(Environment* env, return result.explicit_md ? result.explicit_md : result.implicit_md; #else Utf8Value utf8(env->isolate(), algorithm); - return EVP_get_digestbyname(*utf8); + return ncrypto::getDigestByName(utf8.ToStringView()); #endif } @@ -220,7 +221,7 @@ void Hash::OneShotDigest(const FunctionCallbackInfo& args) { CHECK(args[5]->IsUint32() || args[5]->IsUndefined()); // outputEncodingId const EVP_MD* md = GetDigestImplementation(env, args[0], args[1], args[2]); - if (md == nullptr) { + if (md == nullptr) [[unlikely]] { Utf8Value method(isolate, args[0]); std::string message = "Digest method " + method.ToString() + " is not supported"; @@ -229,41 +230,36 @@ void Hash::OneShotDigest(const FunctionCallbackInfo& args) { enum encoding output_enc = ParseEncoding(isolate, args[4], args[5], HEX); - int md_len = EVP_MD_size(md); - unsigned int result_size; - ByteSource::Builder output(md_len); - int success; - // On smaller inputs, EVP_Digest() can be slower than the - // deprecated helpers e.g SHA256_XXX. The speedup may not - // be worth using deprecated APIs, however, so we use - // EVP_Digest(), unless there's a better alternative - // in the future. - // https://github.com/openssl/openssl/issues/19612 - if (args[3]->IsString()) { - Utf8Value utf8(isolate, args[3]); - success = EVP_Digest(utf8.out(), - utf8.length(), - output.data(), - &result_size, - md, - nullptr); - } else { + DataPointer output = ([&] { + if (args[3]->IsString()) { + Utf8Value utf8(isolate, args[3]); + ncrypto::Buffer buf{ + .data = reinterpret_cast(utf8.out()), + .len = utf8.length(), + }; + return ncrypto::hashDigest(buf, md); + } + ArrayBufferViewContents input(args[3]); - success = EVP_Digest(input.data(), - input.length(), - output.data(), - &result_size, - md, - nullptr); - } - if (!success) { + ncrypto::Buffer buf{ + .data = reinterpret_cast(input.data()), + .len = input.length(), + }; + return ncrypto::hashDigest(buf, md); + })(); + + if (!output) [[unlikely]] { return ThrowCryptoError(env, ERR_get_error()); } Local error; - MaybeLocal rc = StringBytes::Encode( - env->isolate(), output.data(), md_len, output_enc, &error); - if (rc.IsEmpty()) { + MaybeLocal rc = + StringBytes::Encode(env->isolate(), + static_cast(output.get()), + output.size(), + output_enc, + &error); + if (rc.IsEmpty()) [[unlikely]] { CHECK(!error.IsEmpty()); env->isolate()->ThrowException(error); return; @@ -314,7 +310,8 @@ void Hash::New(const FunctionCallbackInfo& args) { const EVP_MD* md = nullptr; if (args[0]->IsObject()) { ASSIGN_OR_RETURN_UNWRAP(&orig, args[0].As()); - md = EVP_MD_CTX_md(orig->mdctx_.get()); + CHECK_NOT_NULL(orig); + md = orig->mdctx_.getDigest(); } else { md = GetDigestImplementation(env, args[0], args[2], args[3]); } @@ -331,25 +328,25 @@ void Hash::New(const FunctionCallbackInfo& args) { "Digest method not supported"); } - if (orig != nullptr && - 0 >= EVP_MD_CTX_copy(hash->mdctx_.get(), orig->mdctx_.get())) { + if (orig != nullptr && !orig->mdctx_.copyTo(hash->mdctx_)) { return ThrowCryptoError(env, ERR_get_error(), "Digest copy error"); } } bool Hash::HashInit(const EVP_MD* md, Maybe xof_md_len) { - mdctx_.reset(EVP_MD_CTX_new()); - if (!mdctx_ || EVP_DigestInit_ex(mdctx_.get(), md, nullptr) <= 0) { + mdctx_ = EVPMDCtxPointer::New(); + if (!mdctx_.digestInit(md)) [[unlikely]] { mdctx_.reset(); return false; } - md_len_ = EVP_MD_size(md); + md_len_ = mdctx_.getDigestSize(); if (xof_md_len.IsJust() && xof_md_len.FromJust() != 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 ((EVP_MD_flags(md) & EVP_MD_FLAG_XOF) == 0) { + if (!mdctx_.hasXofFlag()) [[unlikely]] { EVPerr(EVP_F_EVP_DIGESTFINALXOF, EVP_R_NOT_XOF_OR_INVALID_LENGTH); + mdctx_.reset(); return false; } md_len_ = xof_md_len.FromJust(); @@ -359,9 +356,11 @@ bool Hash::HashInit(const EVP_MD* md, Maybe xof_md_len) { } bool Hash::HashUpdate(const char* data, size_t len) { - if (!mdctx_) - return false; - return EVP_DigestUpdate(mdctx_.get(), data, len) == 1; + if (!mdctx_) return false; + return mdctx_.digestUpdate(ncrypto::Buffer{ + .data = data, + .len = len, + }); } void Hash::HashUpdate(const FunctionCallbackInfo& args) { @@ -402,31 +401,18 @@ void Hash::HashDigest(const FunctionCallbackInfo& args) { // and Hash.digest can both be used to retrieve the digest, // so we need to cache it. // See https://github.com/nodejs/node/issues/28245. - - ByteSource::Builder digest(len); - - size_t default_len = EVP_MD_CTX_size(hash->mdctx_.get()); - int ret; - if (len == default_len) { - ret = EVP_DigestFinal_ex( - hash->mdctx_.get(), digest.data(), &len); - // The output length should always equal hash->md_len_ - CHECK_EQ(len, hash->md_len_); - } else { - ret = EVP_DigestFinalXOF( - hash->mdctx_.get(), digest.data(), len); - } - - if (ret != 1) + auto data = hash->mdctx_.digestFinal(len); + if (!data) [[unlikely]] { return ThrowCryptoError(env, ERR_get_error()); + } - hash->digest_ = std::move(digest).release(); + hash->digest_ = ByteSource::Allocated(data.release()); } Local error; MaybeLocal rc = StringBytes::Encode( env->isolate(), hash->digest_.data(), len, encoding, &error); - if (rc.IsEmpty()) { + if (rc.IsEmpty()) [[unlikely]] { CHECK(!error.IsEmpty()); env->isolate()->ThrowException(error); return; @@ -469,7 +455,7 @@ Maybe HashTraits::AdditionalConfig( CHECK(args[offset]->IsString()); // Hash algorithm Utf8Value digest(env->isolate(), args[offset]); - params->digest = EVP_get_digestbyname(*digest); + params->digest = ncrypto::getDigestByName(digest.ToStringView()); if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); @@ -492,7 +478,7 @@ Maybe HashTraits::AdditionalConfig( static_cast(args[offset + 2] .As()->Value()) / CHAR_BIT; if (params->length != expected) { - if ((EVP_MD_flags(params->digest) & EVP_MD_FLAG_XOF) == 0) { + if ((EVP_MD_flags(params->digest) & EVP_MD_FLAG_XOF) == 0) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Digest method not supported"); return Nothing(); } @@ -506,29 +492,19 @@ bool HashTraits::DeriveBits( Environment* env, const HashConfig& params, ByteSource* out) { - EVPMDCtxPointer ctx(EVP_MD_CTX_new()); + auto ctx = EVPMDCtxPointer::New(); - if (!ctx || EVP_DigestInit_ex(ctx.get(), params.digest, nullptr) <= 0 || - EVP_DigestUpdate(ctx.get(), params.in.data(), params.in.size()) <= - 0) [[unlikely]] { + if (!ctx.digestInit(params.digest) || !ctx.digestUpdate(params.in)) + [[unlikely]] { return false; } if (params.length > 0) [[likely]] { - unsigned int length = params.length; - ByteSource::Builder buf(length); - - size_t expected = EVP_MD_CTX_size(ctx.get()); - - int ret = - (length == expected) - ? EVP_DigestFinal_ex(ctx.get(), buf.data(), &length) - : EVP_DigestFinalXOF(ctx.get(), buf.data(), length); - - if (ret != 1) [[unlikely]] + auto data = ctx.digestFinal(params.length); + if (!data) [[unlikely]] return false; - *out = std::move(buf).release(); + *out = ByteSource::Allocated(data.release()); } return true; @@ -548,7 +524,7 @@ void InternalVerifyIntegrity(const v8::FunctionCallbackInfo& args) { CHECK(args[2]->IsArrayBufferView()); ArrayBufferOrViewContents expected(args[2]); - const EVP_MD* md_type = EVP_get_digestbyname(*algorithm); + const EVP_MD* md_type = ncrypto::getDigestByName(algorithm.ToStringView()); unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int digest_size; if (md_type == nullptr || EVP_Digest(content.data(), @@ -556,7 +532,7 @@ void InternalVerifyIntegrity(const v8::FunctionCallbackInfo& args) { digest, &digest_size, md_type, - nullptr) != 1) { + nullptr) != 1) [[unlikely]] { return ThrowCryptoError( env, ERR_get_error(), "Digest method not supported"); } @@ -570,7 +546,7 @@ void InternalVerifyIntegrity(const v8::FunctionCallbackInfo& args) { digest_size, BASE64, &error); - if (rc.IsEmpty()) { + if (rc.IsEmpty()) [[unlikely]] { CHECK(!error.IsEmpty()); env->isolate()->ThrowException(error); return; diff --git a/src/crypto/crypto_hkdf.cc b/src/crypto/crypto_hkdf.cc index 2a465c849def44..10bb8e4258bf63 100644 --- a/src/crypto/crypto_hkdf.cc +++ b/src/crypto/crypto_hkdf.cc @@ -55,7 +55,7 @@ Maybe HKDFTraits::AdditionalConfig( Utf8Value hash(env->isolate(), args[offset]); params->digest = ncrypto::getDigestByName(hash.ToStringView()); - if (params->digest == nullptr) { + if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *hash); return Nothing(); } @@ -88,7 +88,7 @@ Maybe HKDFTraits::AdditionalConfig( // HKDF-Expand computes up to 255 HMAC blocks, each having as many bits as the // output of the hash function. 255 is a hard limit because HKDF appends an // 8-bit counter to each HMAC'd message, starting at 1. - if (!ncrypto::checkHkdfLength(params->digest, params->length)) { + if (!ncrypto::checkHkdfLength(params->digest, params->length)) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_KEYLEN(env); return Nothing(); } diff --git a/src/crypto/crypto_hmac.cc b/src/crypto/crypto_hmac.cc index 25ccb1b9d04e51..56a09e1a2d9b0f 100644 --- a/src/crypto/crypto_hmac.cc +++ b/src/crypto/crypto_hmac.cc @@ -70,15 +70,21 @@ void Hmac::New(const FunctionCallbackInfo& args) { void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) { HandleScope scope(env()->isolate()); - const EVP_MD* md = EVP_get_digestbyname(hash_type); - if (md == nullptr) + const EVP_MD* md = ncrypto::getDigestByName(hash_type); + if (md == nullptr) [[unlikely]] { return THROW_ERR_CRYPTO_INVALID_DIGEST( env(), "Invalid digest: %s", hash_type); + } if (key_len == 0) { key = ""; } - ctx_.reset(HMAC_CTX_new()); - if (!ctx_ || !HMAC_Init_ex(ctx_.get(), key, key_len, md, nullptr)) { + + ctx_ = HMACCtxPointer::New(); + ncrypto::Buffer key_buf{ + .data = key, + .len = static_cast(key_len), + }; + if (!ctx_.init(key_buf, md)) [[unlikely]] { ctx_.reset(); return ThrowCryptoError(env(), ERR_get_error()); } @@ -95,9 +101,11 @@ void Hmac::HmacInit(const FunctionCallbackInfo& args) { } bool Hmac::HmacUpdate(const char* data, size_t len) { - return ctx_ && HMAC_Update(ctx_.get(), - reinterpret_cast(data), - len) == 1; + ncrypto::Buffer buf{ + .data = data, + .len = len, + }; + return ctx_.update(buf); } void Hmac::HmacUpdate(const FunctionCallbackInfo& args) { @@ -123,24 +131,27 @@ void Hmac::HmacDigest(const FunctionCallbackInfo& args) { } unsigned char md_value[EVP_MAX_MD_SIZE]; - unsigned int md_len = 0; + ncrypto::Buffer buf{ + .data = md_value, + .len = sizeof(md_value), + }; if (hmac->ctx_) { - bool ok = HMAC_Final(hmac->ctx_.get(), md_value, &md_len); - hmac->ctx_.reset(); - if (!ok) { + if (!hmac->ctx_.digestInto(&buf)) [[unlikely]] { + hmac->ctx_.reset(); return ThrowCryptoError(env, ERR_get_error(), "Failed to finalize HMAC"); } + hmac->ctx_.reset(); } Local error; MaybeLocal rc = StringBytes::Encode(env->isolate(), reinterpret_cast(md_value), - md_len, + buf.len, encoding, &error); - if (rc.IsEmpty()) { + if (rc.IsEmpty()) [[unlikely]] { CHECK(!error.IsEmpty()); env->isolate()->ThrowException(error); return; @@ -188,8 +199,8 @@ Maybe HmacTraits::AdditionalConfig( CHECK(args[offset + 2]->IsObject()); // Key Utf8Value digest(env->isolate(), args[offset + 1]); - params->digest = EVP_get_digestbyname(*digest); - if (params->digest == nullptr) { + params->digest = ncrypto::getDigestByName(digest.ToStringView()); + if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); } @@ -225,31 +236,29 @@ bool HmacTraits::DeriveBits( Environment* env, const HmacConfig& params, ByteSource* out) { - HMACCtxPointer ctx(HMAC_CTX_new()); + auto ctx = HMACCtxPointer::New(); - if (!ctx || !HMAC_Init_ex(ctx.get(), - params.key.GetSymmetricKey(), - params.key.GetSymmetricKeySize(), - params.digest, - nullptr)) { + ncrypto::Buffer key_buf{ + .data = params.key.GetSymmetricKey(), + .len = params.key.GetSymmetricKeySize(), + }; + if (!ctx.init(key_buf, params.digest)) [[unlikely]] { return false; } - if (!HMAC_Update( - ctx.get(), - params.data.data(), - params.data.size())) { + ncrypto::Buffer buffer{ + .data = params.data.data(), + .len = params.data.size(), + }; + if (!ctx.update(buffer)) [[unlikely]] { return false; } - ByteSource::Builder buf(EVP_MAX_MD_SIZE); - unsigned int len; - - if (!HMAC_Final(ctx.get(), buf.data(), &len)) { + auto buf = ctx.digest(); + if (!buf) [[unlikely]] return false; - } - *out = std::move(buf).release(len); + *out = ByteSource::Allocated(buf.release()); return true; } @@ -258,9 +267,9 @@ MaybeLocal HmacTraits::EncodeOutput(Environment* env, const HmacConfig& params, ByteSource* out) { switch (params.mode) { - case SignConfiguration::kSign: + case SignConfiguration::Mode::Sign: return out->ToArrayBuffer(env); - case SignConfiguration::kVerify: + case SignConfiguration::Mode::Verify: return Boolean::New( env->isolate(), out->size() > 0 && out->size() == params.signature.size() && diff --git a/src/crypto/crypto_keygen.cc b/src/crypto/crypto_keygen.cc index 246191f5d51796..7c3a85e9f8a24d 100644 --- a/src/crypto/crypto_keygen.cc +++ b/src/crypto/crypto_keygen.cc @@ -47,10 +47,8 @@ Maybe NidKeyPairGenTraits::AdditionalConfig( } EVPKeyCtxPointer NidKeyPairGenTraits::Setup(NidKeyPairGenConfig* params) { - EVPKeyCtxPointer ctx = - EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(params->params.id, nullptr)); - if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0) return {}; - + auto ctx = EVPKeyCtxPointer::NewFromID(params->params.id); + if (!ctx || !ctx.initForKeygen()) return {}; return ctx; } diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc index bedcf04d036478..2c55828facc35b 100644 --- a/src/crypto/crypto_keys.cc +++ b/src/crypto/crypto_keys.cc @@ -349,7 +349,7 @@ KeyObjectData::GetPrivateKeyEncodingFromJs( if (context != kKeyContextInput) { if (args[*offset]->IsString()) { Utf8Value cipher_name(env->isolate(), args[*offset]); - config.cipher = EVP_get_cipherbyname(*cipher_name); + config.cipher = ncrypto::getCipherByName(cipher_name.ToStringView()); if (config.cipher == nullptr) { THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env); return Nothing(); @@ -597,7 +597,7 @@ bool KeyObjectHandle::HasInstance(Environment* env, Local value) { return !t.IsEmpty() && t->HasInstance(value); } -v8::Local KeyObjectHandle::Initialize(Environment* env) { +Local KeyObjectHandle::Initialize(Environment* env) { Local templ = env->crypto_key_object_handle_constructor(); if (templ.IsEmpty()) { Isolate* isolate = env->isolate(); @@ -958,14 +958,10 @@ bool KeyObjectHandle::CheckEcKeyData() const { CHECK_EQ(key.id(), EVP_PKEY_EC); if (data_.GetKeyType() == kKeyTypePrivate) { - return EVP_PKEY_check(ctx.get()) == 1; + return ctx.privateCheck(); } -#if OPENSSL_VERSION_MAJOR >= 3 - return EVP_PKEY_public_check_quick(ctx.get()) == 1; -#else - return EVP_PKEY_public_check(ctx.get()) == 1; -#endif + return ctx.publicCheck(); } void KeyObjectHandle::CheckEcKeyData(const FunctionCallbackInfo& args) { @@ -1202,6 +1198,9 @@ void Initialize(Environment* env, Local target) { constexpr int kKeyFormatJWK = static_cast(EVPKeyPointer::PKFormatType::JWK); + constexpr auto kSigEncDER = DSASigEnc::DER; + constexpr auto kSigEncP1363 = DSASigEnc::P1363; + NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatRaw); NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatPKCS8); NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatSPKI); diff --git a/src/crypto/crypto_pbkdf2.cc b/src/crypto/crypto_pbkdf2.cc index dcaa430aacd3d7..1a0dff8238d938 100644 --- a/src/crypto/crypto_pbkdf2.cc +++ b/src/crypto/crypto_pbkdf2.cc @@ -88,20 +88,20 @@ Maybe PBKDF2Traits::AdditionalConfig( CHECK(args[offset + 4]->IsString()); // digest_name params->iterations = args[offset + 2].As()->Value(); - if (params->iterations < 0) { + if (params->iterations < 0) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "iterations must be <= %d", INT_MAX); return Nothing(); } params->length = args[offset + 3].As()->Value(); - if (params->length < 0) { + if (params->length < 0) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "length must be <= %d", INT_MAX); return Nothing(); } Utf8Value name(args.GetIsolate(), args[offset + 4]); params->digest = ncrypto::getDigestByName(name.ToStringView()); - if (params->digest == nullptr) { + if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *name); return Nothing(); } diff --git a/src/crypto/crypto_random.cc b/src/crypto/crypto_random.cc index cb96698aa644c3..4e9ff906c06571 100644 --- a/src/crypto/crypto_random.cc +++ b/src/crypto/crypto_random.cc @@ -14,7 +14,6 @@ namespace node { using ncrypto::BignumPointer; using ncrypto::ClearErrorOnReturn; using v8::ArrayBuffer; -using v8::BackingStore; using v8::Boolean; using v8::FunctionCallbackInfo; using v8::Int32; @@ -25,6 +24,7 @@ using v8::MaybeLocal; using v8::Nothing; using v8::Object; using v8::Uint32; +using v8::Undefined; using v8::Value; namespace crypto { @@ -39,7 +39,7 @@ BignumPointer::PrimeCheckCallback getPrimeCheckCallback(Environment* env) { } // namespace MaybeLocal RandomBytesTraits::EncodeOutput( Environment* env, const RandomBytesConfig& params, ByteSource* unused) { - return v8::Undefined(env->isolate()); + return Undefined(env->isolate()); } Maybe RandomBytesTraits::AdditionalConfig( @@ -78,14 +78,13 @@ void RandomPrimeConfig::MemoryInfo(MemoryTracker* tracker) const { MaybeLocal RandomPrimeTraits::EncodeOutput( Environment* env, const RandomPrimeConfig& params, ByteSource* unused) { size_t size = params.prime.byteLength(); - std::shared_ptr store = - ArrayBuffer::NewBackingStore(env->isolate(), size); + auto store = ArrayBuffer::NewBackingStore(env->isolate(), size); CHECK_EQ(size, BignumPointer::EncodePaddedInto( params.prime.get(), reinterpret_cast(store->Data()), size)); - return ArrayBuffer::New(env->isolate(), store); + return ArrayBuffer::New(env->isolate(), std::move(store)); } Maybe RandomPrimeTraits::AdditionalConfig( @@ -104,7 +103,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( if (!args[offset + 2]->IsUndefined()) { ArrayBufferOrViewContents add(args[offset + 2]); params->add.reset(add.data(), add.size()); - if (!params->add) { + if (!params->add) [[unlikely]] { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime"); return Nothing(); } @@ -113,7 +112,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( if (!args[offset + 3]->IsUndefined()) { ArrayBufferOrViewContents rem(args[offset + 3]); params->rem.reset(rem.data(), rem.size()); - if (!params->rem) { + if (!params->rem) [[unlikely]] { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime"); return Nothing(); } @@ -124,7 +123,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( CHECK_GT(bits, 0); if (params->add) { - if (BignumPointer::GetBitCount(params->add.get()) > bits) { + if (BignumPointer::GetBitCount(params->add.get()) > bits) [[unlikely]] { // If we allowed this, the best case would be returning a static prime // that wasn't generated randomly. The worst case would be an infinite // loop within OpenSSL, blocking the main thread or one of the threads @@ -133,7 +132,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( return Nothing(); } - if (params->rem && params->add <= params->rem) { + if (params->rem && params->add <= params->rem) [[unlikely]] { // This would definitely lead to an infinite loop if allowed since // OpenSSL does not check this condition. THROW_ERR_OUT_OF_RANGE(env, "invalid options.rem"); @@ -144,7 +143,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( params->bits = bits; params->safe = safe; params->prime = BignumPointer::NewSecure(); - if (!params->prime) { + if (!params->prime) [[unlikely]] { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime"); return Nothing(); } @@ -195,7 +194,8 @@ bool CheckPrimeTraits::DeriveBits( const CheckPrimeConfig& params, ByteSource* out) { int ret = params.candidate.isPrime(params.checks, getPrimeCheckCallback(env)); - if (ret < 0) return false; + if (ret < 0) [[unlikely]] + return false; ByteSource::Builder buf(1); buf.data()[0] = ret; *out = std::move(buf).release(); diff --git a/src/crypto/crypto_rsa.cc b/src/crypto/crypto_rsa.cc index 05a3882c7e17d7..f0e0f9fd5f94a1 100644 --- a/src/crypto/crypto_rsa.cc +++ b/src/crypto/crypto_rsa.cc @@ -15,13 +15,15 @@ namespace node { using ncrypto::BignumPointer; +using ncrypto::DataPointer; using ncrypto::EVPKeyCtxPointer; using ncrypto::EVPKeyPointer; using ncrypto::RSAPointer; using v8::ArrayBuffer; -using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::FunctionCallbackInfo; using v8::Int32; +using v8::Integer; using v8::JustVoid; using v8::Local; using v8::Maybe; @@ -34,37 +36,28 @@ using v8::Value; namespace crypto { EVPKeyCtxPointer RsaKeyGenTraits::Setup(RsaKeyPairGenConfig* params) { - EVPKeyCtxPointer ctx( - EVP_PKEY_CTX_new_id( - params->params.variant == kKeyVariantRSA_PSS - ? EVP_PKEY_RSA_PSS - : EVP_PKEY_RSA, - nullptr)); - - if (EVP_PKEY_keygen_init(ctx.get()) <= 0) - return EVPKeyCtxPointer(); - - if (EVP_PKEY_CTX_set_rsa_keygen_bits( - ctx.get(), - params->params.modulus_bits) <= 0) { - return EVPKeyCtxPointer(); + auto ctx = EVPKeyCtxPointer::NewFromID( + params->params.variant == kKeyVariantRSA_PSS ? EVP_PKEY_RSA_PSS + : EVP_PKEY_RSA); + + if (!ctx.initForKeygen() || + !ctx.setRsaKeygenBits(params->params.modulus_bits)) { + return {}; } // 0x10001 is the default RSA exponent. - if (params->params.exponent != 0x10001) { + if (params->params.exponent != EVPKeyCtxPointer::kDefaultRsaExponent) { auto bn = BignumPointer::New(); - CHECK(bn.setWord(params->params.exponent)); - // EVP_CTX accepts ownership of bn on success. - if (EVP_PKEY_CTX_set_rsa_keygen_pubexp(ctx.get(), bn.get()) <= 0) - return EVPKeyCtxPointer(); - - bn.release(); + if (!bn.setWord(params->params.exponent) || + !ctx.setRsaKeygenPubExp(std::move(bn))) { + return {}; + } } if (params->params.variant == kKeyVariantRSA_PSS) { if (params->params.md != nullptr && - EVP_PKEY_CTX_set_rsa_pss_keygen_md(ctx.get(), params->params.md) <= 0) { - return EVPKeyCtxPointer(); + !ctx.setRsaPssKeygenMd(params->params.md)) { + return {}; } // TODO(tniessen): This appears to only be necessary in OpenSSL 3, while @@ -76,11 +69,8 @@ EVPKeyCtxPointer RsaKeyGenTraits::Setup(RsaKeyPairGenConfig* params) { mgf1_md = params->params.md; } - if (mgf1_md != nullptr && - EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md( - ctx.get(), - mgf1_md) <= 0) { - return EVPKeyCtxPointer(); + if (mgf1_md != nullptr && !ctx.setRsaPssKeygenMgf1Md(mgf1_md)) { + return {}; } int saltlen = params->params.saltlen; @@ -88,11 +78,8 @@ EVPKeyCtxPointer RsaKeyGenTraits::Setup(RsaKeyPairGenConfig* params) { saltlen = EVP_MD_size(params->params.md); } - if (saltlen >= 0 && - EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen( - ctx.get(), - saltlen) <= 0) { - return EVPKeyCtxPointer(); + if (saltlen >= 0 && !ctx.setRsaPssSaltlen(saltlen)) { + return {}; } } @@ -154,7 +141,7 @@ Maybe RsaKeyGenTraits::AdditionalConfig( if (!args[*offset]->IsUndefined()) { CHECK(args[*offset]->IsString()); Utf8Value digest(env->isolate(), args[*offset]); - params->params.md = EVP_get_digestbyname(*digest); + params->params.md = ncrypto::getDigestByName(digest.ToStringView()); if (params->params.md == nullptr) { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); @@ -164,7 +151,7 @@ Maybe RsaKeyGenTraits::AdditionalConfig( if (!args[*offset + 1]->IsUndefined()) { CHECK(args[*offset + 1]->IsString()); Utf8Value digest(env->isolate(), args[*offset + 1]); - params->params.mgf1_md = EVP_get_digestbyname(*digest); + params->params.mgf1_md = ncrypto::getDigestByName(digest.ToStringView()); if (params->params.mgf1_md == nullptr) { THROW_ERR_CRYPTO_INVALID_DIGEST( env, "Invalid MGF1 digest: %s", *digest); @@ -196,8 +183,11 @@ WebCryptoKeyExportStatus RSA_JWK_Export(const KeyObjectData& key_data, return WebCryptoKeyExportStatus::FAILED; } -template +using Cipher_t = DataPointer(const EVPKeyPointer& key, + const ncrypto::Rsa::CipherParams& params, + const ncrypto::Buffer in); + +template WebCryptoCipherStatus RSA_Cipher(Environment* env, const KeyObjectData& key_data, const RSACipherConfig& params, @@ -206,45 +196,16 @@ WebCryptoCipherStatus RSA_Cipher(Environment* env, CHECK_NE(key_data.GetKeyType(), kKeyTypeSecret); Mutex::ScopedLock lock(key_data.mutex()); const auto& m_pkey = key_data.GetAsymmetricKey(); + const ncrypto::Rsa::CipherParams nparams{ + .padding = params.padding, + .digest = params.digest, + .label = params.label, + }; - EVPKeyCtxPointer ctx = m_pkey.newCtx(); - - if (!ctx || init(ctx.get()) <= 0) - return WebCryptoCipherStatus::FAILED; - - if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), params.padding) <= 0) { - return WebCryptoCipherStatus::FAILED; - } - - if (params.digest != nullptr && - (EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), params.digest) <= 0 || - EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), params.digest) <= 0)) { - return WebCryptoCipherStatus::FAILED; - } - - if (!SetRsaOaepLabel(ctx, params.label)) return WebCryptoCipherStatus::FAILED; - - size_t out_len = 0; - if (cipher( - ctx.get(), - nullptr, - &out_len, - in.data(), - in.size()) <= 0) { - return WebCryptoCipherStatus::FAILED; - } - - ByteSource::Builder buf(out_len); - - if (cipher(ctx.get(), - buf.data(), - &out_len, - in.data(), - in.size()) <= 0) { - return WebCryptoCipherStatus::FAILED; - } + auto data = cipher(m_pkey, nparams, in); + if (!data) return WebCryptoCipherStatus::FAILED; - *out = std::move(buf).release(out_len); + *out = ByteSource::Allocated(data.release()); return WebCryptoCipherStatus::OK; } } // namespace @@ -316,7 +277,7 @@ Maybe RSACipherTraits::AdditionalConfig( CHECK(args[offset + 1]->IsString()); // digest Utf8Value digest(env->isolate(), args[offset + 1]); - params->digest = EVP_get_digestbyname(*digest); + params->digest = ncrypto::getDigestByName(digest.ToStringView()); if (params->digest == nullptr) { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); @@ -349,12 +310,10 @@ WebCryptoCipherStatus RSACipherTraits::DoCipher(Environment* env, switch (cipher_mode) { case kWebCryptoCipherEncrypt: CHECK_EQ(key_data.GetKeyType(), kKeyTypePublic); - return RSA_Cipher( - env, key_data, params, in, out); + return RSA_Cipher(env, key_data, params, in, out); case kWebCryptoCipherDecrypt: CHECK_EQ(key_data.GetKeyType(), kKeyTypePrivate); - return RSA_Cipher( - env, key_data, params, in, out); + return RSA_Cipher(env, key_data, params, in, out); } return WebCryptoCipherStatus::FAILED; } @@ -364,50 +323,37 @@ Maybe ExportJWKRsaKey(Environment* env, Local target) { Mutex::ScopedLock lock(key.mutex()); const auto& m_pkey = key.GetAsymmetricKey(); - int type = m_pkey.id(); - CHECK(type == EVP_PKEY_RSA || type == EVP_PKEY_RSA_PSS); - // TODO(tniessen): Remove the "else" branch once we drop support for OpenSSL - // versions older than 1.1.1e via FIPS / dynamic linking. - const RSA* rsa; - if (OpenSSL_version_num() >= 0x1010105fL) { - rsa = EVP_PKEY_get0_RSA(m_pkey.get()); - } else { - rsa = static_cast(EVP_PKEY_get0(m_pkey.get())); - } - CHECK_NOT_NULL(rsa); - - const BIGNUM* n; - const BIGNUM* e; - const BIGNUM* d; - const BIGNUM* p; - const BIGNUM* q; - const BIGNUM* dp; - const BIGNUM* dq; - const BIGNUM* qi; - RSA_get0_key(rsa, &n, &e, &d); - - if (target->Set( - env->context(), - env->jwk_kty_string(), - env->jwk_rsa_string()).IsNothing()) { + const ncrypto::Rsa rsa = m_pkey; + if (!rsa || + target->Set(env->context(), env->jwk_kty_string(), env->jwk_rsa_string()) + .IsNothing()) { return Nothing(); } - if (SetEncodedValue(env, target, env->jwk_n_string(), n).IsNothing() || - SetEncodedValue(env, target, env->jwk_e_string(), e).IsNothing()) { + auto pub_key = rsa.getPublicKey(); + + if (SetEncodedValue(env, target, env->jwk_n_string(), pub_key.n) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_e_string(), pub_key.e) + .IsNothing()) { return Nothing(); } if (key.GetKeyType() == kKeyTypePrivate) { - RSA_get0_factors(rsa, &p, &q); - RSA_get0_crt_params(rsa, &dp, &dq, &qi); - if (SetEncodedValue(env, target, env->jwk_d_string(), d).IsNothing() || - SetEncodedValue(env, target, env->jwk_p_string(), p).IsNothing() || - SetEncodedValue(env, target, env->jwk_q_string(), q).IsNothing() || - SetEncodedValue(env, target, env->jwk_dp_string(), dp).IsNothing() || - SetEncodedValue(env, target, env->jwk_dq_string(), dq).IsNothing() || - SetEncodedValue(env, target, env->jwk_qi_string(), qi).IsNothing()) { + auto pvt_key = rsa.getPrivateKey(); + if (SetEncodedValue(env, target, env->jwk_d_string(), pub_key.d) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_p_string(), pvt_key.p) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_q_string(), pvt_key.q) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_dp_string(), pvt_key.dp) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_dq_string(), pvt_key.dq) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_qi_string(), pvt_key.qi) + .IsNothing()) { return Nothing(); } } @@ -440,15 +386,12 @@ KeyObjectData ImportJWKRsaKey(Environment* env, KeyType type = d_value->IsString() ? kKeyTypePrivate : kKeyTypePublic; RSAPointer rsa(RSA_new()); + ncrypto::Rsa rsa_view(rsa.get()); ByteSource n = ByteSource::FromEncodedString(env, n_value.As()); ByteSource e = ByteSource::FromEncodedString(env, e_value.As()); - if (!RSA_set0_key( - rsa.get(), - n.ToBN().release(), - e.ToBN().release(), - nullptr)) { + if (!rsa_view.setPublicKey(n.ToBN(), e.ToBN())) { THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key"); return {}; } @@ -485,20 +428,15 @@ KeyObjectData ImportJWKRsaKey(Environment* env, ByteSource dq = ByteSource::FromEncodedString(env, dq_value.As()); ByteSource qi = ByteSource::FromEncodedString(env, qi_value.As()); - if (!RSA_set0_key(rsa.get(), nullptr, nullptr, d.ToBN().release()) || - !RSA_set0_factors(rsa.get(), p.ToBN().release(), q.ToBN().release()) || - !RSA_set0_crt_params( - rsa.get(), - dp.ToBN().release(), - dq.ToBN().release(), - qi.ToBN().release())) { + if (!rsa_view.setPrivateKey( + d.ToBN(), q.ToBN(), p.ToBN(), dp.ToBN(), dq.ToBN(), qi.ToBN())) { THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key"); return {}; } } - auto pkey = EVPKeyPointer::New(); - CHECK_EQ(EVP_PKEY_set1_RSA(pkey.get(), rsa.get()), 1); + auto pkey = EVPKeyPointer::NewRSA(std::move(rsa)); + if (!pkey) return {}; return KeyObjectData::CreateAsymmetric(type, std::move(pkey)); } @@ -506,43 +444,32 @@ KeyObjectData ImportJWKRsaKey(Environment* env, Maybe GetRsaKeyDetail(Environment* env, const KeyObjectData& key, Local target) { - const BIGNUM* e; // Public Exponent - const BIGNUM* n; // Modulus - Mutex::ScopedLock lock(key.mutex()); const auto& m_pkey = key.GetAsymmetricKey(); - int type = m_pkey.id(); - CHECK(type == EVP_PKEY_RSA || type == EVP_PKEY_RSA_PSS); // TODO(tniessen): Remove the "else" branch once we drop support for OpenSSL // versions older than 1.1.1e via FIPS / dynamic linking. - const RSA* rsa; - if (OpenSSL_version_num() >= 0x1010105fL) { - rsa = EVP_PKEY_get0_RSA(m_pkey.get()); - } else { - rsa = static_cast(EVP_PKEY_get0(m_pkey.get())); - } - CHECK_NOT_NULL(rsa); + const ncrypto::Rsa rsa = m_pkey; + if (!rsa) return Nothing(); - RSA_get0_key(rsa, &n, &e, nullptr); + auto pub_key = rsa.getPublicKey(); if (target ->Set(env->context(), env->modulus_length_string(), - Number::New(env->isolate(), - static_cast(BignumPointer::GetBitCount(n)))) + Number::New( + env->isolate(), + static_cast(BignumPointer::GetBitCount(pub_key.n)))) .IsNothing()) { return Nothing(); } - std::unique_ptr public_exponent; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - public_exponent = ArrayBuffer::NewBackingStore( - env->isolate(), BignumPointer::GetByteCount(e)); - } + auto public_exponent = ArrayBuffer::NewBackingStore( + env->isolate(), + BignumPointer::GetByteCount(pub_key.e), + BackingStoreInitializationMode::kUninitialized); CHECK_EQ(BignumPointer::EncodePaddedInto( - e, + pub_key.e, static_cast(public_exponent->Data()), public_exponent->ByteLength()), public_exponent->ByteLength()); @@ -555,7 +482,7 @@ Maybe GetRsaKeyDetail(Environment* env, return Nothing(); } - if (type == EVP_PKEY_RSA_PSS) { + if (m_pkey.id() == EVP_PKEY_RSA_PSS) { // Due to the way ASN.1 encoding works, default values are omitted when // encoding the data structure. However, there are also RSA-PSS keys for // which no parameters are set. In that case, the ASN.1 RSASSA-PSS-params @@ -565,64 +492,34 @@ Maybe GetRsaKeyDetail(Environment* env, // In that case, RSA_get0_pss_params does not return nullptr but all fields // of the returned RSA_PSS_PARAMS will be set to nullptr. - const RSA_PSS_PARAMS* params = RSA_get0_pss_params(rsa); - if (params != nullptr) { - int hash_nid = NID_sha1; - int mgf_nid = NID_mgf1; - int mgf1_hash_nid = NID_sha1; - int64_t salt_length = 20; - - if (params->hashAlgorithm != nullptr) { - const ASN1_OBJECT* hash_obj; - X509_ALGOR_get0(&hash_obj, nullptr, nullptr, params->hashAlgorithm); - hash_nid = OBJ_obj2nid(hash_obj); - } - + auto maybe_params = rsa.getPssParams(); + if (maybe_params.has_value()) { + auto& params = maybe_params.value(); if (target - ->Set( - env->context(), - env->hash_algorithm_string(), - OneByteString(env->isolate(), OBJ_nid2ln(hash_nid))) + ->Set(env->context(), + env->hash_algorithm_string(), + OneByteString(env->isolate(), params.digest)) .IsNothing()) { return Nothing(); } - if (params->maskGenAlgorithm != nullptr) { - const ASN1_OBJECT* mgf_obj; - X509_ALGOR_get0(&mgf_obj, nullptr, nullptr, params->maskGenAlgorithm); - mgf_nid = OBJ_obj2nid(mgf_obj); - if (mgf_nid == NID_mgf1) { - const ASN1_OBJECT* mgf1_hash_obj; - X509_ALGOR_get0(&mgf1_hash_obj, nullptr, nullptr, params->maskHash); - mgf1_hash_nid = OBJ_obj2nid(mgf1_hash_obj); - } - } - // If, for some reason, the MGF is not MGF1, then the MGF1 hash function // is intentionally not added to the object. - if (mgf_nid == NID_mgf1) { + if (params.mgf1_digest.has_value()) { + auto digest = params.mgf1_digest.value(); if (target - ->Set( - env->context(), - env->mgf1_hash_algorithm_string(), - OneByteString(env->isolate(), OBJ_nid2ln(mgf1_hash_nid))) + ->Set(env->context(), + env->mgf1_hash_algorithm_string(), + OneByteString(env->isolate(), digest)) .IsNothing()) { return Nothing(); } } - if (params->saltLength != nullptr) { - if (ASN1_INTEGER_get_int64(&salt_length, params->saltLength) != 1) { - ThrowCryptoError(env, ERR_get_error(), "ASN1_INTEGER_get_in64 error"); - return Nothing(); - } - } - if (target - ->Set( - env->context(), - env->salt_length_string(), - Number::New(env->isolate(), static_cast(salt_length))) + ->Set(env->context(), + env->salt_length_string(), + Integer::New(env->isolate(), params.salt_length)) .IsNothing()) { return Nothing(); } diff --git a/src/crypto/crypto_sig.cc b/src/crypto/crypto_sig.cc index abb8a804c1b508..175a8e92ef437f 100644 --- a/src/crypto/crypto_sig.cc +++ b/src/crypto/crypto_sig.cc @@ -14,20 +14,20 @@ namespace node { using ncrypto::BignumPointer; using ncrypto::ClearErrorOnReturn; +using ncrypto::DataPointer; using ncrypto::ECDSASigPointer; -using ncrypto::ECKeyPointer; using ncrypto::EVPKeyCtxPointer; using ncrypto::EVPKeyPointer; using ncrypto::EVPMDCtxPointer; using v8::ArrayBuffer; using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Boolean; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; using v8::Int32; using v8::Isolate; -using v8::Just; using v8::JustVoid; using v8::Local; using v8::Maybe; @@ -39,44 +39,40 @@ using v8::Value; namespace crypto { namespace { -bool ValidateDSAParameters(EVP_PKEY* key) { - /* Validate DSA2 parameters from FIPS 186-4 */ - auto id = EVPKeyPointer::base_id(key); -#if OPENSSL_VERSION_MAJOR >= 3 - if (EVP_default_properties_is_fips_enabled(nullptr) && EVP_PKEY_DSA == id) { -#else - if (FIPS_mode() && EVP_PKEY_DSA == id) { -#endif - const DSA* dsa = EVP_PKEY_get0_DSA(key); - const BIGNUM* p; - const BIGNUM* q; - DSA_get0_pqg(dsa, &p, &q, nullptr); - int L = BignumPointer::GetBitCount(p); - int N = BignumPointer::GetBitCount(q); - - return (L == 1024 && N == 160) || - (L == 2048 && N == 224) || - (L == 2048 && N == 256) || - (L == 3072 && N == 256); +int GetPaddingFromJS(const EVPKeyPointer& key, Local val) { + int padding = key.getDefaultSignPadding(); + if (!val->IsUndefined()) [[likely]] { + CHECK(val->IsInt32()); + padding = val.As()->Value(); } + return padding; +} - return true; +std::optional GetSaltLenFromJS(Local val) { + std::optional salt_len; + if (!val->IsUndefined()) [[likely]] { + CHECK(val->IsInt32()); + salt_len = val.As()->Value(); + } + return salt_len; +} + +DSASigEnc GetDSASigEncFromJS(Local val) { + CHECK(val->IsInt32()); + int i = val.As()->Value(); + if (i < 0 || i >= static_cast(DSASigEnc::Invalid)) [[unlikely]] { + return DSASigEnc::Invalid; + } + return static_cast(val.As()->Value()); } bool ApplyRSAOptions(const EVPKeyPointer& pkey, EVP_PKEY_CTX* pkctx, int padding, - const Maybe& salt_len) { - int id = pkey.id(); - if (id == EVP_PKEY_RSA || id == EVP_PKEY_RSA2 || id == EVP_PKEY_RSA_PSS) { - if (EVP_PKEY_CTX_set_rsa_padding(pkctx, padding) <= 0) - return false; - if (padding == RSA_PKCS1_PSS_PADDING && salt_len.IsJust()) { - if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, salt_len.FromJust()) <= 0) - return false; - } + std::optional salt_len) { + if (pkey.isRsaVariant()) { + return EVPKeyCtxPointer::setRsaPadding(pkctx, padding, salt_len); } - return true; } @@ -84,38 +80,31 @@ std::unique_ptr Node_SignFinal(Environment* env, EVPMDCtxPointer&& mdctx, const EVPKeyPointer& pkey, int padding, - Maybe pss_salt_len) { - unsigned char m[EVP_MAX_MD_SIZE]; - unsigned int m_len; - - if (!EVP_DigestFinal_ex(mdctx.get(), m, &m_len)) + std::optional pss_salt_len) { + auto data = mdctx.digestFinal(mdctx.getExpectedSize()); + if (!data) [[unlikely]] return nullptr; - size_t sig_len = pkey.size(); - std::unique_ptr sig; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - sig = ArrayBuffer::NewBackingStore(env->isolate(), sig_len); - } + auto sig = ArrayBuffer::NewBackingStore(env->isolate(), pkey.size()); + ncrypto::Buffer sig_buf{ + .data = static_cast(sig->Data()), + .len = pkey.size(), + }; + EVPKeyCtxPointer pkctx = pkey.newCtx(); - if (pkctx && EVP_PKEY_sign_init(pkctx.get()) > 0 && + if (pkctx.initForSign() > 0 && ApplyRSAOptions(pkey, pkctx.get(), padding, pss_salt_len) && - EVP_PKEY_CTX_set_signature_md(pkctx.get(), EVP_MD_CTX_md(mdctx.get())) > - 0 && - EVP_PKEY_sign(pkctx.get(), - static_cast(sig->Data()), - &sig_len, - m, - m_len) > 0) { - CHECK_LE(sig_len, sig->ByteLength()); - if (sig_len == 0) { - sig = ArrayBuffer::NewBackingStore(env->isolate(), 0); - } else if (sig_len != sig->ByteLength()) { - std::unique_ptr old_sig = std::move(sig); - sig = ArrayBuffer::NewBackingStore(env->isolate(), sig_len); - memcpy(static_cast(sig->Data()), - static_cast(old_sig->Data()), - sig_len); + pkctx.setSignatureMd(mdctx) && pkctx.signInto(data, &sig_buf)) + [[likely]] { + CHECK_LE(sig_buf.len, sig->ByteLength()); + if (sig_buf.len < sig->ByteLength()) { + auto new_sig = ArrayBuffer::NewBackingStore(env->isolate(), sig_buf.len); + if (sig_buf.len > 0) [[likely]] { + memcpy(static_cast(new_sig->Data()), + static_cast(sig->Data()), + sig_buf.len); + } + sig = std::move(new_sig); } return sig; } @@ -123,62 +112,26 @@ std::unique_ptr Node_SignFinal(Environment* env, return nullptr; } -int GetDefaultSignPadding(const EVPKeyPointer& m_pkey) { - return m_pkey.id() == EVP_PKEY_RSA_PSS ? RSA_PKCS1_PSS_PADDING - : RSA_PKCS1_PADDING; -} - -unsigned int GetBytesOfRS(const EVPKeyPointer& pkey) { - int bits, base_id = pkey.base_id(); - - if (base_id == EVP_PKEY_DSA) { - const DSA* dsa_key = EVP_PKEY_get0_DSA(pkey.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)); - } else if (base_id == EVP_PKEY_EC) { - bits = EC_GROUP_order_bits(ECKeyPointer::GetGroup(pkey)); - } else { - return kNoDsaSignature; - } - - return (bits + 7) / 8; -} - -bool ExtractP1363( - const unsigned char* sig_data, - unsigned char* out, - size_t len, - size_t n) { - ncrypto::Buffer sig_buffer{ - .data = sig_data, - .len = len, - }; - auto asn1_sig = ECDSASigPointer::Parse(sig_buffer); - if (!asn1_sig) - return false; - - return BignumPointer::EncodePaddedInto(asn1_sig.r(), out, n) > 0 && - BignumPointer::EncodePaddedInto(asn1_sig.s(), out + n, n) > 0; -} - // Returns the maximum size of each of the integers (r, s) of the DSA signature. std::unique_ptr ConvertSignatureToP1363( Environment* env, const EVPKeyPointer& pkey, std::unique_ptr&& signature) { - unsigned int n = GetBytesOfRS(pkey); - if (n == kNoDsaSignature) - return std::move(signature); + uint32_t n = pkey.getBytesOfRS().value_or(kNoDsaSignature); + if (n == kNoDsaSignature) return std::move(signature); - std::unique_ptr buf; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - buf = ArrayBuffer::NewBackingStore(env->isolate(), 2 * n); - } - if (!ExtractP1363(static_cast(signature->Data()), - static_cast(buf->Data()), - signature->ByteLength(), n)) + auto buf = ArrayBuffer::NewBackingStore( + env->isolate(), 2 * n, BackingStoreInitializationMode::kUninitialized); + + ncrypto::Buffer sig_buffer{ + .data = static_cast(signature->Data()), + .len = signature->ByteLength(), + }; + + if (!ncrypto::extractP1363( + sig_buffer, static_cast(buf->Data()), n)) { return std::move(signature); + } return buf; } @@ -187,30 +140,33 @@ std::unique_ptr ConvertSignatureToP1363( ByteSource ConvertSignatureToP1363(Environment* env, const EVPKeyPointer& pkey, const ByteSource& signature) { - unsigned int n = GetBytesOfRS(pkey); - if (n == kNoDsaSignature) - return ByteSource(); + unsigned int n = pkey.getBytesOfRS().value_or(kNoDsaSignature); + if (n == kNoDsaSignature) [[unlikely]] + return {}; - const unsigned char* sig_data = signature.data(); + auto data = DataPointer::Alloc(n * 2); + if (!data) [[unlikely]] + return {}; + unsigned char* out = static_cast(data.get()); - ByteSource::Builder out(n * 2); - memset(out.data(), 0, n * 2); + // Extracting the signature may not actually use all of the allocated space. + // We need to ensure that the buffer is zeroed out before use. + data.zero(); - if (!ExtractP1363(sig_data, out.data(), signature.size(), n)) - return ByteSource(); + if (!ncrypto::extractP1363(signature, out, n)) [[unlikely]] { + return {}; + } - return std::move(out).release(); + return ByteSource::Allocated(data.release()); } ByteSource ConvertSignatureToDER(const EVPKeyPointer& pkey, ByteSource&& out) { - unsigned int n = GetBytesOfRS(pkey); - if (n == kNoDsaSignature) - return std::move(out); + unsigned int n = pkey.getBytesOfRS().value_or(kNoDsaSignature); + if (n == kNoDsaSignature) return std::move(out); const unsigned char* sig_data = out.data(); - if (out.size() != 2 * n) - return ByteSource(); + if (out.size() != 2 * n) return {}; auto asn1_sig = ECDSASigPointer::New(); CHECK(asn1_sig); @@ -221,7 +177,8 @@ ByteSource ConvertSignatureToDER(const EVPKeyPointer& pkey, ByteSource&& out) { CHECK(asn1_sig.setParams(std::move(r), std::move(s))); auto buf = asn1_sig.encode(); - if (buf.len <= 0) return ByteSource(); + if (buf.len <= 0) [[unlikely]] + return {}; CHECK_NOT_NULL(buf.data); return ByteSource::Allocated(buf); @@ -231,95 +188,87 @@ void CheckThrow(Environment* env, SignBase::Error error) { HandleScope scope(env->isolate()); switch (error) { - case SignBase::Error::kSignUnknownDigest: + case SignBase::Error::UnknownDigest: return THROW_ERR_CRYPTO_INVALID_DIGEST(env); - case SignBase::Error::kSignNotInitialised: + case SignBase::Error::NotInitialised: return THROW_ERR_CRYPTO_INVALID_STATE(env, "Not initialised"); - case SignBase::Error::kSignMalformedSignature: + case SignBase::Error::MalformedSignature: return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Malformed signature"); - case SignBase::Error::kSignInit: - case SignBase::Error::kSignUpdate: - case SignBase::Error::kSignPrivateKey: - case SignBase::Error::kSignPublicKey: - { - unsigned long err = ERR_get_error(); // NOLINT(runtime/int) - if (err) - return ThrowCryptoError(env, err); - switch (error) { - case SignBase::Error::kSignInit: - return THROW_ERR_CRYPTO_OPERATION_FAILED(env, - "EVP_SignInit_ex failed"); - case SignBase::Error::kSignUpdate: - return THROW_ERR_CRYPTO_OPERATION_FAILED(env, - "EVP_SignUpdate failed"); - case SignBase::Error::kSignPrivateKey: - return THROW_ERR_CRYPTO_OPERATION_FAILED(env, - "PEM_read_bio_PrivateKey failed"); - case SignBase::Error::kSignPublicKey: - return THROW_ERR_CRYPTO_OPERATION_FAILED(env, - "PEM_read_bio_PUBKEY failed"); - default: - ABORT(); - } + case SignBase::Error::Init: + case SignBase::Error::Update: + case SignBase::Error::PrivateKey: + case SignBase::Error::PublicKey: { + unsigned long err = ERR_get_error(); // NOLINT(runtime/int) + if (err) return ThrowCryptoError(env, err); + switch (error) { + case SignBase::Error::Init: + return THROW_ERR_CRYPTO_OPERATION_FAILED(env, + "EVP_SignInit_ex failed"); + case SignBase::Error::Update: + return THROW_ERR_CRYPTO_OPERATION_FAILED(env, + "EVP_SignUpdate failed"); + case SignBase::Error::PrivateKey: + return THROW_ERR_CRYPTO_OPERATION_FAILED( + env, "PEM_read_bio_PrivateKey failed"); + case SignBase::Error::PublicKey: + return THROW_ERR_CRYPTO_OPERATION_FAILED( + env, "PEM_read_bio_PUBKEY failed"); + default: + ABORT(); } + } - case SignBase::Error::kSignOk: + case SignBase::Error::Ok: return; } } -bool IsOneShot(const EVPKeyPointer& key) { - return key.id() == EVP_PKEY_ED25519 || key.id() == EVP_PKEY_ED448; -} - -bool UseP1363Encoding(const EVPKeyPointer& key, const DSASigEnc& dsa_encoding) { - return (key.id() == EVP_PKEY_EC || key.id() == EVP_PKEY_DSA) && - dsa_encoding == kSigEncP1363; +bool UseP1363Encoding(const EVPKeyPointer& key, const DSASigEnc dsa_encoding) { + return key.isSigVariant() && dsa_encoding == DSASigEnc::P1363; } } // namespace -SignBase::Error SignBase::Init(const char* sign_type) { +SignBase::Error SignBase::Init(std::string_view digest) { CHECK_NULL(mdctx_); - // Historically, "dss1" and "DSS1" were DSA aliases for SHA-1 - // exposed through the public API. - if (strcmp(sign_type, "dss1") == 0 || - strcmp(sign_type, "DSS1") == 0) { - sign_type = "SHA1"; - } - const EVP_MD* md = EVP_get_digestbyname(sign_type); - if (md == nullptr) - return kSignUnknownDigest; - - mdctx_.reset(EVP_MD_CTX_new()); - if (!mdctx_ || !EVP_DigestInit_ex(mdctx_.get(), md, nullptr)) { + auto md = ncrypto::getDigestByName(digest); + if (md == nullptr) [[unlikely]] + return Error::UnknownDigest; + + mdctx_ = EVPMDCtxPointer::New(); + + if (!mdctx_.digestInit(md)) [[unlikely]] { mdctx_.reset(); - return kSignInit; + return Error::Init; } - return kSignOk; + return Error::Ok; } SignBase::Error SignBase::Update(const char* data, size_t len) { - if (mdctx_ == nullptr) - return kSignNotInitialised; - if (!EVP_DigestUpdate(mdctx_.get(), data, len)) - return kSignUpdate; - return kSignOk; + if (mdctx_ == nullptr) [[unlikely]] + return Error::NotInitialised; + + ncrypto::Buffer buf{ + .data = data, + .len = len, + }; + + return mdctx_.digestUpdate(buf) ? Error::Ok : Error::Update; } SignBase::SignBase(Environment* env, Local wrap) - : BaseObject(env, wrap) {} + : BaseObject(env, wrap) { + MakeWeak(); +} void SignBase::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackFieldWithSize("mdctx", mdctx_ ? kSizeOf_EVP_MD_CTX : 0); } -Sign::Sign(Environment* env, Local wrap) : SignBase(env, wrap) { - MakeWeak(); -} +Sign::Sign(Environment* env, Local wrap) : SignBase(env, wrap) {} void Sign::Initialize(Environment* env, Local target) { Isolate* isolate = env->isolate(); @@ -335,8 +284,13 @@ void Sign::Initialize(Environment* env, Local target) { SignJob::Initialize(env, target); - constexpr int kSignJobModeSign = SignConfiguration::kSign; - constexpr int kSignJobModeVerify = SignConfiguration::kVerify; + constexpr int kSignJobModeSign = + static_cast(SignConfiguration::Mode::Sign); + constexpr int kSignJobModeVerify = + static_cast(SignConfiguration::Mode::Verify); + + constexpr auto kSigEncDER = DSASigEnc::DER; + constexpr auto kSigEncP1363 = DSASigEnc::P1363; NODE_DEFINE_CONSTANT(target, kSignJobModeSign); NODE_DEFINE_CONSTANT(target, kSignJobModeVerify); @@ -363,8 +317,8 @@ void Sign::SignInit(const FunctionCallbackInfo& args) { Sign* sign; ASSIGN_OR_RETURN_UNWRAP(&sign, args.This()); - const node::Utf8Value sign_type(args.GetIsolate(), args[0]); - crypto::CheckThrow(env, sign->Init(*sign_type)); + const node::Utf8Value sign_type(env->isolate(), args[0]); + crypto::CheckThrow(env, sign->Init(sign_type.ToStringView())); } void Sign::SignUpdate(const FunctionCallbackInfo& args) { @@ -380,20 +334,22 @@ void Sign::SignUpdate(const FunctionCallbackInfo& args) { Sign::SignResult Sign::SignFinal(const EVPKeyPointer& pkey, int padding, - const Maybe& salt_len, + std::optional salt_len, DSASigEnc dsa_sig_enc) { - if (!mdctx_) - return SignResult(kSignNotInitialised); + if (!mdctx_) [[unlikely]] { + return SignResult(Error::NotInitialised); + } EVPMDCtxPointer mdctx = std::move(mdctx_); - if (!ValidateDSAParameters(pkey.get())) - return SignResult(kSignPrivateKey); + if (!pkey.validateDsaParameters()) { + return SignResult(Error::PrivateKey); + } - std::unique_ptr buffer = + auto buffer = Node_SignFinal(env(), std::move(mdctx), pkey, padding, salt_len); - Error error = buffer ? kSignOk : kSignPrivateKey; - if (error == kSignOk && dsa_sig_enc == kSigEncP1363) { + Error error = buffer ? Error::Ok : Error::PrivateKey; + if (error == Error::Ok && dsa_sig_enc == DSASigEnc::P1363) { buffer = ConvertSignatureToP1363(env(), pkey, std::move(buffer)); CHECK_NOT_NULL(buffer->Data()); } @@ -412,49 +368,34 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) { if (!data) [[unlikely]] return; const auto& key = data.GetAsymmetricKey(); - if (!key) + if (!key) [[unlikely]] return; - if (IsOneShot(key)) { + if (key.isOneShotVariant()) [[unlikely]] { THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env); return; } - int padding = GetDefaultSignPadding(key); - if (!args[offset]->IsUndefined()) { - CHECK(args[offset]->IsInt32()); - padding = args[offset].As()->Value(); - } - - Maybe salt_len = Nothing(); - if (!args[offset + 1]->IsUndefined()) { - CHECK(args[offset + 1]->IsInt32()); - salt_len = Just(args[offset + 1].As()->Value()); + int padding = GetPaddingFromJS(key, args[offset]); + std::optional salt_len = GetSaltLenFromJS(args[offset + 1]); + DSASigEnc dsa_sig_enc = GetDSASigEncFromJS(args[offset + 2]); + if (dsa_sig_enc == DSASigEnc::Invalid) [[unlikely]] { + THROW_ERR_OUT_OF_RANGE(env, "invalid signature encoding"); + return; } - CHECK(args[offset + 2]->IsInt32()); - DSASigEnc dsa_sig_enc = - static_cast(args[offset + 2].As()->Value()); + SignResult ret = sign->SignFinal(key, padding, salt_len, dsa_sig_enc); - SignResult ret = sign->SignFinal( - key, - padding, - salt_len, - dsa_sig_enc); - - if (ret.error != kSignOk) + if (ret.error != Error::Ok) [[unlikely]] { return crypto::CheckThrow(env, ret.error); + } - Local ab = - ArrayBuffer::New(env->isolate(), std::move(ret.signature)); + auto ab = ArrayBuffer::New(env->isolate(), std::move(ret.signature)); args.GetReturnValue().Set( Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local())); } -Verify::Verify(Environment* env, Local wrap) - : SignBase(env, wrap) { - MakeWeak(); -} +Verify::Verify(Environment* env, Local wrap) : SignBase(env, wrap) {} void Verify::Initialize(Environment* env, Local target) { Isolate* isolate = env->isolate(); @@ -486,8 +427,8 @@ void Verify::VerifyInit(const FunctionCallbackInfo& args) { Verify* verify; ASSIGN_OR_RETURN_UNWRAP(&verify, args.This()); - const node::Utf8Value verify_type(args.GetIsolate(), args[0]); - crypto::CheckThrow(env, verify->Init(*verify_type)); + const node::Utf8Value verify_type(env->isolate(), args[0]); + crypto::CheckThrow(env, verify->Init(verify_type.ToStringView())); } void Verify::VerifyUpdate(const FunctionCallbackInfo& args) { @@ -495,8 +436,9 @@ void Verify::VerifyUpdate(const FunctionCallbackInfo& args) { const FunctionCallbackInfo& args, const char* data, size_t size) { Environment* env = Environment::GetCurrent(args); - if (size > INT_MAX) [[unlikely]] + if (size > INT_MAX) [[unlikely]] { return THROW_ERR_OUT_OF_RANGE(env, "data is too long"); + } Error err = verify->Update(data, size); crypto::CheckThrow(verify->env(), err); }); @@ -505,35 +447,30 @@ void Verify::VerifyUpdate(const FunctionCallbackInfo& args) { SignBase::Error Verify::VerifyFinal(const EVPKeyPointer& pkey, const ByteSource& sig, int padding, - const Maybe& saltlen, + std::optional saltlen, bool* verify_result) { - if (!mdctx_) - return kSignNotInitialised; + if (!mdctx_) [[unlikely]] + return Error::NotInitialised; - unsigned char m[EVP_MAX_MD_SIZE]; - unsigned int m_len; *verify_result = false; EVPMDCtxPointer mdctx = std::move(mdctx_); - if (!EVP_DigestFinal_ex(mdctx.get(), m, &m_len)) - return kSignPublicKey; + auto data = mdctx.digestFinal(mdctx.getExpectedSize()); + if (!data) [[unlikely]] + return Error::PublicKey; EVPKeyCtxPointer pkctx = pkey.newCtx(); - if (pkctx) { - const int init_ret = EVP_PKEY_verify_init(pkctx.get()); - if (init_ret == -2) { - return kSignPublicKey; - } + if (pkctx) [[likely]] { + const int init_ret = pkctx.initForVerify(); + if (init_ret == -2) [[unlikely]] + return Error::PublicKey; if (init_ret > 0 && ApplyRSAOptions(pkey, pkctx.get(), padding, saltlen) && - EVP_PKEY_CTX_set_signature_md(pkctx.get(), EVP_MD_CTX_md(mdctx.get())) > - 0) { - const unsigned char* s = sig.data(); - const int r = EVP_PKEY_verify(pkctx.get(), s, sig.size(), m, m_len); - *verify_result = r == 1; + pkctx.setSignatureMd(mdctx)) { + *verify_result = pkctx.verify(sig, data); } } - return kSignOk; + return Error::Ok; } void Verify::VerifyFinal(const FunctionCallbackInfo& args) { @@ -545,47 +482,42 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) { unsigned int offset = 0; auto data = KeyObjectData::GetPublicOrPrivateKeyFromJs(args, &offset); - if (!data) return; - const auto& pkey = data.GetAsymmetricKey(); - if (!pkey) + if (!data) [[unlikely]] + return; + const auto& key = data.GetAsymmetricKey(); + if (!key) [[unlikely]] return; - if (IsOneShot(pkey)) { + if (key.isOneShotVariant()) [[unlikely]] { THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env); return; } ArrayBufferOrViewContents hbuf(args[offset]); - if (!hbuf.CheckSizeInt32()) [[unlikely]] + if (!hbuf.CheckSizeInt32()) [[unlikely]] { return THROW_ERR_OUT_OF_RANGE(env, "buffer is too big"); - - int padding = GetDefaultSignPadding(pkey); - if (!args[offset + 1]->IsUndefined()) { - CHECK(args[offset + 1]->IsInt32()); - padding = args[offset + 1].As()->Value(); } - Maybe salt_len = Nothing(); - if (!args[offset + 2]->IsUndefined()) { - CHECK(args[offset + 2]->IsInt32()); - salt_len = Just(args[offset + 2].As()->Value()); + int padding = GetPaddingFromJS(key, args[offset + 1]); + std::optional salt_len = GetSaltLenFromJS(args[offset + 2]); + DSASigEnc dsa_sig_enc = GetDSASigEncFromJS(args[offset + 3]); + if (dsa_sig_enc == DSASigEnc::Invalid) [[unlikely]] { + THROW_ERR_OUT_OF_RANGE(env, "invalid signature encoding"); + return; } - CHECK(args[offset + 3]->IsInt32()); - DSASigEnc dsa_sig_enc = - static_cast(args[offset + 3].As()->Value()); - ByteSource signature = hbuf.ToByteSource(); - if (dsa_sig_enc == kSigEncP1363) { - signature = ConvertSignatureToDER(pkey, hbuf.ToByteSource()); - if (signature.data() == nullptr) - return crypto::CheckThrow(env, Error::kSignMalformedSignature); + if (dsa_sig_enc == DSASigEnc::P1363) { + signature = ConvertSignatureToDER(key, hbuf.ToByteSource()); + if (signature.data() == nullptr) [[unlikely]] { + return crypto::CheckThrow(env, Error::MalformedSignature); + } } bool verify_result; - Error err = verify->VerifyFinal(pkey, signature, padding, - salt_len, &verify_result); - if (err != kSignOk) + Error err = + verify->VerifyFinal(key, signature, padding, salt_len, &verify_result); + if (err != Error::Ok) [[unlikely]] return crypto::CheckThrow(env, err); args.GetReturnValue().Set(verify_result); } @@ -633,7 +565,7 @@ Maybe SignTraits::AdditionalConfig( static_cast(args[offset].As()->Value()); unsigned int keyParamOffset = offset + 1; - if (params->mode == SignConfiguration::kVerify) { + if (params->mode == SignConfiguration::Mode::Verify) { auto data = KeyObjectData::GetPublicOrPrivateKeyFromJs(args, &keyParamOffset); if (!data) return Nothing(); @@ -655,8 +587,8 @@ Maybe SignTraits::AdditionalConfig( if (args[offset + 6]->IsString()) { Utf8Value digest(env->isolate(), args[offset + 6]); - params->digest = EVP_get_digestbyname(*digest); - if (params->digest == nullptr) { + params->digest = ncrypto::getDigestByName(digest.ToStringView()); + if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); } @@ -664,24 +596,24 @@ Maybe SignTraits::AdditionalConfig( if (args[offset + 7]->IsInt32()) { // Salt length params->flags |= SignConfiguration::kHasSaltLength; - params->salt_length = args[offset + 7].As()->Value(); + params->salt_length = + GetSaltLenFromJS(args[offset + 7]).value_or(params->salt_length); } if (args[offset + 8]->IsUint32()) { // Padding params->flags |= SignConfiguration::kHasPadding; - params->padding = args[offset + 8].As()->Value(); + params->padding = + GetPaddingFromJS(params->key.GetAsymmetricKey(), args[offset + 8]); } if (args[offset + 9]->IsUint32()) { // DSA Encoding - params->dsa_encoding = - static_cast(args[offset + 9].As()->Value()); - if (params->dsa_encoding != kSigEncDER && - params->dsa_encoding != kSigEncP1363) { + params->dsa_encoding = GetDSASigEncFromJS(args[offset + 9]); + if (params->dsa_encoding == DSASigEnc::Invalid) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "invalid signature encoding"); return Nothing(); } } - if (params->mode == SignConfiguration::kVerify) { + if (params->mode == SignConfiguration::Mode::Verify) { ArrayBufferOrViewContents signature(args[offset + 10]); if (!signature.CheckSizeInt32()) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "signature is too big"); @@ -708,97 +640,69 @@ bool SignTraits::DeriveBits( const SignConfiguration& params, ByteSource* out) { ClearErrorOnReturn clear_error_on_return; - EVPMDCtxPointer context(EVP_MD_CTX_new()); - EVP_PKEY_CTX* ctx = nullptr; - + auto context = EVPMDCtxPointer::New(); + if (!context) [[unlikely]] + return false; const auto& key = params.key.GetAsymmetricKey(); - switch (params.mode) { - case SignConfiguration::kSign: - if (!EVP_DigestSignInit( - context.get(), &ctx, params.digest, nullptr, key.get())) { - crypto::CheckThrow(env, SignBase::Error::kSignInit); - return false; - } - break; - case SignConfiguration::kVerify: - if (!EVP_DigestVerifyInit( - context.get(), &ctx, params.digest, nullptr, key.get())) { - crypto::CheckThrow(env, SignBase::Error::kSignInit); - return false; - } - break; + auto ctx = ([&] { + switch (params.mode) { + case SignConfiguration::Mode::Sign: + return context.signInit(key, params.digest); + case SignConfiguration::Mode::Verify: + return context.verifyInit(key, params.digest); + } + UNREACHABLE(); + })(); + + if (!ctx.has_value()) [[unlikely]] { + crypto::CheckThrow(env, SignBase::Error::Init); + return false; } int padding = params.flags & SignConfiguration::kHasPadding ? params.padding - : GetDefaultSignPadding(key); + : key.getDefaultSignPadding(); - Maybe salt_length = params.flags & SignConfiguration::kHasSaltLength - ? Just(params.salt_length) : Nothing(); + std::optional salt_length = + params.flags & SignConfiguration::kHasSaltLength + ? std::optional(params.salt_length) + : std::nullopt; - if (!ApplyRSAOptions(key, ctx, padding, salt_length)) { - crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey); + if (!ApplyRSAOptions(key, *ctx, padding, salt_length)) { + crypto::CheckThrow(env, SignBase::Error::PrivateKey); return false; } switch (params.mode) { - case SignConfiguration::kSign: { - if (IsOneShot(key)) { - size_t len; - if (!EVP_DigestSign( - context.get(), - nullptr, - &len, - params.data.data(), - params.data.size())) { - crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey); - return false; - } - ByteSource::Builder buf(len); - if (!EVP_DigestSign(context.get(), - buf.data(), - &len, - params.data.data(), - params.data.size())) { - crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey); + case SignConfiguration::Mode::Sign: { + if (key.isOneShotVariant()) { + auto data = context.signOneShot(params.data); + if (!data) [[unlikely]] { + crypto::CheckThrow(env, SignBase::Error::PrivateKey); return false; } - *out = std::move(buf).release(len); + *out = ByteSource::Allocated(data.release()); } else { - size_t len; - if (!EVP_DigestSignUpdate( - context.get(), - params.data.data(), - params.data.size()) || - !EVP_DigestSignFinal(context.get(), nullptr, &len)) { - crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey); - return false; - } - ByteSource::Builder buf(len); - if (!EVP_DigestSignFinal( - context.get(), buf.data(), &len)) { - crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey); + auto data = context.sign(params.data); + if (!data) [[unlikely]] { + crypto::CheckThrow(env, SignBase::Error::PrivateKey); return false; } + auto bs = ByteSource::Allocated(data.release()); if (UseP1363Encoding(key, params.dsa_encoding)) { - *out = ConvertSignatureToP1363(env, key, std::move(buf).release()); + *out = ConvertSignatureToP1363(env, key, std::move(bs)); } else { - *out = std::move(buf).release(len); + *out = std::move(bs); } } break; } - case SignConfiguration::kVerify: { + case SignConfiguration::Mode::Verify: { ByteSource::Builder buf(1); buf.data()[0] = 0; - if (EVP_DigestVerify( - context.get(), - params.signature.data(), - params.signature.size(), - params.data.data(), - params.data.size()) == 1) { + if (context.verify(params.data, params.signature)) { buf.data()[0] = 1; } *out = std::move(buf).release(); @@ -812,9 +716,9 @@ MaybeLocal SignTraits::EncodeOutput(Environment* env, const SignConfiguration& params, ByteSource* out) { switch (params.mode) { - case SignConfiguration::kSign: + case SignConfiguration::Mode::Sign: return out->ToArrayBuffer(env); - case SignConfiguration::kVerify: + case SignConfiguration::Mode::Verify: return Boolean::New(env->isolate(), out->data()[0] == 1); } UNREACHABLE(); diff --git a/src/crypto/crypto_sig.h b/src/crypto/crypto_sig.h index 3a3c27b4e8e748..36c51b07bb5692 100644 --- a/src/crypto/crypto_sig.h +++ b/src/crypto/crypto_sig.h @@ -13,27 +13,24 @@ namespace node { namespace crypto { static const unsigned int kNoDsaSignature = static_cast(-1); -enum DSASigEnc { - kSigEncDER, - kSigEncP1363 -}; +enum class DSASigEnc { DER, P1363, Invalid }; class SignBase : public BaseObject { public: - enum Error { - kSignOk, - kSignUnknownDigest, - kSignInit, - kSignNotInitialised, - kSignUpdate, - kSignPrivateKey, - kSignPublicKey, - kSignMalformedSignature + enum class Error { + Ok, + UnknownDigest, + Init, + NotInitialised, + Update, + PrivateKey, + PublicKey, + MalformedSignature }; SignBase(Environment* env, v8::Local wrap); - Error Init(const char* sign_type); + Error Init(std::string_view digest); Error Update(const char* data, size_t len); // TODO(joyeecheung): track the memory used by OpenSSL types @@ -45,7 +42,7 @@ class SignBase : public BaseObject { ncrypto::EVPMDCtxPointer mdctx_; }; -class Sign : public SignBase { +class Sign final : public SignBase { public: static void Initialize(Environment* env, v8::Local target); static void RegisterExternalReferences(ExternalReferenceRegistry* registry); @@ -54,15 +51,14 @@ class Sign : public SignBase { Error error; std::unique_ptr signature; - explicit SignResult( - Error err, - std::unique_ptr&& sig = nullptr) - : error(err), signature(std::move(sig)) {} + inline explicit SignResult( + Error err, std::unique_ptr&& sig = nullptr) + : error(err), signature(std::move(sig)) {} }; SignResult SignFinal(const ncrypto::EVPKeyPointer& pkey, int padding, - const v8::Maybe& saltlen, + std::optional saltlen, DSASigEnc dsa_sig_enc); static void SignSync(const v8::FunctionCallbackInfo& args); @@ -76,7 +72,7 @@ class Sign : public SignBase { Sign(Environment* env, v8::Local wrap); }; -class Verify : public SignBase { +class Verify final : public SignBase { public: static void Initialize(Environment* env, v8::Local target); static void RegisterExternalReferences(ExternalReferenceRegistry* registry); @@ -84,7 +80,7 @@ class Verify : public SignBase { Error VerifyFinal(const ncrypto::EVPKeyPointer& key, const ByteSource& sig, int padding, - const v8::Maybe& saltlen, + std::optional saltlen, bool* verify_result); static void VerifySync(const v8::FunctionCallbackInfo& args); @@ -99,10 +95,7 @@ class Verify : public SignBase { }; struct SignConfiguration final : public MemoryRetainer { - enum Mode { - kSign, - kVerify - }; + enum class Mode { Sign, Verify }; enum Flags { kHasNone = 0, kHasSaltLength = 1, @@ -118,7 +111,7 @@ struct SignConfiguration final : public MemoryRetainer { int flags = SignConfiguration::kHasNone; int padding = 0; int salt_length = 0; - DSASigEnc dsa_encoding = kSigEncDER; + DSASigEnc dsa_encoding = DSASigEnc::DER; SignConfiguration() = default; @@ -135,8 +128,6 @@ struct SignTraits final { using AdditionalParameters = SignConfiguration; static constexpr const char* JobName = "SignJob"; -// TODO(@jasnell): Sign request vs. Verify request - static constexpr AsyncWrap::ProviderType Provider = AsyncWrap::PROVIDER_SIGNREQUEST; diff --git a/src/crypto/crypto_tls.cc b/src/crypto/crypto_tls.cc index 0f1defef16661a..7104ee19a6dd79 100644 --- a/src/crypto/crypto_tls.cc +++ b/src/crypto/crypto_tls.cc @@ -46,6 +46,7 @@ using v8::Array; using v8::ArrayBuffer; using v8::ArrayBufferView; using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Boolean; using v8::Context; using v8::DontDelete; @@ -60,6 +61,7 @@ using v8::Isolate; using v8::Local; using v8::MaybeLocal; using v8::Null; +using v8::Number; using v8::Object; using v8::PropertyAttribute; using v8::ReadOnly; @@ -161,7 +163,7 @@ int NewSessionCallback(SSL* s, SSL_SESSION* sess) { HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); - if (!w->has_session_callbacks()) + if (!w->has_session_callbacks()) [[unlikely]] return 0; // Check if session is small enough to be stored @@ -225,10 +227,9 @@ int SSLCertCallback(SSL* s, void* arg) { auto servername = SSLPointer::GetServerName(s); Local servername_str = - !servername.has_value() ? String::Empty(env->isolate()) - : OneByteString(env->isolate(), - servername.value().data(), - servername.value().length()); + !servername.has_value() + ? String::Empty(env->isolate()) + : OneByteString(env->isolate(), servername.value()); Local ocsp = Boolean::New( env->isolate(), SSL_get_tlsext_status_type(s) == TLSEXT_STATUSTYPE_ocsp); @@ -257,30 +258,27 @@ int SelectALPNCallback( Environment* env = w->env(); HandleScope handle_scope(env->isolate()); - Local callback_arg = - Buffer::Copy(env, reinterpret_cast(in), inlen) - .ToLocalChecked(); + Local callback_arg; + Local callback_result; - MaybeLocal maybe_callback_result = - w->MakeCallback(env->alpn_callback_string(), 1, &callback_arg); - - if (maybe_callback_result.IsEmpty()) [[unlikely]] { - // Implies the callback didn't return, because some exception was thrown - // during processing, e.g. if callback returned an invalid ALPN value. + if (!Buffer::Copy(env, reinterpret_cast(in), inlen) + .ToLocal(&callback_arg)) { return SSL_TLSEXT_ERR_ALERT_FATAL; } - Local callback_result = maybe_callback_result.ToLocalChecked(); + if (!w->MakeCallback(env->alpn_callback_string(), 1, &callback_arg) + .ToLocal(&callback_result)) { + return SSL_TLSEXT_ERR_ALERT_FATAL; + } - if (callback_result->IsUndefined()) { + if (callback_result->IsUndefined() && !callback_result->IsNumber()) { // If you set an ALPN callback, but you return undefined for an ALPN // request, you're rejecting all proposed ALPN protocols, and so we send // a fatal alert: return SSL_TLSEXT_ERR_ALERT_FATAL; } - CHECK(callback_result->IsNumber()); - unsigned int result_int = callback_result.As()->Value(); + unsigned int result_int = callback_result.As()->Value(); // The callback returns an offset into the given buffer, for the selected // protocol that should be returned. We then set outlen & out to point @@ -1087,10 +1085,10 @@ int TLSWrap::DoWrite(WriteWrap* w, // and copying it when it could just be used. if (nonempty_count != 1) { - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env()->isolate(), length); - } + bs = ArrayBuffer::NewBackingStore( + env()->isolate(), + length, + BackingStoreInitializationMode::kUninitialized); size_t offset = 0; for (i = 0; i < count; i++) { memcpy(static_cast(bs->Data()) + offset, @@ -1107,8 +1105,10 @@ int TLSWrap::DoWrite(WriteWrap* w, written = SSL_write(ssl_.get(), buf->base, buf->len); if (written == -1) { - NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env()->isolate(), length); + bs = ArrayBuffer::NewBackingStore( + env()->isolate(), + length, + BackingStoreInitializationMode::kUninitialized); memcpy(bs->Data(), buf->base, buf->len); } } @@ -1750,11 +1750,8 @@ void TLSWrap::GetFinished(const FunctionCallbackInfo& args) { if (len == 0) return; - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), len); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), len, BackingStoreInitializationMode::kUninitialized); CHECK_EQ(bs->ByteLength(), SSL_get_finished(w->ssl_.get(), bs->Data(), bs->ByteLength())); @@ -1781,11 +1778,8 @@ void TLSWrap::GetPeerFinished(const FunctionCallbackInfo& args) { if (len == 0) return; - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), len); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), len, BackingStoreInitializationMode::kUninitialized); CHECK_EQ(bs->ByteLength(), SSL_get_peer_finished(w->ssl_.get(), bs->Data(), bs->ByteLength())); @@ -1810,11 +1804,8 @@ void TLSWrap::GetSession(const FunctionCallbackInfo& args) { if (slen <= 0) return; // Invalid or malformed session. - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), slen); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), slen, BackingStoreInitializationMode::kUninitialized); unsigned char* p = static_cast(bs->Data()); CHECK_LT(0, i2d_SSL_SESSION(sess, &p)); @@ -1997,11 +1988,8 @@ void TLSWrap::ExportKeyingMaterial(const FunctionCallbackInfo& args) { uint32_t olen = args[0].As()->Value(); Utf8Value label(env->isolate(), args[1]); - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), olen); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), olen, BackingStoreInitializationMode::kUninitialized); ByteSource context; bool use_context = !args[2]->IsUndefined(); diff --git a/src/crypto/crypto_tls.h b/src/crypto/crypto_tls.h index ed1ee337b42ec1..f02c37182ff189 100644 --- a/src/crypto/crypto_tls.h +++ b/src/crypto/crypto_tls.h @@ -58,15 +58,17 @@ class TLSWrap : public AsyncWrap, ~TLSWrap() override; - bool is_cert_cb_running() const { return cert_cb_running_; } - bool is_waiting_cert_cb() const { return cert_cb_ != nullptr; } - bool has_session_callbacks() const { return session_callbacks_; } - void set_cert_cb_running(bool on = true) { cert_cb_running_ = on; } - void set_awaiting_new_session(bool on = true) { awaiting_new_session_ = on; } - void enable_session_callbacks() { session_callbacks_ = true; } - bool is_server() const { return kind_ == Kind::kServer; } - bool is_client() const { return kind_ == Kind::kClient; } - bool is_awaiting_new_session() const { return awaiting_new_session_; } + inline bool is_cert_cb_running() const { return cert_cb_running_; } + inline bool is_waiting_cert_cb() const { return cert_cb_ != nullptr; } + inline bool has_session_callbacks() const { return session_callbacks_; } + inline void set_cert_cb_running(bool on = true) { cert_cb_running_ = on; } + inline void set_awaiting_new_session(bool on = true) { + awaiting_new_session_ = on; + } + inline void enable_session_callbacks() { session_callbacks_ = true; } + inline bool is_server() const { return kind_ == Kind::kServer; } + inline bool is_client() const { return kind_ == Kind::kClient; } + inline bool is_awaiting_new_session() const { return awaiting_new_session_; } // Implement StreamBase: bool IsAlive() override; @@ -128,7 +130,7 @@ class TLSWrap : public AsyncWrap, // Alternative to StreamListener::stream(), that returns a StreamBase instead // of a StreamResource. - StreamBase* underlying_stream() const { + inline StreamBase* underlying_stream() const { return static_cast(stream()); } diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc index 60610b1b795c9b..61f8cbf6703b50 100644 --- a/src/crypto/crypto_util.cc +++ b/src/crypto/crypto_util.cc @@ -630,17 +630,12 @@ Maybe SetEncodedValue(Environment* env, : Nothing(); } -bool SetRsaOaepLabel(const EVPKeyCtxPointer& ctx, const ByteSource& label) { +bool SetRsaOaepLabel(EVPKeyCtxPointer* ctx, const ByteSource& label) { if (label.size() != 0) { // OpenSSL takes ownership of the label, so we need to create a copy. - void* label_copy = OPENSSL_memdup(label.data(), label.size()); - CHECK_NOT_NULL(label_copy); - int ret = EVP_PKEY_CTX_set0_rsa_oaep_label( - ctx.get(), static_cast(label_copy), label.size()); - if (ret <= 0) { - OPENSSL_free(label_copy); - return false; - } + auto dup = ncrypto::DataPointer::Copy(label); + if (!dup) return false; + return ctx->setRsaOaepLabel(std::move(dup)); } return true; } diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h index a5967c7d24b836..e3c9a82c17b382 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h @@ -61,10 +61,9 @@ void InitCryptoOnce(); void InitCrypto(v8::Local target); -extern void UseExtraCaCerts(const std::string& file); +extern void UseExtraCaCerts(std::string_view file); int PasswordCallback(char* buf, int size, int rwflag, void* u); - int NoPasswordCallback(char* buf, int size, int rwflag, void* u); // Decode is used by the various stream-based crypto utilities to decode @@ -165,7 +164,7 @@ T* MallocOpenSSL(size_t count) { // A helper class representing a read-only byte array. When deallocated, its // contents are zeroed. -class ByteSource { +class ByteSource final { public: class Builder { public: @@ -224,17 +223,25 @@ class ByteSource { ByteSource& operator=(const ByteSource&) = delete; template - const T* data() const { + inline const T* data() const { return reinterpret_cast(data_); } - size_t size() const { return size_; } + template + operator ncrypto::Buffer() const { + return ncrypto::Buffer{ + .data = data(), + .len = size(), + }; + } + + inline size_t size() const { return size_; } - bool empty() const { return size_ == 0; } + inline bool empty() const { return size_ == 0; } - operator bool() const { return data_ != nullptr; } + inline operator bool() const { return data_ != nullptr; } - ncrypto::BignumPointer ToBN() const { + inline ncrypto::BignumPointer ToBN() const { return ncrypto::BignumPointer(data(), size()); } @@ -518,7 +525,7 @@ void ThrowCryptoError(Environment* env, unsigned long err, // NOLINT(runtime/int) const char* message = nullptr); -class CipherPushContext { +class CipherPushContext final { public: inline explicit CipherPushContext(Environment* env) : list_(env->isolate()), env_(env) {} @@ -546,16 +553,13 @@ void array_push_back(const TypeName* evp_ref, const char* from, const char* to, void* arg) { - if (!from) - return; + if (!from) return; const TypeName* real_instance = getbyname(from); - if (!real_instance) - return; + if (!real_instance) return; const char* real_name = getname(real_instance); - if (!real_name) - return; + if (!real_name) return; // EVP_*_fetch() does not support alias names, so we need to pass it the // real/original algorithm name. @@ -564,8 +568,7 @@ void array_push_back(const TypeName* evp_ref, // algorithms are used internally by OpenSSL and are also passed to this // callback). TypeName* fetched = fetch_type(nullptr, real_name, nullptr); - if (!fetched) - return; + if (!fetched) return; free_type(fetched); static_cast(arg)->push_back(from); @@ -576,8 +579,7 @@ void array_push_back(const TypeName* evp_ref, const char* from, const char* to, void* arg) { - if (!from) - return; + if (!from) return; static_cast(arg)->push_back(from); } #endif @@ -590,7 +592,7 @@ inline bool IsAnyBufferSource(v8::Local arg) { } template -class ArrayBufferOrViewContents { +class ArrayBufferOrViewContents final { public: ArrayBufferOrViewContents() = default; ArrayBufferOrViewContents(const ArrayBufferOrViewContents&) = delete; @@ -707,8 +709,7 @@ v8::Maybe SetEncodedValue(Environment* env, const BIGNUM* bn, int size = 0); -bool SetRsaOaepLabel(const ncrypto::EVPKeyCtxPointer& rsa, - const ByteSource& label); +bool SetRsaOaepLabel(ncrypto::EVPKeyCtxPointer* rsa, const ByteSource& label); namespace Util { void Initialize(Environment* env, v8::Local target); diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc index 3465454e4de4a7..782eced277518d 100644 --- a/src/crypto/crypto_x509.cc +++ b/src/crypto/crypto_x509.cc @@ -21,13 +21,12 @@ using ncrypto::ClearErrorOnReturn; using ncrypto::DataPointer; using ncrypto::ECKeyPointer; using ncrypto::SSLPointer; -using ncrypto::StackOfASN1; using ncrypto::X509Pointer; using ncrypto::X509View; using v8::Array; using v8::ArrayBuffer; using v8::ArrayBufferView; -using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Boolean; using v8::Context; using v8::Date; @@ -38,6 +37,7 @@ using v8::FunctionTemplate; using v8::Integer; using v8::Isolate; using v8::Local; +using v8::LocalVector; using v8::MaybeLocal; using v8::NewStringType; using v8::Object; @@ -55,14 +55,13 @@ ManagedX509::ManagedX509(const ManagedX509& that) { ManagedX509& ManagedX509::operator=(const ManagedX509& that) { cert_.reset(that.get()); - - if (cert_) + if (cert_) [[likely]] X509_up_ref(cert_.get()); - return *this; } void ManagedX509::MemoryInfo(MemoryTracker* tracker) const { + if (!cert_) return; // This is an approximation based on the der encoding size. int size = i2d_X509(cert_.get(), nullptr); tracker->TrackFieldWithSize("cert", size); @@ -94,7 +93,8 @@ void Fingerprint(const FunctionCallbackInfo& args) { } MaybeLocal ToV8Value(Local context, BIOPointer&& bio) { - if (!bio) return {}; + if (!bio) [[unlikely]] + return {}; BUF_MEM* mem = bio; Local ret; if (!String::NewFromUtf8(context->GetIsolate(), @@ -135,7 +135,7 @@ MaybeLocal ToV8Value(Local context, const ASN1_STRING* str) { // not escape anything. unsigned char* value_str; int value_str_size = ASN1_STRING_to_UTF8(&value_str, str); - if (value_str_size < 0) { + if (value_str_size < 0) [[unlikely]] { return Undefined(context->GetIsolate()); } DataPointer free_value_str(value_str, value_str_size); @@ -152,7 +152,8 @@ MaybeLocal ToV8Value(Local context, const ASN1_STRING* str) { } MaybeLocal ToV8Value(Local context, const BIOPointer& bio) { - if (!bio) return {}; + if (!bio) [[unlikely]] + return {}; BUF_MEM* mem = bio; Local ret; if (!String::NewFromUtf8(context->GetIsolate(), @@ -165,7 +166,8 @@ MaybeLocal ToV8Value(Local context, const BIOPointer& bio) { } MaybeLocal ToBuffer(Environment* env, BIOPointer* bio) { - if (bio == nullptr || !*bio) return {}; + if (bio == nullptr || !*bio) [[unlikely]] + return {}; BUF_MEM* mem = *bio; auto backing = ArrayBuffer::NewBackingStore( mem->data, @@ -183,7 +185,8 @@ MaybeLocal ToBuffer(Environment* env, BIOPointer* bio) { MaybeLocal GetDer(Environment* env, const X509View& view) { Local ret; auto bio = view.toDER(); - if (!bio) return Undefined(env->isolate()); + if (!bio) [[unlikely]] + return Undefined(env->isolate()); if (!ToBuffer(env, &bio).ToLocal(&ret)) { return {}; } @@ -194,7 +197,8 @@ MaybeLocal GetSubjectAltNameString(Environment* env, const X509View& view) { Local ret; auto bio = view.getSubjectAltName(); - if (!bio) return Undefined(env->isolate()); + if (!bio) [[unlikely]] + return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) return {}; return ret; } @@ -202,7 +206,8 @@ MaybeLocal GetSubjectAltNameString(Environment* env, MaybeLocal GetInfoAccessString(Environment* env, const X509View& view) { Local ret; auto bio = view.getInfoAccess(); - if (!bio) return Undefined(env->isolate()); + if (!bio) [[unlikely]] + return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) { return {}; } @@ -212,7 +217,8 @@ MaybeLocal GetInfoAccessString(Environment* env, const X509View& view) { MaybeLocal GetValidFrom(Environment* env, const X509View& view) { Local ret; auto bio = view.getValidFrom(); - if (!bio) return Undefined(env->isolate()); + if (!bio) [[unlikely]] + return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) { return {}; } @@ -222,7 +228,8 @@ MaybeLocal GetValidFrom(Environment* env, const X509View& view) { MaybeLocal GetValidTo(Environment* env, const X509View& view) { Local ret; auto bio = view.getValidTo(); - if (!bio) return Undefined(env->isolate()); + if (!bio) [[unlikely]] + return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) { return {}; } @@ -248,25 +255,12 @@ MaybeLocal GetSerialNumber(Environment* env, const X509View& view) { } MaybeLocal GetKeyUsage(Environment* env, const X509View& cert) { - StackOfASN1 eku(static_cast( - X509_get_ext_d2i(cert.get(), NID_ext_key_usage, nullptr, nullptr))); - if (eku) { - const int count = sk_ASN1_OBJECT_num(eku.get()); - MaybeStackBuffer, 16> ext_key_usage(count); - char buf[256]; - - int j = 0; - for (int i = 0; i < count; i++) { - if (OBJ_obj2txt( - buf, sizeof(buf), sk_ASN1_OBJECT_value(eku.get(), i), 1) >= 0) { - ext_key_usage[j++] = OneByteString(env->isolate(), buf); - } - } - - return Array::New(env->isolate(), ext_key_usage.out(), count); - } - - return Undefined(env->isolate()); + LocalVector vec(env->isolate()); + bool res = cert.enumUsages([&](std::string_view view) { + vec.push_back(OneByteString(env->isolate(), view)); + }); + if (!res) return Undefined(env->isolate()); + return Array::New(env->isolate(), vec.data(), vec.size()); } void Pem(const FunctionCallbackInfo& args) { @@ -387,7 +381,7 @@ void PublicKey(const FunctionCallbackInfo& args) { // TODO(tniessen): consider checking X509_get_pubkey() when the // X509Certificate object is being created. auto result = cert->view().getPublicKey(); - if (!result.value) { + if (!result.value) [[unlikely]] { ThrowCryptoError(env, result.error.value_or(0)); return; } @@ -547,7 +541,9 @@ void Parse(const FunctionCallbackInfo& args) { .len = buf.length(), }); - if (!result.value) return ThrowCryptoError(env, result.error.value_or(0)); + if (!result.value) [[unlikely]] { + return ThrowCryptoError(env, result.error.value_or(0)); + } if (X509Certificate::New(env, std::move(result.value)).ToLocal(&cert)) { args.GetReturnValue().Set(cert); @@ -571,7 +567,8 @@ bool Set(Environment* env, Local name, MaybeLocal maybe_value) { Local value; - if (!maybe_value.ToLocal(&value)) return false; + if (!maybe_value.ToLocal(&value)) [[unlikely]] + return false; // Undefined is ignored, but still considered successful if (value->IsUndefined()) return true; @@ -585,7 +582,8 @@ bool Set(Environment* env, uint32_t index, MaybeLocal maybe_value) { Local value; - if (!maybe_value.ToLocal(&value)) return false; + if (!maybe_value.ToLocal(&value)) [[unlikely]] + return false; // Undefined is ignored, but still considered successful if (value->IsUndefined()) return true; @@ -664,33 +662,32 @@ static MaybeLocal GetX509NameObject(Environment* env, return result; } -MaybeLocal GetPubKey(Environment* env, OSSL3_CONST RSA* rsa) { +MaybeLocal GetPubKey(Environment* env, const ncrypto::Rsa& rsa) { int size = i2d_RSA_PUBKEY(rsa, nullptr); CHECK_GE(size, 0); - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), size); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), size, BackingStoreInitializationMode::kUninitialized); - unsigned char* serialized = reinterpret_cast(bs->Data()); + auto serialized = reinterpret_cast(bs->Data()); CHECK_GE(i2d_RSA_PUBKEY(rsa, &serialized), 0); - Local ab = ArrayBuffer::New(env->isolate(), std::move(bs)); + auto ab = ArrayBuffer::New(env->isolate(), std::move(bs)); return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local()); } MaybeLocal GetModulusString(Environment* env, const BIGNUM* n) { auto bio = BIOPointer::New(n); - if (!bio) return {}; + if (!bio) [[unlikely]] + return {}; return ToV8Value(env->context(), bio); } MaybeLocal GetExponentString(Environment* env, const BIGNUM* e) { uint64_t exponent_word = static_cast(BignumPointer::GetWord(e)); auto bio = BIOPointer::NewMem(); - if (!bio) return {}; + if (!bio) [[unlikely]] + return {}; BIO_printf(bio.get(), "0x%" PRIx64, exponent_word); return ToV8Value(env->context(), bio); } @@ -699,14 +696,16 @@ MaybeLocal GetECPubKey(Environment* env, const EC_GROUP* group, OSSL3_CONST EC_KEY* ec) { const auto pubkey = ECKeyPointer::GetPublicKey(ec); - if (pubkey == nullptr) return Undefined(env->isolate()); + if (pubkey == nullptr) [[unlikely]] + return Undefined(env->isolate()); return ECPointToBuffer(env, group, pubkey, EC_KEY_get_conv_form(ec), nullptr) .FromMaybe(Local()); } MaybeLocal GetECGroupBits(Environment* env, const EC_GROUP* group) { - if (group == nullptr) return Undefined(env->isolate()); + if (group == nullptr) [[unlikely]] + return Undefined(env->isolate()); int bits = EC_GROUP_order_bits(group); if (bits <= 0) return Undefined(env->isolate()); @@ -716,10 +715,9 @@ MaybeLocal GetECGroupBits(Environment* env, const EC_GROUP* group) { template MaybeLocal GetCurveName(Environment* env, const int nid) { - const char* name = nid2string(nid); - return name != nullptr - ? MaybeLocal(OneByteString(env->isolate(), name)) - : MaybeLocal(Undefined(env->isolate())); + std::string_view name = nid2string(nid); + return name.size() ? MaybeLocal(OneByteString(env->isolate(), name)) + : MaybeLocal(Undefined(env->isolate())); } MaybeLocal X509ToObject(Environment* env, const X509View& cert) { @@ -745,68 +743,68 @@ MaybeLocal X509ToObject(Environment* env, const X509View& cert) { !Set(env, info, env->ca_string(), - Boolean::New(env->isolate(), cert.isCA()))) { + Boolean::New(env->isolate(), cert.isCA()))) [[unlikely]] { return {}; } - OSSL3_CONST EVP_PKEY* pkey = X509_get0_pubkey(cert.get()); - OSSL3_CONST RSA* rsa = nullptr; - OSSL3_CONST EC_KEY* ec = nullptr; - if (pkey != nullptr) { - switch (EVP_PKEY_id(pkey)) { - case EVP_PKEY_RSA: - rsa = EVP_PKEY_get0_RSA(pkey); - break; - case EVP_PKEY_EC: - ec = EVP_PKEY_get0_EC_KEY(pkey); - break; - } + if (!cert.ifRsa([&](const ncrypto::Rsa& rsa) { + auto pub_key = rsa.getPublicKey(); + if (!Set(env, + info, + env->modulus_string(), + GetModulusString(env, pub_key.n)) || + !Set(env, + info, + env->bits_string(), + Integer::New(env->isolate(), + BignumPointer::GetBitCount(pub_key.n))) || + !Set(env, + info, + env->exponent_string(), + GetExponentString(env, pub_key.e)) || + !Set(env, info, env->pubkey_string(), GetPubKey(env, rsa))) + [[unlikely]] { + return false; + } + return true; + })) [[unlikely]] { + return {}; } - if (rsa) { - const BIGNUM* n; - const BIGNUM* e; - RSA_get0_key(rsa, &n, &e, nullptr); - if (!Set( - env, info, env->modulus_string(), GetModulusString(env, n)) || - !Set( - env, - info, - env->bits_string(), - Integer::New(env->isolate(), BignumPointer::GetBitCount(n))) || - !Set( - env, info, env->exponent_string(), GetExponentString(env, e)) || - !Set(env, info, env->pubkey_string(), GetPubKey(env, rsa))) { - return {}; - } - } else if (ec) { - const auto group = ECKeyPointer::GetGroup(ec); + if (!cert.ifEc([&](const ncrypto::Ec& ec) { + const auto group = ec.getGroup(); - if (!Set( - env, info, env->bits_string(), GetECGroupBits(env, group)) || - !Set( - env, info, env->pubkey_string(), GetECPubKey(env, group, ec))) { - return {}; - } + if (!Set( + env, info, env->bits_string(), GetECGroupBits(env, group)) || + !Set( + env, info, env->pubkey_string(), GetECPubKey(env, group, ec))) + [[unlikely]] { + return false; + } - const int nid = EC_GROUP_get_curve_name(group); - if (nid != 0) { - // Curve is well-known, get its OID and NIST nick-name (if it has one). - - if (!Set(env, - info, - env->asn1curve_string(), - GetCurveName(env, nid)) || - !Set(env, - info, - env->nistcurve_string(), - GetCurveName(env, nid))) { - return {}; - } - } else { - // Unnamed curves can be described by their mathematical properties, - // but aren't used much (at all?) with X.509/TLS. Support later if needed. - } + const int nid = ec.getCurve(); + if (nid != 0) [[likely]] { + // Curve is well-known, get its OID and NIST nick-name (if it has + // one). + + if (!Set(env, + info, + env->asn1curve_string(), + GetCurveName(env, nid)) || + !Set(env, + info, + env->nistcurve_string(), + GetCurveName(env, nid))) + [[unlikely]] { + return false; + } + } + // Unnamed curves can be described by their mathematical properties, + // but aren't used much (at all?) with X.509/TLS. Support later if + // needed. + return true; + })) [[unlikely]] { + return {}; } if (!Set( @@ -828,7 +826,8 @@ MaybeLocal X509ToObject(Environment* env, const X509View& cert) { env, info, env->ext_key_usage_string(), GetKeyUsage(env, cert)) || !Set( env, info, env->serial_number_string(), GetSerialNumber(env, cert)) || - !Set(env, info, env->raw_string(), GetDer(env, cert))) { + !Set(env, info, env->raw_string(), GetDer(env, cert))) + [[unlikely]] { return {}; } @@ -910,7 +909,8 @@ MaybeLocal X509Certificate::New(Environment* env, MaybeLocal X509Certificate::GetCert(Environment* env, const SSLPointer& ssl) { auto cert = X509View::From(ssl); - if (!cert) return {}; + if (!cert) [[unlikely]] + return {}; return New(env, cert.clone()); } @@ -930,7 +930,7 @@ MaybeLocal X509Certificate::GetPeerCert(Environment* env, if (!cert && (ssl_certs == nullptr || sk_X509_num(ssl_certs) == 0)) return MaybeLocal(); - if (!cert) { + if (!cert) [[unlikely]] { cert.reset(sk_X509_value(ssl_certs, 0)); sk_X509_delete(ssl_certs, 0); } @@ -945,7 +945,8 @@ v8::MaybeLocal X509Certificate::toObject(Environment* env) { v8::MaybeLocal X509Certificate::toObject(Environment* env, const X509View& cert) { - if (!cert) return {}; + if (!cert) [[unlikely]] + return {}; return X509ToObject(env, cert).FromMaybe(Local()); } @@ -979,7 +980,7 @@ X509Certificate::X509CertificateTransferData::Deserialize( Environment* env, Local context, std::unique_ptr self) { - if (context != env->context()) { + if (context != env->context()) [[unlikely]] { THROW_ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE(env); return {}; } diff --git a/src/crypto/crypto_x509.h b/src/crypto/crypto_x509.h index 54f4b2a40732d2..39f1878be1925f 100644 --- a/src/crypto/crypto_x509.h +++ b/src/crypto/crypto_x509.h @@ -25,10 +25,10 @@ class ManagedX509 final : public MemoryRetainer { ManagedX509(const ManagedX509& that); ManagedX509& operator=(const ManagedX509& that); - operator bool() const { return !!cert_; } - X509* get() const { return cert_.get(); } - ncrypto::X509View view() const { return cert_; } - operator ncrypto::X509View() const { return cert_; } + inline operator bool() const { return !!cert_; } + inline X509* get() const { return cert_.get(); } + inline ncrypto::X509View view() const { return cert_; } + inline operator ncrypto::X509View() const { return cert_; } void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(ManagedX509) @@ -77,7 +77,7 @@ class X509Certificate final : public BaseObject { } inline ncrypto::X509View view() const { return *cert_; } - X509* get() { return cert_->get(); } + inline X509* get() { return cert_->get(); } v8::MaybeLocal toObject(Environment* env); static v8::MaybeLocal toObject(Environment* env,