1 module dcrypt.aead.gcm.gcm;
2 
3 public import dcrypt.aead.aead;
4 import dcrypt.aead.gcm.ghash;
5 import dcrypt.aead.gcm.multiplier;
6 
7 public import dcrypt.exceptions: InvalidCipherTextException, IllegalArgumentException;
8 
9 
10 /// Implementation of the Galois/Counter mode (GCM)
11 /// as described in NIST Special Publication 800-38D
12 /// 
13 /// Standards: NIST Special Publication 800-38D
14 
15 
16 // TODO Shoup tables
17 // TODO support for uneven macSize
18 
19 //alias GCMEngine(T) = AEADCipherWrapper!(GCM!T); // would be nice but does not yet work
20 
21 import dcrypt.blockcipher.aes;
22 static assert(isAEADCipher!(GCM!AES), "GCM ist not a AEADCipher.");
23 
24 ///
25 ///	usage of OOP API:
26 ///	auto aes_gcm = new AEADCipherWrapper!(GCM!AES)();
27 ///
28 @safe
29 public struct GCM(T) if(is(T == void) || (isBlockCipher!T && T.blockSize == 16))
30 {
31 
32 	private enum OOP = is(T == void); // use OOP API
33 
34 	public enum blockSize = 16;
35 	public enum macSize = 16;
36 
37 	// if T == void: use OOP API for underlying block cipher
38 	static if(OOP) {
39 		/**
40 		 * Params:
41 		 * c = underlying BlockCipher
42 		 */
43 		public this(IBlockCipher c)
44 		in {
45 			assert(c.blockSize() == blockSize, "GCM: block size of underlying cipher must be 128 bits!");	
46 		}
47 		body {
48 			blockCipher = c;
49 		}
50 	} else {
51 		static assert(T.blockSize == blockSize, "GCM: block size of underlying cipher must be 128 bits!");
52 	}
53 
54 	private {
55 
56 		static if(OOP) {
57 			IBlockCipher blockCipher;
58 		} else {
59 			T blockCipher;	/// underlying BlockCipher
60 		}
61 
62 		GHash gHash;					/// provides the multiplication in GF(2^128) by H
63 		CircularBlockBuffer!blockSize buf;	/// stores input data before processing
64 
65 		ubyte[blockSize] Y;				/// counter
66 		ubyte[blockSize] E0;			/// E(key, Y0), needed to derive AuthTag from GHASH
67 		ubyte[blockSize] mac;			/// used to store the encrypted ghash TODO: use other buffer, e.g. E0 itself
68 		
69 		ubyte[blockSize] initialY;		/// used to reset Y
70 		
71 		bool forEncryption;				/// Tells wether we are in ecryption or decryption mode.
72 		bool initialized = false;		/// True if and only if GCM has been initialized
73 	}
74 
75 	public {
76 
77 		/// Initialize the underlying cipher.
78 		/// Params:
79 		/// forEncryption = true if we are setting up for encryption, false otherwise.
80 		/// key	= Secret key.
81 		/// nonce	= Number used only once.
82 		void start(bool forEncryption, in ubyte[] key, in ubyte[] iv) nothrow @nogc
83 		in {
84 			assert(iv !is null, "Must provide an IV.");
85 		}
86 		body {
87 			
88 			this.forEncryption = forEncryption;
89 			
90 			// init underyling cipher
91 			blockCipher.start(true, key);
92 			
93 			// init gHash
94 			ubyte[blockSize] H;
95 			H[] = 0;
96 			blockCipher.processBlock(H,H); // calculate H=E(K,0^128);
97 			
98 			gHash.init(H);
99 			
100 			// init IV
101 			if(iv.length == 12) { // 96 bit IV is optimal
102 				Y[0..iv.length] = iv[];
103 				Y[$-1] = 1;
104 			}else {
105 				gHash.updateCipherData(iv);
106 				gHash.doFinal(Y);
107 			}
108 			
109 			// generate key stream used later to encrypt ghash
110 			genNextKeyStreamBlock(E0);
111 			
112 			initialY = Y; // remember this to reset the cipher
113 			
114 			initialized = true;
115 		}
116 		
117 		static if(OOP) {
118 			/**
119 			 * Returns: the algorithm name.
120 			 */
121 			string name() pure nothrow {
122 				return blockCipher.name ~ "/GCM";
123 			}
124 		} else {
125 			public enum name = T.name~"/GCM";
126 		}
127 
128 		static if(OOP) {
129 			/**
130 			 * Returns: the cipher this object wraps.
131 			 */
132 			IBlockCipher getUnderlyingCipher() pure nothrow @nogc {
133 				return blockCipher;
134 			}
135 		} else {
136 			/**
137 			 * Returns: the cipher this object wraps.
138 			 */
139 			ref T getUnderlyingCipher() pure nothrow @nogc {
140 				return blockCipher;
141 			}
142 		}
143 		
144 		/// Process additional authenticated data.
145 		void processAADBytes(in ubyte[] aad...) nothrow @nogc 
146 		in {
147 			assert(initialized, "not initialized");
148 		}
149 		body {
150 			gHash.updateAAD(aad);
151 		}
152 
153 		/// Process a block of bytes from in putting the result into out.
154 		///
155 		/// Params:
156 		/// input = The input byte array.
157 		/// output = The output buffer the processed bytes go into.
158 		/// 
159 		/// Returns:
160 		/// Returns a slice pointing to the output data.
161 		ubyte[] processBytes(in ubyte[] input, ubyte[] output) nothrow 
162 		in {
163 			assert(initialized, "not initialized");
164 			assert(output.length >= getUpdateOutputSize(input.length), "output buffer too short");
165 		}
166 		body {
167 
168 			import std.algorithm: min;
169 
170 			size_t outputBytes = 0;
171 
172 			const(ubyte)[] iBuf = input;
173 			ubyte[] outPtr = output;
174 
175 			while(iBuf.length > 0) {
176 				if(buf.isFull()) {
177 					// encrypt one block
178 					outputBlock(outPtr);
179 					outPtr = outPtr[blockSize..$];
180 					outputBytes += blockSize;
181 				}
182 
183 				// copy max one block to the buffer
184 				size_t procLen = buf.put(iBuf);
185 				iBuf = iBuf[procLen..$];
186 			}
187 
188 			return output[0..outputBytes];
189 		}
190 		
191 
192 		/// Finish the operation. Does not append mac tag to the cipher text.
193 		/// Mac tag does NOT get verified in decryption mode.
194 		///
195 		/// Params: out = space for any resulting output data.
196 		/// Returns: number of bytes written into out.
197 		size_t finish(ubyte[] macBuf, ubyte[] output) nothrow
198 		in {
199 			assert(initialized, "not initialized");
200 
201 			assert(output.length >= buf.length, "output buffer too small");
202 			assert(macBuf.length == 16, "MAC buffer must be 16 bytes.");
203 		}
204 		body{
205 
206 			size_t outputBytes = 0;
207 
208 			//			if(!forEncryption) {
209 			//				if(buf.length < macLen) {
210 			//					throw new InvalidCipherTextException("ciphertext so short that it can't even contain the MAC");
211 			//				}
212 			//			}
213 
214 			size_t partialBlockLen = buf.length;
215 
216 			ubyte[2*blockSize] lastBlocks; // last two blocks. probably not full. last few bytes are the token.
217 
218 			
219 			// copy partial cipher data block
220 			buf.drainAll(lastBlocks);
221 
222 			assert(output.length >= partialBlockLen, "output buffer too short");
223 			// encrypt last partial block
224 			ubyte[2*blockSize] keyStream;
225 
226 			// generate two blocks of key stream
227 			genNextKeyStreamBlock(keyStream[0..blockSize]);
228 			genNextKeyStreamBlock(keyStream[blockSize..2*blockSize]);
229 
230 			output[0..partialBlockLen] = lastBlocks[0..partialBlockLen] ^ keyStream[0..partialBlockLen];
231 
232 			// update ghash
233 			gHash.updateCipherData(forEncryption ? output[0..partialBlockLen] : lastBlocks[0..partialBlockLen]);
234 
235 			output = output[partialBlockLen..$];
236 			outputBytes += partialBlockLen;
237 			
238 			// calculate the hash
239 			ubyte[16] mac;
240 			gHash.doFinal(mac);
241 
242 			mac[] ^= E0[]; // calculate the token
243 
244 			macBuf[0..16] = mac[];
245 
246 			return outputBytes;
247 		}
248 
249 		/// Returns: Return the size of the output buffer required for a processBytes an input of len bytes.
250 		size_t getUpdateOutputSize(size_t len) nothrow @nogc pure const {
251 			size_t total = len + buf.length;
252 			//return (total + blockSize - 1) && (~blockSize+1);
253 			return total - (total % blockSize);
254 		}
255 		
256 
257 		/// Returns: Return the size of the output buffer required for a processBytes plus a finish with an input of len bytes.
258 		size_t getOutputSize(size_t len) nothrow @nogc pure const {
259 			return len;
260 		}
261 
262 		/// Reset the cipher. After resetting the cipher is in the same state
263 		/// as it was after the last init (if there was one).
264 		void reset() nothrow 
265 		{
266 			gHash.reset();
267 			buf.reset();
268 
269 			Y = initialY;
270 			blockCipher.reset();
271 		}
272 	}
273 
274 	
275 	private nothrow @safe @nogc {
276 
277 		/**
278 		 * generates the next key stream block by incrementing the counter
279 		 * and encrypting it.
280 		 * 
281 		 * bufOff is set to 0
282 		 */
283 		void genNextKeyStreamBlock(ubyte[] buf)
284 		in {
285 			assert(buf.length == blockSize);
286 			//assert(keyStreamBufOff == BLOCKSIZE, "not yet ready to generate next block");
287 		}
288 		body {
289 			blockCipher.processBlock(Y,buf);
290 			incrCounter();
291 		}
292 
293 		/**
294 		 * encrypt or decrypt a block and write it to output
295 		 * update GHash
296 		 */
297 		void outputBlock(ubyte[] output)
298 		in {
299 			assert(output.length >= blockSize, "output buffer too short");
300 			assert(buf.length >= blockSize, "not enough data in buffer");
301 		}
302 		body {
303 			ubyte[blockSize] keyStream;
304 			ubyte[blockSize] inputBuf;
305 			genNextKeyStreamBlock(keyStream);
306 
307 			buf.drainBlock(inputBuf);
308 
309 			// encrypt the buffer
310 			output[0..blockSize] = keyStream[0..blockSize] ^ inputBuf[0..blockSize];
311 
312 			// update gHash
313 			gHash.updateCipherData(forEncryption ? output[0..blockSize] : inputBuf[0..blockSize]);
314 		}
315 
316 		/** 
317 		 * increment Y by 1
318 		 * treats rightmost 32 bits as uint, lsb on the right
319 		 */
320 		void incrCounter() {
321 			for(uint i = blockSize -1; i >= blockSize-4; --i) {
322 				if(++Y[i] != 0) {
323 					break;
324 				}
325 				// increment next element on overflow of the previous
326 			}
327 		}
328 
329 	}
330 }
331 
332 /// Test with test vectors from
333 /// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
334 /// section 2.2.1
335 unittest {
336 	import dcrypt.blockcipher.aes;
337 
338 	alias const(ubyte)[] octets;
339 
340 	octets key = cast(octets)x"AD7A2BD03EAC835A6F620FDCB506B345";
341 	octets iv = cast(octets)x"12153524C0895E81B2C28465"; // 96 bits
342 
343 	GCM!AES gcm;
344 	gcm.start(true, key, iv);
345 
346 	ubyte[48] output;
347 	ubyte[] oBuf = output;
348 	size_t outLen;
349 
350 	gcm.processAADBytes(cast(octets)x"D609B1F056637A0D46DF998D88E52E00");
351 
352 	outLen = gcm.processBytes(cast(octets)x"08000F101112131415161718191A1B1C", oBuf).length;
353 	oBuf = oBuf[outLen..$];
354 	outLen = gcm.processBytes(cast(octets)x"1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A", oBuf).length;
355 	oBuf = oBuf[outLen..$];
356 
357 	outLen = gcm.processBytes(cast(octets)x"0002", oBuf).length;
358 	oBuf = oBuf[outLen..$];
359 
360 	gcm.processAADBytes(cast(octets)x"B2C2846512153524C0895E81");
361 	ubyte[16] mac;
362 	outLen = gcm.finish(mac, oBuf);
363 	//	import std.stdio;
364 	//	writefln("%(%x%)", output);
365 	assert(output == cast(octets)x"701AFA1CC039C0D765128A665DAB69243899BF7318CCDC81C9931DA17FBE8EDD7D17CB8B4C26FC81E3284F2B7FBA713D");
366 	assert(mac == cast(octets)x"4F8D55E7D3F06FD5A13C0C29B9D5B880");
367 }
368 
369 /// test decryption
370 /// test vectors from
371 /// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
372 /// section 2.2.1
373 unittest {
374 	import dcrypt.blockcipher.aes;
375 	
376 	alias const(ubyte)[] octets;
377 	
378 	octets key = cast(octets)x"AD7A2BD03EAC835A6F620FDCB506B345";
379 	octets iv = cast(octets)x"12153524C0895E81B2C28465"; // 96 bits
380 
381 	GCM!AES gcm;
382 	gcm.start(false, key, iv);
383 	
384 	ubyte[48] output;
385 	ubyte[] oBuf = output;
386 	size_t outLen;
387 	
388 	gcm.processAADBytes(cast(octets)x"D609B1F056637A0D46DF998D88E52E00");
389 
390 	// add ciphertext
391 	outLen = gcm.processBytes(cast(octets)
392 		x"701AFA1CC039C0D765128A665DAB6924
393 	      3899BF7318CCDC81C9931DA17FBE8EDD
394 	      7D17CB8B4C26FC81E3284F2B7FBA713D", oBuf).length;
395 	oBuf = oBuf[outLen..$];
396 
397 	gcm.processAADBytes(cast(octets)x"B2C2846512153524C0895E81");
398 	ubyte[16] mac;
399 	outLen = gcm.finish(mac, oBuf);
400 	//		import std.stdio;
401 	//		writefln("%(%.2x%)", output);
402 	
403 	assert(output == 
404 		x"08000F101112131415161718191A1B1
405 	      C1D1E1F202122232425262728292A2B
406 	      2C2D2E2F303132333435363738393A0002");
407 
408 	assert(mac == x"4F8D55E7D3F06FD5A13C0C29B9D5B880");
409 }
410 
411 /// Test decryption with modified cipher data. An exception should be thrown beacause of wrong token.
412 /// 
413 /// test vectors from
414 /// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
415 /// section 2.2.1
416 unittest {
417 	import dcrypt.blockcipher.aes;
418 	
419 	alias const(ubyte)[] octets;
420 	
421 	octets key = cast(octets)x"AD7A2BD03EAC835A6F620FDCB506B345";
422 	octets iv = cast(octets)x"12153524C0895E81B2C28465"; // 96 bits
423 	
424 	GCM!AES gcm;
425 	gcm.start(false, key, iv);
426 	
427 	ubyte[48] output;
428 	ubyte[] oBuf = output[];
429 	size_t outLen;
430 	
431 	gcm.processAADBytes(cast(octets)x"D609B1F056637A0D46DF998D88E52E00");
432 	
433 	// add ciphertext
434 	outLen = gcm.processBytes(cast(octets)
435 		x"701AFA1CC039C0D765128A665DAB6924
436 	      3899BF7318CCDC81C9931DA17FBE8EDD
437 	      7D17CB8B4C26FC81E3284F2B7FBA713D", oBuf).length; // 880 has been changed do EEF
438 	oBuf = oBuf[outLen..$];
439 	
440 	gcm.processAADBytes(cast(octets)x"B2C2846512153524C0895E81");
441 	ubyte[16] mac;
442 	outLen = gcm.finish(mac, oBuf);
443 	assert(mac != x"4F8D55E7D3F06FD5A13C0C29B9D5BEEF");
444 }
445 
446 /// Test decryption with altered AAD. An exception should be thrown beacause of wrong token.
447 /// 
448 /// test vectors from
449 /// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
450 /// section 2.2.1
451 unittest {
452 	import dcrypt.blockcipher.aes;
453 	
454 	alias const(ubyte)[] octets;
455 	
456 	octets key = cast(octets)x"AD7A2BD03EAC835A6F620FDCB506B345";
457 	octets iv = cast(octets)x"12153524C0895E81B2C28465"; // 96 bits
458 	
459 	GCM!AES gcm;
460 	gcm.start(false, key, iv);
461 	
462 	ubyte[48] output;
463 	ubyte[] oBuf = output;
464 	size_t outLen;
465 	
466 	gcm.processAADBytes(cast(octets)x"D609B1F056637A0D46DF998D88E52E00");
467 	
468 	// add ciphertext
469 	outLen = gcm.processBytes(cast(octets)
470 		x"701AFA1CC039C0D765128A665DAB6924
471 	      3899BF7318CCDC81C9931DA17FBE8EDD
472 	      7D17CB8B4C26FC81E3284F2B7FBA713D", oBuf).length;
473 	oBuf = oBuf[outLen..$];
474 	
475 	gcm.processAADBytes(cast(octets)x"B2C2846512153524C089beef"); // changed 5E81 to beef
476 	ubyte[16] mac;
477 	gcm.finish(mac, oBuf);
478 	assert(mac != x"4F8D55E7D3F06FD5A13C0C29B9D5B880");
479 	// verify that an InvalidCipherTextException is thrown
480 	//	bool exception = false;
481 	//	try {
482 	//		outLen = gcm.finish(oBuf);
483 	//	} catch (InvalidCipherTextException e) {
484 	//		exception = true;
485 	//	}
486 	//	assert(exception, "AAD has been altered but no exception has been thrown!");
487 }
488 
489 // test vectors from
490 // gcm-spec: Test Case 6
491 unittest {
492 
493 	import dcrypt.blockcipher.aes;
494 	
495 	alias const(ubyte)[] octets;
496 	
497 	octets key = cast(octets)x"feffe9928665731c6d6a8f9467308308";
498 	octets iv = cast(octets)
499 		x"9313225df88406e555909c5aff5269aa
500           6a7a9538534f7da1e4c303d2a318a728
501           c3c0c95156809539fcf0e2429a6b5254
502 	      16aedbf5a0de6a57a637b39b"; // more than 96 bits
503 
504 	GCM!AES gcm;
505 	gcm.start(true, key, iv);
506 	
507 	octets aad = cast(octets)(
508 		x"feedfacedeadbeeffeedfacedeadbeef
509           abaddad2"
510 		);
511 
512 	octets plaintext = cast(octets)(
513 		x"d9313225f88406e5a55909c5aff5269a
514           86a7a9531534f7da2e4c303d8a318a72
515           1c3c0c95956809532fcf0e2449a6b525
516           b16aedf5aa0de657ba637b39"
517 		);
518 
519 	ubyte[] output = new ubyte[gcm.getOutputSize(plaintext.length)];
520 	ubyte[] oBuf = output;
521 	size_t outLen;
522 
523 	outLen = gcm.processBytes(plaintext, oBuf).length;
524 	oBuf = oBuf[outLen..$];
525 
526 	gcm.processAADBytes(aad);
527 	ubyte[16] mac;
528 	outLen = gcm.finish(mac, oBuf);
529 	oBuf = oBuf[outLen..$];
530 
531 	octets expectedCiphertext = cast(octets) (
532 		x"8ce24998625615b603a033aca13fb894
533           be9112a5c3a211a8ba262a3cca7e2ca7
534           01e4a9a4fba43c90ccdcb281d48c7c6f
535           d62875d2aca417034c34aee5"
536 		);
537 
538 	octets expectedMac = cast(octets) x"619cc5aefffe0bfa462af43c1699d050";
539 	
540 	assert(output == expectedCiphertext);
541 	assert(mac == expectedMac);
542 }
543 
544 /// test GCM with different MAC sizes
545 unittest { 
546 
547 	import dcrypt.blockcipher.aes;
548 
549 	string[] keys = [
550 		x"00000000000000000000000000000000",
551 		x"00000000000000000000000000000000",
552 		x"00000000000000000000000000000000",
553 		x"00000000000000000000000000000000",
554 		x"00000000000000000000000000000000",
555 		x"00000000000000000000000000000000",
556 		x"00000000000000000000000000000000",
557 		x"00000000000000000000000000000000",
558 		x"00000000000000000000000000000000",
559 		x"00000000000000000000000000000000",
560 		x"00000000000000000000000000000000",
561 		x"00000000000000000000000000000000",
562 		x"00000000000000000000000000000000",
563 	];
564 	string[] ivs = [
565 		x"00",
566 		x"00000000",
567 		x"00000000000000",
568 		x"00000000000000000000",
569 		x"00000000000000000000000000",
570 		x"00000000000000000000000000000000",
571 		x"00000000000000000000000000000000000000",
572 		x"00000000000000000000000000000000000000000000",
573 		x"00000000000000000000000000000000000000000000000000",
574 		x"00000000000000000000000000000000000000000000000000000000",
575 		x"00000000000000000000000000000000000000000000000000000000000000",
576 		x"00000000000000000000000000000000000000000000000000000000000000000000",
577 		x"00000000000000000000000000000000000000000000000000000000000000000000000000",
578 	];
579 	string[] aads = [
580 		x"",
581 		x"00000000000000",
582 		x"0000000000000000000000000000",
583 		x"000000000000000000000000000000000000000000",
584 		x"00000000000000000000000000000000000000000000000000000000",
585 		x"0000000000000000000000000000000000000000000000000000000000000000000000",
586 		x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
587 		x"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
588 		x"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
589 		x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
590 		x"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
591 		x"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
592 		x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
593 	];
594 	string[] plains = [
595 		x"",
596 		x"0000000000",
597 		x"00000000000000000000",
598 		x"000000000000000000000000000000",
599 		x"0000000000000000000000000000000000000000",
600 		x"00000000000000000000000000000000000000000000000000",
601 		x"000000000000000000000000000000000000000000000000000000000000",
602 		x"0000000000000000000000000000000000000000000000000000000000000000000000",
603 		x"00000000000000000000000000000000000000000000000000000000000000000000000000000000",
604 		x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
605 		x"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
606 		x"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
607 		x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
608 	];
609 	string[] ciphers = [
610 		x"3c2fa7a9",
611 		x"078bb038e6b2353f0e05",
612 		x"d6a480d4dec719bd36a60efde3aaf1f8",
613 		x"e37dd3785cc7017f206df18d831e37cfe63f9e057a23",
614 		x"3fe95bef64662ddcf19a96cc584d2146499320eef8d518bb5e7e49a7",
615 		x"a3b22b8449afafbcd6c09f2cfa9de2be938f8bbf235863d0cefb4075046c9a4d351e",
616 		x"a0912f3bde077afa3f21725fbcae1c9c2e00b28b6eb462745e9b65a026cc4ba84d13b408b7061fe1",
617 		x"535b0d13cbb1012df5402f748cea5304d52db1e4b997317a54c2296b95e0300c6692f911625bfe617d16b63a237b",
618 		x"547096f9d7a83ba8d128467baac4a9d861ebd51cc2dfff111915cd0b4260b7dc49c8d8723eb15429024ac21eed99ca1338844092",
619 		x"95e67a9eade034290efa90e33f51710f02f3aba4c32873545891924aa52dcc092695e983b529b60e7b13aee5f7d6de278c77410e216d0fdbd7e1",
620 		x"0957e69831df479e8cf7b214e1cef4d3e7a2716e8179deaf8061383f35eeabd017080c3d7972b98009a38b5842a2a08a9123412338e16de05a72b76849629b48",
621 		x"07052b0f8b95c9491ae43bac6693802384688e9dd19d9ce295b4ab550163a2bb4b0dd905012a56094e895ea7a5857f8100af40b4adb6452d0b8e78e709c5c9f1d432b5f59317",
622 		x"e0902e27a95867acaa788920ac71b2f2a61863bdc40ee869bea53470edf02fc71800465c550a58ba69220c67243899d756cf0a5ac4fda582fc6e9d2f8498a0e73e0e809bfb8d86ab5fdf066c",
623 	];
624 	uint[] macSizes = [
625 		32,
626 		40,
627 		48,
628 		56,
629 		64,
630 		72,
631 		80,
632 		88,
633 		96,
634 		104,
635 		112,
636 		120,
637 		128,
638 	];
639 
640 	AEADCipherTest(
641 		new GCMEngine(new AESEngine), 
642 		keys,
643 		ivs,
644 		plains,
645 		aads,
646 		ciphers,
647 		macSizes);
648 
649 }
650 
651 /// OOP Wrapper for GCM
652 @safe
653 public class GCMEngine: IAEADEngine {
654 
655 	private GCM!void cipher = void;
656 	
657 	public {
658 		
659 		/// Params: c = underlying block cipher
660 		this(IBlockCipher c) {
661 			cipher = GCM!void(c);
662 		}
663 		
664 		void start(bool forEncryption, in ubyte[] key, in ubyte[] iv) nothrow @nogc {
665 			cipher.start(forEncryption, key, iv);
666 		}
667 		
668 		@property
669 		string name() pure nothrow {
670 			return cipher.name;
671 		}
672 		
673 		IBlockCipher getUnderlyingCipher() pure nothrow {
674 			return cipher.getUnderlyingCipher();
675 		}
676 		
677 		void processAADBytes(in ubyte[] aad) nothrow {
678 			cipher.processAADBytes(aad);
679 		}
680 
681 		ubyte[] processBytes(in ubyte[] input, ubyte[] output) nothrow {
682 			return cipher.processBytes(input, output);
683 		}
684 
685 		size_t finish(ubyte[] macBuf, ubyte[] output) {
686 			return cipher.finish(macBuf, output);
687 		}
688 
689 		size_t getUpdateOutputSize(size_t len) nothrow const {
690 			return cipher.getUpdateOutputSize(len);
691 		}
692 		
693 		size_t getOutputSize(size_t len) nothrow const {
694 			return cipher.getOutputSize(len);
695 		}
696 
697 		void reset() nothrow {
698 			cipher.reset();
699 		}
700 	}
701 }
702 
703 /// Circular buffer holding 2*BLOCKSIZE bytes of data.
704 @safe
705 private struct CircularBlockBuffer(size_t BLOCKSIZE) {
706 
707 	import std.algorithm: min;
708 
709 	private {
710 		ubyte[2*BLOCKSIZE] buf;
711 		size_t offset = 0;
712 		size_t contentLen = 0;
713 		ubyte nextOutputBlock = 0;
714 	}
715 
716 	invariant {
717 		assert(offset <= 2*BLOCKSIZE, "offset out of bounds");
718 		assert(contentLen <= 2*BLOCKSIZE, "contentLen out of bounds");
719 		assert(nextOutputBlock <= 2, "nextOutputBlock out of bounds");
720 	}
721 
722 	
723 	public nothrow @nogc  {
724 
725 		/**
726 		 * try to fill the buffer
727 		 * 
728 		 * Returns: number of bytes written to buffer
729 		 */
730 		size_t put(in ubyte[] input)
731 		out (result){
732 			assert(result <= input.length);
733 		}
734 		body {
735 
736 			size_t procLen = min(input.length, 2*BLOCKSIZE - contentLen);
737 
738 			const(ubyte)[] iBuf = input;
739 
740 			// copy input into buffer
741 			foreach(i;0..procLen) {
742 				buf[offset] = input[i];
743 				offset = (offset + 1) % (2*BLOCKSIZE);
744 			}
745 
746 			contentLen += procLen;
747 
748 			return procLen;
749 		}
750 
751 		bool isFull() {
752 			return contentLen == buf.length;
753 		}
754 
755 		/**
756 		 * write max one block to output if buffer is full
757 		 * 
758 		 * Returns: number of bytes written to output
759 		 */
760 		size_t drainBlock(ubyte[] output)
761 		in {
762 			assert(output.length >= BLOCKSIZE, "output buffer too short");
763 		}
764 		body {
765 			if(isFull()) {
766 
767 				size_t blockOff = nextOutputBlock * BLOCKSIZE;
768 
769 				// copy one block to output
770 				output[0..BLOCKSIZE] = buf[blockOff..blockOff+BLOCKSIZE];
771 
772 				nextOutputBlock ^= 0x01; // 0,1,0,1,...
773 				contentLen -= BLOCKSIZE;
774 				return BLOCKSIZE;
775 			}
776 
777 			return 0;
778 		}
779 
780 		/**
781 		 * write whole buffer content to output
782 		 * 
783 		 * Returns: number of bytes written to output
784 		 */
785 		size_t drainAll(ubyte[] output)
786 		in {
787 			assert(output.length >= contentLen, "output buffer too short");
788 		}
789 		body {
790 
791 			size_t startOff = nextOutputBlock * BLOCKSIZE;
792 
793 			// copy data to output
794 			foreach(i;0..contentLen) {
795 				output[i] = buf[(startOff + i) % (2*BLOCKSIZE)];
796 			}
797 
798 			size_t outLen = contentLen;
799 			contentLen = 0;
800 			nextOutputBlock = 0;
801 			offset = 0;
802 			return outLen;
803 		}
804 
805 		@property
806 		size_t length() const {
807 			return contentLen;
808 		}
809 
810 		void reset() {
811 			buf[] = 0;
812 			offset = 0;
813 			contentLen = 0;
814 			nextOutputBlock = 0;
815 		}
816 		
817 	}
818 
819 	
820 }