Skip to content

Commit

Permalink
src: move more crypto code to ncrypto
Browse files Browse the repository at this point in the history
PR-URL: nodejs#54320
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
  • Loading branch information
jasnell committed Aug 19, 2024
1 parent 746e287 commit 561bc87
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 50 deletions.
53 changes: 53 additions & 0 deletions deps/ncrypto/ncrypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
51 changes: 49 additions & 2 deletions deps/ncrypto/ncrypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
#include <optional>
#include <string>
#include <string_view>
#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/x509.h>
#include <openssl/dh.h>
#include <openssl/dsa.h>
#include <openssl/ec.h>
Expand All @@ -17,6 +17,7 @@
#include <openssl/kdf.h>
#include <openssl/rsa.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#ifndef OPENSSL_NO_ENGINE
# include <openssl/engine.h>
#endif // !OPENSSL_NO_ENGINE
Expand Down Expand Up @@ -192,7 +193,6 @@ template <typename T, void (*function)(T*)>
using DeleteFnPtr = typename FunctionDeleter<T, function>::Pointer;

using BignumCtxPointer = DeleteFnPtr<BN_CTX, BN_CTX_free>;
using BIOPointer = DeleteFnPtr<BIO, BIO_free_all>;
using CipherCtxPointer = DeleteFnPtr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>;
using DHPointer = DeleteFnPtr<DH, DH_free>;
using DSAPointer = DeleteFnPtr<DSA, DSA_free>;
Expand Down Expand Up @@ -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 <typename...Args>
static void Printf(BIOPointer* bio, const char* format, Args...args) {
if (bio == nullptr || !*bio) return;
BIO_printf(bio->get(), format, std::forward<Args...>(args...));
}

private:
mutable DeleteFnPtr<BIO, BIO_free_all> bio_;
};

class BignumPointer final {
public:
BignumPointer() = default;
Expand Down
8 changes: 4 additions & 4 deletions src/crypto/crypto_bio.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<int>(len) ||
if (!bio || len > INT_MAX ||
BIOPointer::Write(&bio, std::string_view(data, len)) !=
static_cast<int>(len) ||
BIO_set_mem_eof_return(bio.get(), 0) != 1) {
return BIOPointer();
}
Expand Down
22 changes: 11 additions & 11 deletions src/crypto/crypto_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,17 @@ X509_STORE* GetOrCreateRootCertStore() {
// Caller responsible for BIO_free_all-ing the returned object.
BIOPointer LoadBIO(Environment* env, Local<Value> 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<char>(), bsrc.size());
if (written < 0) return nullptr;
if (static_cast<size_t>(written) != bsrc.size()) return nullptr;
if (bsrc.size() > INT_MAX) return {};
int written = BIOPointer::Write(
&bio, std::string_view(bsrc.data<char>(), bsrc.size()));
if (written < 0) return {};
if (static_cast<size_t>(written) != bsrc.size()) return {};
return bio;
}
return nullptr;
return {};
}

namespace {
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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<String> 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);
}

Expand Down
2 changes: 1 addition & 1 deletion src/crypto/crypto_ec.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
32 changes: 15 additions & 17 deletions src/crypto/crypto_keys.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<char*>(key_pem), key_pem_len));
auto bp = BIOPointer::New(key_pem, key_pem_len);
if (!bp)
return ParseKeyResult::kParseKeyFailed;

Expand All @@ -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);
Expand All @@ -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));
Expand Down Expand Up @@ -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;

Expand All @@ -233,7 +233,7 @@ ParseKeyResult ParsePrivateKey(EVPKeyPointer* pkey,
const unsigned char* p = reinterpret_cast<const unsigned char*>(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;

Expand Down Expand Up @@ -270,12 +270,10 @@ ParseKeyResult ParsePrivateKey(EVPKeyPointer* pkey,
return ParseKeyResult::kParseKeyFailed;
}

MaybeLocal<Value> BIOToStringOrBuffer(
Environment* env,
BIO* bio,
PKFormatType format) {
BUF_MEM* bptr;
BIO_get_mem_ptr(bio, &bptr);
MaybeLocal<Value> 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,
Expand All @@ -292,7 +290,7 @@ MaybeLocal<Value> BIOToStringOrBuffer(
MaybeLocal<Value> 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
Expand Down Expand Up @@ -388,7 +386,7 @@ MaybeLocal<Value> WritePrivateKey(Environment* env,
ThrowCryptoError(env, ERR_get_error(), "Failed to encode private key");
return MaybeLocal<Value>();
}
return BIOToStringOrBuffer(env, bio.get(), config.format_);
return BIOToStringOrBuffer(env, bio, config.format_);
}

bool WritePublicKeyInner(OSSL3_CONST EVP_PKEY* pkey,
Expand Down Expand Up @@ -422,14 +420,14 @@ bool WritePublicKeyInner(OSSL3_CONST EVP_PKEY* pkey,
MaybeLocal<Value> 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<Value>();
}
return BIOToStringOrBuffer(env, bio.get(), config.format_);
return BIOToStringOrBuffer(env, bio, config.format_);
}

Maybe<void> ExportJWKSecretKey(Environment* env,
Expand Down Expand Up @@ -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;
Expand All @@ -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()))
Expand Down
11 changes: 6 additions & 5 deletions src/crypto/crypto_tls.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 <cstdio>
#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"
Expand Down Expand Up @@ -1244,7 +1245,7 @@ void TLSWrap::EnableTrace(const FunctionCallbackInfo<Value>& 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 {
Expand Down
3 changes: 1 addition & 2 deletions src/crypto/crypto_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,7 @@ MaybeLocal<Uint8Array> 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<void>(), bptr->data, bptr->length);
return std::move(out).release();
Expand Down
Loading

0 comments on commit 561bc87

Please sign in to comment.