bip39

BIP39 mnemonic codes in Haskell (docs.ppad.tech/bip39).
git clone git://git.ppad.tech/bip39.git
Log | Files | Refs | README | LICENSE

commit 0f9aa55cd8cbe342b66ec2891bbee5cb7ca9c233
parent 0be45607ae3c84f66d17a625f691c730ecdb58d3
Author: Jared Tobin <jared@jtobin.io>
Date:   Fri, 20 Jun 2025 17:44:05 +0400

lib: use updated bip32 api

Diffstat:
Mflake.lock | 64++++++++++++++++++++++++++++++++--------------------------------
Mlib/Crypto/KDF/BIP39.hs | 4++--
Mtest/Main.hs | 22+++++++++-------------
3 files changed, 43 insertions(+), 47 deletions(-)

diff --git a/flake.lock b/flake.lock @@ -51,11 +51,11 @@ ] }, "locked": { - "lastModified": 1739979569, - "narHash": "sha256-omEcmgzRlzIE5Vdty0/SskEcR2f7OtcHzGFE4i1dI60=", + "lastModified": 1741625558, + "narHash": "sha256-ZBDXRD5fsVqA5bGrAlcnhiu67Eo50q0M9614nR3NBwY=", "ref": "master", - "rev": "4439e0efafbb5185bd7d9bfb352a17c2a31b96b4", - "revCount": 15, + "rev": "fb63457f2e894eda28250dfe65d0fcd1d195ac2f", + "revCount": 24, "type": "git", "url": "git://git.ppad.tech/base16.git" }, @@ -123,11 +123,11 @@ ] }, "locked": { - "lastModified": 1739979602, - "narHash": "sha256-RrhqNPuDmKo1mG0Y2ZyaeuMuCjtIhGLLO87TaJeEDZI=", + "lastModified": 1750335817, + "narHash": "sha256-XReE3NMudmbpWgvMGwvsMt5SO3DkKYFYVLvsh5waDRg=", "ref": "master", - "rev": "40e80c1b2c39c829976aab44f6d318ebe4e2e784", - "revCount": 22, + "rev": "10c667ad8dc7583407fd1545259dc140497e2b17", + "revCount": 27, "type": "git", "url": "git://git.ppad.tech/base58.git" }, @@ -166,11 +166,11 @@ ] }, "locked": { - "lastModified": 1740292997, - "narHash": "sha256-EOmvT1aBIVfSlleDn1Qg219rF6FgHovu/ZWGgfcYfqc=", + "lastModified": 1750423628, + "narHash": "sha256-HF7jKLvMGhCp4GCpHb7q+69zZu6VfE1Bu8cNJQpig5k=", "ref": "master", - "rev": "ce0978beadab34fd1103287a99c1e3f335f5f58a", - "revCount": 37, + "rev": "d4b9d2f26e19416e1153571e449321ac5d62c939", + "revCount": 45, "type": "git", "url": "git://git.ppad.tech/bip32.git" }, @@ -271,11 +271,11 @@ ] }, "locked": { - "lastModified": 1740456623, - "narHash": "sha256-s29IVCEZMOhlq56Z4DAq+2UX8yYn/f+n3mIQMTGbuaQ=", + "lastModified": 1749628166, + "narHash": "sha256-zjZqto6oVW/Zcc0hYkup2mzEV27k+Y7dZUezAaqTp9E=", "ref": "master", - "rev": "d249a794ca47cb4e3abe494dcde80767d84b8632", - "revCount": 12, + "rev": "93af1a1af0ed7731c953ef4c415f05c1401bc094", + "revCount": 14, "type": "git", "url": "git://git.ppad.tech/pbkdf.git" }, @@ -305,11 +305,11 @@ ] }, "locked": { - "lastModified": 1738846817, - "narHash": "sha256-mjefp2TM88Psw8moP0ioHHaYOrG0n8jEN1d5hjvTE6k=", + "lastModified": 1740802961, + "narHash": "sha256-sEW9RGhoWs8BmsQ+FHowMaWgSEjJc68qB7Bg33c0yKw=", "ref": "master", - "rev": "d46ac86d87779e651b7a1d2f36ca03beaa08574d", - "revCount": 22, + "rev": "d703a7a58bad51f8271cfb9b1731e37c05202eeb", + "revCount": 26, "type": "git", "url": "git://git.ppad.tech/ripemd160.git" }, @@ -348,11 +348,11 @@ ] }, "locked": { - "lastModified": 1739709462, - "narHash": "sha256-bgdKy8Cx67DeNxIANAxFnelovj2LP4opaprkWp5KwA4=", + "lastModified": 1750336608, + "narHash": "sha256-hNTKr/dkUjiWkqg0qzCl+hVrY51Xc6lkmC3QnpBs5cg=", "ref": "master", - "rev": "0c075b2ca6b95f98924fa76b278402f089e33f71", - "revCount": 138, + "rev": "0216aa125e71d3e23f890084bb80345a7cb13e2f", + "revCount": 171, "type": "git", "url": "git://git.ppad.tech/secp256k1.git" }, @@ -379,11 +379,11 @@ ] }, "locked": { - "lastModified": 1739979751, - "narHash": "sha256-FSU4s+TtpqdrWn9DYmFprSnlD6pEcVQmsPX+m8oadvo=", + "lastModified": 1740802974, + "narHash": "sha256-GTD9UrxwMa5zY7hxzDSXjKXKUwMK4r3FBHLG0nvgapk=", "ref": "master", - "rev": "a0e34487f0c025fac1addf72b96498e6084e4e8f", - "revCount": 93, + "rev": "ab0957e305dff0243dcab11e381470585849fd20", + "revCount": 94, "type": "git", "url": "git://git.ppad.tech/sha256.git" }, @@ -410,11 +410,11 @@ ] }, "locked": { - "lastModified": 1738754198, - "narHash": "sha256-6XuhiWKKAfplrLM7kKBrmkbaJ7hKzqpzV6fnHKqO2xA=", + "lastModified": 1740802979, + "narHash": "sha256-6VAXmA1XiIT/WFcP+eFb6uK3YyfgVqIgDv3ASNIoCMs=", "ref": "master", - "rev": "2b2fb3318127985b8eb8f361d6d8cbb86f08b2ed", - "revCount": 25, + "rev": "ff165b29fb21b99749460ae7e3fdca42a85c822b", + "revCount": 28, "type": "git", "url": "git://git.ppad.tech/sha512.git" }, diff --git a/lib/Crypto/KDF/BIP39.hs b/lib/Crypto/KDF/BIP39.hs @@ -166,7 +166,7 @@ _seed wlist mnem pass = do guard (_valid wlist mnem) let salt = TE.encodeUtf8 ("mnemonic" <> ICU.nfkd pass) norm = TE.encodeUtf8 (ICU.nfkd mnem) - pure $! PBKDF.derive SHA512.hmac norm salt 2048 64 where + PBKDF.derive SHA512.hmac norm salt 2048 64 {-# INLINE _seed #-} -- | Derive a master seed from a provided mnemonic and passphrase. @@ -186,7 +186,7 @@ seed_unsafe mnem pass = do guard (length (T.words mnem) `elem` [12, 15, 18, 21, 24]) let salt = TE.encodeUtf8 ("mnemonic" <> ICU.nfkd pass) norm = TE.encodeUtf8 (ICU.nfkd mnem) - pure $! PBKDF.derive SHA512.hmac norm salt 2048 64 where + PBKDF.derive SHA512.hmac norm salt 2048 64 -- | Validate a mnemonic against the default English wordlist. -- diff --git a/test/Main.hs b/test/Main.hs @@ -1,3 +1,4 @@ +{-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} @@ -6,7 +7,6 @@ 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.Maybe as M import qualified Data.Text.ICU.Normalize2 as ICU import qualified Data.Text.IO as TIO import Test.Tasty @@ -76,12 +76,10 @@ execute wlist V.Bip39Test {..} = do mnem = bt_mnemonic seed = bt_seed xprv = bt_xprv - out_mnem = M.fromJust (BIP39._mnemonic wl entr) - giv_seed = M.fromJust (seed_fn mnem "TREZOR") - out_seed = M.fromJust (seed_fn out_mnem "TREZOR") - out_xprv = case BIP32.master out_seed of - Just hd -> BIP32.xprv hd - Nothing -> error "bang (bip32)" + Just out_mnem = BIP39._mnemonic wl entr + Just giv_seed = seed_fn mnem "TREZOR" + Just out_seed = seed_fn out_mnem "TREZOR" + Just out_xprv = BIP32.master out_seed >>= BIP32.xprv t_msg = mempty testGroup t_msg [ -- we always output (NFKD) normalized UTF8, but test inputs may not be @@ -116,12 +114,10 @@ execute_jp V.JPBip39Test {..} = do pass = jp_passphrase seed = jp_seed xprv = jp_xprv - out_mnem = M.fromJust (BIP39._mnemonic BIP39.japanese entr) - giv_seed = M.fromJust (BIP39.seed_unsafe mnem pass) - out_seed = M.fromJust (BIP39.seed_unsafe out_mnem pass) - out_xprv = case BIP32.master out_seed of - Just hd -> BIP32.xprv hd - Nothing -> error "bang (bip32, jp)" + Just out_mnem = BIP39._mnemonic BIP39.japanese entr + Just giv_seed = BIP39.seed_unsafe mnem pass + Just out_seed = BIP39.seed_unsafe out_mnem pass + Just out_xprv = BIP32.master out_seed >>= BIP32.xprv testGroup mempty [ testCase "mnemonic" $ assertEqual mempty (ICU.nfkd mnem) out_mnem , testCase "seed (from given mnemonic)" $ assertEqual mempty seed giv_seed