X-Git-Url: http://git.silcnet.org/gitweb/?p=crypto.git;a=blobdiff_plain;f=lib%2Fsilccrypt%2Fsilcmac.c;fp=lib%2Fsilccrypt%2Fsilcmac.c;h=6a2a8b4b4358355bab1c278d95107576846c397c;hp=0000000000000000000000000000000000000000;hb=8cbd4e2524c7e45c506bd00779d05f6608a81f66;hpb=57af98efbd82c4c89c81850f42b02585fed1a16a diff --git a/lib/silccrypt/silcmac.c b/lib/silccrypt/silcmac.c new file mode 100644 index 00000000..6a2a8b4b --- /dev/null +++ b/lib/silccrypt/silcmac.c @@ -0,0 +1,484 @@ +/* + + silcmac.c + + Author: Pekka Riikonen + + Copyright (C) 1999 - 2008 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#include "silccrypto.h" + +/* MAC context */ +struct SilcMacStruct { + SilcMacObject *mac; + SilcHash hash; + unsigned char inner_pad[64]; + unsigned char outer_pad[64]; + unsigned char *key; + unsigned int key_len : 31; + unsigned int allocated_hash : 1; /* TRUE if the hash was allocated */ +}; + +#ifndef SILC_SYMBIAN +/* List of dynamically registered MACs. */ +SilcDList silc_mac_list = NULL; +#endif /* SILC_SYMBIAN */ + +/* Default macs for silc_mac_register_default(). */ +const SilcMacObject silc_default_macs[] = +{ + { "hmac-sha256-96", 12 }, + { "hmac-sha512-96", 12 }, + { "hmac-sha1-96", 12 }, + { "hmac-md5-96", 12 }, + { "hmac-sha256", 32 }, + { "hmac-sha512", 64 }, + { "hmac-sha1", 20 }, + { "hmac-md5", 16 }, + + { NULL, 0 } +}; + +static void silc_mac_init_internal(SilcMac mac, unsigned char *key, + SilcUInt32 key_len) +{ + SilcHash hash = mac->hash; + SilcUInt32 block_len; + unsigned char hvalue[SILC_HASH_MAXLEN]; + int i; + + memset(mac->inner_pad, 0, sizeof(mac->inner_pad)); + memset(mac->outer_pad, 0, sizeof(mac->outer_pad)); + + block_len = silc_hash_block_len(hash); + + /* If the key length is more than block size of the hash function, the + key is hashed. */ + if (key_len > block_len) { + silc_hash_make(hash, key, key_len, hvalue); + key = hvalue; + key_len = silc_hash_len(hash); + } + + /* Copy the key into the pads */ + memcpy(mac->inner_pad, key, key_len); + memcpy(mac->outer_pad, key, key_len); + + /* XOR the key with pads */ + for (i = 0; i < block_len; i++) { + mac->inner_pad[i] ^= 0x36; + mac->outer_pad[i] ^= 0x5c; + } +} + +/* Registers a new MAC */ + +SilcBool silc_mac_register(const SilcMacObject *mac) +{ +#ifndef SILC_SYMBIAN + SilcMacObject *new; + + SILC_LOG_DEBUG(("Registering new MAC `%s'", mac->name)); + + /* Check for existing */ + if (silc_mac_list) { + SilcMacObject *entry; + silc_dlist_start(silc_mac_list); + while ((entry = silc_dlist_get(silc_mac_list)) != SILC_LIST_END) { + if (!strcmp(entry->name, mac->name)) + return FALSE; + } + } + + new = silc_calloc(1, sizeof(*new)); + if (!new) + return FALSE; + new->name = strdup(mac->name); + new->len = mac->len; + + /* Add to list */ + if (silc_mac_list == NULL) + silc_mac_list = silc_dlist_init(); + silc_dlist_add(silc_mac_list, new); + +#endif /* SILC_SYMBIAN */ + return TRUE; +} + +/* Unregister a MAC */ + +SilcBool silc_mac_unregister(SilcMacObject *mac) +{ +#ifndef SILC_SYMBIAN + SilcMacObject *entry; + + SILC_LOG_DEBUG(("Unregistering MAC")); + + if (!silc_mac_list) + return FALSE; + + silc_dlist_start(silc_mac_list); + while ((entry = silc_dlist_get(silc_mac_list)) != SILC_LIST_END) { + if (mac == SILC_ALL_MACS || entry == mac) { + silc_dlist_del(silc_mac_list, entry); + silc_free(entry->name); + silc_free(entry); + + if (silc_dlist_count(silc_mac_list) == 0) { + silc_dlist_uninit(silc_mac_list); + silc_mac_list = NULL; + } + + return TRUE; + } + } + +#endif /* SILC_SYMBIAN */ + return FALSE; +} + +/* Register default MACs */ + +SilcBool silc_mac_register_default(void) +{ + /* We use builtin MACs */ + return TRUE; +} + +/* Unregister all MACs */ + +SilcBool silc_mac_unregister_all(void) +{ +#ifndef SILC_SYMBIAN + SilcMacObject *entry; + + if (!silc_mac_list) + return FALSE; + + silc_dlist_start(silc_mac_list); + while ((entry = silc_dlist_get(silc_mac_list)) != SILC_LIST_END) { + silc_mac_unregister(entry); + if (!silc_mac_list) + break; + } +#endif /* SILC_SYMBIAN */ + return TRUE; +} + +/* Allocates a new SilcMac object of name of `name'. The `hash' may + be provided as argument. If provided it is used as the hash function + of the MAC. If it is NULL then the hash function is allocated and + the name of the hash algorithm is derived from the `name'. */ + +SilcBool silc_mac_alloc(const char *name, SilcMac *new_mac) +{ + SilcMacObject *entry = NULL; + SilcHash hash = NULL; + int i; + + SILC_LOG_DEBUG(("Allocating new MAC")); + + /* Allocate the new object */ + *new_mac = silc_calloc(1, sizeof(**new_mac)); + if (!(*new_mac)) + return FALSE; + + if (!hash) { + char *tmp = strdup(name), *hname; + + hname = tmp; + if (strchr(hname, '-')) + hname = strchr(hname, '-') + 1; + if (strchr(hname, '-')) + *strchr(hname, '-') = '\0'; + + if (!silc_hash_alloc(hname, &hash)) { + silc_free(tmp); + silc_free(*new_mac); + *new_mac = NULL; + return FALSE; + } + + (*new_mac)->allocated_hash = TRUE; + silc_free(tmp); + } + + (*new_mac)->hash = hash; + +#ifndef SILC_SYMBIAN + /* Check registered list of MACs */ + if (silc_mac_list) { + silc_dlist_start(silc_mac_list); + while ((entry = silc_dlist_get(silc_mac_list)) != SILC_LIST_END) { + if (!strcmp(entry->name, name)) { + (*new_mac)->mac = entry; + return TRUE; + } + } + } +#endif /* SILC_SYMBIAN */ + + if (!entry) { + /* Check builtin list of MACs */ + for (i = 0; silc_default_macs[i].name; i++) { + if (!strcmp(silc_default_macs[i].name, name)) { + (*new_mac)->mac = (SilcMacObject *)&(silc_default_macs[i]); + return TRUE; + } + } + } + + silc_free(*new_mac); + *new_mac = NULL; + return FALSE; +} + +/* Free's the SilcMac object. */ + +void silc_mac_free(SilcMac mac) +{ + if (mac) { + if (mac->allocated_hash) + silc_hash_free(mac->hash); + + if (mac->key) { + memset(mac->key, 0, mac->key_len); + silc_free(mac->key); + } + + silc_free(mac); + } +} + +/* Returns the length of the MAC that the MAC will produce. */ + +SilcUInt32 silc_mac_len(SilcMac mac) +{ + return mac->mac->len; +} + +/* Get hash context */ + +SilcHash silc_mac_get_hash(SilcMac mac) +{ + return mac->hash; +} + +/* Return name of mac */ + +const char *silc_mac_get_name(SilcMac mac) +{ + return mac->mac->name; +} + +/* Returns TRUE if MAC `name' is supported. */ + +SilcBool silc_mac_is_supported(const char *name) +{ + SilcMacObject *entry; + int i; + + if (!name) + return FALSE; + +#ifndef SILC_SYMBIAN + if (silc_mac_list) { + silc_dlist_start(silc_mac_list); + while ((entry = silc_dlist_get(silc_mac_list)) != SILC_LIST_END) { + if (!strcmp(entry->name, name)) + return TRUE; + } + } +#endif /* SILC_SYMBIAN */ + + for (i = 0; silc_default_macs[i].name; i++) + if (!strcmp(silc_default_macs[i].name, name)) + return TRUE; + + return FALSE; +} + +/* Returns comma separated list of supported MACs. */ + +char *silc_mac_get_supported() +{ + SilcMacObject *entry, *entry2; + char *list = NULL; + int len = 0, i; + +#ifndef SILC_SYMBIAN + if (silc_mac_list) { + silc_dlist_start(silc_mac_list); + while ((entry = silc_dlist_get(silc_mac_list)) != SILC_LIST_END) { + len += strlen(entry->name); + list = silc_realloc(list, len + 1); + + memcpy(list + (len - strlen(entry->name)), + entry->name, strlen(entry->name)); + memcpy(list + len, ",", 1); + len++; + } + } +#endif /* SILC_SYMBIAN */ + + + for (i = 0; silc_default_macs[i].name; i++) { + entry = (SilcMacObject *)&(silc_default_macs[i]); + + if (silc_mac_list) { + silc_dlist_start(silc_mac_list); + while ((entry2 = silc_dlist_get(silc_mac_list)) != SILC_LIST_END) { + if (!strcmp(entry2->name, entry->name)) + break; + } + if (entry2) + continue; + } + + len += strlen(entry->name); + list = silc_realloc(list, len + 1); + + memcpy(list + (len - strlen(entry->name)), + entry->name, strlen(entry->name)); + memcpy(list + len, ",", 1); + len++; + } + + list[len - 1] = 0; + + return list; +} + +/* Sets the MAC key used in the MAC creation */ + +void silc_mac_set_key(SilcMac mac, const unsigned char *key, + SilcUInt32 key_len) +{ + if (mac->key) { + memset(mac->key, 0, mac->key_len); + silc_free(mac->key); + } + mac->key = silc_malloc(key_len); + if (!mac->key) + return; + mac->key_len = key_len; + memcpy(mac->key, key, key_len); +} + +/* Return MAC key */ + +const unsigned char *silc_mac_get_key(SilcMac mac, SilcUInt32 *key_len) +{ + if (key_len) + *key_len = mac->key_len; + return (const unsigned char *)mac->key; +} + +/* Create the MAC. This is thee make_mac function pointer. This + uses the internal key set with silc_mac_set_key. */ + +void silc_mac_make(SilcMac mac, unsigned char *data, + SilcUInt32 data_len, unsigned char *return_hash, + SilcUInt32 *return_len) +{ + SILC_LOG_DEBUG(("Making MAC for message")); + + silc_mac_init(mac); + silc_mac_update(mac, data, data_len); + silc_mac_final(mac, return_hash, return_len); +} + +/* Creates MAC just as above except that this doesn't use the internal + key. The key is sent as argument to the function. */ + +void silc_mac_make_with_key(SilcMac mac, unsigned char *data, + SilcUInt32 data_len, + unsigned char *key, SilcUInt32 key_len, + unsigned char *return_hash, + SilcUInt32 *return_len) +{ + SILC_LOG_DEBUG(("Making MAC for message")); + + silc_mac_init_with_key(mac, key, key_len); + silc_mac_update(mac, data, data_len); + silc_mac_final(mac, return_hash, return_len); +} + +/* Creates the MAC just as above except that the hash value is truncated + to the truncated_len sent as argument. NOTE: One should not truncate to + less than half of the length of original hash value. However, this + routine allows these dangerous truncations. */ + +void silc_mac_make_truncated(SilcMac mac, unsigned char *data, + SilcUInt32 data_len, + SilcUInt32 truncated_len, + unsigned char *return_hash) +{ + unsigned char hvalue[SILC_HASH_MAXLEN]; + + SILC_LOG_DEBUG(("Making MAC for message")); + + silc_mac_init(mac); + silc_mac_update(mac, data, data_len); + silc_mac_final(mac, return_hash, NULL); + memcpy(return_hash, hvalue, truncated_len); + memset(hvalue, 0, sizeof(hvalue)); +} + +/* Init MAC for silc_mac_update and silc_mac_final. */ + +void silc_mac_init(SilcMac mac) +{ + silc_mac_init_with_key(mac, mac->key, mac->key_len); +} + +/* Same as above but with specific key */ + +void silc_mac_init_with_key(SilcMac mac, const unsigned char *key, + SilcUInt32 key_len) +{ + SilcHash hash = mac->hash; + silc_mac_init_internal(mac, (unsigned char *)key, key_len); + silc_hash_init(hash); + silc_hash_update(hash, mac->inner_pad, silc_hash_block_len(hash)); +} + +/* Add data to be used in the MAC computation. */ + +void silc_mac_update(SilcMac mac, const unsigned char *data, + SilcUInt32 data_len) +{ + SilcHash hash = mac->hash; + silc_hash_update(hash, data, data_len); +} + +/* Compute the final MAC. */ + +void silc_mac_final(SilcMac mac, unsigned char *return_hash, + SilcUInt32 *return_len) +{ + SilcHash hash = mac->hash; + unsigned char digest[SILC_HASH_MAXLEN]; + + silc_hash_final(hash, digest); + silc_hash_init(hash); + silc_hash_update(hash, mac->outer_pad, silc_hash_block_len(hash)); + silc_hash_update(hash, digest, silc_hash_len(hash)); + silc_hash_final(hash, digest); + memcpy(return_hash, digest, mac->mac->len); + memset(digest, 0, sizeof(digest)); + + if (return_len) + *return_len = mac->mac->len; +}