5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1999 - 2005 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 {
27 unsigned char inner_pad[64];
28 unsigned char outer_pad[64];
30 unsigned int key_len : 31;
31 unsigned int allocated_hash : 1; /* TRUE if the hash was allocated */
35 /* List of dynamically registered HMACs. */
36 SilcDList silc_hmac_list = NULL;
37 #endif /* SILC_EPOC */
39 /* Default hmacs for silc_hmac_register_default(). */
40 const SilcHmacObject silc_default_hmacs[] =
42 { "hmac-sha1-96", 12 },
43 { "hmac-md5-96", 12 },
50 static void silc_hmac_init_internal(SilcHmac hmac, unsigned char *key,
53 SilcHash hash = hmac->hash;
55 unsigned char hvalue[20];
58 memset(hmac->inner_pad, 0, sizeof(hmac->inner_pad));
59 memset(hmac->outer_pad, 0, sizeof(hmac->outer_pad));
61 block_len = silc_hash_block_len(hash);
63 /* If the key length is more than block size of the hash function, the
65 if (key_len > block_len) {
66 silc_hash_make(hash, key, key_len, hvalue);
68 key_len = silc_hash_len(hash);
71 /* Copy the key into the pads */
72 memcpy(hmac->inner_pad, key, key_len);
73 memcpy(hmac->outer_pad, key, key_len);
75 /* XOR the key with pads */
76 for (i = 0; i < block_len; i++) {
77 hmac->inner_pad[i] ^= 0x36;
78 hmac->outer_pad[i] ^= 0x5c;
82 /* Registers a new HMAC into the SILC. This function is used at the
83 initialization of the SILC. */
85 bool silc_hmac_register(const SilcHmacObject *hmac)
90 SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
92 /* Check for existing */
94 SilcHmacObject *entry;
95 silc_dlist_start(silc_hmac_list);
96 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
97 if (!strcmp(entry->name, hmac->name))
102 new = silc_calloc(1, sizeof(*new));
103 new->name = strdup(hmac->name);
104 new->len = hmac->len;
107 if (silc_hmac_list == NULL)
108 silc_hmac_list = silc_dlist_init();
109 silc_dlist_add(silc_hmac_list, new);
111 #endif /* SILC_EPOC */
115 /* Unregister a HMAC from the SILC. */
117 bool silc_hmac_unregister(SilcHmacObject *hmac)
120 SilcHmacObject *entry;
122 SILC_LOG_DEBUG(("Unregistering HMAC"));
127 silc_dlist_start(silc_hmac_list);
128 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
129 if (hmac == SILC_ALL_HMACS || entry == hmac) {
130 silc_dlist_del(silc_hmac_list, entry);
131 silc_free(entry->name);
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 bool silc_hmac_unregister_all(void)
166 SilcHmacObject *entry;
171 silc_dlist_start(silc_hmac_list);
172 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
173 silc_hmac_unregister(entry);
177 #endif /* SILC_EPOC */
181 /* Allocates a new SilcHmac object of name of `name'. The `hash' may
182 be provided as argument. If provided it is used as the hash function
183 of the HMAC. If it is NULL then the hash function is allocated and
184 the name of the hash algorithm is derived from the `name'. */
186 bool silc_hmac_alloc(const char *name, SilcHash hash, SilcHmac *new_hmac)
188 SILC_LOG_DEBUG(("Allocating new HMAC"));
190 /* Allocate the new object */
191 *new_hmac = silc_calloc(1, sizeof(**new_hmac));
194 char *tmp = strdup(name), *hname;
197 if (strchr(hname, '-'))
198 hname = strchr(hname, '-') + 1;
199 if (strchr(hname, '-'))
200 *strchr(hname, '-') = '\0';
202 if (!silc_hash_alloc(hname, &hash)) {
204 silc_free(*new_hmac);
209 (*new_hmac)->allocated_hash = TRUE;
213 (*new_hmac)->hash = hash;
216 if (silc_hmac_list) {
217 SilcHmacObject *entry;
218 silc_dlist_start(silc_hmac_list);
219 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
220 if (!strcmp(entry->name, name)) {
221 (*new_hmac)->hmac = entry;
228 /* On EPOC which don't have globals we check our constant hash list. */
230 for (i = 0; silc_default_hmacs[i].name; i++) {
231 if (!strcmp(silc_default_hmacs[i].name, name)) {
232 (*new_hmac)->hmac = (SilcHmacObject *)&(silc_default_hmacs[i]);
237 #endif /* SILC_EPOC */
239 silc_free(*new_hmac);
244 /* Free's the SilcHmac object. */
246 void silc_hmac_free(SilcHmac hmac)
249 if (hmac->allocated_hash)
250 silc_hash_free(hmac->hash);
253 memset(hmac->key, 0, hmac->key_len);
254 silc_free(hmac->key);
261 /* Returns the length of the MAC that the HMAC will produce. */
263 SilcUInt32 silc_hmac_len(SilcHmac hmac)
265 return hmac->hmac->len;
268 /* Get hash context */
270 SilcHash silc_hmac_get_hash(SilcHmac hmac)
275 /* Return name of hmac */
277 const char *silc_hmac_get_name(SilcHmac hmac)
279 return hmac->hmac->name;
282 /* Returns TRUE if HMAC `name' is supported. */
284 bool silc_hmac_is_supported(const char *name)
287 SilcHmacObject *entry;
292 if (silc_hmac_list) {
293 silc_dlist_start(silc_hmac_list);
294 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
295 if (!strcmp(entry->name, name))
302 for (i = 0; silc_default_hmacs[i].name; i++)
303 if (!strcmp(silc_default_hmacs[i].name, name))
306 #endif /* SILC_EPOC */
310 /* Returns comma separated list of supported HMACs. */
312 char *silc_hmac_get_supported()
314 SilcHmacObject *entry;
319 if (silc_hmac_list) {
320 silc_dlist_start(silc_hmac_list);
321 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
322 len += strlen(entry->name);
323 list = silc_realloc(list, len + 1);
325 memcpy(list + (len - strlen(entry->name)),
326 entry->name, strlen(entry->name));
327 memcpy(list + len, ",", 1);
334 for (i = 0; silc_default_hmacs[i].name; i++) {
335 entry = (SilcHmacObject *)&(silc_default_hmacs[i]);
336 len += strlen(entry->name);
337 list = silc_realloc(list, len + 1);
339 memcpy(list + (len - strlen(entry->name)),
340 entry->name, strlen(entry->name));
341 memcpy(list + len, ",", 1);
345 #endif /* SILC_EPOC */
352 /* Sets the HMAC key used in the HMAC creation */
354 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
358 memset(hmac->key, 0, hmac->key_len);
359 silc_free(hmac->key);
361 hmac->key = silc_calloc(key_len, sizeof(unsigned char));
362 hmac->key_len = key_len;
363 memcpy(hmac->key, key, key_len);
366 /* Create the HMAC. This is thee make_hmac function pointer. This
367 uses the internal key set with silc_hmac_set_key. */
369 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
370 SilcUInt32 data_len, unsigned char *return_hash,
371 SilcUInt32 *return_len)
373 SILC_LOG_DEBUG(("Making HMAC for message"));
375 silc_hmac_init(hmac);
376 silc_hmac_update(hmac, data, data_len);
377 silc_hmac_final(hmac, return_hash, return_len);
380 /* Creates HMAC just as above except that this doesn't use the internal
381 key. The key is sent as argument to the function. */
383 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
385 unsigned char *key, SilcUInt32 key_len,
386 unsigned char *return_hash,
387 SilcUInt32 *return_len)
389 SILC_LOG_DEBUG(("Making HMAC for message"));
391 silc_hmac_init_with_key(hmac, key, key_len);
392 silc_hmac_update(hmac, data, data_len);
393 silc_hmac_final(hmac, return_hash, return_len);
396 /* Creates the HMAC just as above except that the hash value is truncated
397 to the truncated_len sent as argument. NOTE: One should not truncate to
398 less than half of the length of original hash value. However, this
399 routine allows these dangerous truncations. */
401 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
403 SilcUInt32 truncated_len,
404 unsigned char *return_hash)
406 unsigned char hvalue[20];
408 SILC_LOG_DEBUG(("Making HMAC for message"));
410 silc_hmac_init(hmac);
411 silc_hmac_update(hmac, data, data_len);
412 silc_hmac_final(hmac, return_hash, NULL);
413 memcpy(return_hash, hvalue, truncated_len);
414 memset(hvalue, 0, sizeof(hvalue));
417 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
419 void silc_hmac_init(SilcHmac hmac)
421 silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
424 /* Same as above but with specific key */
426 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
429 SilcHash hash = hmac->hash;
430 silc_hmac_init_internal(hmac, (unsigned char *)key, key_len);
431 silc_hash_init(hash);
432 silc_hash_update(hash, hmac->inner_pad, silc_hash_block_len(hash));
435 /* Add data to be used in the MAC computation. */
437 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
440 SilcHash hash = hmac->hash;
441 silc_hash_update(hash, data, data_len);
444 /* Compute the final MAC. */
446 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
447 SilcUInt32 *return_len)
449 SilcHash hash = hmac->hash;
450 unsigned char mac[20];
452 silc_hash_final(hash, mac);
453 silc_hash_init(hash);
454 silc_hash_update(hash, hmac->outer_pad, silc_hash_block_len(hash));
455 silc_hash_update(hash, mac, silc_hash_len(hash));
456 silc_hash_final(hash, mac);
457 memcpy(return_hash, mac, hmac->hmac->len);
458 memset(mac, 0, sizeof(mac));
461 *return_len = hmac->hmac->len;