GNUnet  0.11.x
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 #include "platform.h"
54 #include "gnunet_crypto_lib.h"
55 #include "benchmark.h"
56 #else
57 #define GNUNET_NO 0
58 #define GNUNET_YES 1
59 #define GNUNET_SYSERR -1
60 #include <stdlib.h>
61 #endif
62 
63 #include <gcrypt.h>
64 
65 
76 static const void *
77 doHMAC (gcry_md_hd_t mac, const void *key, size_t key_len, const void *buf,
78  size_t buf_len)
79 {
80  if (GPG_ERR_NO_ERROR != gcry_md_setkey (mac, key, key_len))
81  {
82  GNUNET_break (0);
83  return NULL;
84  }
85  gcry_md_write (mac, buf, buf_len);
86 
87  return (const void *) gcry_md_read (mac, 0);
88 }
89 
90 
101 static int
102 getPRK (gcry_md_hd_t mac, const void *xts, size_t xts_len, const void *skm,
103  size_t skm_len, void *prk)
104 {
105  const void *ret;
106  size_t dlen;
107 
108  dlen = gcry_md_get_algo_dlen (gcry_md_get_algo (mac));
109 
110  /* sanity check to bound stack allocation */
111  GNUNET_assert (dlen <= 512);
112 
113  /* From RFC 5869:
114  * salt - optional salt value (a non-secret random value);
115  * if not provided, it is set to a string of HashLen zeros. */
116 
117  if (xts_len == 0)
118  {
119  char zero_salt[dlen];
120  memset (zero_salt, 0, dlen);
121  ret = doHMAC (mac, zero_salt, dlen, skm, skm_len);
122  }
123  else
124  {
125  ret = doHMAC (mac, xts, xts_len, skm, skm_len);
126  }
127  if (ret == NULL)
128  return GNUNET_SYSERR;
129  GNUNET_memcpy (prk, ret, dlen);
130 
131  return GNUNET_YES;
132 }
133 
134 
135 #if DEBUG_HKDF
136 static void
137 dump (const char *src, const void *p, unsigned int l)
138 {
139  unsigned int i;
140 
141  printf ("\n%s: ", src);
142  for (i = 0; i < l; i++)
143  {
144  printf ("%2x", (int) ((const unsigned char *) p)[i]);
145  }
146  printf ("\n");
147 }
148 
149 
150 #endif
151 
152 
166 int
167 GNUNET_CRYPTO_hkdf_v (void *result, size_t out_len, int xtr_algo, int prf_algo,
168  const void *xts, size_t xts_len, const void *skm,
169  size_t skm_len, va_list argp)
170 {
171  gcry_md_hd_t xtr;
172  gcry_md_hd_t prf;
173  const void *hc;
174  unsigned long i;
175  unsigned long t;
176  unsigned long d;
177  unsigned int k = gcry_md_get_algo_dlen (prf_algo);
178  unsigned int xtr_len = gcry_md_get_algo_dlen (xtr_algo);
179  char prk[xtr_len];
180  int ret;
181  size_t ctx_len;
182  va_list args;
183 
184  BENCHMARK_START (hkdf);
185 
186  if (0 == k)
187  return GNUNET_SYSERR;
188  if (GPG_ERR_NO_ERROR !=
189  gcry_md_open (&xtr, xtr_algo, GCRY_MD_FLAG_HMAC))
190  return GNUNET_SYSERR;
191  if (GPG_ERR_NO_ERROR !=
192  gcry_md_open (&prf, prf_algo, GCRY_MD_FLAG_HMAC))
193  {
194  gcry_md_close (xtr);
195  return GNUNET_SYSERR;
196  }
197  va_copy (args, argp);
198 
199  ctx_len = 0;
200  while (NULL != va_arg (args, void *))
201  {
202  size_t nxt = va_arg (args, size_t);
203  if (nxt + ctx_len < nxt)
204  {
205  /* integer overflow */
206  GNUNET_break (0);
207  va_end (args);
208  goto hkdf_error;
209  }
210  ctx_len += nxt;
211  }
212 
213  va_end (args);
214 
215  if ( (k + ctx_len < ctx_len) ||
216  (k + ctx_len + 1 < ctx_len) )
217  {
218  /* integer overflow */
219  GNUNET_break (0);
220  goto hkdf_error;
221  }
222 
223  memset (result, 0, out_len);
224  if (getPRK (xtr, xts, xts_len, skm, skm_len, prk) != GNUNET_YES)
225  goto hkdf_error;
226 #if DEBUG_HKDF
227  dump ("PRK", prk, xtr_len);
228 #endif
229 
230  t = out_len / k;
231  d = out_len % k;
232 
233  /* K(1) */
234  {
235  size_t plain_len = k + ctx_len + 1;
236  char *plain;
237  const void *ctx;
238  char *dst;
239 
240  plain = GNUNET_malloc (plain_len);
241  dst = plain + k;
242  va_copy (args, argp);
243  while ((ctx = va_arg (args, void *)))
244  {
245  size_t len;
246 
247  len = va_arg (args, size_t);
248  GNUNET_memcpy (dst, ctx, len);
249  dst += len;
250  }
251  va_end (args);
252 
253  if (t > 0)
254  {
255  plain[k + ctx_len] = (char) 1;
256 #if DEBUG_HKDF
257  dump ("K(1)", plain, plain_len);
258 #endif
259  hc = doHMAC (prf, prk, xtr_len, &plain[k], ctx_len + 1);
260  if (hc == NULL)
261  {
262  GNUNET_free (plain);
263  goto hkdf_error;
264  }
265  GNUNET_memcpy (result, hc, k);
266  result += k;
267  }
268 
269  /* K(i+1) */
270  for (i = 1; i < t; i++)
271  {
272  GNUNET_memcpy (plain, result - k, k);
273  plain[k + ctx_len] = (char) (i + 1);
274  gcry_md_reset (prf);
275 #if DEBUG_HKDF
276  dump ("K(i+1)", plain, plain_len);
277 #endif
278  hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
279  if (hc == NULL)
280  {
281  GNUNET_free (plain);
282  goto hkdf_error;
283  }
284  GNUNET_memcpy (result, hc, k);
285  result += k;
286  }
287 
288  /* K(t):d */
289  if (d > 0)
290  {
291  if (t > 0)
292  {
293  GNUNET_memcpy (plain, result - k, k);
294  i++;
295  }
296  plain[k + ctx_len] = (char) i;
297  gcry_md_reset (prf);
298 #if DEBUG_HKDF
299  dump ("K(t):d", plain, plain_len);
300 #endif
301  if (t > 0)
302  hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
303  else
304  hc = doHMAC (prf, prk, xtr_len, plain + k, plain_len - k);
305  if (hc == NULL)
306  {
307  GNUNET_free (plain);
308  goto hkdf_error;
309  }
310  GNUNET_memcpy (result, hc, d);
311  }
312 #if DEBUG_HKDF
313  dump ("result", result - k, out_len);
314 #endif
315 
316  ret = GNUNET_YES;
317  GNUNET_free (plain);
318  goto hkdf_ok;
319  }
320 hkdf_error:
321  ret = GNUNET_SYSERR;
322 hkdf_ok:
323  gcry_md_close (xtr);
324  gcry_md_close (prf);
325  BENCHMARK_END (hkdf);
326  return ret;
327 }
328 
329 
342 int
343 GNUNET_CRYPTO_hkdf (void *result, size_t out_len, int xtr_algo, int prf_algo,
344  const void *xts, size_t xts_len, const void *skm,
345  size_t skm_len, ...)
346 {
347  va_list argp;
348  int ret;
349 
350  va_start (argp, skm_len);
351  ret =
352  GNUNET_CRYPTO_hkdf_v (result, out_len, xtr_algo, prf_algo, xts, xts_len,
353  skm, skm_len, argp);
354  va_end (argp);
355 
356  return ret;
357 }
358 
359 
360 /* 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 int 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:102
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:77
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:37
static struct GNUNET_DNSSTUB_Context * ctx
Context for DNS resolution.
static struct GNUNET_SCHEDULER_Task * t
Main task.
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
@ GNUNET_YES
Definition: gnunet_common.h:97
@ GNUNET_SYSERR
Definition: gnunet_common.h:93
cryptographic primitives for GNUnet
int 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:167
int 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:343
#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.