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