Documented supported ciphers and hash functions.
[crypto.git] / lib / silccrypt / silchmac.c
1 /*
2
3   silchmac.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1999 - 2008 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
20 #include "silccrypto.h"
21
22 /* HMAC context */
23 struct SilcHmacStruct {
24   SilcHmacObject *hmac;
25   SilcHash hash;
26   unsigned char inner_pad[64];
27   unsigned char outer_pad[64];
28   unsigned char *key;
29   unsigned int key_len        : 31;
30   unsigned int allocated_hash : 1;   /* TRUE if the hash was allocated */
31 };
32
33 #ifndef SILC_SYMBIAN
34 /* List of dynamically registered HMACs. */
35 SilcDList silc_hmac_list = NULL;
36 #endif /* SILC_SYMBIAN */
37
38 /* Default hmacs for silc_hmac_register_default(). */
39 const SilcHmacObject silc_default_hmacs[] =
40 {
41   { "hmac-sha256-96", 12 },
42   { "hmac-sha512-96", 12 },
43   { "hmac-sha1-96", 12 },
44   { "hmac-md5-96", 12 },
45   { "hmac-sha256", 32 },
46   { "hmac-sha512", 64 },
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   SilcUInt32 block_len;
58   unsigned char hvalue[SILC_HASH_MAXLEN];
59   int i;
60
61   memset(hmac->inner_pad, 0, sizeof(hmac->inner_pad));
62   memset(hmac->outer_pad, 0, sizeof(hmac->outer_pad));
63
64   block_len = silc_hash_block_len(hash);
65
66   /* If the key length is more than block size of the hash function, the
67      key is hashed. */
68   if (key_len > block_len) {
69     silc_hash_make(hash, key, key_len, hvalue);
70     key = hvalue;
71     key_len = silc_hash_len(hash);
72   }
73
74   /* Copy the key into the pads */
75   memcpy(hmac->inner_pad, key, key_len);
76   memcpy(hmac->outer_pad, key, key_len);
77
78   /* XOR the key with pads */
79   for (i = 0; i < block_len; i++) {
80     hmac->inner_pad[i] ^= 0x36;
81     hmac->outer_pad[i] ^= 0x5c;
82   }
83 }
84
85 /* Registers a new HMAC */
86
87 SilcBool silc_hmac_register(const SilcHmacObject *hmac)
88 {
89 #ifndef SILC_SYMBIAN
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   if (!new)
106     return FALSE;
107   new->name = strdup(hmac->name);
108   new->len = hmac->len;
109
110   /* Add to list */
111   if (silc_hmac_list == NULL)
112     silc_hmac_list = silc_dlist_init();
113   silc_dlist_add(silc_hmac_list, new);
114
115 #endif /* SILC_SYMBIAN */
116   return TRUE;
117 }
118
119 /* Unregister a HMAC */
120
121 SilcBool silc_hmac_unregister(SilcHmacObject *hmac)
122 {
123 #ifndef SILC_SYMBIAN
124   SilcHmacObject *entry;
125
126   SILC_LOG_DEBUG(("Unregistering HMAC"));
127
128   if (!silc_hmac_list)
129     return FALSE;
130
131   silc_dlist_start(silc_hmac_list);
132   while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
133     if (hmac == SILC_ALL_HMACS || entry == hmac) {
134       silc_dlist_del(silc_hmac_list, entry);
135       silc_free(entry->name);
136       silc_free(entry);
137
138       if (silc_dlist_count(silc_hmac_list) == 0) {
139         silc_dlist_uninit(silc_hmac_list);
140         silc_hmac_list = NULL;
141       }
142
143       return TRUE;
144     }
145   }
146
147 #endif /* SILC_SYMBIAN */
148   return FALSE;
149 }
150
151 /* Register default HMACs */
152
153 SilcBool silc_hmac_register_default(void)
154 {
155   /* We use builtin HMACs */
156   return TRUE;
157 }
158
159 /* Unregister all HMACs */
160
161 SilcBool silc_hmac_unregister_all(void)
162 {
163 #ifndef SILC_SYMBIAN
164   SilcHmacObject *entry;
165
166   if (!silc_hmac_list)
167     return FALSE;
168
169   silc_dlist_start(silc_hmac_list);
170   while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
171     silc_hmac_unregister(entry);
172     if (!silc_hmac_list)
173       break;
174   }
175 #endif /* SILC_SYMBIAN */
176   return TRUE;
177 }
178
179 /* Allocates a new SilcHmac object of name of `name'.  The `hash' may
180    be provided as argument.  If provided it is used as the hash function
181    of the HMAC.  If it is NULL then the hash function is allocated and
182    the name of the hash algorithm is derived from the `name'. */
183
184 SilcBool silc_hmac_alloc(const char *name, SilcHash hash, SilcHmac *new_hmac)
185 {
186   SilcHmacObject *entry = NULL;
187   int i;
188
189   SILC_LOG_DEBUG(("Allocating new HMAC"));
190
191   /* Allocate the new object */
192   *new_hmac = silc_calloc(1, sizeof(**new_hmac));
193   if (!(*new_hmac))
194     return FALSE;
195
196   if (!hash) {
197     char *tmp = strdup(name), *hname;
198
199     hname = tmp;
200     if (strchr(hname, '-'))
201       hname = strchr(hname, '-') + 1;
202     if (strchr(hname, '-'))
203       *strchr(hname, '-') = '\0';
204
205     if (!silc_hash_alloc(hname, &hash)) {
206       silc_free(tmp);
207       silc_free(*new_hmac);
208       *new_hmac = NULL;
209       return FALSE;
210     }
211
212     (*new_hmac)->allocated_hash = TRUE;
213     silc_free(tmp);
214   }
215
216   (*new_hmac)->hash = hash;
217
218 #ifndef SILC_SYMBIAN
219   /* Check registered list of HMACs */
220   if (silc_hmac_list) {
221     silc_dlist_start(silc_hmac_list);
222     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
223       if (!strcmp(entry->name, name)) {
224         (*new_hmac)->hmac = entry;
225         return TRUE;
226       }
227     }
228   }
229 #endif /* SILC_SYMBIAN */
230
231   if (!entry) {
232     /* Check builtin list of HMACs */
233     for (i = 0; silc_default_hmacs[i].name; i++) {
234       if (!strcmp(silc_default_hmacs[i].name, name)) {
235         (*new_hmac)->hmac = (SilcHmacObject *)&(silc_default_hmacs[i]);
236         return TRUE;
237       }
238     }
239   }
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   SilcHmacObject *entry;
289   int i;
290
291   if (!name)
292     return FALSE;
293
294 #ifndef SILC_SYMBIAN
295   if (silc_hmac_list) {
296     silc_dlist_start(silc_hmac_list);
297     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
298       if (!strcmp(entry->name, name))
299         return TRUE;
300     }
301   }
302 #endif /* SILC_SYMBIAN */
303
304   for (i = 0; silc_default_hmacs[i].name; i++)
305     if (!strcmp(silc_default_hmacs[i].name, name))
306       return TRUE;
307
308   return FALSE;
309 }
310
311 /* Returns comma separated list of supported HMACs. */
312
313 char *silc_hmac_get_supported()
314 {
315   SilcHmacObject *entry, *entry2;
316   char *list = NULL;
317   int len = 0, i;
318
319 #ifndef SILC_SYMBIAN
320   if (silc_hmac_list) {
321     silc_dlist_start(silc_hmac_list);
322     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
323       len += strlen(entry->name);
324       list = silc_realloc(list, len + 1);
325
326       memcpy(list + (len - strlen(entry->name)),
327              entry->name, strlen(entry->name));
328       memcpy(list + len, ",", 1);
329       len++;
330     }
331   }
332 #endif /* SILC_SYMBIAN */
333
334
335   for (i = 0; silc_default_hmacs[i].name; i++) {
336     entry = (SilcHmacObject *)&(silc_default_hmacs[i]);
337
338     if (silc_hmac_list) {
339       silc_dlist_start(silc_hmac_list);
340       while ((entry2 = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
341         if (!strcmp(entry2->name, entry->name))
342           break;
343       }
344       if (entry2)
345         continue;
346     }
347
348     len += strlen(entry->name);
349     list = silc_realloc(list, len + 1);
350
351     memcpy(list + (len - strlen(entry->name)),
352            entry->name, strlen(entry->name));
353     memcpy(list + len, ",", 1);
354     len++;
355   }
356
357   list[len - 1] = 0;
358
359   return list;
360 }
361
362 /* Sets the HMAC key used in the HMAC creation */
363
364 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
365                        SilcUInt32 key_len)
366 {
367   if (hmac->key) {
368     memset(hmac->key, 0, hmac->key_len);
369     silc_free(hmac->key);
370   }
371   hmac->key = silc_malloc(key_len);
372   if (!hmac->key)
373     return;
374   hmac->key_len = key_len;
375   memcpy(hmac->key, key, key_len);
376 }
377
378 /* Return HMAC key */
379
380 const unsigned char *silc_hmac_get_key(SilcHmac hmac, SilcUInt32 *key_len)
381 {
382   if (key_len)
383     *key_len = hmac->key_len;
384   return (const unsigned char *)hmac->key;
385 }
386
387 /* Create the HMAC. This is thee make_hmac function pointer.  This
388    uses the internal key set with silc_hmac_set_key. */
389
390 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
391                     SilcUInt32 data_len, unsigned char *return_hash,
392                     SilcUInt32 *return_len)
393 {
394   SILC_LOG_DEBUG(("Making HMAC for message"));
395
396   silc_hmac_init(hmac);
397   silc_hmac_update(hmac, data, data_len);
398   silc_hmac_final(hmac, return_hash, return_len);
399 }
400
401 /* Creates HMAC just as above except that this doesn't use the internal
402    key. The key is sent as argument to the function. */
403
404 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
405                              SilcUInt32 data_len,
406                              unsigned char *key, SilcUInt32 key_len,
407                              unsigned char *return_hash,
408                              SilcUInt32 *return_len)
409 {
410   SILC_LOG_DEBUG(("Making HMAC for message"));
411
412   silc_hmac_init_with_key(hmac, key, key_len);
413   silc_hmac_update(hmac, data, data_len);
414   silc_hmac_final(hmac, return_hash, return_len);
415 }
416
417 /* Creates the HMAC just as above except that the hash value is truncated
418    to the truncated_len sent as argument. NOTE: One should not truncate to
419    less than half of the length of original hash value. However, this
420    routine allows these dangerous truncations. */
421
422 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
423                               SilcUInt32 data_len,
424                               SilcUInt32 truncated_len,
425                               unsigned char *return_hash)
426 {
427   unsigned char hvalue[SILC_HASH_MAXLEN];
428
429   SILC_LOG_DEBUG(("Making HMAC for message"));
430
431   silc_hmac_init(hmac);
432   silc_hmac_update(hmac, data, data_len);
433   silc_hmac_final(hmac, return_hash, NULL);
434   memcpy(return_hash, hvalue, truncated_len);
435   memset(hvalue, 0, sizeof(hvalue));
436 }
437
438 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
439
440 void silc_hmac_init(SilcHmac hmac)
441 {
442   silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
443 }
444
445 /* Same as above but with specific key */
446
447 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
448                              SilcUInt32 key_len)
449 {
450   SilcHash hash = hmac->hash;
451   silc_hmac_init_internal(hmac, (unsigned char *)key, key_len);
452   silc_hash_init(hash);
453   silc_hash_update(hash, hmac->inner_pad, silc_hash_block_len(hash));
454 }
455
456 /* Add data to be used in the MAC computation. */
457
458 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
459                       SilcUInt32 data_len)
460 {
461   SilcHash hash = hmac->hash;
462   silc_hash_update(hash, data, data_len);
463 }
464
465 /* Compute the final MAC. */
466
467 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
468                      SilcUInt32 *return_len)
469 {
470   SilcHash hash = hmac->hash;
471   unsigned char mac[SILC_HASH_MAXLEN];
472
473   silc_hash_final(hash, mac);
474   silc_hash_init(hash);
475   silc_hash_update(hash, hmac->outer_pad, silc_hash_block_len(hash));
476   silc_hash_update(hash, mac, silc_hash_len(hash));
477   silc_hash_final(hash, mac);
478   memcpy(return_hash, mac, hmac->hmac->len);
479   memset(mac, 0, sizeof(mac));
480
481   if (return_len)
482     *return_len = hmac->hmac->len;
483 }