packages feed

ppad-bip39 0.1.1 → 0.2.0

raw patch · 4 files changed

+94/−51 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Crypto.KDF.BIP39: seed_unsafe :: Text -> Text -> ByteString

Files

CHANGELOG view
@@ -1,5 +1,10 @@ # Changelog +- 0.2.0 (2025-02-27)+  * 'seed' now validates the words in the mnemonic supplied to it;+    'seed_unsafe' has been added as a function that works for any+    wordlist.+ - 0.1.1 (2025-02-27)   * Include wordlists in generated Haddocks. 
lib/Crypto/KDF/BIP39.hs view
@@ -22,6 +22,7 @@   -- * Seed derivation   , seed   , _seed+  , seed_unsafe    -- * Wordlists   , Wordlist(..)@@ -65,15 +66,17 @@ -- | Generate a BIP39 mnemonic from some entropy, using the default English --   wordlist. -----   The entropy must be at least 128 bits long, at most 256 bits long,---   and its length must be a multiple of 32 bits. Providing invalid---   entropy will result in an 'ErrorCall' exception.+--   The entropy must be at least 128 bits long and at most 256 bits+--   long. Providing invalid entropy will result in an 'ErrorCall'+--   exception. -- --   >>> import qualified System.Entropy as E --   >>> trop <- E.getEntropy 16 --   >>> mnemonic trop --   "coral maze mimic half fat breeze thought club give brass bone snake"-mnemonic :: BS.ByteString -> T.Text+mnemonic+  :: BS.ByteString -- ^ 128-256 bits of entropy+  -> T.Text mnemonic = _mnemonic english  -- | Generate a BIP39 mnemonic from some entropy, using the provided@@ -87,7 +90,10 @@ --   >>> trop <- E.getEntropy 16 --   >>> _mnemonic czech trop --   "naslepo lysina dikobraz slupka beseda rorejs ostraha kobliha napevno blahobyt kazivost jiskra"-_mnemonic :: Wordlist -> BS.ByteString -> T.Text+_mnemonic+  :: Wordlist+  -> BS.ByteString -- ^ 128-256 bits of entropy+  -> T.Text _mnemonic (Wordlist wlist) entropy@(BI.PS _ _ l)   | l < 16 = error "ppad-bip39 (mnemonic): invalid entropy length"   | l > 32 = error "ppad-bip39 (mnemonic): invalid entropy length"@@ -124,6 +130,69 @@         Nothing {-# INLINE words #-} +-- | Derive a master seed from a provided mnemonic and passphrase, where the+--   mnemonic has been generated from the default English wordlist.+--+--   The mnemonic's length and words are validated. If you want to+--   validate the mnemonic's words against a non-English wordlist, use+--   '_seed'.+--+--   >>> let mnem = "coral maze mimic half fat breeze thought club give brass bone snake"+--   >>  let pass = "hunter2"+--   >>> seed mnem pass+--   <512-bit long seed>+seed+  :: T.Text        -- ^ mnemonic+  -> T.Text        -- ^ passphrase (use e.g. "" or 'mempty' if not required)+  -> BS.ByteString -- ^ seed+seed = _seed english++-- | Derive a master seed from a provided mnemonic and passphrase, where the+--   mnemonic has been generated from an arbitrary wordlist.+--+--   The provided mnemonic is checked for validity using '_valid'.+--   Providing an invalid mnemonic will result in an 'ErrorCall'+--   exception.+--+--   >>> let mnem = "coral maze mimic half fat breeze thought club give brass bone snake"+--   >>  let pass = "hunter2"+--   >>> _seed english mnem pass+--   <512-bit long seed>+_seed+  :: Wordlist      -- ^ wordlist+  -> T.Text        -- ^ mnemonic+  -> T.Text        -- ^ passphrase (use e.g. "" or 'mempty' if not required)+  -> BS.ByteString -- ^ seed+_seed wlist mnem pass+  | not (_valid wlist mnem) =+      error "ppad-bip39 (seed): invalid mnemonic"+  | otherwise =+      let salt = TE.encodeUtf8 ("mnemonic" <> ICU.nfkd pass)+          norm = TE.encodeUtf8 (ICU.nfkd mnem)+      in  PBKDF.derive SHA512.hmac norm salt 2048 64 where+{-# INLINE _seed #-}++-- | Derive a master seed from a provided mnemonic and passphrase.+--+--   The mnemonic's length is validated, but its individual words are+--   /not/. This function thus works for every wordlist.+--+--   >>> let mnem = "coral maze mimic half fat breeze thought club give brass bone snake"+--   >>  let pass = "hunter2"+--   >>> seed_unsafe mnem pass+--   <512-bit long seed>+seed_unsafe+  :: T.Text        -- ^ mnemonic+  -> T.Text        -- ^ passphrase (use e.g. "" or 'mempty' if not required)+  -> BS.ByteString -- ^ seed+seed_unsafe mnem pass+  | length (T.words mnem) `notElem` [12, 15, 18, 21, 24] =+      error "ppad-bip39 (seed_unsafe): invalid mnemonic"+  | otherwise =+      let salt = TE.encodeUtf8 ("mnemonic" <> ICU.nfkd pass)+          norm = TE.encodeUtf8 (ICU.nfkd mnem)+      in  PBKDF.derive SHA512.hmac norm salt 2048 64 where+ -- | Validate a mnemonic against the default English wordlist. -- --   Verifies that the mnemonic has a valid length, and that every word@@ -133,7 +202,9 @@ --   True --   >>> valid "coral maze mimic half fat breeze thought club give brass bone" --   False-valid :: T.Text -> Bool+valid+  :: T.Text -- ^ mnemonic+  -> Bool   -- ^ 'True' if valid valid mnem =        length ws `elem` [12, 15, 18, 21, 24]     && all M.isJust (fmap (\word -> F.find (== word) wlist) ws)@@ -151,51 +222,15 @@ --   True --   >>> _valid chinese_simplified mnem --   False-_valid :: Wordlist -> T.Text -> Bool+_valid+  :: Wordlist+  -> T.Text   -- ^ mnemonic+  -> Bool     -- ^ 'True' if valid _valid (Wordlist wlist) mnem =        length ws `elem` [12, 15, 18, 21, 24]     && all M.isJust (fmap (\word -> F.find (== word) wlist) ws)   where     ws = T.words mnem---- | Derive a master seed from a provided mnemonic and passphrase.------   The mnemonic's length is validated, but its individual words are---   /not/. If you want to validate the mnemonic's words against a---   wordlist, use '_seed'.------   >>> let mnem = "coral maze mimic half fat breeze thought club give brass bone snake"---   >>  let pass = "hunter2"---   >>> seed mnem pass---   <512-bit long seed>-seed :: T.Text -> T.Text -> BS.ByteString-seed mnem pass-  | length (T.words mnem) `notElem` [12, 15, 18, 21, 24] =-      error "ppad-bip39 (seed): invalid mnemonic"-  | otherwise =-      let salt = TE.encodeUtf8 ("mnemonic" <> ICU.nfkd pass)-          norm = TE.encodeUtf8 (ICU.nfkd mnem)-      in  PBKDF.derive SHA512.hmac norm salt 2048 64 where---- | Derive a master seed from a provided mnemonic and passphrase, where the---   mnemonic has been generated from an arbitrary wordlist.------   The provided mnemonic is checked for validity using '_valid'.---   Providing an invalid mnemonic will result in an 'ErrorCall'---   exception.------   >>> let mnem = "coral maze mimic half fat breeze thought club give brass bone snake"---   >>  let pass = "hunter2"---   >>> _seed english mnem pass---   <512-bit long seed>-_seed :: Wordlist -> T.Text -> T.Text -> BS.ByteString-_seed wlist mnem pass-  | not (_valid wlist mnem) =-      error "ppad-bip39 (seed): invalid mnemonic"-  | otherwise =-      let salt = TE.encodeUtf8 ("mnemonic" <> ICU.nfkd pass)-          norm = TE.encodeUtf8 (ICU.nfkd mnem)-      in  PBKDF.derive SHA512.hmac norm salt 2048 64 where  -- wordlists ------------------------------------------------------------------ 
ppad-bip39.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               ppad-bip39-version:            0.1.1+version:            0.2.0 synopsis:           BIP39 mnemonic codes. license:            MIT license-file:       LICENSE
test/Main.hs view
@@ -76,8 +76,8 @@         seed = bt_seed         xprv = bt_xprv         out_mnem = BIP39._mnemonic wl entr-        giv_seed = BIP39.seed mnem "TREZOR"-        out_seed = BIP39.seed out_mnem "TREZOR"+        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)"@@ -93,6 +93,9 @@       , 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@@ -113,8 +116,8 @@       seed = jp_seed       xprv = jp_xprv       out_mnem = BIP39._mnemonic BIP39.japanese entr-      giv_seed = BIP39.seed mnem pass-      out_seed = BIP39.seed out_mnem pass+      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)"