diff options
Diffstat (limited to 'src/utils/crypto/CbcCipher.scala')
-rw-r--r-- | src/utils/crypto/CbcCipher.scala | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/src/utils/crypto/CbcCipher.scala b/src/utils/crypto/CbcCipher.scala new file mode 100644 index 0000000..28a88cd --- /dev/null +++ b/src/utils/crypto/CbcCipher.scala @@ -0,0 +1,50 @@ +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 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 + 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 xor state).tap(_ => state = data) + + private def encBlock(data: Seq[Byte]): Seq[Byte] = + cipher.update((data xor state).toArray).tap(state = _) + + // 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) xor state).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 +} |