@@ -246,6 +246,39 @@ const EVP_MD* GetDigestImplementation(Environment* env,
246246#endif
247247}
248248
249+ void MarkInvalidXofLength () {
250+ EVPerr (EVP_F_EVP_DIGESTFINALXOF , EVP_R_NOT_XOF_OR_INVALID_LENGTH );
251+ }
252+
253+ // DEP0198 EOL requires XOFs without an OpenSSL-defined default output length
254+ // to fail when outputLength is omitted. OpenSSL 3.4 and later report a digest
255+ // size of 0 for such XOFs, including SHAKE, which had weak historical defaults
256+ // before OpenSSL 3.4. For older OpenSSL versions, identify those resolved
257+ // EVP_MD values explicitly to keep the missing-outputLength error
258+ // version-independent.
259+ #if !OPENSSL_VERSION_PREREQ(3, 4)
260+ bool IsShakeDigest (const EVP_MD * md) {
261+ #if OPENSSL_VERSION_MAJOR >= 3
262+ return EVP_MD_is_a (md, " SHAKE128" ) || EVP_MD_is_a (md, " SHAKE256" );
263+ #else
264+ const char * name = OBJ_nid2sn (EVP_MD_type (md));
265+ return name != nullptr &&
266+ (strcmp (name, " SHAKE128" ) == 0 || strcmp (name, " SHAKE256" ) == 0 );
267+ #endif
268+ }
269+ #endif
270+
271+ bool ShouldRejectMissingXofLength (const EVP_MD * md, size_t default_length) {
272+ if (default_length == 0 ) return true ;
273+
274+ #if !OPENSSL_VERSION_PREREQ(3, 4)
275+ return IsShakeDigest (md);
276+ #else
277+ static_cast <void >(md);
278+ return false ;
279+ #endif
280+ }
281+
249282// crypto.digest(algorithm, algorithmId, algorithmCache,
250283// input, outputEncoding, outputEncodingId, outputLength)
251284void Hash::OneShotDigest (const FunctionCallbackInfo<Value>& args) {
@@ -287,18 +320,10 @@ void Hash::OneShotDigest(const FunctionCallbackInfo<Value>& args) {
287320 } else if (is_xof) {
288321 if (!args[6 ]->IsUndefined ()) {
289322 output_length = args[6 ].As <Uint32>()->Value ();
290- } else if (output_length == 0 ) {
291- // This is to handle OpenSSL 3.4's breaking change in SHAKE128/256
292- // default lengths
293- // TODO(@panva): remove this behaviour when DEP0198 is End-Of-Life
294- const char * name = OBJ_nid2sn (EVP_MD_type (md));
295- if (name != nullptr ) {
296- if (strcmp (name, " SHAKE128" ) == 0 ) {
297- output_length = 16 ;
298- } else if (strcmp (name, " SHAKE256" ) == 0 ) {
299- output_length = 32 ;
300- }
301- }
323+ } else if (ShouldRejectMissingXofLength (md, output_length)) {
324+ MarkInvalidXofLength ();
325+ return ThrowCryptoError (
326+ env, ERR_get_error (), " Digest method not supported" );
302327 }
303328 }
304329
@@ -387,6 +412,12 @@ void Hash::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
387412void Hash::New (const FunctionCallbackInfo<Value>& args) {
388413 Environment* env = Environment::GetCurrent (args);
389414
415+ Maybe<unsigned int > xof_md_len = Nothing<unsigned int >();
416+ if (!args[1 ]->IsUndefined ()) {
417+ CHECK (args[1 ]->IsUint32 ());
418+ xof_md_len = Just<unsigned int >(args[1 ].As <Uint32>()->Value ());
419+ }
420+
390421 const Hash* orig = nullptr ;
391422 const EVP_MD * md = nullptr ;
392423 if (args[0 ]->IsObject ()) {
@@ -397,12 +428,6 @@ void Hash::New(const FunctionCallbackInfo<Value>& args) {
397428 md = GetDigestImplementation (env, args[0 ], args[2 ], args[3 ]);
398429 }
399430
400- Maybe<unsigned int > xof_md_len = Nothing<unsigned int >();
401- if (!args[1 ]->IsUndefined ()) {
402- CHECK (args[1 ]->IsUint32 ());
403- xof_md_len = Just<unsigned int >(args[1 ].As <Uint32>()->Value ());
404- }
405-
406431 Hash* hash = new Hash (env, args.This ());
407432 if (md == nullptr || !hash->HashInit (md, xof_md_len)) {
408433 return ThrowCryptoError (env, ERR_get_error (),
@@ -423,18 +448,11 @@ bool Hash::HashInit(const EVP_MD* md, Maybe<unsigned int> xof_md_len) {
423448
424449 md_len_ = mdctx_.getDigestSize ();
425450
426- // This is to handle OpenSSL 3.4's breaking change in SHAKE128/256
427- // default lengths
428- // TODO(@panva): remove this behaviour when DEP0198 is End-Of-Life
429- if (mdctx_.hasXofFlag () && !xof_md_len.IsJust () && md_len_ == 0 ) {
430- const char * name = OBJ_nid2sn (EVP_MD_type (md));
431- if (name != nullptr ) {
432- if (strcmp (name, " SHAKE128" ) == 0 ) {
433- md_len_ = 16 ;
434- } else if (strcmp (name, " SHAKE256" ) == 0 ) {
435- md_len_ = 32 ;
436- }
437- }
451+ if (mdctx_.hasXofFlag () && !xof_md_len.IsJust () &&
452+ ShouldRejectMissingXofLength (md, md_len_)) {
453+ MarkInvalidXofLength ();
454+ mdctx_.reset ();
455+ return false ;
438456 }
439457
440458 if (xof_md_len.IsJust () && xof_md_len.FromJust () != md_len_) {
0 commit comments