cryptopals

Matasano's cryptopals challenges (cryptopals.com).
git clone git://git.jtobin.io/cryptopals.git
Log | Files | Refs | README | LICENSE

ByteString.hs (1349B)


      1 module Cryptopals.Util.ByteString (
      2     hamming
      3   , nhamming
      4   , panhamming
      5   , chunks
      6   , rotate
      7   ) where
      8 
      9 import qualified Data.Bits as B
     10 import qualified Data.ByteString as BS
     11 import qualified Data.List as L
     12 import qualified Data.List.NonEmpty as NE
     13 
     14 -- | Hamming distance between bytestrings.
     15 hamming :: BS.ByteString -> BS.ByteString -> Maybe Int
     16 hamming l r
     17     | BS.length l /= BS.length r = Nothing
     18     | otherwise = Just (foldr alg 0 (BS.zip l r))
     19   where
     20     ham a b = B.popCount (B.xor a b)
     21     alg = (+) . uncurry ham
     22 
     23 -- | Normalized Hamming distance between bytestrings.
     24 nhamming :: BS.ByteString -> BS.ByteString -> Maybe Double
     25 nhamming a b =
     26   let len = fromIntegral (BS.length a)
     27   in  fmap (\s -> fromIntegral s / len) (hamming a b)
     28 
     29 -- | Average pairwise normalized Hamming distance between bytestrings.
     30 panhamming:: [BS.ByteString] -> Maybe Double
     31 panhamming bs = case bs of
     32   [] -> Nothing
     33   _  ->  do
     34     ps <- sequence [nhamming h b | (h:t) <- L.tails bs, b <- t]
     35     pure $ sum ps / fromIntegral (length ps)
     36 
     37 chunks :: Int -> BS.ByteString -> [BS.ByteString]
     38 chunks size = loop mempty where
     39   loop !acc bs
     40     | BS.null bs = reverse acc
     41     | otherwise = case BS.splitAt size bs of
     42         (chunk, rest) -> loop (chunk : acc) rest
     43 
     44 rotate :: Int -> BS.ByteString -> [BS.ByteString]
     45 rotate rows = BS.transpose . chunks rows