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