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 }