Skip to content

Make HashDataAsync, HmacDataAsync and VerifyHmacAsync asynchronous and cancellable - #655

Merged
SimonCropp merged 2 commits into
mainfrom
fix/hashdataasync-async
Oct 11, 2026
Merged

SimonCropp merged 2 commits into
mainfrom
fix/hashdataasync-async

Conversation

@SimonCropp

Copy link
Copy Markdown
Owner

Bug

The HashDataAsync polyfills on MD5, SHA1, SHA256, SHA384 and SHA512, and HashDataAsync / HmacDataAsync / VerifyHmacAsync on CryptographicOperations (both the recreated type and the extension members), were not asynchronous:

  • They hashed the stream with the synchronous ComputeHash(Stream) and wrapped the result in a completed ValueTask, blocking the caller for the whole read. A stream that only supports asynchronous reads could not be used.
  • The token was only checked once up front, never while reading. SHA256.HashDataAsync(Stream, CancellationToken) did not check it at all.
  • A pre-cancelled token threw OperationCanceledException synchronously. The BCL returns a cancelled task.

Fix

A shared internal helper (Polyfill_HashAlgorithm.cs) reads the stream with ReadAsync, passing the token, and feeds TransformBlock / TransformFinalBlock. All of the async members now go through it.

As in the BCL, arguments are validated synchronously (null source, null key / hash, a destination that is too short, a hash of the wrong length), and a pre-cancelled token gives a cancelled task without touching the stream. The behaviour was checked against net11 first.

Test

AsyncOnlyStream is a test stream that throws on a synchronous Read, returns a byte per ReadAsync, and observes the token. New tests in the MD5, SHA1, SHA256, SHA384, SHA512 and CryptographicOperations test classes cover:

  • the hash is correct when the stream can only be read asynchronously
  • a pre-cancelled token returns a cancelled task and reads nothing
  • cancelling during the read cancels the task after a single read
  • a short destination, a null source and a wrong length hash throw synchronously without reading

All pass on net11.0 (BCL). On net10.0, where only VerifyHmacAsync is polyfilled, exactly the VerifyHmacAsync tests failed before the fix. They pass on net462, net48, net8.0, net9.0 and net10.0 with the fix.

@SimonCropp SimonCropp added this to the 11.4.4 milestone Oct 11, 2026
@SimonCropp
SimonCropp merged commit 721095e into main Oct 11, 2026
4 checks passed
@SimonCropp
SimonCropp deleted the fix/hashdataasync-async branch October 11, 2026 22:11
This was referenced Oct 12, 2026
This was referenced Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant