@@ -14,11 +14,13 @@ namespace node {
14
14
using v8::Context;
15
15
using v8::FunctionCallbackInfo;
16
16
using v8::FunctionTemplate;
17
+ using v8::Int32;
17
18
using v8::Isolate;
18
19
using v8::Just;
19
20
using v8::Local;
20
21
using v8::Maybe;
21
22
using v8::MaybeLocal;
23
+ using v8::Name;
22
24
using v8::Nothing;
23
25
using v8::Object;
24
26
using v8::Uint32;
@@ -34,22 +36,170 @@ void Hash::MemoryInfo(MemoryTracker* tracker) const {
34
36
tracker->TrackFieldWithSize (" md" , digest_ ? md_len_ : 0 );
35
37
}
36
38
39
+ #if OPENSSL_VERSION_MAJOR >= 3
40
+ void PushAliases (const char * name, void * data) {
41
+ static_cast <std::vector<std::string>*>(data)->push_back (name);
42
+ }
43
+
44
+ EVP_MD* GetCachedMDByID (Environment* env, size_t id) {
45
+ CHECK_LT (id, env->evp_md_cache .size ());
46
+ EVP_MD* result = env->evp_md_cache [id].get ();
47
+ CHECK_NOT_NULL (result);
48
+ return result;
49
+ }
50
+
51
+ struct MaybeCachedMD {
52
+ EVP_MD* explicit_md = nullptr ;
53
+ const EVP_MD* implicit_md = nullptr ;
54
+ int32_t cache_id = -1 ;
55
+ };
56
+
57
+ MaybeCachedMD FetchAndMaybeCacheMD (Environment* env, const char * search_name) {
58
+ const EVP_MD* implicit_md = EVP_get_digestbyname (search_name);
59
+ if (!implicit_md) return {nullptr , nullptr , -1 };
60
+
61
+ const char * real_name = EVP_MD_get0_name (implicit_md);
62
+ if (!real_name) return {nullptr , implicit_md, -1 };
63
+
64
+ auto it = env->alias_to_md_id_map .find (real_name);
65
+ if (it != env->alias_to_md_id_map .end ()) {
66
+ size_t id = it->second ;
67
+ return {GetCachedMDByID (env, id), implicit_md, static_cast <int32_t >(id)};
68
+ }
69
+
70
+ // EVP_*_fetch() does not support alias names, so we need to pass it the
71
+ // real/original algorithm name.
72
+ // We use EVP_*_fetch() as a filter here because it will only return an
73
+ // instance if the algorithm is supported by the public OpenSSL APIs (some
74
+ // algorithms are used internally by OpenSSL and are also passed to this
75
+ // callback).
76
+ EVP_MD* explicit_md = EVP_MD_fetch (nullptr , real_name, nullptr );
77
+ if (!explicit_md) return {nullptr , implicit_md, -1 };
78
+
79
+ // Cache the EVP_MD* fetched.
80
+ env->evp_md_cache .emplace_back (explicit_md);
81
+ size_t id = env->evp_md_cache .size () - 1 ;
82
+
83
+ // Add all the aliases to the map to speed up next lookup.
84
+ std::vector<std::string> aliases;
85
+ EVP_MD_names_do_all (explicit_md, PushAliases, &aliases);
86
+ for (const auto & alias : aliases) {
87
+ env->alias_to_md_id_map .emplace (alias, id);
88
+ }
89
+ env->alias_to_md_id_map .emplace (search_name, id);
90
+
91
+ return {explicit_md, implicit_md, static_cast <int32_t >(id)};
92
+ }
93
+
94
+ void SaveSupportedHashAlgorithmsAndCacheMD (const EVP_MD* md,
95
+ const char * from,
96
+ const char * to,
97
+ void * arg) {
98
+ if (!from) return ;
99
+ Environment* env = static_cast <Environment*>(arg);
100
+ auto result = FetchAndMaybeCacheMD (env, from);
101
+ if (result.explicit_md ) {
102
+ env->supported_hash_algorithms .push_back (from);
103
+ }
104
+ }
105
+
106
+ #else
107
+ void SaveSupportedHashAlgorithms (const EVP_MD* md,
108
+ const char * from,
109
+ const char * to,
110
+ void * arg) {
111
+ if (!from) return ;
112
+ Environment* env = static_cast <Environment*>(arg);
113
+ env->supported_hash_algorithms .push_back (from);
114
+ }
115
+ #endif // OPENSSL_VERSION_MAJOR >= 3
116
+
117
+ const std::vector<std::string>& GetSupportedHashAlgorithms (Environment* env) {
118
+ if (env->supported_hash_algorithms .empty ()) {
119
+ MarkPopErrorOnReturn mark_pop_error_on_return;
120
+ #if OPENSSL_VERSION_MAJOR >= 3
121
+ // Since we'll fetch the EVP_MD*, cache them along the way to speed up
122
+ // later lookups instead of throwing them away immediately.
123
+ EVP_MD_do_all_sorted (SaveSupportedHashAlgorithmsAndCacheMD, env);
124
+ #else
125
+ EVP_MD_do_all_sorted (SaveSupportedHashAlgorithms, env);
126
+ #endif
127
+ }
128
+ return env->supported_hash_algorithms ;
129
+ }
130
+
37
131
void Hash::GetHashes (const FunctionCallbackInfo<Value>& args) {
38
- Environment* env = Environment::GetCurrent (args);
39
- MarkPopErrorOnReturn mark_pop_error_on_return;
40
- CipherPushContext ctx (env);
41
- EVP_MD_do_all_sorted (
132
+ Local<Context> context = args.GetIsolate ()->GetCurrentContext ();
133
+ Environment* env = Environment::GetCurrent (context);
134
+ const std::vector<std::string>& results = GetSupportedHashAlgorithms (env);
135
+
136
+ Local<Value> ret;
137
+ if (ToV8Value (context, results).ToLocal (&ret)) {
138
+ args.GetReturnValue ().Set (ret);
139
+ }
140
+ }
141
+
142
+ void Hash::GetCachedAliases (const FunctionCallbackInfo<Value>& args) {
143
+ Isolate* isolate = args.GetIsolate ();
144
+ Local<Context> context = args.GetIsolate ()->GetCurrentContext ();
145
+ Environment* env = Environment::GetCurrent (context);
146
+ std::vector<Local<Name>> names;
147
+ std::vector<Local<Value>> values;
148
+ size_t size = env->alias_to_md_id_map .size ();
42
149
#if OPENSSL_VERSION_MAJOR >= 3
43
- array_push_back<EVP_MD,
44
- EVP_MD_fetch,
45
- EVP_MD_free,
46
- EVP_get_digestbyname,
47
- EVP_MD_get0_name>,
150
+ names.reserve (size);
151
+ values.reserve (size);
152
+ for (auto & [alias, id] : env->alias_to_md_id_map ) {
153
+ names.push_back (OneByteString (isolate, alias.c_str (), alias.size ()));
154
+ values.push_back (v8::Uint32::New (isolate, id));
155
+ }
48
156
#else
49
- array_push_back<EVP_MD>,
157
+ CHECK (env->alias_to_md_id_map .empty ());
158
+ #endif
159
+ Local<Value> prototype = v8::Null (isolate);
160
+ Local<Object> result =
161
+ Object::New (isolate, prototype, names.data (), values.data (), size);
162
+ args.GetReturnValue ().Set (result);
163
+ }
164
+
165
+ const EVP_MD* GetDigestImplementation (Environment* env,
166
+ Local<Value> algorithm,
167
+ Local<Value> cache_id_val,
168
+ Local<Value> algorithm_cache) {
169
+ CHECK (algorithm->IsString ());
170
+ CHECK (cache_id_val->IsInt32 ());
171
+ CHECK (algorithm_cache->IsObject ());
172
+
173
+ #if OPENSSL_VERSION_MAJOR >= 3
174
+ int32_t cache_id = cache_id_val.As <Int32>()->Value ();
175
+ if (cache_id != -1 ) { // Alias already cached, return the cached EVP_MD*.
176
+ return GetCachedMDByID (env, cache_id);
177
+ }
178
+
179
+ // Only decode the algorithm when we don't have it cached to avoid
180
+ // unnecessary overhead.
181
+ Isolate* isolate = env->isolate ();
182
+ Utf8Value utf8 (isolate, algorithm);
183
+
184
+ auto result = FetchAndMaybeCacheMD (env, *utf8);
185
+ if (result.cache_id != -1 ) {
186
+ // Add the alias to both C++ side and JS side to speedup the lookup
187
+ // next time.
188
+ env->alias_to_md_id_map .emplace (*utf8, result.cache_id );
189
+ if (algorithm_cache.As <Object>()
190
+ ->Set (isolate->GetCurrentContext (),
191
+ algorithm,
192
+ v8::Int32::New (isolate, result.cache_id ))
193
+ .IsNothing ()) {
194
+ return nullptr ;
195
+ }
196
+ }
197
+
198
+ return result.explicit_md ? result.explicit_md : result.implicit_md ;
199
+ #else
200
+ Utf8Value utf8 (env->isolate (), algorithm);
201
+ return EVP_get_digestbyname (*utf8);
50
202
#endif
51
- &ctx);
52
- args.GetReturnValue ().Set (ctx.ToJSArray ());
53
203
}
54
204
55
205
void Hash::Initialize (Environment* env, Local<Object> target) {
@@ -65,6 +215,7 @@ void Hash::Initialize(Environment* env, Local<Object> target) {
65
215
SetConstructorFunction (context, target, " Hash" , t);
66
216
67
217
SetMethodNoSideEffect (context, target, " getHashes" , GetHashes);
218
+ SetMethodNoSideEffect (context, target, " getCachedAliases" , GetCachedAliases);
68
219
69
220
HashJob::Initialize (env, target);
70
221
@@ -77,24 +228,24 @@ void Hash::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
77
228
registry->Register (HashUpdate);
78
229
registry->Register (HashDigest);
79
230
registry->Register (GetHashes);
231
+ registry->Register (GetCachedAliases);
80
232
81
233
HashJob::RegisterExternalReferences (registry);
82
234
83
235
registry->Register (InternalVerifyIntegrity);
84
236
}
85
237
238
+ // new Hash(algorithm, algorithmId, xofLen, algorithmCache)
86
239
void Hash::New (const FunctionCallbackInfo<Value>& args) {
87
240
Environment* env = Environment::GetCurrent (args);
88
241
89
242
const Hash* orig = nullptr ;
90
243
const EVP_MD* md = nullptr ;
91
-
92
244
if (args[0 ]->IsObject ()) {
93
245
ASSIGN_OR_RETURN_UNWRAP (&orig, args[0 ].As <Object>());
94
246
md = EVP_MD_CTX_md (orig->mdctx_ .get ());
95
247
} else {
96
- const Utf8Value hash_type (env->isolate (), args[0 ]);
97
- md = EVP_get_digestbyname (*hash_type);
248
+ md = GetDigestImplementation (env, args[0 ], args[2 ], args[3 ]);
98
249
}
99
250
100
251
Maybe<unsigned int > xof_md_len = Nothing<unsigned int >();
@@ -284,7 +435,7 @@ bool HashTraits::DeriveBits(
284
435
Environment* env,
285
436
const HashConfig& params,
286
437
ByteSource* out) {
287
- EVPMDPointer ctx (EVP_MD_CTX_new ());
438
+ EVPMDCtxPointer ctx (EVP_MD_CTX_new ());
288
439
289
440
if (UNLIKELY (!ctx ||
290
441
EVP_DigestInit_ex (ctx.get (), params.digest , nullptr ) <= 0 ||
@@ -357,6 +508,5 @@ void InternalVerifyIntegrity(const v8::FunctionCallbackInfo<v8::Value>& args) {
357
508
args.GetReturnValue ().Set (rc.FromMaybe (Local<Value>()));
358
509
}
359
510
}
360
-
361
511
} // namespace crypto
362
512
} // namespace node
0 commit comments