summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2014-11-28 04:22:20 -0800
committeriximeow <me@iximeow.net>2014-11-28 04:22:20 -0800
commit47888049de29336c3f753d0372b37923d8c71121 (patch)
tree1cd7d3ee2e2d6f0ed3b1357e285f87120bc50dd8
parent6268e2c446d6d54b43668d859184d6f3a779fa3a (diff)
Challenge 11
-rw-r--r--src/utils/CryptoUtils.scala22
-rw-r--r--src/utils/crypto/CbcCipher.scala (renamed from src/utils/crypto/CBCCipher.scala)2
-rw-r--r--src/utils/crypto/CipherGenerator.scala14
-rw-r--r--src/utils/crypto/EcbCipher.scala49
-rw-r--r--test/Gloria.scala33
-rw-r--r--test/Set2Spec.scala11
6 files changed, 127 insertions, 4 deletions
diff --git a/src/utils/CryptoUtils.scala b/src/utils/CryptoUtils.scala
index 54e5034..7a31002 100644
--- a/src/utils/CryptoUtils.scala
+++ b/src/utils/CryptoUtils.scala
@@ -1,6 +1,8 @@
package ixee.cryptopals.utils
import ixee.cryptopals.utils.crypto._
+import ixee.cryptopals.utils.StreamUtils._
+import ixee.cryptopals.utils.FunctionUtils._
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec
@@ -19,4 +21,24 @@ object CryptoUtils {
def cbcDecrypt(builder: CbcBuilder)(data: Seq[Byte]) =
stripPkcs7Pad(builder.decrypt.end(data))
+
+ def ecbEncrypt(builder: EcbBuilder)(data: Seq[Byte]) =
+ builder.encrypt.end(data)
+
+ def ecbDecrypt(builder: EcbBuilder)(data: Seq[Byte]) =
+ stripPkcs7Pad(builder.decrypt.end(data))
+
+ def detectMode(xs: Seq[Byte]): String = {
+ def dupBlocks(xs: Seq[Seq[Byte]]) =
+ pairsOf(xs).map(tup(_ == _)).count(_ == true)
+
+ def countDupBlocks(xs: Seq[Byte]): Int =
+ dupBlocks(xs.grouped(16).toSeq.init.toStream)
+
+ //.... well very probably.
+ if (countDupBlocks(xs) > 0) "ECB"
+ else "CBC"
+
+ }
+
}
diff --git a/src/utils/crypto/CBCCipher.scala b/src/utils/crypto/CbcCipher.scala
index 227b635..28a88cd 100644
--- a/src/utils/crypto/CBCCipher.scala
+++ b/src/utils/crypto/CbcCipher.scala
@@ -7,7 +7,7 @@ import ixee.cryptopals.utils.CryptoUtils._
import ixee.cryptopals.utils.TupleUtils._
import ixee.cryptopals.utils.ByteUtils._
-class CBCCipher(private[this] val cipher: Cipher, private[this] val iv: Seq[Byte], mode: Int) extends IxeeCipher {
+class CbcCipher(private[this] val cipher: Cipher, private[this] val iv: Seq[Byte], mode: Int) extends IxeeCipher {
val blockSize = cipher.getBlockSize
var state: Seq[Byte] = iv
diff --git a/src/utils/crypto/CipherGenerator.scala b/src/utils/crypto/CipherGenerator.scala
index e7651cb..391710b 100644
--- a/src/utils/crypto/CipherGenerator.scala
+++ b/src/utils/crypto/CipherGenerator.scala
@@ -9,8 +9,16 @@ sealed trait CipherGenerator {
case class EcbBuilder(primitives: SchemeBuilder) extends CipherGenerator {
// lol
- def encrypt = ???
- def decrypt = ???
+ def encrypt =
+ setup(Cipher.ENCRYPT_MODE)
+ def decrypt =
+ setup(Cipher.DECRYPT_MODE)
+
+ private[this] def setup(cipherMode: Int) = {
+ val cipher = primitives.cipher
+ cipher.init(cipherMode, primitives.key)
+ new EcbCipher(cipher, cipherMode)
+ }
}
case class CbcBuilder(primitives: SchemeBuilder, iv: Seq[Byte]) extends CipherGenerator {
@@ -22,6 +30,6 @@ case class CbcBuilder(primitives: SchemeBuilder, iv: Seq[Byte]) extends CipherGe
private[this] def setup(cipherMode: Int) = {
val cipher = primitives.cipher
cipher.init(cipherMode, primitives.key)
- new CBCCipher(cipher, iv, cipherMode)
+ new CbcCipher(cipher, iv, cipherMode)
}
}
diff --git a/src/utils/crypto/EcbCipher.scala b/src/utils/crypto/EcbCipher.scala
new file mode 100644
index 0000000..306d5fa
--- /dev/null
+++ b/src/utils/crypto/EcbCipher.scala
@@ -0,0 +1,49 @@
+
+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 EcbCipher(private[this] val cipher: Cipher, mode: Int) extends IxeeCipher {
+ val blockSize = cipher.getBlockSize
+
+ var leftover: Seq[Byte] = Seq()
+
+ lazy val handleBlock: Seq[Byte] => Seq[Byte] =
+ if (mode == Cipher.ENCRYPT_MODE) encBlock _
+ else decBlock _
+
+ def update(data: Seq[Byte]): Seq[Byte] = {
+ val blocks = blockized(leftover ++ data).tap(updateLeftover)._1
+ blocks.foldLeft(Seq[Byte]())(_ ++ handleBlock(_))
+ }
+
+ private def decBlock(data: Seq[Byte]): Seq[Byte] =
+ cipher.update(data.toArray).toSeq
+
+ private def encBlock(data: Seq[Byte]): Seq[Byte] =
+ cipher.update(data.toArray).toSeq
+ // wouldn't hurt to invalidate this object afterward, but meh
+ // TODO: strip padding!
+ // to do it right really requires writing decryption as its own part
+ // it's already obvious that's necessary, but to do padding stripping
+ // properly, the last block must be withheld until an end() call is made
+ // which is much different stateful behavior from encryption.
+ //
+ // in cryptoUtils for now.
+ def end(): Seq[Byte] =
+ if (mode == Cipher.DECRYPT_MODE) Seq()
+ else cipher.update(pkcs7pad(leftover, blockSize).toArray)
+
+ def blockized(data: Seq[Byte]): (Seq[Seq[Byte]], Seq[Byte]) =
+ groupBlocks <-: data.splitAt(data.length - (data.length % blockSize))
+
+ def groupBlocks: Seq[Byte] => Seq[Seq[Byte]] = _.grouped(blockSize).toSeq
+
+ def updateLeftover(pair: (Seq[Seq[Byte]], Seq[Byte])) =
+ leftover = pair._2
+}
diff --git a/test/Gloria.scala b/test/Gloria.scala
new file mode 100644
index 0000000..3141a3a
--- /dev/null
+++ b/test/Gloria.scala
@@ -0,0 +1,33 @@
+package ixee.cryptopals.test
+
+import ixee.cryptopals.utils._
+import CryptoUtils._
+import crypto.SchemeBuilder
+
+object Gloria {
+
+ val decide = new java.util.Random()
+
+ def encryptify(input: Seq[Byte]): (String, Seq[Byte]) = {
+ val mode = if (Gloria.decide.nextBoolean) "ECB" else "CBC"
+
+ val betterText =
+ Gloria.say(atLeast = 5, atMost = 10) ++ input ++ Gloria.say(atLeast = 5, atMost = 10)
+
+ val builder = SchemeBuilder("AES", Gloria.sayExactly(16))
+
+ val encryptor =
+ if (mode == "ECB") ecbEncrypt(builder.ecb) _
+ else cbcEncrypt(builder.cbc(Gloria.sayExactly(16))) _
+
+ (mode, encryptor(betterText))
+ }
+
+ def voice: Iterator[Byte] = Stream.continually(Gloria.decide.nextInt.toByte).iterator
+
+ def sayExactly(n: Int) = voice.take(n).toSeq
+
+ def say(atLeast: Int, atMost: Int): Seq[Byte] =
+ (voice.take(atLeast) ++ voice.take(Gloria.decide.nextInt(atMost - atLeast))).toSeq
+
+}
diff --git a/test/Set2Spec.scala b/test/Set2Spec.scala
index d230a18..156949b 100644
--- a/test/Set2Spec.scala
+++ b/test/Set2Spec.scala
@@ -73,6 +73,17 @@ class Set2Spec extends IxeeSpec {
}
}
+
+ "Challenge 11: ECB/CBC detection" - {
+
+ val input = "keke" * 32
+
+ val (mode, ciphertext) = Gloria.encryptify(input.asBytes)
+
+ CryptoUtils.detectMode(ciphertext) mustBe mode // ...always.
+
+ }
+
}
}