Bech32.hs (2614B)
1 {-# OPTIONS_HADDOCK prune #-} 2 {-# LANGUAGE ViewPatterns #-} 3 4 -- | 5 -- Module: Data.ByteString.Bech32 6 -- Copyright: (c) 2024 Jared Tobin 7 -- License: MIT 8 -- Maintainer: Jared Tobin <jared@ppad.tech> 9 -- 10 -- The 11 -- [BIP0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) 12 -- bech32 checksummed base32 encoding, with decoding and checksum verification. 13 14 module Data.ByteString.Bech32 ( 15 -- * Encoding and Decoding 16 encode 17 , decode 18 19 -- * Checksum 20 , verify 21 ) where 22 23 import Control.Monad (guard) 24 import qualified Data.ByteString as BS 25 import qualified Data.ByteString.Char8 as B8 26 import qualified Data.ByteString.Base32 as B32 27 import qualified Data.ByteString.Bech32.Internal as BI 28 import qualified Data.ByteString.Internal as BSI 29 import qualified Data.Char as C (toLower, isLower, isAlpha) 30 31 create_checksum :: BS.ByteString -> BS.ByteString -> BS.ByteString 32 create_checksum = BI.create_checksum BI.Bech32 33 34 -- | Encode a base256 human-readable part and input as bech32. 35 -- 36 -- >>> let Just bech32 = encode "bc" "my string" 37 -- >>> bech32 38 -- "bc1d4ujqum5wf5kuecmu02w2" 39 encode 40 :: BS.ByteString -- ^ base256-encoded human-readable part 41 -> BS.ByteString -- ^ base256-encoded data part 42 -> Maybe BS.ByteString -- ^ bech32-encoded bytestring 43 encode (B8.map C.toLower -> hrp) (B32.encode -> dat) = do 44 guard (BI.valid_hrp hrp) 45 ws <- BI.as_word5 dat 46 let check = create_checksum hrp ws 47 res = BS.concat [hrp, BS.singleton 49, dat, BI.as_base32 check] 48 guard (BS.length res < 91) 49 pure res 50 51 -- | Decode a bech32-encoded 'ByteString' into its human-readable and data 52 -- parts. 53 -- 54 -- >>> decode "hi1df6x7cnfdcs8wctnyp5x2un9wed5st" 55 -- Just ("hi","jtobin was here") 56 -- >>> decode "hey1df6x7cnfdcs8wctnyp5x2un9wed5st" -- s/hi/hey 57 -- Nothing 58 decode 59 :: BS.ByteString -- ^ bech23-encoded bytestring 60 -> Maybe (BS.ByteString, BS.ByteString) -- ^ (hrp, data less checksum) 61 decode bs@(BSI.PS _ _ l) = do 62 guard (l <= 90) 63 guard (B8.all (\a -> if C.isAlpha a then C.isLower a else True) bs) 64 guard (verify bs) 65 sep <- BS.elemIndexEnd 0x31 bs 66 case BS.splitAt sep bs of 67 (hrp, raw) -> do 68 guard (BI.valid_hrp hrp) 69 guard (BS.length raw >= 6) 70 (_, BS.dropEnd 6 -> bech32dat) <- BS.uncons raw 71 dat <- B32.decode bech32dat 72 pure (hrp, dat) 73 74 -- | Verify that a bech32 string has a valid checksum. 75 -- 76 -- >>> verify "bc1d4ujqum5wf5kuecmu02w2" 77 -- True 78 -- >>> verify "bc1d4ujquw5wf5kuecmu02w2" -- s/m/w 79 -- False 80 verify 81 :: BS.ByteString -- ^ bech32-encoded bytestring 82 -> Bool 83 verify = BI.verify BI.Bech32 84