Merge branch 'topic/mm-fixes' of git://208.110.73.182/silc into silc.1.1.branch
[silc.git] / lib / silccrypt / silchmac.c
1 /*
2
3   silchmac.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1999 - 2006 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22
23 /* HMAC context */
24 struct SilcHmacStruct {
25   SilcHmacObject *hmac;
26   SilcHash hash;
27   unsigned char inner_pad[64];
28   unsigned char outer_pad[64];
29   unsigned char *key;
30   unsigned int key_len        : 31;
31   unsigned int allocated_hash : 1;   /* TRUE if the hash was allocated */
32 };
33
34 #ifndef SILC_SYMBIAN
35 /* List of dynamically registered HMACs. */
36 SilcDList silc_hmac_list = NULL;
37 #endif /* SILC_SYMBIAN */
38
39 /* Default hmacs for silc_hmac_register_default(). */
40 const SilcHmacObject silc_default_hmacs[] =
41 {
42   { "hmac-sha256-96", 12 },
43   { "hmac-sha1-96", 12 },
44   { "hmac-md5-96", 12 },
45   { "hmac-sha256", 32 },
46   { "hmac-sha1", 20 },
47   { "hmac-md5", 16 },
48
49   { NULL, 0 }
50 };
51
52 static void silc_hmac_init_internal(SilcHmac hmac, unsigned char *key,
53                                     SilcUInt32 key_len)
54 {
55   SilcHash hash = hmac->hash;
56   SilcUInt32 block_len;
57   unsigned char hvalue[SILC_HASH_MAXLEN];
58   int i;
59
60   memset(hmac->inner_pad, 0, sizeof(hmac->inner_pad));
61   memset(hmac->outer_pad, 0, sizeof(hmac->outer_pad));
62
63   block_len = silc_hash_block_len(hash);
64
65   /* If the key length is more than block size of the hash function, the
66      key is hashed. */
67   if (key_len > block_len) {
68     silc_hash_make(hash, key, key_len, hvalue);
69     key = hvalue;
70     key_len = silc_hash_len(hash);
71   }
72
73   /* Copy the key into the pads */
74   memcpy(hmac->inner_pad, key, key_len);
75   memcpy(hmac->outer_pad, key, key_len);
76
77   /* XOR the key with pads */
78   for (i = 0; i < block_len; i++) {
79     hmac->inner_pad[i] ^= 0x36;
80     hmac->outer_pad[i] ^= 0x5c;
81   }
82 }
83
84 /* Registers a new HMAC into the SILC. This function is used at the
85    initialization of the SILC. */
86
87 SilcBool silc_hmac_register(const SilcHmacObject *hmac)
88 {
89 #ifndef SILC_SYMBIAN
90   SilcHmacObject *new;
91
92   SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
93
94   /* Check for existing */
95   if (silc_hmac_list) {
96     SilcHmacObject *entry;
97     silc_dlist_start(silc_hmac_list);
98     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
99       if (!strcmp(entry->name, hmac->name))
100         return FALSE;
101     }
102   }
103
104   new = silc_calloc(1, sizeof(*new));
105   if (!new)
106     return FALSE;
107   new->name = strdup(hmac->name);
108   new->len = hmac->len;
109
110   /* Add to list */
111   if (silc_hmac_list == NULL)
112     silc_hmac_list = silc_dlist_init();
113   silc_dlist_add(silc_hmac_list, new);
114
115 #endif /* SILC_SYMBIAN */
116   return TRUE;
117 }
118
119 /* Unregister a HMAC from the SILC. */
120
121 SilcBool silc_hmac_unregister(SilcHmacObject *hmac)
122 {
123 #ifndef SILC_SYMBIAN
124   SilcHmacObject *entry;
125
126   SILC_LOG_DEBUG(("Unregistering HMAC"));
127
128   if (!silc_hmac_list)
129     return FALSE;
130
131   silc_dlist_start(silc_hmac_list);
132   while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
133     if (hmac == SILC_ALL_HMACS || entry == hmac) {
134       silc_dlist_del(silc_hmac_list, entry);
135       silc_free(entry->name);
136       silc_free(entry);
137
138       if (silc_dlist_count(silc_hmac_list) == 0) {
139         silc_dlist_uninit(silc_hmac_list);
140         silc_hmac_list = NULL;
141       }
142
143       return TRUE;
144     }
145   }
146
147 #endif /* SILC_SYMBIAN */
148   return FALSE;
149 }
150
151 /* Function that registers all the default hmacs (all builtin ones).
152    The application may use this to register the default hmacs if
153    specific hmacs in any specific order is not wanted. */
154
155 SilcBool silc_hmac_register_default(void)
156 {
157 #ifndef SILC_SYMBIAN
158   int i;
159
160   for (i = 0; silc_default_hmacs[i].name; i++)
161     silc_hmac_register(&(silc_default_hmacs[i]));
162
163 #endif /* SILC_SYMBIAN */
164   return TRUE;
165 }
166
167 SilcBool silc_hmac_unregister_all(void)
168 {
169 #ifndef SILC_SYMBIAN
170   SilcHmacObject *entry;
171
172   if (!silc_hmac_list)
173     return FALSE;
174
175   silc_dlist_start(silc_hmac_list);
176   while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
177     silc_hmac_unregister(entry);
178     if (!silc_hmac_list)
179       break;
180   }
181 #endif /* SILC_SYMBIAN */
182   return TRUE;
183 }
184
185 /* Allocates a new SilcHmac object of name of `name'.  The `hash' may
186    be provided as argument.  If provided it is used as the hash function
187    of the HMAC.  If it is NULL then the hash function is allocated and
188    the name of the hash algorithm is derived from the `name'. */
189
190 SilcBool silc_hmac_alloc(const char *name, SilcHash hash, SilcHmac *new_hmac)
191 {
192   SILC_LOG_DEBUG(("Allocating new HMAC"));
193
194   /* Allocate the new object */
195   *new_hmac = silc_calloc(1, sizeof(**new_hmac));
196   if (!(*new_hmac))
197     return FALSE;
198
199   if (!hash) {
200     char *tmp = strdup(name), *hname;
201
202     hname = tmp;
203     if (strchr(hname, '-'))
204       hname = strchr(hname, '-') + 1;
205     if (strchr(hname, '-'))
206       *strchr(hname, '-') = '\0';
207
208     if (!silc_hash_alloc(hname, &hash)) {
209       silc_free(tmp);
210       silc_free(*new_hmac);
211       *new_hmac = NULL;
212       return FALSE;
213     }
214
215     (*new_hmac)->allocated_hash = TRUE;
216     silc_free(tmp);
217   }
218
219   (*new_hmac)->hash = hash;
220
221 #ifndef SILC_SYMBIAN
222   if (silc_hmac_list) {
223     SilcHmacObject *entry;
224     silc_dlist_start(silc_hmac_list);
225     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
226       if (!strcmp(entry->name, name)) {
227         (*new_hmac)->hmac = entry;
228         return TRUE;
229       }
230     }
231   }
232 #else
233   {
234     /* On EPOC which don't have globals we check our constant hash list. */
235     int i;
236     for (i = 0; silc_default_hmacs[i].name; i++) {
237       if (!strcmp(silc_default_hmacs[i].name, name)) {
238         (*new_hmac)->hmac = (SilcHmacObject *)&(silc_default_hmacs[i]);
239         return TRUE;
240       }
241     }
242   }
243 #endif /* SILC_SYMBIAN */
244
245   silc_free(*new_hmac);
246   *new_hmac = NULL;
247   return FALSE;
248 }
249
250 /* Free's the SilcHmac object. */
251
252 void silc_hmac_free(SilcHmac hmac)
253 {
254   if (hmac) {
255     if (hmac->allocated_hash)
256       silc_hash_free(hmac->hash);
257
258     if (hmac->key) {
259       memset(hmac->key, 0, hmac->key_len);
260       silc_free(hmac->key);
261     }
262
263     silc_free(hmac);
264   }
265 }
266
267 /* Returns the length of the MAC that the HMAC will produce. */
268
269 SilcUInt32 silc_hmac_len(SilcHmac hmac)
270 {
271   return hmac->hmac->len;
272 }
273
274 /* Get hash context */
275
276 SilcHash silc_hmac_get_hash(SilcHmac hmac)
277 {
278   return hmac->hash;
279 }
280
281 /* Return name of hmac */
282
283 const char *silc_hmac_get_name(SilcHmac hmac)
284 {
285   return hmac->hmac->name;
286 }
287
288 /* Returns TRUE if HMAC `name' is supported. */
289
290 SilcBool silc_hmac_is_supported(const char *name)
291 {
292 #ifndef SILC_SYMBIAN
293   SilcHmacObject *entry;
294
295   if (!name)
296     return FALSE;
297
298   if (silc_hmac_list) {
299     silc_dlist_start(silc_hmac_list);
300     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
301       if (!strcmp(entry->name, name))
302         return TRUE;
303     }
304   }
305 #else
306   {
307     int i;
308     for (i = 0; silc_default_hmacs[i].name; i++)
309       if (!strcmp(silc_default_hmacs[i].name, name))
310         return TRUE;
311   }
312 #endif /* SILC_SYMBIAN */
313   return FALSE;
314 }
315
316 /* Returns comma separated list of supported HMACs. */
317
318 char *silc_hmac_get_supported()
319 {
320   SilcHmacObject *entry;
321   char *list = NULL;
322   int len = 0;
323
324 #ifndef SILC_SYMBIAN
325   if (silc_hmac_list) {
326     silc_dlist_start(silc_hmac_list);
327     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
328       len += strlen(entry->name);
329       list = silc_realloc(list, len + 1);
330
331       memcpy(list + (len - strlen(entry->name)),
332              entry->name, strlen(entry->name));
333       memcpy(list + len, ",", 1);
334       len++;
335     }
336   }
337 #else
338   {
339     int i;
340     for (i = 0; silc_default_hmacs[i].name; i++) {
341       entry = (SilcHmacObject *)&(silc_default_hmacs[i]);
342       len += strlen(entry->name);
343       list = silc_realloc(list, len + 1);
344
345       memcpy(list + (len - strlen(entry->name)),
346              entry->name, strlen(entry->name));
347       memcpy(list + len, ",", 1);
348       len++;
349     }
350   }
351 #endif /* SILC_SYMBIAN */
352
353   list[len - 1] = 0;
354
355   return list;
356 }
357
358 /* Sets the HMAC key used in the HMAC creation */
359
360 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
361                        SilcUInt32 key_len)
362 {
363   if (hmac->key) {
364     memset(hmac->key, 0, hmac->key_len);
365     silc_free(hmac->key);
366   }
367   hmac->key = silc_malloc(key_len);
368   if (!hmac->key)
369     return;
370   hmac->key_len = key_len;
371   memcpy(hmac->key, key, key_len);
372 }
373
374 /* Return HMAC key */
375
376 const unsigned char *silc_hmac_get_key(SilcHmac hmac, SilcUInt32 *key_len)
377 {
378   if (key_len)
379     *key_len = hmac->key_len;
380   return (const unsigned char *)hmac->key;
381 }
382
383 /* Create the HMAC. This is thee make_hmac function pointer.  This
384    uses the internal key set with silc_hmac_set_key. */
385
386 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
387                     SilcUInt32 data_len, unsigned char *return_hash,
388                     SilcUInt32 *return_len)
389 {
390   SILC_LOG_DEBUG(("Making HMAC for message"));
391
392   silc_hmac_init(hmac);
393   silc_hmac_update(hmac, data, data_len);
394   silc_hmac_final(hmac, return_hash, return_len);
395 }
396
397 /* Creates HMAC just as above except that this doesn't use the internal
398    key. The key is sent as argument to the function. */
399
400 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
401                              SilcUInt32 data_len,
402                              unsigned char *key, SilcUInt32 key_len,
403                              unsigned char *return_hash,
404                              SilcUInt32 *return_len)
405 {
406   SILC_LOG_DEBUG(("Making HMAC for message"));
407
408   silc_hmac_init_with_key(hmac, key, key_len);
409   silc_hmac_update(hmac, data, data_len);
410   silc_hmac_final(hmac, return_hash, return_len);
411 }
412
413 /* Creates the HMAC just as above except that the hash value is truncated
414    to the truncated_len sent as argument. NOTE: One should not truncate to
415    less than half of the length of original hash value. However, this
416    routine allows these dangerous truncations. */
417
418 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
419                               SilcUInt32 data_len,
420                               SilcUInt32 truncated_len,
421                               unsigned char *return_hash)
422 {
423   unsigned char hvalue[SILC_HASH_MAXLEN];
424
425   SILC_LOG_DEBUG(("Making HMAC for message"));
426
427   silc_hmac_init(hmac);
428   silc_hmac_update(hmac, data, data_len);
429   silc_hmac_final(hmac, return_hash, NULL);
430   memcpy(return_hash, hvalue, truncated_len);
431   memset(hvalue, 0, sizeof(hvalue));
432 }
433
434 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
435
436 void silc_hmac_init(SilcHmac hmac)
437 {
438   silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
439 }
440
441 /* Same as above but with specific key */
442
443 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
444                              SilcUInt32 key_len)
445 {
446   SilcHash hash = hmac->hash;
447   silc_hmac_init_internal(hmac, (unsigned char *)key, key_len);
448   silc_hash_init(hash);
449   silc_hash_update(hash, hmac->inner_pad, silc_hash_block_len(hash));
450 }
451
452 /* Add data to be used in the MAC computation. */
453
454 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
455                       SilcUInt32 data_len)
456 {
457   SilcHash hash = hmac->hash;
458   silc_hash_update(hash, data, data_len);
459 }
460
461 /* Compute the final MAC. */
462
463 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
464                      SilcUInt32 *return_len)
465 {
466   SilcHash hash = hmac->hash;
467   unsigned char mac[SILC_HASH_MAXLEN];
468
469   silc_hash_final(hash, mac);
470   silc_hash_init(hash);
471   silc_hash_update(hash, hmac->outer_pad, silc_hash_block_len(hash));
472   silc_hash_update(hash, mac, silc_hash_len(hash));
473   silc_hash_final(hash, mac);
474   memcpy(return_hash, mac, hmac->hmac->len);
475   memset(mac, 0, sizeof(mac));
476
477   if (return_len)
478     *return_len = hmac->hmac->len;
479 }