package ixee.cryptopals.solvers import ixee.cryptopals.utils._ import ixee.cryptopals.utils.ByteUtils._ import ixee.cryptopals.utils.FunctionUtils._ object XorDecrypt { implicit val freq = Frequencies.cornell40kSample def decryptToAscii(ciphertext: Seq[Byte])(key: Byte): String = { new String((ciphertext xor Stream.continually(key)).toArray) } def tryBestDecrypt(ciphertext: Seq[Byte]): String = { (findBestSingleByteKey _ :| decryptToAscii(ciphertext) _)(ciphertext) } def findBestSingleByteKey(ciphertext: Seq[Byte]): Byte = candidates(ciphertext) .reduceOption( (a: (Double, Int), b: (Double, Int)) => if (a._1 < b._1) a else b ).map(_._2.toByte).getOrElse(0.toByte) def candidates(ciphertext: Seq[Byte]) = (0 until 256) .map(_.toByte) .map(x => TextScorer.score(ciphertext xor Stream.continually(x))) .zipWithIndex .filter(_._1 != -1.0) def candidatesAsAscii(ciphertext: Seq[Byte]) = candidates(ciphertext).sortBy(_._1).map(x => new String((ciphertext xor Stream.continually(x._2.toByte)).toArray) ) }