5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1999 - 2008 Pekka Riikonen
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.
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.
20 #include "silccrypto.h"
23 struct SilcMacStruct {
26 unsigned char inner_pad[64];
27 unsigned char outer_pad[64];
29 unsigned int key_len : 31;
30 unsigned int allocated_hash : 1; /* TRUE if the hash was allocated */
34 /* List of dynamically registered MACs. */
35 SilcDList silc_mac_list = NULL;
36 #endif /* SILC_SYMBIAN */
38 /* Default macs for silc_mac_register_default(). */
39 const SilcMacObject silc_default_macs[] =
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 },
53 static void silc_mac_init_internal(SilcMac mac, unsigned char *key,
56 SilcHash hash = mac->hash;
58 unsigned char hvalue[SILC_HASH_MAXLEN];
61 memset(mac->inner_pad, 0, sizeof(mac->inner_pad));
62 memset(mac->outer_pad, 0, sizeof(mac->outer_pad));
64 block_len = silc_hash_block_len(hash);
66 /* If the key length is more than block size of the hash function, the
68 if (key_len > block_len) {
69 silc_hash_make(hash, key, key_len, hvalue);
71 key_len = silc_hash_len(hash);
74 /* Copy the key into the pads */
75 memcpy(mac->inner_pad, key, key_len);
76 memcpy(mac->outer_pad, key, key_len);
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;
85 /* Registers a new MAC */
87 SilcBool silc_mac_register(const SilcMacObject *mac)
92 SILC_LOG_DEBUG(("Registering new MAC `%s'", mac->name));
94 /* Check for existing */
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))
104 new = silc_calloc(1, sizeof(*new));
107 new->name = strdup(mac->name);
111 if (silc_mac_list == NULL)
112 silc_mac_list = silc_dlist_init();
113 silc_dlist_add(silc_mac_list, new);
115 #endif /* SILC_SYMBIAN */
119 /* Unregister a MAC */
121 SilcBool silc_mac_unregister(SilcMacObject *mac)
124 SilcMacObject *entry;
126 SILC_LOG_DEBUG(("Unregistering MAC"));
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);
138 if (silc_dlist_count(silc_mac_list) == 0) {
139 silc_dlist_uninit(silc_mac_list);
140 silc_mac_list = NULL;
147 #endif /* SILC_SYMBIAN */
151 /* Register default MACs */
153 SilcBool silc_mac_register_default(void)
155 /* We use builtin MACs */
159 /* Unregister all MACs */
161 SilcBool silc_mac_unregister_all(void)
164 SilcMacObject *entry;
169 silc_dlist_start(silc_mac_list);
170 while ((entry = silc_dlist_get(silc_mac_list)) != SILC_LIST_END) {
171 silc_mac_unregister(entry);
175 #endif /* SILC_SYMBIAN */
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'. */
184 SilcBool silc_mac_alloc(const char *name, SilcMac *new_mac)
186 SilcMacObject *entry = NULL;
187 SilcHash hash = NULL;
190 SILC_LOG_DEBUG(("Allocating new MAC"));
192 /* Allocate the new object */
193 *new_mac = silc_calloc(1, sizeof(**new_mac));
198 char *tmp = strdup(name), *hname;
201 if (strchr(hname, '-'))
202 hname = strchr(hname, '-') + 1;
203 if (strchr(hname, '-'))
204 *strchr(hname, '-') = '\0';
206 if (!silc_hash_alloc(hname, &hash)) {
213 (*new_mac)->allocated_hash = TRUE;
217 (*new_mac)->hash = hash;
220 /* Check registered list of MACs */
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;
230 #endif /* SILC_SYMBIAN */
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]);
247 /* Free's the SilcMac object. */
249 void silc_mac_free(SilcMac mac)
252 if (mac->allocated_hash)
253 silc_hash_free(mac->hash);
256 memset(mac->key, 0, mac->key_len);
264 /* Returns the length of the MAC that the MAC will produce. */
266 SilcUInt32 silc_mac_len(SilcMac mac)
268 return mac->mac->len;
271 /* Get hash context */
273 SilcHash silc_mac_get_hash(SilcMac mac)
278 /* Return name of mac */
280 const char *silc_mac_get_name(SilcMac mac)
282 return mac->mac->name;
285 /* Returns TRUE if MAC `name' is supported. */
287 SilcBool silc_mac_is_supported(const char *name)
289 SilcMacObject *entry;
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))
303 #endif /* SILC_SYMBIAN */
305 for (i = 0; silc_default_macs[i].name; i++)
306 if (!strcmp(silc_default_macs[i].name, name))
312 /* Returns comma separated list of supported MACs. */
314 char *silc_mac_get_supported()
316 SilcMacObject *entry, *entry2;
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);
327 memcpy(list + (len - strlen(entry->name)),
328 entry->name, strlen(entry->name));
329 memcpy(list + len, ",", 1);
333 #endif /* SILC_SYMBIAN */
336 for (i = 0; silc_default_macs[i].name; i++) {
337 entry = (SilcMacObject *)&(silc_default_macs[i]);
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))
349 len += strlen(entry->name);
350 list = silc_realloc(list, len + 1);
352 memcpy(list + (len - strlen(entry->name)),
353 entry->name, strlen(entry->name));
354 memcpy(list + len, ",", 1);
363 /* Sets the MAC key used in the MAC creation */
365 void silc_mac_set_key(SilcMac mac, const unsigned char *key,
369 memset(mac->key, 0, mac->key_len);
372 mac->key = silc_malloc(key_len);
375 mac->key_len = key_len;
376 memcpy(mac->key, key, key_len);
381 const unsigned char *silc_mac_get_key(SilcMac mac, SilcUInt32 *key_len)
384 *key_len = mac->key_len;
385 return (const unsigned char *)mac->key;
388 /* Create the MAC. This is thee make_mac function pointer. This
389 uses the internal key set with silc_mac_set_key. */
391 void silc_mac_make(SilcMac mac, unsigned char *data,
392 SilcUInt32 data_len, unsigned char *return_hash,
393 SilcUInt32 *return_len)
395 SILC_LOG_DEBUG(("Making MAC for message"));
398 silc_mac_update(mac, data, data_len);
399 silc_mac_final(mac, return_hash, return_len);
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. */
405 void silc_mac_make_with_key(SilcMac mac, unsigned char *data,
407 unsigned char *key, SilcUInt32 key_len,
408 unsigned char *return_hash,
409 SilcUInt32 *return_len)
411 SILC_LOG_DEBUG(("Making MAC for message"));
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);
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. */
423 void silc_mac_make_truncated(SilcMac mac, unsigned char *data,
425 SilcUInt32 truncated_len,
426 unsigned char *return_hash)
428 unsigned char hvalue[SILC_HASH_MAXLEN];
430 SILC_LOG_DEBUG(("Making MAC for message"));
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));
439 /* Init MAC for silc_mac_update and silc_mac_final. */
441 void silc_mac_init(SilcMac mac)
443 silc_mac_init_with_key(mac, mac->key, mac->key_len);
446 /* Same as above but with specific key */
448 void silc_mac_init_with_key(SilcMac mac, const unsigned char *key,
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));
457 /* Add data to be used in the MAC computation. */
459 void silc_mac_update(SilcMac mac, const unsigned char *data,
462 SilcHash hash = mac->hash;
463 silc_hash_update(hash, data, data_len);
466 /* Compute the final MAC. */
468 void silc_mac_final(SilcMac mac, unsigned char *return_hash,
469 SilcUInt32 *return_len)
471 SilcHash hash = mac->hash;
472 unsigned char digest[SILC_HASH_MAXLEN];
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));
483 *return_len = mac->mac->len;