5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1999 - 2007 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.
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_SYMBIAN */
39 /* Default hmacs for silc_hmac_register_default(). */
40 const SilcHmacObject silc_default_hmacs[] =
42 { "hmac-sha256-96", 12 },
43 { "hmac-sha512-96", 12 },
44 { "hmac-sha1-96", 12 },
45 { "hmac-md5-96", 12 },
46 { "hmac-sha256", 32 },
47 { "hmac-sha512", 64 },
54 static void silc_hmac_init_internal(SilcHmac hmac, unsigned char *key,
57 SilcHash hash = hmac->hash;
59 unsigned char hvalue[SILC_HASH_MAXLEN];
62 memset(hmac->inner_pad, 0, sizeof(hmac->inner_pad));
63 memset(hmac->outer_pad, 0, sizeof(hmac->outer_pad));
65 block_len = silc_hash_block_len(hash);
67 /* If the key length is more than block size of the hash function, the
69 if (key_len > block_len) {
70 silc_hash_make(hash, key, key_len, hvalue);
72 key_len = silc_hash_len(hash);
75 /* Copy the key into the pads */
76 memcpy(hmac->inner_pad, key, key_len);
77 memcpy(hmac->outer_pad, key, key_len);
79 /* XOR the key with pads */
80 for (i = 0; i < block_len; i++) {
81 hmac->inner_pad[i] ^= 0x36;
82 hmac->outer_pad[i] ^= 0x5c;
86 /* Registers a new HMAC */
88 SilcBool silc_hmac_register(const SilcHmacObject *hmac)
93 SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
95 /* Check for existing */
97 SilcHmacObject *entry;
98 silc_dlist_start(silc_hmac_list);
99 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
100 if (!strcmp(entry->name, hmac->name))
105 new = silc_calloc(1, sizeof(*new));
108 new->name = strdup(hmac->name);
109 new->len = hmac->len;
112 if (silc_hmac_list == NULL)
113 silc_hmac_list = silc_dlist_init();
114 silc_dlist_add(silc_hmac_list, new);
116 #endif /* SILC_SYMBIAN */
120 /* Unregister a HMAC */
122 SilcBool silc_hmac_unregister(SilcHmacObject *hmac)
125 SilcHmacObject *entry;
127 SILC_LOG_DEBUG(("Unregistering HMAC"));
132 silc_dlist_start(silc_hmac_list);
133 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
134 if (hmac == SILC_ALL_HMACS || entry == hmac) {
135 silc_dlist_del(silc_hmac_list, entry);
136 silc_free(entry->name);
139 if (silc_dlist_count(silc_hmac_list) == 0) {
140 silc_dlist_uninit(silc_hmac_list);
141 silc_hmac_list = NULL;
148 #endif /* SILC_SYMBIAN */
152 /* Register default HMACs */
154 SilcBool silc_hmac_register_default(void)
156 /* We use builtin HMACs */
160 /* Unregister all HMACs */
162 SilcBool silc_hmac_unregister_all(void)
165 SilcHmacObject *entry;
170 silc_dlist_start(silc_hmac_list);
171 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
172 silc_hmac_unregister(entry);
176 #endif /* SILC_SYMBIAN */
180 /* Allocates a new SilcHmac object of name of `name'. The `hash' may
181 be provided as argument. If provided it is used as the hash function
182 of the HMAC. If it is NULL then the hash function is allocated and
183 the name of the hash algorithm is derived from the `name'. */
185 SilcBool silc_hmac_alloc(const char *name, SilcHash hash, SilcHmac *new_hmac)
187 SilcHmacObject *entry = NULL;
190 SILC_LOG_DEBUG(("Allocating new HMAC"));
192 /* Allocate the new object */
193 *new_hmac = silc_calloc(1, sizeof(**new_hmac));
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)) {
208 silc_free(*new_hmac);
213 (*new_hmac)->allocated_hash = TRUE;
217 (*new_hmac)->hash = hash;
220 /* Check registered list of HMACs */
221 if (silc_hmac_list) {
222 silc_dlist_start(silc_hmac_list);
223 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
224 if (!strcmp(entry->name, name)) {
225 (*new_hmac)->hmac = entry;
230 #endif /* SILC_SYMBIAN */
233 /* Check builtin list of HMACs */
234 for (i = 0; silc_default_hmacs[i].name; i++) {
235 if (!strcmp(silc_default_hmacs[i].name, name)) {
236 (*new_hmac)->hmac = (SilcHmacObject *)&(silc_default_hmacs[i]);
242 silc_free(*new_hmac);
247 /* Free's the SilcHmac object. */
249 void silc_hmac_free(SilcHmac hmac)
252 if (hmac->allocated_hash)
253 silc_hash_free(hmac->hash);
256 memset(hmac->key, 0, hmac->key_len);
257 silc_free(hmac->key);
264 /* Returns the length of the MAC that the HMAC will produce. */
266 SilcUInt32 silc_hmac_len(SilcHmac hmac)
268 return hmac->hmac->len;
271 /* Get hash context */
273 SilcHash silc_hmac_get_hash(SilcHmac hmac)
278 /* Return name of hmac */
280 const char *silc_hmac_get_name(SilcHmac hmac)
282 return hmac->hmac->name;
285 /* Returns TRUE if HMAC `name' is supported. */
287 SilcBool silc_hmac_is_supported(const char *name)
289 SilcHmacObject *entry;
296 if (silc_hmac_list) {
297 silc_dlist_start(silc_hmac_list);
298 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
299 if (!strcmp(entry->name, name))
303 #endif /* SILC_SYMBIAN */
305 for (i = 0; silc_default_hmacs[i].name; i++)
306 if (!strcmp(silc_default_hmacs[i].name, name))
312 /* Returns comma separated list of supported HMACs. */
314 char *silc_hmac_get_supported()
316 SilcHmacObject *entry, *entry2;
321 if (silc_hmac_list) {
322 silc_dlist_start(silc_hmac_list);
323 while ((entry = silc_dlist_get(silc_hmac_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_hmacs[i].name; i++) {
337 entry = (SilcHmacObject *)&(silc_default_hmacs[i]);
339 if (silc_hmac_list) {
340 silc_dlist_start(silc_hmac_list);
341 while ((entry2 = silc_dlist_get(silc_hmac_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 HMAC key used in the HMAC creation */
365 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
369 memset(hmac->key, 0, hmac->key_len);
370 silc_free(hmac->key);
372 hmac->key = silc_malloc(key_len);
375 hmac->key_len = key_len;
376 memcpy(hmac->key, key, key_len);
379 /* Return HMAC key */
381 const unsigned char *silc_hmac_get_key(SilcHmac hmac, SilcUInt32 *key_len)
384 *key_len = hmac->key_len;
385 return (const unsigned char *)hmac->key;
388 /* Create the HMAC. This is thee make_hmac function pointer. This
389 uses the internal key set with silc_hmac_set_key. */
391 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
392 SilcUInt32 data_len, unsigned char *return_hash,
393 SilcUInt32 *return_len)
395 SILC_LOG_DEBUG(("Making HMAC for message"));
397 silc_hmac_init(hmac);
398 silc_hmac_update(hmac, data, data_len);
399 silc_hmac_final(hmac, return_hash, return_len);
402 /* Creates HMAC 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_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
407 unsigned char *key, SilcUInt32 key_len,
408 unsigned char *return_hash,
409 SilcUInt32 *return_len)
411 SILC_LOG_DEBUG(("Making HMAC for message"));
413 silc_hmac_init_with_key(hmac, key, key_len);
414 silc_hmac_update(hmac, data, data_len);
415 silc_hmac_final(hmac, return_hash, return_len);
418 /* Creates the HMAC 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_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
425 SilcUInt32 truncated_len,
426 unsigned char *return_hash)
428 unsigned char hvalue[SILC_HASH_MAXLEN];
430 SILC_LOG_DEBUG(("Making HMAC for message"));
432 silc_hmac_init(hmac);
433 silc_hmac_update(hmac, data, data_len);
434 silc_hmac_final(hmac, return_hash, NULL);
435 memcpy(return_hash, hvalue, truncated_len);
436 memset(hvalue, 0, sizeof(hvalue));
439 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
441 void silc_hmac_init(SilcHmac hmac)
443 silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
446 /* Same as above but with specific key */
448 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
451 SilcHash hash = hmac->hash;
452 silc_hmac_init_internal(hmac, (unsigned char *)key, key_len);
453 silc_hash_init(hash);
454 silc_hash_update(hash, hmac->inner_pad, silc_hash_block_len(hash));
457 /* Add data to be used in the MAC computation. */
459 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
462 SilcHash hash = hmac->hash;
463 silc_hash_update(hash, data, data_len);
466 /* Compute the final MAC. */
468 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
469 SilcUInt32 *return_len)
471 SilcHash hash = hmac->hash;
472 unsigned char mac[SILC_HASH_MAXLEN];
474 silc_hash_final(hash, mac);
475 silc_hash_init(hash);
476 silc_hash_update(hash, hmac->outer_pad, silc_hash_block_len(hash));
477 silc_hash_update(hash, mac, silc_hash_len(hash));
478 silc_hash_final(hash, mac);
479 memcpy(return_hash, mac, hmac->hmac->len);
480 memset(mac, 0, sizeof(mac));
483 *return_len = hmac->hmac->len;