Added SILC Server library.
[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 "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_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-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 into the SILC. This function is used at the
85    initialization of the SILC. */
86
87 SilcBool 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 SilcBool 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       silc_free(entry->name);
134       silc_free(entry);
135
136       if (silc_dlist_count(silc_hmac_list) == 0) {
137         silc_dlist_uninit(silc_hmac_list);
138         silc_hmac_list = NULL;
139       }
140
141       return TRUE;
142     }
143   }
144
145 #endif /* SILC_EPOC */
146   return FALSE;
147 }
148
149 /* Function that registers all the default hmacs (all builtin ones).
150    The application may use this to register the default hmacs if
151    specific hmacs in any specific order is not wanted. */
152
153 SilcBool silc_hmac_register_default(void)
154 {
155 #ifndef SILC_EPOC
156   int i;
157
158   for (i = 0; silc_default_hmacs[i].name; i++)
159     silc_hmac_register(&(silc_default_hmacs[i]));
160
161 #endif /* SILC_EPOC */
162   return TRUE;
163 }
164
165 SilcBool silc_hmac_unregister_all(void)
166 {
167 #ifndef SILC_EPOC
168   SilcHmacObject *entry;
169
170   if (!silc_hmac_list)
171     return FALSE;
172
173   silc_dlist_start(silc_hmac_list);
174   while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
175     silc_hmac_unregister(entry);
176     if (!silc_hmac_list)
177       break;
178   }
179 #endif /* SILC_EPOC */
180   return TRUE;
181 }
182
183 /* Allocates a new SilcHmac object of name of `name'.  The `hash' may
184    be provided as argument.  If provided it is used as the hash function
185    of the HMAC.  If it is NULL then the hash function is allocated and
186    the name of the hash algorithm is derived from the `name'. */
187
188 SilcBool silc_hmac_alloc(const char *name, SilcHash hash, SilcHmac *new_hmac)
189 {
190   SILC_LOG_DEBUG(("Allocating new HMAC"));
191
192   /* Allocate the new object */
193   *new_hmac = silc_calloc(1, sizeof(**new_hmac));
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_EPOC
218   if (silc_hmac_list) {
219     SilcHmacObject *entry;
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 #else
229   {
230     /* On EPOC which don't have globals we check our constant hash list. */
231     int i;
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 #endif /* SILC_EPOC */
240
241   silc_free(*new_hmac);
242   *new_hmac = NULL;
243   return FALSE;
244 }
245
246 /* Free's the SilcHmac object. */
247
248 void silc_hmac_free(SilcHmac hmac)
249 {
250   if (hmac) {
251     if (hmac->allocated_hash)
252       silc_hash_free(hmac->hash);
253
254     if (hmac->key) {
255       memset(hmac->key, 0, hmac->key_len);
256       silc_free(hmac->key);
257     }
258
259     silc_free(hmac);
260   }
261 }
262
263 /* Returns the length of the MAC that the HMAC will produce. */
264
265 SilcUInt32 silc_hmac_len(SilcHmac hmac)
266 {
267   return hmac->hmac->len;
268 }
269
270 /* Get hash context */
271
272 SilcHash silc_hmac_get_hash(SilcHmac hmac)
273 {
274   return hmac->hash;
275 }
276
277 /* Return name of hmac */
278
279 const char *silc_hmac_get_name(SilcHmac hmac)
280 {
281   return hmac->hmac->name;
282 }
283
284 /* Returns TRUE if HMAC `name' is supported. */
285
286 SilcBool silc_hmac_is_supported(const char *name)
287 {
288 #ifndef SILC_EPOC
289   SilcHmacObject *entry;
290
291   if (!name)
292     return FALSE;
293
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 #else
302   {
303     int i;
304     for (i = 0; silc_default_hmacs[i].name; i++)
305       if (!strcmp(silc_default_hmacs[i].name, name))
306         return TRUE;
307   }
308 #endif /* SILC_EPOC */
309   return FALSE;
310 }
311
312 /* Returns comma separated list of supported HMACs. */
313
314 char *silc_hmac_get_supported()
315 {
316   SilcHmacObject *entry;
317   char *list = NULL;
318   int len = 0;
319
320 #ifndef SILC_EPOC
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 #else
334   {
335     int i;
336     for (i = 0; silc_default_hmacs[i].name; i++) {
337       entry = (SilcHmacObject *)&(silc_default_hmacs[i]);
338       len += strlen(entry->name);
339       list = silc_realloc(list, len + 1);
340
341       memcpy(list + (len - strlen(entry->name)),
342              entry->name, strlen(entry->name));
343       memcpy(list + len, ",", 1);
344       len++;
345     }
346   }
347 #endif /* SILC_EPOC */
348
349   list[len - 1] = 0;
350
351   return list;
352 }
353
354 /* Sets the HMAC key used in the HMAC creation */
355
356 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
357                        SilcUInt32 key_len)
358 {
359   if (hmac->key) {
360     memset(hmac->key, 0, hmac->key_len);
361     silc_free(hmac->key);
362   }
363   hmac->key = silc_calloc(key_len, sizeof(unsigned char));
364   hmac->key_len = key_len;
365   memcpy(hmac->key, key, key_len);
366 }
367
368 /* Create the HMAC. This is thee make_hmac function pointer.  This
369    uses the internal key set with silc_hmac_set_key. */
370
371 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
372                     SilcUInt32 data_len, unsigned char *return_hash,
373                     SilcUInt32 *return_len)
374 {
375   SILC_LOG_DEBUG(("Making HMAC for message"));
376
377   silc_hmac_init(hmac);
378   silc_hmac_update(hmac, data, data_len);
379   silc_hmac_final(hmac, return_hash, return_len);
380 }
381
382 /* Creates HMAC just as above except that this doesn't use the internal
383    key. The key is sent as argument to the function. */
384
385 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
386                              SilcUInt32 data_len,
387                              unsigned char *key, SilcUInt32 key_len,
388                              unsigned char *return_hash,
389                              SilcUInt32 *return_len)
390 {
391   SILC_LOG_DEBUG(("Making HMAC for message"));
392
393   silc_hmac_init_with_key(hmac, key, key_len);
394   silc_hmac_update(hmac, data, data_len);
395   silc_hmac_final(hmac, return_hash, return_len);
396 }
397
398 /* Creates the HMAC just as above except that the hash value is truncated
399    to the truncated_len sent as argument. NOTE: One should not truncate to
400    less than half of the length of original hash value. However, this
401    routine allows these dangerous truncations. */
402
403 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
404                               SilcUInt32 data_len,
405                               SilcUInt32 truncated_len,
406                               unsigned char *return_hash)
407 {
408   unsigned char hvalue[SILC_HASH_MAXLEN];
409
410   SILC_LOG_DEBUG(("Making HMAC for message"));
411
412   silc_hmac_init(hmac);
413   silc_hmac_update(hmac, data, data_len);
414   silc_hmac_final(hmac, return_hash, NULL);
415   memcpy(return_hash, hvalue, truncated_len);
416   memset(hvalue, 0, sizeof(hvalue));
417 }
418
419 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
420
421 void silc_hmac_init(SilcHmac hmac)
422 {
423   silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
424 }
425
426 /* Same as above but with specific key */
427
428 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
429                              SilcUInt32 key_len)
430 {
431   SilcHash hash = hmac->hash;
432   silc_hmac_init_internal(hmac, (unsigned char *)key, key_len);
433   silc_hash_init(hash);
434   silc_hash_update(hash, hmac->inner_pad, silc_hash_block_len(hash));
435 }
436
437 /* Add data to be used in the MAC computation. */
438
439 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
440                       SilcUInt32 data_len)
441 {
442   SilcHash hash = hmac->hash;
443   silc_hash_update(hash, data, data_len);
444 }
445
446 /* Compute the final MAC. */
447
448 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
449                      SilcUInt32 *return_len)
450 {
451   SilcHash hash = hmac->hash;
452   unsigned char mac[SILC_HASH_MAXLEN];
453
454   silc_hash_final(hash, mac);
455   silc_hash_init(hash);
456   silc_hash_update(hash, hmac->outer_pad, silc_hash_block_len(hash));
457   silc_hash_update(hash, mac, silc_hash_len(hash));
458   silc_hash_final(hash, mac);
459   memcpy(return_hash, mac, hmac->hmac->len);
460   memset(mac, 0, sizeof(mac));
461
462   if (return_len)
463     *return_len = hmac->hmac->len;
464 }