using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tofvesson.Crypto
{
public abstract class BlockCipher
{
public Int32 BlockSize { get; private set; }
public BlockCipher(int blockSize)
{
this.BlockSize = blockSize;
}
public abstract byte[] Encrypt(byte[] message);
public abstract byte[] Decrypt(byte[] ciphertext);
}
public abstract class GenericCBC : BlockCipher
{
private readonly byte[] iv_e;
public byte[] IV { get => (byte[])iv_e.Clone(); }
protected readonly byte[] currentIV_e;
protected readonly byte[] currentIV_d;
protected readonly BlockCipher cipher;
protected readonly RandomProvider provider;
public GenericCBC(BlockCipher cipher, RandomProvider provider) : base(cipher.BlockSize)
{
this.cipher = cipher;
this.provider = provider;
// Generate initialization vector and set it as the current iv
iv_e = provider.GetBytes(new byte[cipher.BlockSize]);
currentIV_e = new byte[cipher.BlockSize];
currentIV_d = new byte[cipher.BlockSize];
Array.Copy(iv_e, currentIV_e, iv_e.Length);
Array.Copy(iv_e, currentIV_d, iv_e.Length);
}
protected byte[][] SplitBlocks(byte[] message)
{
byte[][] blocks = new byte[(message.Length / cipher.BlockSize) + (message.Length % cipher.BlockSize == 0 ? 0 : 1)][];
for (int i = 0; i < blocks.Length; ++i)
{
blocks[i] = message.SubArray(i * cipher.BlockSize, Math.Min((i + 1) * cipher.BlockSize, message.Length));
if (blocks[i].Length != cipher.BlockSize)
{
byte[] res = new byte[cipher.BlockSize];
Array.Copy(blocks[i], res, blocks[i].Length);
blocks[i] = res;
}
}
return blocks;
}
protected byte[] CollectBlocks(byte[][] result)
{
byte[] collected = new byte[result.Length * cipher.BlockSize];
for (int i = 0; i < result.Length; ++i) Array.Copy(result[i], 0, collected, cipher.BlockSize * i, cipher.BlockSize);
return collected;
}
// Resets the state of this CBC instance
public virtual void Reset()
{
Array.Copy(iv_e, currentIV_e, iv_e.Length);
Array.Copy(iv_e, currentIV_d, iv_e.Length);
}
}
///
/// Standard cipher block chaining implementation (not recommended, but available nonetheless)
///
public class CBC : GenericCBC
{
public CBC(BlockCipher cipher, RandomProvider provider) : base(cipher, provider)
{ }
public override byte[] Encrypt(byte[] message)
{
byte[][] blocks = SplitBlocks(message);
for (int i = 0; i < blocks.Length; ++i)
{
byte[] enc_result = cipher.Encrypt(blocks[i].XOR(currentIV_e));
Array.Copy(enc_result, currentIV_e, enc_result.Length);
Array.Copy(enc_result, blocks[i], cipher.BlockSize);
}
return CollectBlocks(blocks);
}
public override byte[] Decrypt(byte[] ciphertext)
{
// Split ciphertext into encrypted blocks
byte[][] blocks = SplitBlocks(ciphertext);
for(int i = 0; i