packages feed

ppad-bip39-0.2.0: test/Main.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

module Main where

import qualified Crypto.HDKey.BIP32 as BIP32
import qualified Crypto.KDF.BIP39 as BIP39
import qualified Data.Aeson as A
import qualified Data.Text.ICU.Normalize2 as ICU
import qualified Data.Text.IO as TIO
import Test.Tasty
import Test.Tasty.HUnit
import qualified Vectors as V

data Wordlist =
    English
  | ChineseTraditional
  | ChineseSimplified
  | Czech
  | French
  | Italian
  | Japanese
  | Korean
  | Portuguese
  | Spanish

main :: IO ()
main = do
  vectors_bip39 <- TIO.readFile "etc/vectors.json"
  vectors_jp_bip39 <- TIO.readFile "etc/test_JP_BIP39.json"
  let vectors = do
        a <- A.decodeStrictText vectors_bip39 :: Maybe V.Vectors
        b <- A.decodeStrictText vectors_jp_bip39 :: Maybe [V.JPBip39Test]
        pure (a, b)
  case vectors of
    Nothing -> error "couldn't parse bip39 vectors"
    Just (vs, js) -> defaultMain $
      testGroup "ppad-bip39" [
          bip39_tests vs
        , jp_bip39_tests js
        ]

jp_bip39_tests :: [V.JPBip39Test] -> TestTree
jp_bip39_tests jp_vectors =
  testGroup "jp bip39 vectors" (fmap execute_jp jp_vectors)

bip39_tests :: V.Vectors -> TestTree
bip39_tests V.Vectors {..} =
  testGroup "bip39 vectors" [
      testGroup "english"
        (fmap (execute English) v_english)
    , testGroup "chinese_traditional"
        (fmap (execute ChineseTraditional) v_chinese_traditional)
    , testGroup "chinese_simplified"
        (fmap (execute ChineseSimplified) v_chinese_simplified)
    , testGroup "french"
        (fmap (execute French) v_french)
    , testGroup "czech"
        (fmap (execute Czech) v_czech)
    , testGroup "italian"
        (fmap (execute Italian) v_italian)
    , testGroup "japanese"
        (fmap (execute Japanese) v_japanese)
    , testGroup "korean"
        (fmap (execute Korean) v_korean)
    , testGroup "portuguese"
        (fmap (execute Portuguese) v_portuguese)
    , testGroup "spanish"
        (fmap (execute Spanish) v_spanish)
    ]

execute :: Wordlist -> V.Bip39Test -> TestTree
execute wlist V.Bip39Test {..} = do
    let entr = bt_entropy
        mnem = bt_mnemonic
        seed = bt_seed
        xprv = bt_xprv
        out_mnem = BIP39._mnemonic wl entr
        giv_seed = seed_fn mnem "TREZOR"
        out_seed = seed_fn out_mnem "TREZOR"
        out_xprv = case BIP32.master out_seed of
          Just hd -> BIP32.xprv hd
          Nothing -> error "bang (bip32)"
        t_msg = mempty
    testGroup t_msg [
        -- we always output (NFKD) normalized UTF8, but test inputs may not be
        -- normalized in this fashion
        testCase "mnemonic" $ assertEqual mempty (ICU.nfkd mnem) out_mnem
        -- testing from the given mnemonic ensures we're normalizing properly
        -- before seed calculation
      , testCase "seed (from given mnemonic)" $ assertEqual mempty seed giv_seed
      , testCase "seed (from derived mnemonic)" $ assertEqual mempty seed out_seed
      , testCase "xprv" $ assertEqual mempty xprv out_xprv
      ]
  where
    seed_fn = case wlist of
      English -> BIP39.seed
      _ -> BIP39.seed_unsafe
    wl = case wlist of
      English -> BIP39.english
      ChineseTraditional -> BIP39.chinese_traditional
      ChineseSimplified -> BIP39.chinese_simplified
      Czech -> BIP39.czech
      French -> BIP39.french
      Italian -> BIP39.italian
      Japanese -> BIP39.japanese
      Korean -> BIP39.korean
      Portuguese -> BIP39.portuguese
      Spanish -> BIP39.spanish

execute_jp :: V.JPBip39Test -> TestTree
execute_jp V.JPBip39Test {..} = do
  let entr = jp_entropy
      mnem = jp_mnemonic
      pass = jp_passphrase
      seed = jp_seed
      xprv = jp_xprv
      out_mnem = BIP39._mnemonic BIP39.japanese entr
      giv_seed = BIP39.seed_unsafe mnem pass
      out_seed = BIP39.seed_unsafe out_mnem pass
      out_xprv = case BIP32.master out_seed of
        Just hd -> BIP32.xprv hd
        Nothing -> error "bang (bip32, jp)"
  testGroup mempty [
      testCase "mnemonic" $ assertEqual mempty (ICU.nfkd mnem) out_mnem
    , testCase "seed (from given mnemonic)" $ assertEqual mempty seed giv_seed
    , testCase "seed (from derived mnemonic)" $ assertEqual mempty seed out_seed
    , testCase "xprv" $ assertEqual mempty xprv out_xprv
    ]