Skip to content

Commit 17e82d8

Browse files
committed
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.
1 parent 9791836 commit 17e82d8

18 files changed

+849
-239
lines changed

Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@ with-code-cache test-code-cache:
175175
out/Makefile: config.gypi common.gypi node.gyp \
176176
deps/uv/uv.gyp deps/llhttp/llhttp.gyp deps/zlib/zlib.gyp \
177177
deps/simdutf/simdutf.gyp deps/ada/ada.gyp deps/nbytes/nbytes.gyp \
178-
tools/v8_gypfiles/toolchain.gypi tools/v8_gypfiles/features.gypi \
178+
tools/v8_gypfiles/toolchain.gypi \
179+
tools/v8_gypfiles/features.gypi \
179180
tools/v8_gypfiles/inspector.gypi tools/v8_gypfiles/v8.gyp
180181
$(PYTHON) tools/gyp_node.py -f make
181182

deps/ncrypto/BUILD.gn

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
##############################################################################
2+
# #
3+
# DO NOT EDIT THIS FILE! #
4+
# #
5+
##############################################################################
6+
7+
# This file is used by GN for building, which is NOT the build system used for
8+
# building official binaries.
9+
# Please modify the gyp files if you are making changes to build system.
10+
11+
import("unofficial.gni")
12+
13+
ncrypto_gn_build("ncrypto") {
14+
}

deps/ncrypto/README.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Node.js crypto (ncrypto) library
2+
3+
The `ncrypto` library extracts the base internal implementation of Node.js crypto operations
4+
that support both `node:crypto` and Web Crypto implementations and makes them available for
5+
use in other projects that need to emulate Node.js' behavior.

deps/ncrypto/engine.cc

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#include "ncrypto.h"
2+
3+
namespace ncrypto {
4+
5+
// ============================================================================
6+
// Engine
7+
8+
#ifndef OPENSSL_NO_ENGINE
9+
EnginePointer::EnginePointer(ENGINE* engine_, bool finish_on_exit_)
10+
: engine(engine_),
11+
finish_on_exit(finish_on_exit_) {}
12+
13+
EnginePointer::EnginePointer(EnginePointer&& other) noexcept
14+
: engine(other.engine),
15+
finish_on_exit(other.finish_on_exit) {
16+
other.release();
17+
}
18+
19+
EnginePointer::~EnginePointer() { reset(); }
20+
21+
EnginePointer& EnginePointer::operator=(EnginePointer&& other) noexcept {
22+
if (this == &other) return *this;
23+
this->~EnginePointer();
24+
return *new (this) EnginePointer(std::move(other));
25+
}
26+
27+
void EnginePointer::reset(ENGINE* engine_, bool finish_on_exit_) {
28+
if (engine != nullptr) {
29+
if (finish_on_exit) {
30+
// This also does the equivalent of ENGINE_free.
31+
ENGINE_finish(engine);
32+
} else {
33+
ENGINE_free(engine);
34+
}
35+
}
36+
engine = engine_;
37+
finish_on_exit = finish_on_exit_;
38+
}
39+
40+
ENGINE* EnginePointer::release() {
41+
ENGINE* ret = engine;
42+
engine = nullptr;
43+
finish_on_exit = false;
44+
return ret;
45+
}
46+
47+
EnginePointer EnginePointer::getEngineByName(const std::string_view name,
48+
CryptoErrorList* errors) {
49+
MarkPopErrorOnReturn mark_pop_error_on_return(errors);
50+
EnginePointer engine(ENGINE_by_id(name.data()));
51+
if (!engine) {
52+
// Engine not found, try loading dynamically.
53+
engine = EnginePointer(ENGINE_by_id("dynamic"));
54+
if (engine) {
55+
if (!ENGINE_ctrl_cmd_string(engine.get(), "SO_PATH", name.data(), 0) ||
56+
!ENGINE_ctrl_cmd_string(engine.get(), "LOAD", nullptr, 0)) {
57+
engine.reset();
58+
}
59+
}
60+
}
61+
return std::move(engine);
62+
}
63+
64+
bool EnginePointer::setAsDefault(uint32_t flags, CryptoErrorList* errors) {
65+
if (engine == nullptr) return false;
66+
ClearErrorOnReturn clear_error_on_return(errors);
67+
return ENGINE_set_default(engine, flags) != 0;
68+
}
69+
70+
bool EnginePointer::init(bool finish_on_exit) {
71+
if (engine == nullptr) return false;
72+
if (finish_on_exit) setFinishOnExit();
73+
return ENGINE_init(engine) == 1;
74+
}
75+
76+
EVPKeyPointer EnginePointer::loadPrivateKey(const std::string_view key_name) {
77+
if (engine == nullptr) return EVPKeyPointer();
78+
return EVPKeyPointer(ENGINE_load_private_key(engine, key_name.data(), nullptr, nullptr));
79+
}
80+
81+
void EnginePointer::initEnginesOnce() {
82+
static bool initialized = false;
83+
if (!initialized) {
84+
ENGINE_load_builtin_engines();
85+
ENGINE_register_all_complete();
86+
initialized = true;
87+
}
88+
}
89+
90+
#endif // OPENSSL_NO_ENGINE
91+
92+
} // namespace ncrypto

deps/ncrypto/ncrypto.cc

+223
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
#include "ncrypto.h"
2+
#include <algorithm>
3+
#include <cstring>
4+
#include "openssl/bn.h"
5+
#if OPENSSL_VERSION_MAJOR >= 3
6+
#include "openssl/provider.h"
7+
#endif
8+
9+
namespace ncrypto {
10+
11+
// ============================================================================
12+
13+
ClearErrorOnReturn::ClearErrorOnReturn(CryptoErrorList* errors) : errors_(errors) {
14+
ERR_clear_error();
15+
}
16+
17+
ClearErrorOnReturn::~ClearErrorOnReturn() {
18+
if (errors_ != nullptr) errors_->capture();
19+
ERR_clear_error();
20+
}
21+
22+
int ClearErrorOnReturn::peeKError() { return ERR_peek_error(); }
23+
24+
MarkPopErrorOnReturn::MarkPopErrorOnReturn(CryptoErrorList* errors) : errors_(errors) {
25+
ERR_set_mark();
26+
}
27+
28+
MarkPopErrorOnReturn::~MarkPopErrorOnReturn() {
29+
if (errors_ != nullptr) errors_->capture();
30+
ERR_pop_to_mark();
31+
}
32+
33+
int MarkPopErrorOnReturn::peekError() { return ERR_peek_error(); }
34+
35+
CryptoErrorList::CryptoErrorList(CryptoErrorList::Option option) {
36+
if (option == Option::CAPTURE_ON_CONSTRUCT) capture();
37+
}
38+
39+
void CryptoErrorList::capture() {
40+
errors_.clear();
41+
while(const auto err = ERR_get_error()) {
42+
char buf[256];
43+
ERR_error_string_n(err, buf, sizeof(buf));
44+
errors_.emplace_front(buf);
45+
}
46+
}
47+
48+
void CryptoErrorList::add(std::string error) {
49+
errors_.push_back(error);
50+
}
51+
52+
std::optional<std::string> CryptoErrorList::pop_back() {
53+
if (errors_.empty()) return std::nullopt;
54+
std::string error = errors_.back();
55+
errors_.pop_back();
56+
return error;
57+
}
58+
59+
std::optional<std::string> CryptoErrorList::pop_front() {
60+
if (errors_.empty()) return std::nullopt;
61+
std::string error = errors_.front();
62+
errors_.pop_front();
63+
return error;
64+
}
65+
66+
// ============================================================================
67+
bool isFipsEnabled() {
68+
#if OPENSSL_VERSION_MAJOR >= 3
69+
return EVP_default_properties_is_fips_enabled(nullptr) == 1;
70+
#else
71+
return FIPS_mode() == 1;
72+
#endif
73+
}
74+
75+
bool setFipsEnabled(bool enable, CryptoErrorList* errors) {
76+
if (isFipsEnabled() == enable) return true;
77+
ClearErrorOnReturn clearErrorOnReturn(errors);
78+
#if OPENSSL_VERSION_MAJOR >= 3
79+
return EVP_default_properties_enable_fips(nullptr, enable ? 1 : 0) == 1;
80+
#else
81+
return FIPS_mode_set(enable ? 1 : 0) == 1;
82+
#endif
83+
}
84+
85+
bool testFipsEnabled() {
86+
#if OPENSSL_VERSION_MAJOR >= 3
87+
OSSL_PROVIDER* fips_provider = nullptr;
88+
if (OSSL_PROVIDER_available(nullptr, "fips")) {
89+
fips_provider = OSSL_PROVIDER_load(nullptr, "fips");
90+
}
91+
const auto enabled = fips_provider == nullptr ? 0 :
92+
OSSL_PROVIDER_self_test(fips_provider) ? 1 : 0;
93+
#else
94+
#ifdef OPENSSL_FIPS
95+
const auto enabled = FIPS_selftest() ? 1 : 0;
96+
#else // OPENSSL_FIPS
97+
const auto enabled = 0;
98+
#endif // OPENSSL_FIPS
99+
#endif
100+
101+
return enabled;
102+
}
103+
104+
// ============================================================================
105+
// Bignum
106+
BignumPointer::BignumPointer(BIGNUM* bignum) : bn_(bignum) {}
107+
108+
BignumPointer::BignumPointer(BignumPointer&& other) noexcept
109+
: bn_(other.release()) {}
110+
111+
BignumPointer& BignumPointer::operator=(BignumPointer&& other) noexcept {
112+
if (this == &other) return *this;
113+
this->~BignumPointer();
114+
return *new (this) BignumPointer(std::move(other));
115+
}
116+
117+
BignumPointer::~BignumPointer() { reset(); }
118+
119+
void BignumPointer::reset(BIGNUM* bn) {
120+
bn_.reset(bn);
121+
}
122+
123+
BIGNUM* BignumPointer::release() {
124+
return bn_.release();
125+
}
126+
127+
size_t BignumPointer::byteLength() {
128+
if (bn_ == nullptr) return 0;
129+
return BN_num_bytes(bn_.get());
130+
}
131+
132+
std::vector<uint8_t> BignumPointer::encode() {
133+
return encodePadded(bn_.get(), byteLength());
134+
}
135+
136+
std::vector<uint8_t> BignumPointer::encodePadded(size_t size) {
137+
return encodePadded(bn_.get(), size);
138+
}
139+
140+
std::vector<uint8_t> BignumPointer::encode(const BIGNUM* bn) {
141+
return encodePadded(bn, bn != nullptr ? BN_num_bytes(bn) : 0);
142+
}
143+
144+
std::vector<uint8_t> BignumPointer::encodePadded(const BIGNUM* bn, size_t s) {
145+
if (bn == nullptr) return std::vector<uint8_t>(0);
146+
size_t size = std::max(s, static_cast<size_t>(BN_num_bytes(bn)));
147+
std::vector<uint8_t> buf(size);
148+
BN_bn2binpad(bn, buf.data(), size);
149+
return buf;
150+
}
151+
152+
bool BignumPointer::operator==(const BignumPointer& other) noexcept {
153+
if (bn_ == nullptr && other.bn_ != nullptr) return false;
154+
if (bn_ != nullptr && other.bn_ == nullptr) return false;
155+
if (bn_ == nullptr && other.bn_ == nullptr) return true;
156+
return BN_cmp(bn_.get(), other.bn_.get()) == 0;
157+
}
158+
159+
bool BignumPointer::operator==(const BIGNUM* other) noexcept {
160+
if (bn_ == nullptr && other != nullptr) return false;
161+
if (bn_ != nullptr && other == nullptr) return false;
162+
if (bn_ == nullptr && other == nullptr) return true;
163+
return BN_cmp(bn_.get(), other) == 0;
164+
}
165+
166+
// ============================================================================
167+
// Utility methods
168+
169+
bool CSPRNG(void* buffer, size_t length) {
170+
auto buf = reinterpret_cast<unsigned char*>(buffer);
171+
do {
172+
if (1 == RAND_status()) {
173+
#if OPENSSL_VERSION_MAJOR >= 3
174+
if (1 == RAND_bytes_ex(nullptr, buf, length, 0)) {
175+
return true;
176+
}
177+
#else
178+
while (length > INT_MAX && 1 == RAND_bytes(buf, INT_MAX)) {
179+
buf += INT_MAX;
180+
length -= INT_MAX;
181+
}
182+
if (length <= INT_MAX && 1 == RAND_bytes(buf, static_cast<int>(length)))
183+
return true;
184+
#endif
185+
}
186+
#if OPENSSL_VERSION_MAJOR >= 3
187+
const auto code = ERR_peek_last_error();
188+
// A misconfigured OpenSSL 3 installation may report 1 from RAND_poll()
189+
// and RAND_status() but fail in RAND_bytes() if it cannot look up
190+
// a matching algorithm for the CSPRNG.
191+
if (ERR_GET_LIB(code) == ERR_LIB_RAND) {
192+
const auto reason = ERR_GET_REASON(code);
193+
if (reason == RAND_R_ERROR_INSTANTIATING_DRBG ||
194+
reason == RAND_R_UNABLE_TO_FETCH_DRBG ||
195+
reason == RAND_R_UNABLE_TO_CREATE_DRBG) {
196+
return false;
197+
}
198+
}
199+
#endif
200+
} while (1 == RAND_poll());
201+
202+
return false;
203+
}
204+
205+
int NoPasswordCallback(char* buf, int size, int rwflag, void* u) {
206+
return 0;
207+
}
208+
209+
int PasswordCallback(char* buf, int size, int rwflag, void* u) {
210+
const Buffer* passphrase = static_cast<const Buffer*>(u);
211+
if (passphrase != nullptr) {
212+
size_t buflen = static_cast<size_t>(size);
213+
size_t len = passphrase->len;
214+
if (buflen < len)
215+
return -1;
216+
memcpy(buf, reinterpret_cast<const char*>(passphrase->data), len);
217+
return len;
218+
}
219+
220+
return -1;
221+
}
222+
223+
} // namespace ncrypto

deps/ncrypto/ncrypto.gyp

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
'variables': {
3+
'ncrypto_sources': [
4+
'engine.cc',
5+
'ncrypto.cc',
6+
'ncrypto.h',
7+
],
8+
},
9+
'targets': [
10+
{
11+
'target_name': 'ncrypto',
12+
'type': 'static_library',
13+
'include_dirs': ['.'],
14+
'direct_dependent_settings': {
15+
'include_dirs': ['.'],
16+
},
17+
'sources': [ '<@(ncrypto_sources)' ],
18+
'conditions': [
19+
['node_shared_openssl=="false"', {
20+
'dependencies': [
21+
'../openssl/openssl.gyp:openssl'
22+
]
23+
}],
24+
]
25+
},
26+
]
27+
}

0 commit comments

Comments
 (0)