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 import ocean.util.cipher.gcrypt.core.MessageDigestCore;
28 import ocean.util.cipher.gcrypt.c.md;
29 
30 /******************************************************************************/
31 
32 public class HMAC: MessageDigestCore
33 {
34     import ocean.util.cipher.gcrypt.core.Gcrypt: GcryptException;
35 
36     /***************************************************************************
37 
38         Constructor.
39 
40         Params:
41             algorithm = the hash algorithm to use
42             flags     = flags to `gcry_md_open()`
43 
44         Throws:
45             `GcryptException` on error. There are two possible error causes:
46               - The parameters are invalid or not supported by the libcrypt
47                 of the run-time enviromnent.
48               - libgcrypt failed allocating memory.
49 
50     ***************************************************************************/
51 
52     public this ( gcry_md_algos algorithm, gcry_md_flags flags = cast(gcry_md_flags)0,
53                   string file = __FILE__, int line = __LINE__ )
54     {
55         super(algorithm, flags | flags.GCRY_MD_FLAG_HMAC, file, line);
56     }
57 
58     /***************************************************************************
59 
60         Calculates the HMAC from the authentication key and the input data.
61 
62         Discards the result of a previous hash calculation, invalidating and
63         overwriting a previously returned result.
64 
65         An error can be caused only by the parameters passed to the constructor.
66         If this method does not throw, it is safe to assume it will never throw
67         for the same set of constructor parameters.
68 
69         The length of the returned hash is the return value of
70         `gcry_md_get_algo_dlen(algorithm)` for the algorithm passed to the
71         constructor of this class.
72 
73         An empty `key` and/or empty `input_data` are tolerated.
74 
75         Params:
76             key        = the HMAC key
77             input_data = the data to hash, which will be concatenated
78 
79         Returns:
80             the resuting HMAC.
81 
82         Throws:
83             `GcryptException` on error.
84 
85     ***************************************************************************/
86 
87     public ubyte[] calculate (const(ubyte)[] key, const(ubyte)[][] input_data ... )
88     {
89         gcry_md_reset(this.md);
90 
91         if (key.length)
92         {
93             GcryptException.throwNewIfGcryptError(
94                 gcry_md_setkey(this.md, key.ptr, key.length)
95             );
96         }
97 
98         return this.calculate_(input_data);
99     }
100 }
101 
102 /******************************************************************************/
103 
104 version (unittest)
105 {
106     import ocean.core.Test;
107 }
108 
109 unittest
110 {
111     // https://tools.ietf.org/html/rfc4231#section-4.2
112     static immutable(ubyte)[] sha224_hmac = [
113         0x89, 0x6f, 0xb1, 0x12, 0x8a, 0xbb, 0xdf,
114         0x19, 0x68, 0x32, 0x10, 0x7c, 0xd4, 0x9d,
115         0xf3, 0x3f, 0x47, 0xb4, 0xb1, 0x16, 0x99,
116         0x12, 0xba, 0x4f, 0x53, 0x68, 0x4b, 0x22
117     ];
118 
119     immutable(ubyte)[20] key = 0x0b;
120     scope hmacgen = new HMAC(gcry_md_algos.GCRY_MD_SHA224);
121     test!("==")(
122         hmacgen.calculate(
123             key,
124             cast(immutable(ubyte)[])"Hi",
125             cast(immutable(ubyte)[])" ",
126             cast(immutable(ubyte)[])"There"
127         ),
128         sha224_hmac
129     );
130 
131     test!("==")(
132         hmacgen.calculate(
133             key,
134             [
135                 cast(immutable(ubyte)[])"Hi",
136                 cast(immutable(ubyte)[])" ",
137                 cast(immutable(ubyte)[])"There"
138             ]
139         ),
140         sha224_hmac
141     );
142 }