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