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