content: module Blake2Spec (spec) where
  
  import Crypto.Hash.Blake2Native
  import Data.Bits
  import Data.ByteString (ByteString, pack)
  import Data.Foldable (foldlM)
  import Data.Word
  import Test.Hspec
  
  spec :: Spec
  spec = do
    describe "blake2b_selftest" blake2b_selftest
    describe "blake2s_selftest" blake2s_selftest
  
  -- test from RFC7693
  
  -- | grand hash of hash results
  blake2b_res :: ByteString
  blake2b_res = pack [
          0xC2, 0x3A, 0x78, 0x00, 0xD9, 0x81, 0x23, 0xBD,
          0x10, 0xF5, 0x06, 0xC6, 0x1E, 0x29, 0xDA, 0x56,
          0x03, 0xD7, 0x63, 0xB8, 0xBB, 0xAD, 0x2E, 0x73,
          0x7F, 0x5E, 0x76, 0x5A, 0x7B, 0xCC, 0xD4, 0x75
      ]
  
  -- parameter sets
  b2b_md_len :: [Int]; b2b_md_len = [ 20, 32, 48, 64 ];
  b2b_in_len :: [Int]; b2b_in_len = [ 0, 3, 128, 129, 255, 1024 ];
  
  
  selftest_seq :: Int -> Word32 -> ByteString
  selftest_seq len seed =
    let a' = 0xDEAD4BAD * seed
        b' = 1
    in pack $ reverse $ fst $ ffoldl ([],(a',b')) [0..(pred len)] $ \(bs,(a,b)) _ ->
      let t = a + b
          a'' = b
          b'' = t
      in (fromIntegral ((t `shiftR` 24) .&. 0xFF):bs,(a'',b''))
  
  
  
  
  blake2b_selftest :: Spec
  blake2b_selftest =
    it "final hash correct" $ (blake2b_final <$> hashing) `shouldBe` Right blake2b_res
    where hashing = blake2b_init 32 mempty >>= \cxinit -> foldlM f cxinit b2b_md_len
          f cx outlen = foldlM g cx b2b_in_len
            where g cx0 inlen = do
                    let inB = selftest_seq inlen (fromIntegral inlen)
                    md0 <- blake2b outlen mempty inB
                    let cx1 = blake2b_update md0 cx0
                        key = selftest_seq outlen (fromIntegral outlen)
                    md1 <- blake2b outlen key inB
                    Right $ blake2b_update md1 cx1
  
  -- | Grand hash of hash results.
  blake2s_res :: ByteString
  blake2s_res = pack [
          0x6A, 0x41, 0x1F, 0x08, 0xCE, 0x25, 0xAD, 0xCD,
          0xFB, 0x02, 0xAB, 0xA6, 0x41, 0x45, 0x1C, 0xEC,
          0x53, 0xC5, 0x98, 0xB2, 0x4F, 0x4F, 0xC7, 0x87,
          0xFB, 0xDC, 0x88, 0x79, 0x7F, 0x4C, 0x1D, 0xFE
      ];
  
  -- Parameter sets.
  b2s_md_len :: [Int]; b2s_md_len = [ 16, 20, 28, 32 ];
  b2s_in_len :: [Int]; b2s_in_len = [ 0,  3,  64, 65, 255, 1024 ];
  
  blake2s_selftest :: Spec
  blake2s_selftest =
    it "final hash correct" $ (blake2s_final <$> hashing) `shouldBe` Right blake2s_res
    where hashing = blake2s_init 32 mempty >>= \cxinit -> foldlM f cxinit b2s_md_len
          f cx outlen = foldlM g cx b2s_in_len
            where g cx0 inlen = do
                    let inB = selftest_seq inlen (fromIntegral inlen)
                    md0 <- blake2s outlen mempty inB
                    let cx1 = blake2s_update md0 cx0
                        key = selftest_seq outlen (fromIntegral outlen)
                    md1 <- blake2s outlen key inB
                    Right $ blake2s_update md1 cx1
  