diff options
Diffstat (limited to 'src/utils')
| -rw-r--r-- | src/utils/CryptoUtils.scala | 87 | 
1 files changed, 87 insertions, 0 deletions
| diff --git a/src/utils/CryptoUtils.scala b/src/utils/CryptoUtils.scala index 7a31002..fc33220 100644 --- a/src/utils/CryptoUtils.scala +++ b/src/utils/CryptoUtils.scala @@ -1,8 +1,10 @@  package ixee.cryptopals.utils  import ixee.cryptopals.utils.crypto._ +import ixee.cryptopals.utils.TupleUtils._  import ixee.cryptopals.utils.StreamUtils._  import ixee.cryptopals.utils.FunctionUtils._ +import ixee.cryptopals.utils.ConversionUtils._  import javax.crypto.Cipher  import javax.crypto.spec.SecretKeySpec @@ -38,7 +40,92 @@ object CryptoUtils {                                //.... well very probably.      if (countDupBlocks(xs) > 0) "ECB"      else "CBC" +  } + +  def isEcb(xs: Seq[Byte]): Boolean = detectMode(xs) == "ECB" + +  def detectEcbBlockSize(encryptor: Seq[Byte] => Seq[Byte]): Int = { +    val pad = (2 to 512 by 2).map("@" * _).zipWithIndex.map { +      _.mapAll(_1 = _.asBytes, _2 = _ + 1) +    } + +    val minPadSize = pad +      .map( { encryptor(_) } <-: _ ) +      .find( { x => isEcb(x._1) }) +      .map(_._2) + +    minPadSize.get // if this was a None we have serious possibility of this not being ECB +  } + +  def extractUnknownViaEcbOracle(encrypt: Seq[Byte] => Seq[Byte]) = { +    val blockSize = detectEcbBlockSize(encrypt) + +    def rainbow(prefix: Seq[Byte]): Map[Seq[Byte], Byte] = { +      (0 to 255) +        .map(_.toByte) +        .map(prefix :+ _) +        .map(encrypt) +        .map(_.take(16).toSeq) +        .zipWithIndex +        .map(_ :-> { _.toByte } ) +        .toMap +    } + +    def probeFirstBlockAndPaddings: (Seq[Byte], Map[Int, Seq[Byte]]) = { +      def prefix(known: Seq[Byte]) = (" " * (blockSize - 1 - known.length)).asBytes +      def genRainbow(known: Seq[Byte]) = rainbow(prefix(known) ++ known) +      def firstCryptedBlock(known: Seq[Byte]) = encrypt(prefix(known)).take(blockSize).toSeq +      def nextByte(known: Seq[Byte]) = genRainbow(known)(firstCryptedBlock(known)) +      (0 until 16).foldLeft((Seq[Byte](), Map[Int, Seq[Byte]]())) { (ac, idx) => +        (ac._1 :+ nextByte(ac._1), ac._2 + (idx -> encrypt(prefix(ac._1)))) +      } +    } + +    def probeLastBlockSize: Int = { +      val baseBlockCount = encrypt(Seq[Byte]()).length +      val firstLargerCiphertext = (0 until blockSize) +        .map(" " * _).map(_.asBytes).map(encrypt) +        .zipWithIndex +        .find(_._1.length != baseBlockCount) + +        // this will always be Some(_) because +        // somewhere between 0..blockSize WILL grow the text. +      firstLargerCiphertext.get._2 +    } + +    println("Last block is " + probeLastBlockSize + " bytes") + +    val (firstBlock, ciphertexts) = probeFirstBlockAndPaddings +    /* +     * zip together cipher blocks so that they look like +     * rot0b0, rot1b0, rot2b0, rot3b0, rot4b0, rot5b0, rot6b0, rot7b0 +     * rot0b1, rot1b1, rot2b1, ... +     * +     */ +    val transposed = ciphertexts.toSeq.sortBy(_._1).map(_ :-> { x => +      val y = x.grouped(16).toSeq +      println(y.length) +      y +    }) + +//    println("Also...") +//    println(rainbow(firstBlock.tail)(transposed(1)(0))) + +    val prefix = firstBlock.tail +    val currRainbow = rainbow(prefix) +    println(ciphertexts(0).drop(16).take(16)) +    val next = currRainbow(ciphertexts(0).drop(16).take(16).toSeq) +    println("Next: " + new String(Array(next.toByte))) +    val nowPrefix = (prefix.tail :+ next) +    val r2 = rainbow(nowPrefix) +    val next2 = r2(ciphertexts(1).drop(16).take(16).toSeq) +    println("Next: " + new String(Array(next2.toByte))) +    val pref3 = (nowPrefix.tail :+ next2) +    val r3 = rainbow(pref3) +    val next3 = r3(ciphertexts(2).drop(16).take(16).toSeq) +    println("Next: " + new String(Array(next3.toByte))) +    firstBlock    }  } | 
