This repository was archived by the owner on Sep 20, 2023. It is now read-only.
This repository was archived by the owner on Sep 20, 2023. It is now read-only.
Incorrect hashes for all hash algorithms beyond 4 GiB of input #330
Closed
Description
I found a problem together with @chpatrick:
Click to show imports
:set -XTypeApplications
import qualified Crypto.Hash as Hash
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BS8
> let hash = show . Hash.hash @BS.ByteString @Hash.SHA1
> let a = BS8.replicate 5000000000 'a' <> BS8.pack "hello"
> let b = BS8.replicate 5000000000 'a' <> BS8.pack "world"
> a == b
False
> hash a == hash b
True
Two different ByteStrings hash to the same SHA1.
Trying SHA256, same issue:
> let hash = show . Hash.hash @BS.ByteString @Hash.SHA256
> hash a == hash b
True
Conclusion
fromIntegral
is bad.
Until we deprecatefromIntegral
, Haskell code will always be subtly wrong and never be secure.
I proposed this 3 years ago (easier-to-read threaded view here).
Concretely:- If we don't fix this, people will shy away from using Haskell for serious work. I found this bug in production, and you can imagine how happy I am about that. Rust and C both do this better.
- If the authors of key crypto libraries fall for these traps (no blame on them), who can get it right? We should remove the traps.
Details
Demonstrating that the the overflow occurs at the uint32
boundary:
Good:
> show $ Hash.hash @BS.ByteString @Hash.SHA1 (BS8.replicate (2^32 - 2) 'a' <> BS8.pack "b")
"78c1411ef0ae4e0328660b7dddc08c06000eb9f0"
> show $ Hash.hash @BS.ByteString @Hash.SHA1 (BS8.replicate (2^32 - 2) 'a' <> BS8.pack "c")
"21f556aba357dd19d7bf01a2a7f05482162ed3fc"
Bad:
> show $ Hash.hash @BS.ByteString @Hash.SHA1 (BS8.replicate (2^32 - 1) 'a' <> BS8.pack "b")
"da39a3ee5e6b4b0d3255bfef95601890afd80709"
> show $ Hash.hash @BS.ByteString @Hash.SHA1 (BS8.replicate (2^32 - 1) 'a' <> BS8.pack "c")
"da39a3ee5e6b4b0d3255bfef95601890afd80709"
Location of the problem:
For SHA1 (note uint32_t len
):
cryptonite/cbits/cryptonite_sha1.c
Line 159 in 10dc63c
For all hash algorithms (note Word32
for the length parameter):
cryptonite/Crypto/Hash/Types.hs
Lines 59 to 60 in 10dc63c
This is where the silent overflow occurs due to fromIntegral
:
Line 91 in 10dc63c
Metadata
Metadata
Assignees
Labels
No labels