1 module dcrypt.blockcipher.bufferedblockcipher;
2 
3 import dcrypt.blockcipher.blockcipher;
4 import dcrypt.blockcipher.modes.ctr;
5 import std.algorithm: min;
6 
7 unittest {
8 	import dcrypt.blockcipher.aes;
9 	
10 	alias const(ubyte)[] octets;
11 
12 	IBufferedBlockCipher bbc = new BufferedBlockCipherWrapper!AES;
13 	bbc.start(true, cast(octets) x"2b7e151628aed2a6abf7158809cf4f3c");
14 
15 	octets plain = cast(octets) x"6bc1bee22e409f96e93d7e117393172a";
16 	plain ~= plain;
17 	octets cipher = cast(octets) x"3ad77bb40d7a3660a89ecaf32466ef97";
18 	cipher ~= cipher;
19 	
20 	ubyte[] output = new ubyte[32];
21 	
22 	uint len = 0;
23 	len += bbc.processBytes(plain[0..0], output[len..$]);
24 	len += bbc.processBytes(plain[0..1], output[len..$]);
25 	len += bbc.processBytes(plain[1..7], output[len..$]);
26 	len += bbc.processBytes(plain[7..14], output[len..$]);
27 	assert(len == 0);
28 	len += bbc.processBytes(plain[14..20], output[len..$]);
29 	assert(len == 16);
30 	
31 	assert(output[0..16] == cipher[0..16], "BufferedBlockCipher failed");
32 	
33 	// feed it with single bytes
34 	foreach(b; plain[20..$]) {
35 		len += bbc.processByte(b, output[len..$]);
36 	}
37 	assert(output == cipher, "BufferedBlockCipher.processByte(...) failed");
38 }
39 
40 /// test buffered AES/CTR encryption
41 /// test vectors: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
42 @safe
43 unittest {
44 	import dcrypt.blockcipher.aes;
45 	import dcrypt.blockcipher.modes.ctr;
46 	import dcrypt.blockcipher.modes.cbc;
47 	import std.range;
48 	import std.conv: text;
49 
50 	BufferedBlockCipher!(CTR!AES) cipher;
51 	
52 	const ubyte[] key = cast(const ubyte[])x"2b7e151628aed2a6abf7158809cf4f3c";
53 	const ubyte[] iv = cast(const ubyte[])x"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
54 	
55 	const ubyte[] plain = cast(const ubyte[])x"
56 		6bc1bee22e409f96e93d7e117393172a
57 		ae2d8a571e03ac9c9eb76fac45af8e51
58 		30c81c46a35ce411e5fbc1191a0a52ef
59 		f69f2445df4f9b17ad2b417be66c3710
60 	";
61 	
62 	const ubyte[] expected_ciphertext = cast(const ubyte[])x"
63 		874d6191b620e3261bef6864990db6ce
64 		9806f66b7970fdff8617187bb9fffdff
65 		5ae4df3edbd5d35e5b4f09020db03eab
66 		1e031dda2fbe03d1792170a0f3009cee
67 	";
68 	
69 	
70 	// encryption mode
71 	cipher.start(true, key, iv);
72 
73 	ubyte[plain.length] buf;
74 
75 	size_t len;
76 	len = cipher.processBytes(plain, buf);
77 	len += cipher.doFinal(buf[len..$]);
78 	assert(len == plain.length);
79 	
80 	assert(buf == expected_ciphertext, text(cipher.name,": encryption failed"));
81 	
82 	// decryption mode
83 	cipher.start(false, key, iv);
84 	
85 	len = cipher.processBytes(buf, buf);
86 	len += cipher.doFinal(buf[len..$]);
87 	assert(len == plain.length);
88 	
89 	assert(buf == plain, text(cipher.name,": decryption failed"));
90 	
91 }
92 
93 /// test buffered AES/CTR encryption with incomplete last block
94 /// test vectors: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
95 @safe
96 unittest {
97 	import dcrypt.blockcipher.aes;
98 	import dcrypt.blockcipher.modes.ctr;
99 	import dcrypt.blockcipher.modes.cbc;
100 	import std.range;
101 	import std.conv: text;
102 	
103 	BufferedBlockCipher!(CTR!AES, true) cipher; // true: allow partial block
104 	
105 	const ubyte[] key = cast(const ubyte[])x"2b7e151628aed2a6abf7158809cf4f3c";
106 	const ubyte[] iv = cast(const ubyte[])x"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
107 	
108 	const ubyte[] plain = cast(const ubyte[])x"
109 		6bc1bee22e409f96e93d7e117393172a
110 		ae2d8a571e03ac9c9eb76fac45af8e51
111 		30c81c46a35ce411e5fbc1191a0a52ef
112 		f69f2445df4f9b17
113 	";
114 	
115 	const ubyte[] expected_ciphertext = cast(const ubyte[])x"
116 		874d6191b620e3261bef6864990db6ce
117 		9806f66b7970fdff8617187bb9fffdff
118 		5ae4df3edbd5d35e5b4f09020db03eab
119 		1e031dda2fbe03d1
120 	";
121 	
122 	
123 	// encryption mode
124 	cipher.start(true, key, iv);
125 	
126 	ubyte[plain.length] buf;
127 	
128 	size_t len;
129 	len = cipher.processBytes(plain, buf);
130 	len += cipher.doFinal(buf[len..$]);
131 	assert(len == plain.length);
132 	
133 	assert(buf == expected_ciphertext, text(cipher.name,": encryption failed"));
134 	
135 	// decryption mode
136 	cipher.start(false, key, iv);
137 	
138 	len = cipher.processBytes(buf, buf);
139 	len += cipher.doFinal(buf[len..$]);
140 	assert(len == plain.length);
141 	
142 	assert(buf == plain, text(cipher.name,": decryption failed"));
143 	
144 }
145 
146 ///
147 /// test if T is a block cipher
148 ///
149 @safe
150 template isBufferedBlockCipher(T)
151 {
152 	enum bool isBufferedBlockCipher =
153 		is(T == struct) &&
154 			is(typeof(
155 					{
156 						ubyte[0] block;
157 						T bc = void;
158 						string name = bc.getAlgorithmName();
159 						uint blockSize = T.blockSize;
160 						bc.start(true, cast(const(ubyte)[]) block, cast(const(ubyte)[]) block); // init with key and iv
161 						uint len = bc.processByte(cast(ubyte)0,block);
162 						uint len = bc.processBytes(block, block);
163 						uint len = bc.doFinal(block);
164 						bc.reset();
165 					}));
166 }
167 
168 ///
169 ///	Params:
170 ///	T = a block cipher or a block cipher combined with a mode (CTR, CBC, ...)
171 ///	permitPartialBlock = tells wether the underlying cipher supports a partial last block (CTR does). default: false
172 ///
173 ///	Examples:
174 ///	BufferedBlockCipher!AES) ecbEncryption;
175 /// BufferedBlockCipher!(CTR!AES, true) ctrEncryption;
176 /// 
177 @safe
178 public struct BufferedBlockCipher(Cipher, bool permitPartialBlock = false) if(isBlockCipher!Cipher)
179 {
180 	public {
181 
182 		enum blockSize = Cipher.blockSize;
183 		enum name = Cipher.name;
184 
185 		void start(bool forEncryption, in ubyte[] key, in ubyte[] iv = null) nothrow @nogc {
186 			cipher.start(forEncryption, key, iv);
187 		}
188 
189 		void reset() nothrow {
190 			cipher.reset();
191 			buf[] = 0;
192 			bufOff = 0;
193 		}
194 
195 		/**
196 		 * takes one byte and stores it in a buffer. Only if the buffer is full it gets encrypted
197 		 * and the cipher text gets written to output.
198 		 * 
199 		 * Params:
200 		 * b	=	the byte to encrypt
201 		 * output	=	the output buffer
202 		 * 
203 		 * Returns: the number of bytes written to output. Will be 0 or BLOCKSIZE of underlying cipher.
204 		 */
205 		@nogc
206 		uint processByte(in ubyte b, ubyte[] output) nothrow
207 		in {
208 			assert(bufOff < buf.length, "bufOff can't be larger than buf.length");
209 			assert(output.length >= buf.length, "output buffer too small");
210 		}
211 		body {
212 			buf[bufOff] = b;
213 			++bufOff;
214 
215 			if(bufOff == buf.length) {
216 				bufOff = 0;
217 				return cipher.processBlock(buf, output);
218 			}
219 			return 0;
220 		}
221 
222 		/**
223 		 * encrypt or decrypt byte array
224 		 * 
225 		 * Params:
226 		 * i	=	the bytes to encrypt
227 		 * output	=	the output buffer
228 		 * 
229 		 * Returns: the number of bytes written to output. Will be 0 or BLOCKSIZE of underlying cipher.
230 		 */
231 		@nogc
232 		uint processBytes(in ubyte[] i, ubyte[] output) nothrow
233 		in {
234 			assert(output.length >= bufOff + i.length, "output buffer too small");
235 		}
236 		body {
237 			uint outLen = 0;
238 
239 			const(ubyte)[] input = i;
240 
241 			if(bufOff > 0) {
242 				// fill the buffer and process it if full
243 				uint remainingBuf = cast(uint)(buf.length)-bufOff;
244 				uint len = min(remainingBuf, input.length);
245 
246 				buf[bufOff..bufOff + len] = input[0..len];
247 				bufOff += len;
248 
249 				// drop used input bytes
250 				input = input[len..$];
251 
252 				if(bufOff == buf.length) {
253 					// block is full, process it
254 					bufOff = 0;
255 
256 					len = cipher.processBlock(buf, output);
257 					output = output[len..$];
258 					outLen += len;
259 				}
260 			}
261 
262 			while (input.length >= buf.length) {
263 				assert(bufOff == 0, "blocks not aligned");
264 
265 				uint len = cipher.processBlock(input[0..buf.length], output); 
266 				
267 				assert(len == buf.length); // this can be assumed. TODO: replace len with blockSize
268 
269 				input = input[len..$];
270 				output = output[len..$];
271 				outLen += len;
272 			}
273 
274 			// still some remaining bytes?
275 			if(input.length > 0){
276 				assert(input.length < buf.length);
277 
278 				buf[0..input.length] = input[]; // copy remaining data into buffer
279 				bufOff += cast(uint)input.length; // cast is safe, because length has to be smaller than blocksize
280 			}
281 
282 			return outLen;
283 		}
284 
285 		///
286 		/// encrypt the remaining bytes in the buffer
287 		///
288 		///	Params: output = output buffer
289 		/// Returns: number of written bytes
290 		uint doFinal(ubyte[] output)
291 		in {
292 			if(permitPartialBlock) {
293 				assert(output.length >= bufOff, "output buffer too small");
294 			}else if (bufOff > 0) {
295 				assert(output.length >= buf.length, "output buffer too small");
296 			}
297 		}
298 
299 		body {
300 			scope(success) {reset();} // ensure reset() is called after successful encryption/decryption
301 
302 			uint outLen = 0;
303 			if(bufOff != 0) {
304 				buf[bufOff..$] = 0; // don't encrypt old bytes
305 				cipher.processBlock(buf, buf);
306 
307 				static if(permitPartialBlock) {
308 					output[0..bufOff] = buf[0..bufOff]; // copy partial block
309 				} else {
310 					output[0..buf.length] = buf[]; // copy full block
311 				}
312 
313 				outLen = bufOff;
314 			}
315 			return outLen;
316 		}
317 
318 		
319 		/// Returns: the BlockCipher passed once to the constructor.
320 		ref Cipher getUnderlyingCipher() nothrow @nogc {
321 			return cipher;
322 		}
323 	}
324 
325 	protected {
326 		Cipher cipher; /// the underlying block cipher
327 		ubyte[blockSize] buf; /// buffer for incomplete blocks
328 		uint bufOff = 0; /// where the next byte get added to the buffer (i.e. buf[bufOff++] = newByte)
329 	}
330 
331 	private {
332 		invariant {
333 			// there's no reason for the offset to be larger than the buffer length
334 			assert(bufOff <= buf.length, "bufOff can't be larger than buf.length");
335 		}
336 	}
337 }
338 
339 @safe
340 public interface IBufferedBlockCipher
341 {
342 	
343 	public {
344 
345 		void start(bool forEncryption, in ubyte[] userKey, in ubyte[] iv = null) nothrow @nogc;
346 		
347 		@property
348 		string name() pure nothrow;
349 		
350 		@property
351 		uint blockSize() pure nothrow;
352 		
353 		void reset() nothrow;
354 		
355 		/**
356 		 * takes one byte and stores it in a buffer. Only if the buffer is full it gets encrypted
357 		 * and the cipher text gets written to output.
358 		 * 
359 		 * Params:
360 		 * b	=	the byte to encrypt
361 		 * output	=	the output buffer
362 		 * 
363 		 * Returns: the number of bytes written to output. Will be 0 or BLOCKSIZE of underlying cipher.
364 		 */
365 		uint processByte(in ubyte b, ubyte[] output) nothrow @nogc;
366 		
367 		/**
368 		 * Params:
369 		 * i	=	the bytes to encrypt
370 		 * output	=	the output buffer
371 		 * 
372 		 * Returns: the number of bytes written to output. Will be 0 or BLOCKSIZE of underlying cipher.
373 		 */
374 		uint processBytes(in ubyte[] i, ubyte[] output) nothrow @nogc;
375 		
376 		/**
377 		 * encrypt the remaining bytes in the buffer
378 		 */
379 		uint doFinal(ubyte[] output) nothrow @nogc;
380 		
381 	}
382 	
383 }
384 
385 /// OOP API wrapper class for BufferedBlockCipher
386 @safe
387 public class BufferedBlockCipherWrapper(Cipher) if(isBlockCipher!Cipher): IBufferedBlockCipher
388 {
389 
390 	private BufferedBlockCipher!Cipher cipher;
391 
392 	public {
393 
394 		void start(bool forEncryption, in ubyte[] userKey, in ubyte[] iv = null) nothrow @nogc {
395 			cipher.start(forEncryption, userKey, iv);
396 		}
397 		
398 		@property
399 		string name() pure nothrow {
400 			return Cipher.name;
401 		}
402 		
403 		@property
404 		uint blockSize() pure nothrow {
405 			return Cipher.blockSize;
406 		}
407 		
408 		void reset() nothrow {
409 			cipher.reset();
410 		}
411 		
412 		/**
413 		 * takes one byte and stores it in a buffer. Only if the buffer is full it gets encrypted
414 		 * and the cipher text gets written to output.
415 		 * 
416 		 * Params:
417 		 * b	=	the byte to encrypt
418 		 * output	=	the output buffer
419 		 * 
420 		 * Returns: the number of bytes written to output. Will be 0 or BLOCKSIZE of underlying cipher.
421 		 */
422 		@nogc
423 		uint processByte(in ubyte b, ubyte[] output) nothrow
424 		{
425 			return cipher.processByte(b, output);
426 		}
427 		
428 		/**
429 		 * Params:
430 		 * i	=	the bytes to encrypt
431 		 * output	=	the output buffer
432 		 * 
433 		 * Returns: the number of bytes written to output. Will be 0 or BLOCKSIZE of underlying cipher.
434 		 */
435 		@nogc
436 		uint processBytes(in ubyte[] i, ubyte[] output) nothrow
437 		{
438 			return cipher.processBytes(i, output);
439 		}
440 		
441 		/**
442 		 * encrypt the remaining bytes in the buffer
443 		 */
444 		uint doFinal(ubyte[] output)
445 		{
446 			return cipher.doFinal(output);
447 		}
448 	}
449 
450 }