commit 1092b0928bfa398fbedcf7278e4baa278d5a4c52
parent 88dd2825e002d0a1e81b5657a10dbba1a2f5afaf
Author: Jared Tobin <jared@jtobin.io>
Date: Fri, 28 Jul 2023 16:19:12 -0230
Add CTR mode for AES128.
Diffstat:
3 files changed, 39 insertions(+), 2 deletions(-)
diff --git a/cryptopals.cabal b/cryptopals.cabal
@@ -30,6 +30,7 @@ library
base
, base16
, base64
+ , binary
, bytestring
, containers
, cryptonite
diff --git a/docs/s3.md b/docs/s3.md
@@ -75,3 +75,14 @@ padding oracle attack (for arbitrary ciphertexts):
"000003Cooking MC's like a pound of bacon"
"000004Burning 'em, if you ain't quick and nimble"
+#### 3.18
+
+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:
+
+ > let Right cip = B64.decodeBase64 "L77na/nrFsKvynd6HzOoG7GHTLXsTVu9qvY/2syLXzhPweyyMTJULu/6/kXX0KSvoOLSFQ=="
+ > decryptCtrAES128 0 "YELLOW SUBMARINE" cip
+ "Yo, VIP Let's kick it Ice, Ice, baby Ice, Ice, baby "
+
+
diff --git a/lib/Cryptopals/AES.hs b/lib/Cryptopals/AES.hs
@@ -1,15 +1,23 @@
module Cryptopals.AES (
encryptCbcAES128
, encryptEcbAES128
+
, decryptCbcAES128
, decryptEcbAES128
+
+ , encryptCtrAES128
+ , decryptCtrAES128
) where
-import qualified Data.ByteString as BS
-import qualified Cryptopals.Util as CU
import qualified Crypto.Cipher.AES as CAES
import qualified Crypto.Cipher.Types as CT
import qualified Crypto.Error as CE
+import qualified Cryptopals.Util as CU
+import qualified Data.Binary.Get as BG
+import qualified Data.Binary.Put as BP
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as BSL
+import GHC.Word (Word64)
initAES128 :: BS.ByteString -> CAES.AES128
initAES128 = CE.throwCryptoError . CT.cipherInit
@@ -46,3 +54,20 @@ decryptCbcAES128 key ciphertext =
then nacc
else loop b nacc (BS.splitAt 16 bs)
+encryptCtrAES128 :: Word64 -> BS.ByteString -> BS.ByteString -> BS.ByteString
+encryptCtrAES128 nonce key plaintext = loop mempty 0 bs where
+ bs = CU.chunks 16 plaintext
+ iv = BS.replicate 16 0
+ no = BP.runPut (BP.putWord64le nonce)
+
+ loop !acc !ctr cs = case cs of
+ [] -> acc
+ (h:t) ->
+ let bc = BP.runPut (BP.putWord64le ctr)
+ pt = BSL.toStrict (no <> bc)
+ ks = BS.drop 16 $ encryptCbcAES128 iv key pt
+ in loop (acc <> CU.fixedXor h ks) (ctr + 1) t
+
+decryptCtrAES128 :: Word64 -> BS.ByteString -> BS.ByteString -> BS.ByteString
+decryptCtrAES128 = encryptCtrAES128
+