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];
37 /* List of dynamically registered HMACs. */
38 SilcDList silc_hmac_list = NULL;
40 /* Default hmacs for silc_hmac_register_default(). */
41 SilcHmacObject silc_default_hmacs[] =
43 { "hmac-sha1-96", 12 },
44 { "hmac-md5-96", 12 },
51 static void silc_hmac_init_internal(SilcHmac hmac, unsigned char *key,
54 SilcHash hash = hmac->hash;
55 unsigned char hvalue[20];
58 memset(hmac->inner_pad, 0, sizeof(hmac->inner_pad));
59 memset(hmac->outer_pad, 0, sizeof(hmac->outer_pad));
61 /* If the key length is more than block size of the hash function, the
63 if (key_len > hash->hash->block_len) {
64 silc_hash_make(hash, key, key_len, hvalue);
66 key_len = hash->hash->hash_len;
69 /* Copy the key into the pads */
70 memcpy(hmac->inner_pad, key, key_len);
71 memcpy(hmac->outer_pad, key, key_len);
73 /* XOR the key with pads */
74 for (i = 0; i < hash->hash->block_len; i++) {
75 hmac->inner_pad[i] ^= 0x36;
76 hmac->outer_pad[i] ^= 0x5c;
80 /* Registers a new HMAC into the SILC. This function is used at the
81 initialization of the SILC. */
83 bool silc_hmac_register(SilcHmacObject *hmac)
87 SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
89 /* Check for existing */
91 SilcHmacObject *entry;
92 silc_dlist_start(silc_hmac_list);
93 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
94 if (!strcmp(entry->name, hmac->name))
99 new = silc_calloc(1, sizeof(*new));
100 new->name = strdup(hmac->name);
101 new->len = hmac->len;
104 if (silc_hmac_list == NULL)
105 silc_hmac_list = silc_dlist_init();
106 silc_dlist_add(silc_hmac_list, new);
111 /* Unregister a HMAC from the SILC. */
113 bool silc_hmac_unregister(SilcHmacObject *hmac)
115 SilcHmacObject *entry;
117 SILC_LOG_DEBUG(("Unregistering HMAC"));
122 silc_dlist_start(silc_hmac_list);
123 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
124 if (hmac == SILC_ALL_HMACS || entry == hmac) {
125 silc_dlist_del(silc_hmac_list, entry);
127 if (silc_dlist_count(silc_hmac_list) == 0) {
128 silc_dlist_uninit(silc_hmac_list);
129 silc_hmac_list = NULL;
139 /* Function that registers all the default hmacs (all builtin ones).
140 The application may use this to register the default hmacs if
141 specific hmacs in any specific order is not wanted. */
143 bool silc_hmac_register_default(void)
147 for (i = 0; silc_default_hmacs[i].name; i++)
148 silc_hmac_register(&(silc_default_hmacs[i]));
153 /* Allocates a new SilcHmac object of name of `name'. The `hash' may
154 be provided as argument. If provided it is used as the hash function
155 of the HMAC. If it is NULL then the hash function is allocated and
156 the name of the hash algorithm is derived from the `name'. */
158 bool silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
160 SilcHmacObject *entry;
162 SILC_LOG_DEBUG(("Allocating new HMAC"));
164 /* Allocate the new object */
165 *new_hmac = silc_calloc(1, sizeof(**new_hmac));
168 char *tmp = strdup(name), *hname;
171 if (strchr(hname, '-'))
172 hname = strchr(hname, '-') + 1;
173 if (strchr(hname, '-'))
174 *strchr(hname, '-') = '\0';
176 if (!silc_hash_alloc(hname, &hash)) {
178 silc_free(*new_hmac);
183 (*new_hmac)->allocated_hash = TRUE;
187 (*new_hmac)->hash = hash;
189 if (silc_hmac_list) {
190 silc_dlist_start(silc_hmac_list);
191 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
192 if (!strcmp(entry->name, name)) {
193 (*new_hmac)->hmac = entry;
199 silc_free(*new_hmac);
204 /* Free's the SilcHmac object. */
206 void silc_hmac_free(SilcHmac hmac)
209 if (hmac->allocated_hash)
210 silc_hash_free(hmac->hash);
213 memset(hmac->key, 0, hmac->key_len);
214 silc_free(hmac->key);
217 silc_free(hmac->hash_context);
222 /* Returns the length of the MAC that the HMAC will produce. */
224 uint32 silc_hmac_len(SilcHmac hmac)
226 return hmac->hmac->len;
229 /* Get hash context */
231 SilcHash silc_hmac_get_hash(SilcHmac hmac)
236 /* Return name of hmac */
238 const char *silc_hmac_get_name(SilcHmac hmac)
240 return hmac->hmac->name;
243 /* Returns TRUE if HMAC `name' is supported. */
245 bool silc_hmac_is_supported(const char *name)
247 SilcHmacObject *entry;
252 if (silc_hmac_list) {
253 silc_dlist_start(silc_hmac_list);
254 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
255 if (!strcmp(entry->name, name))
263 /* Returns comma separated list of supported HMACs. */
265 char *silc_hmac_get_supported()
267 SilcHmacObject *entry;
272 if (silc_hmac_list) {
273 silc_dlist_start(silc_hmac_list);
274 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
275 len += strlen(entry->name);
276 list = silc_realloc(list, len + 1);
278 memcpy(list + (len - strlen(entry->name)),
279 entry->name, strlen(entry->name));
280 memcpy(list + len, ",", 1);
289 /* Sets the HMAC key used in the HMAC creation */
291 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
295 memset(hmac->key, 0, hmac->key_len);
296 silc_free(hmac->key);
298 hmac->key = silc_calloc(key_len, sizeof(unsigned char));
299 hmac->key_len = key_len;
300 memcpy(hmac->key, key, key_len);
303 /* Create the HMAC. This is thee make_hmac function pointer. This
304 uses the internal key set with silc_hmac_set_key. */
306 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
307 uint32 data_len, unsigned char *return_hash,
310 SILC_LOG_DEBUG(("Making HMAC for message"));
312 silc_hmac_init(hmac);
313 silc_hmac_update(hmac, data, data_len);
314 silc_hmac_final(hmac, return_hash, return_len);
317 /* Creates HMAC just as above except that this doesn't use the internal
318 key. The key is sent as argument to the function. */
320 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
322 unsigned char *key, uint32 key_len,
323 unsigned char *return_hash,
326 SILC_LOG_DEBUG(("Making HMAC for message"));
328 silc_hmac_init_with_key(hmac, key, key_len);
329 silc_hmac_update(hmac, data, data_len);
330 silc_hmac_final(hmac, return_hash, return_len);
333 /* Creates the HMAC just as above except that the hash value is truncated
334 to the truncated_len sent as argument. NOTE: One should not truncate to
335 less than half of the length of original hash value. However, this
336 routine allows these dangerous truncations. */
338 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
340 uint32 truncated_len,
341 unsigned char *return_hash)
343 unsigned char hvalue[20];
345 SILC_LOG_DEBUG(("Making HMAC for message"));
347 silc_hmac_init(hmac);
348 silc_hmac_update(hmac, data, data_len);
349 silc_hmac_final(hmac, return_hash, NULL);
350 memcpy(return_hash, hvalue, truncated_len);
351 memset(hvalue, 0, sizeof(hvalue));
354 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
356 void silc_hmac_init(SilcHmac hmac)
358 silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
361 /* Same as above but with specific key */
363 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
366 SilcHash hash = hmac->hash;
368 silc_hmac_init_internal(hmac, hmac->key, hmac->key_len);
370 if (!hmac->hash_context)
371 hmac->hash_context = silc_calloc(1, hash->hash->context_len());
373 hash->hash->init(hmac->hash_context);
374 hash->hash->update(hmac->hash_context, hmac->inner_pad,
375 hash->hash->block_len);
378 /* Add data to be used in the MAC computation. */
380 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
383 SilcHash hash = hmac->hash;
384 hash->hash->update(hmac->hash_context, (unsigned char *)data, data_len);
387 /* Compute the final MAC. */
389 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
392 SilcHash hash = hmac->hash;
393 unsigned char mac[20];
395 hash->hash->final(hmac->hash_context, mac);
396 hash->hash->init(hmac->hash_context);
397 hash->hash->update(hmac->hash_context, hmac->outer_pad,
398 hash->hash->block_len);
399 hash->hash->update(hmac->hash_context, mac, hash->hash->hash_len);
400 hash->hash->final(hmac->hash_context, mac);
401 memcpy(return_hash, mac, hmac->hmac->len);
402 memset(mac, 0, sizeof(mac));
405 *return_len = hmac->hmac->len;