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 }