5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1999 - 2001 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.
21 #include "silcincludes.h"
24 struct SilcHmacStruct {
31 unsigned char inner_pad[64];
32 unsigned char outer_pad[64];
33 bool allocated_hash; /* TRUE if the hash was allocated */
37 /* List of dynamically registered HMACs. */
38 SilcDList silc_hmac_list = NULL;
39 #endif /* SILC_EPOC */
41 /* Default hmacs for silc_hmac_register_default(). */
42 const SilcHmacObject silc_default_hmacs[] =
44 { "hmac-sha1-96", 12 },
45 { "hmac-md5-96", 12 },
52 static void silc_hmac_init_internal(SilcHmac hmac, unsigned char *key,
55 SilcHash hash = hmac->hash;
57 unsigned char hvalue[20];
60 memset(hmac->inner_pad, 0, sizeof(hmac->inner_pad));
61 memset(hmac->outer_pad, 0, sizeof(hmac->outer_pad));
63 block_len = silc_hash_block_len(hash);
65 /* If the key length is more than block size of the hash function, the
67 if (key_len > block_len) {
68 silc_hash_make(hash, key, key_len, hvalue);
70 key_len = silc_hash_len(hash);
73 /* Copy the key into the pads */
74 memcpy(hmac->inner_pad, key, key_len);
75 memcpy(hmac->outer_pad, key, key_len);
77 /* XOR the key with pads */
78 for (i = 0; i < block_len; i++) {
79 hmac->inner_pad[i] ^= 0x36;
80 hmac->outer_pad[i] ^= 0x5c;
84 /* Registers a new HMAC into the SILC. This function is used at the
85 initialization of the SILC. */
87 bool silc_hmac_register(const SilcHmacObject *hmac)
92 SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
94 /* Check for existing */
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))
104 new = silc_calloc(1, sizeof(*new));
105 new->name = strdup(hmac->name);
106 new->len = hmac->len;
109 if (silc_hmac_list == NULL)
110 silc_hmac_list = silc_dlist_init();
111 silc_dlist_add(silc_hmac_list, new);
113 #endif /* SILC_EPOC */
117 /* Unregister a HMAC from the SILC. */
119 bool silc_hmac_unregister(SilcHmacObject *hmac)
122 SilcHmacObject *entry;
124 SILC_LOG_DEBUG(("Unregistering HMAC"));
129 silc_dlist_start(silc_hmac_list);
130 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
131 if (hmac == SILC_ALL_HMACS || entry == hmac) {
132 silc_dlist_del(silc_hmac_list, entry);
134 if (silc_dlist_count(silc_hmac_list) == 0) {
135 silc_dlist_uninit(silc_hmac_list);
136 silc_hmac_list = NULL;
143 #endif /* SILC_EPOC */
147 /* Function that registers all the default hmacs (all builtin ones).
148 The application may use this to register the default hmacs if
149 specific hmacs in any specific order is not wanted. */
151 bool silc_hmac_register_default(void)
156 for (i = 0; silc_default_hmacs[i].name; i++)
157 silc_hmac_register(&(silc_default_hmacs[i]));
159 #endif /* SILC_EPOC */
163 /* Allocates a new SilcHmac object of name of `name'. The `hash' may
164 be provided as argument. If provided it is used as the hash function
165 of the HMAC. If it is NULL then the hash function is allocated and
166 the name of the hash algorithm is derived from the `name'. */
168 bool silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
170 SILC_LOG_DEBUG(("Allocating new HMAC"));
172 /* Allocate the new object */
173 *new_hmac = silc_calloc(1, sizeof(**new_hmac));
176 char *tmp = strdup(name), *hname;
179 if (strchr(hname, '-'))
180 hname = strchr(hname, '-') + 1;
181 if (strchr(hname, '-'))
182 *strchr(hname, '-') = '\0';
184 if (!silc_hash_alloc(hname, &hash)) {
186 silc_free(*new_hmac);
191 (*new_hmac)->allocated_hash = TRUE;
195 (*new_hmac)->hash = hash;
198 if (silc_hmac_list) {
199 SilcHmacObject *entry;
200 silc_dlist_start(silc_hmac_list);
201 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
202 if (!strcmp(entry->name, name)) {
203 (*new_hmac)->hmac = entry;
210 /* On EPOC which don't have globals we check our constant hash list. */
212 for (i = 0; silc_default_hmacs[i].name; i++) {
213 if (!strcmp(silc_default_hmacs[i].name, name)) {
214 (*new_hmac)->hmac = (SilcHmacObject *)&(silc_default_hmacs[i]);
219 #endif /* SILC_EPOC */
221 silc_free(*new_hmac);
226 /* Free's the SilcHmac object. */
228 void silc_hmac_free(SilcHmac hmac)
231 if (hmac->allocated_hash)
232 silc_hash_free(hmac->hash);
235 memset(hmac->key, 0, hmac->key_len);
236 silc_free(hmac->key);
243 /* Returns the length of the MAC that the HMAC will produce. */
245 SilcUInt32 silc_hmac_len(SilcHmac hmac)
247 return hmac->hmac->len;
250 /* Get hash context */
252 SilcHash silc_hmac_get_hash(SilcHmac hmac)
257 /* Return name of hmac */
259 const char *silc_hmac_get_name(SilcHmac hmac)
261 return hmac->hmac->name;
264 /* Returns TRUE if HMAC `name' is supported. */
266 bool silc_hmac_is_supported(const char *name)
269 SilcHmacObject *entry;
274 if (silc_hmac_list) {
275 silc_dlist_start(silc_hmac_list);
276 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
277 if (!strcmp(entry->name, name))
284 for (i = 0; silc_default_hmacs[i].name; i++)
285 if (!strcmp(silc_default_hmacs[i].name, name))
288 #endif /* SILC_EPOC */
292 /* Returns comma separated list of supported HMACs. */
294 char *silc_hmac_get_supported()
296 SilcHmacObject *entry;
301 if (silc_hmac_list) {
302 silc_dlist_start(silc_hmac_list);
303 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
304 len += strlen(entry->name);
305 list = silc_realloc(list, len + 1);
307 memcpy(list + (len - strlen(entry->name)),
308 entry->name, strlen(entry->name));
309 memcpy(list + len, ",", 1);
316 for (i = 0; silc_default_hmacs[i].name; i++) {
317 entry = (SilcHmacObject *)&(silc_default_hmacs[i]);
318 len += strlen(entry->name);
319 list = silc_realloc(list, len + 1);
321 memcpy(list + (len - strlen(entry->name)),
322 entry->name, strlen(entry->name));
323 memcpy(list + len, ",", 1);
327 #endif /* SILC_EPOC */
334 /* Sets the HMAC key used in the HMAC creation */
336 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
340 memset(hmac->key, 0, hmac->key_len);
341 silc_free(hmac->key);
343 hmac->key = silc_calloc(key_len, sizeof(unsigned char));
344 hmac->key_len = key_len;
345 memcpy(hmac->key, key, key_len);
348 /* Create the HMAC. This is thee make_hmac function pointer. This
349 uses the internal key set with silc_hmac_set_key. */
351 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
352 SilcUInt32 data_len, unsigned char *return_hash,
353 SilcUInt32 *return_len)
355 SILC_LOG_DEBUG(("Making HMAC for message"));
357 silc_hmac_init(hmac);
358 silc_hmac_update(hmac, data, data_len);
359 silc_hmac_final(hmac, return_hash, return_len);
362 /* Creates HMAC just as above except that this doesn't use the internal
363 key. The key is sent as argument to the function. */
365 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
367 unsigned char *key, SilcUInt32 key_len,
368 unsigned char *return_hash,
369 SilcUInt32 *return_len)
371 SILC_LOG_DEBUG(("Making HMAC for message"));
373 silc_hmac_init_with_key(hmac, key, key_len);
374 silc_hmac_update(hmac, data, data_len);
375 silc_hmac_final(hmac, return_hash, return_len);
378 /* Creates the HMAC just as above except that the hash value is truncated
379 to the truncated_len sent as argument. NOTE: One should not truncate to
380 less than half of the length of original hash value. However, this
381 routine allows these dangerous truncations. */
383 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
385 SilcUInt32 truncated_len,
386 unsigned char *return_hash)
388 unsigned char hvalue[20];
390 SILC_LOG_DEBUG(("Making HMAC for message"));
392 silc_hmac_init(hmac);
393 silc_hmac_update(hmac, data, data_len);
394 silc_hmac_final(hmac, return_hash, NULL);
395 memcpy(return_hash, hvalue, truncated_len);
396 memset(hvalue, 0, sizeof(hvalue));
399 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
401 void silc_hmac_init(SilcHmac hmac)
403 silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
406 /* Same as above but with specific key */
408 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
411 SilcHash hash = hmac->hash;
412 silc_hmac_init_internal(hmac, hmac->key, hmac->key_len);
413 silc_hash_init(hash);
414 silc_hash_update(hash, hmac->inner_pad, silc_hash_block_len(hash));
417 /* Add data to be used in the MAC computation. */
419 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
422 SilcHash hash = hmac->hash;
423 silc_hash_update(hash, data, data_len);
426 /* Compute the final MAC. */
428 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
429 SilcUInt32 *return_len)
431 SilcHash hash = hmac->hash;
432 unsigned char mac[20];
434 silc_hash_final(hash, mac);
435 silc_hash_init(hash);
436 silc_hash_update(hash, hmac->outer_pad, silc_hash_block_len(hash));
437 silc_hash_update(hash, mac, silc_hash_len(hash));
438 silc_hash_final(hash, mac);
439 memcpy(return_hash, mac, hmac->hmac->len);
440 memset(mac, 0, sizeof(mac));
443 *return_len = hmac->hmac->len;