5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 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; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
22 #include "silcincludes.h"
24 /* List of dynamically registered HMACs. */
25 SilcDList silc_hmac_list = NULL;
27 /* Registers a new HMAC into the SILC. This function is used at the
28 initialization of the SILC. */
30 int silc_hmac_register(SilcHmacObject *hmac)
34 SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
36 new = silc_calloc(1, sizeof(*new));
37 new->name = strdup(hmac->name);
41 if (silc_hmac_list == NULL)
42 silc_hmac_list = silc_dlist_init();
43 silc_dlist_add(silc_hmac_list, new);
48 /* Unregister a HMAC from the SILC. */
50 int silc_hmac_unregister(SilcHmacObject *hmac)
52 SilcHmacObject *entry;
54 SILC_LOG_DEBUG(("Unregistering HMAC"));
59 silc_dlist_start(silc_hmac_list);
60 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
62 silc_dlist_del(silc_hmac_list, entry);
64 if (silc_dlist_count(silc_hmac_list) == 0) {
65 silc_dlist_uninit(silc_hmac_list);
66 silc_hmac_list = NULL;
76 /* Allocates a new SilcHmac object of name of `name'. The `hash' may
77 be provided as argument. If provided it is used as the hash function
78 of the HMAC. If it is NULL then the hash function is allocated and
79 the name of the hash algorithm is derived from the `name'. */
81 int silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
83 SilcHmacObject *entry;
85 SILC_LOG_DEBUG(("Allocating new HMAC"));
87 /* Allocate the new object */
88 *new_hmac = silc_calloc(1, sizeof(**new_hmac));
91 char *tmp = strdup(name), *hname;
94 if (strchr(hname, '-'))
95 hname = strchr(hname, '-') + 1;
96 if (strchr(hname, '-'))
97 *strchr(hname, '-') = '\0';
99 if (!silc_hash_alloc(hname, &hash)) {
104 (*new_hmac)->allocated_hash = TRUE;
108 (*new_hmac)->hash = hash;
110 if (silc_hmac_list) {
111 silc_dlist_start(silc_hmac_list);
112 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
113 if (!strcmp(entry->name, name)) {
114 (*new_hmac)->hmac = entry;
123 /* Free's the SilcHmac object. */
125 void silc_hmac_free(SilcHmac hmac)
128 if (hmac->allocated_hash)
129 silc_hash_free(hmac->hash);
134 /* Returns the length of the MAC that the HMAC will produce. */
136 unsigned int silc_hmac_len(SilcHmac hmac)
138 return hmac->hmac->len;
141 /* Returns TRUE if HMAC `name' is supported. */
143 int silc_hmac_is_supported(const char *name)
145 SilcHmacObject *entry;
150 if (silc_hmac_list) {
151 silc_dlist_start(silc_hmac_list);
152 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
153 if (!strcmp(entry->name, name))
161 /* Returns comma separated list of supported HMACs. */
163 char *silc_hmac_get_supported()
165 SilcHmacObject *entry;
170 if (silc_hmac_list) {
171 silc_dlist_start(silc_hmac_list);
172 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
173 len += strlen(entry->name);
174 list = silc_realloc(list, len + 1);
176 memcpy(list + (len - strlen(entry->name)),
177 entry->name, strlen(entry->name));
178 memcpy(list + len, ",", 1);
188 /* Sets the HMAC key used in the HMAC creation */
190 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
191 unsigned int key_len)
193 hmac->key = silc_calloc(key_len, sizeof(unsigned char));
194 hmac->key_len = key_len;
195 memcpy(hmac->key, key, key_len);
198 /* Creates the HMAC. The created keyed hash value is returned to
199 return_hash argument. */
201 void silc_hmac_make_internal(SilcHmac hmac, unsigned char *data,
202 unsigned int data_len, unsigned char *key,
203 unsigned int key_len, unsigned char *return_hash)
205 SilcHash hash = hmac->hash;
206 unsigned char inner_pad[hash->hash->block_len + 1];
207 unsigned char outer_pad[hash->hash->block_len + 1];
208 unsigned char hvalue[hash->hash->hash_len];
209 unsigned char mac[128];
213 SILC_LOG_DEBUG(("Making HMAC for message"));
215 hash_context = silc_calloc(1, hash->hash->context_len());
217 memset(inner_pad, 0, sizeof(inner_pad));
218 memset(outer_pad, 0, sizeof(outer_pad));
220 /* If the key length is more than block size of the hash function, the
222 if (key_len > hash->hash->block_len) {
223 silc_hash_make(hash, key, key_len, hvalue);
225 key_len = hash->hash->hash_len;
228 /* Copy the key into the pads */
229 memcpy(inner_pad, key, key_len);
230 memcpy(outer_pad, key, key_len);
232 /* XOR the key with pads */
233 for (i = 0; i < hash->hash->block_len; i++) {
234 inner_pad[i] ^= 0x36;
235 outer_pad[i] ^= 0x5c;
238 /* Do the HMAC transform (too bad I can't do make_hash directly, sigh) */
239 hash->hash->init(hash_context);
240 hash->hash->update(hash_context, inner_pad, hash->hash->block_len);
241 hash->hash->update(hash_context, data, data_len);
242 hash->hash->final(hash_context, mac);
243 hash->hash->init(hash_context);
244 hash->hash->update(hash_context, outer_pad, hash->hash->block_len);
245 hash->hash->update(hash_context, mac, hash->hash->hash_len);
246 hash->hash->final(hash_context, mac);
247 memcpy(return_hash, mac, hmac->hmac->len);
248 memset(mac, 0, sizeof(mac));
251 /* Create the HMAC. This is thee make_hmac function pointer. This
252 uses the internal key set with silc_hmac_set_key. */
254 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
255 unsigned int data_len, unsigned char *return_hash,
256 unsigned int *return_len)
258 silc_hmac_make_internal(hmac, data, data_len, hmac->key,
259 hmac->key_len, return_hash);
261 *return_len = hmac->hmac->len;
264 /* Creates HMAC just as above except that this doesn't use the internal
265 key. The key is sent as argument to the function. */
267 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
268 unsigned int data_len,
269 unsigned char *key, unsigned int key_len,
270 unsigned char *return_hash,
271 unsigned int *return_len)
273 silc_hmac_make_internal(hmac, data, data_len, key, key_len, return_hash);
275 *return_len = hmac->hmac->len;
278 /* Creates the HMAC just as above except that the hash value is truncated
279 to the truncated_len sent as argument. NOTE: One should not truncate to
280 less than half of the length of original hash value. However, this
281 routine allows these dangerous truncations. */
283 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
284 unsigned int data_len,
285 unsigned int truncated_len,
286 unsigned char *return_hash)
288 unsigned char hvalue[hmac->hash->hash->hash_len];
290 silc_hmac_make_internal(hmac, data, data_len,
291 hmac->key, hmac->key_len, hvalue);
292 memcpy(return_hash, hvalue, truncated_len);
293 memset(hvalue, 0, sizeof(hvalue));