support chunked context parameter for HKDF
[oweals/gnunet.git] / src / util / crypto_hkdf.c
1 /*
2     Copyright (c) 2010 Nils Durner
3
4     Permission is hereby granted, free of charge, to any person obtaining a copy
5     of this software and associated documentation files (the "Software"), to deal
6     in the Software without restriction, including without limitation the rights
7     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8     copies of the Software, and to permit persons to whom the Software is
9     furnished to do so, subject to the following conditions:
10
11     The above copyright notice and this permission notice shall be included in
12     all copies or substantial portions of the Software.
13
14     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20     THE SOFTWARE.
21 */
22
23 /**
24  * @file src/util/crypto_hkdf.c
25  * @brief Hash-based KDF as defined in RFC 5869
26  * @see http://www.rfc-editor.org/rfc/rfc5869.txt
27  * @author Nils Durner
28  */
29
30 #include <gcrypt.h>
31
32 #include "platform.h"
33 #include "gnunet_crypto_lib.h"
34
35 #define DEBUG_HKDF GNUNET_NO
36
37 /**
38  * @brief Compute the HMAC
39  * @param mac gcrypt MAC handle
40  * @param key HMAC key
41  * @param key_len length of key
42  * @param buf message to be processed
43  * @param buf_len length of buf
44  * @return HMAC, freed by caller via gcry_md_close/_reset
45  */
46 static void *
47 doHMAC (gcry_md_hd_t mac, const void *key, const size_t key_len,
48     const void *buf, const size_t buf_len)
49 {
50   gcry_md_setkey (mac, key, key_len);
51   gcry_md_write (mac, buf, buf_len);
52
53   return (void *) gcry_md_read (mac, 0);
54 }
55
56 /**
57  * @brief Generate pseudo-random key
58  * @param mac gcrypt HMAC handle
59  * @param xts salt
60  * @param xts_len length of the salt
61  * @param skm source key material
62  * @param skm_len length of skm
63  * @param prk result buffer (allocated by caller; at least gcry_md_dlen() bytes)
64  * @return GNUNET_YES on success
65  */
66 static int
67 getPRK (gcry_md_hd_t mac, const void *xts, const unsigned long long xts_len,
68     const void *skm, const unsigned long long skm_len, void *prk)
69 {
70   void *ret;
71
72   ret = doHMAC (mac, xts, xts_len, skm, skm_len);
73   if (ret == NULL)
74     return GNUNET_SYSERR;
75   memcpy (prk, ret, gcry_md_get_algo_dlen (gcry_md_get_algo (mac)));
76
77   return GNUNET_YES;
78 }
79
80 #if DEBUG_HKDF
81 static void dump(char *src, void *p, unsigned int l)
82 {
83   unsigned int i;
84
85   printf("\n%s: ", src);
86   for (i = 0; i < l; i++)
87     {
88       printf("%2x", (int) ((unsigned char *) p)[i]);
89     }
90   printf("\n");
91 }
92 #endif
93
94 /**
95  * @brief Derive key
96  * @param result buffer for the derived key, allocated by caller
97  * @param out_len desired length of the derived key
98  * @param xtr_algo hash algorithm for the extraction phase, GCRY_MD_...
99  * @param prf_algo hash algorithm for the expansion phase, GCRY_MD_...
100  * @param xts salt
101  * @param xts_len length of xts
102  * @param skm source key material
103  * @param skm_len length of skm
104  * @param ctx context info
105  * @param ctx_len length of ctx
106  * @return GNUNET_YES on success
107  */
108 int
109 GNUNET_CRYPTO_hkdf (void *result, const unsigned long long out_len,
110     int xtr_algo, int prf_algo, const void *xts, const size_t xts_len,
111     const void *skm, const size_t skm_len, ...)
112 {
113   void *prk, *hc, *plain;
114   unsigned long long plain_len;
115   unsigned long i, t, d;
116   unsigned int k, xtr_len;
117   int ret;
118   gcry_md_hd_t xtr, prf;
119   size_t ctx_len;
120   va_list argp;
121
122   prk = plain = NULL;
123   xtr_len = gcry_md_get_algo_dlen (xtr_algo);
124   k = gcry_md_get_algo_dlen (prf_algo);
125   gcry_md_open(&xtr, xtr_algo, GCRY_MD_FLAG_HMAC);
126   gcry_md_open(&prf, prf_algo, GCRY_MD_FLAG_HMAC);
127
128   if (out_len > (2 ^ 32 * k) || !xtr_algo || !prf_algo)
129     return GNUNET_SYSERR;
130
131   va_start(argp, skm_len);
132   for (ctx_len = 0; va_arg (argp, void *);)
133     ctx_len += va_arg (argp, size_t);
134   va_end(argp);
135
136   prk = GNUNET_malloc (xtr_len);
137
138   memset (result, 0, out_len);
139   gcry_md_reset (xtr);
140   if (getPRK (xtr, xts, xts_len, skm, skm_len, prk)
141       != GNUNET_YES)
142     goto hkdf_error;
143 #if DEBUG_HKDF
144   dump("PRK", prk, xtr_len);
145 #endif
146
147   t = out_len / k;
148   d = out_len % k;
149
150   /* K(1) */
151   plain_len = k + ctx_len + 1;
152   plain = GNUNET_malloc (plain_len);
153   if (t > 0)
154     {
155       void *ctx, *dst;
156
157       dst = plain;
158       va_start (argp, skm_len);
159       while ((ctx = va_arg (argp, void *)))
160         {
161           size_t len;
162
163           len = va_arg (argp, size_t);
164           memcpy (dst, ctx, len);
165           dst += len;
166         }
167       va_end (argp);
168
169       memset (dst, 1, 1);
170       gcry_md_reset (prf);
171 #if DEBUG_HKDF
172       dump("K(1)", plain, plain_len);
173 #endif
174       hc = doHMAC (prf, prk, xtr_len, plain, ctx_len + 1);
175       if (hc == NULL)
176         goto hkdf_error;
177       memcpy (result, hc, k);
178       result += k;
179     }
180
181   if (t > 1 || d > 0)
182     {
183       void *ctx, *dst;
184
185       dst = plain + k;
186       va_start(argp, skm_len);
187       while ((ctx = va_arg (argp, void *)))
188         memcpy (dst, ctx, va_arg (argp, size_t));
189       va_end (argp);
190     }
191
192   /* K(i+1) */
193   for (i = 1; i < t; i++)
194     {
195       memcpy (plain, result - k, k);
196       memset (plain + k + ctx_len, i + 1, 1);
197       gcry_md_reset (prf);
198 #if DEBUG_HKDF
199       dump("K(i+1)", plain, plain_len);
200 #endif
201       hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
202       if (hc == NULL)
203         goto hkdf_error;
204       memcpy (result, hc, k);
205       result += k;
206     }
207
208   /* K(t):d */
209   if (d > 0)
210     {
211       if (t > 0)
212         memcpy (plain, result - k, k);
213       memset (plain + k + ctx_len, i + 1, 1);
214       gcry_md_reset (prf);
215 #if DEBUG_HKDF
216       dump("K(t):d", plain, plain_len);
217 #endif
218       hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
219       if (hc == NULL)
220         goto hkdf_error;
221       memcpy (result, hc, d);
222     }
223 #if DEBUG_HKDF
224   dump("result", result - k, out_len);
225 #endif
226
227   ret = GNUNET_YES;
228   goto hkdf_ok;
229
230 hkdf_error:
231   ret = GNUNET_SYSERR;
232 hkdf_ok:
233   GNUNET_free (prk);
234   GNUNET_free_non_null (plain);
235   gcry_md_close (prf);
236   gcry_md_close (xtr);
237
238   return ret;
239 }
240
241
242 /* end of crypto_hkdf.c */