Skip to content

Commit 8e66a18

Browse files
jasnelltargos
authored andcommitted
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: #53803 Reviewed-By: Yagiz Nizipli <[email protected]>
1 parent 63cf715 commit 8e66a18

18 files changed

+849
-239
lines changed

‎Makefile‎

Lines changed: 2 additions & 1 deletion
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 common_node.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‎

Lines changed: 14 additions & 0 deletions
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‎

Lines changed: 5 additions & 0 deletions
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‎

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#include"ncrypto.h"
2+
3+
namespacencrypto{
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+
voidEnginePointer::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+
returnstd::move(engine);
62+
}
63+
64+
boolEnginePointer::setAsDefault(uint32_t flags, CryptoErrorList* errors){
65+
if (engine == nullptr) returnfalse;
66+
ClearErrorOnReturn clear_error_on_return(errors);
67+
returnENGINE_set_default(engine, flags) != 0;
68+
}
69+
70+
boolEnginePointer::init(bool finish_on_exit){
71+
if (engine == nullptr) returnfalse;
72+
if (finish_on_exit) setFinishOnExit();
73+
returnENGINE_init(engine) == 1;
74+
}
75+
76+
EVPKeyPointer EnginePointer::loadPrivateKey(const std::string_view key_name){
77+
if (engine == nullptr) returnEVPKeyPointer();
78+
returnEVPKeyPointer(ENGINE_load_private_key(engine, key_name.data(), nullptr, nullptr));
79+
}
80+
81+
voidEnginePointer::initEnginesOnce(){
82+
staticbool 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‎

Lines changed: 223 additions & 0 deletions
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+
namespacencrypto{
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+
intClearErrorOnReturn::peeKError(){returnERR_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+
intMarkPopErrorOnReturn::peekError(){returnERR_peek_error()}
34+
35+
CryptoErrorList::CryptoErrorList(CryptoErrorList::Option option){
36+
if (option == Option::CAPTURE_ON_CONSTRUCT) capture();
37+
}
38+
39+
voidCryptoErrorList::capture(){
40+
errors_.clear();
41+
while(constauto 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+
voidCryptoErrorList::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+
boolisFipsEnabled(){
68+
#if OPENSSL_VERSION_MAJOR >= 3
69+
returnEVP_default_properties_is_fips_enabled(nullptr) == 1;
70+
#else
71+
returnFIPS_mode() == 1;
72+
#endif
73+
}
74+
75+
boolsetFipsEnabled(bool enable, CryptoErrorList* errors){
76+
if (isFipsEnabled() == enable) returntrue;
77+
ClearErrorOnReturn clearErrorOnReturn(errors);
78+
#if OPENSSL_VERSION_MAJOR >= 3
79+
returnEVP_default_properties_enable_fips(nullptr, enable ? 1 : 0) == 1;
80+
#else
81+
returnFIPS_mode_set(enable ? 1 : 0) == 1;
82+
#endif
83+
}
84+
85+
booltestFipsEnabled(){
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+
constauto enabled = fips_provider == nullptr ? 0 :
92+
OSSL_PROVIDER_self_test(fips_provider) ? 1 : 0;
93+
#else
94+
#ifdef OPENSSL_FIPS
95+
constauto enabled = FIPS_selftest() ? 1 : 0;
96+
#else// OPENSSL_FIPS
97+
constauto 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+
voidBignumPointer::reset(BIGNUM* bn){
120+
bn_.reset(bn);
121+
}
122+
123+
BIGNUM* BignumPointer::release(){
124+
return bn_.release();
125+
}
126+
127+
size_tBignumPointer::byteLength(){
128+
if (bn_ == nullptr) return0;
129+
returnBN_num_bytes(bn_.get());
130+
}
131+
132+
std::vector<uint8_t> BignumPointer::encode(){
133+
returnencodePadded(bn_.get(), byteLength());
134+
}
135+
136+
std::vector<uint8_t> BignumPointer::encodePadded(size_t size){
137+
returnencodePadded(bn_.get(), size);
138+
}
139+
140+
std::vector<uint8_t> BignumPointer::encode(const BIGNUM* bn){
141+
returnencodePadded(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) returnfalse;
154+
if (bn_ != nullptr && other.bn_ == nullptr) returnfalse;
155+
if (bn_ == nullptr && other.bn_ == nullptr) returntrue;
156+
returnBN_cmp(bn_.get(), other.bn_.get()) == 0;
157+
}
158+
159+
bool BignumPointer::operator==(const BIGNUM* other) noexcept{
160+
if (bn_ == nullptr && other != nullptr) returnfalse;
161+
if (bn_ != nullptr && other == nullptr) returnfalse;
162+
if (bn_ == nullptr && other == nullptr) returntrue;
163+
returnBN_cmp(bn_.get(), other) == 0;
164+
}
165+
166+
// ============================================================================
167+
// Utility methods
168+
169+
boolCSPRNG(void* buffer, size_t length){
170+
auto buf = reinterpret_cast<unsignedchar*>(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+
returntrue;
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+
returntrue;
184+
#endif
185+
}
186+
#if OPENSSL_VERSION_MAJOR >= 3
187+
constauto 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+
constauto 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+
returnfalse;
197+
}
198+
}
199+
#endif
200+
} while (1 == RAND_poll());
201+
202+
returnfalse;
203+
}
204+
205+
intNoPasswordCallback(char* buf, int size, int rwflag, void* u){
206+
return0;
207+
}
208+
209+
intPasswordCallback(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<constchar*>(passphrase->data), len);
217+
return len;
218+
}
219+
220+
return -1;
221+
}
222+
223+
} // namespace ncrypto

‎deps/ncrypto/ncrypto.gyp‎

Lines changed: 27 additions & 0 deletions
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)