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