bip39

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

commit 3b47962a90a5c3dc7c23abbe4044e6f43da91997
parent bc25954dd7207249d83438d092619d4c0e28514a
Author: Jared Tobin <jared@jtobin.io>
Date:   Wed, 26 Feb 2025 22:48:44 +0400

lib: checksum is measured in bits, not bytes

Diffstat:
Mlib/Crypto/KDF/BIP39.hs | 20++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/lib/Crypto/KDF/BIP39.hs b/lib/Crypto/KDF/BIP39.hs @@ -26,6 +26,7 @@ fi :: (Integral a, Num b) => a -> b fi = fromIntegral {-# INLINE fi #-} +-- remaining, bits pool, number of bits in pool type Acc = (BS.ByteString, Word64, Int) words :: BS.ByteString -> [BS.ByteString] @@ -34,30 +35,33 @@ words bs = L.unfoldr coalg (bs, 0, 0) where coalg :: Acc -> Maybe (BS.ByteString, Acc) coalg (etc, acc, len) | len > 10 = - let w11 = fi ((acc .>>. (len - 11)) .&. mask) - nacc = acc .&. ((1 .<<. (len - 11)) - 1) - nlen = len - 11 + let w11 = fi ((acc .>>. (len - 11)) .&. mask) -- take bits from pool + nacc = acc .&. ((1 .<<. (len - 11)) - 1) -- adjust pool + nlen = len - 11 -- track less bits word = PA.indexArray english w11 in Just (word, (etc, nacc, nlen)) | not (BS.null etc) = let next = BU.unsafeHead etc rest = BU.unsafeTail etc - nacc = (acc .<<. 8) .|. fi next - nlen = len + 8 + nacc = (acc .<<. 8) .|. fi next -- add bits to pool + nlen = len + 8 -- track additional bits in coalg (rest, nacc, nlen) | otherwise = Nothing mnemonic :: BS.ByteString -> Mnemonic mnemonic entropy@(BI.PS _ _ l) + | l `rem` 4 /= 0 = error "ppad-bip39 (mnemonic): invalid entropy length" | l < 16 = error "ppad-bip39 (mnemonic): invalid entropy length" | l > 32 = error "ppad-bip39 (mnemonic): invalid entropy length" | otherwise = - let kek = BS.take (l `quot` 4) (SHA256.hash entropy) - cat = entropy <> kek + let has = SHA256.hash entropy + h = BU.unsafeHead has + n = l `quot` 4 + kek = h .&. (0b1111_1111 .<<. (8 - n)) -- top n bits + cat = entropy <> BS.singleton kek in Mnemonic (BS.intercalate " " (words cat)) - seed :: BS.ByteString -> BS.ByteString -> BS.ByteString seed mnem pass = PBKDF.derive SHA512.hmac mnem salt 2048 64 where salt = "mnemonic" <> pass