diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index 8230fd5191fb03..dbd6a5f61d34fe 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -128,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_); @@ -1160,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); @@ -2273,15 +2324,26 @@ Result EVPKeyPointer::writePublicKey( } bool EVPKeyPointer::isRsaVariant() const { + if (!pkey_) return false; int type = id(); - return type == EVP_PKEY_RSA || - type == EVP_PKEY_RSA2 || + 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; + return id() == EVP_PKEY_RSA_PSS ? RSA_PKCS1_PSS_PADDING : RSA_PKCS1_PADDING; } std::optional EVPKeyPointer::getBytesOfRS() const { @@ -2317,6 +2379,28 @@ EVPKeyPointer::operator Rsa() const { 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) {} @@ -3189,6 +3273,42 @@ bool EVPKeyCtxPointer::privateCheck() const { 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 { @@ -3417,6 +3537,20 @@ DataPointer Cipher::recover(const EVPKeyPointer& key, // ============================================================================ +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) {} @@ -3429,9 +3563,13 @@ EVPMDCtxPointer& EVPMDCtxPointer::operator=(EVPMDCtxPointer&& other) noexcept { return *this; } -EVPMDCtxPointer::~EVPMDCtxPointer() { reset(); } +EVPMDCtxPointer::~EVPMDCtxPointer() { + reset(); +} -void EVPMDCtxPointer::reset(EVP_MD_CTX* ctx) { ctx_.reset(ctx); } +void EVPMDCtxPointer::reset(EVP_MD_CTX* ctx) { + ctx_.reset(ctx); +} EVP_MD_CTX* EVPMDCtxPointer::release() { return ctx_.release(); @@ -3452,18 +3590,31 @@ DataPointer EVPMDCtxPointer::digestFinal(size_t length) { auto buf = DataPointer::Alloc(length); if (!buf) return {}; - auto ptr = static_cast(buf.get()); - int ret = - (length == getExpectedSize()) - ? EVP_DigestFinal_ex(ctx_.get(), ptr, nullptr) - : EVP_DigestFinalXOF(ctx_.get(), ptr, length); + Buffer buffer = buf; - if (ret != 1) [[unlikely]] return {}; + 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()); @@ -3484,13 +3635,90 @@ bool EVPMDCtxPointer::hasXofFlag() const { } bool EVPMDCtxPointer::copyTo(const EVPMDCtxPointer& other) const { - if (!ctx_ ||!other) return {}; + 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; +} + } // namespace ncrypto diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index 254b48b8ced4e1..fdb8f09efd1832 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -222,6 +222,8 @@ class ECDSASigPointer; class ECGroupPointer; class ECPointPointer; class ECKeyPointer; +class Rsa; +class Ec; struct StackOfXASN1Deleter { void operator()(STACK_OF(ASN1_OBJECT) * p) const { @@ -347,6 +349,22 @@ class Rsa final { 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 { @@ -369,6 +387,9 @@ 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 @@ -600,6 +621,12 @@ class EVPKeyCtxPointer final { 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, @@ -732,9 +759,13 @@ class EVPKeyPointer final { std::optional getBytesOfRS() const; int getDefaultSignPadding() const; - bool isRsaVariant() const; operator Rsa() const; + bool isRsaVariant() const; + bool isOneShotVariant() const; + bool isSigVariant() const; + bool validateDsaParameters() const; + private: DeleteFnPtr pkey_; }; @@ -820,9 +851,6 @@ struct StackOfX509Deleter { }; using StackOfX509 = std::unique_ptr; -class X509Pointer; -class X509View; - class SSLCtxPointer final { public: SSLCtxPointer() = default; @@ -949,6 +977,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; }; @@ -1124,8 +1160,19 @@ class EVPMDCtxPointer final { 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; @@ -1222,6 +1269,10 @@ const EVP_CIPHER* getCipherByName(const std::string_view name); // 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 4b315eb82ab0ae..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; @@ -765,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), @@ -845,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()) { 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 8f1e6dc7110b11..86a76fd7b1e9b4 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_ec.cc b/src/crypto/crypto_ec.cc index bcd7d3d7a089ad..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())); diff --git a/src/crypto/crypto_hash.cc b/src/crypto/crypto_hash.cc index 5cf9dfc6e1f62f..588ab6d9a391d4 100644 --- a/src/crypto/crypto_hash.cc +++ b/src/crypto/crypto_hash.cc @@ -361,9 +361,9 @@ 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 mdctx_.digestUpdate(ncrypto::Buffer { - .data = data, - .len = len, + return mdctx_.digestUpdate(ncrypto::Buffer{ + .data = data, + .len = len, }); } diff --git a/src/crypto/crypto_hmac.cc b/src/crypto/crypto_hmac.cc index 1ddfffba3945ac..7d9ec677ed5d81 100644 --- a/src/crypto/crypto_hmac.cc +++ b/src/crypto/crypto_hmac.cc @@ -71,14 +71,14 @@ void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) { HandleScope scope(env()->isolate()); const EVP_MD* md = ncrypto::getDigestByName(hash_type); - if (md == nullptr) + 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)) { + if (!ctx_ || !HMAC_Init_ex(ctx_.get(), key, key_len, md, nullptr)) [[unlikely]] { ctx_.reset(); return ThrowCryptoError(env(), ERR_get_error()); } @@ -189,7 +189,7 @@ Maybe HmacTraits::AdditionalConfig( Utf8Value digest(env->isolate(), args[offset + 1]); params->digest = ncrypto::getDigestByName(digest.ToStringView()); - if (params->digest == nullptr) { + if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); } @@ -258,9 +258,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 16bfbc7b9232ca..7c3a85e9f8a24d 100644 --- a/src/crypto/crypto_keygen.cc +++ b/src/crypto/crypto_keygen.cc @@ -48,7 +48,7 @@ Maybe NidKeyPairGenTraits::AdditionalConfig( EVPKeyCtxPointer NidKeyPairGenTraits::Setup(NidKeyPairGenConfig* params) { auto ctx = EVPKeyCtxPointer::NewFromID(params->params.id); - if (!ctx.initForKeygen()) return {}; + if (!ctx || !ctx.initForKeygen()) return {}; return ctx; } diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc index 2308e38c7f9929..2c55828facc35b 100644 --- a/src/crypto/crypto_keys.cc +++ b/src/crypto/crypto_keys.cc @@ -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(); 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..03c5eb9fc31b18 100644 --- a/src/crypto/crypto_random.cc +++ b/src/crypto/crypto_random.cc @@ -25,6 +25,7 @@ using v8::MaybeLocal; using v8::Nothing; using v8::Object; using v8::Uint32; +using v8::Undefined; using v8::Value; namespace crypto { @@ -39,7 +40,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 +79,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 +104,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 +113,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 +124,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 +133,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 +144,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 +195,7 @@ 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_sig.cc b/src/crypto/crypto_sig.cc index c0a7de174b36e0..175a8e92ef437f 100644 --- a/src/crypto/crypto_sig.cc +++ b/src/crypto/crypto_sig.cc @@ -14,13 +14,14 @@ 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; @@ -38,33 +39,9 @@ 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); - } - - return true; -} - int GetPaddingFromJS(const EVPKeyPointer& key, Local val) { int padding = key.getDefaultSignPadding(); - if (!val->IsUndefined()) { + if (!val->IsUndefined()) [[likely]] { CHECK(val->IsInt32()); padding = val.As()->Value(); } @@ -73,7 +50,7 @@ int GetPaddingFromJS(const EVPKeyPointer& key, Local val) { std::optional GetSaltLenFromJS(Local val) { std::optional salt_len; - if (!val->IsUndefined()) { + if (!val->IsUndefined()) [[likely]] { CHECK(val->IsInt32()); salt_len = val.As()->Value(); } @@ -83,7 +60,7 @@ std::optional GetSaltLenFromJS(Local val) { DSASigEnc GetDSASigEncFromJS(Local val) { CHECK(val->IsInt32()); int i = val.As()->Value(); - if (i < 0 || i >= static_cast(DSASigEnc::Invalid)) { + if (i < 0 || i >= static_cast(DSASigEnc::Invalid)) [[unlikely]] { return DSASigEnc::Invalid; } return static_cast(val.As()->Value()); @@ -104,36 +81,30 @@ std::unique_ptr Node_SignFinal(Environment* env, const EVPKeyPointer& pkey, int padding, std::optional pss_salt_len) { - unsigned char m[EVP_MAX_MD_SIZE]; - unsigned int m_len; - - if (!EVP_DigestFinal_ex(mdctx.get(), m, &m_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.initForSign() > 0 && ApplyRSAOptions(pkey, pkctx.get(), padding, pss_salt_len) && - pkctx.setSignatureMd(mdctx) && - 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; } @@ -141,41 +112,26 @@ std::unique_ptr Node_SignFinal(Environment* env, return nullptr; } -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 = pkey.getBytesOfRS().value_or(kNoDsaSignature); - 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; } @@ -185,29 +141,32 @@ ByteSource ConvertSignatureToP1363(Environment* env, const EVPKeyPointer& pkey, const ByteSource& signature) { unsigned int n = pkey.getBytesOfRS().value_or(kNoDsaSignature); - if (n == kNoDsaSignature) - return ByteSource(); + 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 = pkey.getBytesOfRS().value_or(kNoDsaSignature); - if (n == kNoDsaSignature) - return std::move(out); + 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); @@ -218,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); @@ -228,89 +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 == DSASigEnc::P1363; +bool UseP1363Encoding(const EVPKeyPointer& key, const DSASigEnc dsa_encoding) { + return key.isSigVariant() && dsa_encoding == DSASigEnc::P1363; } } // namespace SignBase::Error SignBase::Init(std::string_view digest) { CHECK_NULL(mdctx_); auto md = ncrypto::getDigestByName(digest); - if (md == nullptr) return kSignUnknownDigest; + if (md == nullptr) [[unlikely]] + return Error::UnknownDigest; mdctx_ = EVPMDCtxPointer::New(); - if (!mdctx_.digestInit(md)) { + 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(); @@ -326,8 +284,10 @@ 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; @@ -357,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) { @@ -376,18 +336,20 @@ Sign::SignResult Sign::SignFinal(const EVPKeyPointer& pkey, int padding, 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 == DSASigEnc::P1363) { + 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()); } @@ -406,10 +368,10 @@ 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; } @@ -417,30 +379,23 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) { 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) { + if (dsa_sig_enc == DSASigEnc::Invalid) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "invalid signature encoding"); return; } - 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(); @@ -472,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) { @@ -481,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); }); @@ -493,32 +449,28 @@ SignBase::Error Verify::VerifyFinal(const EVPKeyPointer& pkey, int padding, 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) { + if (pkctx) [[likely]] { const int init_ret = pkctx.initForVerify(); - if (init_ret == -2) { - return kSignPublicKey; - } + if (init_ret == -2) [[unlikely]] + return Error::PublicKey; if (init_ret > 0 && ApplyRSAOptions(pkey, pkctx.get(), padding, saltlen) && pkctx.setSignatureMd(mdctx)) { - const unsigned char* s = sig.data(); - const int r = EVP_PKEY_verify(pkctx.get(), s, sig.size(), m, m_len); - *verify_result = r == 1; + *verify_result = pkctx.verify(sig, data); } } - return kSignOk; + return Error::Ok; } void Verify::VerifyFinal(const FunctionCallbackInfo& args) { @@ -530,24 +482,26 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) { unsigned int offset = 0; auto data = KeyObjectData::GetPublicOrPrivateKeyFromJs(args, &offset); - if (!data) return; + 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; } 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 = 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) { + if (dsa_sig_enc == DSASigEnc::Invalid) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "invalid signature encoding"); return; } @@ -555,14 +509,15 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) { ByteSource signature = hbuf.ToByteSource(); if (dsa_sig_enc == DSASigEnc::P1363) { signature = ConvertSignatureToDER(key, hbuf.ToByteSource()); - if (signature.data() == nullptr) - return crypto::CheckThrow(env, Error::kSignMalformedSignature); + if (signature.data() == nullptr) [[unlikely]] { + return crypto::CheckThrow(env, Error::MalformedSignature); + } } bool verify_result; - Error err = verify->VerifyFinal(key, 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); } @@ -610,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(); @@ -633,7 +588,7 @@ Maybe SignTraits::AdditionalConfig( if (args[offset + 6]->IsString()) { Utf8Value digest(env->isolate(), args[offset + 6]); params->digest = ncrypto::getDigestByName(digest.ToStringView()); - if (params->digest == nullptr) { + if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); } @@ -641,25 +596,24 @@ Maybe SignTraits::AdditionalConfig( if (args[offset + 7]->IsInt32()) { // Salt length params->flags |= SignConfiguration::kHasSaltLength; - params->salt_length = GetSaltLenFromJS(args[offset + 7]) - .value_or(params->salt_length); + params->salt_length = + GetSaltLenFromJS(args[offset + 7]).value_or(params->salt_length); } if (args[offset + 8]->IsUint32()) { // Padding params->flags |= SignConfiguration::kHasPadding; - params->padding = GetPaddingFromJS( - params->key.GetAsymmetricKey(), - args[offset + 8]); + params->padding = + GetPaddingFromJS(params->key.GetAsymmetricKey(), args[offset + 8]); } if (args[offset + 9]->IsUint32()) { // DSA Encoding params->dsa_encoding = GetDSASigEncFromJS(args[offset + 9]); - if (params->dsa_encoding == DSASigEnc::Invalid) { + 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"); @@ -687,25 +641,23 @@ bool SignTraits::DeriveBits( ByteSource* out) { ClearErrorOnReturn clear_error_on_return; auto context = EVPMDCtxPointer::New(); - EVP_PKEY_CTX* ctx = nullptr; - + 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 @@ -717,68 +669,40 @@ bool SignTraits::DeriveBits( ? 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); + case SignConfiguration::Mode::Sign: { + if (key.isOneShotVariant()) { + auto data = context.signOneShot(params.data); + if (!data) [[unlikely]] { + crypto::CheckThrow(env, SignBase::Error::PrivateKey); 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); - 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(); @@ -792,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 2c89142886f990..36c51b07bb5692 100644 --- a/src/crypto/crypto_sig.h +++ b/src/crypto/crypto_sig.h @@ -13,23 +13,19 @@ namespace node { namespace crypto { static const unsigned int kNoDsaSignature = static_cast(-1); -enum class DSASigEnc { - DER, - P1363, - Invalid -}; +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); @@ -46,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); @@ -55,10 +51,9 @@ 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, @@ -77,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); @@ -100,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, @@ -136,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.h b/src/crypto/crypto_util.h index 7da0512e758ab4..504746e3c87812 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: @@ -228,17 +227,18 @@ class ByteSource { return reinterpret_cast(data_); } - inline operator ncrypto::Buffer() const { - return ncrypto::Buffer{.data = data(), .len = size()}; + template + operator ncrypto::Buffer() const { + return ncrypto::Buffer{.data = data(), .len = size()}; } - size_t size() const { return 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()); } @@ -522,7 +522,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) {} @@ -550,16 +550,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. @@ -568,8 +565,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); @@ -580,8 +576,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 @@ -594,7 +589,7 @@ inline bool IsAnyBufferSource(v8::Local arg) { } template -class ArrayBufferOrViewContents { +class ArrayBufferOrViewContents final { public: ArrayBufferOrViewContents() = default; ArrayBufferOrViewContents(const ArrayBufferOrViewContents&) = delete; diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc index 289d45bf108ae6..782eced277518d 100644 --- a/src/crypto/crypto_x509.cc +++ b/src/crypto/crypto_x509.cc @@ -21,7 +21,6 @@ using ncrypto::ClearErrorOnReturn; using ncrypto::DataPointer; using ncrypto::ECKeyPointer; using ncrypto::SSLPointer; -using ncrypto::StackOfASN1; using ncrypto::X509Pointer; using ncrypto::X509View; using v8::Array; @@ -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; @@ -680,14 +678,16 @@ MaybeLocal GetPubKey(Environment* env, const ncrypto::Rsa& rsa) { 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); } @@ -696,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()); @@ -713,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) { @@ -742,71 +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) { - ncrypto::Rsa nrsa(rsa); - auto pub_key = nrsa.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, nrsa))) { - 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,