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 {
27 bool allocated_hash; /* TRUE if the hash was allocated */
32 unsigned char inner_pad[64];
33 unsigned char outer_pad[64];
38 /* List of dynamically registered HMACs. */
39 SilcDList silc_hmac_list = NULL;
40 #endif /* SILC_EPOC */
42 /* Default hmacs for silc_hmac_register_default(). */
43 const SilcHmacObject silc_default_hmacs[] =
45 { "hmac-sha1-96", 12 },
46 { "hmac-md5-96", 12 },
53 static void silc_hmac_init_internal(SilcHmac hmac, unsigned char *key,
56 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 /* If the key length is more than block size of the hash function, the
65 if (key_len > hash->hash->block_len) {
66 silc_hash_make(hash, key, key_len, hvalue);
68 key_len = hash->hash->hash_len;
71 /* Copy the key into the pads */
72 memcpy(hmac->inner_pad, key, key_len);
73 memcpy(hmac->outer_pad, key, key_len);
75 /* XOR the key with pads */
76 for (i = 0; i < hash->hash->block_len; i++) {
77 hmac->inner_pad[i] ^= 0x36;
78 hmac->outer_pad[i] ^= 0x5c;
82 /* Registers a new HMAC into the SILC. This function is used at the
83 initialization of the SILC. */
85 bool silc_hmac_register(const SilcHmacObject *hmac)
90 SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
92 /* Check for existing */
94 SilcHmacObject *entry;
95 silc_dlist_start(silc_hmac_list);
96 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
97 if (!strcmp(entry->name, hmac->name))
102 new = silc_calloc(1, sizeof(*new));
103 new->name = strdup(hmac->name);
104 new->len = hmac->len;
107 if (silc_hmac_list == NULL)
108 silc_hmac_list = silc_dlist_init();
109 silc_dlist_add(silc_hmac_list, new);
111 #endif /* SILC_EPOC */
115 /* Unregister a HMAC from the SILC. */
117 bool silc_hmac_unregister(SilcHmacObject *hmac)
120 SilcHmacObject *entry;
122 SILC_LOG_DEBUG(("Unregistering HMAC"));
127 silc_dlist_start(silc_hmac_list);
128 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
129 if (hmac == SILC_ALL_HMACS || entry == hmac) {
130 silc_dlist_del(silc_hmac_list, entry);
132 if (silc_dlist_count(silc_hmac_list) == 0) {
133 silc_dlist_uninit(silc_hmac_list);
134 silc_hmac_list = NULL;
141 #endif /* SILC_EPOC */
145 /* Function that registers all the default hmacs (all builtin ones).
146 The application may use this to register the default hmacs if
147 specific hmacs in any specific order is not wanted. */
149 bool silc_hmac_register_default(void)
154 for (i = 0; silc_default_hmacs[i].name; i++)
155 silc_hmac_register(&(silc_default_hmacs[i]));
157 #endif /* SILC_EPOC */
161 /* Allocates a new SilcHmac object of name of `name'. The `hash' may
162 be provided as argument. If provided it is used as the hash function
163 of the HMAC. If it is NULL then the hash function is allocated and
164 the name of the hash algorithm is derived from the `name'. */
166 bool silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
168 SILC_LOG_DEBUG(("Allocating new HMAC"));
170 /* Allocate the new object */
171 *new_hmac = silc_calloc(1, sizeof(**new_hmac));
174 char *tmp = strdup(name), *hname;
177 if (strchr(hname, '-'))
178 hname = strchr(hname, '-') + 1;
179 if (strchr(hname, '-'))
180 *strchr(hname, '-') = '\0';
182 if (!silc_hash_alloc(hname, &hash)) {
184 silc_free(*new_hmac);
189 (*new_hmac)->allocated_hash = TRUE;
193 (*new_hmac)->hash = hash;
196 if (silc_hmac_list) {
197 SilcHmacObject *entry;
198 silc_dlist_start(silc_hmac_list);
199 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
200 if (!strcmp(entry->name, name)) {
201 (*new_hmac)->hmac = entry;
208 /* On EPOC which don't have globals we check our constant hash list. */
210 for (i = 0; silc_default_hmacs[i].name; i++) {
211 if (!strcmp(silc_default_hmacs[i].name, name)) {
212 (*new_hmac)->hmac = (SilcHmacObject *)&(silc_default_hmacs[i]);
217 #endif /* SILC_EPOC */
219 silc_free(*new_hmac);
224 /* Free's the SilcHmac object. */
226 void silc_hmac_free(SilcHmac hmac)
229 if (hmac->allocated_hash)
230 silc_hash_free(hmac->hash);
233 memset(hmac->key, 0, hmac->key_len);
234 silc_free(hmac->key);
237 silc_free(hmac->hash_context);
242 /* Returns the length of the MAC that the HMAC will produce. */
244 SilcUInt32 silc_hmac_len(SilcHmac hmac)
246 return hmac->hmac->len;
249 /* Get hash context */
251 SilcHash silc_hmac_get_hash(SilcHmac hmac)
256 /* Return name of hmac */
258 const char *silc_hmac_get_name(SilcHmac hmac)
260 return hmac->hmac->name;
263 /* Returns TRUE if HMAC `name' is supported. */
265 bool silc_hmac_is_supported(const char *name)
268 SilcHmacObject *entry;
273 if (silc_hmac_list) {
274 silc_dlist_start(silc_hmac_list);
275 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
276 if (!strcmp(entry->name, name))
283 for (i = 0; silc_default_hmacs[i].name; i++)
284 if (!strcmp(silc_default_hmacs[i].name, name))
287 #endif /* SILC_EPOC */
291 /* Returns comma separated list of supported HMACs. */
293 char *silc_hmac_get_supported()
295 SilcHmacObject *entry;
300 if (silc_hmac_list) {
301 silc_dlist_start(silc_hmac_list);
302 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
303 len += strlen(entry->name);
304 list = silc_realloc(list, len + 1);
306 memcpy(list + (len - strlen(entry->name)),
307 entry->name, strlen(entry->name));
308 memcpy(list + len, ",", 1);
315 for (i = 0; silc_default_hmacs[i].name; i++) {
316 entry = (SilcHmacObject *)&(silc_default_hmacs[i]);
317 len += strlen(entry->name);
318 list = silc_realloc(list, len + 1);
320 memcpy(list + (len - strlen(entry->name)),
321 entry->name, strlen(entry->name));
322 memcpy(list + len, ",", 1);
326 #endif /* SILC_EPOC */
333 /* Sets the HMAC key used in the HMAC creation */
335 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
339 memset(hmac->key, 0, hmac->key_len);
340 silc_free(hmac->key);
342 hmac->key = silc_calloc(key_len, sizeof(unsigned char));
343 hmac->key_len = key_len;
344 memcpy(hmac->key, key, key_len);
347 /* Create the HMAC. This is thee make_hmac function pointer. This
348 uses the internal key set with silc_hmac_set_key. */
350 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
351 SilcUInt32 data_len, unsigned char *return_hash,
352 SilcUInt32 *return_len)
354 SILC_LOG_DEBUG(("Making HMAC for message"));
356 silc_hmac_init(hmac);
357 silc_hmac_update(hmac, data, data_len);
358 silc_hmac_final(hmac, return_hash, return_len);
361 /* Creates HMAC just as above except that this doesn't use the internal
362 key. The key is sent as argument to the function. */
364 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
366 unsigned char *key, SilcUInt32 key_len,
367 unsigned char *return_hash,
368 SilcUInt32 *return_len)
370 SILC_LOG_DEBUG(("Making HMAC for message"));
372 silc_hmac_init_with_key(hmac, key, key_len);
373 silc_hmac_update(hmac, data, data_len);
374 silc_hmac_final(hmac, return_hash, return_len);
377 /* Creates the HMAC just as above except that the hash value is truncated
378 to the truncated_len sent as argument. NOTE: One should not truncate to
379 less than half of the length of original hash value. However, this
380 routine allows these dangerous truncations. */
382 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
384 SilcUInt32 truncated_len,
385 unsigned char *return_hash)
387 unsigned char hvalue[20];
389 SILC_LOG_DEBUG(("Making HMAC for message"));
391 silc_hmac_init(hmac);
392 silc_hmac_update(hmac, data, data_len);
393 silc_hmac_final(hmac, return_hash, NULL);
394 memcpy(return_hash, hvalue, truncated_len);
395 memset(hvalue, 0, sizeof(hvalue));
398 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
400 void silc_hmac_init(SilcHmac hmac)
402 silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
405 /* Same as above but with specific key */
407 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
410 SilcHash hash = hmac->hash;
412 silc_hmac_init_internal(hmac, hmac->key, hmac->key_len);
414 if (!hmac->hash_context)
415 hmac->hash_context = silc_calloc(1, hash->hash->context_len());
417 hash->hash->init(hmac->hash_context);
418 hash->hash->update(hmac->hash_context, hmac->inner_pad,
419 hash->hash->block_len);
422 /* Add data to be used in the MAC computation. */
424 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
427 SilcHash hash = hmac->hash;
428 hash->hash->update(hmac->hash_context, (unsigned char *)data, data_len);
431 /* Compute the final MAC. */
433 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
434 SilcUInt32 *return_len)
436 SilcHash hash = hmac->hash;
437 unsigned char mac[20];
439 hash->hash->final(hmac->hash_context, mac);
440 hash->hash->init(hmac->hash_context);
441 hash->hash->update(hmac->hash_context, hmac->outer_pad,
442 hash->hash->block_len);
443 hash->hash->update(hmac->hash_context, mac, hash->hash->hash_len);
444 hash->hash->final(hmac->hash_context, mac);
445 memcpy(return_hash, mac, hmac->hmac->len);
446 memset(mac, 0, sizeof(mac));
449 *return_len = hmac->hmac->len;