mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
deps: start working on ncrypto dep
Start moving src/crypto functionality out to a separate dep that can be shared with other projects that need to emulate Node.js crypto behavior. PR-URL: https://github.com/nodejs/node/pull/53803 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
This commit is contained in:
parent
c77424c990
commit
efe5b81df9
3
Makefile
3
Makefile
@ -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
|
||||
|
||||
|
14
deps/ncrypto/BUILD.gn
vendored
Normal file
14
deps/ncrypto/BUILD.gn
vendored
Normal file
@ -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
deps/ncrypto/README.md
vendored
Normal file
5
deps/ncrypto/README.md
vendored
Normal file
@ -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
deps/ncrypto/engine.cc
vendored
Normal file
92
deps/ncrypto/engine.cc
vendored
Normal file
@ -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);
|
||||
}
|
||||
|
||||
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
deps/ncrypto/ncrypto.cc
vendored
Normal file
223
deps/ncrypto/ncrypto.cc
vendored
Normal file
@ -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
deps/ncrypto/ncrypto.gyp
vendored
Normal file
27
deps/ncrypto/ncrypto.gyp
vendored
Normal file
@ -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'
|
||||
]
|
||||
}],
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
298
deps/ncrypto/ncrypto.h
vendored
Normal file
298
deps/ncrypto/ncrypto.h
vendored
Normal file
@ -0,0 +1,298 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include "openssl/bn.h"
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/kdf.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/ssl.h>
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
# include <openssl/engine.h>
|
||||
#endif // !OPENSSL_NO_ENGINE
|
||||
// The FIPS-related functions are only available
|
||||
// when the OpenSSL itself was compiled with FIPS support.
|
||||
#if defined(OPENSSL_FIPS) && OPENSSL_VERSION_MAJOR < 3
|
||||
# include <openssl/fips.h>
|
||||
#endif // OPENSSL_FIPS
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define NCRYPTO_MUST_USE_RESULT __attribute__((warn_unused_result))
|
||||
#else
|
||||
#define NCRYPTO_MUST_USE_RESULT
|
||||
#endif
|
||||
|
||||
namespace ncrypto {
|
||||
|
||||
// ============================================================================
|
||||
// Utility macros
|
||||
|
||||
#if NCRYPTO_DEVELOPMENT_CHECKS
|
||||
#define NCRYPTO_STR(x) #x
|
||||
#define NCRYPTO_REQUIRE(EXPR) \
|
||||
{ \
|
||||
if (!(EXPR) { abort(); }) }
|
||||
|
||||
#define NCRYPTO_FAIL(MESSAGE) \
|
||||
do { \
|
||||
std::cerr << "FAIL: " << (MESSAGE) << std::endl; \
|
||||
abort(); \
|
||||
} while (0);
|
||||
#define NCRYPTO_ASSERT_EQUAL(LHS, RHS, MESSAGE) \
|
||||
do { \
|
||||
if (LHS != RHS) { \
|
||||
std::cerr << "Mismatch: '" << LHS << "' - '" << RHS << "'" << std::endl; \
|
||||
NCRYPTO_FAIL(MESSAGE); \
|
||||
} \
|
||||
} while (0);
|
||||
#define NCRYPTO_ASSERT_TRUE(COND) \
|
||||
do { \
|
||||
if (!(COND)) { \
|
||||
std::cerr << "Assert at line " << __LINE__ << " of file " << __FILE__ \
|
||||
<< std::endl; \
|
||||
NCRYPTO_FAIL(NCRYPTO_STR(COND)); \
|
||||
} \
|
||||
} while (0);
|
||||
#else
|
||||
#define NCRYPTO_FAIL(MESSAGE)
|
||||
#define NCRYPTO_ASSERT_EQUAL(LHS, RHS, MESSAGE)
|
||||
#define NCRYPTO_ASSERT_TRUE(COND)
|
||||
#endif
|
||||
|
||||
#define NCRYPTO_DISALLOW_COPY(Name) \
|
||||
Name(const Name&) = delete; \
|
||||
Name& operator=(const Name&) = delete;
|
||||
#define NCRYPTO_DISALLOW_MOVE(Name) \
|
||||
Name(Name&&) = delete; \
|
||||
Name& operator=(Name&&) = delete;
|
||||
#define NCRYPTO_DISALLOW_COPY_AND_MOVE(Name) \
|
||||
NCRYPTO_DISALLOW_COPY(Name) \
|
||||
NCRYPTO_DISALLOW_MOVE(Name)
|
||||
#define NCRYPTO_DISALLOW_NEW_DELETE() \
|
||||
void* operator new(size_t) = delete; \
|
||||
void operator delete(void*) = delete;
|
||||
|
||||
// ============================================================================
|
||||
// Error handling utilities
|
||||
|
||||
// Capture the current OpenSSL Error Stack. The stack will be ordered such
|
||||
// that the error currently at the top of the stack is at the end of the
|
||||
// list and the error at the bottom of the stack is at the beginning.
|
||||
class CryptoErrorList final {
|
||||
public:
|
||||
enum class Option {
|
||||
NONE,
|
||||
CAPTURE_ON_CONSTRUCT
|
||||
};
|
||||
CryptoErrorList(Option option = Option::CAPTURE_ON_CONSTRUCT);
|
||||
|
||||
void capture();
|
||||
|
||||
// Add an error message to the end of the stack.
|
||||
void add(std::string message);
|
||||
|
||||
inline const std::string& peek_back() const { return errors_.back(); }
|
||||
inline size_t size() const { return errors_.size(); }
|
||||
inline bool empty() const { return errors_.empty(); }
|
||||
|
||||
inline auto begin() const noexcept { return errors_.begin(); }
|
||||
inline auto end() const noexcept { return errors_.end(); }
|
||||
inline auto rbegin() const noexcept { return errors_.rbegin(); }
|
||||
inline auto rend() const noexcept { return errors_.rend(); }
|
||||
|
||||
std::optional<std::string> pop_back();
|
||||
std::optional<std::string> pop_front();
|
||||
|
||||
private:
|
||||
std::list<std::string> errors_;
|
||||
};
|
||||
|
||||
// Forcibly clears the error stack on destruction. This stops stale errors
|
||||
// from popping up later in the lifecycle of crypto operations where they
|
||||
// would cause spurious failures. It is a rather blunt method, though, and
|
||||
// ERR_clear_error() isn't necessarily cheap.
|
||||
//
|
||||
// If created with a pointer to a CryptoErrorList, the current OpenSSL error
|
||||
// stack will be captured before clearing the error.
|
||||
class ClearErrorOnReturn final {
|
||||
public:
|
||||
ClearErrorOnReturn(CryptoErrorList* errors = nullptr);
|
||||
~ClearErrorOnReturn();
|
||||
NCRYPTO_DISALLOW_COPY_AND_MOVE(ClearErrorOnReturn)
|
||||
NCRYPTO_DISALLOW_NEW_DELETE()
|
||||
|
||||
int peeKError();
|
||||
|
||||
private:
|
||||
CryptoErrorList* errors_;
|
||||
};
|
||||
|
||||
// Pop errors from OpenSSL's error stack that were added between when this
|
||||
// was constructed and destructed.
|
||||
//
|
||||
// If created with a pointer to a CryptoErrorList, the current OpenSSL error
|
||||
// stack will be captured before resetting the error to the mark.
|
||||
class MarkPopErrorOnReturn final {
|
||||
public:
|
||||
MarkPopErrorOnReturn(CryptoErrorList* errors = nullptr);
|
||||
~MarkPopErrorOnReturn();
|
||||
NCRYPTO_DISALLOW_COPY_AND_MOVE(MarkPopErrorOnReturn)
|
||||
NCRYPTO_DISALLOW_NEW_DELETE()
|
||||
|
||||
int peekError();
|
||||
|
||||
private:
|
||||
CryptoErrorList* errors_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Various smart pointer aliases for OpenSSL types.
|
||||
|
||||
template <typename T, void (*function)(T*)>
|
||||
struct FunctionDeleter {
|
||||
void operator()(T* pointer) const { function(pointer); }
|
||||
typedef std::unique_ptr<T, FunctionDeleter> Pointer;
|
||||
};
|
||||
|
||||
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>;
|
||||
using DSASigPointer = DeleteFnPtr<DSA_SIG, DSA_SIG_free>;
|
||||
using ECDSASigPointer = DeleteFnPtr<ECDSA_SIG, ECDSA_SIG_free>;
|
||||
using ECPointer = DeleteFnPtr<EC_KEY, EC_KEY_free>;
|
||||
using ECGroupPointer = DeleteFnPtr<EC_GROUP, EC_GROUP_free>;
|
||||
using ECKeyPointer = DeleteFnPtr<EC_KEY, EC_KEY_free>;
|
||||
using ECPointPointer = DeleteFnPtr<EC_POINT, EC_POINT_free>;
|
||||
using EVPKeyCtxPointer = DeleteFnPtr<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
|
||||
using EVPKeyPointer = DeleteFnPtr<EVP_PKEY, EVP_PKEY_free>;
|
||||
using EVPMDCtxPointer = DeleteFnPtr<EVP_MD_CTX, EVP_MD_CTX_free>;
|
||||
using HMACCtxPointer = DeleteFnPtr<HMAC_CTX, HMAC_CTX_free>;
|
||||
using NetscapeSPKIPointer = DeleteFnPtr<NETSCAPE_SPKI, NETSCAPE_SPKI_free>;
|
||||
using PKCS8Pointer = DeleteFnPtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>;
|
||||
using RSAPointer = DeleteFnPtr<RSA, RSA_free>;
|
||||
using SSLCtxPointer = DeleteFnPtr<SSL_CTX, SSL_CTX_free>;
|
||||
using SSLPointer = DeleteFnPtr<SSL, SSL_free>;
|
||||
using SSLSessionPointer = DeleteFnPtr<SSL_SESSION, SSL_SESSION_free>;
|
||||
using X509Pointer = DeleteFnPtr<X509, X509_free>;
|
||||
|
||||
class BignumPointer final {
|
||||
public:
|
||||
BignumPointer() = default;
|
||||
explicit BignumPointer(BIGNUM* bignum);
|
||||
BignumPointer(BignumPointer&& other) noexcept;
|
||||
BignumPointer& operator=(BignumPointer&& other) noexcept;
|
||||
NCRYPTO_DISALLOW_COPY(BignumPointer)
|
||||
~BignumPointer();
|
||||
|
||||
bool operator==(const BignumPointer& other) noexcept;
|
||||
bool operator==(const BIGNUM* other) noexcept;
|
||||
inline bool operator==(std::nullptr_t) noexcept { return bn_ == nullptr; }
|
||||
inline operator bool() const { return bn_ != nullptr; }
|
||||
inline BIGNUM* get() const noexcept { return bn_.get(); }
|
||||
void reset(BIGNUM* bn = nullptr);
|
||||
BIGNUM* release();
|
||||
|
||||
size_t byteLength();
|
||||
|
||||
std::vector<uint8_t> encode();
|
||||
std::vector<uint8_t> encodePadded(size_t size);
|
||||
|
||||
static std::vector<uint8_t> encode(const BIGNUM* bn);
|
||||
static std::vector<uint8_t> encodePadded(const BIGNUM* bn, size_t size);
|
||||
|
||||
private:
|
||||
DeleteFnPtr<BIGNUM, BN_clear_free> bn_;
|
||||
};
|
||||
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
class EnginePointer final {
|
||||
public:
|
||||
EnginePointer() = default;
|
||||
|
||||
explicit EnginePointer(ENGINE* engine_, bool finish_on_exit = false);
|
||||
EnginePointer(EnginePointer&& other) noexcept;
|
||||
EnginePointer& operator=(EnginePointer&& other) noexcept;
|
||||
NCRYPTO_DISALLOW_COPY(EnginePointer)
|
||||
~EnginePointer();
|
||||
|
||||
inline operator bool() const { return engine != nullptr; }
|
||||
inline ENGINE* get() { return engine; }
|
||||
inline void setFinishOnExit() { finish_on_exit = true; }
|
||||
|
||||
void reset(ENGINE* engine_ = nullptr, bool finish_on_exit_ = false);
|
||||
|
||||
bool setAsDefault(uint32_t flags, CryptoErrorList* errors = nullptr);
|
||||
bool init(bool finish_on_exit = false);
|
||||
EVPKeyPointer loadPrivateKey(const std::string_view key_name);
|
||||
|
||||
// Release ownership of the ENGINE* pointer.
|
||||
ENGINE* release();
|
||||
|
||||
// Retrieve an OpenSSL Engine instance by name. If the name does not
|
||||
// identify a valid named engine, the returned EnginePointer will be
|
||||
// empty.
|
||||
static EnginePointer getEngineByName(const std::string_view name,
|
||||
CryptoErrorList* errors = nullptr);
|
||||
|
||||
// Call once when initializing OpenSSL at startup for the process.
|
||||
static void initEnginesOnce();
|
||||
|
||||
private:
|
||||
ENGINE* engine = nullptr;
|
||||
bool finish_on_exit = false;
|
||||
};
|
||||
#endif // !OPENSSL_NO_ENGINE
|
||||
|
||||
// ============================================================================
|
||||
// FIPS
|
||||
bool isFipsEnabled();
|
||||
|
||||
bool setFipsEnabled(bool enabled, CryptoErrorList* errors);
|
||||
|
||||
bool testFipsEnabled();
|
||||
|
||||
// ============================================================================
|
||||
// Various utilities
|
||||
|
||||
struct Buffer {
|
||||
const void* data;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
bool CSPRNG(void* buffer, size_t length) NCRYPTO_MUST_USE_RESULT;
|
||||
|
||||
// This callback is used to avoid the default passphrase callback in OpenSSL
|
||||
// which will typically prompt for the passphrase. The prompting is designed
|
||||
// for the OpenSSL CLI, but works poorly for some environments like Node.js
|
||||
// because it involves synchronous interaction with the controlling terminal,
|
||||
// something we never want, and use this function to avoid it.
|
||||
int NoPasswordCallback(char* buf, int size, int rwflag, void* u);
|
||||
|
||||
int PasswordCallback(char* buf, int size, int rwflag, void* u);
|
||||
|
||||
// ============================================================================
|
||||
// Version metadata
|
||||
#define NCRYPTO_VERSION "0.0.1"
|
||||
|
||||
enum {
|
||||
NCRYPTO_VERSION_MAJOR = 0,
|
||||
NCRYPTO_VERSION_MINOR = 0,
|
||||
NCRYPTO_VERSION_REVISION = 1,
|
||||
};
|
||||
|
||||
} // namespace ncrypto
|
9
node.gyp
9
node.gyp
@ -940,6 +940,9 @@
|
||||
'<@(node_crypto_sources)',
|
||||
'<@(node_quic_sources)',
|
||||
],
|
||||
'dependencies': [
|
||||
'deps/ncrypto/ncrypto.gyp:ncrypto',
|
||||
],
|
||||
}],
|
||||
[ 'OS in "linux freebsd mac solaris" and '
|
||||
'target_arch=="x64" and '
|
||||
@ -1201,6 +1204,9 @@
|
||||
'defines': [
|
||||
'HAVE_OPENSSL=1',
|
||||
],
|
||||
'dependencies': [
|
||||
'deps/ncrypto/ncrypto.gyp:ncrypto',
|
||||
],
|
||||
'sources': [ '<@(node_cctest_openssl_sources)' ],
|
||||
}],
|
||||
['v8_enable_inspector==1', {
|
||||
@ -1394,6 +1400,9 @@
|
||||
'defines': [ 'NODE_MKSNAPSHOT_USE_ARRAY_LITERALS=1' ],
|
||||
}],
|
||||
[ 'node_use_openssl=="true"', {
|
||||
'dependencies': [
|
||||
'deps/ncrypto/ncrypto.gyp:ncrypto',
|
||||
],
|
||||
'defines': [
|
||||
'HAVE_OPENSSL=1',
|
||||
],
|
||||
|
@ -1,10 +1,11 @@
|
||||
#include "crypto/crypto_context.h"
|
||||
#include "base_object-inl.h"
|
||||
#include "crypto/crypto_bio.h"
|
||||
#include "crypto/crypto_common.h"
|
||||
#include "crypto/crypto_util.h"
|
||||
#include "base_object-inl.h"
|
||||
#include "env-inl.h"
|
||||
#include "memory_tracker-inl.h"
|
||||
#include "ncrypto.h"
|
||||
#include "node.h"
|
||||
#include "node_buffer.h"
|
||||
#include "node_options.h"
|
||||
@ -655,26 +656,28 @@ void SecureContext::SetEngineKey(const FunctionCallbackInfo<Value>& args) {
|
||||
"experimental permission model is enabled");
|
||||
}
|
||||
|
||||
CryptoErrorStore errors;
|
||||
ncrypto::CryptoErrorList errors;
|
||||
Utf8Value engine_id(env->isolate(), args[1]);
|
||||
EnginePointer engine = LoadEngineById(*engine_id, &errors);
|
||||
auto engine = ncrypto::EnginePointer::getEngineByName(
|
||||
engine_id.ToStringView(), &errors);
|
||||
if (!engine) {
|
||||
Local<Value> exception;
|
||||
if (errors.ToException(env).ToLocal(&exception))
|
||||
if (errors.empty()) {
|
||||
errors.add(getNodeCryptoErrorString(NodeCryptoError::ENGINE_NOT_FOUND,
|
||||
*engine_id));
|
||||
}
|
||||
if (cryptoErrorListToException(env, errors).ToLocal(&exception))
|
||||
env->isolate()->ThrowException(exception);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ENGINE_init(engine.get())) {
|
||||
if (!engine.init(true /* finish on exit*/)) {
|
||||
return THROW_ERR_CRYPTO_OPERATION_FAILED(
|
||||
env, "Failure to initialize engine");
|
||||
}
|
||||
|
||||
engine.finish_on_exit = true;
|
||||
|
||||
Utf8Value key_name(env->isolate(), args[0]);
|
||||
EVPKeyPointer key(ENGINE_load_private_key(engine.get(), *key_name,
|
||||
nullptr, nullptr));
|
||||
auto key = engine.loadPrivateKey(key_name.ToStringView());
|
||||
|
||||
if (!key)
|
||||
return ThrowCryptoError(env, ERR_get_error(), "ENGINE_load_private_key");
|
||||
@ -1143,12 +1146,17 @@ void SecureContext::SetClientCertEngine(
|
||||
"experimental permission model is enabled");
|
||||
}
|
||||
|
||||
CryptoErrorStore errors;
|
||||
ncrypto::CryptoErrorList errors;
|
||||
const Utf8Value engine_id(env->isolate(), args[0]);
|
||||
EnginePointer engine = LoadEngineById(*engine_id, &errors);
|
||||
auto engine = ncrypto::EnginePointer::getEngineByName(
|
||||
engine_id.ToStringView(), &errors);
|
||||
if (!engine) {
|
||||
Local<Value> exception;
|
||||
if (errors.ToException(env).ToLocal(&exception))
|
||||
if (errors.empty()) {
|
||||
errors.add(getNodeCryptoErrorString(NodeCryptoError::ENGINE_NOT_FOUND,
|
||||
*engine_id));
|
||||
}
|
||||
if (cryptoErrorListToException(env, errors).ToLocal(&exception))
|
||||
env->isolate()->ThrowException(exception);
|
||||
return;
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ class SecureContext final : public BaseObject {
|
||||
X509Pointer issuer_;
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
bool client_cert_engine_provided_ = false;
|
||||
EnginePointer private_key_engine_;
|
||||
ncrypto::EnginePointer private_key_engine_;
|
||||
#endif // !OPENSSL_NO_ENGINE
|
||||
|
||||
unsigned char ticket_key_name_[16];
|
||||
|
@ -64,6 +64,26 @@ using v8::Value;
|
||||
namespace crypto {
|
||||
|
||||
namespace {
|
||||
|
||||
// Our custom implementation of the certificate verify callback
|
||||
// used when establishing a TLS handshake. Because we cannot perform
|
||||
// I/O quickly enough with X509_STORE_CTX_ APIs in this callback,
|
||||
// we ignore preverify_ok errors here and let the handshake continue.
|
||||
// In other words, this VerifyCallback is a non-op. It is imperative
|
||||
// that the user user Connection::VerifyError after the `secure`
|
||||
// callback has been made.
|
||||
int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) {
|
||||
// From https://www.openssl.org/docs/man1.1.1/man3/SSL_verify_cb:
|
||||
//
|
||||
// If VerifyCallback returns 1, the verification process is continued. If
|
||||
// VerifyCallback always returns 1, the TLS/SSL handshake will not be
|
||||
// terminated with respect to verification failures and the connection will
|
||||
// be established. The calling process can however retrieve the error code
|
||||
// of the last verification error using SSL_get_verify_result(3) or by
|
||||
// maintaining its own error storage managed by VerifyCallback.
|
||||
return 1;
|
||||
}
|
||||
|
||||
SSL_SESSION* GetSessionCallback(
|
||||
SSL* s,
|
||||
const unsigned char* key,
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "crypto/crypto_keys.h"
|
||||
#include "env-inl.h"
|
||||
#include "memory_tracker-inl.h"
|
||||
#include "ncrypto.h"
|
||||
#include "node_buffer.h"
|
||||
#include "node_options-inl.h"
|
||||
#include "string_bytes.h"
|
||||
@ -11,6 +12,10 @@
|
||||
#include "util-inl.h"
|
||||
#include "v8.h"
|
||||
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
#include <openssl/engine.h>
|
||||
#endif // !OPENSSL_NO_ENGINE
|
||||
|
||||
#include "math.h"
|
||||
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
@ -43,22 +48,6 @@ using v8::Uint8Array;
|
||||
using v8::Value;
|
||||
|
||||
namespace crypto {
|
||||
int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) {
|
||||
// From https://www.openssl.org/docs/man1.1.1/man3/SSL_verify_cb:
|
||||
//
|
||||
// If VerifyCallback returns 1, the verification process is continued. If
|
||||
// VerifyCallback always returns 1, the TLS/SSL handshake will not be
|
||||
// terminated with respect to verification failures and the connection will
|
||||
// be established. The calling process can however retrieve the error code
|
||||
// of the last verification error using SSL_get_verify_result(3) or by
|
||||
// maintaining its own error storage managed by VerifyCallback.
|
||||
//
|
||||
// Since we cannot perform I/O quickly enough with X509_STORE_CTX_ APIs in
|
||||
// this callback, we ignore all preverify_ok errors and let the handshake
|
||||
// continue. It is imperative that the user use Connection::VerifyError after
|
||||
// the 'secure' callback has been made.
|
||||
return 1;
|
||||
}
|
||||
|
||||
MUST_USE_RESULT CSPRNGResult CSPRNG(void* buffer, size_t length) {
|
||||
unsigned char* buf = static_cast<unsigned char*>(buffer);
|
||||
@ -206,21 +195,14 @@ void InitCryptoOnce() {
|
||||
sk_SSL_COMP_zero(SSL_COMP_get_compression_methods());
|
||||
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
ERR_load_ENGINE_strings();
|
||||
ENGINE_load_builtin_engines();
|
||||
ncrypto::EnginePointer::initEnginesOnce();
|
||||
#endif // !OPENSSL_NO_ENGINE
|
||||
}
|
||||
|
||||
void GetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
|
||||
Mutex::ScopedLock lock(per_process::cli_options_mutex);
|
||||
Mutex::ScopedLock fips_lock(fips_mutex);
|
||||
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
args.GetReturnValue().Set(EVP_default_properties_is_fips_enabled(nullptr) ?
|
||||
1 : 0);
|
||||
#else
|
||||
args.GetReturnValue().Set(FIPS_mode() ? 1 : 0);
|
||||
#endif
|
||||
args.GetReturnValue().Set(ncrypto::isFipsEnabled() ? 1 : 0);
|
||||
}
|
||||
|
||||
void SetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
|
||||
@ -232,43 +214,19 @@ void SetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK(env->owns_process_state());
|
||||
bool enable = args[0]->BooleanValue(env->isolate());
|
||||
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
if (enable == EVP_default_properties_is_fips_enabled(nullptr))
|
||||
#else
|
||||
if (static_cast<int>(enable) == FIPS_mode())
|
||||
#endif
|
||||
return; // No action needed.
|
||||
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
if (!EVP_default_properties_enable_fips(nullptr, enable)) {
|
||||
#else
|
||||
if (!FIPS_mode_set(enable)) {
|
||||
#endif
|
||||
unsigned long err = ERR_get_error(); // NOLINT(runtime/int)
|
||||
return ThrowCryptoError(env, err);
|
||||
ncrypto::CryptoErrorList errors;
|
||||
if (!ncrypto::setFipsEnabled(enable, &errors)) {
|
||||
Local<Value> exception;
|
||||
if (cryptoErrorListToException(env, errors).ToLocal(&exception)) {
|
||||
env->isolate()->ThrowException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestFipsCrypto(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Mutex::ScopedLock lock(per_process::cli_options_mutex);
|
||||
Mutex::ScopedLock fips_lock(fips_mutex);
|
||||
|
||||
#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
|
||||
|
||||
args.GetReturnValue().Set(enabled);
|
||||
args.GetReturnValue().Set(ncrypto::testFipsEnabled() ? 1 : 0);
|
||||
}
|
||||
|
||||
void CryptoErrorStore::Capture() {
|
||||
@ -285,6 +243,60 @@ bool CryptoErrorStore::Empty() const {
|
||||
return errors_.empty();
|
||||
}
|
||||
|
||||
MaybeLocal<Value> cryptoErrorListToException(
|
||||
Environment* env, const ncrypto::CryptoErrorList& errors) {
|
||||
// The CryptoErrorList contains a listing of zero or more errors.
|
||||
// If there are no errors, it is likely a bug but we will return
|
||||
// an error anyway.
|
||||
if (errors.empty()) {
|
||||
return Exception::Error(FIXED_ONE_BYTE_STRING(env->isolate(), "Ok"));
|
||||
}
|
||||
|
||||
// The last error in the list is the one that will be used as the
|
||||
// error message. All other errors will be added to the .opensslErrorStack
|
||||
// property. We know there has to be at least one error in the list at
|
||||
// this point.
|
||||
auto& last = errors.peek_back();
|
||||
Local<String> message;
|
||||
if (!String::NewFromUtf8(
|
||||
env->isolate(), last.data(), NewStringType::kNormal, last.size())
|
||||
.ToLocal(&message)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Local<Value> exception = Exception::Error(message);
|
||||
CHECK(!exception.IsEmpty());
|
||||
|
||||
if (errors.size() > 1) {
|
||||
CHECK(exception->IsObject());
|
||||
Local<Object> exception_obj = exception.As<Object>();
|
||||
std::vector<Local<Value>> stack(errors.size() - 1);
|
||||
|
||||
// Iterate over all but the last error in the list.
|
||||
auto current = errors.begin();
|
||||
auto last = errors.end();
|
||||
last--;
|
||||
while (current != last) {
|
||||
Local<Value> error;
|
||||
if (!ToV8Value(env->context(), *current).ToLocal(&error)) {
|
||||
return {};
|
||||
}
|
||||
stack.push_back(error);
|
||||
++current;
|
||||
}
|
||||
|
||||
Local<v8::Array> stackArray =
|
||||
v8::Array::New(env->isolate(), &stack[0], stack.size());
|
||||
|
||||
if (!exception_obj
|
||||
->Set(env->context(), env->openssl_error_stack(), stackArray)
|
||||
.IsNothing()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return exception;
|
||||
}
|
||||
|
||||
MaybeLocal<Value> CryptoErrorStore::ToException(
|
||||
Environment* env,
|
||||
Local<String> exception_string) const {
|
||||
@ -591,54 +603,8 @@ void ThrowCryptoError(Environment* env,
|
||||
}
|
||||
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
EnginePointer LoadEngineById(const char* id, CryptoErrorStore* errors) {
|
||||
MarkPopErrorOnReturn mark_pop_error_on_return;
|
||||
|
||||
EnginePointer engine(ENGINE_by_id(id));
|
||||
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", id, 0) ||
|
||||
!ENGINE_ctrl_cmd_string(engine.get(), "LOAD", nullptr, 0)) {
|
||||
engine.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!engine && errors != nullptr) {
|
||||
errors->Capture();
|
||||
if (errors->Empty()) {
|
||||
errors->Insert(NodeCryptoError::ENGINE_NOT_FOUND, id);
|
||||
}
|
||||
}
|
||||
|
||||
return engine;
|
||||
}
|
||||
|
||||
bool SetEngine(const char* id, uint32_t flags, CryptoErrorStore* errors) {
|
||||
ClearErrorOnReturn clear_error_on_return;
|
||||
EnginePointer engine = LoadEngineById(id, errors);
|
||||
if (!engine)
|
||||
return false;
|
||||
|
||||
if (!ENGINE_set_default(engine.get(), flags)) {
|
||||
if (errors != nullptr)
|
||||
errors->Capture();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetEngine(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
CHECK(args.Length() >= 2 && args[0]->IsString());
|
||||
uint32_t flags;
|
||||
if (!args[1]->Uint32Value(env->context()).To(&flags)) return;
|
||||
|
||||
const node::Utf8Value engine_id(env->isolate(), args[0]);
|
||||
|
||||
if (UNLIKELY(env->permission()->enabled())) {
|
||||
return THROW_ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED(
|
||||
env,
|
||||
@ -646,7 +612,16 @@ void SetEngine(const FunctionCallbackInfo<Value>& args) {
|
||||
"experimental permission model is enabled");
|
||||
}
|
||||
|
||||
args.GetReturnValue().Set(SetEngine(*engine_id, flags));
|
||||
CHECK(args.Length() >= 2 && args[0]->IsString());
|
||||
uint32_t flags;
|
||||
if (!args[1]->Uint32Value(env->context()).To(&flags)) return;
|
||||
|
||||
const node::Utf8Value engine_id(env->isolate(), args[0]);
|
||||
// If the engine name is not known, calling setAsDefault on the
|
||||
// empty engine pointer will be non-op that always returns false.
|
||||
args.GetReturnValue().Set(
|
||||
ncrypto::EnginePointer::getEngineByName(engine_id.ToStringView())
|
||||
.setAsDefault(flags));
|
||||
}
|
||||
#endif // !OPENSSL_NO_ENGINE
|
||||
|
||||
@ -655,8 +630,8 @@ MaybeLocal<Value> EncodeBignum(
|
||||
const BIGNUM* bn,
|
||||
int size,
|
||||
Local<Value>* error) {
|
||||
std::vector<uint8_t> buf(size);
|
||||
CHECK_EQ(BN_bn2binpad(bn, buf.data(), size), size);
|
||||
std::vector<uint8_t> buf = ncrypto::BignumPointer::encodePadded(bn, size);
|
||||
CHECK_EQ(buf.size(), static_cast<size_t>(size));
|
||||
return StringBytes::Encode(
|
||||
env->isolate(),
|
||||
reinterpret_cast<const char*>(buf.data()),
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "util.h"
|
||||
#include "v8.h"
|
||||
|
||||
#include "ncrypto.h"
|
||||
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/err.h>
|
||||
@ -20,9 +22,7 @@
|
||||
#include <openssl/kdf.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/ssl.h>
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
# include <openssl/engine.h>
|
||||
#endif // !OPENSSL_NO_ENGINE
|
||||
|
||||
// The FIPS-related functions are only available
|
||||
// when the OpenSSL itself was compiled with FIPS support.
|
||||
#if defined(OPENSSL_FIPS) && OPENSSL_VERSION_MAJOR < 3
|
||||
@ -54,39 +54,33 @@ constexpr size_t kSizeOf_EVP_PKEY_CTX = 80;
|
||||
constexpr size_t kSizeOf_HMAC_CTX = 32;
|
||||
|
||||
// Define smart pointers for the most commonly used OpenSSL types:
|
||||
using X509Pointer = DeleteFnPtr<X509, X509_free>;
|
||||
using BIOPointer = DeleteFnPtr<BIO, BIO_free_all>;
|
||||
using SSLCtxPointer = DeleteFnPtr<SSL_CTX, SSL_CTX_free>;
|
||||
using SSLSessionPointer = DeleteFnPtr<SSL_SESSION, SSL_SESSION_free>;
|
||||
using SSLPointer = DeleteFnPtr<SSL, SSL_free>;
|
||||
using PKCS8Pointer = DeleteFnPtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>;
|
||||
using EVPKeyPointer = DeleteFnPtr<EVP_PKEY, EVP_PKEY_free>;
|
||||
using EVPKeyCtxPointer = DeleteFnPtr<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
|
||||
using EVPMDCtxPointer = DeleteFnPtr<EVP_MD_CTX, EVP_MD_CTX_free>;
|
||||
using RSAPointer = DeleteFnPtr<RSA, RSA_free>;
|
||||
using ECPointer = DeleteFnPtr<EC_KEY, EC_KEY_free>;
|
||||
using BignumPointer = DeleteFnPtr<BIGNUM, BN_clear_free>;
|
||||
using BignumCtxPointer = DeleteFnPtr<BN_CTX, BN_CTX_free>;
|
||||
using NetscapeSPKIPointer = DeleteFnPtr<NETSCAPE_SPKI, NETSCAPE_SPKI_free>;
|
||||
using ECGroupPointer = DeleteFnPtr<EC_GROUP, EC_GROUP_free>;
|
||||
using ECPointPointer = DeleteFnPtr<EC_POINT, EC_POINT_free>;
|
||||
using ECKeyPointer = DeleteFnPtr<EC_KEY, EC_KEY_free>;
|
||||
using DHPointer = DeleteFnPtr<DH, DH_free>;
|
||||
using ECDSASigPointer = DeleteFnPtr<ECDSA_SIG, ECDSA_SIG_free>;
|
||||
using HMACCtxPointer = DeleteFnPtr<HMAC_CTX, HMAC_CTX_free>;
|
||||
using CipherCtxPointer = DeleteFnPtr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>;
|
||||
using RsaPointer = DeleteFnPtr<RSA, RSA_free>;
|
||||
using DsaPointer = DeleteFnPtr<DSA, DSA_free>;
|
||||
using DsaSigPointer = DeleteFnPtr<DSA_SIG, DSA_SIG_free>;
|
||||
using X509Pointer = ncrypto::X509Pointer;
|
||||
using BIOPointer = ncrypto::BIOPointer;
|
||||
using SSLCtxPointer = ncrypto::SSLCtxPointer;
|
||||
using SSLSessionPointer = ncrypto::SSLSessionPointer;
|
||||
using SSLPointer = ncrypto::SSLPointer;
|
||||
using PKCS8Pointer = ncrypto::PKCS8Pointer;
|
||||
using EVPKeyPointer = ncrypto::EVPKeyPointer;
|
||||
using EVPKeyCtxPointer = ncrypto::EVPKeyCtxPointer;
|
||||
using EVPMDCtxPointer = ncrypto::EVPMDCtxPointer;
|
||||
using RSAPointer = ncrypto::RSAPointer;
|
||||
using ECPointer = ncrypto::ECPointer;
|
||||
using BignumPointer = ncrypto::BignumPointer;
|
||||
using BignumCtxPointer = ncrypto::BignumCtxPointer;
|
||||
using NetscapeSPKIPointer = ncrypto::NetscapeSPKIPointer;
|
||||
using ECGroupPointer = ncrypto::ECGroupPointer;
|
||||
using ECPointPointer = ncrypto::ECPointPointer;
|
||||
using ECKeyPointer = ncrypto::ECKeyPointer;
|
||||
using DHPointer = ncrypto::DHPointer;
|
||||
using ECDSASigPointer = ncrypto::ECDSASigPointer;
|
||||
using HMACCtxPointer = ncrypto::HMACCtxPointer;
|
||||
using CipherCtxPointer = ncrypto::CipherCtxPointer;
|
||||
using RsaPointer = ncrypto::RSAPointer;
|
||||
using DsaPointer = ncrypto::DSAPointer;
|
||||
using DsaSigPointer = ncrypto::DSASigPointer;
|
||||
|
||||
// Our custom implementation of the certificate verify callback
|
||||
// used when establishing a TLS handshake. Because we cannot perform
|
||||
// I/O quickly enough with X509_STORE_CTX_ APIs in this callback,
|
||||
// we ignore preverify_ok errors here and let the handshake continue.
|
||||
// In other words, this VerifyCallback is a non-op. It is imperative
|
||||
// that the user user Connection::VerifyError after the `secure`
|
||||
// callback has been made.
|
||||
extern int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx);
|
||||
using ClearErrorOnReturn = ncrypto::ClearErrorOnReturn;
|
||||
using MarkPopErrorOnReturn = ncrypto::MarkPopErrorOnReturn;
|
||||
|
||||
bool ProcessFipsOptions();
|
||||
|
||||
@ -97,21 +91,6 @@ void InitCrypto(v8::Local<v8::Object> target);
|
||||
|
||||
extern void UseExtraCaCerts(const std::string& file);
|
||||
|
||||
// Forcibly clear OpenSSL's error stack on return. This stops stale errors
|
||||
// from popping up later in the lifecycle of crypto operations where they
|
||||
// would cause spurious failures. It's a rather blunt method, though.
|
||||
// ERR_clear_error() isn't necessarily cheap either.
|
||||
struct ClearErrorOnReturn {
|
||||
~ClearErrorOnReturn() { ERR_clear_error(); }
|
||||
};
|
||||
|
||||
// Pop errors from OpenSSL's error stack that were added
|
||||
// between when this was constructed and destructed.
|
||||
struct MarkPopErrorOnReturn {
|
||||
MarkPopErrorOnReturn() { ERR_set_mark(); }
|
||||
~MarkPopErrorOnReturn() { ERR_pop_to_mark(); }
|
||||
};
|
||||
|
||||
struct CSPRNGResult {
|
||||
const bool ok;
|
||||
MUST_USE_RESULT bool is_ok() const { return ok; }
|
||||
@ -165,6 +144,21 @@ enum class NodeCryptoError {
|
||||
#undef V
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
std::string getNodeCryptoErrorString(const NodeCryptoError error,
|
||||
Args&&... args) {
|
||||
const char* error_string = nullptr;
|
||||
switch (error) {
|
||||
#define V(CODE, DESCRIPTION) \
|
||||
case NodeCryptoError::CODE: \
|
||||
error_string = DESCRIPTION; \
|
||||
break;
|
||||
NODE_CRYPTO_ERROR_CODES_MAP(V)
|
||||
#undef V
|
||||
}
|
||||
return SPrintF(error_string, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Utility struct used to harvest error information from openssl's error stack
|
||||
struct CryptoErrorStore final : public MemoryRetainer {
|
||||
public:
|
||||
@ -200,6 +194,9 @@ void CryptoErrorStore::Insert(const NodeCryptoError error, Args&&... args) {
|
||||
std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
v8::MaybeLocal<v8::Value> cryptoErrorListToException(
|
||||
Environment* env, const ncrypto::CryptoErrorList& errors);
|
||||
|
||||
template <typename T>
|
||||
T* MallocOpenSSL(size_t count) {
|
||||
void* mem = OPENSSL_malloc(MultiplyWithOverflowCheck(count, sizeof(T)));
|
||||
@ -552,72 +549,6 @@ void ThrowCryptoError(Environment* env,
|
||||
unsigned long err, // NOLINT(runtime/int)
|
||||
const char* message = nullptr);
|
||||
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
struct EnginePointer {
|
||||
ENGINE* engine = nullptr;
|
||||
bool finish_on_exit = false;
|
||||
|
||||
inline EnginePointer() = default;
|
||||
|
||||
inline explicit EnginePointer(ENGINE* engine_, bool finish_on_exit_ = false)
|
||||
: engine(engine_),
|
||||
finish_on_exit(finish_on_exit_) {}
|
||||
|
||||
inline EnginePointer(EnginePointer&& other) noexcept
|
||||
: engine(other.engine),
|
||||
finish_on_exit(other.finish_on_exit) {
|
||||
other.release();
|
||||
}
|
||||
|
||||
inline ~EnginePointer() { reset(); }
|
||||
|
||||
inline EnginePointer& operator=(EnginePointer&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
this->~EnginePointer();
|
||||
return *new (this) EnginePointer(std::move(other));
|
||||
}
|
||||
|
||||
inline operator bool() const { return engine != nullptr; }
|
||||
|
||||
inline ENGINE* get() { return engine; }
|
||||
|
||||
inline void reset(ENGINE* engine_ = nullptr, bool finish_on_exit_ = false) {
|
||||
if (engine != nullptr) {
|
||||
if (finish_on_exit) {
|
||||
// This also does the equivalent of ENGINE_free.
|
||||
CHECK_EQ(ENGINE_finish(engine), 1);
|
||||
} else {
|
||||
CHECK_EQ(ENGINE_free(engine), 1);
|
||||
}
|
||||
}
|
||||
engine = engine_;
|
||||
finish_on_exit = finish_on_exit_;
|
||||
}
|
||||
|
||||
inline ENGINE* release() {
|
||||
ENGINE* ret = engine;
|
||||
engine = nullptr;
|
||||
finish_on_exit = false;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
EnginePointer LoadEngineById(const char* id, CryptoErrorStore* errors);
|
||||
|
||||
bool SetEngine(
|
||||
const char* id,
|
||||
uint32_t flags,
|
||||
CryptoErrorStore* errors = nullptr);
|
||||
|
||||
void SetEngine(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
#endif // !OPENSSL_NO_ENGINE
|
||||
|
||||
void GetFipsCrypto(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
void SetFipsCrypto(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
void TestFipsCrypto(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
class CipherPushContext {
|
||||
public:
|
||||
inline explicit CipherPushContext(Environment* env) : env_(env) {}
|
||||
|
@ -25,12 +25,13 @@
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
#include <openssl/crypto.h>
|
||||
#include "ncrypto.h"
|
||||
#if NODE_OPENSSL_HAS_QUIC
|
||||
#include <openssl/quic.h>
|
||||
#endif
|
||||
#endif // HAVE_OPENSSL
|
||||
|
||||
#ifdef OPENSSL_INFO_QUIC
|
||||
#ifdef NODE_OPENSSL_HAS_QUIC
|
||||
#include <ngtcp2/version.h>
|
||||
#include <nghttp3/version.h>
|
||||
#endif
|
||||
@ -118,6 +119,7 @@ Metadata::Versions::Versions() {
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
openssl = GetOpenSSLVersion();
|
||||
ncrypto = NCRYPTO_VERSION;
|
||||
#endif
|
||||
|
||||
#ifdef NODE_HAVE_I18N_SUPPORT
|
||||
@ -125,7 +127,7 @@ Metadata::Versions::Versions() {
|
||||
unicode = U_UNICODE_VERSION;
|
||||
#endif // NODE_HAVE_I18N_SUPPORT
|
||||
|
||||
#ifdef OPENSSL_INFO_QUIC
|
||||
#ifdef NODE_OPENSSL_HAS_QUIC
|
||||
ngtcp2 = NGTCP2_VERSION;
|
||||
nghttp3 = NGHTTP3_VERSION;
|
||||
#endif
|
||||
|
@ -55,7 +55,7 @@ namespace node {
|
||||
V(cjs_module_lexer)
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
#define NODE_VERSIONS_KEY_CRYPTO(V) V(openssl)
|
||||
#define NODE_VERSIONS_KEY_CRYPTO(V) V(openssl) V(ncrypto)
|
||||
#else
|
||||
#define NODE_VERSIONS_KEY_CRYPTO(V)
|
||||
#endif
|
||||
|
@ -1,3 +1,4 @@
|
||||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
@ -34,6 +34,7 @@ if (hasUndici) {
|
||||
|
||||
if (common.hasCrypto) {
|
||||
expected_keys.push('openssl');
|
||||
expected_keys.push('ncrypto');
|
||||
}
|
||||
|
||||
if (common.hasQuic) {
|
||||
@ -78,6 +79,7 @@ assert.match(process.versions.modules, /^\d+$/);
|
||||
assert.match(process.versions.cjs_module_lexer, commonTemplate);
|
||||
|
||||
if (common.hasCrypto) {
|
||||
assert.match(process.versions.ncrypto, commonTemplate);
|
||||
if (process.config.variables.node_shared_openssl) {
|
||||
assert.ok(process.versions.openssl);
|
||||
} else {
|
||||
|
@ -178,6 +178,7 @@ template("node_gn_build") {
|
||||
deps += [ "//third_party/icu" ]
|
||||
}
|
||||
if (node_use_openssl) {
|
||||
deps += [ "deps/ncrypto" ]
|
||||
public_deps += [ "deps/openssl" ]
|
||||
sources += gypi_values.node_crypto_sources
|
||||
}
|
||||
@ -339,6 +340,7 @@ template("node_gn_build") {
|
||||
|
||||
sources = gypi_values.node_cctest_sources
|
||||
if (node_use_openssl) {
|
||||
deps += [ "deps/ncrypto" ]
|
||||
sources += gypi_values.node_cctest_openssl_sources
|
||||
}
|
||||
if (node_enable_inspector) {
|
||||
|
Loading…
Reference in New Issue
Block a user