audit
[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  * @todo remove GNUNET references
28  * @author Nils Durner
29  *
30  * The following list of people have reviewed this code and considered
31  * it correct on the date given (if you reviewed it, please
32  * have your name added to the list):
33  *
34  * - Christian Grothoff (08.10.2010)
35  * - Nathan Evans (08.10.2010)
36  * - Matthias Wachs (08.10.2010)
37  */
38
39 #include <gcrypt.h>
40
41 #include "platform.h"
42 #include "gnunet_crypto_lib.h"
43
44 #define DEBUG_HKDF GNUNET_NO
45
46 /**
47  * @brief Compute the HMAC
48  * @todo use chunked buffers
49  * @param mac gcrypt MAC handle
50  * @param key HMAC key
51  * @param key_len length of key
52  * @param buf message to be processed
53  * @param buf_len length of buf
54  * @return HMAC, freed by caller via gcry_md_close/_reset
55  */
56 static const void *
57 doHMAC (gcry_md_hd_t mac, 
58         const void *key, size_t key_len,
59         const void *buf, size_t buf_len)
60 {
61   gcry_md_setkey (mac, key, key_len);
62   gcry_md_write (mac, buf, buf_len);
63
64   return (const void *) gcry_md_read (mac, 0);
65 }
66
67 /**
68  * @brief Generate pseudo-random key
69  * @param mac gcrypt HMAC handle
70  * @param xts salt
71  * @param xts_len length of the salt
72  * @param skm source key material
73  * @param skm_len length of skm
74  * @param prk result buffer (allocated by caller; at least gcry_md_dlen() bytes)
75  * @return GNUNET_YES on success
76  */
77 static int
78 getPRK (gcry_md_hd_t mac, 
79         const void *xts, size_t xts_len,
80         const void *skm, size_t skm_len,
81         void *prk)
82 {
83   const void *ret;
84
85   ret = doHMAC (mac, xts, xts_len, skm, skm_len);
86   if (ret == NULL)
87     return GNUNET_SYSERR;
88   memcpy (prk,
89           ret,
90           gcry_md_get_algo_dlen (gcry_md_get_algo (mac)));
91
92   return GNUNET_YES;
93 }
94
95
96 #if DEBUG_HKDF
97 static void 
98 dump(const char *src, 
99      const void *p, 
100      unsigned int l)
101 {
102   unsigned int i;
103
104   printf("\n%s: ", src);
105   for (i = 0; i < l; i++)
106     {
107       printf("%2x", (int) ((const unsigned char *) p)[i]);
108     }
109   printf("\n");
110 }
111 #endif
112
113
114 /**
115  * @brief Derive key
116  * @param result buffer for the derived key, allocated by caller
117  * @param out_len desired length of the derived key
118  * @param xtr_algo hash algorithm for the extraction phase, GCRY_MD_...
119  * @param prf_algo hash algorithm for the expansion phase, GCRY_MD_...
120  * @param xts salt
121  * @param xts_len length of xts
122  * @param skm source key material
123  * @param skm_len length of skm
124  * @param argp va_list of void * & size_t pairs for context chunks
125  * @return GNUNET_YES on success
126  */
127 int
128 GNUNET_CRYPTO_hkdf_v (void *result, size_t out_len,
129                       int xtr_algo, int prf_algo, 
130                       const void *xts, size_t xts_len,
131                       const void *skm, size_t skm_len,
132                       va_list argp)
133 {
134   const void *hc;
135   unsigned long i, t, d;
136   unsigned int k = gcry_md_get_algo_dlen (prf_algo);
137   unsigned int xtr_len = gcry_md_get_algo_dlen (xtr_algo);
138   char prk[xtr_len];
139   int ret;
140   gcry_md_hd_t xtr, prf;
141   size_t ctx_len;
142   va_list args;
143
144   if (k == 0)
145     return GNUNET_SYSERR;
146
147   // FIXME: what is the check for?
148   if (out_len > (2 ^ 32 * k))
149     return GNUNET_SYSERR;
150
151   if (gcry_md_open(&xtr, xtr_algo, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR)
152     return GNUNET_SYSERR;
153
154   if (gcry_md_open(&prf, prf_algo, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR)
155   {
156     gcry_md_close (xtr);
157     return GNUNET_SYSERR;
158   }
159
160   va_copy (args, argp);
161
162   ctx_len = 0;
163   while (NULL != va_arg (args, void *))
164     ctx_len += va_arg (args, size_t);
165   va_end(args);
166
167   memset (result, 0, out_len);
168   if (getPRK (xtr, xts, xts_len, skm, skm_len, prk)
169       != GNUNET_YES)
170     goto hkdf_error;
171 #if DEBUG_HKDF
172   dump("PRK", prk, xtr_len);
173 #endif
174
175   t = out_len / k;
176   d = out_len % k;
177
178   /* K(1) */
179   {
180   size_t plain_len = k + ctx_len + 1;
181   char plain[plain_len];
182       const void *ctx;
183       char *dst;
184
185       dst = plain + k;
186       va_copy (args, argp);
187       while ((ctx = va_arg (args, void *)))
188         {
189           size_t len;
190
191           len = va_arg (args, size_t);
192           memcpy (dst, ctx, len);
193           dst += len;
194         }
195       va_end (args);
196
197   if (t > 0)
198     {
199       memset (plain + k + ctx_len, 1, 1);
200 #if DEBUG_HKDF
201       dump("K(1)", plain, plain_len);
202 #endif
203       hc = doHMAC (prf, 
204                    prk,
205                    xtr_len, &plain[k], ctx_len + 1);
206       if (hc == NULL)
207         goto hkdf_error;
208       memcpy (result, hc, k);
209       result += k;
210     }
211
212   /* K(i+1) */
213   for (i = 1; i < t; i++)
214     {
215       memcpy (plain, result - k, k);
216       memset (plain + k + ctx_len, i + 1, 1);
217       gcry_md_reset (prf);
218 #if DEBUG_HKDF
219       dump("K(i+1)", plain, plain_len);
220 #endif
221       hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
222       if (hc == NULL)
223         goto hkdf_error;
224       memcpy (result, hc, k);
225       result += k;
226     }
227
228   /* K(t):d */
229   if (d > 0)
230     {
231       if (t > 0)
232         memcpy (plain, result - k, k);
233       memset (plain + k + ctx_len, i + 1, 1);
234       gcry_md_reset (prf);
235 #if DEBUG_HKDF
236       dump("K(t):d", plain, plain_len);
237 #endif
238       if (t > 0)
239         hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
240       else
241         hc = doHMAC (prf, prk, xtr_len, plain + k, plain_len - k);
242       if (hc == NULL)
243         goto hkdf_error;
244       memcpy (result, hc, d);
245     }
246 #if DEBUG_HKDF
247   dump("result", result - k, out_len);
248 #endif
249
250   ret = GNUNET_YES;
251   goto hkdf_ok;
252   }
253 hkdf_error:
254   ret = GNUNET_SYSERR;
255 hkdf_ok:
256   gcry_md_close (prf);
257   gcry_md_close (xtr);
258
259   return ret;
260 }
261
262
263 /**
264  * @brief Derive key
265  * @param result buffer for the derived key, allocated by caller
266  * @param out_len desired length of the derived key
267  * @param xtr_algo hash algorithm for the extraction phase, GCRY_MD_...
268  * @param prf_algo hash algorithm for the expansion phase, GCRY_MD_...
269  * @param xts salt
270  * @param xts_len length of xts
271  * @param skm source key material
272  * @param skm_len length of skm
273  * @param ctx context info
274  * @param ctx_len length of ctx
275  * @return GNUNET_YES on success
276  */
277 int
278 GNUNET_CRYPTO_hkdf (void *result, size_t out_len,
279                     int xtr_algo, int prf_algo, 
280                     const void *xts, size_t xts_len,
281                     const void *skm, size_t skm_len, 
282                     ...)
283 {
284   va_list argp;
285   int ret;
286
287   va_start(argp, skm_len);
288   ret = GNUNET_CRYPTO_hkdf_v (result, out_len, xtr_algo, prf_algo, xts,
289       xts_len, skm, skm_len, argp);
290   va_end(argp);
291
292   return ret;
293 }
294
295 /* end of crypto_hkdf.c */