1 /*******************************************************************************
2 
3     libgcrypt hash-based message authentication code generator utility class.
4 
5     Be aware that not all versions of libcgrypt support all hash algorithms; the
6     constructor will throw if the specified algorithm is not supported by the
7     run-time version of libgcrypt. However, if the constructor does not throw,
8     it is safe to assume it will never throw for the same set of parameters
9     (except for the fatal situation that libgcrypt failed allocating memory).
10 
11     Requires linking with libgcrypt:
12             -L-lgcrypt
13 
14     Copyright:
15         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
16         All rights reserved.
17 
18     License:
19         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
20         Alternatively, this file may be distributed under the terms of the Tango
21         3-Clause BSD License (see LICENSE_BSD.txt for details).
22 
23 *******************************************************************************/
24 
25 module ocean.util.cipher.gcrypt.HMAC;
26 
27 
28 import ocean.util.cipher.gcrypt.core.MessageDigestCore;
29 import ocean.transition;
30 import ocean.util.cipher.gcrypt.c.md;
31 
32 /******************************************************************************/
33 
34 public class HMAC: MessageDigestCore
35 {
36     import ocean.util.cipher.gcrypt.core.Gcrypt: GcryptException;
37 
38     /***************************************************************************
39 
40         Constructor.
41 
42         Params:
43             algorithm = the hash algorithm to use
44             flags     = flags to `gcry_md_open()`
45 
46         Throws:
47             `GcryptException` on error. There are two possible error causes:
48               - The parameters are invalid or not supported by the libcrypt
49                 of the run-time enviromnent.
50               - libgcrypt failed allocating memory.
51 
52     ***************************************************************************/
53 
54     public this ( gcry_md_algos algorithm, gcry_md_flags flags = cast(gcry_md_flags)0,
55                      istring file = __FILE__, int line = __LINE__ )
56     {
57         super(algorithm, flags | flags.GCRY_MD_FLAG_HMAC, file, line);
58     }
59 
60     /***************************************************************************
61 
62         Calculates the HMAC from the authentication key and the input data.
63 
64         Discards the result of a previous hash calculation, invalidating and
65         overwriting a previously returned result.
66 
67         An error can be caused only by the parameters passed to the constructor.
68         If this method does not throw, it is safe to assume it will never throw
69         for the same set of constructor parameters.
70 
71         The length of the returned hash is the return value of
72         `gcry_md_get_algo_dlen(algorithm)` for the algorithm passed to the
73         constructor of this class.
74 
75         An empty `key` and/or empty `input_data` are tolerated.
76 
77         Params:
78             key        = the HMAC key
79             input_data = the data to hash, which will be concatenated
80 
81         Returns:
82             the resuting HMAC.
83 
84         Throws:
85             `GcryptException` on error.
86 
87     ***************************************************************************/
88 
89     public ubyte[] calculate ( Const!(ubyte)[] key, Const!(ubyte)[][] input_data ... )
90     {
91         gcry_md_reset(this.md);
92 
93         if (key.length)
94         {
95             GcryptException.throwNewIfGcryptError(
96                 gcry_md_setkey(this.md, key.ptr, key.length)
97             );
98         }
99 
100         return this.calculate_(input_data);
101     }
102 }
103 
104 /******************************************************************************/
105 
106 version ( UnitTest )
107 {
108     import ocean.core.Test;
109 }
110 
111 unittest
112 {
113     // https://tools.ietf.org/html/rfc4231#section-4.2
114     static Immut!(ubyte)[] sha224_hmac = [
115         0x89, 0x6f, 0xb1, 0x12, 0x8a, 0xbb, 0xdf,
116         0x19, 0x68, 0x32, 0x10, 0x7c, 0xd4, 0x9d,
117         0xf3, 0x3f, 0x47, 0xb4, 0xb1, 0x16, 0x99,
118         0x12, 0xba, 0x4f, 0x53, 0x68, 0x4b, 0x22
119     ];
120 
121     Immut!(ubyte)[20] key = 0x0b;
122     scope hmacgen = new HMAC(gcry_md_algos.GCRY_MD_SHA224);
123     test!("==")(
124         hmacgen.calculate(
125             key,
126             cast(Immut!(ubyte)[])"Hi",
127             cast(Immut!(ubyte)[])" ",
128             cast(Immut!(ubyte)[])"There"
129         ),
130         sha224_hmac
131     );
132 
133     test!("==")(
134         hmacgen.calculate(
135             key,
136             [
137                 cast(Immut!(ubyte)[])"Hi",
138                 cast(Immut!(ubyte)[])" ",
139                 cast(Immut!(ubyte)[])"There"
140             ]
141         ),
142         sha224_hmac
143     );
144 }