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