cryptopals

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

commit 1ac28233b57d87caa99986e645d5a6a31615ee48
parent 892e529ea8cd73b8c927125c0aae1ab7c842230d
Author: Jared Tobin <jared@jtobin.io>
Date:   Sun,  6 Aug 2023 19:43:19 -0230

4.26, plus some misc other fixes.

Diffstat:
Mdocs/s2.md | 30+++++++++++++++++++++++-------
Mdocs/s4.md | 57++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mlib/Cryptopals/Block/Attacks.hs | 13+++++++++++++
Mlib/Cryptopals/Stream/Attacks.hs | 17+++++++++++++++++
Msrc/AES.hs | 2+-
5 files changed, 110 insertions(+), 9 deletions(-)

diff --git a/docs/s2.md b/docs/s2.md @@ -240,10 +240,10 @@ chaining) mode proceeds as follows: xor operator + let c_0 = IV - c_1 = enc_k(c_0 + m_1) - c_2 = enc_k(c_1 + m_2) + c_1 = enc_k(c_0 + p_1) + c_2 = enc_k(c_1 + p_2) .. - c_l = enc_k(c_{l-1} + m_l) + c_l = enc_k(c_{l-1} + p_l) in ciphertext c = (c_0, c_1, c_2, .., c_l) @@ -289,9 +289,10 @@ return the following, given the malicious input: cf39e6549b264c0eb44340b5f0e3ebdc %20a%20pound%20o 214abfcb615d8c63406ee84093538051 fbacon__________ -We can see that 0x3c and 0x37 in ciphertext block 2 (i.e. the third, counting -the IV) need to be changed. Some calculation shows what we need to XOR them -by: +We can see that 0x3c and 0x37 in ciphertext block 2 (i.e. the third, +counting the IV) need to be changed. The bytes are at index 37 and 43 in +the raw ciphertext, respectively; some calculation shows what we need to +XOR them by: > showHex (ord '!' `B.xor` ord ';') mempty "1a" @@ -299,13 +300,28 @@ by: and > showHex (ord '!' `B.xor` ord '=') mempty - "`c" + "1c" so we replace 0x3c in `c_2` with `0x3c + 0x1a` and 0x37 with `0x37 + 0x1c` and pass the resulting munged ciphertext through the ";admin=true;" substring checker found in `Cryptopals.Block.Attacks.bfcChecker`: + > let cip = bfcEncrypter "AAAAA!admin!true" + > :{ + ghci| let munge = loop 0 mempty where + ghci| loop j acc !bs = case BS.uncons bs of + ghci| Nothing -> acc + ghci| Just (b, etc) + ghci| | j == 37 -> let nex = BS.snoc acc (0x3c `B.xor` 0x1a) + ghci| in loop (succ j) nex etc + ghci| | j == 43 -> let nex = BS.snoc acc (0x37 `B.xor` 0x1c) + ghci| in loop (succ j) nex etc + ghci| | otherwise -> loop (succ j) (BS.snoc acc b) etc + ghci| :} + > let munged = munge cip + > bfcChecker cip + False > bfcChecker munged True diff --git a/docs/s4.md b/docs/s4.md @@ -19,8 +19,63 @@ Cryptopals.Stream.Attacks.rawrCtrAttack implements such an "attack": pure $ ks `CU.fixedXor` cip -Which gives the expected plaintext: +which gives the expected plaintext: > fmap (BS.take 33) rawrCtrAttack "I'm back and I'm ringin' the bell" +#### 4.26 + +After making the necessary adjustments, we can generate an analogous +ciphertext via: + + > -- Cryptopals.Stream.Attacks.bfcEncrypter + > let cip = bfcEncrypter "AAAAA!admin!true" + +Encoded as hex, this one looks like: + + 911197ace68288173aeea79803ba30ff comment1=cooking + 7a45f6350fefac4ff9b4f1277500e130 %20MCs;userdata= + 843d67e24f8af8305928b16352b943e9 AAAAA!admin!true + 39e33d0551e309a752c2bb658a8ba9ee ;comment=%20like + dda6c8de9da59acc48f4d83f0d63fb8b %20a%20pound%20o + 4969b8aac52e78b701c8da0f253efe59 fbacon__________ + +Here we want to target the desired bytes themselves, not the bytes in +a previous block as was done for the attack on CBC mode. Since we know +the ciphertext and plaintext byte values, we can trivially recover the +keystream at those bytes (i.e. at indices 37 and 43). Then it's just a +matter of replacing the ciphertext bytes by the keystream bytes XOR'd by +the desired plaintext: + + > -- Cryptopals.Stream.Attacks.bfcEncrypter + > let cip = bfcEncrypter "AAAAA!admin!true" + > :{ + ghci| let munge cip = loop 0 mempty cip where + ghci| p = fi (C.ord '!') + ghci| s = fi (C.ord ';') + ghci| e = fi (C.ord '=') + ghci| loop j acc !bs = case BS.uncons bs of + ghci| Nothing -> acc + ghci| Just (b, etc) + ghci| | j == 37 -> let c = BS.index cip j + ghci| k = c `B.xor` p + ghci| vil = k `B.xor` s + ghci| nex = BS.snoc acc vil + ghci| in loop (succ j) nex etc + ghci| | j == 43 -> let c = BS.index cip j + ghci| k = c `B.xor` p + ghci| vil = k `B.xor` e + ghci| nex = BS.snoc acc vil + ghci| in loop (succ j) nex etc + ghci| | otherwise -> loop (succ j) (BS.snoc acc b) etc + ghci| :} + > let munged = munge cip + > bfcChecker cip + False + > bfcChecker munged + True + +#### 4.27 + + diff --git a/lib/Cryptopals/Block/Attacks.hs b/lib/Cryptopals/Block/Attacks.hs @@ -205,6 +205,7 @@ attackProxy oracle input = loop where else pure $ BS.drop 16 target -- bitflipping CBC + bfcEncrypter :: BS.ByteString -> BS.ByteString bfcEncrypter input = AES.encryptCbcAES128 iv consistentKey padded where iv = BS.replicate 16 0 @@ -359,3 +360,15 @@ rnBest s = loop (0, 1 / 0, s) 0 where | sc < asc -> loop (b, sc, xo) (succ b) | otherwise -> loop acc (succ b) +-- CBC key recovery w/IV=key + +bfcIvEncrypter :: BS.ByteString -> BS.ByteString +bfcIvEncrypter input = + AES.encryptCbcAES128 consistentKey consistentKey padded + where + filtered = BS.filter (`notElem` (BS.unpack ";=")) input + plaintext = "comment1=cooking%20MCs;userdata=" <> filtered <> + ";comment2=%20like%20a%20pound%20of%20bacon" + padded = CU.lpkcs7 plaintext + + diff --git a/lib/Cryptopals/Stream/Attacks.hs b/lib/Cryptopals/Stream/Attacks.hs @@ -133,3 +133,20 @@ rawrCtrAttack = do pure $ ks `CU.fixedXor` cip + +-- bitflipping CTR + +bfcEncrypter :: BS.ByteString -> BS.ByteString +bfcEncrypter input = AES.encryptCtrAES128 n k padded where + n = consistentNonce + k = consistentKey + filtered = BS.filter (`notElem` (BS.unpack ";=")) input + plaintext = "comment1=cooking%20MCs;userdata=" <> filtered <> + ";comment2=%20like%20a%20pound%20of%20bacon" + padded = CU.lpkcs7 plaintext + +bfcChecker :: BS.ByteString -> Bool +bfcChecker ciphertext = target /= mempty where + plaintext = AES.decryptCtrAES128 consistentNonce consistentKey ciphertext + (_, target) = BS.breakSubstring ";admin=true;" plaintext + diff --git a/src/AES.hs b/src/AES.hs @@ -117,7 +117,7 @@ aes Args {..} = do SE.exitFailure Right iv -> - out $ AES.decryptCbcAES128 k v + out $ AES.decryptCbcAES128 k (iv <> v) CTR -> case argsNonce of Nothing -> do