commit 892e529ea8cd73b8c927125c0aae1ab7c842230d
parent ff01c61677c0e9206e7e0549fede3f3571b5c04c
Author: Jared Tobin <jared@jtobin.io>
Date: Wed, 2 Aug 2023 22:14:04 -0230
Add 4.25.
Diffstat:
4 files changed, 153 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
@@ -11,4 +11,5 @@ to build binaries. Use `cabal install` to (I think) dump them in
* [Problem Set 1](docs/s1.md)
* [Problem Set 2](docs/s2.md)
* [Problem Set 3](docs/s3.md)
+* [Problem Set 4](docs/s4.md)
diff --git a/data/s4/q25_input.txt b/data/s4/q25_input.txt
@@ -0,0 +1,64 @@
+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
diff --git a/docs/s4.md b/docs/s4.md
@@ -0,0 +1,26 @@
+### Set 4
+
+#### 4.25
+
+If we can control the offset and plaintext input, then we can ask
+the oracle to encrypt a plaintext with the same length of the
+ciphertext at offset 0. *Et voilĂ *, there's our keystream, which we
+can just xor with the original ciphertext in order to decrypt it.
+Cryptopals.Stream.Attacks.rawrCtrAttack implements such an "attack":
+
+ rawrCtrAttack :: IO BS.ByteString
+ rawrCtrAttack = do
+ cip <- rawrCtrOracle (maxBound :: Int) mempty
+ let l = BS.length cip
+ p = BS.replicate l 65
+
+ new <- rawrCtrOracle 0 p
+ let ks = new `CU.fixedXor` p
+
+ pure $ ks `CU.fixedXor` cip
+
+Which gives the expected plaintext:
+
+ > fmap (BS.take 33) rawrCtrAttack
+ "I'm back and I'm ringin' the bell"
+
diff --git a/lib/Cryptopals/Stream/Attacks.hs b/lib/Cryptopals/Stream/Attacks.hs
@@ -1,18 +1,37 @@
module Cryptopals.Stream.Attacks where
import Control.Monad
+import Control.Monad.Primitive
import qualified Control.Monad.ST as ST
import qualified Data.Binary.Put as BP
import qualified Data.ByteString as BS
+import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Base64 as B64
import qualified Data.ByteString.Lazy as BSL
import qualified Data.Text as T
import qualified Data.Time.Clock.System as TS
+import qualified Cryptopals.AES as AES
import qualified Cryptopals.Stream.RNG.MT19937 as MT
import qualified Cryptopals.Util as CU
-import GHC.Word (Word16, Word8)
+import GHC.Word (Word64, Word16, Word8)
import qualified System.Random.MWC as MWC
+bytes :: PrimMonad m => Int -> MWC.Gen (PrimState m) -> m BS.ByteString
+bytes n gen = fmap BS.pack $ replicateM n (MWC.uniform gen)
+
+-- | An unknown AES key.
+consistentKey :: BS.ByteString
+consistentKey = ST.runST $ do
+ gen <- MWC.create
+ bytes 16 gen
+
+consistentNonce :: Word64
+consistentNonce = ST.runST $ do
+ gen <- MWC.create
+ MWC.uniformR (0, 0xffffffffffffffff) gen
+
+-- MT19937-related attacks
+
keystream :: Int -> MT.Gen -> BS.ByteString
keystream nb g =
let l = nb `quot` 4 + nb `rem` 4
@@ -72,3 +91,45 @@ isPwnt token = do
let g = MT.seed s
ks = keystream 16 g
pure $ token == B64.encodeBase64 ks
+
+-- CTR attacks
+
+ctrEdit
+ :: BS.ByteString
+ -> BS.ByteString
+ -> Word64
+ -> Int
+ -> BS.ByteString
+ -> BS.ByteString
+ctrEdit cip key non off new =
+ let (pre, _) = BS.splitAt off cip
+ ced = AES.encryptCtrAES128 non key new
+ in pre <> ced
+
+rawrCtrInput :: IO BS.ByteString
+rawrCtrInput = do
+ raw <- B8.readFile "data/s4/q25_input.txt"
+ let bs = B64.decodeBase64Lenient . mconcat .B8.lines $ raw
+ let pay = AES.decryptEcbAES128 "YELLOW SUBMARINE" bs
+ pure $ AES.encryptCtrAES128 consistentNonce consistentKey pay
+
+rawrCtrOracle :: Int -> BS.ByteString -> IO BS.ByteString
+rawrCtrOracle off pay = do
+ let k = consistentKey
+ n = consistentNonce
+
+ cip <- rawrCtrInput
+
+ pure $ ctrEdit cip k n off pay
+
+rawrCtrAttack :: IO BS.ByteString
+rawrCtrAttack = do
+ cip <- rawrCtrOracle (maxBound :: Int) mempty
+ let l = BS.length cip
+ p = BS.replicate l 65
+
+ new <- rawrCtrOracle 0 p
+ let ks = new `CU.fixedXor` p
+
+ pure $ ks `CU.fixedXor` cip
+