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 importocean.core.Verify;
29 importocean.meta.types.Qualifiers;
30 importocean.util.cipher.gcrypt.c.gcrypt;
31 32 version (unittest)
33 {
34 importocean.core.Test;
35 }
36 37 38 /*******************************************************************************
39 40 Alias for the libgcrypt algorithm
41 42 *******************************************************************************/43 44 publicaliasgcry_cipher_algosAlgorithm;
45 46 /*******************************************************************************
47 48 Alias for the libgcrypt modes
49 50 *******************************************************************************/51 52 publicaliasgcry_cipher_modesMode;
53 54 55 /*******************************************************************************
56 57 Reusable exception class
58 59 *******************************************************************************/60 61 publicclassGcryptException : Exception62 {
63 importocean.core.Exception;
64 importocean.text.util.StringC;
65 importocean.util.cipher.gcrypt.c.gcrypt;
66 67 /***************************************************************************
68 69 Mixin the reusable exception parts
70 71 ***************************************************************************/72 73 mixinReusableExceptionImplementation!();
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 publicvoidthrowIfGcryptError ( gcry_error_terror,
91 istringfile = __FILE__,
92 intline = __LINE__ )
93 {
94 if ( error )
95 {
96 this.setGcryptErrorMsg(error, file, line);
97 throwthis;
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 publicvoidthrowIfLenMismatch ( cstringid, size_tlen,
119 size_texpected,
120 istringfile = __FILE__,
121 intline = __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 throwthis;
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 publicstaticvoidthrowNewIfGcryptError ( gcry_error_terror,
150 istringfile = __FILE__,
151 intline = __LINE__ )
152 {
153 if (error)
154 {
155 autoe = newtypeof(this);
156 e.setGcryptErrorMsg(error, file, line);
157 throwe;
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 privatevoidsetGcryptErrorMsg ( gcry_error_terror,
173 istringfile = __FILE__,
174 intline = __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 privateclassGcryptBase ( Algorithmalgorithm, Modemode )
200 {
201 /***************************************************************************
202 203 Gcrypt handle. Set by the call to gcry_cipher_open() in the ctor, if
204 successful.
205 206 ***************************************************************************/207 208 protectedgcry_cipher_hd_thandle;
209 210 /***************************************************************************
211 212 Reusable exception
213 214 ***************************************************************************/215 216 protectedGcryptExceptionexception;
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 publicthis ( invoid[] key )
232 {
233 this.exception = newGcryptException;
234 235 with ( gcry_ctl_cmds )
236 {
237 // We don't need secure memory238 autoerr = 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 mode246 uintflags = 0;
247 autoerr = 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 need251 // 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 !isnull )
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 publicstaticsize_trequired_key_len ( )
277 out ( blk_len )
278 {
279 assert(blk_len != 0);
280 }
281 do282 {
283 returngcry_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 publicstaticsize_trequired_blk_len ( )
294 out ( key_len )
295 {
296 assert(key_len != 0);
297 }
298 do299 {
300 returngcry_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 publicvoidclose ( )
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 protectedvoidsetKey ( invoid[] key )
330 {
331 autoerr = 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 privatestaticvoid[] generateString ( size_tlength )
360 {
361 autostr = newubyte[length];
362 ubytei;
363 foreach ( refv; str )
364 {
365 v = i++;
366 }
367 returnstr;
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 publicstaticvoid[] generateKey ( )
381 {
382 returngenerateString(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 publicstaticchar[] generateMessage ( )
397 {
398 autolength = typeof(this).required_blk_len;
399 autostr = newchar[length];
400 chari = 'a';
401 foreach ( refv; str )
402 {
403 v = i++;
404 }
405 returnstr;
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 publicstaticvoidtestFixedKeyLength (const(ubyte)[] key)
429 {
430 // Too short should fail431 testThrown!(GcryptException)(newtypeof(this)(key[0 .. $-1]));
432 433 // Too long should fail434 key.length = key.length + 1;
435 testThrown!(GcryptException)(newtypeof(this)(key));
436 key.length = key.length - 1;
437 438 scopeworks = newtypeof(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 publicclassGcryptWithIV ( Algorithmalgorithm, Modemode )
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 publicthis ( invoid[] 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 publicvoidencrypt ( mstringbuffer, invoid[] iv )
487 {
488 verify(this.handle !isnull);
489 490 if ( !buffer.length )
491 return;
492 493 this.setInitVector(iv);
494 495 autoerr =
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 publicvoiddecrypt ( mstringbuffer, invoid[] iv )
514 {
515 verify(this.handle !isnull);
516 517 if ( !buffer.length )
518 return;
519 520 this.setInitVector(iv);
521 522 autoerr =
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 publicstaticsize_trequired_iv_len ( )
537 out ( iv_len )
538 {
539 assert(iv_len != 0);
540 }
541 do542 {
543 returngcry_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 protectedvoidsetInitVector ( invoid[] iv )
559 {
560 this.exception.throwIfLenMismatch("iv", iv.length, this.required_iv_len);
561 562 autoerr = 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 publicstaticvoid[] generateIV ( )
585 {
586 returngenerateString(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 unittest598 {
599 autokey = generateKey();
600 autoiv = generateIV();
601 602 autocrypt = newtypeof(this)(key);
603 604 autobuf = generateMessage();
605 606 // Too short should fail607 testThrown!(GcryptException)(crypt.encrypt(buf, iv[0 .. $-1]));
608 609 // Too long should fail610 iv.length = iv.length + 1;
611 testThrown!(GcryptException)(crypt.encrypt(buf, iv));
612 iv.length = iv.length - 1;
613 614 // The correct length should succeed615 crypt.encrypt(buf, iv);
616 }
617 618 /***************************************************************************
619 620 Test encrypting and decrypting a short value.
621 622 ***************************************************************************/623 624 unittest625 {
626 autokey = generateKey();
627 autoiv = generateIV();
628 629 autocrypt = newtypeof(this)(key);
630 631 autooriginal = generateMessage();
632 mstringbuf;
633 buf ~= original;
634 635 // Encrypt buf in place636 crypt.encrypt(buf, iv);
637 test!("!=")(buf, original);
638 639 // Decrypt buf in place640 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 unittest651 {
652 autokey = generateKey();
653 654 autocrypt = newtypeof(this)(key);
655 656 autooriginal = generateMessage();
657 mstringbuf;
658 buf ~= original;
659 660 // Encrypt buf in place661 autoiv = generateIV();
662 crypt.encrypt(buf, iv);
663 664 // Encrypt with a different IV and test that is not the same as before665 mstringbuf2;
666 buf2 ~= original;
667 autoiv2 = generateIV();
668 foreach ( refb; 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 publicclassGcryptNoIV ( Algorithmalgorithm, Modemode )
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 publicthis ( invoid[] 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 publicvoidencrypt ( mstringbuffer )
721 {
722 verify(this.handle !isnull);
723 724 if ( !buffer.length )
725 return;
726 727 autoerr =
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 publicvoiddecrypt ( mstringbuffer )
745 {
746 verify(this.handle !isnull);
747 748 if ( !buffer.length )
749 return;
750 751 autoerr =
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 unittest763 {
764 autokey = generateKey();
765 766 autocrypt = newtypeof(this)(key);
767 768 autooriginal = generateMessage();
769 mstringbuf;
770 buf ~= original;
771 772 // Encrypt buf in place773 crypt.encrypt(buf);
774 test!("!=")(buf, original);
775 776 // Decrypt buf in place777 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 unittest789 {
790 autokey = generateKey();
791 792 autocrypt = newtypeof(this)(key);
793 794 autooriginal = generateMessage();
795 mstringbuf;
796 buf ~= original;
797 798 // Encrypt buf in place799 crypt.encrypt(buf);
800 801 // Generate and set an IV802 autoiv = 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 before806 mstringbuf2;
807 buf2 ~= original;
808 crypt.encrypt(buf2);
809 810 test!("==")(buf, buf2);
811 }
812 }