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 }