@@ -14,13 +14,15 @@ namespace node {
1414using v8::Context;
1515using v8::FunctionCallbackInfo;
1616using v8::FunctionTemplate;
17+ using v8::Int32;
1718using v8::Isolate;
1819using v8::Just;
1920using v8::Local;
2021using v8::Maybe;
2122using v8::MaybeLocal;
2223using v8::Nothing;
2324using v8::Object;
25+ using v8::String;
2426using v8::Uint32;
2527using v8::Value;
2628
@@ -34,44 +36,80 @@ void Hash::MemoryInfo(MemoryTracker* tracker) const {
3436 tracker->TrackFieldWithSize (" md" , digest_ ? md_len_ : 0 );
3537}
3638
37- 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 (
39+ void CacheSupportedHashAlgorithms (const EVP_MD* md,
40+ const char * from,
41+ const char * to,
42+ void * arg) {
43+ if (!from) return ;
44+
45+ #if OPENSSL_VERSION_MAJOR >= 3
46+ const EVP_MD* implicit_md = EVP_get_digestbyname (from);
47+ if (!implicit_md) return ;
48+ const char * real_name = EVP_MD_get0_name (implicit_md);
49+ if (!real_name) return ;
50+ // EVP_*_fetch() does not support alias names, so we need to pass it the
51+ // real/original algorithm name.
52+ // We use EVP_*_fetch() as a filter here because it will only return an
53+ // instance if the algorithm is supported by the public OpenSSL APIs (some
54+ // algorithms are used internally by OpenSSL and are also passed to this
55+ // callback).
56+ EVP_MD* explicit_md = EVP_MD_fetch (nullptr , real_name, nullptr );
57+ if (!explicit_md) return ;
58+ #endif // OPENSSL_VERSION_MAJOR >= 3
59+
60+ Environment* env = static_cast <Environment*>(arg);
61+ env->supported_hash_algorithms .push_back (from);
62+
4263#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>,
48- #else
49- array_push_back<EVP_MD>,
50- #endif
51- &ctx);
52- args.GetReturnValue ().Set (ctx.ToJSArray ());
64+ env->evp_md_cache .emplace_back (explicit_md);
65+ #endif // OPENSSL_VERSION_MAJOR >= 3
5366}
5467
55- const EVP_MD* GetDigestImplementation (Environment* env,
56- const Utf8Value& hash_type) {
68+ const std::vector<std::string>& GetSupportedHashAlgorithms (Environment* env) {
69+ if (env->supported_hash_algorithms .empty ()) {
70+ MarkPopErrorOnReturn mark_pop_error_on_return;
71+ std::vector<std::string> results;
72+ EVP_MD_do_all_sorted (CacheSupportedHashAlgorithms, env);
5773#if OPENSSL_VERSION_MAJOR >= 3
58- std::string hash_type_str = hash_type.ToString ();
59- auto it = env->evp_md_cache .find (hash_type_str);
60- if (it == env->evp_md_cache .end ()) {
61- EVP_MD* explicit_md = EVP_MD_fetch (nullptr , hash_type_str.c_str (), nullptr );
62- if (explicit_md != nullptr ) {
63- env->evp_md_cache .emplace (hash_type_str, explicit_md);
64- return explicit_md;
65- } else {
66- // We'll do a fallback.
67- ERR_clear_error ();
68- }
69- } else {
70- return it->second .get ();
71- }
74+ CHECK_EQ (env->supported_hash_algorithms .size (), env->evp_md_cache .size ());
75+ CHECK_GE (env->supported_hash_algorithms .size (), 0 );
7276#endif // OPENSSL_VERSION_MAJOR >= 3
73- // EVP_MD_fetch failed, fallback to EVP_get_digestbyname.
74- return EVP_get_digestbyname (*hash_type);
77+ }
78+ return env->supported_hash_algorithms ;
79+ }
80+
81+ void Hash::GetHashes (const FunctionCallbackInfo<Value>& args) {
82+ Local<Context> context = args.GetIsolate ()->GetCurrentContext ();
83+ Environment* env = Environment::GetCurrent (context);
84+ const std::vector<std::string>& results = GetSupportedHashAlgorithms (env);
85+
86+ Local<Value> ret;
87+ if (ToV8Value (context, results).ToLocal (&ret)) {
88+ args.GetReturnValue ().Set (ret);
89+ }
90+ }
91+
92+ const EVP_MD* GetDigestImplementation (Environment* env,
93+ Local<Value> algorithm,
94+ Local<Value> algorithm_id) {
95+ CHECK (algorithm->IsString ());
96+ CHECK (algorithm_id->IsInt32 ());
97+ int32_t id = algorithm_id.As <Int32>()->Value ();
98+
99+ const std::vector<std::string>& algorithms = GetSupportedHashAlgorithms (env);
100+ if (id != -1 ) {
101+ CHECK_LT (static_cast <size_t >(id), algorithms.size ());
102+ auto & ptr = env->evp_md_cache [id];
103+ CHECK_NOT_NULL (ptr.get ());
104+ return ptr.get ();
105+ }
106+
107+ // It could be unsupported algorithms.
108+ std::string algorithm_str;
109+ Utf8Value utf8 (env->isolate (), algorithm);
110+ algorithm_str = utf8.ToString ();
111+ const EVP_MD* implicit_md = EVP_get_digestbyname (algorithm_str.c_str ());
112+ return implicit_md;
75113}
76114
77115void Hash::Initialize (Environment* env, Local<Object> target) {
@@ -110,19 +148,17 @@ void Hash::New(const FunctionCallbackInfo<Value>& args) {
110148
111149 const Hash* orig = nullptr ;
112150 const EVP_MD* md = nullptr ;
113-
114151 if (args[0 ]->IsObject ()) {
115152 ASSIGN_OR_RETURN_UNWRAP (&orig, args[0 ].As <Object>());
116153 md = EVP_MD_CTX_md (orig->mdctx_ .get ());
117154 } else {
118- const Utf8Value hash_type (env->isolate (), args[0 ]);
119- md = GetDigestImplementation (env, hash_type);
155+ md = GetDigestImplementation (env, args[0 ], args[1 ]);
120156 }
121157
122158 Maybe<unsigned int > xof_md_len = Nothing<unsigned int >();
123- if (!args[1 ]->IsUndefined ()) {
124- CHECK (args[1 ]->IsUint32 ());
125- xof_md_len = Just<unsigned int >(args[1 ].As <Uint32>()->Value ());
159+ if (!args[2 ]->IsUndefined ()) {
160+ CHECK (args[2 ]->IsUint32 ());
161+ xof_md_len = Just<unsigned int >(args[2 ].As <Uint32>()->Value ());
126162 }
127163
128164 Hash* hash = new Hash (env, args.This ());
0 commit comments