GNUnet  0.20.0
crypto_hkdf.c
Go to the documentation of this file.
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 
39 #define LOG(kind, ...) GNUNET_log_from (kind, "util-crypto-hkdf", __VA_ARGS__)
40 
44 #define GNUNET_BUILD 1
45 
49 #define DEBUG_HKDF 0
50 
51 
52 #if GNUNET_BUILD
53 
54 #include "platform.h"
55 #include "gnunet_util_lib.h"
56 #include "benchmark.h"
57 #else
58 #define GNUNET_NO 0
59 #define GNUNET_YES 1
60 #define GNUNET_SYSERR -1
61 #include <stdlib.h>
62 #endif
63 
64 #include <gcrypt.h>
65 
66 
77 static const void *
78 doHMAC (gcry_md_hd_t mac,
79  const void *key,
80  size_t key_len,
81  const void *buf,
82  size_t buf_len)
83 {
84  if (GPG_ERR_NO_ERROR !=
85  gcry_md_setkey (mac, key, key_len))
86  {
87  GNUNET_break (0);
88  return NULL;
89  }
90  gcry_md_write (mac,
91  buf,
92  buf_len);
93  return (const void *) gcry_md_read (mac, 0);
94 }
95 
96 
107 static enum GNUNET_GenericReturnValue
108 getPRK (gcry_md_hd_t mac,
109  const void *xts,
110  size_t xts_len,
111  const void *skm,
112  size_t skm_len,
113  void *prk)
114 {
115  const void *ret;
116  size_t dlen;
117 
118  dlen = gcry_md_get_algo_dlen (gcry_md_get_algo (mac));
119 
120  /* sanity check to bound stack allocation */
121  GNUNET_assert (dlen <= 512);
122 
123  /* From RFC 5869:
124  * salt - optional salt value (a non-secret random value);
125  * if not provided, it is set to a string of HashLen zeros. */
126 
127  if (0 == xts_len)
128  {
129  char zero_salt[dlen];
130 
131  memset (zero_salt, 0, dlen);
132  ret = doHMAC (mac, zero_salt, dlen, skm, skm_len);
133  }
134  else
135  {
136  ret = doHMAC (mac, xts, xts_len, skm, skm_len);
137  }
138  if (NULL == ret)
139  return GNUNET_SYSERR;
140  GNUNET_memcpy (prk,
141  ret,
142  dlen);
143  return GNUNET_YES;
144 }
145 
146 
147 #if DEBUG_HKDF
148 static void
149 dump (const char *src,
150  const void *p,
151  unsigned int l)
152 {
153  printf ("\n%s: ", src);
154  for (unsigned int i = 0; i < l; i++)
155  {
156  printf ("%2x", (int) ((const unsigned char *) p)[i]);
157  }
158  printf ("\n");
159 }
160 
161 
162 #endif
163 
164 
167  size_t out_len,
168  int xtr_algo,
169  int prf_algo,
170  const void *xts,
171  size_t xts_len,
172  const void *skm,
173  size_t skm_len,
174  va_list argp)
175 {
176  gcry_md_hd_t xtr;
177  gcry_md_hd_t prf;
178  const void *hc;
179  unsigned long i;
180  unsigned long t;
181  unsigned long d;
182  unsigned int k = gcry_md_get_algo_dlen (prf_algo);
183  unsigned int xtr_len = gcry_md_get_algo_dlen (xtr_algo);
184  char prk[xtr_len];
185  int ret;
186  size_t ctx_len;
187  va_list args;
188 
189  BENCHMARK_START (hkdf);
190 
191  if (0 == k)
192  return GNUNET_SYSERR;
193  if (GPG_ERR_NO_ERROR !=
194  gcry_md_open (&xtr,
195  xtr_algo,
196  GCRY_MD_FLAG_HMAC))
197  return GNUNET_SYSERR;
198  if (GPG_ERR_NO_ERROR !=
199  gcry_md_open (&prf,
200  prf_algo,
201  GCRY_MD_FLAG_HMAC))
202  {
203  gcry_md_close (xtr);
204  return GNUNET_SYSERR;
205  }
206  va_copy (args, argp);
207 
208  ctx_len = 0;
209  while (NULL != va_arg (args, void *))
210  {
211  size_t nxt = va_arg (args, size_t);
212  if (nxt + ctx_len < nxt)
213  {
214  /* integer overflow */
215  GNUNET_break (0);
216  va_end (args);
217  goto hkdf_error;
218  }
219  ctx_len += nxt;
220  }
221 
222  va_end (args);
223 
224  if ( (k + ctx_len < ctx_len) ||
225  (k + ctx_len + 1 < ctx_len) )
226  {
227  /* integer overflow */
228  GNUNET_break (0);
229  goto hkdf_error;
230  }
231 
232  memset (result, 0, out_len);
233  if (GNUNET_YES !=
234  getPRK (xtr, xts, xts_len, skm, skm_len, prk))
235  goto hkdf_error;
236 #if DEBUG_HKDF
237  dump ("PRK", prk, xtr_len);
238 #endif
239 
240  t = out_len / k;
241  d = out_len % k;
242 
243  /* K(1) */
244  {
245  size_t plain_len = k + ctx_len + 1;
246  char *plain;
247  const void *ctx;
248  char *dst;
249 
250  plain = GNUNET_malloc (plain_len);
251  dst = plain + k;
252  va_copy (args, argp);
253  while ((ctx = va_arg (args, void *)))
254  {
255  size_t len;
256 
257  len = va_arg (args, size_t);
258  GNUNET_memcpy (dst, ctx, len);
259  dst += len;
260  }
261  va_end (args);
262 
263  if (t > 0)
264  {
265  plain[k + ctx_len] = (char) 1;
266 #if DEBUG_HKDF
267  dump ("K(1)", plain, plain_len);
268 #endif
269  hc = doHMAC (prf, prk, xtr_len, &plain[k], ctx_len + 1);
270  if (hc == NULL)
271  {
272  GNUNET_free (plain);
273  goto hkdf_error;
274  }
275  GNUNET_memcpy (result, hc, k);
276  result += k;
277  }
278 
279  /* K(i+1) */
280  for (i = 1; i < t; i++)
281  {
282  GNUNET_memcpy (plain, result - k, k);
283  plain[k + ctx_len] = (char) (i + 1);
284  gcry_md_reset (prf);
285 #if DEBUG_HKDF
286  dump ("K(i+1)", plain, plain_len);
287 #endif
288  hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
289  if (NULL == hc)
290  {
291  GNUNET_free (plain);
292  goto hkdf_error;
293  }
294  GNUNET_memcpy (result, hc, k);
295  result += k;
296  }
297 
298  /* K(t):d */
299  if (d > 0)
300  {
301  if (t > 0)
302  {
303  GNUNET_memcpy (plain, result - k, k);
304  i++;
305  }
306  plain[k + ctx_len] = (char) i;
307  gcry_md_reset (prf);
308 #if DEBUG_HKDF
309  dump ("K(t):d", plain, plain_len);
310 #endif
311  if (t > 0)
312  hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
313  else
314  hc = doHMAC (prf, prk, xtr_len, plain + k, plain_len - k);
315  if (hc == NULL)
316  {
317  GNUNET_free (plain);
318  goto hkdf_error;
319  }
320  GNUNET_memcpy (result, hc, d);
321  }
322 #if DEBUG_HKDF
323  dump ("result", result - k, out_len);
324 #endif
325 
326  ret = GNUNET_YES;
327  GNUNET_free (plain);
328  goto hkdf_ok;
329  }
330 hkdf_error:
331  ret = GNUNET_SYSERR;
332 hkdf_ok:
333  gcry_md_close (xtr);
334  gcry_md_close (prf);
335  BENCHMARK_END (hkdf);
336  return ret;
337 }
338 
339 
342  size_t out_len,
343  int xtr_algo,
344  int prf_algo,
345  const void *xts,
346  size_t xts_len,
347  const void *skm,
348  size_t skm_len, ...)
349 {
350  va_list argp;
352 
353  va_start (argp, skm_len);
354  ret =
356  out_len,
357  xtr_algo,
358  prf_algo,
359  xts,
360  xts_len,
361  skm,
362  skm_len,
363  argp);
364  va_end (argp);
365  return ret;
366 }
367 
368 
369 /* end of crypto_hkdf.c */
benchmarking for various operations
#define BENCHMARK_START(opname)
Definition: benchmark.h:57
#define BENCHMARK_END(opname)
Definition: benchmark.h:58
static enum GNUNET_GenericReturnValue getPRK(gcry_md_hd_t mac, const void *xts, size_t xts_len, const void *skm, size_t skm_len, void *prk)
Generate pseudo-random key.
Definition: crypto_hkdf.c:108
static const void * doHMAC(gcry_md_hd_t mac, const void *key, size_t key_len, const void *buf, size_t buf_len)
Compute the HMAC.
Definition: crypto_hkdf.c:78
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
static struct LoggingHandle * l
static int dump
Dump the database.
struct GNUNET_HashCode key
The key used in the DHT.
uint16_t len
length of data (which is always a uint32_t, but presumably this can be used to specify that fewer byt...
static int result
Global testing status.
static char buf[2048]
static struct GNUNET_OS_Process * p
Helper process we started.
Definition: gnunet-uri.c:38
static struct GNUNET_DNSSTUB_Context * ctx
Context for DNS resolution.
static struct GNUNET_SCHEDULER_Task * t
Main task.
enum GNUNET_GenericReturnValue GNUNET_CRYPTO_hkdf_v(void *result, size_t out_len, int xtr_algo, int prf_algo, const void *xts, size_t xts_len, const void *skm, size_t skm_len, va_list argp)
Derive key.
Definition: crypto_hkdf.c:166
enum GNUNET_GenericReturnValue GNUNET_CRYPTO_hkdf(void *result, size_t out_len, int xtr_algo, int prf_algo, const void *xts, size_t xts_len, const void *skm, size_t skm_len,...)
Derive key.
Definition: crypto_hkdf.c:341
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
GNUNET_GenericReturnValue
Named constants for return values.
@ GNUNET_YES
@ GNUNET_SYSERR
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur.
#define GNUNET_malloc(size)
Wrapper around malloc.
#define GNUNET_free(ptr)
Wrapper around free.