1 module dcrypt.blockcipher.blockcipher; 2 3 /// Use this to check if type is a block cipher. 4 @safe 5 template isBlockCipher(T) 6 { 7 enum bool isBlockCipher = 8 is(T == struct) && 9 is(typeof( 10 { 11 ubyte[0] block; 12 T bc = T.init; // Can define 13 string name = T.name; 14 uint blockSize = T.blockSize; 15 bc.start(true, cast(const ubyte[]) block, cast(const ubyte[]) block); // init with secret key and iv 16 uint len = bc.processBlock(cast (const ubyte[]) block, block); 17 bc.reset(); 18 })); 19 } 20 21 /// OOP API for block ciphers 22 @safe 23 public interface IBlockCipher { 24 25 26 @safe public: 27 28 /** 29 * Initialize the cipher. 30 * 31 * Params: 32 * forEncryption = if true the cipher is initialised for 33 * encryption, if false for decryption. 34 * userKey = A secret key. 35 * iv = A nonce. 36 */ 37 void start(bool forEncryption, in ubyte[] userKey, in ubyte[] iv = null) nothrow @nogc; 38 39 /** 40 * Return the name of the algorithm the cipher implements. 41 * 42 * Returns: the name of the algorithm the cipher implements. 43 */ 44 @property 45 string name() pure nothrow; 46 47 /** 48 * Return the block size for this cipher (in bytes). 49 * 50 * Returns: the block size for this cipher in bytes. 51 */ 52 @property 53 uint blockSize() pure nothrow @nogc; 54 55 /** 56 * Process one block of input from the array in and write it to 57 * the out array. 58 * 59 * Params: 60 * input = the slice containing the input data. 61 * output = the slice the output data will be copied into. 62 * Throws: IllegalStateException if the cipher isn't initialised. 63 * Returns: the number of bytes processed and produced. 64 */ 65 @nogc 66 uint processBlock(in ubyte[] input, ubyte[] output) nothrow; 67 68 /** 69 * Reset the cipher. After resetting the cipher is in the same state 70 * as it was after the last init (if there was one). 71 */ 72 @nogc 73 void reset() nothrow; 74 } 75 76 /// Wraps block ciphers into the OOP API 77 @safe 78 public class BlockCipherWrapper(T) if(isBlockCipher!T): IBlockCipher { 79 80 private T cipher; 81 82 @safe public: 83 84 /** 85 * Initialize the cipher. 86 * 87 * Params: 88 * forEncryption = if true the cipher is initialised for 89 * encryption, if false for decryption. 90 * params = the key and other data required by the cipher. 91 * 92 * Throws: IllegalArgumentException if the params argument is 93 * inappropriate. 94 */ 95 void start(bool forEncryption, in ubyte[] key, in ubyte[] iv = null) nothrow { 96 cipher.start(forEncryption, key, iv); 97 } 98 99 /** 100 * Return the name of the algorithm the cipher implements. 101 * 102 * Returns: the name of the algorithm the cipher implements. 103 */ 104 @property 105 string name() pure nothrow { 106 return cipher.name; 107 } 108 109 /** 110 * Return the block size for this cipher (in bytes). 111 * 112 * Returns: the block size for this cipher in bytes. 113 */ 114 @property 115 uint blockSize() pure nothrow @nogc { 116 return T.blockSize; 117 } 118 119 /** 120 * Process one block of input from the array in and write it to 121 * the out array. 122 * 123 * Params: 124 * input = the slice containing the input data. 125 * output = the slice the output data will be copied into. 126 * Throws: IllegalStateException if the cipher isn't initialised. 127 * Returns: the number of bytes processed and produced. 128 */ 129 uint processBlock(in ubyte[] input, ubyte[] output) nothrow @nogc { 130 return cipher.processBlock(input, output); 131 } 132 133 /** 134 * Reset the cipher. After resetting the cipher is in the same state 135 * as it was after the last init (if there was one). 136 */ 137 void reset() nothrow @nogc { 138 cipher.reset(); 139 } 140 } 141 142 version(unittest) { 143 144 // unittest helper functions 145 146 import std.format: format; 147 148 /// Runs decryption and encryption using BlockCipher bc with given keys, plaintexts, and ciphertexts 149 /// 150 /// Params: 151 /// keys = The encryption/decryption keys. 152 /// plaintexts = Plaintexts. 153 /// cipherTexts = Corresponding ciphertexts. 154 /// ivs = Initialization vectors. 155 /// 156 @safe 157 public void blockCipherTest(IBlockCipher bc, string[] keys, string[] plaintexts, string[] cipherTexts, string[] ivs = null) { 158 159 foreach (uint i, string test_key; keys) 160 { 161 ubyte[] buffer = new ubyte[bc.blockSize]; 162 163 164 const ubyte[] key = cast(const ubyte[]) test_key; 165 const (ubyte)[] iv = null; 166 if(ivs !is null) { 167 iv = cast(const (ubyte)[]) ivs[i]; 168 } 169 170 // Encryption 171 bc.start(true, key, iv); 172 bc.processBlock(cast(const ubyte[]) plaintexts[i], buffer); 173 174 assert(buffer == cipherTexts[i], 175 format("%s failed to encrypt.", bc.name)); 176 177 // Decryption 178 bc.start(false, key, iv); 179 bc.processBlock(cast(const ubyte[]) cipherTexts[i], buffer); 180 assert(buffer == plaintexts[i], 181 format("%s failed to decrypt.", bc.name)); 182 } 183 } 184 }