cryptopals

Matasano's cryptopals challenges (cryptopals.com).
Log | Files | Refs | README | LICENSE

commit 9b00748ce1fc78a38de6e8546200847a74b66463
parent 91d0db9973f11b6c292a2c9c99e67b68cb84d22a
Author: Jared Tobin <jared@jtobin.ca>
Date:   Fri,  6 Jan 2017 23:56:09 +1300

Add misc utilities.

Diffstat:
M.gitignore | 1+
Aetc/data/3.txt | 1+
Alib/b642hex/B642Hex.hs | 17+++++++++++++++++
Alib/b642hex/b642hex | 0
Alib/chunks/Chunks.hs | 31+++++++++++++++++++++++++++++++
Alib/hamming/Hamming.hs | 37+++++++++++++++++++++++++++++++++++++
Alib/rotate/Rotate.hs | 31+++++++++++++++++++++++++++++++
Alib/score_keysizes/Score.hs | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/score_keysizes/score_keysizes | 0
9 files changed, 192 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -5,3 +5,4 @@ target *.swp *.o *.hi +etc/working diff --git a/etc/data/3.txt b/etc/data/3.txt @@ -0,0 +1 @@ +1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736 diff --git a/lib/b642hex/B642Hex.hs b/lib/b642hex/B642Hex.hs @@ -0,0 +1,17 @@ +{-# LANGUAGE OverloadedStrings #-} + +import qualified Data.ByteString as B +import qualified Data.ByteString.Char8 as B8 +import qualified Data.ByteString.Base64 as B64 +import qualified Data.ByteString.Base16 as B16 +import System.IO + +main :: IO () +main = do + bs <- B.getContents + + let decoded = B64.decodeLenient bs + encoded = B16.encode decoded + + B8.hPutStrLn stdout encoded + diff --git a/lib/b642hex/b642hex b/lib/b642hex/b642hex Binary files differ. diff --git a/lib/chunks/Chunks.hs b/lib/chunks/Chunks.hs @@ -0,0 +1,31 @@ +{-# OPTIONS_GHC -Wall #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE OverloadedStrings #-} + +import Control.Error (readMay) +import qualified Data.ByteString as B +import qualified Data.ByteString.Char8 as B8 +import System.Environment +import System.IO + +-- | Split a bytestring into chunks. +chunks :: Int -> B.ByteString -> [B.ByteString] +chunks size = loop mempty where + loop !acc bs + | B.null bs = reverse acc + | otherwise = case B.splitAt size bs of + (chunk, rest) -> loop (chunk : acc) rest + +main :: IO () +main = do + args <- getArgs + + case args of + (narg:_) -> case readMay narg :: Maybe Int of + Nothing -> hPutStrLn stderr "chunks: invalid keysize" + Just size -> do + bs <- B8.getContents + let chunked = chunks size (B8.filter (/= '\n') bs) + mapM_ B8.putStrLn chunked + + _ -> putStrLn "USAGE: echo FOO | ./chunks KEYSIZE" diff --git a/lib/hamming/Hamming.hs b/lib/hamming/Hamming.hs @@ -0,0 +1,37 @@ +{-# LANGUAGE OverloadedStrings #-} + +import Data.Bits +import qualified Data.ByteString as B +import qualified Data.ByteString.Char8 as B8 +import System.Environment +import System.Exit +import System.IO + +-- | Hamming distance between bytestrings. +-- +-- Returns Nothing if bytestrings are of unequal length. +distance :: B.ByteString -> B.ByteString -> Maybe Int +distance s0 s1 + | B.length s0 /= B.length s1 = Nothing + | otherwise = Just (foldr alg 0 (B.zip s0 s1)) + where + hamming a b = popCount (xor a b) + alg = (+) . uncurry hamming + +main :: IO () +main = do + args <- getArgs + case args of + (s0:s1:_) -> do + let b0 = B8.pack s0 + b1 = B8.pack s1 + mhamming = distance b0 b1 + case mhamming of + Nothing -> do + hPutStrLn stderr "hamming: string lengths unequal" + exitFailure + + Just hamming -> print hamming + + _ -> hPutStrLn stderr "USAGE: ./hamming STRING STRING" + diff --git a/lib/rotate/Rotate.hs b/lib/rotate/Rotate.hs @@ -0,0 +1,31 @@ +{-# OPTIONS_GHC -Wall #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE OverloadedStrings #-} + +import Control.Error (readMay) +import qualified Data.ByteString as B +import qualified Data.ByteString.Char8 as B8 +import System.Environment +import System.IO + +-- | Split a bytestring into chunks. +chunks :: Int -> B.ByteString -> [B.ByteString] +chunks size = loop mempty where + loop !acc bs + | B.null bs = reverse acc + | otherwise = case B.splitAt size bs of + (chunk, rest) -> loop (chunk : acc) rest + +main :: IO () +main = do + args <- getArgs + + case args of + (narg:_) -> case readMay narg :: Maybe Int of + Nothing -> hPutStrLn stderr "rotate: invalid keysize" + Just size -> do + bs <- B8.getContents + let flipped = B.transpose $ chunks size (B8.filter (/= '\n') bs) + mapM_ B8.putStrLn flipped + + _ -> putStrLn "USAGE: echo FOO | ./rotate KEYSIZE" diff --git a/lib/score_keysizes/Score.hs b/lib/score_keysizes/Score.hs @@ -0,0 +1,74 @@ +{-# OPTIONS_GHC -Wall #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE OverloadedStrings #-} + +import Control.Error (readMay) +import Data.Bits +import qualified Data.ByteString as B +import qualified Data.ByteString.Char8 as B8 +import qualified Data.ByteString.Base64 as B64 +import qualified Data.IntPSQ as PSQ +import System.Environment + +-- | Hamming distance between bytestrings. +-- +-- Returns Nothing if bytestrings are of unequal length. +distance :: B.ByteString -> B.ByteString -> Maybe Int +distance s0 s1 + | B.length s0 /= B.length s1 = Nothing + | otherwise = Just (foldr alg 0 (B.zip s0 s1)) + where + hamming a b = popCount (xor a b) + alg = (+) . uncurry hamming + +-- | Score a keysize applied to a bytestring. +score :: Fractional a => B.ByteString -> Int -> Maybe a +score text size = do + let (chunk0, rest) = B.splitAt size text + chunk1 = B.take size rest + hamming <- distance chunk0 chunk1 + return $ fromIntegral hamming / fromIntegral size + +-- | Score keysizes 2-40 over a given bytestring. +scoreKeysizes :: B.ByteString -> PSQ.IntPSQ Double () +scoreKeysizes text = loop PSQ.empty 2 where + plain = B64.decodeLenient text + loop !acc size + | size == 40 = acc + | otherwise = case score plain size of + Nothing -> acc + Just prio -> + let nacc = PSQ.insert size prio () acc + in loop nacc (succ size) + +-- | Return the best (smallest) n keys from a queue, by key.. +best :: Ord p => Int -> PSQ.IntPSQ p v -> [(Int, p)] +best = loop mempty where + loop !acc idx queue + | idx <= 0 = reverse acc + | otherwise = case PSQ.minView queue of + Nothing -> reverse acc + Just (key, prio, _, rest) -> + let nacc = (key, prio) : acc + in loop nacc (pred idx) rest + +main :: IO () +main = do + bs <- B8.getContents + args <- getArgs + + case args of + (narg:_) -> do + let n = case readMay narg :: Maybe Int of + Nothing -> PSQ.size scored + Just val -> val + + scored = scoreKeysizes bs + top = best n scored + + render (k, v) = show k ++ ": " ++ show v + + putStrLn "keysize: score" + mapM_ (putStrLn . render) top + + _ -> putStrLn "USAGE: echo BASE64 | ./score_keysizes NUM_RESULTS" diff --git a/lib/score_keysizes/score_keysizes b/lib/score_keysizes/score_keysizes Binary files differ.