@@ -23,6 +23,7 @@ using v8::MaybeLocal;
2323using v8::Name;
2424using v8::Nothing;
2525using v8::Object;
26+ using v8::String;
2627using v8::Uint32;
2728using v8::Value;
2829
@@ -202,6 +203,71 @@ const EVP_MD* GetDigestImplementation(Environment* env,
202203#endif
203204}
204205
206+ // crypto.digest(algorithm, algorithmId, algorithmCache,
207+ // input, outputEncoding, outputEncodingId)
208+ void Hash::OneShotDigest (const FunctionCallbackInfo<Value>& args) {
209+ Environment* env = Environment::GetCurrent (args);
210+ Isolate* isolate = env->isolate ();
211+ CHECK_EQ (args.Length (), 6 );
212+ CHECK (args[0 ]->IsString ()); // algorithm
213+ CHECK (args[1 ]->IsInt32 ()); // algorithmId
214+ CHECK (args[2 ]->IsObject ()); // algorithmCache
215+ CHECK (args[3 ]->IsString () || args[3 ]->IsArrayBufferView ()); // input
216+ CHECK (args[4 ]->IsString ()); // outputEncoding
217+ CHECK (args[5 ]->IsUint32 () || args[5 ]->IsUndefined ()); // outputEncodingId
218+
219+ const EVP_MD* md = GetDigestImplementation (env, args[0 ], args[1 ], args[2 ]);
220+ if (md == nullptr ) {
221+ Utf8Value method (isolate, args[0 ]);
222+ std::string message =
223+ " Digest method " + method.ToString () + " is not supported" ;
224+ return ThrowCryptoError (env, ERR_get_error (), message.c_str ());
225+ }
226+
227+ enum encoding output_enc = ParseEncoding (isolate, args[4 ], args[5 ], HEX);
228+
229+ int md_len = EVP_MD_size (md);
230+ unsigned int result_size;
231+ ByteSource::Builder output (md_len);
232+ int success;
233+ // On smaller inputs, EVP_Digest() can be slower than the
234+ // deprecated helpers e.g SHA256_XXX. The speedup may not
235+ // be worth using deprecated APIs, however, so we use
236+ // EVP_Digest(), unless there's a better alternative
237+ // in the future.
238+ // https://github.com/openssl/openssl/issues/19612
239+ if (args[3 ]->IsString ()) {
240+ Utf8Value utf8 (isolate, args[3 ]);
241+ success = EVP_Digest (utf8.out (),
242+ utf8.length (),
243+ output.data <unsigned char >(),
244+ &result_size,
245+ md,
246+ nullptr );
247+ } else {
248+ ArrayBufferViewContents<unsigned char > input (args[3 ]);
249+ success = EVP_Digest (input.data (),
250+ input.length (),
251+ output.data <unsigned char >(),
252+ &result_size,
253+ md,
254+ nullptr );
255+ }
256+ if (!success) {
257+ return ThrowCryptoError (env, ERR_get_error ());
258+ }
259+
260+ Local<Value> error;
261+ MaybeLocal<Value> rc = StringBytes::Encode (
262+ env->isolate (), output.data <char >(), md_len, output_enc, &error);
263+ if (rc.IsEmpty ()) {
264+ CHECK (!error.IsEmpty ());
265+ env->isolate ()->ThrowException (error);
266+ return ;
267+ }
268+ args.GetReturnValue ().Set (rc.FromMaybe (Local<Value>()));
269+ }
270+
205271void Hash::Initialize (Environment* env, Local<Object> target) {
206272 Isolate* isolate = env->isolate ();
207273 Local<Context> context = env->context ();
@@ -216,6 +282,7 @@ void Hash::Initialize(Environment* env, Local<Object> target) {
216282
217283 SetMethodNoSideEffect (context, target, " getHashes" , GetHashes);
218284 SetMethodNoSideEffect (context, target, " getCachedAliases" , GetCachedAliases);
285+ SetMethodNoSideEffect (context, target, " oneShotDigest" , OneShotDigest);
219286
220287 HashJob::Initialize (env, target);
221288
@@ -229,6 +296,7 @@ void Hash::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
229296 registry->Register (HashDigest);
230297 registry->Register (GetHashes);
231298 registry->Register (GetCachedAliases);
299+ registry->Register (OneShotDigest);
232300
233301 HashJob::RegisterExternalReferences (registry);
234302
@@ -294,14 +362,17 @@ bool Hash::HashUpdate(const char* data, size_t len) {
294362}
295363
296364void Hash::HashUpdate (const FunctionCallbackInfo<Value>& args) {
297- Decode<Hash>(args, [](Hash* hash, const FunctionCallbackInfo<Value>& args,
298- const char * data, size_t size) {
299- Environment* env = Environment::GetCurrent (args);
300- if (UNLIKELY (size > INT_MAX))
301- return THROW_ERR_OUT_OF_RANGE (env, " data is too long" );
302- bool r = hash->HashUpdate (data, size);
303- args.GetReturnValue ().Set (r);
304- });
365+ Decode<Hash>(args,
366+ [](Hash* hash,
367+ const FunctionCallbackInfo<Value>& args,
368+ const char * data,
369+ size_t size) {
370+ Environment* env = Environment::GetCurrent (args);
371+ if (UNLIKELY (size > INT_MAX))
372+ return THROW_ERR_OUT_OF_RANGE (env, " data is too long" );
373+ bool r = hash->HashUpdate (data, size);
374+ args.GetReturnValue ().Set (r);
375+ });
305376}
306377
307378void Hash::HashDigest (const FunctionCallbackInfo<Value>& args) {
0 commit comments