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