X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilccrypt%2Fsilchash.c;h=67c149522d4eef9606930d792f9b081234cfb47f;hp=58cecec9cb44225d0b0f2b450fe1ebb8dc6db15e;hb=e7b6c157b80152bf9fb9266e6bdd93f9fb0db776;hpb=32fd2624bef1d1e64b3250d7ff8475db043fd4a5 diff --git a/lib/silccrypt/silchash.c b/lib/silccrypt/silchash.c index 58cecec9..67c14952 100644 --- a/lib/silccrypt/silchash.c +++ b/lib/silccrypt/silchash.c @@ -2,15 +2,14 @@ silchash.c - Author: Pekka Riikonen + Author: Pekka Riikonen - Copyright (C) 1997 - 2001 Pekka Riikonen + Copyright (C) 1997 - 2007 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; either version 2 of the License, or - (at your option) any later version. - + 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 @@ -19,171 +18,244 @@ */ /* $Id$ */ -#include "silcincludes.h" +#include "silc.h" #include "md5.h" #include "sha1.h" +#include "sha256.h" +#include "sha512.h" -/* List of all hash functions in SILC. You can dynamically add new hash - functions into the list. At the initialization of SILC this list is - filled with the configured hash functions. */ -struct SilcHashListStruct { +/* The main SILC hash structure. */ +struct SilcHashStruct { SilcHashObject *hash; - struct SilcHashListStruct *next; + void *context; }; +#ifndef SILC_SYMBIAN /* List of dynamically registered hash functions. */ -struct SilcHashListStruct *silc_hash_list = NULL; +SilcDList silc_hash_list = NULL; +#endif /* SILC_SYMBIAN */ -/* Statically declared list of hash functions. */ -SilcHashObject silc_hash_builtin_list[] = +/* Default hash functions for silc_hash_register_default(). */ +const SilcHashObject silc_default_hash[] = { - { "md5", 16, 64, silc_md5_init, silc_md5_update, silc_md5_final, - silc_md5_transform, silc_md5_context_len }, - { "sha1", 20, 64, silc_sha1_init, silc_sha1_update, silc_sha1_final, + { "sha256", "2.16.840.1.101.3.4.2.1", + 32, 64, silc_sha256_init, silc_sha256_update, silc_sha256_final, + silc_sha256_transform, silc_sha256_context_len }, + { "sha512", "2.16.840.1.101.3.4.2.3", + 32, 64, silc_sha512_init, silc_sha512_update, silc_sha512_final, + silc_sha512_transform, silc_sha512_context_len }, + { "sha1", "1.3.14.3.2.26", + 20, 64, silc_sha1_init, silc_sha1_update, silc_sha1_final, silc_sha1_transform, silc_sha1_context_len }, + { "md5", "1.2.840.113549.2.5", + 16, 64, silc_md5_init, silc_md5_update, silc_md5_final, + silc_md5_transform, silc_md5_context_len }, - { NULL, 0, 0, NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL } }; -/* Registers a ned hash function into the SILC. This function is used at - the initialization of the SILC. */ +/* Registers a new hash function */ -int silc_hash_register(SilcHashObject *hash) +SilcBool silc_hash_register(const SilcHashObject *hash) { - struct SilcHashListStruct *new, *h; - - SILC_LOG_DEBUG(("Registering new hash function")); +#ifndef SILC_SYMBIAN + SilcHashObject *new; - new = silc_calloc(1, sizeof(*new)); - new->hash = silc_calloc(1, sizeof(*new->hash)); - - /* Set the pointers */ - new->hash->name = silc_calloc(1, strlen(hash->name)); - memcpy(new->hash->name, hash->name, strlen(hash->name)); - new->hash->hash_len = hash->hash_len; - new->hash->block_len = hash->block_len; - new->hash->init = hash->init; - new->hash->update = hash->update; - new->hash->final = hash->final; - new->hash->context_len = hash->context_len; - new->next = NULL; - - /* Add the new hash function to the list */ - if (!silc_hash_list) { - silc_hash_list = new; - return TRUE; - } + SILC_LOG_DEBUG(("Registering new hash function `%s'", hash->name)); - h = silc_hash_list; - while (h) { - if (!h->next) { - h->next = new; - break; + /* Check for existing */ + if (silc_hash_list) { + SilcHashObject *entry; + silc_dlist_start(silc_hash_list); + while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) { + if (!strcmp(entry->name, hash->name)) + return FALSE; } - h = h->next; } + new = silc_calloc(1, sizeof(*new)); + if (!new) + return FALSE; + new->name = strdup(hash->name); + if (!new->name) { + silc_free(new); + return FALSE; + } + new->oid = strdup(hash->oid); + if (!new->oid) { + silc_free(new); + return FALSE; + } + new->hash_len = hash->hash_len; + new->block_len = hash->block_len; + new->init = hash->init; + new->update = hash->update; + new->final = hash->final; + new->transform = hash->transform; + new->context_len = hash->context_len; + + /* Add to list */ + if (silc_hash_list == NULL) + silc_hash_list = silc_dlist_init(); + silc_dlist_add(silc_hash_list, new); + +#endif /* SILC_SYMBIAN */ return TRUE; } -/* Unregister a hash function from the SILC. */ +/* Unregister a hash function */ -int silc_hash_unregister(SilcHashObject *hash) +SilcBool silc_hash_unregister(SilcHashObject *hash) { - struct SilcHashListStruct *h, *tmp; +#ifndef SILC_SYMBIAN + SilcHashObject *entry; SILC_LOG_DEBUG(("Unregistering hash function")); - h = silc_hash_list; + if (!silc_hash_list) + return FALSE; + + silc_dlist_start(silc_hash_list); + while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) { + if (hash == SILC_ALL_HASH_FUNCTIONS || entry == hash) { + silc_dlist_del(silc_hash_list, entry); + silc_free(entry->name); + silc_free(entry->oid); + silc_free(entry); - /* Unregister all hash functions */ - if (hash == SILC_ALL_HASH_FUNCTIONS) { - /* Unregister all ciphers */ - while (h) { - tmp = h->next; - silc_free(h->hash->name); - silc_free(h); - h = tmp; + if (silc_dlist_count(silc_hash_list) == 0) { + silc_dlist_uninit(silc_hash_list); + silc_hash_list = NULL; + } + + return TRUE; } + } - return TRUE; +#endif /* SILC_SYMBIAN */ + return FALSE; +} + +/* Register default hash functions */ + +SilcBool silc_hash_register_default(void) +{ + /* We use builtin hash functions */ + return TRUE; +} + +/* Unregister all hash functions */ + +SilcBool silc_hash_unregister_all(void) +{ +#ifndef SILC_SYMBIAN + SilcHashObject *entry; + + if (!silc_hash_list) + return FALSE; + + silc_dlist_start(silc_hash_list); + while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) { + silc_hash_unregister(entry); + if (!silc_hash_list) + break; } +#endif /* SILC_SYMBIAN */ + return TRUE; +} - /* Unregister the hash function */ - if (h->hash == hash) { - tmp = h->next; - silc_free(h->hash->name); - silc_free(h); - silc_hash_list = tmp; +/* Allocates a new SilcHash object. New object is returned into new_hash + argument. */ - return TRUE; +SilcBool silc_hash_alloc(const char *name, SilcHash *new_hash) +{ + SilcHashObject *entry = NULL; + int i; + + SILC_LOG_DEBUG(("Allocating new hash %s", name)); + +#ifndef SILC_SYMBIAN + /* Check list of registered hash functions */ + if (silc_hash_list) { + silc_dlist_start(silc_hash_list); + while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) { + if (!strcmp(entry->name, name)) + break; + } } +#endif /* SILC_SYMBIAN */ - while (h) { - if (h->next->hash == hash) { - tmp = h->next->next; - silc_free(h->hash->name); - silc_free(h); - h->next = tmp; - return TRUE; + if (!entry) { + /* Check builtin hash function list */ + for (i = 0; silc_default_hash[i].name; i++) { + if (!strcmp(silc_default_hash[i].name, name)) { + entry = (SilcHashObject *)&(silc_default_hash[i]); + break; + } } + } - h = h->next; + if (entry) { + *new_hash = silc_calloc(1, sizeof(**new_hash)); + if (!(*new_hash)) + return FALSE; + (*new_hash)->hash = entry; + (*new_hash)->context = silc_calloc(1, entry->context_len()); + if (!(*new_hash)->context) { + silc_free(*new_hash); + return FALSE; + } + return TRUE; } return FALSE; } -/* Allocates a new SilcHash object. New object is returned into new_hash - argument. */ +/* Allocate hash by OID string */ -int silc_hash_alloc(const unsigned char *name, SilcHash *new_hash) +SilcBool silc_hash_alloc_by_oid(const char *oid, SilcHash *new_hash) { - struct SilcHashListStruct *h; + SilcHashObject *entry = NULL; int i; - - SILC_LOG_DEBUG(("Allocating new hash object")); - /* Allocate the new object */ - *new_hash = silc_calloc(1, sizeof(**new_hash)); + SILC_LOG_DEBUG(("Allocating new hash %s", oid)); +#ifndef SILC_SYMBIAN + /* Check list of registered hash functions */ if (silc_hash_list) { - h = silc_hash_list; - while (h) { - if (!strcmp(h->hash->name, name)) + silc_dlist_start(silc_hash_list); + while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) { + if (!strcmp(entry->oid, oid)) break; - h = h->next; } + } +#endif /* SILC_SYMBIAN */ - if (!h || !h->hash->context_len) - goto check_builtin; - - /* Set the pointers */ - (*new_hash)->hash = h->hash; - (*new_hash)->context = silc_calloc(1, h->hash->context_len()); - (*new_hash)->make_hash = silc_hash_make; + if (!entry) { + /* Check builtin hash function list */ + for (i = 0; silc_default_hash[i].oid; i++) { + if (!strcmp(silc_default_hash[i].oid, oid)) { + entry = (SilcHashObject *)&(silc_default_hash[i]); + break; + } + } + } + if (entry) { + *new_hash = silc_calloc(1, sizeof(**new_hash)); + if (!(*new_hash)) + return FALSE; + (*new_hash)->hash = entry; + (*new_hash)->context = silc_calloc(1, entry->context_len()); + if (!(*new_hash)->context) { + silc_free(*new_hash); + return FALSE; + } return TRUE; } - check_builtin: - for (i = 0; silc_hash_builtin_list[i].name; i++) - if (!strcmp(silc_hash_builtin_list[i].name, name)) - break; - - if (silc_hash_builtin_list[i].name == NULL) { - silc_free(*new_hash); - *new_hash = NULL; - return FALSE; - } - - /* Set the pointers */ - (*new_hash)->hash = &silc_hash_builtin_list[i]; - (*new_hash)->context = silc_calloc(1, (*new_hash)->hash->context_len()); - (*new_hash)->make_hash = silc_hash_make; - - return TRUE; + return FALSE; } /* Free's the SilcHash object */ @@ -196,25 +268,53 @@ void silc_hash_free(SilcHash hash) } } +/* Returns the length of the hash digest. */ + +SilcUInt32 silc_hash_len(SilcHash hash) +{ + return hash->hash->hash_len; +} + +/* Returns the block lenght of the hash. */ + +SilcUInt32 silc_hash_block_len(SilcHash hash) +{ + return hash->hash->block_len; +} + +/* Returns the name of the hash function */ + +const char *silc_hash_get_name(SilcHash hash) +{ + return hash->hash->name; +} + +/* Returns hash OID string */ + +const char *silc_hash_get_oid(SilcHash hash) +{ + return hash->hash->oid; +} + /* Returns TRUE if hash algorithm `name' is supported. */ -int silc_hash_is_supported(const unsigned char *name) +SilcBool silc_hash_is_supported(const char *name) { - struct SilcHashListStruct *h; + SilcHashObject *entry; int i; - - if (silc_hash_list) { - h = silc_hash_list; - while (h) { - if (!strcmp(h->hash->name, name)) +#ifndef SILC_SYMBIAN + if (silc_hash_list) { + silc_dlist_start(silc_hash_list); + while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) { + if (!strcmp(entry->name, name)) return TRUE; - h = h->next; } } +#endif /* SILC_SYMBIAN */ - for (i = 0; silc_hash_builtin_list[i].name; i++) - if (!strcmp(silc_hash_builtin_list[i].name, name)) + for (i = 0; silc_default_hash[i].name; i++) + if (!strcmp(silc_default_hash[i].name, name)) return TRUE; return FALSE; @@ -222,36 +322,45 @@ int silc_hash_is_supported(const unsigned char *name) /* Returns comma separated list of supported hash functions. */ -char *silc_hash_get_supported() +char *silc_hash_get_supported(void) { + SilcHashObject *entry, *entry2; char *list = NULL; - int i, len; - struct SilcHashListStruct *h; + int i, len = 0; - len = 0; +#ifndef SILC_SYMBIAN if (silc_hash_list) { - h = silc_hash_list; - - while (h) { - len += strlen(h->hash->name); + silc_dlist_start(silc_hash_list); + while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) { + len += strlen(entry->name); list = silc_realloc(list, len + 1); - - memcpy(list + (len - strlen(h->hash->name)), - h->hash->name, strlen(h->hash->name)); + + memcpy(list + (len - strlen(entry->name)), + entry->name, strlen(entry->name)); memcpy(list + len, ",", 1); len++; - - h = h->next; } } +#endif /* SILC_SYMBIAN */ + + for (i = 0; silc_default_hash[i].name; i++) { + entry = (SilcHashObject *)&(silc_default_hash[i]); + + if (silc_hash_list) { + silc_dlist_start(silc_hash_list); + while ((entry2 = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) { + if (!strcmp(entry2->name, entry->name)) + break; + } + if (entry2) + continue; + } - for (i = 0; silc_hash_builtin_list[i].name; i++) { - len += strlen(silc_hash_builtin_list[i].name); + len += strlen(entry->name); list = silc_realloc(list, len + 1); - - memcpy(list + (len - strlen(silc_hash_builtin_list[i].name)), - silc_hash_builtin_list[i].name, - strlen(silc_hash_builtin_list[i].name)); + + memcpy(list + (len - strlen(entry->name)), + entry->name, strlen(entry->name)); memcpy(list + len, ",", 1); len++; } @@ -263,42 +372,132 @@ char *silc_hash_get_supported() /* Creates the hash value and returns it to the return_hash argument. */ -void silc_hash_make(SilcHash hash, const unsigned char *data, - unsigned int len, unsigned char *return_hash) +void silc_hash_make(SilcHash hash, const unsigned char *data, + SilcUInt32 len, unsigned char *return_hash) +{ + silc_hash_init(hash); + silc_hash_update(hash, data, len); + silc_hash_final(hash, return_hash); +} + +void silc_hash_init(SilcHash hash) { hash->hash->init(hash->context); - hash->hash->update(hash->context, (unsigned char *)data, len); +} + +void silc_hash_update(SilcHash hash, const unsigned char *data, + SilcUInt32 data_len) +{ + hash->hash->update(hash->context, (unsigned char *)data, data_len); +} + +void silc_hash_final(SilcHash hash, unsigned char *return_hash) +{ hash->hash->final(hash->context, return_hash); } +void silc_hash_transform(SilcHash hash, void *state, + const unsigned char *data) +{ + hash->hash->transform(state, data); +} + /* Creates fingerprint of the data. If `hash' is NULL SHA1 is used as - default hash function. The returned fingerprint must be free's by the + default hash function. The returned fingerprint must be freed by the caller. */ char *silc_hash_fingerprint(SilcHash hash, const unsigned char *data, - unsigned int data_len) + SilcUInt32 data_len) { - char fingerprint[64], *cp; + SilcHash new_hash = NULL; unsigned char h[32]; - int i; + char *ret; - if (!hash) - silc_hash_alloc("sha1", &hash); + if (!hash) { + if (!silc_hash_alloc("sha1", &new_hash)) + return NULL; + hash = new_hash; + } silc_hash_make(hash, data, data_len, h); - - memset(fingerprint, 0, sizeof(fingerprint)); - cp = fingerprint; - for (i = 0; i < hash->hash->hash_len; i++) { - snprintf(cp, sizeof(fingerprint), "%02X", h[i]); - cp += 2; - - if ((i + 1) % 2 == 0) - snprintf(cp++, sizeof(fingerprint), " "); - - if ((i + 1) % 10 == 0) - snprintf(cp++, sizeof(fingerprint), " "); + ret = silc_fingerprint(h, hash->hash->hash_len); + + if (new_hash != NULL) + silc_hash_free(new_hash); + return ret; +} + +static const char vo[]= "aeiouy"; +static const char co[]= "bcdfghklmnprstvzx"; + +/* Creates a babbleprint (Bubble Babble Encoding, developed by Antti + Huima (draft-huima-babble-01.txt)), by first computing real fingerprint + using `hash' or if NULL, then using SHA1, and then encoding the + fingerprint to the babbleprint. */ + +char *silc_hash_babbleprint(SilcHash hash, const unsigned char *data, + SilcUInt32 data_len) +{ + SilcHash new_hash = NULL; + char *babbleprint; + unsigned char hval[32]; + unsigned int a, b, c, d, e, check; + int i, k, out_len; + + if (!hash) { + if (!silc_hash_alloc("sha1", &new_hash)) + return NULL; + hash = new_hash; } - - return strdup(fingerprint); + + /* Take fingerprint */ + silc_hash_make(hash, data, data_len, hval); + + /* Encode babbleprint */ + out_len = (((hash->hash->hash_len + 1) / 2) + 1) * 6; + babbleprint = silc_calloc(out_len, sizeof(*babbleprint)); + if (!babbleprint) { + silc_hash_free(new_hash); + return NULL; + } + babbleprint[0] = co[16]; + + check = 1; + for (i = 0, k = 1; i < hash->hash->hash_len - 1; i += 2, k += 6) { + a = (((hval[i] >> 6) & 3) + check) % 6; + b = (hval[i] >> 2) & 15; + c = ((hval[i] & 3) + (check / 6)) % 6; + d = (hval[i + 1] >> 4) & 15; + e = hval[i + 1] & 15; + + check = ((check * 5) + (hval[i] * 7) + hval[i + 1]) % 36; + + babbleprint[k + 0] = vo[a]; + babbleprint[k + 1] = co[b]; + babbleprint[k + 2] = vo[c]; + babbleprint[k + 3] = co[d]; + babbleprint[k + 4] = '-'; + babbleprint[k + 5] = co[e]; + } + + if ((hash->hash->hash_len % 2) != 0) { + a = (((hval[i] >> 6) & 3) + check) % 6; + b = (hval[i] >> 2) & 15; + c = ((hval[i] & 3) + (check / 6)) % 6; + babbleprint[k + 0] = vo[a]; + babbleprint[k + 1] = co[b]; + babbleprint[k + 2] = vo[c]; + } else { + a = check % 6; + b = 16; + c = check / 6; + babbleprint[k + 0] = vo[a]; + babbleprint[k + 1] = co[b]; + babbleprint[k + 2] = vo[c]; + } + babbleprint[k + 3] = co[16]; + + if (new_hash != NULL) + silc_hash_free(new_hash); + return babbleprint; }