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 moduleocean.util.cipher.gcrypt.core.Gcrypt;
27 28 29 30 importocean.util.cipher.gcrypt.c.gcrypt;
31 importocean.transition;
32 importocean.core.Verify;
33 34 version (UnitTest)
35 {
36 importocean.core.Test;
37 }
38 39 40 /*******************************************************************************
41 42 Alias for the libgcrypt algorithm
43 44 *******************************************************************************/45 46 publicaliasgcry_cipher_algosAlgorithm;
47 48 /*******************************************************************************
49 50 Alias for the libgcrypt modes
51 52 *******************************************************************************/53 54 publicaliasgcry_cipher_modesMode;
55 56 57 /*******************************************************************************
58 59 Reusable exception class
60 61 *******************************************************************************/62 63 publicclassGcryptException : Exception64 {
65 importocean.core.Exception;
66 importocean.text.util.StringC;
67 importocean.util.cipher.gcrypt.c.gcrypt;
68 69 /***************************************************************************
70 71 Mixin the reusable exception parts
72 73 ***************************************************************************/74 75 mixinReusableExceptionImplementation!();
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 publicvoidthrowIfGcryptError ( gcry_error_terror,
93 istringfile = __FILE__,
94 intline = __LINE__ )
95 {
96 if ( error )
97 {
98 this.setGcryptErrorMsg(error, file, line);
99 throwthis;
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 publicvoidthrowIfLenMismatch ( cstringid, size_tlen,
121 size_texpected,
122 istringfile = __FILE__,
123 intline = __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 throwthis;
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 publicstaticvoidthrowNewIfGcryptError ( gcry_error_terror,
152 istringfile = __FILE__,
153 intline = __LINE__ )
154 {
155 if (error)
156 {
157 autoe = newtypeof(this);
158 e.setGcryptErrorMsg(error, file, line);
159 throwe;
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 privatevoidsetGcryptErrorMsg ( gcry_error_terror,
175 istringfile = __FILE__,
176 intline = __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 privateclassGcryptBase ( Algorithmalgorithm, Modemode )
202 {
203 /***************************************************************************
204 205 Gcrypt handle. Set by the call to gcry_cipher_open() in the ctor, if
206 successful.
207 208 ***************************************************************************/209 210 protectedgcry_cipher_hd_thandle;
211 212 /***************************************************************************
213 214 Reusable exception
215 216 ***************************************************************************/217 218 protectedGcryptExceptionexception;
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 publicthis ( invoid[] key )
234 {
235 this.exception = newGcryptException;
236 237 with ( gcry_ctl_cmds )
238 {
239 // We don't need secure memory240 autoerr = 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 mode248 uintflags = 0;
249 autoerr = 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 need253 // 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 !isnull )
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 publicstaticsize_trequired_key_len ( )
279 out ( blk_len )
280 {
281 assert(blk_len != 0);
282 }
283 body284 {
285 returngcry_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 publicstaticsize_trequired_blk_len ( )
296 out ( key_len )
297 {
298 assert(key_len != 0);
299 }
300 body301 {
302 returngcry_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 publicvoidclose ( )
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 protectedvoidsetKey ( invoid[] key )
332 {
333 autoerr = 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 privatestaticvoid[] generateString ( size_tlength )
362 {
363 autostr = newubyte[length];
364 ubytei;
365 foreach ( refv; str )
366 {
367 v = i++;
368 }
369 returnstr;
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 publicstaticvoid[] generateKey ( )
383 {
384 returngenerateString(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 publicstaticchar[] generateMessage ( )
399 {
400 autolength = typeof(this).required_blk_len;
401 autostr = newchar[length];
402 chari = 'a';
403 foreach ( refv; str )
404 {
405 v = i++;
406 }
407 returnstr;
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 publicstaticvoidtestFixedKeyLength (Const!(ubyte)[] key)
431 {
432 // Too short should fail433 testThrown!(GcryptException)(newtypeof(this)(key[0 .. $-1]));
434 435 // Too long should fail436 key.length = key.length + 1;
437 testThrown!(GcryptException)(newtypeof(this)(key));
438 key.length = key.length - 1;
439 440 scopeworks = newtypeof(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 publicclassGcryptWithIV ( Algorithmalgorithm, Modemode )
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 publicthis ( invoid[] 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 publicvoidencrypt ( mstringbuffer, invoid[] iv )
489 {
490 verify(this.handle !isnull);
491 492 if ( !buffer.length )
493 return;
494 495 this.setInitVector(iv);
496 497 autoerr =
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 publicvoiddecrypt ( mstringbuffer, invoid[] iv )
516 {
517 verify(this.handle !isnull);
518 519 if ( !buffer.length )
520 return;
521 522 this.setInitVector(iv);
523 524 autoerr =
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 publicstaticsize_trequired_iv_len ( )
539 out ( iv_len )
540 {
541 assert(iv_len != 0);
542 }
543 body544 {
545 returngcry_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 protectedvoidsetInitVector ( invoid[] iv )
561 {
562 this.exception.throwIfLenMismatch("iv", iv.length, this.required_iv_len);
563 564 autoerr = 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 publicstaticvoid[] generateIV ( )
587 {
588 returngenerateString(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 unittest600 {
601 autokey = generateKey();
602 autoiv = generateIV();
603 604 autocrypt = newtypeof(this)(key);
605 606 autobuf = generateMessage();
607 608 // Too short should fail609 testThrown!(GcryptException)(crypt.encrypt(buf, iv[0 .. $-1]));
610 611 // Too long should fail612 iv.length = iv.length + 1;
613 testThrown!(GcryptException)(crypt.encrypt(buf, iv));
614 iv.length = iv.length - 1;
615 616 // The correct length should succeed617 crypt.encrypt(buf, iv);
618 }
619 620 /***************************************************************************
621 622 Test encrypting and decrypting a short value.
623 624 ***************************************************************************/625 626 unittest627 {
628 autokey = generateKey();
629 autoiv = generateIV();
630 631 autocrypt = newtypeof(this)(key);
632 633 autooriginal = generateMessage();
634 mstringbuf;
635 buf ~= original;
636 637 // Encrypt buf in place638 crypt.encrypt(buf, iv);
639 test!("!=")(buf, original);
640 641 // Decrypt buf in place642 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 unittest653 {
654 autokey = generateKey();
655 656 autocrypt = newtypeof(this)(key);
657 658 autooriginal = generateMessage();
659 mstringbuf;
660 buf ~= original;
661 662 // Encrypt buf in place663 autoiv = generateIV();
664 crypt.encrypt(buf, iv);
665 666 // Encrypt with a different IV and test that is not the same as before667 mstringbuf2;
668 buf2 ~= original;
669 autoiv2 = generateIV();
670 foreach ( refb; 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 publicclassGcryptNoIV ( Algorithmalgorithm, Modemode )
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 publicthis ( invoid[] 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 publicvoidencrypt ( mstringbuffer )
723 {
724 verify(this.handle !isnull);
725 726 if ( !buffer.length )
727 return;
728 729 autoerr =
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 publicvoiddecrypt ( mstringbuffer )
747 {
748 verify(this.handle !isnull);
749 750 if ( !buffer.length )
751 return;
752 753 autoerr =
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 unittest765 {
766 autokey = generateKey();
767 768 autocrypt = newtypeof(this)(key);
769 770 autooriginal = generateMessage();
771 mstringbuf;
772 buf ~= original;
773 774 // Encrypt buf in place775 crypt.encrypt(buf);
776 test!("!=")(buf, original);
777 778 // Decrypt buf in place779 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 unittest791 {
792 autokey = generateKey();
793 794 autocrypt = newtypeof(this)(key);
795 796 autooriginal = generateMessage();
797 mstringbuf;
798 buf ~= original;
799 800 // Encrypt buf in place801 crypt.encrypt(buf);
802 803 // Generate and set an IV804 autoiv = 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 before808 mstringbuf2;
809 buf2 ~= original;
810 crypt.encrypt(buf2);
811 812 test!("==")(buf, buf2);
813 }
814 }