cryptopals

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

commit 6494621f70376ecb3feb2a234306dca3793709a4
parent 4b307a8b848020c4b29634f00ff97ccc8be2ab1f
Author: Jared Tobin <jared@jtobin.ca>
Date:   Sat, 26 Aug 2017 19:39:39 +1200

Clean up old stuff.

Diffstat:
M.gitignore | 1+
Mdocs/s2.md | 4++--
Dsrc/s1c1.rs | 18------------------
Dsrc/s1c2.rs | 24------------------------
Dsrc/s1c3.hs | 48------------------------------------------------
Dsrc/s1c3.rs | 54------------------------------------------------------
Dsrc/s1c4.hs | 59-----------------------------------------------------------
Dsrc/s1c5.rs | 40----------------------------------------
Dsrc/s1c6.hs | 123-------------------------------------------------------------------------------
9 files changed, 3 insertions(+), 368 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -6,3 +6,4 @@ target *.o *.hi etc/working +deprecated diff --git a/docs/s2.md b/docs/s2.md @@ -57,12 +57,12 @@ So you could do something crazy, like: else ./bin/aes_cbc --encrypt -k $AES_KEY --iv $IV; fi zcE4rONdRk04w8v4Sm8HYQ== -and then: +and then make the guess: $ echo "zcE4rONdRk04w8v4Sm8HYQ==" | ./bin/ecb_detector likely cbc -which is actually the wrong guess here. +which is actually the wrong one here. But, uh, let's not use bash for this. diff --git a/src/s1c1.rs b/src/s1c1.rs @@ -1,18 +0,0 @@ -extern crate rustc_serialize as serialize; - -// tips: -// -// always operate on raw bytes, never encoded strings -// only use hex and base64 for pretty printing - -use serialize::base64::{self, ToBase64}; -use serialize::hex::FromHex; - -fn main() { - let input = "49276d206b696c6c696e6720796f757220627261696e206c696b652061207\ - 06f69736f6e6f7573206d757368726f6f6d"; - - let result = input.from_hex().unwrap().to_base64(base64::STANDARD); - - println!("{}", result); -} diff --git a/src/s1c2.rs b/src/s1c2.rs @@ -1,24 +0,0 @@ -extern crate rustc_serialize; - -use rustc_serialize::hex::{ToHex, FromHex}; - -fn fixed_xor(target: &str, partner: &str) -> String { - assert_eq!(target.len(), partner.len()); - - let mut l = target.from_hex().unwrap(); - let r = partner.from_hex().unwrap(); - - for (lb, rb) in l.iter_mut().zip(r) { *lb ^= rb } - - l.to_hex() -} - -fn main() { - let left = "1c0111001f010100061a024b53535009181c"; - let right = "686974207468652062756c6c277320657965"; - - let result = fixed_xor(left, right); - - println!("{}", result); -} - diff --git a/src/s1c3.hs b/src/s1c3.hs @@ -1,48 +0,0 @@ -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} - -import Data.Bits -import qualified Data.ByteString as B -import qualified Data.ByteString.Char8 as B8 -import qualified Data.ByteString.Base16 as B16 -import qualified Data.Map.Strict as MS -import GHC.Word - -hash :: B.ByteString -hash = "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736" - -fromHex :: B.ByteString -> [Word8] -fromHex = B.unpack . fst . B16.decode - -tally :: Ord a => [a] -> MS.Map a Int -tally = loop MS.empty where - loop !acc [] = acc - loop !acc (x:xs) = - let nacc = case MS.lookup x acc of - Nothing -> MS.insert x 1 acc - Just _ -> MS.update (Just . succ) x acc - in loop nacc xs - -mostFrequent :: MS.Map a Int -> Maybe a -mostFrequent ms = case MS.toList ms of - [] -> Nothing - ((k, v):xs) -> Just (loop k v xs) - where - loop mk _ [] = mk - loop mk mv ((k, v):xs) = case compare v mv of - GT -> loop k v xs - _ -> loop mk mv xs - -decrypt :: B8.ByteString -> B8.ByteString -decrypt bs = case mostFrequent (tally bytes) of - Nothing -> bs - Just c -> - let xored = fmap (`xor` c) bytes - in B.pack xored - where - bytes = fromHex bs - -main :: IO () -main = do - B8.putStrLn hash - B8.putStrLn (decrypt hash) diff --git a/src/s1c3.rs b/src/s1c3.rs @@ -1,54 +0,0 @@ -extern crate rustc_serialize as serialize; - -use serialize::hex::FromHex; -use std::collections::HashMap; -use std::string::String; -use std::vec::Vec; - -const HASH: &'static str = - "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"; - -fn decode(s: &str) -> Vec<u8> { - s.from_hex().unwrap() -} - -fn tally(vec: Vec<u8>) -> HashMap<u8, u8> { - let mut hashmap = HashMap::new(); - - for byte in vec { - let count = hashmap.entry(byte).or_insert(0); - *count += 1; - } - - hashmap -} - -fn max_elem(hashmap: HashMap<u8, u8>) -> u8 { - let mut max = 0; - let mut max_index = 0; - - for (byte, count) in hashmap.iter() { - if count > &max { - max = *count; - max_index = *byte; - } - } - - max_index -} - -fn main() { - let decoded = decode(HASH); - let tallied = tally(decoded); - let max = max_elem(tallied); - - let mut i_am_a_rust_noob = decode(HASH); - - for byte in i_am_a_rust_noob.iter_mut() { *byte ^= max; } - - let decrypted = String::from_utf8(i_am_a_rust_noob).unwrap(); - println!("{}", HASH); - println!("{}", decrypted); -} - - diff --git a/src/s1c4.hs b/src/s1c4.hs @@ -1,59 +0,0 @@ -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} - -import Data.Bits -import qualified Data.ByteString as B -import qualified Data.ByteString.Char8 as B8 -import qualified Data.ByteString.Base16 as B16 -import qualified Data.Map.Strict as MS -import GHC.Word - -fromHex :: B.ByteString -> [Word8] -fromHex = B.unpack . fst . B16.decode - -tally :: Ord a => [a] -> MS.Map a Int -tally = loop MS.empty where - loop !acc [] = acc - loop !acc (x:xs) = - let nacc = case MS.lookup x acc of - Nothing -> MS.insert x 1 acc - Just _ -> MS.update (Just . succ) x acc - in loop nacc xs - -mostFrequent :: MS.Map a Int -> Maybe (a, Int) -mostFrequent ms = case MS.toList ms of - [] -> Nothing - ((k, v):xs) -> Just (loop k v xs) - where - loop mk mv [] = (mk, mv) - loop mk mv ((k, v):xs) = case compare v mv of - GT -> loop k v xs - _ -> loop mk mv xs - -decrypt :: B8.ByteString -> B8.ByteString -decrypt bs = case mostFrequent (tally bytes) of - Nothing -> bs - Just (c, _) -> - let xored = filter printable $ fmap (`xor` c) bytes - in B.pack xored - where - bytes = fromHex bs - printable c = elem c [33..126] - -prune :: [B8.ByteString] -> [B8.ByteString] -prune = filter highscoring where - highscoring string = case mostFrequent (tally (fromHex string)) of - Nothing -> False - Just (c, v) -> v > 4 - -display :: B8.ByteString -> IO () -display string = do - B8.putStrLn string - B8.putStrLn (decrypt string) - B8.putStrLn mempty - -batchDecrypt :: FilePath -> IO () -batchDecrypt file = do - strings <- fmap B8.lines $ B.readFile file - mapM_ display (prune strings) - diff --git a/src/s1c5.rs b/src/s1c5.rs @@ -1,40 +0,0 @@ -extern crate rustc_serialize as serialize; - -use serialize::hex::{ToHex}; -use std::vec::Vec; - -const STRING: &'static str = - "Burning 'em, if you ain't quick and nimble\n\ - I go crazy when I hear a cymbal"; - -fn repeating_key_xor(text: &str, key: &str) -> String { - let text_bytes = text.as_bytes(); - let key_bytes = key.as_bytes(); - - let mut xored: Vec<u8> = vec![0; text_bytes.len()]; - - for (idx, val) in text_bytes.iter().enumerate() { - let byte_idx = idx % key_bytes.len(); - xored[idx] = val ^ key_bytes[byte_idx]; - } - - xored.to_hex() -} - -fn main() { - println!("{}", STRING); - println!("{}", repeating_key_xor(STRING, "ICE")); -} - -// to read from stdin: -// -// use std::io::{self, Read}; -// -// let mut buffer = String::new(); -// -// io::stdin().read_to_string(&mut buffer) -// .expect("Couldn't read."); -// -// println!("{}", &buffer); -// println!("{}", repeating_key_xor(&buffer, "ICE")); - diff --git a/src/s1c6.hs b/src/s1c6.hs @@ -1,123 +0,0 @@ -{-# OPTIONS_GHC -Wall #-} -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TypeOperators #-} - -import Control.Error -import Data.Bits -import qualified Data.ByteString as B -import qualified Data.ByteString.Char8 as B8 -import qualified Data.ByteString.Base16 as B16 -import qualified Data.ByteString.Base64 as B64 -import qualified Data.IntPSQ as PSQ -import qualified Data.Map.Strict as MS -import GHC.Word -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 - --- | 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 - --- | More meticulously score a keysize applied to a bytestring. -altScore :: Fractional a => B.ByteString -> Int -> Maybe a -altScore text size = do - let chunked = chunks size text - leading = take 4 chunked - - chunk0 <- atMay leading 0 - chunk1 <- atMay leading 1 - chunk2 <- atMay leading 2 - chunk3 <- atMay leading 3 - - hamming0 <- distance chunk0 chunk1 - hamming1 <- distance chunk0 chunk2 - hamming2 <- distance chunk0 chunk3 - - let dsum = hamming0 + hamming1 + hamming2 - - return $ fromIntegral dsum / (3 * fromIntegral size) - --- | Score keysizes 2-40 over a given bytestring. -scoreKeysizes - :: (B.ByteString -> Int -> Maybe Double) - -> B.ByteString - -> PSQ.IntPSQ Double () -scoreKeysizes scorer 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 - --- | 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 - -tally :: Ord a => [a] -> MS.Map a Int -tally = loop MS.empty where - loop !acc [] = acc - loop !acc (x:xs) = - let nacc = case MS.lookup x acc of - Nothing -> MS.insert x 1 acc - Just count -> MS.update (Just . succ) x acc - in loop nacc xs - -mostFrequent :: MS.Map a Int -> Maybe a -mostFrequent ms = case MS.toList ms of - [] -> Nothing - ((k, v):xs) -> Just (loop k v xs) - where - loop mk _ [] = mk - loop mk mv ((k, v):xs) = case compare v mv of - GT -> loop k v xs - _ -> loop mk mv xs - -main :: IO () -main = do - raw <- B.readFile "etc/data/6.txt" - let mdecoded = B64.decode (B8.filter (/= '\n') raw) - - case mdecoded of - Left msg -> hPutStrLn stderr msg - Right decoded -> do - let hexed = B16.encode decoded - chunked = chunks 3 hexed - rotated = B.transpose chunked - unpacked = fmap B.unpack rotated - - return () - - return ()