Since 2026.0.0, Session.ReceiveMessage verifies the server MAC by reusing a single _serverMac HashAlgorithm instance across the whole session via TransformBlock/TransformFinalBlock, without ever calling Initialize() between packets. This violates HashAlgorithm's reuse contract. On certain older .NET Framework mscorlib.dll builds (pre-HMAC-rework, CAPI-backed implementations), the inner hash state isn't reset after TransformFinalBlock, so the second and subsequent MAC-protected packets fail — silently producing a wrong hash for hmac-sha2-* (SshConnectionException: MAC error) or throwing CryptographicException for hmac-sha1.
Fix
In both the ETM and non-ETM MAC verification branches of Session.ReceiveMessage, call _serverMac.Initialize() right after computing/comparing the MAC and before evaluating whether to throw, so the instance is always ready for the next packet.
Applied unconditionally, not gated to .NET Framework, since this addresses an actual HashAlgorithm reuse contract violation rather than a platform-specific quirk.
_=_serverMac.TransformFinalBlock(Array.Empty<byte>(),0,0);varmacIsValid=CryptoAbstraction.FixedTimeEquals(_serverMac.Hash, ...);// Not all HashAlgorithm implementations reset their internal state after// TransformFinalBlock(), so we need to explicitly reinitialize the algorithm// before it can be reused to compute the MAC of the next packet._serverMac.Initialize();if(!macIsValid){thrownewSshConnectionException("MAC error",DisconnectReason.MacError);}
The client-side send path (_clientMac.TryComputeHash) already performs a one-shot hash computation per packet and is unaffected by this issue.
CopilotAI
changed the title
[WIP] Fix MAC error and CryptographicException in .NET Framework
Reinitialize server MAC HashAlgorithm between packets to prevent spurious MAC errors
Sep 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
WojciechNagorski
Awaiting requested review from WojciechNagorski
WojciechNagorski is a code owner
drieseng
Awaiting requested review from drieseng
drieseng will be requested when the pull request is marked ready for review
drieseng is a code owner
Rob-Hague
Awaiting requested review from Rob-Hague
Rob-Hague will be requested when the pull request is marked ready for review
Rob-Hague is a code owner
At least 1 approving review is required to merge this pull request.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Since 2026.0.0, Session.ReceiveMessage verifies the server MAC by reusing a single _serverMac HashAlgorithm instance across the whole session via TransformBlock/TransformFinalBlock, without ever calling Initialize() between packets. This violates HashAlgorithm's reuse contract. On certain older .NET Framework mscorlib.dll builds (pre-HMAC-rework, CAPI-backed implementations), the inner hash state isn't reset after TransformFinalBlock, so the second and subsequent MAC-protected packets fail — silently producing a wrong hash for hmac-sha2-* (SshConnectionException: MAC error) or throwing CryptographicException for hmac-sha1.
Fix
The client-side send path (_clientMac.TryComputeHash) already performs a one-shot hash computation per packet and is unaffected by this issue.