summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/10.txt64
-rw-r--r--src/solvers/Challenge10.scala51
-rw-r--r--src/utils/CryptoUtils.scala21
-rw-r--r--src/utils/FunctionUtils.scala2
-rw-r--r--src/utils/TupleUtils.scala5
-rw-r--r--src/utils/crypto/CBCCipher.scala23
-rw-r--r--src/utils/crypto/CBCDecrypter.scala36
-rw-r--r--test/Set2Spec.scala4
8 files changed, 191 insertions, 15 deletions
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 {