From e3fb8622d84a3fde751d568ea380c152e900a7f5 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 11 Aug 2024 12:11:26 -0700 Subject: [PATCH] src: move more crypto code to ncrypto --- deps/ncrypto/ncrypto.cc | 53 ++++++++++++++++++++++++++++++++++++ deps/ncrypto/ncrypto.h | 51 ++++++++++++++++++++++++++++++++-- src/crypto/crypto_bio.cc | 8 +++--- src/crypto/crypto_context.cc | 22 +++++++-------- src/crypto/crypto_ec.cc | 2 +- src/crypto/crypto_keys.cc | 32 ++++++++++------------ src/crypto/crypto_tls.cc | 11 ++++---- src/crypto/crypto_util.cc | 3 +- src/crypto/crypto_x509.cc | 15 +++++----- 9 files changed, 147 insertions(+), 50 deletions(-) diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index df5bd6f33c43c1..c3f4b3fd9893f7 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -963,4 +963,57 @@ X509Pointer X509Pointer::IssuerFrom(const SSL_CTX* ctx, const X509View& cert) { X509Pointer X509Pointer::PeerFrom(const SSLPointer& ssl) { return X509Pointer(SSL_get_peer_certificate(ssl.get())); } +// ============================================================================ +// BIOPointer + +BIOPointer::BIOPointer(BIO* bio) : bio_(bio) {} + +BIOPointer::BIOPointer(BIOPointer&& other) noexcept : bio_(other.release()) {} + +BIOPointer& BIOPointer::operator=(BIOPointer&& other) noexcept { + if (this == &other) return *this; + this->~BIOPointer(); + return *new (this) BIOPointer(std::move(other)); +} + +BIOPointer::~BIOPointer() { reset(); } + +void BIOPointer::reset(BIO* bio) { bio_.reset(bio); } + +BIO* BIOPointer::release() { return bio_.release(); } + +bool BIOPointer::resetBio() const { + if (!bio_) return 0; + return BIO_reset(bio_.get()) == 1; +} + +BIOPointer BIOPointer::NewMem() { + return BIOPointer(BIO_new(BIO_s_mem())); +} + +BIOPointer BIOPointer::NewSecMem() { + return BIOPointer(BIO_new(BIO_s_secmem())); +} + +BIOPointer BIOPointer::New(const BIO_METHOD* method) { + return BIOPointer(BIO_new(method)); +} + +BIOPointer BIOPointer::New(const void* data, size_t len) { + return BIOPointer(BIO_new_mem_buf(data, len)); +} + +BIOPointer BIOPointer::NewFile(std::string_view filename, std::string_view mode) { + return BIOPointer(BIO_new_file(filename.data(), mode.data())); +} + +BIOPointer BIOPointer::NewFp(FILE* fd, int close_flag) { + return BIOPointer(BIO_new_fp(fd, close_flag)); +} + +int BIOPointer::Write(BIOPointer* bio, std::string_view message) { + if (bio == nullptr || !*bio) return 0; + return BIO_write(bio->get(), message.data(), message.size()); +} + } // namespace ncrypto diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index 50e86538edda7c..e62a99595ae2c6 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -6,8 +6,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -17,6 +17,7 @@ #include #include #include +#include #ifndef OPENSSL_NO_ENGINE # include #endif // !OPENSSL_NO_ENGINE @@ -192,7 +193,6 @@ template using DeleteFnPtr = typename FunctionDeleter::Pointer; using BignumCtxPointer = DeleteFnPtr; -using BIOPointer = DeleteFnPtr; using CipherCtxPointer = DeleteFnPtr; using DHPointer = DeleteFnPtr; using DSAPointer = DeleteFnPtr; @@ -265,6 +265,53 @@ class DataPointer final { size_t len_ = 0; }; +class BIOPointer final { +public: + static BIOPointer NewMem(); + static BIOPointer NewSecMem(); + static BIOPointer New(const BIO_METHOD* method); + static BIOPointer New(const void* data, size_t len); + static BIOPointer NewFile(std::string_view filename, std::string_view mode); + static BIOPointer NewFp(FILE* fd, int flags); + + BIOPointer() = default; + BIOPointer(std::nullptr_t) : bio_(nullptr) {} + explicit BIOPointer(BIO* bio); + BIOPointer(BIOPointer&& other) noexcept; + BIOPointer& operator=(BIOPointer&& other) noexcept; + NCRYPTO_DISALLOW_COPY(BIOPointer) + ~BIOPointer(); + + inline bool operator==(std::nullptr_t) noexcept { return bio_ == nullptr; } + inline operator bool() const { return bio_ != nullptr; } + inline BIO* get() const noexcept { return bio_.get(); } + + inline operator BUF_MEM*() const { + BUF_MEM* mem = nullptr; + if (!bio_) return mem; + BIO_get_mem_ptr(bio_.get(), &mem); + return mem; + } + + inline operator BIO*() const { return bio_.get(); } + + void reset(BIO* bio = nullptr); + BIO* release(); + + bool resetBio() const; + + static int Write(BIOPointer* bio, std::string_view message); + + template + static void Printf(BIOPointer* bio, const char* format, Args...args) { + if (bio == nullptr || !*bio) return; + BIO_printf(bio->get(), format, std::forward(args...)); + } + +private: + mutable DeleteFnPtr bio_; +}; + class BignumPointer final { public: BignumPointer() = default; diff --git a/src/crypto/crypto_bio.cc b/src/crypto/crypto_bio.cc index 47045365ceaf81..e9c920ccffa70a 100644 --- a/src/crypto/crypto_bio.cc +++ b/src/crypto/crypto_bio.cc @@ -33,7 +33,7 @@ namespace node { namespace crypto { BIOPointer NodeBIO::New(Environment* env) { - BIOPointer bio(BIO_new(GetMethod())); + auto bio = BIOPointer::New(GetMethod()); if (bio && env != nullptr) NodeBIO::FromBIO(bio.get())->env_ = env; return bio; @@ -43,9 +43,9 @@ BIOPointer NodeBIO::New(Environment* env) { BIOPointer NodeBIO::NewFixed(const char* data, size_t len, Environment* env) { BIOPointer bio = New(env); - if (!bio || - len > INT_MAX || - BIO_write(bio.get(), data, len) != static_cast(len) || + if (!bio || len > INT_MAX || + BIOPointer::Write(&bio, std::string_view(data, len)) != + static_cast(len) || BIO_set_mem_eof_return(bio.get(), 0) != 1) { return BIOPointer(); } diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index 1efe7bfcdfe603..48fecc82c159d8 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -64,16 +64,17 @@ X509_STORE* GetOrCreateRootCertStore() { // Caller responsible for BIO_free_all-ing the returned object. BIOPointer LoadBIO(Environment* env, Local v) { if (v->IsString() || v->IsArrayBufferView()) { - BIOPointer bio(BIO_new(BIO_s_secmem())); - if (!bio) return nullptr; + auto bio = BIOPointer::NewSecMem(); + if (!bio) return {}; ByteSource bsrc = ByteSource::FromStringOrBuffer(env, v); - if (bsrc.size() > INT_MAX) return nullptr; - int written = BIO_write(bio.get(), bsrc.data(), bsrc.size()); - if (written < 0) return nullptr; - if (static_cast(written) != bsrc.size()) return nullptr; + if (bsrc.size() > INT_MAX) return {}; + int written = BIOPointer::Write( + &bio, std::string_view(bsrc.data(), bsrc.size())); + if (written < 0) return {}; + if (static_cast(written) != bsrc.size()) return {}; return bio; } - return nullptr; + return {}; } namespace { @@ -202,7 +203,7 @@ unsigned long LoadCertsFromFile( // NOLINT(runtime/int) const char* file) { MarkPopErrorOnReturn mark_pop_error_on_return; - BIOPointer bio(BIO_new_file(file, "r")); + auto bio = BIOPointer::NewFile(file, "r"); if (!bio) return ERR_get_error(); while (X509* x509 = PEM_read_bio_X509( @@ -1012,16 +1013,15 @@ void SecureContext::SetSessionIdContext( if (SSL_CTX_set_session_id_context(sc->ctx_.get(), sid_ctx, sid_ctx_len) == 1) return; - BUF_MEM* mem; Local message; - BIOPointer bio(BIO_new(BIO_s_mem())); + auto bio = BIOPointer::NewMem(); if (!bio) { message = FIXED_ONE_BYTE_STRING(env->isolate(), "SSL_CTX_set_session_id_context error"); } else { ERR_print_errors(bio.get()); - BIO_get_mem_ptr(bio.get(), &mem); + BUF_MEM* mem = bio; message = OneByteString(env->isolate(), mem->data, mem->length); } diff --git a/src/crypto/crypto_ec.cc b/src/crypto/crypto_ec.cc index cd6bee36dae102..d9e29a01d297e4 100644 --- a/src/crypto/crypto_ec.cc +++ b/src/crypto/crypto_ec.cc @@ -736,7 +736,7 @@ WebCryptoKeyExportStatus ECKeyExportTraits::DoExport( CHECK_EQ(1, EC_KEY_set_public_key(ec.get(), uncompressed.get())); EVPKeyPointer pkey(EVP_PKEY_new()); CHECK_EQ(1, EVP_PKEY_set1_EC_KEY(pkey.get(), ec.get())); - BIOPointer bio(BIO_new(BIO_s_mem())); + auto bio = BIOPointer::NewMem(); CHECK(bio); if (!i2d_PUBKEY_bio(bio.get(), pkey.get())) return WebCryptoKeyExportStatus::FAILED; diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc index 35474c31bfc2e3..f49bb1254cb219 100644 --- a/src/crypto/crypto_keys.cc +++ b/src/crypto/crypto_keys.cc @@ -104,7 +104,7 @@ ParseKeyResult TryParsePublicKey(EVPKeyPointer* pkey, ParseKeyResult ParsePublicKeyPEM(EVPKeyPointer* pkey, const char* key_pem, int key_pem_len) { - BIOPointer bp(BIO_new_mem_buf(const_cast(key_pem), key_pem_len)); + auto bp = BIOPointer::New(key_pem, key_pem_len); if (!bp) return ParseKeyResult::kParseKeyFailed; @@ -119,7 +119,7 @@ ParseKeyResult ParsePublicKeyPEM(EVPKeyPointer* pkey, return ret; // Maybe it is PKCS#1. - CHECK(BIO_reset(bp.get())); + CHECK(bp.resetBio()); ret = TryParsePublicKey(pkey, bp, "RSA PUBLIC KEY", [](const unsigned char** p, long l) { // NOLINT(runtime/int) return d2i_PublicKey(EVP_PKEY_RSA, nullptr, p, l); @@ -128,7 +128,7 @@ ParseKeyResult ParsePublicKeyPEM(EVPKeyPointer* pkey, return ret; // X.509 fallback. - CHECK(BIO_reset(bp.get())); + CHECK(bp.resetBio()); return TryParsePublicKey(pkey, bp, "CERTIFICATE", [](const unsigned char** p, long l) { // NOLINT(runtime/int) X509Pointer x509(d2i_X509(nullptr, p, l)); @@ -218,7 +218,7 @@ ParseKeyResult ParsePrivateKey(EVPKeyPointer* pkey, const ByteSource* passphrase = config.passphrase_.get(); if (config.format_ == kKeyFormatPEM) { - BIOPointer bio(BIO_new_mem_buf(key, key_len)); + auto bio = BIOPointer::New(key, key_len); if (!bio) return ParseKeyResult::kParseKeyFailed; @@ -233,7 +233,7 @@ ParseKeyResult ParsePrivateKey(EVPKeyPointer* pkey, const unsigned char* p = reinterpret_cast(key); pkey->reset(d2i_PrivateKey(EVP_PKEY_RSA, nullptr, &p, key_len)); } else if (config.type_.ToChecked() == kKeyEncodingPKCS8) { - BIOPointer bio(BIO_new_mem_buf(key, key_len)); + auto bio = BIOPointer::New(key, key_len); if (!bio) return ParseKeyResult::kParseKeyFailed; @@ -270,12 +270,10 @@ ParseKeyResult ParsePrivateKey(EVPKeyPointer* pkey, return ParseKeyResult::kParseKeyFailed; } -MaybeLocal BIOToStringOrBuffer( - Environment* env, - BIO* bio, - PKFormatType format) { - BUF_MEM* bptr; - BIO_get_mem_ptr(bio, &bptr); +MaybeLocal BIOToStringOrBuffer(Environment* env, + const BIOPointer& bio, + PKFormatType format) { + BUF_MEM* bptr = bio; if (format == kKeyFormatPEM) { // PEM is an ASCII format, so we will return it as a string. return String::NewFromUtf8(env->isolate(), bptr->data, @@ -292,7 +290,7 @@ MaybeLocal BIOToStringOrBuffer( MaybeLocal WritePrivateKey(Environment* env, OSSL3_CONST EVP_PKEY* pkey, const PrivateKeyEncodingConfig& config) { - BIOPointer bio(BIO_new(BIO_s_mem())); + auto bio = BIOPointer::NewMem(); CHECK(bio); // If an empty string was passed as the passphrase, the ByteSource might @@ -388,7 +386,7 @@ MaybeLocal WritePrivateKey(Environment* env, ThrowCryptoError(env, ERR_get_error(), "Failed to encode private key"); return MaybeLocal(); } - return BIOToStringOrBuffer(env, bio.get(), config.format_); + return BIOToStringOrBuffer(env, bio, config.format_); } bool WritePublicKeyInner(OSSL3_CONST EVP_PKEY* pkey, @@ -422,14 +420,14 @@ bool WritePublicKeyInner(OSSL3_CONST EVP_PKEY* pkey, MaybeLocal WritePublicKey(Environment* env, OSSL3_CONST EVP_PKEY* pkey, const PublicKeyEncodingConfig& config) { - BIOPointer bio(BIO_new(BIO_s_mem())); + auto bio = BIOPointer::NewMem(); CHECK(bio); if (!WritePublicKeyInner(pkey, bio, config)) { ThrowCryptoError(env, ERR_get_error(), "Failed to encode public key"); return MaybeLocal(); } - return BIOToStringOrBuffer(env, bio.get(), config.format_); + return BIOToStringOrBuffer(env, bio, config.format_); } Maybe ExportJWKSecretKey(Environment* env, @@ -1448,7 +1446,7 @@ WebCryptoKeyExportStatus PKEY_SPKI_Export( CHECK_EQ(key_data->GetKeyType(), kKeyTypePublic); ManagedEVPPKey m_pkey = key_data->GetAsymmetricKey(); Mutex::ScopedLock lock(*m_pkey.mutex()); - BIOPointer bio(BIO_new(BIO_s_mem())); + auto bio = BIOPointer::NewMem(); CHECK(bio); if (!i2d_PUBKEY_bio(bio.get(), m_pkey.get())) return WebCryptoKeyExportStatus::FAILED; @@ -1464,7 +1462,7 @@ WebCryptoKeyExportStatus PKEY_PKCS8_Export( ManagedEVPPKey m_pkey = key_data->GetAsymmetricKey(); Mutex::ScopedLock lock(*m_pkey.mutex()); - BIOPointer bio(BIO_new(BIO_s_mem())); + auto bio = BIOPointer::NewMem(); CHECK(bio); PKCS8Pointer p8inf(EVP_PKEY2PKCS8(m_pkey.get())); if (!i2d_PKCS8_PRIV_KEY_INFO_bio(bio.get(), p8inf.get())) diff --git a/src/crypto/crypto_tls.cc b/src/crypto/crypto_tls.cc index 0ad6fa28542978..4013c1d8e2ff68 100644 --- a/src/crypto/crypto_tls.cc +++ b/src/crypto/crypto_tls.cc @@ -20,12 +20,13 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "crypto/crypto_tls.h" -#include "crypto/crypto_context.h" -#include "crypto/crypto_common.h" -#include "crypto/crypto_util.h" +#include +#include "async_wrap-inl.h" #include "crypto/crypto_bio.h" #include "crypto/crypto_clienthello-inl.h" -#include "async_wrap-inl.h" +#include "crypto/crypto_common.h" +#include "crypto/crypto_context.h" +#include "crypto/crypto_util.h" #include "debug_utils-inl.h" #include "memory_tracker-inl.h" #include "node_buffer.h" @@ -1244,7 +1245,7 @@ void TLSWrap::EnableTrace(const FunctionCallbackInfo& args) { #if HAVE_SSL_TRACE if (wrap->ssl_) { - wrap->bio_trace_.reset(BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT)); + wrap->bio_trace_ = BIOPointer::NewFp(stderr, BIO_NOCLOSE | BIO_FP_TEXT); SSL_set_msg_callback(wrap->ssl_.get(), [](int write_p, int version, int content_type, const void* buf, size_t len, SSL* ssl, void* arg) -> void { diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc index 78656b5ee6f46d..501177f15cbdb0 100644 --- a/src/crypto/crypto_util.cc +++ b/src/crypto/crypto_util.cc @@ -355,8 +355,7 @@ MaybeLocal ByteSource::ToBuffer(Environment* env) { ByteSource ByteSource::FromBIO(const BIOPointer& bio) { CHECK(bio); - BUF_MEM* bptr; - BIO_get_mem_ptr(bio.get(), &bptr); + BUF_MEM* bptr = bio; ByteSource::Builder out(bptr->length); memcpy(out.data(), bptr->data, bptr->length); return std::move(out).release(); diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc index 367ae2bea384b7..386528e65945f2 100644 --- a/src/crypto/crypto_x509.cc +++ b/src/crypto/crypto_x509.cc @@ -102,8 +102,7 @@ void Fingerprint(const FunctionCallbackInfo& args) { MaybeLocal ToV8Value(Local context, BIOPointer&& bio) { if (!bio) return {}; - BUF_MEM* mem; - BIO_get_mem_ptr(bio.get(), &mem); + BUF_MEM* mem = bio; Local ret; if (!String::NewFromUtf8(context->GetIsolate(), mem->data, @@ -161,8 +160,7 @@ MaybeLocal ToV8Value(Local context, const ASN1_STRING* str) { MaybeLocal ToV8Value(Local context, const BIOPointer& bio) { if (!bio) return {}; - BUF_MEM* mem; - BIO_get_mem_ptr(bio.get(), &mem); + BUF_MEM* mem = bio; Local ret; if (!String::NewFromUtf8(context->GetIsolate(), mem->data, @@ -175,8 +173,7 @@ MaybeLocal ToV8Value(Local context, const BIOPointer& bio) { MaybeLocal ToBuffer(Environment* env, BIOPointer* bio) { if (bio == nullptr || !*bio) return {}; - BUF_MEM* mem; - BIO_get_mem_ptr(bio->get(), &mem); + BUF_MEM* mem = *bio; auto backing = ArrayBuffer::NewBackingStore( mem->data, mem->length, @@ -664,14 +661,16 @@ MaybeLocal GetPubKey(Environment* env, OSSL3_CONST RSA* rsa) { } MaybeLocal GetModulusString(Environment* env, const BIGNUM* n) { - BIOPointer bio(BIO_new(BIO_s_mem())); + auto bio = BIOPointer::NewMem(); + if (!bio) return {}; BN_print(bio.get(), n); return ToV8Value(env->context(), bio); } MaybeLocal GetExponentString(Environment* env, const BIGNUM* e) { uint64_t exponent_word = static_cast(BignumPointer::GetWord(e)); - BIOPointer bio(BIO_new(BIO_s_mem())); + auto bio = BIOPointer::NewMem(); + if (!bio) return {}; BIO_printf(bio.get(), "0x%" PRIx64, exponent_word); return ToV8Value(env->context(), bio); }