minor
[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 /**
36  * @brief Compute the HMAC
37  * @param mac gcrypt MAC handle
38  * @param key HMAC key
39  * @param key_len length of key
40  * @param buf message to be processed
41  * @param buf_len length of buf
42  * @return HMAC, freed by caller via gcry_md_close/_reset
43  */
44 static void *
45 doHMAC (gcry_md_hd_t mac, const void *key, const size_t key_len,
46     const void *buf, const size_t buf_len)
47 {
48   gcry_md_setkey (mac, key, key_len);
49   gcry_md_write (mac, buf, buf_len);
50
51   return (void *) gcry_md_read (mac, 0);
52 }
53
54 /**
55  * @brief Generate pseudo-random key
56  * @param mac gcrypt HMAC handle
57  * @param xts salt
58  * @param xts_len length of the salt
59  * @param skm source key material
60  * @param skm_len length of skm
61  * @param prk result buffer (allocated by caller; at least gcry_md_dlen() bytes)
62  * @return GNUNET_YES on success
63  */
64 static int
65 getPRK (gcry_md_hd_t mac, const void *xts, const unsigned long long xts_len,
66     const void *skm, const unsigned long long skm_len, void *prk)
67 {
68   void *ret;
69
70   ret = doHMAC (mac, xts, xts_len, skm, skm_len);
71   if (ret == NULL)
72     return GNUNET_SYSERR;
73   memcpy (prk, ret, gcry_md_get_algo_dlen (gcry_md_get_algo (mac)));
74
75   return GNUNET_YES;
76 }
77
78 static void dump(void *p, unsigned int l)
79 {
80   unsigned int i;
81
82   printf("\n");
83   for (i = 0; i < l; i++)
84     {
85       printf("%2x", (int) ((unsigned char *) p)[i]);
86     }
87   printf("\n");
88 }
89
90 /**
91  * @brief Derive key
92  * @param xtr_algo hash algorithm for the extraction phase, GCRY_MD_...
93  * @param prf_algo hash algorithm for the expansion phase, GCRY_MD_...
94  * @param xts salt
95  * @param xts_len length of xts
96  * @param skm source key material
97  * @param skm_len length of skm
98  * @param ctx context info
99  * @param ctx_len length of ctx
100  * @param out_len desired length of the derived key
101  * @param result buffer for the derived key, allocated by caller
102  * @return GNUNET_YES on success
103  */
104 int
105 GNUNET_CRYPTO_hkdf (int xtr_algo, int prf_algo, const void *xts,
106     const size_t xts_len, const void *skm, const size_t skm_len,
107     const void *ctx, const size_t ctx_len, const unsigned long long out_len,
108     void *result)
109 {
110   void *prk, *hc, *plain;
111   unsigned long long plain_len;
112   unsigned long i, t, d;
113   unsigned int k, xtr_len;
114   int ret;
115   gcry_md_hd_t xtr, prf;
116
117   prk = plain = NULL;
118   xtr_len = gcry_md_get_algo_dlen (xtr_algo);
119   k = gcry_md_get_algo_dlen (prf_algo);
120   gcry_md_open(&xtr, xtr_algo, GCRY_MD_FLAG_HMAC);
121   gcry_md_open(&prf, prf_algo, GCRY_MD_FLAG_HMAC);
122
123   if (out_len > (2 ^ 32 * k) || !xtr_algo || !prf_algo)
124     return GNUNET_SYSERR;
125
126   prk = GNUNET_malloc (xtr_len);
127
128   memset (result, 0, out_len);
129   gcry_md_reset (xtr);
130   if (getPRK (xtr, xts, xts_len, skm, skm_len, prk)
131       != GNUNET_YES)
132     goto hkdf_error;
133 dump(prk, xtr_len);
134
135   /* K(1) */
136   plain_len = k + ctx_len + 4;
137   plain = GNUNET_malloc (plain_len);
138   memset (plain, 0, k);
139   memcpy (plain + k, ctx, ctx_len);
140   t = out_len / k;
141   if (t > 0)
142     {
143       memset (plain + k + ctx_len, 0, 4);
144       gcry_md_reset (prf);
145 dump(plain, plain_len);
146       hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
147       if (hc == NULL)
148         goto hkdf_error;
149       memcpy (result, hc, k);
150       result += k;
151     }
152
153   /* K(i+1) */
154   for (i = 1; i < t; i++)
155     {
156       memcpy (plain, result - k, k);
157       memcpy (plain + k + ctx_len, &i, 4);
158       gcry_md_reset (prf);
159 dump(plain, plain_len);
160       hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
161       if (hc == NULL)
162         goto hkdf_error;
163       memcpy (result, hc, k);
164       result += k;
165     }
166
167   /* K(t):d */
168   d = out_len % k;
169   if (d > 0)
170     {
171       if (t > 0)
172         memcpy (plain, result - k, k);
173       memcpy (plain + k + ctx_len, &i, 4);
174       gcry_md_reset (prf);
175 dump(plain, plain_len);
176       hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
177       if (hc == NULL)
178         goto hkdf_error;
179       memcpy (result, hc, d);
180     }
181 dump(result - k, out_len);
182
183   ret = GNUNET_YES;
184   goto hkdf_ok;
185
186 hkdf_error:
187   ret = GNUNET_SYSERR;
188 hkdf_ok:
189   GNUNET_free (prk);
190   GNUNET_free_non_null (plain);
191   gcry_md_close (prf);
192   gcry_md_close (xtr);
193
194   return ret;
195 }
196
197
198 /* end of crypto_hkdf.c */