ROBODoc documented the SILC Hash Interface and improved
[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
28   unsigned char *key;
29   SilcUInt32 key_len;
30
31   unsigned char inner_pad[64];
32   unsigned char outer_pad[64];
33   bool allocated_hash;          /* TRUE if the hash was allocated */
34 };
35
36 #ifndef SILC_EPOC
37 /* List of dynamically registered HMACs. */
38 SilcDList silc_hmac_list = NULL;
39 #endif /* SILC_EPOC */
40
41 /* Default hmacs for silc_hmac_register_default(). */
42 const SilcHmacObject silc_default_hmacs[] =
43 {
44   { "hmac-sha1-96", 12 },
45   { "hmac-md5-96", 12 },
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[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   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 bool silc_hmac_register(const SilcHmacObject *hmac)
88 {
89 #ifndef SILC_EPOC
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   new->name = strdup(hmac->name);
106   new->len = hmac->len;
107
108   /* Add to list */
109   if (silc_hmac_list == NULL)
110     silc_hmac_list = silc_dlist_init();
111   silc_dlist_add(silc_hmac_list, new);
112
113 #endif /* SILC_EPOC */
114   return TRUE;
115 }
116
117 /* Unregister a HMAC from the SILC. */
118
119 bool silc_hmac_unregister(SilcHmacObject *hmac)
120 {
121 #ifndef SILC_EPOC
122   SilcHmacObject *entry;
123
124   SILC_LOG_DEBUG(("Unregistering HMAC"));
125
126   if (!silc_hmac_list)
127     return FALSE;
128
129   silc_dlist_start(silc_hmac_list);
130   while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
131     if (hmac == SILC_ALL_HMACS || entry == hmac) {
132       silc_dlist_del(silc_hmac_list, 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 /* Allocates a new SilcHmac object of name of `name'.  The `hash' may
164    be provided as argument.  If provided it is used as the hash function
165    of the HMAC.  If it is NULL then the hash function is allocated and
166    the name of the hash algorithm is derived from the `name'. */
167
168 bool silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
169 {
170   SILC_LOG_DEBUG(("Allocating new HMAC"));
171
172   /* Allocate the new object */
173   *new_hmac = silc_calloc(1, sizeof(**new_hmac));
174
175   if (!hash) {
176     char *tmp = strdup(name), *hname;
177
178     hname = tmp;
179     if (strchr(hname, '-'))
180       hname = strchr(hname, '-') + 1;
181     if (strchr(hname, '-'))
182       *strchr(hname, '-') = '\0';
183
184     if (!silc_hash_alloc(hname, &hash)) {
185       silc_free(tmp);
186       silc_free(*new_hmac);
187       *new_hmac = NULL;
188       return FALSE;
189     }
190
191     (*new_hmac)->allocated_hash = TRUE;
192     silc_free(tmp);
193   }
194
195   (*new_hmac)->hash = hash;
196
197 #ifndef SILC_EPOC
198   if (silc_hmac_list) {
199     SilcHmacObject *entry;
200     silc_dlist_start(silc_hmac_list);
201     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
202       if (!strcmp(entry->name, name)) {
203         (*new_hmac)->hmac = entry; 
204         return TRUE;
205       }
206     }
207   }
208 #else
209   {
210     /* On EPOC which don't have globals we check our constant hash list. */
211     int i;
212     for (i = 0; silc_default_hmacs[i].name; i++) {
213       if (!strcmp(silc_default_hmacs[i].name, name)) {
214         (*new_hmac)->hmac = (SilcHmacObject *)&(silc_default_hmacs[i]);
215         return TRUE;
216       }
217     }
218   }
219 #endif /* SILC_EPOC */
220
221   silc_free(*new_hmac);
222   *new_hmac = NULL;
223   return FALSE;
224 }
225
226 /* Free's the SilcHmac object. */
227
228 void silc_hmac_free(SilcHmac hmac)
229 {
230   if (hmac) {
231     if (hmac->allocated_hash)
232       silc_hash_free(hmac->hash);
233
234     if (hmac->key) {
235       memset(hmac->key, 0, hmac->key_len);
236       silc_free(hmac->key);
237     }
238
239     silc_free(hmac);
240   }
241 }
242
243 /* Returns the length of the MAC that the HMAC will produce. */
244
245 SilcUInt32 silc_hmac_len(SilcHmac hmac)
246 {
247   return hmac->hmac->len;
248 }
249
250 /* Get hash context */
251
252 SilcHash silc_hmac_get_hash(SilcHmac hmac)
253 {
254   return hmac->hash;
255 }
256
257 /* Return name of hmac */
258
259 const char *silc_hmac_get_name(SilcHmac hmac)
260 {
261   return hmac->hmac->name;
262 }
263
264 /* Returns TRUE if HMAC `name' is supported. */
265
266 bool silc_hmac_is_supported(const char *name)
267 {
268 #ifndef SILC_EPOC
269   SilcHmacObject *entry;
270
271   if (!name)
272     return FALSE;
273   
274   if (silc_hmac_list) {
275     silc_dlist_start(silc_hmac_list);
276     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
277       if (!strcmp(entry->name, name))
278         return TRUE;
279     }
280   }
281 #else
282   {
283     int i;
284     for (i = 0; silc_default_hmacs[i].name; i++)
285       if (!strcmp(silc_default_hmacs[i].name, name))
286         return TRUE;
287   }
288 #endif /* SILC_EPOC */
289   return FALSE;
290 }
291
292 /* Returns comma separated list of supported HMACs. */
293
294 char *silc_hmac_get_supported()
295 {
296   SilcHmacObject *entry;
297   char *list = NULL;
298   int len = 0;
299
300 #ifndef SILC_EPOC
301   if (silc_hmac_list) {
302     silc_dlist_start(silc_hmac_list);
303     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
304       len += strlen(entry->name);
305       list = silc_realloc(list, len + 1);
306       
307       memcpy(list + (len - strlen(entry->name)), 
308              entry->name, strlen(entry->name));
309       memcpy(list + len, ",", 1);
310       len++;
311     }
312   }
313 #else
314   {
315     int i;
316     for (i = 0; silc_default_hmacs[i].name; i++) {
317       entry = (SilcHmacObject *)&(silc_default_hmacs[i]);
318       len += strlen(entry->name);
319       list = silc_realloc(list, len + 1);
320       
321       memcpy(list + (len - strlen(entry->name)), 
322              entry->name, strlen(entry->name));
323       memcpy(list + len, ",", 1);
324       len++;
325     }
326   }
327 #endif /* SILC_EPOC */
328
329   list[len - 1] = 0;
330
331   return list;
332 }
333
334 /* Sets the HMAC key used in the HMAC creation */
335
336 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
337                        SilcUInt32 key_len)
338 {
339   if (hmac->key) {
340     memset(hmac->key, 0, hmac->key_len);
341     silc_free(hmac->key);
342   }
343   hmac->key = silc_calloc(key_len, sizeof(unsigned char));
344   hmac->key_len = key_len;
345   memcpy(hmac->key, key, key_len);
346 }
347
348 /* Create the HMAC. This is thee make_hmac function pointer.  This
349    uses the internal key set with silc_hmac_set_key. */
350
351 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
352                     SilcUInt32 data_len, unsigned char *return_hash,
353                     SilcUInt32 *return_len)
354 {
355   SILC_LOG_DEBUG(("Making HMAC for message"));
356
357   silc_hmac_init(hmac);
358   silc_hmac_update(hmac, data, data_len);
359   silc_hmac_final(hmac, return_hash, return_len);
360 }
361
362 /* Creates HMAC just as above except that this doesn't use the internal
363    key. The key is sent as argument to the function. */
364
365 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
366                              SilcUInt32 data_len, 
367                              unsigned char *key, SilcUInt32 key_len,
368                              unsigned char *return_hash,
369                              SilcUInt32 *return_len)
370 {
371   SILC_LOG_DEBUG(("Making HMAC for message"));
372
373   silc_hmac_init_with_key(hmac, key, key_len);
374   silc_hmac_update(hmac, data, data_len);
375   silc_hmac_final(hmac, return_hash, return_len);
376 }
377
378 /* Creates the HMAC just as above except that the hash value is truncated
379    to the truncated_len sent as argument. NOTE: One should not truncate to
380    less than half of the length of original hash value. However, this 
381    routine allows these dangerous truncations. */
382
383 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
384                               SilcUInt32 data_len,
385                               SilcUInt32 truncated_len,
386                               unsigned char *return_hash)
387 {
388   unsigned char hvalue[20];
389
390   SILC_LOG_DEBUG(("Making HMAC for message"));
391
392   silc_hmac_init(hmac);
393   silc_hmac_update(hmac, data, data_len);
394   silc_hmac_final(hmac, return_hash, NULL);
395   memcpy(return_hash, hvalue, truncated_len);
396   memset(hvalue, 0, sizeof(hvalue));
397 }
398
399 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
400
401 void silc_hmac_init(SilcHmac hmac)
402 {
403   silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
404 }
405
406 /* Same as above but with specific key */
407
408 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
409                              SilcUInt32 key_len)
410 {
411   SilcHash hash = hmac->hash;
412   silc_hmac_init_internal(hmac, hmac->key, hmac->key_len);
413   silc_hash_init(hash);
414   silc_hash_update(hash, hmac->inner_pad, silc_hash_block_len(hash));
415 }
416
417 /* Add data to be used in the MAC computation. */
418
419 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
420                       SilcUInt32 data_len)
421 {
422   SilcHash hash = hmac->hash;
423   silc_hash_update(hash, data, data_len);
424 }
425
426 /* Compute the final MAC. */
427
428 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
429                      SilcUInt32 *return_len)
430 {
431   SilcHash hash = hmac->hash;
432   unsigned char mac[20];
433
434   silc_hash_final(hash, mac);
435   silc_hash_init(hash);
436   silc_hash_update(hash, hmac->outer_pad, silc_hash_block_len(hash));
437   silc_hash_update(hash, mac, silc_hash_len(hash));
438   silc_hash_final(hash, mac);
439   memcpy(return_hash, mac, hmac->hmac->len);
440   memset(mac, 0, sizeof(mac));
441
442   if (return_len)
443     *return_len = hmac->hmac->len;
444 }