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 }