packages feed

haskoin-0.0.2: tests/Network/Haskoin/Crypto/Mnemonic/Tests.hs

{-# LANGUAGE OverloadedStrings #-}
module Network.Haskoin.Crypto.Mnemonic.Tests (tests) where

import Data.Bits ((.&.), shiftR)
import Data.Binary (Binary)
import qualified Data.Text as T
import Data.Word (Word32)
import qualified Data.ByteString as BS
import Network.Haskoin.Crypto.Arbitrary()
import Network.Haskoin.Crypto.Mnemonic
import Network.Haskoin.Crypto.BigWord
import Network.Haskoin.Util (encode', fromRight)
import Test.QuickCheck (Arbitrary, Property, arbitrary, choose, (==>))
import Test.Framework (Test, testGroup)
import Test.Framework.Providers.QuickCheck2 (testProperty)


tests :: [Test]
tests = 
    [ testGroup "Encode mnemonic"
        [ testProperty "128-bit entropy -> 12 words" toMnemonic128
        , testProperty "160-bit entropy -> 18 words" toMnemonic160
        , testProperty "256-bit entropy -> 24 words" toMnemonic256
        , testProperty "512-bit entropy -> 48 words" toMnemonic512
        , testProperty "n-bit entropy -> m words" toMnemonicVar
        ]
    , testGroup "Encode/Decode Mnemonic"
        [ testProperty "128-bit entropy" fromToMnemonic128
        , testProperty "160-bit entropy" fromToMnemonic160
        , testProperty "256-bit entropy" fromToMnemonic256
        , testProperty "512-bit entropy" fromToMnemonic512
        , testProperty "n-bit entropy" fromToMnemonicVar
        ]
    , testGroup "Mnemonic to seed"
        [ testProperty "128-bit entropy" mnemonicToSeed128
        , testProperty "160-bit entropy" mnemonicToSeed160
        , testProperty "256-bit entropy" mnemonicToSeed256
        , testProperty "512-bit entropy" mnemonicToSeed512
        , testProperty "n-bit entropy" mnemonicToSeedVar
        ]
    , testGroup "Get bits from ByteString"
        [ testProperty "Byte count" getBitsByteCount
        , testProperty "End bits" getBitsEndBits
        ]
    ]

binWordsToBS :: Binary a => [a] -> BS.ByteString
binWordsToBS = foldr f BS.empty
  where
    f b a = a `BS.append` encode' b

{- Encode mnemonic -}

toMnemonic128 :: Word128 -> Bool
toMnemonic128 x = l == 12
  where
    bs = encode' x
    l = length . T.words . fromRight $ toMnemonic english bs

toMnemonic160 :: Word160 -> Bool
toMnemonic160 x = l == 15
  where
    bs = encode' x
    l = length . T.words . fromRight $ toMnemonic english bs

toMnemonic256 :: Word256 -> Bool
toMnemonic256 x = l == 24
  where
    bs = encode' x
    l = length . T.words . fromRight $ toMnemonic english bs

toMnemonic512 :: Word512 -> Bool
toMnemonic512 x = l == 48
  where
    bs = encode' x
    l = length . T.words . fromRight $ toMnemonic english bs

toMnemonicVar :: [Word32] -> Property
toMnemonicVar ls = not (length ls > 8) ==> l == wc
  where
    bs = binWordsToBS ls
    bl = BS.length bs
    cb = bl `div` 4
    wc = (cb + bl * 8) `div` 11
    l = length . T.words . fromRight $ toMnemonic english bs

{- Encode/Decode -}

fromToMnemonic128 :: Word128 -> Bool
fromToMnemonic128 x = bs == bs'
  where
    bs = encode' x
    bs' = fromRight (fromMnemonic english =<< toMnemonic english bs)

fromToMnemonic160 :: Word160 -> Bool
fromToMnemonic160 x = bs == bs'
  where
    bs = encode' x
    bs' = fromRight (fromMnemonic english =<< toMnemonic english bs)

fromToMnemonic256 :: Word256 -> Bool
fromToMnemonic256 x = bs == bs'
  where
    bs = encode' x
    bs' = fromRight (fromMnemonic english =<< toMnemonic english bs)

fromToMnemonic512 :: Word512 -> Bool
fromToMnemonic512 x = bs == bs'
  where
    bs = encode' x
    bs' = fromRight (fromMnemonic english =<< toMnemonic english bs)

fromToMnemonicVar :: [Word32] -> Property
fromToMnemonicVar ls = not (length ls > 8) ==> bs == bs'
  where
    bs = binWordsToBS ls
    bs' = fromRight (fromMnemonic english =<< toMnemonic english bs)

{- Mnemonic to seed -}

mnemonicToSeed128 :: Word128 -> Bool
mnemonicToSeed128 x = l == 64
  where
    bs = encode' x
    seed = fromRight (mnemonicToSeed english "" =<< toMnemonic english bs)
    l = BS.length seed

mnemonicToSeed160 :: Word160 -> Bool
mnemonicToSeed160 x = l == 64
  where
    bs = encode' x
    seed = fromRight (mnemonicToSeed english "" =<< toMnemonic english bs)
    l = BS.length seed

mnemonicToSeed256 :: Word256 -> Bool
mnemonicToSeed256 x = l == 64
  where
    bs = encode' x
    seed = fromRight (mnemonicToSeed english "" =<< toMnemonic english bs)
    l = BS.length seed

mnemonicToSeed512 :: Word512 -> Bool
mnemonicToSeed512 x = l == 64
  where
    bs = encode' x
    seed = fromRight (mnemonicToSeed english "" =<< toMnemonic english bs)
    l = BS.length seed

mnemonicToSeedVar :: [Word32] -> Property
mnemonicToSeedVar ls = not (length ls > 16) ==> l == 64
  where
    bs = binWordsToBS ls
    seed = fromRight (mnemonicToSeed english "" =<< toMnemonic english bs)
    l = BS.length seed

{- Get bits from ByteString -}

data ByteCountGen = ByteCountGen BS.ByteString Int deriving Show

instance Arbitrary ByteCountGen where
    arbitrary = do
        bs <- arbitrary
        i <- choose (0, BS.length bs * 8)
        return $ ByteCountGen bs i

getBitsByteCount :: ByteCountGen -> Bool
getBitsByteCount (ByteCountGen bs i) = BS.length bits == l
  where
    (q, r) = i `quotRem` 8
    bits = getBits i bs
    l = if r == 0 then q else q + 1

getBitsEndBits :: ByteCountGen -> Bool
getBitsEndBits (ByteCountGen bs i) = mask
  where
    r = i `mod` 8
    bits = getBits i bs
    mask = if r == 0 then True else BS.last bits .&. (0xff `shiftR` r) == 0x00