1 /******************************************************************************* 2 3 Templates for gcrypt algorithms 4 5 Requires linking with libgcrypt: 6 -L-lgcrypt 7 8 See_Also: 9 https://gnupg.org/documentation/manuals/gcrypt/index.html 10 11 Copyright: 12 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 13 All rights reserved. 14 15 License: 16 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 17 Alternatively, this file may be distributed under the terms of the Tango 18 3-Clause BSD License (see LICENSE_BSD.txt for details). 19 20 Bear in mind this module provides bindings to an external library that 21 has its own license, which might be more restrictive. Please check the 22 external library license to see which conditions apply for linking. 23 24 *******************************************************************************/ 25 26 module ocean.util.cipher.gcrypt.core.Gcrypt; 27 28 import ocean.core.Verify; 29 import ocean.meta.types.Qualifiers; 30 import ocean.util.cipher.gcrypt.c.gcrypt; 31 32 version (unittest) 33 { 34 import ocean.core.Test; 35 } 36 37 38 /******************************************************************************* 39 40 Alias for the libgcrypt algorithm 41 42 *******************************************************************************/ 43 44 public alias gcry_cipher_algos Algorithm; 45 46 /******************************************************************************* 47 48 Alias for the libgcrypt modes 49 50 *******************************************************************************/ 51 52 public alias gcry_cipher_modes Mode; 53 54 55 /******************************************************************************* 56 57 Reusable exception class 58 59 *******************************************************************************/ 60 61 public class GcryptException : Exception 62 { 63 import ocean.core.Exception; 64 import ocean.text.util.StringC; 65 import ocean.util.cipher.gcrypt.c.gcrypt; 66 67 /*************************************************************************** 68 69 Mixin the reusable exception parts 70 71 ***************************************************************************/ 72 73 mixin ReusableExceptionImplementation!(); 74 75 /*************************************************************************** 76 77 Throw if variable error indicates an error. The exception message is 78 set to contain the error from libgcrypt. 79 80 Params: 81 error = error code from gcrypt 82 file = file from which this exception can be thrown 83 line = line from which this exception can be thrown 84 85 Throws: 86 this if error != 0 87 88 ***************************************************************************/ 89 90 public void throwIfGcryptError ( gcry_error_t error, 91 istring file = __FILE__, 92 int line = __LINE__ ) 93 { 94 if ( error ) 95 { 96 this.setGcryptErrorMsg(error, file, line); 97 throw this; 98 } 99 } 100 101 /*************************************************************************** 102 103 Throw if len != expected, with exception message explaining 104 the issue. 105 106 Params: 107 id = identifier for message formatting 108 len = actual length 109 expected = expected length 110 file = file from which this exception can be thrown 111 line = line from which this exception can be thrown 112 113 Throws: 114 this if iv_length != block_size 115 116 ***************************************************************************/ 117 118 public void throwIfLenMismatch ( cstring id, size_t len, 119 size_t expected, 120 istring file = __FILE__, 121 int line = __LINE__ ) 122 { 123 if ( len != expected ) 124 { 125 this.set(id, file, line) 126 .append(` length is: `) 127 .append(len) 128 .append(` but needs to be `) 129 .append(expected); 130 throw this; 131 } 132 } 133 134 /*************************************************************************** 135 136 Throws a new instance of this class if `error` indicates an error. The 137 exception message is set to contain the error from libgcrypt. 138 139 Params: 140 error = error code from gcrypt 141 file = file from which this exception can be thrown 142 line = line from which this exception can be thrown 143 144 Throws: 145 a new instance of this class if error != 0 146 147 ***************************************************************************/ 148 149 public static void throwNewIfGcryptError ( gcry_error_t error, 150 istring file = __FILE__, 151 int line = __LINE__ ) 152 { 153 if (error) 154 { 155 auto e = new typeof(this); 156 e.setGcryptErrorMsg(error, file, line); 157 throw e; 158 } 159 } 160 161 /*************************************************************************** 162 163 Set the exception message to contain the error from libgcrypt. 164 165 Params: 166 error = error code from gcrypt 167 file = file from which this exception can be thrown 168 line = line from which this exception can be thrown 169 170 ***************************************************************************/ 171 172 private void setGcryptErrorMsg ( gcry_error_t error, 173 istring file = __FILE__, 174 int line = __LINE__ ) 175 { 176 this.set(`Error: "`, file, line) 177 .append(StringC.toDString(gcry_strerror(error))) 178 .append(`" Source: "`) 179 .append(StringC.toDString(gcry_strsource(error))) 180 .append(`"`); 181 } 182 } 183 184 /******************************************************************************* 185 186 Gcrypt algorithm base class template. 187 188 Params: 189 algorithm = algorithm which this class uses for en/decryption 190 mode = algorithm mode which this class uses for en/decryption 191 192 (The algorithm and mode are provided as template arguments, rather than 193 run-time arguments to the constructor, because it's useful to provide static 194 methods which return information about the algorithm (required_key_len(), 195 for example).) 196 197 *******************************************************************************/ 198 199 private class GcryptBase ( Algorithm algorithm, Mode mode ) 200 { 201 /*************************************************************************** 202 203 Gcrypt handle. Set by the call to gcry_cipher_open() in the ctor, if 204 successful. 205 206 ***************************************************************************/ 207 208 protected gcry_cipher_hd_t handle; 209 210 /*************************************************************************** 211 212 Reusable exception 213 214 ***************************************************************************/ 215 216 protected GcryptException exception; 217 218 /*************************************************************************** 219 220 Constructs the class and sets gcrypt to use the specified algorithm, 221 mode, and key. 222 223 Params: 224 key = the key to use. 225 226 Throws: 227 A GcryptException if gcrypt fails to open or the key fails to be set 228 229 ***************************************************************************/ 230 231 public this ( in void[] key ) 232 { 233 this.exception = new GcryptException; 234 235 with ( gcry_ctl_cmds ) 236 { 237 // We don't need secure memory 238 auto err = gcry_control(GCRYCTL_DISABLE_SECMEM, 0); 239 this.exception.throwIfGcryptError(err); 240 241 err = gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); 242 this.exception.throwIfGcryptError(err); 243 } 244 245 // Open gcrypt with specified algorithm and mode 246 uint flags = 0; 247 auto err = gcry_cipher_open(&this.handle, algorithm, mode, flags); 248 this.exception.throwIfGcryptError(err); 249 250 // Set the key, since we don't call gcrypt's reset function we only need 251 // to do this once. 252 this.setKey(key); 253 } 254 255 /*************************************************************************** 256 257 Destructor; closes gcrypt 258 259 ***************************************************************************/ 260 261 ~this ( ) 262 { 263 if ( this.handle !is null ) 264 this.close(); 265 } 266 267 /*************************************************************************** 268 269 Returns: 270 required length of encryption key (in bytes) for this algorithm. 271 If the algorithm supports multiple key lengths, the maximum 272 supported value is returned. 273 274 ***************************************************************************/ 275 276 public static size_t required_key_len ( ) 277 out ( blk_len ) 278 { 279 assert(blk_len != 0); 280 } 281 do 282 { 283 return gcry_cipher_get_algo_keylen(algorithm); 284 } 285 286 /*************************************************************************** 287 288 Returns: 289 required length of one block (in bytes) for this algorithm 290 291 ***************************************************************************/ 292 293 public static size_t required_blk_len ( ) 294 out ( key_len ) 295 { 296 assert(key_len != 0); 297 } 298 do 299 { 300 return gcry_cipher_get_algo_blklen(algorithm); 301 } 302 303 /*************************************************************************** 304 305 Relinquishes the gcrypt instance used internally. It is not possible to 306 use any methods of this instance after calling this method -- only call 307 this when you are certain that you're finished. 308 309 ***************************************************************************/ 310 311 public void close ( ) 312 { 313 gcry_cipher_close(this.handle); 314 this.handle = null; 315 } 316 317 /*************************************************************************** 318 319 Set the key to use. 320 321 Params: 322 key = the encryption key 323 324 Throws: 325 A GcryptException if the key failed to be set 326 327 ***************************************************************************/ 328 329 protected void setKey ( in void[] key ) 330 { 331 auto err = gcry_cipher_setkey(this.handle, key.ptr, key.length); 332 this.exception.throwIfGcryptError(err); 333 } 334 335 /*************************************************************************** 336 337 Unittests for the class. Note that as this class is a template, the 338 unittests will not be run unless it is instantiated (see modules in 339 ocean.util.cipher.gcrypt). 340 341 ***************************************************************************/ 342 343 version (unittest) 344 { 345 /*********************************************************************** 346 347 Helper function to generate a void[] of the specified length, filled 348 with bytes of incrementing value. 349 350 Params: 351 length = number of bytes to generate 352 353 Returns: 354 void[] containing the specified number of bytes, with 355 incrementing values 356 357 ***********************************************************************/ 358 359 private static void[] generateString ( size_t length ) 360 { 361 auto str = new ubyte[length]; 362 ubyte i; 363 foreach ( ref v; str ) 364 { 365 v = i++; 366 } 367 return str; 368 } 369 370 /*********************************************************************** 371 372 Helper function to generate a void[] suitable for use as a key in 373 unittests. 374 375 Returns: 376 void[] of the correct length for a key 377 378 ***********************************************************************/ 379 380 public static void[] generateKey ( ) 381 { 382 return generateString(typeof(this).required_key_len); 383 } 384 385 /*********************************************************************** 386 387 Helper function to generate a char[] suitable for use as a message 388 to encrypt in unittests. For compatibility with certain algorithms, 389 a message of the defined block-length is generated. 390 391 Returns: 392 char[] of the correct length 393 394 ***********************************************************************/ 395 396 public static char[] generateMessage ( ) 397 { 398 auto length = typeof(this).required_blk_len; 399 auto str = new char[length]; 400 char i = 'a'; 401 foreach ( ref v; str ) 402 { 403 v = i++; 404 } 405 return str; 406 } 407 408 /*********************************************************************** 409 410 Tests that the algorithm accepts a key of the correct length and 411 rejects keys of invalid lengths. 412 413 Some algorithms have possible key length (e.g. 128 or 256 bits), 414 while other algorithms have ranges of key length (e.g. Blowfish 415 allows for keys from 32 to 448 bits). 416 For algorithms with a fixed key length, it is recommended to call 417 this function from an unittest. 418 419 Params: 420 key = Key to use for test. 421 This function will ensure that a larger and smaller key 422 (by 1) triggers an error, while the provided key doesn't. 423 Make sure to pass a key which is not weak (e.g. a default 424 initialized slice), as gcrypt errors on them. 425 426 ***********************************************************************/ 427 428 public static void testFixedKeyLength (const(ubyte)[] key) 429 { 430 // Too short should fail 431 testThrown!(GcryptException)(new typeof(this)(key[0 .. $-1])); 432 433 // Too long should fail 434 key.length = key.length + 1; 435 testThrown!(GcryptException)(new typeof(this)(key)); 436 key.length = key.length - 1; 437 438 scope works = new typeof(this)(key); 439 } 440 } 441 } 442 443 /******************************************************************************* 444 445 Gcrypt algorithm template for algorithms with initialization vectors. 446 447 Params: 448 algorithm = algorithm which this class uses for en/decryption 449 mode = algorithm mode which this class uses for en/decryption 450 451 *******************************************************************************/ 452 453 public class GcryptWithIV ( Algorithm algorithm, Mode mode ) 454 : GcryptBase!(algorithm, mode) 455 { 456 /*************************************************************************** 457 458 Constructor 459 460 Params: 461 key = the key to use. 462 463 Throws: 464 A GcryptException if gcrypt fails to open or the key fails to be set 465 466 ***************************************************************************/ 467 468 public this ( in void[] key ) 469 { 470 super(key); 471 } 472 473 /*************************************************************************** 474 475 Encrypt the content of buffer in place. 476 477 Params: 478 buffer = the content to be encrypted in place 479 iv = the initialisation vector to use 480 481 Throws: 482 if setting the init vector or the encryption fails 483 484 ***************************************************************************/ 485 486 public void encrypt ( mstring buffer, in void[] iv ) 487 { 488 verify(this.handle !is null); 489 490 if ( !buffer.length ) 491 return; 492 493 this.setInitVector(iv); 494 495 auto err = 496 gcry_cipher_encrypt(this.handle, buffer.ptr, buffer.length, null, 0); 497 this.exception.throwIfGcryptError(err); 498 } 499 500 /*************************************************************************** 501 502 Decrypt the content of buffer in place. 503 504 Params: 505 buffer = the content to be decrypted in place 506 iv = the initialisation vector to use 507 508 Throws: 509 if setting the init vector or the decryption fails 510 511 ***************************************************************************/ 512 513 public void decrypt ( mstring buffer, in void[] iv ) 514 { 515 verify(this.handle !is null); 516 517 if ( !buffer.length ) 518 return; 519 520 this.setInitVector(iv); 521 522 auto err = 523 gcry_cipher_decrypt(this.handle, buffer.ptr, buffer.length, null, 0); 524 this.exception.throwIfGcryptError(err); 525 } 526 527 /*************************************************************************** 528 529 Returns: 530 required length of initialisation vector (in bytes) for this 531 algorithm. Note that, if called for an algorithm which does not 532 require an IV, the return value will be undefined. 533 534 ***************************************************************************/ 535 536 public static size_t required_iv_len ( ) 537 out ( iv_len ) 538 { 539 assert(iv_len != 0); 540 } 541 do 542 { 543 return gcry_cipher_get_algo_blklen(algorithm); 544 } 545 546 /*************************************************************************** 547 548 Set the initialization vector to use. 549 550 Params: 551 iv = the initialization vector 552 553 Throws: 554 A GcryptException if the initialization vector failed to be set 555 556 ***************************************************************************/ 557 558 protected void setInitVector ( in void[] iv ) 559 { 560 this.exception.throwIfLenMismatch("iv", iv.length, this.required_iv_len); 561 562 auto err = gcry_cipher_setiv(this.handle, iv.ptr, iv.length); 563 this.exception.throwIfGcryptError(err); 564 } 565 566 /*************************************************************************** 567 568 IV-using algorithm-specific unittest resources 569 570 ***************************************************************************/ 571 572 version (unittest) 573 { 574 /*********************************************************************** 575 576 Helper function to generate a void[] suitable for use as an IV in 577 unittests. 578 579 Returns: 580 void[] of the correct length for an IV 581 582 ***********************************************************************/ 583 584 public static void[] generateIV ( ) 585 { 586 return generateString(typeof(this).required_iv_len); 587 } 588 } 589 590 /*************************************************************************** 591 592 Test that only initialisation vectors of the correct length are 593 acceptable. 594 595 ***************************************************************************/ 596 597 unittest 598 { 599 auto key = generateKey(); 600 auto iv = generateIV(); 601 602 auto crypt = new typeof(this)(key); 603 604 auto buf = generateMessage(); 605 606 // Too short should fail 607 testThrown!(GcryptException)(crypt.encrypt(buf, iv[0 .. $-1])); 608 609 // Too long should fail 610 iv.length = iv.length + 1; 611 testThrown!(GcryptException)(crypt.encrypt(buf, iv)); 612 iv.length = iv.length - 1; 613 614 // The correct length should succeed 615 crypt.encrypt(buf, iv); 616 } 617 618 /*************************************************************************** 619 620 Test encrypting and decrypting a short value. 621 622 ***************************************************************************/ 623 624 unittest 625 { 626 auto key = generateKey(); 627 auto iv = generateIV(); 628 629 auto crypt = new typeof(this)(key); 630 631 auto original = generateMessage(); 632 mstring buf; 633 buf ~= original; 634 635 // Encrypt buf in place 636 crypt.encrypt(buf, iv); 637 test!("!=")(buf, original); 638 639 // Decrypt buf in place 640 crypt.decrypt(buf, iv); 641 test!("==")(buf, original); 642 } 643 644 /*************************************************************************** 645 646 Test that setting an IV does affect the outcome of encryption. 647 648 ***************************************************************************/ 649 650 unittest 651 { 652 auto key = generateKey(); 653 654 auto crypt = new typeof(this)(key); 655 656 auto original = generateMessage(); 657 mstring buf; 658 buf ~= original; 659 660 // Encrypt buf in place 661 auto iv = generateIV(); 662 crypt.encrypt(buf, iv); 663 664 // Encrypt with a different IV and test that is not the same as before 665 mstring buf2; 666 buf2 ~= original; 667 auto iv2 = generateIV(); 668 foreach ( ref b; cast(ubyte[])iv2 ) 669 { 670 b++; 671 } 672 crypt.encrypt(buf2, iv2); 673 674 test!("!=")(buf, buf2); 675 } 676 } 677 678 /******************************************************************************* 679 680 Gcrypt algorithm template for algorithms without initialization vectors. 681 682 Params: 683 algorithm = algorithm which this class uses for en/decryption 684 mode = algorithm mode which this class uses for en/decryption 685 686 *******************************************************************************/ 687 688 public class GcryptNoIV ( Algorithm algorithm, Mode mode ) 689 : GcryptBase!(algorithm, mode) 690 { 691 /*************************************************************************** 692 693 Constructor 694 695 Params: 696 key = the key to use. 697 698 Throws: 699 A GcryptException if gcrypt fails to open or the key fails to be set 700 701 ***************************************************************************/ 702 703 public this ( in void[] key ) 704 { 705 super(key); 706 } 707 708 /*************************************************************************** 709 710 Encrypt the content of buffer in place. 711 712 Params: 713 buffer = the content to be encrypted in place 714 715 Throws: 716 if the encryption fails 717 718 ***************************************************************************/ 719 720 public void encrypt ( mstring buffer ) 721 { 722 verify(this.handle !is null); 723 724 if ( !buffer.length ) 725 return; 726 727 auto err = 728 gcry_cipher_encrypt(this.handle, buffer.ptr, buffer.length, null, 0); 729 this.exception.throwIfGcryptError(err); 730 } 731 732 /*************************************************************************** 733 734 Decrypt the content of buffer in place. 735 736 Params: 737 buffer = the content to be decrypted in place 738 739 Throws: 740 if the decryption fails 741 742 ***************************************************************************/ 743 744 public void decrypt ( mstring buffer ) 745 { 746 verify(this.handle !is null); 747 748 if ( !buffer.length ) 749 return; 750 751 auto err = 752 gcry_cipher_decrypt(this.handle, buffer.ptr, buffer.length, null, 0); 753 this.exception.throwIfGcryptError(err); 754 } 755 756 /*************************************************************************** 757 758 Test encrypting and decrypting a short value. 759 760 ***************************************************************************/ 761 762 unittest 763 { 764 auto key = generateKey(); 765 766 auto crypt = new typeof(this)(key); 767 768 auto original = generateMessage(); 769 mstring buf; 770 buf ~= original; 771 772 // Encrypt buf in place 773 crypt.encrypt(buf); 774 test!("!=")(buf, original); 775 776 // Decrypt buf in place 777 crypt.decrypt(buf); 778 test!("==")(buf, original); 779 } 780 781 /*************************************************************************** 782 783 Test that encrypting a value after setting an IV gives the same result 784 as not setting an IV. For these algorithms, the IV should not be used. 785 786 ***************************************************************************/ 787 788 unittest 789 { 790 auto key = generateKey(); 791 792 auto crypt = new typeof(this)(key); 793 794 auto original = generateMessage(); 795 mstring buf; 796 buf ~= original; 797 798 // Encrypt buf in place 799 crypt.encrypt(buf); 800 801 // Generate and set an IV 802 auto iv = generateMessage(); 803 gcry_cipher_setiv(crypt.handle, iv.ptr, iv.length); 804 805 // Encrypt another buf in place and test that is the same as before 806 mstring buf2; 807 buf2 ~= original; 808 crypt.encrypt(buf2); 809 810 test!("==")(buf, buf2); 811 } 812 }