packages feed

haskoin-core-0.20.0: test/Haskoin/Crypto/HashSpec.hs

{-# LANGUAGE OverloadedStrings #-}
module Haskoin.Crypto.HashSpec (spec) where

import           Data.Bits
import           Data.ByteString         (ByteString)
import           Data.ByteString.Builder
import qualified Data.ByteString.Char8   as C
import qualified Data.ByteString.Lazy    as BL
import qualified Data.ByteString.Short   as BSS
import           Data.Bytes.Get
import           Data.Bytes.Put
import           Data.Bytes.Serial
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

serialVals :: [SerialBox]
serialVals =
    [ SerialBox arbitraryBS
    , SerialBox arbitraryHash160
    , SerialBox arbitraryHash256
    , SerialBox arbitraryHash512
    ]

readVals :: [ReadBox]
readVals =
    [ ReadBox arbitraryBS
    , ReadBox arbitraryBSS
    , ReadBox arbitraryHash160
    , ReadBox arbitraryHash256
    , ReadBox arbitraryHash512
    ]

spec :: Spec
spec =
    describe "Hash" $ do
        testIdentity serialVals readVals [] []
        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 getHash160) ripemd160Vectors
            it "Passes SHA1 test vectors" $
                mapM_ (testVector sha1 getHash160) sha1Vectors
            it "Passes SHA256 test vectors" $
                mapM_ (testVector sha256 getHash256) sha256Vectors
            it "Passes SHA512 test vectors" $
                mapM_ (testVector sha512 getHash512) sha512Vectors
            it "Passes HMAC_SHA256 test vectors" $
                mapM_ (testHMACVector hmac256 getHash256) hmacSha256Vectors
            it "Passes HMAC_SHA512 test vectors" $
                mapM_ (testHMACVector hmac512 getHash512) 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 -> BSS.ShortByteString)
    -> (ByteString, Text)
    -> Assertion
testVector f1 f2 (i, res) =
    assertEqual "Hash matches" res (encodeHex (BSS.fromShort $ f2 $ f1 i))

testHMACVector ::
       (ByteString -> ByteString -> a)
    -> (a -> BSS.ShortByteString)
    -> (Text, Text, Text)
    -> Assertion
testHMACVector f1 f2 (k, m, res) =
    assertEqual "Hash matches" res (encodeHex (BSS.fromShort $ f2 $ f1 bsK bsM))
  where
    bsK = fromJust $ decodeHex k
    bsM = fromJust $ decodeHex m

longTestString :: ByteString
longTestString =
    BL.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")
    , (C.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")
    , (C.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")
    , ( C.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")
    , ( C.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")
    ]