commit bd86792ed179e28005ea5c858567fc177086b34a
parent 83cd6c84acd6f527a8e1fb1bf7c66e8cbacb5cf4
Author: Jared Tobin <jared@jtobin.io>
Date: Thu, 27 Feb 2025 08:21:51 +0400
test: bip39 vectors, passing for mnemonic
Diffstat:
5 files changed, 313 insertions(+), 1 deletion(-)
diff --git a/flake.lock b/flake.lock
@@ -99,6 +99,134 @@
"url": "git://git.ppad.tech/base16.git"
}
},
+ "ppad-base58": {
+ "inputs": {
+ "flake-utils": [
+ "ppad-bip32",
+ "ppad-base58",
+ "ppad-nixpkgs",
+ "flake-utils"
+ ],
+ "nixpkgs": [
+ "ppad-bip32",
+ "ppad-base58",
+ "ppad-nixpkgs",
+ "nixpkgs"
+ ],
+ "ppad-nixpkgs": [
+ "ppad-bip32",
+ "ppad-nixpkgs"
+ ],
+ "ppad-sha256": [
+ "ppad-bip32",
+ "ppad-sha256"
+ ]
+ },
+ "locked": {
+ "lastModified": 1739979602,
+ "narHash": "sha256-RrhqNPuDmKo1mG0Y2ZyaeuMuCjtIhGLLO87TaJeEDZI=",
+ "ref": "master",
+ "rev": "40e80c1b2c39c829976aab44f6d318ebe4e2e784",
+ "revCount": 22,
+ "type": "git",
+ "url": "git://git.ppad.tech/base58.git"
+ },
+ "original": {
+ "ref": "master",
+ "type": "git",
+ "url": "git://git.ppad.tech/base58.git"
+ }
+ },
+ "ppad-bip32": {
+ "inputs": {
+ "flake-utils": [
+ "ppad-bip32",
+ "ppad-nixpkgs",
+ "flake-utils"
+ ],
+ "nixpkgs": [
+ "ppad-bip32",
+ "ppad-nixpkgs",
+ "nixpkgs"
+ ],
+ "ppad-base16": [
+ "ppad-base16"
+ ],
+ "ppad-base58": "ppad-base58",
+ "ppad-nixpkgs": [
+ "ppad-nixpkgs"
+ ],
+ "ppad-ripemd160": "ppad-ripemd160",
+ "ppad-secp256k1": "ppad-secp256k1",
+ "ppad-sha256": [
+ "ppad-sha256"
+ ],
+ "ppad-sha512": [
+ "ppad-sha512"
+ ]
+ },
+ "locked": {
+ "lastModified": 1740292997,
+ "narHash": "sha256-EOmvT1aBIVfSlleDn1Qg219rF6FgHovu/ZWGgfcYfqc=",
+ "ref": "master",
+ "rev": "ce0978beadab34fd1103287a99c1e3f335f5f58a",
+ "revCount": 37,
+ "type": "git",
+ "url": "git://git.ppad.tech/bip32.git"
+ },
+ "original": {
+ "ref": "master",
+ "type": "git",
+ "url": "git://git.ppad.tech/bip32.git"
+ }
+ },
+ "ppad-hmac-drbg": {
+ "inputs": {
+ "flake-utils": [
+ "ppad-bip32",
+ "ppad-secp256k1",
+ "ppad-hmac-drbg",
+ "ppad-nixpkgs",
+ "flake-utils"
+ ],
+ "nixpkgs": [
+ "ppad-bip32",
+ "ppad-secp256k1",
+ "ppad-hmac-drbg",
+ "ppad-nixpkgs",
+ "nixpkgs"
+ ],
+ "ppad-nixpkgs": [
+ "ppad-bip32",
+ "ppad-secp256k1",
+ "ppad-nixpkgs"
+ ],
+ "ppad-sha256": [
+ "ppad-bip32",
+ "ppad-secp256k1",
+ "ppad-sha256"
+ ],
+ "ppad-sha512": [
+ "ppad-bip32",
+ "ppad-secp256k1",
+ "ppad-sha512"
+ ]
+ },
+ "locked": {
+ "lastModified": 1737297956,
+ "narHash": "sha256-3/jNY1Qd1dIYUEQSH47xJxvgg5dS6fVFWwxasgcI9OA=",
+ "ref": "master",
+ "rev": "27a88d0f011578171aee824ef838dfbf60fa6898",
+ "revCount": 43,
+ "type": "git",
+ "url": "git://git.ppad.tech/hmac-drbg.git"
+ },
+ "original": {
+ "ref": "master",
+ "type": "git",
+ "url": "git://git.ppad.tech/hmac-drbg.git"
+ }
+ },
"ppad-nixpkgs": {
"inputs": {
"flake-utils": "flake-utils",
@@ -157,6 +285,83 @@
"url": "git://git.ppad.tech/pbkdf.git"
}
},
+ "ppad-ripemd160": {
+ "inputs": {
+ "flake-utils": [
+ "ppad-bip32",
+ "ppad-ripemd160",
+ "ppad-nixpkgs",
+ "flake-utils"
+ ],
+ "nixpkgs": [
+ "ppad-bip32",
+ "ppad-ripemd160",
+ "ppad-nixpkgs",
+ "nixpkgs"
+ ],
+ "ppad-nixpkgs": [
+ "ppad-bip32",
+ "ppad-nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1738846817,
+ "narHash": "sha256-mjefp2TM88Psw8moP0ioHHaYOrG0n8jEN1d5hjvTE6k=",
+ "ref": "master",
+ "rev": "d46ac86d87779e651b7a1d2f36ca03beaa08574d",
+ "revCount": 22,
+ "type": "git",
+ "url": "git://git.ppad.tech/ripemd160.git"
+ },
+ "original": {
+ "ref": "master",
+ "type": "git",
+ "url": "git://git.ppad.tech/ripemd160.git"
+ }
+ },
+ "ppad-secp256k1": {
+ "inputs": {
+ "flake-utils": [
+ "ppad-bip32",
+ "ppad-secp256k1",
+ "ppad-nixpkgs",
+ "flake-utils"
+ ],
+ "nixpkgs": [
+ "ppad-bip32",
+ "ppad-secp256k1",
+ "ppad-nixpkgs",
+ "nixpkgs"
+ ],
+ "ppad-hmac-drbg": "ppad-hmac-drbg",
+ "ppad-nixpkgs": [
+ "ppad-bip32",
+ "ppad-nixpkgs"
+ ],
+ "ppad-sha256": [
+ "ppad-bip32",
+ "ppad-sha256"
+ ],
+ "ppad-sha512": [
+ "ppad-bip32",
+ "ppad-sha512"
+ ]
+ },
+ "locked": {
+ "lastModified": 1739709462,
+ "narHash": "sha256-bgdKy8Cx67DeNxIANAxFnelovj2LP4opaprkWp5KwA4=",
+ "ref": "master",
+ "rev": "0c075b2ca6b95f98924fa76b278402f089e33f71",
+ "revCount": 138,
+ "type": "git",
+ "url": "git://git.ppad.tech/secp256k1.git"
+ },
+ "original": {
+ "ref": "master",
+ "type": "git",
+ "url": "git://git.ppad.tech/secp256k1.git"
+ }
+ },
"ppad-sha256": {
"inputs": {
"flake-utils": [
@@ -230,6 +435,7 @@
"nixpkgs"
],
"ppad-base16": "ppad-base16",
+ "ppad-bip32": "ppad-bip32",
"ppad-nixpkgs": "ppad-nixpkgs",
"ppad-pbkdf": "ppad-pbkdf",
"ppad-sha256": "ppad-sha256",
diff --git a/flake.nix b/flake.nix
@@ -7,6 +7,15 @@
url = "git://git.ppad.tech/nixpkgs.git";
ref = "master";
};
+ ppad-bip32 = {
+ type = "git";
+ url = "git://git.ppad.tech/bip32.git";
+ ref = "master";
+ inputs.ppad-nixpkgs.follows = "ppad-nixpkgs";
+ inputs.ppad-base16.follows = "ppad-base16";
+ inputs.ppad-sha256.follows = "ppad-sha256";
+ inputs.ppad-sha512.follows = "ppad-sha512";
+ };
ppad-base16 = {
type = "git";
url = "git://git.ppad.tech/base16.git";
@@ -40,6 +49,7 @@
outputs = { self, nixpkgs, flake-utils, ppad-nixpkgs
, ppad-sha256, ppad-sha512
, ppad-base16
+ , ppad-bip32
, ppad-pbkdf
}:
flake-utils.lib.eachDefaultSystem (system:
@@ -51,6 +61,7 @@
hpkgs = pkgs.haskell.packages.ghc981.extend (new: old: {
${lib} = old.callCabal2nixWithOptions lib ./. "--enable-profiling" {};
+ ppad-bip32 = ppad-bip32.packages.${system}.default;
ppad-base16 = ppad-base16.packages.${system}.default;
ppad-sha256 = ppad-sha256.packages.${system}.default;
ppad-sha512 = ppad-sha512.packages.${system}.default;
diff --git a/ppad-bip39.cabal b/ppad-bip39.cabal
@@ -38,18 +38,24 @@ test-suite bip39-tests
default-language: Haskell2010
hs-source-dirs: test
main-is: Main.hs
+ other-modules:
+ Vectors
ghc-options:
-rtsopts -Wall -O2
build-depends:
base
+ , aeson
, array
, bytestring
, ppad-base16
+ , ppad-bip32
, ppad-bip39
, tasty
, tasty-hunit
+ , text
+ , vector
benchmark bip39-bench
type: exitcode-stdio-1.0
diff --git a/test/Main.hs b/test/Main.hs
@@ -1,4 +1,40 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+
module Main where
+import qualified Crypto.KDF.BIP39 as BIP39
+import qualified Data.ByteString as BS
+import qualified Data.Aeson as A
+import qualified Data.Text.IO as TIO
+import Test.Tasty
+import Test.Tasty.HUnit
+import qualified Vectors as V
+
main :: IO ()
-main = pure ()
+main = do
+ vectors_bip39 <- TIO.readFile "etc/vectors.json"
+ let vectors =
+ A.decodeStrictText vectors_bip39 :: Maybe V.Vectors
+ case vectors of
+ Nothing -> error "couldn't parse bip39 vectors"
+ Just vs -> defaultMain $ testGroup "ppad-bip39" [
+ bip39_tests vs
+ ]
+
+bip39_tests :: V.Vectors -> TestTree
+bip39_tests V.Vectors {..} =
+ testGroup "bip39 vectors" $
+ fmap execute v_wordlist
+
+execute :: V.Bip39Test -> TestTree
+execute V.Bip39Test {..} = do
+ let entr = bt_entropy
+ mnem = bt_mnemonic
+ seed = bt_seed
+ xprv = bt_xprv
+ BIP39.Mnemonic out = BIP39.mnemonic entr
+ t_msg = "mnemonic " <> show mnem
+ testCase t_msg $
+ assertEqual mempty mnem out
+
diff --git a/test/Vectors.hs b/test/Vectors.hs
@@ -0,0 +1,53 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+
+module Vectors (
+ Vectors(..)
+ , Bip39Test(..)
+ ) where
+
+import Data.Aeson ((.:))
+import qualified Data.Aeson as A
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Base16 as B16
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as TE
+import Data.Word (Word32, Word64)
+import qualified Data.Vector as V
+
+data Vectors = Vectors {
+ v_wordlist :: ![Bip39Test]
+ } deriving Show
+
+instance A.FromJSON Vectors where
+ parseJSON = A.withObject "Vectors" $ \m -> Vectors
+ <$> m .: "english"
+
+data Bip39Test = Bip39Test {
+ bt_entropy :: !BS.ByteString
+ , bt_mnemonic :: !BS.ByteString
+ , bt_seed :: !BS.ByteString
+ , bt_xprv :: !BS.ByteString
+ } deriving Show
+
+decodehex :: T.Text -> BS.ByteString
+decodehex t = case B16.decode (TE.encodeUtf8 t) of
+ Nothing -> error "bang (decodehex)"
+ Just bs -> bs
+
+instance A.FromJSON Bip39Test where
+ parseJSON = A.withArray "Bip39Test" $ \m ->
+ let bt_entropy = case m V.! 0 of
+ A.String t -> decodehex t
+ _ -> error "bang (entropy)"
+ bt_mnemonic = case m V.! 1 of
+ A.String t -> TE.encodeUtf8 t
+ _ -> error "bang (mnemonic)"
+ bt_seed = case m V.! 2 of
+ A.String t -> decodehex t
+ _ -> error "bang (seed)"
+ bt_xprv = case m V.! 3 of
+ A.String t -> TE.encodeUtf8 t
+ _ -> error "bang (xprv)"
+ in pure Bip39Test {..}
+