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