cryptopals

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

commit ff01c61677c0e9206e7e0549fede3f3571b5c04c
parent 64357d513f00456eab1de95b0917fe73ba37aff2
Author: Jared Tobin <jared@jtobin.io>
Date:   Wed,  2 Aug 2023 09:25:12 -0230

Fix some bit rot & add CTR mode to binaries.

Diffstat:
Mdocs/s3.md | 10++++++++--
Msrc/AES.hs | 30+++++++++++++++++++++++++-----
Msrc/MT19937.hs | 2+-
3 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/docs/s3.md b/docs/s3.md @@ -77,8 +77,6 @@ padding oracle attack (for arbitrary ciphertexts): #### 3.18 -(FIXME, add binaries for these.) - CTR mode is trivial; the only thing to get right is really the specified counter format. `Cryptopals.AES.decryptCtrAES128` (or its synonym, `encryptCtrAES128`) can be used to retrieve our desired plaintext: @@ -87,6 +85,14 @@ counter format. `Cryptopals.AES.decryptCtrAES128` (or its synonym, > decryptCtrAES128 0 "YELLOW SUBMARINE" cip "Yo, VIP Let's kick it Ice, Ice, baby Ice, Ice, baby " +You can get at this from the command line via the 'ctr' arg to the 'aes' +binary: + + $ ct=$(echo "L77na/nrFsKvynd6HzOoG7GHTLXsTVu9qvY/2syLXzhPweyyMTJULu/6/kXX0KSvoOLSFQ==" | base64 -d | xxd -p | tr -d '\n') + $ key=$(echo -n "YELLOW SUBMARINE" | xxd -p) + $ aes decrypt ctr --nonce 0 "$key" "$ct" | xxd -r -p + Yo, VIP Let's kick it Ice, Ice, baby Ice, Ice, baby + #### 3.19 (and 3.20) I used the same approach as was done in question 1.6, taking the diff --git a/src/AES.hs b/src/AES.hs @@ -10,6 +10,7 @@ import qualified Data.Char as C import qualified Data.Text as T import qualified Data.Text.Encoding as TE import qualified Data.Text.IO as TIO +import GHC.Word (Word64) import qualified Options.Applicative as O import qualified System.Exit as SE import qualified System.IO as SIO @@ -21,13 +22,15 @@ data Operation = data Mode = ECB | CBC + | CTR data Args = Args { - argsOpr :: Operation - , argsMod :: Mode - , argsIv :: Maybe T.Text - , argsKey :: T.Text - , argsInp :: T.Text + argsOpr :: Operation + , argsMod :: Mode + , argsIv :: Maybe T.Text + , argsKey :: T.Text + , argsNonce :: Maybe Word64 + , argsInp :: T.Text } ops :: O.Parser Args @@ -36,6 +39,7 @@ ops = Args <*> modeParser <*> optional (O.strOption (O.long "iv" <> O.metavar "IV")) <*> O.argument O.str (O.metavar "KEY") + <*> optional (O.option O.auto (O.long "nonce" <> O.metavar "NONCE")) <*> O.argument O.str (O.metavar "INPUT") operationParser :: O.Parser Operation @@ -53,6 +57,7 @@ modeParser = O.argument mode etc where mode = O.eitherReader $ \input -> case fmap C.toLower input of "ecb" -> pure ECB "cbc" -> pure CBC + "ctr" -> pure CTR _ -> Left ("invalid mode: " <> input) etc = O.metavar "MODE" @@ -91,6 +96,13 @@ aes Args {..} = do Right iv -> out $ AES.encryptCbcAES128 iv k v + CTR -> case argsNonce of + Nothing -> do + err $ "cryptopals: must provide nonce" + SE.exitFailure + + Just n -> out $ AES.encryptCtrAES128 n k v + Decrypt -> case argsMod of ECB -> out $ AES.decryptEcbAES128 k v @@ -98,6 +110,7 @@ aes Args {..} = do Nothing -> do err $ "cryptopals: must provide IV" SE.exitFailure + Just miv -> case B16.decodeBase16 (TE.encodeUtf8 miv) of Left e -> do err $ "cryptopals: " <> e @@ -106,6 +119,13 @@ aes Args {..} = do Right iv -> out $ AES.decryptCbcAES128 k v + CTR -> case argsNonce of + Nothing -> do + err $ "cryptopals: must provide nonce" + SE.exitFailure + + Just n -> out $ AES.decryptCtrAES128 n k v + main :: IO () main = do let pars = O.info (O.helper <*> ops) $ diff --git a/src/MT19937.hs b/src/MT19937.hs @@ -23,7 +23,7 @@ ops = Args mt :: Args -> IO () mt Args {..} = do let gen = MT.seed argsSeed - (bytes, _) = MT.bytes (fromIntegral argsBytes) gen + (bytes, _) = MT.tap (fromIntegral argsBytes) gen for_ bytes $ TIO.putStrLn . T.pack . show