Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deps: start working on ncrypto dep #53803

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ with-code-cache test-code-cache:
out/Makefile: config.gypi common.gypi node.gyp \
deps/uv/uv.gyp deps/llhttp/llhttp.gyp deps/zlib/zlib.gyp \
deps/simdutf/simdutf.gyp deps/ada/ada.gyp deps/nbytes/nbytes.gyp \
tools/v8_gypfiles/toolchain.gypi tools/v8_gypfiles/features.gypi \
tools/v8_gypfiles/toolchain.gypi \
tools/v8_gypfiles/features.gypi \
tools/v8_gypfiles/inspector.gypi tools/v8_gypfiles/v8.gyp
$(PYTHON) tools/gyp_node.py -f make

Expand Down
14 changes: 14 additions & 0 deletions deps/ncrypto/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
##############################################################################
# #
# DO NOT EDIT THIS FILE! #
# #
##############################################################################

# This file is used by GN for building, which is NOT the build system used for
# building official binaries.
# Please modify the gyp files if you are making changes to build system.

import("unofficial.gni")

ncrypto_gn_build("ncrypto") {
}
5 changes: 5 additions & 0 deletions deps/ncrypto/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Node.js crypto (ncrypto) library

The `ncrypto` library extracts the base internal implementation of Node.js crypto operations
that support both `node:crypto` and Web Crypto implementations and makes them available for
use in other projects that need to emulate Node.js' behavior.
92 changes: 92 additions & 0 deletions deps/ncrypto/engine.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#include "ncrypto.h"

namespace ncrypto {

// ============================================================================
// Engine

#ifndef OPENSSL_NO_ENGINE
EnginePointer::EnginePointer(ENGINE* engine_, bool finish_on_exit_)
: engine(engine_),
finish_on_exit(finish_on_exit_) {}

EnginePointer::EnginePointer(EnginePointer&& other) noexcept
: engine(other.engine),
finish_on_exit(other.finish_on_exit) {
other.release();
}

EnginePointer::~EnginePointer() { reset(); }

EnginePointer& EnginePointer::operator=(EnginePointer&& other) noexcept {
if (this == &other) return *this;
this->~EnginePointer();
return *new (this) EnginePointer(std::move(other));
}

void EnginePointer::reset(ENGINE* engine_, bool finish_on_exit_) {
if (engine != nullptr) {
if (finish_on_exit) {
// This also does the equivalent of ENGINE_free.
ENGINE_finish(engine);
} else {
ENGINE_free(engine);
}
}
engine = engine_;
finish_on_exit = finish_on_exit_;
}

ENGINE* EnginePointer::release() {
ENGINE* ret = engine;
engine = nullptr;
finish_on_exit = false;
return ret;
}

EnginePointer EnginePointer::getEngineByName(const std::string_view name,
CryptoErrorList* errors) {
MarkPopErrorOnReturn mark_pop_error_on_return(errors);
EnginePointer engine(ENGINE_by_id(name.data()));
if (!engine) {
// Engine not found, try loading dynamically.
engine = EnginePointer(ENGINE_by_id("dynamic"));
if (engine) {
if (!ENGINE_ctrl_cmd_string(engine.get(), "SO_PATH", name.data(), 0) ||
!ENGINE_ctrl_cmd_string(engine.get(), "LOAD", nullptr, 0)) {
engine.reset();
}
}
}
return std::move(engine);
jasnell marked this conversation as resolved.
Show resolved Hide resolved
}

bool EnginePointer::setAsDefault(uint32_t flags, CryptoErrorList* errors) {
if (engine == nullptr) return false;
ClearErrorOnReturn clear_error_on_return(errors);
return ENGINE_set_default(engine, flags) != 0;
}

bool EnginePointer::init(bool finish_on_exit) {
if (engine == nullptr) return false;
if (finish_on_exit) setFinishOnExit();
return ENGINE_init(engine) == 1;
}

EVPKeyPointer EnginePointer::loadPrivateKey(const std::string_view key_name) {
if (engine == nullptr) return EVPKeyPointer();
return EVPKeyPointer(ENGINE_load_private_key(engine, key_name.data(), nullptr, nullptr));
}

void EnginePointer::initEnginesOnce() {
static bool initialized = false;
if (!initialized) {
ENGINE_load_builtin_engines();
ENGINE_register_all_complete();
initialized = true;
}
}

#endif // OPENSSL_NO_ENGINE

} // namespace ncrypto
223 changes: 223 additions & 0 deletions deps/ncrypto/ncrypto.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
#include "ncrypto.h"
#include <algorithm>
#include <cstring>
#include "openssl/bn.h"
#if OPENSSL_VERSION_MAJOR >= 3
#include "openssl/provider.h"
#endif

namespace ncrypto {

// ============================================================================

ClearErrorOnReturn::ClearErrorOnReturn(CryptoErrorList* errors) : errors_(errors) {
ERR_clear_error();
}

ClearErrorOnReturn::~ClearErrorOnReturn() {
if (errors_ != nullptr) errors_->capture();
ERR_clear_error();
}

int ClearErrorOnReturn::peeKError() { return ERR_peek_error(); }

MarkPopErrorOnReturn::MarkPopErrorOnReturn(CryptoErrorList* errors) : errors_(errors) {
ERR_set_mark();
}

MarkPopErrorOnReturn::~MarkPopErrorOnReturn() {
if (errors_ != nullptr) errors_->capture();
ERR_pop_to_mark();
}

int MarkPopErrorOnReturn::peekError() { return ERR_peek_error(); }

CryptoErrorList::CryptoErrorList(CryptoErrorList::Option option) {
if (option == Option::CAPTURE_ON_CONSTRUCT) capture();
}

void CryptoErrorList::capture() {
errors_.clear();
while(const auto err = ERR_get_error()) {
char buf[256];
ERR_error_string_n(err, buf, sizeof(buf));
errors_.emplace_front(buf);
}
}

void CryptoErrorList::add(std::string error) {
errors_.push_back(error);
}

std::optional<std::string> CryptoErrorList::pop_back() {
if (errors_.empty()) return std::nullopt;
std::string error = errors_.back();
errors_.pop_back();
return error;
}

std::optional<std::string> CryptoErrorList::pop_front() {
if (errors_.empty()) return std::nullopt;
std::string error = errors_.front();
errors_.pop_front();
return error;
}

// ============================================================================
bool isFipsEnabled() {
#if OPENSSL_VERSION_MAJOR >= 3
return EVP_default_properties_is_fips_enabled(nullptr) == 1;
#else
return FIPS_mode() == 1;
#endif
}

bool setFipsEnabled(bool enable, CryptoErrorList* errors) {
if (isFipsEnabled() == enable) return true;
ClearErrorOnReturn clearErrorOnReturn(errors);
#if OPENSSL_VERSION_MAJOR >= 3
return EVP_default_properties_enable_fips(nullptr, enable ? 1 : 0) == 1;
#else
return FIPS_mode_set(enable ? 1 : 0) == 1;
#endif
}

bool testFipsEnabled() {
#if OPENSSL_VERSION_MAJOR >= 3
OSSL_PROVIDER* fips_provider = nullptr;
if (OSSL_PROVIDER_available(nullptr, "fips")) {
fips_provider = OSSL_PROVIDER_load(nullptr, "fips");
}
const auto enabled = fips_provider == nullptr ? 0 :
OSSL_PROVIDER_self_test(fips_provider) ? 1 : 0;
#else
#ifdef OPENSSL_FIPS
const auto enabled = FIPS_selftest() ? 1 : 0;
#else // OPENSSL_FIPS
const auto enabled = 0;
#endif // OPENSSL_FIPS
#endif

return enabled;
}

// ============================================================================
// Bignum
BignumPointer::BignumPointer(BIGNUM* bignum) : bn_(bignum) {}

BignumPointer::BignumPointer(BignumPointer&& other) noexcept
: bn_(other.release()) {}

BignumPointer& BignumPointer::operator=(BignumPointer&& other) noexcept {
if (this == &other) return *this;
this->~BignumPointer();
return *new (this) BignumPointer(std::move(other));
}

BignumPointer::~BignumPointer() { reset(); }

void BignumPointer::reset(BIGNUM* bn) {
bn_.reset(bn);
}

BIGNUM* BignumPointer::release() {
return bn_.release();
}

size_t BignumPointer::byteLength() {
if (bn_ == nullptr) return 0;
return BN_num_bytes(bn_.get());
}

std::vector<uint8_t> BignumPointer::encode() {
return encodePadded(bn_.get(), byteLength());
}

std::vector<uint8_t> BignumPointer::encodePadded(size_t size) {
return encodePadded(bn_.get(), size);
}

std::vector<uint8_t> BignumPointer::encode(const BIGNUM* bn) {
return encodePadded(bn, bn != nullptr ? BN_num_bytes(bn) : 0);
}

std::vector<uint8_t> BignumPointer::encodePadded(const BIGNUM* bn, size_t s) {
if (bn == nullptr) return std::vector<uint8_t>(0);
size_t size = std::max(s, static_cast<size_t>(BN_num_bytes(bn)));
std::vector<uint8_t> buf(size);
BN_bn2binpad(bn, buf.data(), size);
return buf;
}

bool BignumPointer::operator==(const BignumPointer& other) noexcept {
if (bn_ == nullptr && other.bn_ != nullptr) return false;
if (bn_ != nullptr && other.bn_ == nullptr) return false;
if (bn_ == nullptr && other.bn_ == nullptr) return true;
return BN_cmp(bn_.get(), other.bn_.get()) == 0;
}

bool BignumPointer::operator==(const BIGNUM* other) noexcept {
if (bn_ == nullptr && other != nullptr) return false;
if (bn_ != nullptr && other == nullptr) return false;
if (bn_ == nullptr && other == nullptr) return true;
return BN_cmp(bn_.get(), other) == 0;
}

// ============================================================================
// Utility methods

bool CSPRNG(void* buffer, size_t length) {
auto buf = reinterpret_cast<unsigned char*>(buffer);
do {
if (1 == RAND_status()) {
#if OPENSSL_VERSION_MAJOR >= 3
if (1 == RAND_bytes_ex(nullptr, buf, length, 0)) {
return true;
}
#else
while (length > INT_MAX && 1 == RAND_bytes(buf, INT_MAX)) {
buf += INT_MAX;
length -= INT_MAX;
}
if (length <= INT_MAX && 1 == RAND_bytes(buf, static_cast<int>(length)))
return true;
#endif
}
#if OPENSSL_VERSION_MAJOR >= 3
const auto code = ERR_peek_last_error();
// A misconfigured OpenSSL 3 installation may report 1 from RAND_poll()
// and RAND_status() but fail in RAND_bytes() if it cannot look up
// a matching algorithm for the CSPRNG.
if (ERR_GET_LIB(code) == ERR_LIB_RAND) {
const auto reason = ERR_GET_REASON(code);
if (reason == RAND_R_ERROR_INSTANTIATING_DRBG ||
reason == RAND_R_UNABLE_TO_FETCH_DRBG ||
reason == RAND_R_UNABLE_TO_CREATE_DRBG) {
return false;
}
}
#endif
} while (1 == RAND_poll());

return false;
}

int NoPasswordCallback(char* buf, int size, int rwflag, void* u) {
return 0;
}

int PasswordCallback(char* buf, int size, int rwflag, void* u) {
const Buffer* passphrase = static_cast<const Buffer*>(u);
if (passphrase != nullptr) {
size_t buflen = static_cast<size_t>(size);
size_t len = passphrase->len;
if (buflen < len)
return -1;
memcpy(buf, reinterpret_cast<const char*>(passphrase->data), len);
return len;
}

return -1;
}

} // namespace ncrypto
27 changes: 27 additions & 0 deletions deps/ncrypto/ncrypto.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
'variables': {
'ncrypto_sources': [
'engine.cc',
'ncrypto.cc',
'ncrypto.h',
],
},
'targets': [
{
'target_name': 'ncrypto',
'type': 'static_library',
'include_dirs': ['.'],
'direct_dependent_settings': {
'include_dirs': ['.'],
},
'sources': [ '<@(ncrypto_sources)' ],
'conditions': [
['node_shared_openssl=="false"', {
'dependencies': [
'../openssl/openssl.gyp:openssl'
]
}],
]
},
]
}
Loading
Loading