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 new = silc_calloc(1, sizeof(*new));
90 new->name = strdup(hmac->name);
94 if (silc_hmac_list == NULL)
95 silc_hmac_list = silc_dlist_init();
96 silc_dlist_add(silc_hmac_list, new);
101 /* Unregister a HMAC from the SILC. */
103 bool silc_hmac_unregister(SilcHmacObject *hmac)
105 SilcHmacObject *entry;
107 SILC_LOG_DEBUG(("Unregistering HMAC"));
112 silc_dlist_start(silc_hmac_list);
113 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
114 if (hmac == SILC_ALL_HMACS || entry == hmac) {
115 silc_dlist_del(silc_hmac_list, entry);
117 if (silc_dlist_count(silc_hmac_list) == 0) {
118 silc_dlist_uninit(silc_hmac_list);
119 silc_hmac_list = NULL;
129 /* Function that registers all the default hmacs (all builtin ones).
130 The application may use this to register the default hmacs if
131 specific hmacs in any specific order is not wanted. */
133 bool silc_hmac_register_default(void)
137 for (i = 0; silc_default_hmacs[i].name; i++)
138 silc_hmac_register(&(silc_default_hmacs[i]));
143 /* Allocates a new SilcHmac object of name of `name'. The `hash' may
144 be provided as argument. If provided it is used as the hash function
145 of the HMAC. If it is NULL then the hash function is allocated and
146 the name of the hash algorithm is derived from the `name'. */
148 bool silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
150 SilcHmacObject *entry;
152 SILC_LOG_DEBUG(("Allocating new HMAC"));
154 /* Allocate the new object */
155 *new_hmac = silc_calloc(1, sizeof(**new_hmac));
158 char *tmp = strdup(name), *hname;
161 if (strchr(hname, '-'))
162 hname = strchr(hname, '-') + 1;
163 if (strchr(hname, '-'))
164 *strchr(hname, '-') = '\0';
166 if (!silc_hash_alloc(hname, &hash)) {
168 silc_free(*new_hmac);
173 (*new_hmac)->allocated_hash = TRUE;
177 (*new_hmac)->hash = hash;
179 if (silc_hmac_list) {
180 silc_dlist_start(silc_hmac_list);
181 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
182 if (!strcmp(entry->name, name)) {
183 (*new_hmac)->hmac = entry;
189 silc_free(*new_hmac);
194 /* Free's the SilcHmac object. */
196 void silc_hmac_free(SilcHmac hmac)
199 if (hmac->allocated_hash)
200 silc_hash_free(hmac->hash);
203 memset(hmac->key, 0, hmac->key_len);
204 silc_free(hmac->key);
207 silc_free(hmac->hash_context);
212 /* Returns the length of the MAC that the HMAC will produce. */
214 uint32 silc_hmac_len(SilcHmac hmac)
216 return hmac->hmac->len;
219 /* Get hash context */
221 SilcHash silc_hmac_get_hash(SilcHmac hmac)
226 /* Return name of hmac */
228 const char *silc_hmac_get_name(SilcHmac hmac)
230 return hmac->hmac->name;
233 /* Returns TRUE if HMAC `name' is supported. */
235 bool silc_hmac_is_supported(const char *name)
237 SilcHmacObject *entry;
242 if (silc_hmac_list) {
243 silc_dlist_start(silc_hmac_list);
244 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
245 if (!strcmp(entry->name, name))
253 /* Returns comma separated list of supported HMACs. */
255 char *silc_hmac_get_supported()
257 SilcHmacObject *entry;
262 if (silc_hmac_list) {
263 silc_dlist_start(silc_hmac_list);
264 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
265 len += strlen(entry->name);
266 list = silc_realloc(list, len + 1);
268 memcpy(list + (len - strlen(entry->name)),
269 entry->name, strlen(entry->name));
270 memcpy(list + len, ",", 1);
279 /* Sets the HMAC key used in the HMAC creation */
281 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
285 memset(hmac->key, 0, hmac->key_len);
286 silc_free(hmac->key);
288 hmac->key = silc_calloc(key_len, sizeof(unsigned char));
289 hmac->key_len = key_len;
290 memcpy(hmac->key, key, key_len);
293 /* Create the HMAC. This is thee make_hmac function pointer. This
294 uses the internal key set with silc_hmac_set_key. */
296 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
297 uint32 data_len, unsigned char *return_hash,
300 SILC_LOG_DEBUG(("Making HMAC for message"));
302 silc_hmac_init(hmac);
303 silc_hmac_update(hmac, data, data_len);
304 silc_hmac_final(hmac, return_hash, return_len);
307 /* Creates HMAC just as above except that this doesn't use the internal
308 key. The key is sent as argument to the function. */
310 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
312 unsigned char *key, uint32 key_len,
313 unsigned char *return_hash,
316 SILC_LOG_DEBUG(("Making HMAC for message"));
318 silc_hmac_init_with_key(hmac, key, key_len);
319 silc_hmac_update(hmac, data, data_len);
320 silc_hmac_final(hmac, return_hash, return_len);
323 /* Creates the HMAC just as above except that the hash value is truncated
324 to the truncated_len sent as argument. NOTE: One should not truncate to
325 less than half of the length of original hash value. However, this
326 routine allows these dangerous truncations. */
328 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
330 uint32 truncated_len,
331 unsigned char *return_hash)
333 unsigned char hvalue[20];
335 SILC_LOG_DEBUG(("Making HMAC for message"));
337 silc_hmac_init(hmac);
338 silc_hmac_update(hmac, data, data_len);
339 silc_hmac_final(hmac, return_hash, NULL);
340 memcpy(return_hash, hvalue, truncated_len);
341 memset(hvalue, 0, sizeof(hvalue));
344 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
346 void silc_hmac_init(SilcHmac hmac)
348 silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
351 /* Same as above but with specific key */
353 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
356 SilcHash hash = hmac->hash;
358 silc_hmac_init_internal(hmac, hmac->key, hmac->key_len);
360 if (!hmac->hash_context)
361 hmac->hash_context = silc_calloc(1, hash->hash->context_len());
363 hash->hash->init(hmac->hash_context);
364 hash->hash->update(hmac->hash_context, hmac->inner_pad,
365 hash->hash->block_len);
368 /* Add data to be used in the MAC computation. */
370 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
373 SilcHash hash = hmac->hash;
374 hash->hash->update(hmac->hash_context, (unsigned char *)data, data_len);
377 /* Compute the final MAC. */
379 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
382 SilcHash hash = hmac->hash;
383 unsigned char mac[20];
385 hash->hash->final(hmac->hash_context, mac);
386 hash->hash->init(hmac->hash_context);
387 hash->hash->update(hmac->hash_context, hmac->outer_pad,
388 hash->hash->block_len);
389 hash->hash->update(hmac->hash_context, mac, hash->hash->hash_len);
390 hash->hash->final(hmac->hash_context, mac);
391 memcpy(return_hash, mac, hmac->hmac->len);
392 memset(mac, 0, sizeof(mac));
395 *return_len = hmac->hmac->len;