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 }