Added SILC MAC API, removed the SILC HMAC API.
[crypto.git] / lib / silccrypt / silcmac.c
1 /*
2
3   silcmac.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 /* MAC context */
23 struct SilcMacStruct {
24   SilcMacObject *mac;
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 MACs. */
35 SilcDList silc_mac_list = NULL;
36 #endif /* SILC_SYMBIAN */
37
38 /* Default macs for silc_mac_register_default(). */
39 const SilcMacObject silc_default_macs[] =
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_mac_init_internal(SilcMac mac, unsigned char *key,
54                                     SilcUInt32 key_len)
55 {
56   SilcHash hash = mac->hash;
57   SilcUInt32 block_len;
58   unsigned char hvalue[SILC_HASH_MAXLEN];
59   int i;
60
61   memset(mac->inner_pad, 0, sizeof(mac->inner_pad));
62   memset(mac->outer_pad, 0, sizeof(mac->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(mac->inner_pad, key, key_len);
76   memcpy(mac->outer_pad, key, key_len);
77
78   /* XOR the key with pads */
79   for (i = 0; i < block_len; i++) {
80     mac->inner_pad[i] ^= 0x36;
81     mac->outer_pad[i] ^= 0x5c;
82   }
83 }
84
85 /* Registers a new MAC */
86
87 SilcBool silc_mac_register(const SilcMacObject *mac)
88 {
89 #ifndef SILC_SYMBIAN
90   SilcMacObject *new;
91
92   SILC_LOG_DEBUG(("Registering new MAC `%s'", mac->name));
93
94   /* Check for existing */
95   if (silc_mac_list) {
96     SilcMacObject *entry;
97     silc_dlist_start(silc_mac_list);
98     while ((entry = silc_dlist_get(silc_mac_list)) != SILC_LIST_END) {
99       if (!strcmp(entry->name, mac->name))
100         return FALSE;
101     }
102   }
103
104   new = silc_calloc(1, sizeof(*new));
105   if (!new)
106     return FALSE;
107   new->name = strdup(mac->name);
108   new->len = mac->len;
109
110   /* Add to list */
111   if (silc_mac_list == NULL)
112     silc_mac_list = silc_dlist_init();
113   silc_dlist_add(silc_mac_list, new);
114
115 #endif /* SILC_SYMBIAN */
116   return TRUE;
117 }
118
119 /* Unregister a MAC */
120
121 SilcBool silc_mac_unregister(SilcMacObject *mac)
122 {
123 #ifndef SILC_SYMBIAN
124   SilcMacObject *entry;
125
126   SILC_LOG_DEBUG(("Unregistering MAC"));
127
128   if (!silc_mac_list)
129     return FALSE;
130
131   silc_dlist_start(silc_mac_list);
132   while ((entry = silc_dlist_get(silc_mac_list)) != SILC_LIST_END) {
133     if (mac == SILC_ALL_MACS || entry == mac) {
134       silc_dlist_del(silc_mac_list, entry);
135       silc_free(entry->name);
136       silc_free(entry);
137
138       if (silc_dlist_count(silc_mac_list) == 0) {
139         silc_dlist_uninit(silc_mac_list);
140         silc_mac_list = NULL;
141       }
142
143       return TRUE;
144     }
145   }
146
147 #endif /* SILC_SYMBIAN */
148   return FALSE;
149 }
150
151 /* Register default MACs */
152
153 SilcBool silc_mac_register_default(void)
154 {
155   /* We use builtin MACs */
156   return TRUE;
157 }
158
159 /* Unregister all MACs */
160
161 SilcBool silc_mac_unregister_all(void)
162 {
163 #ifndef SILC_SYMBIAN
164   SilcMacObject *entry;
165
166   if (!silc_mac_list)
167     return FALSE;
168
169   silc_dlist_start(silc_mac_list);
170   while ((entry = silc_dlist_get(silc_mac_list)) != SILC_LIST_END) {
171     silc_mac_unregister(entry);
172     if (!silc_mac_list)
173       break;
174   }
175 #endif /* SILC_SYMBIAN */
176   return TRUE;
177 }
178
179 /* Allocates a new SilcMac 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 MAC.  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_mac_alloc(const char *name, SilcMac *new_mac)
185 {
186   SilcMacObject *entry = NULL;
187   SilcHash hash = NULL;
188   int i;
189
190   SILC_LOG_DEBUG(("Allocating new MAC"));
191
192   /* Allocate the new object */
193   *new_mac = silc_calloc(1, sizeof(**new_mac));
194   if (!(*new_mac))
195     return FALSE;
196
197   if (!hash) {
198     char *tmp = strdup(name), *hname;
199
200     hname = tmp;
201     if (strchr(hname, '-'))
202       hname = strchr(hname, '-') + 1;
203     if (strchr(hname, '-'))
204       *strchr(hname, '-') = '\0';
205
206     if (!silc_hash_alloc(hname, &hash)) {
207       silc_free(tmp);
208       silc_free(*new_mac);
209       *new_mac = NULL;
210       return FALSE;
211     }
212
213     (*new_mac)->allocated_hash = TRUE;
214     silc_free(tmp);
215   }
216
217   (*new_mac)->hash = hash;
218
219 #ifndef SILC_SYMBIAN
220   /* Check registered list of MACs */
221   if (silc_mac_list) {
222     silc_dlist_start(silc_mac_list);
223     while ((entry = silc_dlist_get(silc_mac_list)) != SILC_LIST_END) {
224       if (!strcmp(entry->name, name)) {
225         (*new_mac)->mac = entry;
226         return TRUE;
227       }
228     }
229   }
230 #endif /* SILC_SYMBIAN */
231
232   if (!entry) {
233     /* Check builtin list of MACs */
234     for (i = 0; silc_default_macs[i].name; i++) {
235       if (!strcmp(silc_default_macs[i].name, name)) {
236         (*new_mac)->mac = (SilcMacObject *)&(silc_default_macs[i]);
237         return TRUE;
238       }
239     }
240   }
241
242   silc_free(*new_mac);
243   *new_mac = NULL;
244   return FALSE;
245 }
246
247 /* Free's the SilcMac object. */
248
249 void silc_mac_free(SilcMac mac)
250 {
251   if (mac) {
252     if (mac->allocated_hash)
253       silc_hash_free(mac->hash);
254
255     if (mac->key) {
256       memset(mac->key, 0, mac->key_len);
257       silc_free(mac->key);
258     }
259
260     silc_free(mac);
261   }
262 }
263
264 /* Returns the length of the MAC that the MAC will produce. */
265
266 SilcUInt32 silc_mac_len(SilcMac mac)
267 {
268   return mac->mac->len;
269 }
270
271 /* Get hash context */
272
273 SilcHash silc_mac_get_hash(SilcMac mac)
274 {
275   return mac->hash;
276 }
277
278 /* Return name of mac */
279
280 const char *silc_mac_get_name(SilcMac mac)
281 {
282   return mac->mac->name;
283 }
284
285 /* Returns TRUE if MAC `name' is supported. */
286
287 SilcBool silc_mac_is_supported(const char *name)
288 {
289   SilcMacObject *entry;
290   int i;
291
292   if (!name)
293     return FALSE;
294
295 #ifndef SILC_SYMBIAN
296   if (silc_mac_list) {
297     silc_dlist_start(silc_mac_list);
298     while ((entry = silc_dlist_get(silc_mac_list)) != SILC_LIST_END) {
299       if (!strcmp(entry->name, name))
300         return TRUE;
301     }
302   }
303 #endif /* SILC_SYMBIAN */
304
305   for (i = 0; silc_default_macs[i].name; i++)
306     if (!strcmp(silc_default_macs[i].name, name))
307       return TRUE;
308
309   return FALSE;
310 }
311
312 /* Returns comma separated list of supported MACs. */
313
314 char *silc_mac_get_supported()
315 {
316   SilcMacObject *entry, *entry2;
317   char *list = NULL;
318   int len = 0, i;
319
320 #ifndef SILC_SYMBIAN
321   if (silc_mac_list) {
322     silc_dlist_start(silc_mac_list);
323     while ((entry = silc_dlist_get(silc_mac_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 #endif /* SILC_SYMBIAN */
334
335
336   for (i = 0; silc_default_macs[i].name; i++) {
337     entry = (SilcMacObject *)&(silc_default_macs[i]);
338
339     if (silc_mac_list) {
340       silc_dlist_start(silc_mac_list);
341       while ((entry2 = silc_dlist_get(silc_mac_list)) != SILC_LIST_END) {
342         if (!strcmp(entry2->name, entry->name))
343           break;
344       }
345       if (entry2)
346         continue;
347     }
348
349     len += strlen(entry->name);
350     list = silc_realloc(list, len + 1);
351
352     memcpy(list + (len - strlen(entry->name)),
353            entry->name, strlen(entry->name));
354     memcpy(list + len, ",", 1);
355     len++;
356   }
357
358   list[len - 1] = 0;
359
360   return list;
361 }
362
363 /* Sets the MAC key used in the MAC creation */
364
365 void silc_mac_set_key(SilcMac mac, const unsigned char *key,
366                        SilcUInt32 key_len)
367 {
368   if (mac->key) {
369     memset(mac->key, 0, mac->key_len);
370     silc_free(mac->key);
371   }
372   mac->key = silc_malloc(key_len);
373   if (!mac->key)
374     return;
375   mac->key_len = key_len;
376   memcpy(mac->key, key, key_len);
377 }
378
379 /* Return MAC key */
380
381 const unsigned char *silc_mac_get_key(SilcMac mac, SilcUInt32 *key_len)
382 {
383   if (key_len)
384     *key_len = mac->key_len;
385   return (const unsigned char *)mac->key;
386 }
387
388 /* Create the MAC. This is thee make_mac function pointer.  This
389    uses the internal key set with silc_mac_set_key. */
390
391 void silc_mac_make(SilcMac mac, unsigned char *data,
392                     SilcUInt32 data_len, unsigned char *return_hash,
393                     SilcUInt32 *return_len)
394 {
395   SILC_LOG_DEBUG(("Making MAC for message"));
396
397   silc_mac_init(mac);
398   silc_mac_update(mac, data, data_len);
399   silc_mac_final(mac, return_hash, return_len);
400 }
401
402 /* Creates MAC just as above except that this doesn't use the internal
403    key. The key is sent as argument to the function. */
404
405 void silc_mac_make_with_key(SilcMac mac, unsigned char *data,
406                              SilcUInt32 data_len,
407                              unsigned char *key, SilcUInt32 key_len,
408                              unsigned char *return_hash,
409                              SilcUInt32 *return_len)
410 {
411   SILC_LOG_DEBUG(("Making MAC for message"));
412
413   silc_mac_init_with_key(mac, key, key_len);
414   silc_mac_update(mac, data, data_len);
415   silc_mac_final(mac, return_hash, return_len);
416 }
417
418 /* Creates the MAC just as above except that the hash value is truncated
419    to the truncated_len sent as argument. NOTE: One should not truncate to
420    less than half of the length of original hash value. However, this
421    routine allows these dangerous truncations. */
422
423 void silc_mac_make_truncated(SilcMac mac, unsigned char *data,
424                               SilcUInt32 data_len,
425                               SilcUInt32 truncated_len,
426                               unsigned char *return_hash)
427 {
428   unsigned char hvalue[SILC_HASH_MAXLEN];
429
430   SILC_LOG_DEBUG(("Making MAC for message"));
431
432   silc_mac_init(mac);
433   silc_mac_update(mac, data, data_len);
434   silc_mac_final(mac, return_hash, NULL);
435   memcpy(return_hash, hvalue, truncated_len);
436   memset(hvalue, 0, sizeof(hvalue));
437 }
438
439 /* Init MAC for silc_mac_update and silc_mac_final. */
440
441 void silc_mac_init(SilcMac mac)
442 {
443   silc_mac_init_with_key(mac, mac->key, mac->key_len);
444 }
445
446 /* Same as above but with specific key */
447
448 void silc_mac_init_with_key(SilcMac mac, const unsigned char *key,
449                              SilcUInt32 key_len)
450 {
451   SilcHash hash = mac->hash;
452   silc_mac_init_internal(mac, (unsigned char *)key, key_len);
453   silc_hash_init(hash);
454   silc_hash_update(hash, mac->inner_pad, silc_hash_block_len(hash));
455 }
456
457 /* Add data to be used in the MAC computation. */
458
459 void silc_mac_update(SilcMac mac, const unsigned char *data,
460                       SilcUInt32 data_len)
461 {
462   SilcHash hash = mac->hash;
463   silc_hash_update(hash, data, data_len);
464 }
465
466 /* Compute the final MAC. */
467
468 void silc_mac_final(SilcMac mac, unsigned char *return_hash,
469                     SilcUInt32 *return_len)
470 {
471   SilcHash hash = mac->hash;
472   unsigned char digest[SILC_HASH_MAXLEN];
473
474   silc_hash_final(hash, digest);
475   silc_hash_init(hash);
476   silc_hash_update(hash, mac->outer_pad, silc_hash_block_len(hash));
477   silc_hash_update(hash, digest, silc_hash_len(hash));
478   silc_hash_final(hash, digest);
479   memcpy(return_hash, digest, mac->mac->len);
480   memset(digest, 0, sizeof(digest));
481
482   if (return_len)
483     *return_len = mac->mac->len;
484 }