1 module dcrypt.crypto.modes.cbc; 2 3 import std.algorithm: fill; 4 import dcrypt.crypto.blockcipher; 5 import dcrypt.crypto.params.keyparameter; 6 import dcrypt.errors, dcrypt.exceptions; 7 8 9 /// test AES/CBC encryption 10 /// test vectors: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf 11 @safe 12 unittest { 13 import dcrypt.crypto.engines.aes; 14 import std.range; 15 import std.conv: text; 16 17 CBC!AES cbc; 18 19 const ubyte[] key = cast(const ubyte[])x"2b7e151628aed2a6abf7158809cf4f3c"; 20 const ubyte[] iv = cast(const ubyte[])x"000102030405060708090a0b0c0d0e0f"; 21 22 const ubyte[] plain = cast(const ubyte[])x" 23 6bc1bee22e409f96e93d7e117393172a 24 ae2d8a571e03ac9c9eb76fac45af8e51 25 30c81c46a35ce411e5fbc1191a0a52ef 26 f69f2445df4f9b17ad2b417be66c3710 27 "; 28 29 const ubyte[] expected_ciphertext = cast(const ubyte[])x" 30 7649abac8119b246cee98e9b12e9197d 31 5086cb9b507219ee95db113a917678b2 32 73bed6b8e3c1743b7116e69e22229516 33 3ff1caa1681fac09120eca307586e1a7 34 "; 35 36 37 // encryption mode 38 cbc.start(true, key, iv); 39 40 ubyte[plain.length] buf; 41 buf = plain; 42 43 foreach(block; chunks(buf[],16)) { 44 cbc.processBlock(block,block); 45 } 46 47 assert(buf == expected_ciphertext, text(cbc.name,": encryption failed")); 48 49 // decryption mode 50 cbc.start(false, key, iv); 51 52 foreach(block; chunks(buf[],16)) { 53 cbc.processBlock(block,block); 54 } 55 56 assert(buf == plain, text(cbc.name,": decryption failed")); 57 58 } 59 60 // OOP API wrapper 61 alias CBCBlockCipher(T) = BlockCipherWrapper!(CBC!T); 62 63 /** 64 * implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher. 65 */ 66 @safe 67 public struct CBC(Cipher) if(isBlockCipher!Cipher) 68 { 69 public enum blockSize = Cipher.blockSize; 70 public enum name = Cipher.name ~ "/CBC"; 71 72 private{ 73 ubyte[blockSize] cbcV; 74 ubyte[blockSize] cbcNextV; 75 ubyte[blockSize] IV; // IV as provided by user. 76 77 Cipher cipher; 78 bool forEncryption; 79 bool initialized = false; 80 } 81 82 83 /** 84 * return the underlying block cipher that we are wrapping. 85 * 86 * Returns the underlying block cipher that we are wrapping. 87 */ 88 ref Cipher getUnderlyingCipher() pure nothrow 89 { 90 return cipher; 91 } 92 93 94 /// Initialize the cipher and, possibly, the initialization vector (IV). 95 /// 96 /// Params: 97 /// forEncryption = if true the cipher is initialized for encryption, if false for decryption. 98 /// params = the key and other data required by the cipher. 99 public void start(bool forEncryption, KeyParameter keyParam) nothrow 100 in { 101 assert(keyParam !is null, "Nullpointer!"); 102 } 103 body { 104 105 if (ParametersWithIV ivParam = cast(ParametersWithIV) keyParam) 106 { 107 start(forEncryption, ivParam.getKey(), ivParam.getIV()); 108 } 109 else 110 { 111 start(forEncryption, keyParam.getKey()); 112 } 113 } 114 115 116 /// Initialize the cipher and, possibly, the initialization vector (IV). 117 /// If the cipher is already initialized a new IV can be set without the overhead 118 /// of a new key setup: init(forEncryption, null, newIV) 119 /// 120 /// Params: 121 /// forEncryption = if true the cipher is initialized for encryption, if false for decryption. 122 /// params = the key and other data required by the cipher. 123 public void start(bool forEncryption, in ubyte[] userKey, in ubyte[] iv = null) nothrow @nogc 124 in { 125 //assert(iv !is null, "CBC without IV not supported!"); 126 assert(iv is null || iv.length == blockSize, "Length ov IV does not match block size!"); 127 } 128 body { 129 130 bool oldMode = this.forEncryption; 131 this.forEncryption = forEncryption; 132 133 if(userKey is null) { 134 // possible to change iv overhead of new key setup 135 assert(initialized, "cipher not initialized"); 136 assert(forEncryption == oldMode, "Cant switch between encryption and decryption without providing a new key."); 137 138 IV[] = iv[]; 139 } else { 140 141 cipher.start(forEncryption, userKey); 142 143 if(iv !is null) { 144 IV[] = iv[]; 145 } else { 146 IV[] = 0; 147 } 148 } 149 initialized = true; 150 reset(); 151 } 152 153 /** 154 * Process one block of input from the array in and write it to 155 * the out array. 156 * 157 * Params 158 * input = the array containing the input data. 159 * output = the array the output data will be copied into. 160 * Returns: the number of bytes processed and produced. 161 */ 162 public uint processBlock(in ubyte[] input, ubyte[] output) 163 in { 164 assert(input.length == blockSize, "input.length != blockSize"); 165 assert(output.length >= blockSize, "output buffer too small"); 166 assert(initialized, "cipher not initialized"); 167 } 168 body { 169 return (forEncryption) ? encryptBlock(input, output) : decryptBlock(input, output); 170 } 171 172 /** 173 * reset the chaining vector back to the IV and reset the underlying 174 * cipher. 175 */ 176 public void reset() nothrow 177 in { 178 assert(initialized, "cipher not initialized"); 179 } 180 body { 181 cbcV[] = IV[]; 182 cbcNextV[] = 0; // fill with zeros 183 cipher.reset(); 184 } 185 186 private nothrow @nogc @safe: 187 188 /** 189 * Do the appropriate chaining step for CBC mode encryption. 190 * 191 * Params 192 * input = the array containing the input data. 193 * output = the array the output data will be copied into. 194 * Returns: the number of bytes processed and produced. 195 */ 196 uint encryptBlock(in ubyte[] input, ubyte[] output) 197 in { 198 assert(input.length >= blockSize, "input buffer too short"); 199 assert(output.length >= blockSize, "output buffer too short"); 200 assert(initialized, "cipher not initialized"); 201 } 202 body { 203 /* 204 * XOR the cbcV and the input, 205 * then encrypt the cbcV 206 */ 207 cbcV[0..blockSize] ^= input[0..blockSize]; 208 209 uint length = cipher.processBlock(cbcV,output); 210 211 /* 212 * copy ciphertext to cbcV 213 */ 214 215 cbcV[] = output[0..cbcV.length]; 216 217 return length; 218 } 219 220 /** 221 * Do the appropriate chaining step for CBC mode decryption. 222 * 223 * Params 224 * input = the array containing the input data. 225 * output = the array the output data will be copied into. 226 * Returns: the number of bytes processed and produced. 227 */ 228 uint decryptBlock(in ubyte[] input, ubyte[] output) 229 in { 230 assert(input.length >= blockSize, "input buffer too short"); 231 assert(output.length >= blockSize, "output buffer too short"); 232 assert(initialized, "cipher not initialized"); 233 } 234 body { 235 236 cbcNextV[0..blockSize] = input[0..blockSize]; 237 238 uint length = cipher.processBlock(input, output); 239 240 /* 241 * XOR the cbcV and the output 242 */ 243 output[] ^= cbcV[]; 244 245 /* 246 * swap the back up buffer into next position 247 */ 248 ubyte[] tmp; 249 250 tmp = cbcV; 251 cbcV = cbcNextV; 252 cbcNextV = tmp; 253 254 return length; 255 } 256 }