5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1999 - 2008 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.
20 #include "silccrypto.h"
23 struct SilcHmacStruct {
26 unsigned char inner_pad[64];
27 unsigned char outer_pad[64];
29 unsigned int key_len : 31;
30 unsigned int allocated_hash : 1; /* TRUE if the hash was allocated */
34 /* List of dynamically registered HMACs. */
35 SilcDList silc_hmac_list = NULL;
36 #endif /* SILC_SYMBIAN */
38 /* Default hmacs for silc_hmac_register_default(). */
39 const SilcHmacObject silc_default_hmacs[] =
41 { "hmac-sha256-96", 12 },
42 { "hmac-sha512-96", 12 },
43 { "hmac-sha1-96", 12 },
44 { "hmac-md5-96", 12 },
45 { "hmac-sha256", 32 },
46 { "hmac-sha512", 64 },
53 static void silc_hmac_init_internal(SilcHmac hmac, unsigned char *key,
56 SilcHash hash = hmac->hash;
58 unsigned char hvalue[SILC_HASH_MAXLEN];
61 memset(hmac->inner_pad, 0, sizeof(hmac->inner_pad));
62 memset(hmac->outer_pad, 0, sizeof(hmac->outer_pad));
64 block_len = silc_hash_block_len(hash);
66 /* If the key length is more than block size of the hash function, the
68 if (key_len > block_len) {
69 silc_hash_make(hash, key, key_len, hvalue);
71 key_len = silc_hash_len(hash);
74 /* Copy the key into the pads */
75 memcpy(hmac->inner_pad, key, key_len);
76 memcpy(hmac->outer_pad, key, key_len);
78 /* XOR the key with pads */
79 for (i = 0; i < block_len; i++) {
80 hmac->inner_pad[i] ^= 0x36;
81 hmac->outer_pad[i] ^= 0x5c;
85 /* Registers a new HMAC */
87 SilcBool 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));
107 new->name = strdup(hmac->name);
108 new->len = hmac->len;
111 if (silc_hmac_list == NULL)
112 silc_hmac_list = silc_dlist_init();
113 silc_dlist_add(silc_hmac_list, new);
115 #endif /* SILC_SYMBIAN */
119 /* Unregister a HMAC */
121 SilcBool silc_hmac_unregister(SilcHmacObject *hmac)
124 SilcHmacObject *entry;
126 SILC_LOG_DEBUG(("Unregistering HMAC"));
131 silc_dlist_start(silc_hmac_list);
132 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
133 if (hmac == SILC_ALL_HMACS || entry == hmac) {
134 silc_dlist_del(silc_hmac_list, entry);
135 silc_free(entry->name);
138 if (silc_dlist_count(silc_hmac_list) == 0) {
139 silc_dlist_uninit(silc_hmac_list);
140 silc_hmac_list = NULL;
147 #endif /* SILC_SYMBIAN */
151 /* Register default HMACs */
153 SilcBool silc_hmac_register_default(void)
155 /* We use builtin HMACs */
159 /* Unregister all HMACs */
161 SilcBool silc_hmac_unregister_all(void)
164 SilcHmacObject *entry;
169 silc_dlist_start(silc_hmac_list);
170 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
171 silc_hmac_unregister(entry);
175 #endif /* SILC_SYMBIAN */
179 /* Allocates a new SilcHmac object of name of `name'. The `hash' may
180 be provided as argument. If provided it is used as the hash function
181 of the HMAC. If it is NULL then the hash function is allocated and
182 the name of the hash algorithm is derived from the `name'. */
184 SilcBool silc_hmac_alloc(const char *name, SilcHash hash, SilcHmac *new_hmac)
186 SilcHmacObject *entry = NULL;
189 SILC_LOG_DEBUG(("Allocating new HMAC"));
191 /* Allocate the new object */
192 *new_hmac = silc_calloc(1, sizeof(**new_hmac));
197 char *tmp = strdup(name), *hname;
200 if (strchr(hname, '-'))
201 hname = strchr(hname, '-') + 1;
202 if (strchr(hname, '-'))
203 *strchr(hname, '-') = '\0';
205 if (!silc_hash_alloc(hname, &hash)) {
207 silc_free(*new_hmac);
212 (*new_hmac)->allocated_hash = TRUE;
216 (*new_hmac)->hash = hash;
219 /* Check registered list of HMACs */
220 if (silc_hmac_list) {
221 silc_dlist_start(silc_hmac_list);
222 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
223 if (!strcmp(entry->name, name)) {
224 (*new_hmac)->hmac = entry;
229 #endif /* SILC_SYMBIAN */
232 /* Check builtin list of HMACs */
233 for (i = 0; silc_default_hmacs[i].name; i++) {
234 if (!strcmp(silc_default_hmacs[i].name, name)) {
235 (*new_hmac)->hmac = (SilcHmacObject *)&(silc_default_hmacs[i]);
241 silc_free(*new_hmac);
246 /* Free's the SilcHmac object. */
248 void silc_hmac_free(SilcHmac hmac)
251 if (hmac->allocated_hash)
252 silc_hash_free(hmac->hash);
255 memset(hmac->key, 0, hmac->key_len);
256 silc_free(hmac->key);
263 /* Returns the length of the MAC that the HMAC will produce. */
265 SilcUInt32 silc_hmac_len(SilcHmac hmac)
267 return hmac->hmac->len;
270 /* Get hash context */
272 SilcHash silc_hmac_get_hash(SilcHmac hmac)
277 /* Return name of hmac */
279 const char *silc_hmac_get_name(SilcHmac hmac)
281 return hmac->hmac->name;
284 /* Returns TRUE if HMAC `name' is supported. */
286 SilcBool silc_hmac_is_supported(const char *name)
288 SilcHmacObject *entry;
295 if (silc_hmac_list) {
296 silc_dlist_start(silc_hmac_list);
297 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
298 if (!strcmp(entry->name, name))
302 #endif /* SILC_SYMBIAN */
304 for (i = 0; silc_default_hmacs[i].name; i++)
305 if (!strcmp(silc_default_hmacs[i].name, name))
311 /* Returns comma separated list of supported HMACs. */
313 char *silc_hmac_get_supported()
315 SilcHmacObject *entry, *entry2;
320 if (silc_hmac_list) {
321 silc_dlist_start(silc_hmac_list);
322 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
323 len += strlen(entry->name);
324 list = silc_realloc(list, len + 1);
326 memcpy(list + (len - strlen(entry->name)),
327 entry->name, strlen(entry->name));
328 memcpy(list + len, ",", 1);
332 #endif /* SILC_SYMBIAN */
335 for (i = 0; silc_default_hmacs[i].name; i++) {
336 entry = (SilcHmacObject *)&(silc_default_hmacs[i]);
338 if (silc_hmac_list) {
339 silc_dlist_start(silc_hmac_list);
340 while ((entry2 = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
341 if (!strcmp(entry2->name, entry->name))
348 len += strlen(entry->name);
349 list = silc_realloc(list, len + 1);
351 memcpy(list + (len - strlen(entry->name)),
352 entry->name, strlen(entry->name));
353 memcpy(list + len, ",", 1);
362 /* Sets the HMAC key used in the HMAC creation */
364 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
368 memset(hmac->key, 0, hmac->key_len);
369 silc_free(hmac->key);
371 hmac->key = silc_malloc(key_len);
374 hmac->key_len = key_len;
375 memcpy(hmac->key, key, key_len);
378 /* Return HMAC key */
380 const unsigned char *silc_hmac_get_key(SilcHmac hmac, SilcUInt32 *key_len)
383 *key_len = hmac->key_len;
384 return (const unsigned char *)hmac->key;
387 /* Create the HMAC. This is thee make_hmac function pointer. This
388 uses the internal key set with silc_hmac_set_key. */
390 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
391 SilcUInt32 data_len, unsigned char *return_hash,
392 SilcUInt32 *return_len)
394 SILC_LOG_DEBUG(("Making HMAC for message"));
396 silc_hmac_init(hmac);
397 silc_hmac_update(hmac, data, data_len);
398 silc_hmac_final(hmac, return_hash, return_len);
401 /* Creates HMAC just as above except that this doesn't use the internal
402 key. The key is sent as argument to the function. */
404 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
406 unsigned char *key, SilcUInt32 key_len,
407 unsigned char *return_hash,
408 SilcUInt32 *return_len)
410 SILC_LOG_DEBUG(("Making HMAC for message"));
412 silc_hmac_init_with_key(hmac, key, key_len);
413 silc_hmac_update(hmac, data, data_len);
414 silc_hmac_final(hmac, return_hash, return_len);
417 /* Creates the HMAC just as above except that the hash value is truncated
418 to the truncated_len sent as argument. NOTE: One should not truncate to
419 less than half of the length of original hash value. However, this
420 routine allows these dangerous truncations. */
422 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
424 SilcUInt32 truncated_len,
425 unsigned char *return_hash)
427 unsigned char hvalue[SILC_HASH_MAXLEN];
429 SILC_LOG_DEBUG(("Making HMAC for message"));
431 silc_hmac_init(hmac);
432 silc_hmac_update(hmac, data, data_len);
433 silc_hmac_final(hmac, return_hash, NULL);
434 memcpy(return_hash, hvalue, truncated_len);
435 memset(hvalue, 0, sizeof(hvalue));
438 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
440 void silc_hmac_init(SilcHmac hmac)
442 silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
445 /* Same as above but with specific key */
447 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
450 SilcHash hash = hmac->hash;
451 silc_hmac_init_internal(hmac, (unsigned char *)key, key_len);
452 silc_hash_init(hash);
453 silc_hash_update(hash, hmac->inner_pad, silc_hash_block_len(hash));
456 /* Add data to be used in the MAC computation. */
458 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
461 SilcHash hash = hmac->hash;
462 silc_hash_update(hash, data, data_len);
465 /* Compute the final MAC. */
467 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
468 SilcUInt32 *return_len)
470 SilcHash hash = hmac->hash;
471 unsigned char mac[SILC_HASH_MAXLEN];
473 silc_hash_final(hash, mac);
474 silc_hash_init(hash);
475 silc_hash_update(hash, hmac->outer_pad, silc_hash_block_len(hash));
476 silc_hash_update(hash, mac, silc_hash_len(hash));
477 silc_hash_final(hash, mac);
478 memcpy(return_hash, mac, hmac->hmac->len);
479 memset(mac, 0, sizeof(mac));
482 *return_len = hmac->hmac->len;