Static analyzer bug fixes
[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   if (list)
354     list[len - 1] = 0;
355
356   return list;
357 }
358
359 /* Sets the HMAC key used in the HMAC creation */
360
361 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
362                        SilcUInt32 key_len)
363 {
364   if (hmac->key) {
365     memset(hmac->key, 0, hmac->key_len);
366     silc_free(hmac->key);
367   }
368   hmac->key = silc_malloc(key_len);
369   if (!hmac->key)
370     return;
371   hmac->key_len = key_len;
372   memcpy(hmac->key, key, key_len);
373 }
374
375 /* Return HMAC key */
376
377 const unsigned char *silc_hmac_get_key(SilcHmac hmac, SilcUInt32 *key_len)
378 {
379   if (key_len)
380     *key_len = hmac->key_len;
381   return (const unsigned char *)hmac->key;
382 }
383
384 /* Create the HMAC. This is thee make_hmac function pointer.  This
385    uses the internal key set with silc_hmac_set_key. */
386
387 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
388                     SilcUInt32 data_len, unsigned char *return_hash,
389                     SilcUInt32 *return_len)
390 {
391   SILC_LOG_DEBUG(("Making HMAC for message"));
392
393   silc_hmac_init(hmac);
394   silc_hmac_update(hmac, data, data_len);
395   silc_hmac_final(hmac, return_hash, return_len);
396 }
397
398 /* Creates HMAC just as above except that this doesn't use the internal
399    key. The key is sent as argument to the function. */
400
401 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
402                              SilcUInt32 data_len,
403                              unsigned char *key, SilcUInt32 key_len,
404                              unsigned char *return_hash,
405                              SilcUInt32 *return_len)
406 {
407   SILC_LOG_DEBUG(("Making HMAC for message"));
408
409   silc_hmac_init_with_key(hmac, key, key_len);
410   silc_hmac_update(hmac, data, data_len);
411   silc_hmac_final(hmac, return_hash, return_len);
412 }
413
414 /* Creates the HMAC just as above except that the hash value is truncated
415    to the truncated_len sent as argument. NOTE: One should not truncate to
416    less than half of the length of original hash value. However, this
417    routine allows these dangerous truncations. */
418
419 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
420                               SilcUInt32 data_len,
421                               SilcUInt32 truncated_len,
422                               unsigned char *return_hash)
423 {
424   unsigned char hvalue[SILC_HASH_MAXLEN];
425
426   SILC_LOG_DEBUG(("Making HMAC for message"));
427
428   silc_hmac_init(hmac);
429   silc_hmac_update(hmac, data, data_len);
430   silc_hmac_final(hmac, return_hash, NULL);
431   memcpy(return_hash, hvalue, truncated_len);
432   memset(hvalue, 0, sizeof(hvalue));
433 }
434
435 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
436
437 void silc_hmac_init(SilcHmac hmac)
438 {
439   silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
440 }
441
442 /* Same as above but with specific key */
443
444 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
445                              SilcUInt32 key_len)
446 {
447   SilcHash hash = hmac->hash;
448   silc_hmac_init_internal(hmac, (unsigned char *)key, key_len);
449   silc_hash_init(hash);
450   silc_hash_update(hash, hmac->inner_pad, silc_hash_block_len(hash));
451 }
452
453 /* Add data to be used in the MAC computation. */
454
455 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
456                       SilcUInt32 data_len)
457 {
458   SilcHash hash = hmac->hash;
459   silc_hash_update(hash, data, data_len);
460 }
461
462 /* Compute the final MAC. */
463
464 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
465                      SilcUInt32 *return_len)
466 {
467   SilcHash hash = hmac->hash;
468   unsigned char mac[SILC_HASH_MAXLEN];
469
470   silc_hash_final(hash, mac);
471   silc_hash_init(hash);
472   silc_hash_update(hash, hmac->outer_pad, silc_hash_block_len(hash));
473   silc_hash_update(hash, mac, silc_hash_len(hash));
474   silc_hash_final(hash, mac);
475   memcpy(return_hash, mac, hmac->hmac->len);
476   memset(mac, 0, sizeof(mac));
477
478   if (return_len)
479     *return_len = hmac->hmac->len;
480 }