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 }