Internal.hs (4292B)
1 {-# OPTIONS_HADDOCK hide, prune #-} 2 {-# LANGUAGE BangPatterns #-} 3 {-# LANGUAGE BinaryLiterals #-} 4 {-# LANGUAGE LambdaCase #-} 5 {-# LANGUAGE OverloadedStrings #-} 6 {-# LANGUAGE ViewPatterns #-} 7 8 module Data.ByteString.Bech32.Internal ( 9 as_word5 10 , as_base32 11 , Encoding(..) 12 , create_checksum 13 , verify 14 , valid_hrp 15 ) where 16 17 import Data.Bits ((.&.)) 18 import qualified Data.Bits as B 19 import qualified Data.ByteString as BS 20 import qualified Data.ByteString.Builder as BSB 21 import qualified Data.ByteString.Builder.Extra as BE 22 import qualified Data.ByteString.Internal as BI 23 import qualified Data.ByteString.Unsafe as BU 24 import Data.Word (Word8, Word32) 25 26 fi :: (Integral a, Num b) => a -> b 27 fi = fromIntegral 28 {-# INLINE fi #-} 29 30 -- realization for small builders 31 toStrict :: BSB.Builder -> BS.ByteString 32 toStrict = BS.toStrict 33 . BE.toLazyByteStringWith (BE.safeStrategy 128 BE.smallChunkSize) mempty 34 {-# INLINE toStrict #-} 35 36 _BECH32M_CONST :: Word32 37 _BECH32M_CONST = 0x2bc830a3 38 39 bech32_charset :: BS.ByteString 40 bech32_charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" 41 42 word5 :: Word8 -> Maybe Word8 43 word5 = \case 44 113 -> pure $! 00 -- 'q' 45 112 -> pure $! 01 -- 'p' 46 122 -> pure $! 02 -- 'z' 47 114 -> pure $! 03 -- 'r' 48 121 -> pure $! 04 -- 'y' 49 57 -> pure $! 05 -- '9' 50 120 -> pure $! 06 -- 'x' 51 56 -> pure $! 07 -- '8' 52 103 -> pure $! 08 -- 'g' 53 102 -> pure $! 09 -- 'f' 54 50 -> pure $! 10 -- '2' 55 116 -> pure $! 11 -- 't' 56 118 -> pure $! 12 -- 'v' 57 100 -> pure $! 13 -- 'd' 58 119 -> pure $! 14 -- 'w' 59 48 -> pure $! 15 -- '0' 60 115 -> pure $! 16 -- 's' 61 51 -> pure $! 17 -- '3' 62 106 -> pure $! 18 -- 'j' 63 110 -> pure $! 19 -- 'n' 64 53 -> pure $! 20 -- '5' 65 52 -> pure $! 21 -- '4' 66 107 -> pure $! 22 -- 'k' 67 104 -> pure $! 23 -- 'h' 68 99 -> pure $! 24 -- 'c' 69 101 -> pure $! 25 -- 'e' 70 54 -> pure $! 26 -- '6' 71 109 -> pure $! 27 -- 'm' 72 117 -> pure $! 28 -- 'u' 73 97 -> pure $! 29 -- 'a' 74 55 -> pure $! 30 -- '7' 75 108 -> pure $! 31 -- 'l' 76 _ -> Nothing 77 {-# INLINE word5 #-} 78 79 -- base32 -> word5 80 as_word5 :: BS.ByteString -> Maybe BS.ByteString 81 as_word5 = go mempty where 82 go acc bs = case BS.uncons bs of 83 Nothing -> pure (toStrict acc) 84 Just (h, t) -> do 85 w5 <- word5 (fi h) 86 go (acc <> BSB.word8 w5) t 87 88 -- word5 -> base32 89 as_base32 :: BS.ByteString -> BS.ByteString 90 as_base32 = BS.map (BU.unsafeIndex bech32_charset . fi) 91 92 polymod :: BS.ByteString -> Word32 93 polymod = BS.foldl' alg 1 where 94 generator :: Int -> Word32 95 generator = \case 96 0 -> 0x3b6a57b2 97 1 -> 0x26508e6d 98 2 -> 0x1ea119fa 99 3 -> 0x3d4233dd 100 4 -> 0x2a1462b3 101 _ -> error "ppad-bech32: internal error (please report this as a bug!)" 102 103 alg !chk v = 104 let !b = chk `B.shiftR` 25 105 c = (chk .&. 0x1ffffff) `B.shiftL` 5 `B.xor` fi v 106 in loop_gen 0 b c 107 108 loop_gen i b !chk 109 | i > 4 = chk 110 | otherwise = 111 let sor | B.testBit (b `B.shiftR` i) 0 = generator i 112 | otherwise = 0 113 in loop_gen (succ i) b (chk `B.xor` sor) 114 115 valid_hrp :: BS.ByteString -> Bool 116 valid_hrp hrp@(BI.PS _ _ l) 117 | l == 0 || l > 83 = False 118 | otherwise = BS.all (\b -> (b > 32) && (b < 127)) hrp 119 120 hrp_expand :: BS.ByteString -> BS.ByteString 121 hrp_expand bs = toStrict 122 $ BSB.byteString (BS.map (`B.shiftR` 5) bs) 123 <> BSB.word8 0 124 <> BSB.byteString (BS.map (.&. 0b11111) bs) 125 126 data Encoding = 127 Bech32 128 | Bech32m 129 130 create_checksum :: Encoding -> BS.ByteString -> BS.ByteString -> BS.ByteString 131 create_checksum enc hrp dat = 132 let pre = hrp_expand hrp <> dat 133 pay = toStrict $ 134 BSB.byteString pre 135 <> BSB.byteString "\NUL\NUL\NUL\NUL\NUL\NUL" 136 pm = polymod pay `B.xor` case enc of 137 Bech32 -> 1 138 Bech32m -> _BECH32M_CONST 139 140 code i = (fi (pm `B.shiftR` fi i) .&. 0b11111) 141 142 in BS.map code "\EM\DC4\SI\n\ENQ\NUL" -- BS.pack [25, 20, 15, 10, 5, 0] 143 144 verify :: Encoding -> BS.ByteString -> Bool 145 verify enc b32 = case BS.elemIndexEnd 0x31 b32 of 146 Nothing -> False 147 Just idx -> 148 let (hrp, BU.unsafeDrop 1 -> dat) = BS.splitAt idx b32 149 w5s = as_word5 dat 150 in case w5s of 151 Nothing -> False 152 Just ws -> 153 let bs = hrp_expand hrp <> ws 154 in polymod bs == case enc of 155 Bech32 -> 1 156 Bech32m -> _BECH32M_CONST 157