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-sha1-96", 12 },
44 { "hmac-md5-96", 12 },
45 { "hmac-sha256", 32 },
52 static void silc_hmac_init_internal(SilcHmac hmac, unsigned char *key,
55 SilcHash hash = hmac->hash;
57 unsigned char hvalue[SILC_HASH_MAXLEN];
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 */
86 SilcBool silc_hmac_register(const SilcHmacObject *hmac)
91 SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
93 /* Check for existing */
95 SilcHmacObject *entry;
96 silc_dlist_start(silc_hmac_list);
97 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
98 if (!strcmp(entry->name, hmac->name))
103 new = silc_calloc(1, sizeof(*new));
106 new->name = strdup(hmac->name);
107 new->len = hmac->len;
110 if (silc_hmac_list == NULL)
111 silc_hmac_list = silc_dlist_init();
112 silc_dlist_add(silc_hmac_list, new);
114 #endif /* SILC_SYMBIAN */
118 /* Unregister a HMAC */
120 SilcBool silc_hmac_unregister(SilcHmacObject *hmac)
123 SilcHmacObject *entry;
125 SILC_LOG_DEBUG(("Unregistering HMAC"));
130 silc_dlist_start(silc_hmac_list);
131 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
132 if (hmac == SILC_ALL_HMACS || entry == hmac) {
133 silc_dlist_del(silc_hmac_list, entry);
134 silc_free(entry->name);
137 if (silc_dlist_count(silc_hmac_list) == 0) {
138 silc_dlist_uninit(silc_hmac_list);
139 silc_hmac_list = NULL;
146 #endif /* SILC_SYMBIAN */
150 /* Register default HMACs */
152 SilcBool silc_hmac_register_default(void)
154 /* We use builtin HMACs */
158 /* Unregister all HMACs */
160 SilcBool silc_hmac_unregister_all(void)
163 SilcHmacObject *entry;
168 silc_dlist_start(silc_hmac_list);
169 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
170 silc_hmac_unregister(entry);
174 #endif /* SILC_SYMBIAN */
178 /* Allocates a new SilcHmac object of name of `name'. The `hash' may
179 be provided as argument. If provided it is used as the hash function
180 of the HMAC. If it is NULL then the hash function is allocated and
181 the name of the hash algorithm is derived from the `name'. */
183 SilcBool silc_hmac_alloc(const char *name, SilcHash hash, SilcHmac *new_hmac)
185 SilcHmacObject *entry = NULL;
188 SILC_LOG_DEBUG(("Allocating new HMAC"));
190 /* Allocate the new object */
191 *new_hmac = silc_calloc(1, sizeof(**new_hmac));
196 char *tmp = strdup(name), *hname;
199 if (strchr(hname, '-'))
200 hname = strchr(hname, '-') + 1;
201 if (strchr(hname, '-'))
202 *strchr(hname, '-') = '\0';
204 if (!silc_hash_alloc(hname, &hash)) {
206 silc_free(*new_hmac);
211 (*new_hmac)->allocated_hash = TRUE;
215 (*new_hmac)->hash = hash;
218 /* Check registered list of HMACs */
219 if (silc_hmac_list) {
220 silc_dlist_start(silc_hmac_list);
221 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
222 if (!strcmp(entry->name, name)) {
223 (*new_hmac)->hmac = entry;
228 #endif /* SILC_SYMBIAN */
231 /* Check builtin list of HMACs */
232 for (i = 0; silc_default_hmacs[i].name; i++) {
233 if (!strcmp(silc_default_hmacs[i].name, name)) {
234 (*new_hmac)->hmac = (SilcHmacObject *)&(silc_default_hmacs[i]);
240 silc_free(*new_hmac);
245 /* Free's the SilcHmac object. */
247 void silc_hmac_free(SilcHmac hmac)
250 if (hmac->allocated_hash)
251 silc_hash_free(hmac->hash);
254 memset(hmac->key, 0, hmac->key_len);
255 silc_free(hmac->key);
262 /* Returns the length of the MAC that the HMAC will produce. */
264 SilcUInt32 silc_hmac_len(SilcHmac hmac)
266 return hmac->hmac->len;
269 /* Get hash context */
271 SilcHash silc_hmac_get_hash(SilcHmac hmac)
276 /* Return name of hmac */
278 const char *silc_hmac_get_name(SilcHmac hmac)
280 return hmac->hmac->name;
283 /* Returns TRUE if HMAC `name' is supported. */
285 SilcBool silc_hmac_is_supported(const char *name)
287 SilcHmacObject *entry;
294 if (silc_hmac_list) {
295 silc_dlist_start(silc_hmac_list);
296 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
297 if (!strcmp(entry->name, name))
301 #endif /* SILC_SYMBIAN */
303 for (i = 0; silc_default_hmacs[i].name; i++)
304 if (!strcmp(silc_default_hmacs[i].name, name))
310 /* Returns comma separated list of supported HMACs. */
312 char *silc_hmac_get_supported()
314 SilcHmacObject *entry, *entry2;
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);
331 #endif /* SILC_SYMBIAN */
334 for (i = 0; silc_default_hmacs[i].name; i++) {
335 entry = (SilcHmacObject *)&(silc_default_hmacs[i]);
337 if (silc_hmac_list) {
338 silc_dlist_start(silc_hmac_list);
339 while ((entry2 = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
340 if (!strcmp(entry2->name, entry->name))
347 len += strlen(entry->name);
348 list = silc_realloc(list, len + 1);
350 memcpy(list + (len - strlen(entry->name)),
351 entry->name, strlen(entry->name));
352 memcpy(list + len, ",", 1);
361 /* Sets the HMAC key used in the HMAC creation */
363 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
367 memset(hmac->key, 0, hmac->key_len);
368 silc_free(hmac->key);
370 hmac->key = silc_malloc(key_len);
373 hmac->key_len = key_len;
374 memcpy(hmac->key, key, key_len);
377 /* Return HMAC key */
379 const unsigned char *silc_hmac_get_key(SilcHmac hmac, SilcUInt32 *key_len)
382 *key_len = hmac->key_len;
383 return (const unsigned char *)hmac->key;
386 /* Create the HMAC. This is thee make_hmac function pointer. This
387 uses the internal key set with silc_hmac_set_key. */
389 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
390 SilcUInt32 data_len, unsigned char *return_hash,
391 SilcUInt32 *return_len)
393 SILC_LOG_DEBUG(("Making HMAC for message"));
395 silc_hmac_init(hmac);
396 silc_hmac_update(hmac, data, data_len);
397 silc_hmac_final(hmac, return_hash, return_len);
400 /* Creates HMAC just as above except that this doesn't use the internal
401 key. The key is sent as argument to the function. */
403 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
405 unsigned char *key, SilcUInt32 key_len,
406 unsigned char *return_hash,
407 SilcUInt32 *return_len)
409 SILC_LOG_DEBUG(("Making HMAC for message"));
411 silc_hmac_init_with_key(hmac, key, key_len);
412 silc_hmac_update(hmac, data, data_len);
413 silc_hmac_final(hmac, return_hash, return_len);
416 /* Creates the HMAC just as above except that the hash value is truncated
417 to the truncated_len sent as argument. NOTE: One should not truncate to
418 less than half of the length of original hash value. However, this
419 routine allows these dangerous truncations. */
421 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
423 SilcUInt32 truncated_len,
424 unsigned char *return_hash)
426 unsigned char hvalue[SILC_HASH_MAXLEN];
428 SILC_LOG_DEBUG(("Making HMAC for message"));
430 silc_hmac_init(hmac);
431 silc_hmac_update(hmac, data, data_len);
432 silc_hmac_final(hmac, return_hash, NULL);
433 memcpy(return_hash, hvalue, truncated_len);
434 memset(hvalue, 0, sizeof(hvalue));
437 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
439 void silc_hmac_init(SilcHmac hmac)
441 silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
444 /* Same as above but with specific key */
446 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
449 SilcHash hash = hmac->hash;
450 silc_hmac_init_internal(hmac, (unsigned char *)key, key_len);
451 silc_hash_init(hash);
452 silc_hash_update(hash, hmac->inner_pad, silc_hash_block_len(hash));
455 /* Add data to be used in the MAC computation. */
457 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
460 SilcHash hash = hmac->hash;
461 silc_hash_update(hash, data, data_len);
464 /* Compute the final MAC. */
466 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
467 SilcUInt32 *return_len)
469 SilcHash hash = hmac->hash;
470 unsigned char mac[SILC_HASH_MAXLEN];
472 silc_hash_final(hash, mac);
473 silc_hash_init(hash);
474 silc_hash_update(hash, hmac->outer_pad, silc_hash_block_len(hash));
475 silc_hash_update(hash, mac, silc_hash_len(hash));
476 silc_hash_final(hash, mac);
477 memcpy(return_hash, mac, hmac->hmac->len);
478 memset(mac, 0, sizeof(mac));
481 *return_len = hmac->hmac->len;