BreakSingleByteXor.hs (2077B)
1 {-# LANGUAGE OverloadedStrings #-} 2 {-# LANGUAGE RecordWildCards #-} 3 4 module Main where 5 6 import qualified Cryptopals.Util as CU 7 import qualified Data.ByteString as BS 8 import qualified Data.ByteString.Base16 as B16 9 import qualified Data.Char as C 10 import Data.List (foldl') 11 import qualified Data.Text as T 12 import qualified Data.Text.IO as TIO 13 import qualified Data.Text.Encoding as TE 14 import GHC.Word (Word8) 15 import qualified Options.Applicative as O 16 import qualified System.Exit as SE 17 import qualified System.IO as SIO 18 19 data Mode = 20 Decrypt 21 | Log 22 23 data Args = Args { 24 argsInp :: T.Text 25 , argsMod :: Mode 26 } 27 28 ops :: O.Parser Args 29 ops = Args 30 <$> O.argument O.str (O.metavar "INPUT") 31 <*> O.flag Decrypt Log ( 32 O.long "log" <> O.short 'l' <> 33 O.help "log the likely enciphering byte" 34 ) 35 36 best :: BS.ByteString -> (Word8, Double, BS.ByteString) 37 best s = foldl' alg (0, CU.score s, s) [32..126] where 38 alg acc@(_, asc, _) b = 39 let xo = CU.singleByteXor b s 40 sc = CU.score xo 41 in if sc < asc 42 then (b, sc, xo) 43 else acc 44 45 decipher :: Args -> IO () 46 decipher Args {..} = do 47 let render :: Show a => a -> T.Text 48 render = T.pack . show 49 50 err = TIO.hPutStrLn SIO.stderr 51 out = TIO.hPutStrLn SIO.stdout 52 53 args = B16.decodeBase16 $ TE.encodeUtf8 argsInp 54 55 case args of 56 Left e -> do 57 err $ "cryptopals: " <> e 58 SE.exitFailure 59 60 Right s -> do 61 err $ "cryptopals: input similarity score is " <> render (CU.score s) 62 63 let (byt, bsc, b) = best s 64 65 err ( 66 "cryptopals: xor-ing with " <> render byt <> 67 " yields " <> render bsc 68 ) 69 70 err $ "cryptopals: result" 71 case argsMod of 72 Decrypt -> out . TE.decodeUtf8 . B16.encodeBase16' $ b 73 Log -> out . render $ C.chr (fromIntegral byt) 74 75 main :: IO () 76 main = do 77 let pars = O.info (O.helper <*> ops) $ 78 O.fullDesc 79 <> O.progDesc "attempt to break single-byte xor'd ciphertext" 80 <> O.header "break-single-byte-xor" 81 82 args <- O.execParser pars 83 84 decipher args 85