5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1999 - 2001 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 {
31 unsigned char inner_pad[64];
32 unsigned char outer_pad[64];
33 bool allocated_hash; /* TRUE if the hash was allocated */
37 /* List of dynamically registered HMACs. */
38 SilcDList silc_hmac_list = NULL;
39 #endif /* SILC_EPOC */
41 /* Default hmacs for silc_hmac_register_default(). */
42 const SilcHmacObject silc_default_hmacs[] =
44 { "hmac-sha1-96", 12 },
45 { "hmac-md5-96", 12 },
52 static void silc_hmac_init_internal(SilcHmac hmac, unsigned char *key,
55 SilcHash hash = hmac->hash;
57 unsigned char hvalue[20];
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 bool 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));
105 new->name = strdup(hmac->name);
106 new->len = hmac->len;
109 if (silc_hmac_list == NULL)
110 silc_hmac_list = silc_dlist_init();
111 silc_dlist_add(silc_hmac_list, new);
113 #endif /* SILC_EPOC */
117 /* Unregister a HMAC from the SILC. */
119 bool silc_hmac_unregister(SilcHmacObject *hmac)
122 SilcHmacObject *entry;
124 SILC_LOG_DEBUG(("Unregistering HMAC"));
129 silc_dlist_start(silc_hmac_list);
130 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
131 if (hmac == SILC_ALL_HMACS || entry == hmac) {
132 silc_dlist_del(silc_hmac_list, entry);
133 silc_free(entry->name);
136 if (silc_dlist_count(silc_hmac_list) == 0) {
137 silc_dlist_uninit(silc_hmac_list);
138 silc_hmac_list = NULL;
145 #endif /* SILC_EPOC */
149 /* Function that registers all the default hmacs (all builtin ones).
150 The application may use this to register the default hmacs if
151 specific hmacs in any specific order is not wanted. */
153 bool silc_hmac_register_default(void)
158 for (i = 0; silc_default_hmacs[i].name; i++)
159 silc_hmac_register(&(silc_default_hmacs[i]));
161 #endif /* SILC_EPOC */
165 bool silc_hmac_unregister_all(void)
168 SilcHmacObject *entry;
173 silc_dlist_start(silc_hmac_list);
174 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
175 silc_hmac_unregister(entry);
179 #endif /* SILC_EPOC */
183 /* Allocates a new SilcHmac object of name of `name'. The `hash' may
184 be provided as argument. If provided it is used as the hash function
185 of the HMAC. If it is NULL then the hash function is allocated and
186 the name of the hash algorithm is derived from the `name'. */
188 bool silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
190 SILC_LOG_DEBUG(("Allocating new HMAC"));
192 /* Allocate the new object */
193 *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 if (silc_hmac_list) {
219 SilcHmacObject *entry;
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;
230 /* On EPOC which don't have globals we check our constant hash list. */
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]);
239 #endif /* SILC_EPOC */
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 bool silc_hmac_is_supported(const char *name)
289 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))
304 for (i = 0; silc_default_hmacs[i].name; i++)
305 if (!strcmp(silc_default_hmacs[i].name, name))
308 #endif /* SILC_EPOC */
312 /* Returns comma separated list of supported HMACs. */
314 char *silc_hmac_get_supported()
316 SilcHmacObject *entry;
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);
336 for (i = 0; silc_default_hmacs[i].name; i++) {
337 entry = (SilcHmacObject *)&(silc_default_hmacs[i]);
338 len += strlen(entry->name);
339 list = silc_realloc(list, len + 1);
341 memcpy(list + (len - strlen(entry->name)),
342 entry->name, strlen(entry->name));
343 memcpy(list + len, ",", 1);
347 #endif /* SILC_EPOC */
354 /* Sets the HMAC key used in the HMAC creation */
356 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
360 memset(hmac->key, 0, hmac->key_len);
361 silc_free(hmac->key);
363 hmac->key = silc_calloc(key_len, sizeof(unsigned char));
364 hmac->key_len = key_len;
365 memcpy(hmac->key, key, key_len);
368 /* Create the HMAC. This is thee make_hmac function pointer. This
369 uses the internal key set with silc_hmac_set_key. */
371 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
372 SilcUInt32 data_len, unsigned char *return_hash,
373 SilcUInt32 *return_len)
375 SILC_LOG_DEBUG(("Making HMAC for message"));
377 silc_hmac_init(hmac);
378 silc_hmac_update(hmac, data, data_len);
379 silc_hmac_final(hmac, return_hash, return_len);
382 /* Creates HMAC just as above except that this doesn't use the internal
383 key. The key is sent as argument to the function. */
385 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
387 unsigned char *key, SilcUInt32 key_len,
388 unsigned char *return_hash,
389 SilcUInt32 *return_len)
391 SILC_LOG_DEBUG(("Making HMAC for message"));
393 silc_hmac_init_with_key(hmac, key, key_len);
394 silc_hmac_update(hmac, data, data_len);
395 silc_hmac_final(hmac, return_hash, return_len);
398 /* Creates the HMAC just as above except that the hash value is truncated
399 to the truncated_len sent as argument. NOTE: One should not truncate to
400 less than half of the length of original hash value. However, this
401 routine allows these dangerous truncations. */
403 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
405 SilcUInt32 truncated_len,
406 unsigned char *return_hash)
408 unsigned char hvalue[20];
410 SILC_LOG_DEBUG(("Making HMAC for message"));
412 silc_hmac_init(hmac);
413 silc_hmac_update(hmac, data, data_len);
414 silc_hmac_final(hmac, return_hash, NULL);
415 memcpy(return_hash, hvalue, truncated_len);
416 memset(hvalue, 0, sizeof(hvalue));
419 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
421 void silc_hmac_init(SilcHmac hmac)
423 silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
426 /* Same as above but with specific key */
428 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
431 SilcHash hash = hmac->hash;
432 silc_hmac_init_internal(hmac, hmac->key, hmac->key_len);
433 silc_hash_init(hash);
434 silc_hash_update(hash, hmac->inner_pad, silc_hash_block_len(hash));
437 /* Add data to be used in the MAC computation. */
439 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
442 SilcHash hash = hmac->hash;
443 silc_hash_update(hash, data, data_len);
446 /* Compute the final MAC. */
448 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
449 SilcUInt32 *return_len)
451 SilcHash hash = hmac->hash;
452 unsigned char mac[20];
454 silc_hash_final(hash, mac);
455 silc_hash_init(hash);
456 silc_hash_update(hash, hmac->outer_pad, silc_hash_block_len(hash));
457 silc_hash_update(hash, mac, silc_hash_len(hash));
458 silc_hash_final(hash, mac);
459 memcpy(return_hash, mac, hmac->hmac->len);
460 memset(mac, 0, sizeof(mac));
463 *return_len = hmac->hmac->len;