-
Notifications
You must be signed in to change notification settings - Fork 30k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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: #53803 Reviewed-By: Yagiz Nizipli <[email protected]>
- Loading branch information
Showing
18 changed files
with
849 additions
and
239 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
|
||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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' | ||
] | ||
}], | ||
] | ||
}, | ||
] | ||
} |
Oops, something went wrong.