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