From 0af5a2e4ade0b032e1ec6d25c51de2ff95dd2799 Mon Sep 17 00:00:00 2001 From: iximeow Date: Thu, 27 Nov 2014 02:06:03 -0800 Subject: Rough cut of challenge 10 --- data/10.txt | 64 +++++++++++++++++++++++++++++++++++++ src/solvers/Challenge10.scala | 51 +++++++++++++++++++++++++++++ src/utils/CryptoUtils.scala | 21 ++++++++---- src/utils/FunctionUtils.scala | 2 +- src/utils/TupleUtils.scala | 5 ++- src/utils/crypto/CBCCipher.scala | 23 ++++++++++--- src/utils/crypto/CBCDecrypter.scala | 36 +++++++++++++++++++++ test/Set2Spec.scala | 4 +-- 8 files changed, 191 insertions(+), 15 deletions(-) create mode 100644 data/10.txt create mode 100644 src/solvers/Challenge10.scala create mode 100644 src/utils/crypto/CBCDecrypter.scala diff --git a/data/10.txt b/data/10.txt new file mode 100644 index 0000000..f0802a5 --- /dev/null +++ b/data/10.txt @@ -0,0 +1,64 @@ +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 diff --git a/src/solvers/Challenge10.scala b/src/solvers/Challenge10.scala new file mode 100644 index 0000000..a130106 --- /dev/null +++ b/src/solvers/Challenge10.scala @@ -0,0 +1,51 @@ +package ixee.cryptopals.solvers + +import scala.io.Source +import ixee.cryptopals.utils.ByteUtils._ +import ixee.cryptopals.utils.StreamUtils._ +import ixee.cryptopals.utils.ConversionUtils._ +import ixee.cryptopals.utils.FunctionUtils._ +import ixee.cryptopals.utils._ +import io.github.marklister.base64.Base64._ +import javax.crypto.Cipher +import javax.crypto.spec.SecretKeySpec + +object Challenge10 { + val path = "./data/10.txt" + + lazy val ciphertext = + Source + .fromFile(path) + .getLines() + .toSeq + .mkString + .toByteArray + + def run = { + val encInstance = CryptoUtils.cbcEncryptInstance( + "AES", + "YELLOW SUBMARINE".asBytes.toArray, + Stream.continually(0.toByte).take(16) + ) + + val decInstance = CryptoUtils.cbcDecryptInstance( + "AES", + "YELLOW SUBMARINE".asBytes.toArray, + Stream.continually(0.toByte).take(16) + ) + + val s = "foo bar frobnicator the quick brown fox jumps over the lazy dog".asBytes + + val enc = encInstance.enc(s) ++ encInstance.end() + val dec = decInstance.dec(enc) + println(dec.startsWith(enc)) + + val decInstance2 = CryptoUtils.cbcDecryptInstance( + "AES", + "YELLOW SUBMARINE".asBytes.toArray, + Stream.continually(0.toByte).take(16) + ) + + decInstance2.dec(ciphertext) + } +} diff --git a/src/utils/CryptoUtils.scala b/src/utils/CryptoUtils.scala index 2b4d38e..430934b 100644 --- a/src/utils/CryptoUtils.scala +++ b/src/utils/CryptoUtils.scala @@ -1,18 +1,27 @@ package ixee.cryptopals.utils -import ixee.cryptopals.utils.crypto.CBCCipher +import ixee.cryptopals.utils.crypto._ import javax.crypto.Cipher import javax.crypto.spec.SecretKeySpec object CryptoUtils { - def pkcs7pad(s: String, blockSize: Int) = { + def pkcs7pad(s: Seq[Byte], blockSize: Int): Seq[Byte] = { val padLength = blockSize - (s.length % blockSize) - s + s"${padLength.toChar}" * padLength + s ++ Stream.continually(padLength.toByte).take(padLength) } - def cbcDecryptInstance(ecbCipher: Cipher, key: SecretKeySpec): CBCCipher = { - ecbCipher.init(Cipher.DECRYPT_MODE, key) - new CBCCipher(ecbCipher) + def cbcDecryptInstance(cipherPrim: String, key: Seq[Byte], iv: Seq[Byte]): CBCDecrypter = { + val keySpec = new SecretKeySpec(key.toArray, cipherPrim) + val cipher = Cipher.getInstance(s"$cipherPrim/ECB/NoPadding") + cipher.init(Cipher.DECRYPT_MODE, keySpec) + new CBCDecrypter(cipher, iv) + } + + def cbcEncryptInstance(cipherPrim: String, key: Seq[Byte], iv: Seq[Byte]): CBCEncrypter = { + val keySpec = new SecretKeySpec(key.toArray, cipherPrim) + val cipher = Cipher.getInstance(s"$cipherPrim/ECB/NoPadding") + cipher.init(Cipher.ENCRYPT_MODE, keySpec) + new CBCEncrypter(cipher, iv) } } diff --git a/src/utils/FunctionUtils.scala b/src/utils/FunctionUtils.scala index 7456898..21ad8d0 100644 --- a/src/utils/FunctionUtils.scala +++ b/src/utils/FunctionUtils.scala @@ -4,7 +4,7 @@ object FunctionUtils { // Because doing (_ f _).tupled confuses the inferencer... def tup[A, B, C](f: (A, B) => C): ((A, B)) => C = f.tupled - def iden[A](x: A) = x + def ident[A]: A => A = { x => x } implicit class Compositor[A, B](f: A => B) { def :|[C](g: B => C): A => C = f.andThen(g) diff --git a/src/utils/TupleUtils.scala b/src/utils/TupleUtils.scala index 2a6dd6c..2318c11 100644 --- a/src/utils/TupleUtils.scala +++ b/src/utils/TupleUtils.scala @@ -1,10 +1,13 @@ package ixee.cryptopals.utils +import ixee.cryptopals.utils.FunctionUtils._ + object TupleUtils { implicit class Tuple2[A, B](t: (A, B)) { def mapAll[C, D](_1: A => C = ident, _2: B => D = ident): (C, D) = (_1(t._1), _2(t._2)) - def <-:(f: A => C + def <-:[C](f: A => C): (C, B) = (f(t._1), t._2) + def :->[D](f: B => D): (A, D) = (t._1, f(t._2)) } } diff --git a/src/utils/crypto/CBCCipher.scala b/src/utils/crypto/CBCCipher.scala index e1ece8a..d6d99ff 100644 --- a/src/utils/crypto/CBCCipher.scala +++ b/src/utils/crypto/CBCCipher.scala @@ -1,21 +1,34 @@ package ixee.cryptopals.utils.crypto import javax.crypto.Cipher +import ixee.cryptopals.utils.ConversionUtils._ +import ixee.cryptopals.utils.FunctionUtils._ +import ixee.cryptopals.utils.CryptoUtils._ +import ixee.cryptopals.utils.TupleUtils._ +import ixee.cryptopals.utils.ByteUtils._ + +class CBCEncrypter(cipher: Cipher, init: Seq[Byte]) { + val blockSize = 16 -class CBCCipher(cipher: Cipher, init: Seq[Byte]) { var state: Seq[Byte] = init var leftover: Seq[Byte] = Seq() def enc(data: Seq[Byte]): Seq[Byte] = { - (blocks, leftover) = blockized(leftover append data) - blocks.map(encBlock).reduce(_ append _) + val (blocks, newLeftover) = blockized(leftover ++ data)(blockSize) + leftover = newLeftover + blocks.map(encBlock _).foldLeft(Seq[Byte]())(_ ++ _) } def encBlock(data: Seq[Byte]): Seq[Byte] = { - state = cipher.doFinal((data xor state).toArray) + state = cipher.update((data xor state).toArray) state } + def end(): Seq[Byte] = + cipher.doFinal((pkcs7pad(leftover, blockSize) xor state).toArray) + def blockized(data: Seq[Byte])(size: Int): (Seq[Seq[Byte]], Seq[Byte]) = - data.splitAt(data.length - (data.length % size)) + groupBlocks <-: data.splitAt(data.length - (data.length % size)) + + def groupBlocks: Seq[Byte] => Seq[Seq[Byte]] = _.grouped(blockSize).toSeq } diff --git a/src/utils/crypto/CBCDecrypter.scala b/src/utils/crypto/CBCDecrypter.scala new file mode 100644 index 0000000..a6cf855 --- /dev/null +++ b/src/utils/crypto/CBCDecrypter.scala @@ -0,0 +1,36 @@ + +package ixee.cryptopals.utils.crypto + +import javax.crypto.Cipher +import ixee.cryptopals.utils.ConversionUtils._ +import ixee.cryptopals.utils.FunctionUtils._ +import ixee.cryptopals.utils.CryptoUtils._ +import ixee.cryptopals.utils.TupleUtils._ +import ixee.cryptopals.utils.ByteUtils._ + +class CBCDecrypter(cipher: Cipher, init: Seq[Byte]) { + val blockSize = 16 + + var state: Seq[Byte] = init + var leftover: Seq[Byte] = Seq() + + def dec(data: Seq[Byte]): Seq[Byte] = { + val (blocks, newLeftover) = blockized(leftover ++ data)(blockSize) + leftover = newLeftover + blocks.map(decBlock _).foldLeft(Seq[Byte]())(_ ++ _) + } + + def decBlock(data: Seq[Byte]): Seq[Byte] = { + val ret = cipher.update(data.toArray).toSeq xor state + state = data + ret + } + + def end(): Seq[Byte] = + cipher.doFinal((pkcs7pad(leftover, blockSize) xor state).toArray) + + def blockized(data: Seq[Byte])(size: Int): (Seq[Seq[Byte]], Seq[Byte]) = + groupBlocks <-: data.splitAt(data.length - (data.length % size)) + + def groupBlocks: Seq[Byte] => Seq[Seq[Byte]] = _.grouped(blockSize).toSeq +} diff --git a/test/Set2Spec.scala b/test/Set2Spec.scala index df25f57..7884b4e 100644 --- a/test/Set2Spec.scala +++ b/test/Set2Spec.scala @@ -23,12 +23,12 @@ class Set2Spec extends IxeeSpec { def challenge9spec(expected: Byte) = { val truncated = text.dropRight(expected) val padString = - CryptoUtils.pkcs7pad(truncated, 256).takeRight(expected) + CryptoUtils.pkcs7pad(truncated.toByteArray, 256).takeRight(expected) val expectedPad = s"${expected.toChar}" * expected - padString mustBe expectedPad + padString.asAscii mustBe expectedPad } for { -- cgit v1.1