haskoin-core-1.2.2: test/Haskoin/Crypto/HashSpec.hs
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoFieldSelectors #-}
module Haskoin.Crypto.HashSpec (spec) where
import Data.Bits
import Data.ByteString (ByteString)
import Data.ByteString.Builder
import Data.ByteString.Char8 qualified as Char8
import Data.ByteString.Lazy qualified as Lazy
import Data.ByteString.Short qualified as Short
import Data.Bytes.Get
import Data.Bytes.Put
import Data.Bytes.Serial
import Data.Default (def)
import Data.Maybe (fromJust)
import Data.String (fromString)
import Data.String.Conversions
import Data.Text (Text)
import Data.Word
import Haskoin.Block
import Haskoin.Crypto
import Haskoin.Util
import Haskoin.Util.Arbitrary
import Test.HUnit
import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck
identityTests :: IdentityTests
identityTests =
def
{ readTests =
[ ReadBox arbitraryHash160,
ReadBox arbitraryHash256,
ReadBox arbitraryHash512,
ReadBox arbitraryCheckSum32
],
serialTests =
[ SerialBox arbitraryHash160,
SerialBox arbitraryHash256,
SerialBox arbitraryHash512,
SerialBox arbitraryCheckSum32
]
}
spec :: Spec
spec =
describe "Hash" $ do
testIdentity identityTests
describe "Property Tests" $ do
prop "join512( split512(h) ) == h" $
forAll arbitraryHash256 $
forAll arbitraryHash256 . joinSplit512
prop "decodeCompact . encodeCompact i == i" decEncCompact
prop "from string Hash512" $
forAll arbitraryHash512 $ \h ->
fromString (cs $ encodeHex $ runPutS $ serialize h) == h
prop "from string Hash256" $
forAll arbitraryHash256 $ \h ->
fromString (cs $ encodeHex $ runPutS $ serialize h) == h
prop "from string Hash160" $
forAll arbitraryHash160 $ \h ->
fromString (cs $ encodeHex $ runPutS $ serialize h) == h
describe "Test Vectors" $ do
it "Passes RIPEMD160 test vectors" $
mapM_ (testVector ripemd160 (.get)) ripemd160Vectors
it "Passes SHA1 test vectors" $
mapM_ (testVector sha1 (.get)) sha1Vectors
it "Passes SHA256 test vectors" $
mapM_ (testVector sha256 (.get)) sha256Vectors
it "Passes SHA512 test vectors" $
mapM_ (testVector sha512 (.get)) sha512Vectors
it "Passes HMAC_SHA256 test vectors" $
mapM_ (testHMACVector hmac256 (.get)) hmacSha256Vectors
it "Passes HMAC_SHA512 test vectors" $
mapM_ (testHMACVector hmac512 (.get)) hmacSha512Vectors
joinSplit512 :: Hash256 -> Hash256 -> Bool
joinSplit512 a b = split512 (join512 (a, b)) == (a, b)
-- After encoding and decoding, we may loose precision so the new result is >=
-- to the old one.
decEncCompact :: Integer -> Bool
decEncCompact i
-- Integer completely fits inside the mantisse
| abs i <= 0x007fffff = decodeCompact (encodeCompact i) == (i, False)
-- Otherwise precision will be lost and the decoded result will
-- be smaller than the original number
| i >= 0 = fst (decodeCompact (encodeCompact i)) < i
| otherwise = fst (decodeCompact (encodeCompact i)) > i
-- Test vectors from:
-- https://github.com/bitcoin/bitcoin/blob/master/src/test/crypto_tests.cpp
testVector ::
(ByteString -> a) ->
(a -> Short.ShortByteString) ->
(ByteString, Text) ->
Assertion
testVector f1 f2 (i, res) =
assertEqual "Hash matches" res (encodeHex (Short.fromShort $ f2 $ f1 i))
testHMACVector ::
(ByteString -> ByteString -> a) ->
(a -> Short.ShortByteString) ->
(Text, Text, Text) ->
Assertion
testHMACVector f1 f2 (k, m, res) =
assertEqual "Hash matches" res (encodeHex (Short.fromShort $ f2 $ f1 bsK bsM))
where
bsK = fromJust $ decodeHex k
bsM = fromJust $ decodeHex m
longTestString :: ByteString
longTestString =
Lazy.toStrict $! toLazyByteString $! go [0 .. 199999]
where
go :: [Word32] -> Builder
go [] = mempty
go (i : is) =
let i1 = fromIntegral $! i
i2 = fromIntegral $! i `shiftR` 4
i3 = fromIntegral $! i `shiftR` 8
i4 = fromIntegral $! i `shiftR` 12
i5 = fromIntegral $! i `shiftR` 16
in word8 i1 <> word8 i2 <> word8 i3 <> word8 i4 <> word8 i5 <> go is
ripemd160Vectors :: [(ByteString, Text)]
ripemd160Vectors =
[ ("", "9c1185a5c5e9fc54612808977ee8f548b2258d31"),
("abc", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"),
("message digest", "5d0689ef49d2fae572b881b123a85ffa21595f36"),
("secure hash algorithm", "20397528223b6a5f4cbc2808aba0464e645544f9"),
( "RIPEMD160 is considered to be safe",
"a7d78608c7af8a8e728778e81576870734122b66"
),
( "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
"12a053384a9c0c88e405a06c27dcf49ada62eb2b"
),
( "For this sample, this 63-byte string will be used as input data",
"de90dbfee14b63fb5abf27c2ad4a82aaa5f27a11"
),
( "This is exactly 64 bytes long, not counting the terminating byte",
"eda31d51d3a623b81e19eb02e24ff65d27d67b37"
),
(Char8.replicate 1000000 'a', "52783243c1697bdbe16d37f97f68f08325dc1528"),
(longTestString, "464243587bd146ea835cdf57bdae582f25ec45f1")
]
sha1Vectors :: [(ByteString, Text)]
sha1Vectors =
[ ("", "da39a3ee5e6b4b0d3255bfef95601890afd80709"),
("abc", "a9993e364706816aba3e25717850c26c9cd0d89d"),
("message digest", "c12252ceda8be8994d5fa0290a47231c1d16aae3"),
("secure hash algorithm", "d4d6d2f0ebe317513bbd8d967d89bac5819c2f60"),
( "SHA1 is considered to be safe",
"f2b6650569ad3a8720348dd6ea6c497dee3a842a"
),
( "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
"84983e441c3bd26ebaae4aa1f95129e5e54670f1"
),
( "For this sample, this 63-byte string will be used as input data",
"4f0ea5cd0585a23d028abdc1a6684e5a8094dc49"
),
( "This is exactly 64 bytes long, not counting the terminating byte",
"fb679f23e7d1ce053313e66e127ab1b444397057"
),
(Char8.replicate 1000000 'a', "34aa973cd4c4daa4f61eeb2bdbad27316534016f"),
(longTestString, "b7755760681cbfd971451668f32af5774f4656b5")
]
sha256Vectors :: [(ByteString, Text)]
sha256Vectors =
[ ("", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"),
( "abc",
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
),
( "message digest",
"f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650"
),
( "secure hash algorithm",
"f30ceb2bb2829e79e4ca9753d35a8ecc00262d164cc077080295381cbd643f0d"
),
( "SHA256 is considered to be safe",
"6819d915c73f4d1e77e4e1b52d1fa0f9cf9beaead3939f15874bd988e2a23630"
),
( "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
),
( "For this sample, this 63-byte string will be used as input data",
"f08a78cbbaee082b052ae0708f32fa1e50c5c421aa772ba5dbb406a2ea6be342"
),
( "This is exactly 64 bytes long, not counting the terminating byte",
"ab64eff7e88e2e46165e29f2bce41826bd4c7b3552f6b382a9e7d3af47c245f8"
),
( "As Bitcoin relies on 80 byte header hashes, we want to have an example for that.",
"7406e8de7d6e4fffc573daef05aefb8806e7790f55eab5576f31349743cca743"
),
( Char8.replicate 1000000 'a',
"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"
),
( longTestString,
"a316d55510b49662420f49d145d42fb83f31ef8dc016aa4e32df049991a91e26"
)
]
sha512Vectors :: [(ByteString, Text)]
sha512Vectors =
[ ( "",
"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d1\
\3c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
),
( "abc",
"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a219299\
\2a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
),
( "message digest",
"107dbf389d9e9f71a3a95f6c055b9251bc5268c2be16d6c13492ea45b0199f3309e164\
\55ab1e96118e8a905d5597b72038ddb372a89826046de66687bb420e7c"
),
( "secure hash algorithm",
"7746d91f3de30c68cec0dd693120a7e8b04d8073cb699bdce1a3f64127bca7a3d5db50\
\2e814bb63c063a7a5043b2df87c61133395f4ad1edca7fcf4b30c3236e"
),
( "SHA512 is considered to be safe",
"099e6468d889e1c79092a89ae925a9499b5408e01b66cb5b0a3bd0dfa51a99646b4a39\
\01caab1318189f74cd8cf2e941829012f2449df52067d3dd5b978456c2"
),
( "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
"204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15\
\c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"
),
( "For this sample, this 63-byte string will be used as input data",
"b3de4afbc516d2478fe9b518d063bda6c8dd65fc38402dd81d1eb7364e72fb6e6663cf\
\6d2771c8f5a6da09601712fb3d2a36c6ffea3e28b0818b05b0a8660766"
),
( "This is exactly 64 bytes long, not counting the terminating byte",
"70aefeaa0e7ac4f8fe17532d7185a289bee3b428d950c14fa8b713ca09814a387d2458\
\70e007a80ad97c369d193e41701aa07f3221d15f0e65a1ff970cedf030"
),
( "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmn\
\opjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
"8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d28\
\9e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"
),
( Char8.replicate 1000000 'a',
"e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff2\
\44877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"
),
( longTestString,
"40cac46c147e6131c5193dd5f34e9d8bb4951395f27b08c558c65ff4ba2de59437de8c\
\3ef5459d76a52cedc02dc499a3c9ed9dedbfb3281afd9653b8a112fafc"
)
]
-- test cases 1, 2, 3, 4, 6 and 7 of RFC 4231
hmacSha256Vectors :: [(Text, Text, Text)]
hmacSha256Vectors =
[ ( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
"4869205468657265",
"b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"
),
( "4a656665",
"7768617420646f2079612077616e7420666f72206e6f7468696e673f",
"5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"
),
( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\
\dddddddddddddddddddddddddddddd",
"773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"
),
( "0102030405060708090a0b0c0d0e0f10111213141516171819",
"cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd\
\cdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
"82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"
),
( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a65204b\
\6579202d2048617368204b6579204669727374",
"60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"
),
( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"5468697320697320612074657374207573696e672061206c6172676572207468616e20\
\626c6f636b2d73697a65206b657920616e642061206c6172676572207468616e20626c\
\6f636b2d73697a6520646174612e20546865206b6579206e6565647320746f20626520\
\686173686564206265666f7265206265696e6720757365642062792074686520484d41\
\4320616c676f726974686d2e",
"9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"
),
-- Test case with key length 63 bytes.
( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\
\654a6566654a6566654a6566654a6566654a6566654a6566654a6566",
"7768617420646f2079612077616e7420666f72206e6f7468696e673f",
"9de4b546756c83516720a4ad7fe7bdbeac4298c6fdd82b15f895a6d10b0769a6"
),
-- Test case with key length 64 bytes.
( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\
\654a6566654a6566654a6566654a6566654a6566654a6566654a656665",
"7768617420646f2079612077616e7420666f72206e6f7468696e673f",
"528c609a4c9254c274585334946b7c2661bad8f1fc406b20f6892478d19163dd"
),
-- Test case with key length 65 bytes.
( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\
\654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a",
"7768617420646f2079612077616e7420666f72206e6f7468696e673f",
"d06af337f359a2330deffb8e3cbe4b5b7aa8ca1f208528cdbd245d5dc63c4483"
)
]
-- test cases 1, 2, 3, 4, 6 and 7 of RFC 4231
hmacSha512Vectors :: [(Text, Text, Text)]
hmacSha512Vectors =
[ ( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
"4869205468657265",
"87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde\
\daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"
),
( "4a656665",
"7768617420646f2079612077616e7420666f72206e6f7468696e673f",
"164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554\
\9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"
),
( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\
\dddddddddddddddddddddddddddddddddddd",
"fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39\
\bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"
),
( "0102030405060708090a0b0c0d0e0f10111213141516171819",
"cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd\
\cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
"b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3db\
\a91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"
),
( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
\aaaaaa",
"54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a\
\65204b6579202d2048617368204b6579204669727374",
"80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352\
\6b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"
),
( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
\aaaaaa",
"5468697320697320612074657374207573696e672061206c6172676572207468\
\616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074\
\68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565\
\647320746f20626520686173686564206265666f7265206265696e6720757365\
\642062792074686520484d414320616c676f726974686d2e",
"e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944\
\b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58"
),
-- Test case with key length 127 bytes.
( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\
\4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\
\4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\
\4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566",
"7768617420646f2079612077616e7420666f72206e6f7468696e673f",
"267424dfb8eeb999f3e5ec39a4fe9fd14c923e6187e0897063e5c9e02b2e624a\
\c04413e762977df71a9fb5d562b37f89dfdfb930fce2ed1fa783bbc2a203d80e"
),
-- Test case with key length 128 bytes.
( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\
\4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\
\4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\
\4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665",
"7768617420646f2079612077616e7420666f72206e6f7468696e673f",
"43aaac07bb1dd97c82c04df921f83b16a68d76815cd1a30d3455ad43a3d80484\
\2bb35462be42cc2e4b5902de4d204c1c66d93b47d1383e3e13a3788687d61258"
),
-- Test case with key length 129 bytes.
( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\
\4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\
\4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\
\4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\
\4a",
"7768617420646f2079612077616e7420666f72206e6f7468696e673f",
"0b273325191cfc1b4b71d5075c8fcad67696309d292b1dad2cd23983a35feb8e\
\fb29795e79f2ef27f68cb1e16d76178c307a67beaad9456fac5fdffeadb16e2c"
)
]