DetectRepeatingKeyXorKeysize.hs (1909B)
1 {-# LANGUAGE BangPatterns #-} 2 {-# LANGUAGE OverloadedStrings #-} 3 {-# LANGUAGE RecordWildCards #-} 4 5 module Main where 6 7 import qualified Cryptopals.Util as CU 8 import qualified Data.ByteString as BS 9 import qualified Data.ByteString.Base64 as B64 10 import qualified Data.Text as T 11 import qualified Data.Text.IO as TIO 12 import qualified Data.Text.Encoding as TE 13 import qualified Options.Applicative as O 14 import qualified System.Exit as SE 15 import qualified System.IO as SIO 16 17 data Args = Args { argsInp :: T.Text } 18 19 ops :: O.Parser Args 20 ops = Args <$> O.argument O.str (O.metavar "INPUT") 21 22 score :: BS.ByteString -> Maybe (Double, Int) 23 score b = loop Nothing 2 where 24 loop acc siz 25 | siz == 40 = acc 26 | otherwise = 27 let sc = CU.panhamming 28 . filter (\s -> BS.length s == siz) 29 . CU.chunks siz 30 $ b 31 in case sc of 32 Nothing -> loop acc (succ siz) 33 Just s -> 34 let nacc = case acc of 35 Nothing -> Just (s, siz) 36 Just (r, _) -> if s < r 37 then Just (s, siz) 38 else acc 39 in loop nacc (succ siz) 40 41 guess :: Args -> IO () 42 guess Args {..} = do 43 let err = TIO.hPutStrLn SIO.stderr 44 45 render :: Show a => a -> T.Text 46 render = T.pack . show 47 48 s = B64.decodeBase64Lenient $ TE.encodeUtf8 argsInp 49 50 case score s of 51 Nothing -> do 52 err "cryptopals: couldn't guess keysize" 53 SE.exitFailure 54 55 Just (sc, siz) -> do 56 err ("cryptopals: keysize of " <> render siz <> 57 " yields minimum score of " <> render sc) 58 59 main :: IO () 60 main = do 61 let pars = O.info (O.helper <*> ops) $ 62 O.fullDesc 63 <> O.progDesc "guess repeating-key-xor'd keysize" 64 <> O.header "detect-repeating-key-xor-keysize" 65 66 args <- O.execParser pars 67 68 guess args 69