5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1999 - 2006 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 into the SILC. This function is used at the
85 initialization of the SILC. */
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 from the SILC. */
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 /* Function that registers all the default hmacs (all builtin ones).
152 The application may use this to register the default hmacs if
153 specific hmacs in any specific order is not wanted. */
155 SilcBool silc_hmac_register_default(void)
160 for (i = 0; silc_default_hmacs[i].name; i++)
161 silc_hmac_register(&(silc_default_hmacs[i]));
163 #endif /* SILC_SYMBIAN */
167 SilcBool silc_hmac_unregister_all(void)
170 SilcHmacObject *entry;
175 silc_dlist_start(silc_hmac_list);
176 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
177 silc_hmac_unregister(entry);
181 #endif /* SILC_SYMBIAN */
185 /* Allocates a new SilcHmac object of name of `name'. The `hash' may
186 be provided as argument. If provided it is used as the hash function
187 of the HMAC. If it is NULL then the hash function is allocated and
188 the name of the hash algorithm is derived from the `name'. */
190 SilcBool silc_hmac_alloc(const char *name, SilcHash hash, SilcHmac *new_hmac)
192 SILC_LOG_DEBUG(("Allocating new HMAC"));
194 /* Allocate the new object */
195 *new_hmac = silc_calloc(1, sizeof(**new_hmac));
200 char *tmp = strdup(name), *hname;
203 if (strchr(hname, '-'))
204 hname = strchr(hname, '-') + 1;
205 if (strchr(hname, '-'))
206 *strchr(hname, '-') = '\0';
208 if (!silc_hash_alloc(hname, &hash)) {
210 silc_free(*new_hmac);
215 (*new_hmac)->allocated_hash = TRUE;
219 (*new_hmac)->hash = hash;
222 if (silc_hmac_list) {
223 SilcHmacObject *entry;
224 silc_dlist_start(silc_hmac_list);
225 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
226 if (!strcmp(entry->name, name)) {
227 (*new_hmac)->hmac = entry;
234 /* On EPOC which don't have globals we check our constant hash list. */
236 for (i = 0; silc_default_hmacs[i].name; i++) {
237 if (!strcmp(silc_default_hmacs[i].name, name)) {
238 (*new_hmac)->hmac = (SilcHmacObject *)&(silc_default_hmacs[i]);
243 #endif /* SILC_SYMBIAN */
245 silc_free(*new_hmac);
250 /* Free's the SilcHmac object. */
252 void silc_hmac_free(SilcHmac hmac)
255 if (hmac->allocated_hash)
256 silc_hash_free(hmac->hash);
259 memset(hmac->key, 0, hmac->key_len);
260 silc_free(hmac->key);
267 /* Returns the length of the MAC that the HMAC will produce. */
269 SilcUInt32 silc_hmac_len(SilcHmac hmac)
271 return hmac->hmac->len;
274 /* Get hash context */
276 SilcHash silc_hmac_get_hash(SilcHmac hmac)
281 /* Return name of hmac */
283 const char *silc_hmac_get_name(SilcHmac hmac)
285 return hmac->hmac->name;
288 /* Returns TRUE if HMAC `name' is supported. */
290 SilcBool silc_hmac_is_supported(const char *name)
293 SilcHmacObject *entry;
298 if (silc_hmac_list) {
299 silc_dlist_start(silc_hmac_list);
300 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
301 if (!strcmp(entry->name, name))
308 for (i = 0; silc_default_hmacs[i].name; i++)
309 if (!strcmp(silc_default_hmacs[i].name, name))
312 #endif /* SILC_SYMBIAN */
316 /* Returns comma separated list of supported HMACs. */
318 char *silc_hmac_get_supported()
320 SilcHmacObject *entry;
325 if (silc_hmac_list) {
326 silc_dlist_start(silc_hmac_list);
327 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
328 len += strlen(entry->name);
329 list = silc_realloc(list, len + 1);
331 memcpy(list + (len - strlen(entry->name)),
332 entry->name, strlen(entry->name));
333 memcpy(list + len, ",", 1);
340 for (i = 0; silc_default_hmacs[i].name; i++) {
341 entry = (SilcHmacObject *)&(silc_default_hmacs[i]);
342 len += strlen(entry->name);
343 list = silc_realloc(list, len + 1);
345 memcpy(list + (len - strlen(entry->name)),
346 entry->name, strlen(entry->name));
347 memcpy(list + len, ",", 1);
351 #endif /* SILC_SYMBIAN */
358 /* Sets the HMAC key used in the HMAC creation */
360 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
364 memset(hmac->key, 0, hmac->key_len);
365 silc_free(hmac->key);
367 hmac->key = silc_malloc(key_len);
370 hmac->key_len = key_len;
371 memcpy(hmac->key, key, key_len);
374 /* Return HMAC key */
376 const unsigned char *silc_hmac_get_key(SilcHmac hmac, SilcUInt32 *key_len)
379 *key_len = hmac->key_len;
380 return (const unsigned char *)hmac->key;
383 /* Create the HMAC. This is thee make_hmac function pointer. This
384 uses the internal key set with silc_hmac_set_key. */
386 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
387 SilcUInt32 data_len, unsigned char *return_hash,
388 SilcUInt32 *return_len)
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, return_len);
397 /* Creates HMAC just as above except that this doesn't use the internal
398 key. The key is sent as argument to the function. */
400 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
402 unsigned char *key, SilcUInt32 key_len,
403 unsigned char *return_hash,
404 SilcUInt32 *return_len)
406 SILC_LOG_DEBUG(("Making HMAC for message"));
408 silc_hmac_init_with_key(hmac, key, key_len);
409 silc_hmac_update(hmac, data, data_len);
410 silc_hmac_final(hmac, return_hash, return_len);
413 /* Creates the HMAC just as above except that the hash value is truncated
414 to the truncated_len sent as argument. NOTE: One should not truncate to
415 less than half of the length of original hash value. However, this
416 routine allows these dangerous truncations. */
418 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
420 SilcUInt32 truncated_len,
421 unsigned char *return_hash)
423 unsigned char hvalue[SILC_HASH_MAXLEN];
425 SILC_LOG_DEBUG(("Making HMAC for message"));
427 silc_hmac_init(hmac);
428 silc_hmac_update(hmac, data, data_len);
429 silc_hmac_final(hmac, return_hash, NULL);
430 memcpy(return_hash, hvalue, truncated_len);
431 memset(hvalue, 0, sizeof(hvalue));
434 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
436 void silc_hmac_init(SilcHmac hmac)
438 silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
441 /* Same as above but with specific key */
443 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
446 SilcHash hash = hmac->hash;
447 silc_hmac_init_internal(hmac, (unsigned char *)key, key_len);
448 silc_hash_init(hash);
449 silc_hash_update(hash, hmac->inner_pad, silc_hash_block_len(hash));
452 /* Add data to be used in the MAC computation. */
454 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
457 SilcHash hash = hmac->hash;
458 silc_hash_update(hash, data, data_len);
461 /* Compute the final MAC. */
463 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
464 SilcUInt32 *return_len)
466 SilcHash hash = hmac->hash;
467 unsigned char mac[SILC_HASH_MAXLEN];
469 silc_hash_final(hash, mac);
470 silc_hash_init(hash);
471 silc_hash_update(hash, hmac->outer_pad, silc_hash_block_len(hash));
472 silc_hash_update(hash, mac, silc_hash_len(hash));
473 silc_hash_final(hash, mac);
474 memcpy(return_hash, mac, hmac->hmac->len);
475 memset(mac, 0, sizeof(mac));
478 *return_len = hmac->hmac->len;