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)
194 memset(hmac->key, 0, hmac->key_len);
195 silc_free(hmac->key);
197 hmac->key = silc_calloc(key_len, sizeof(unsigned char));
198 hmac->key_len = key_len;
199 memcpy(hmac->key, key, key_len);
202 /* Creates the HMAC. The created keyed hash value is returned to
203 return_hash argument. */
205 void silc_hmac_make_internal(SilcHmac hmac, unsigned char *data,
206 unsigned int data_len, unsigned char *key,
207 unsigned int key_len, unsigned char *return_hash)
209 SilcHash hash = hmac->hash;
210 unsigned char inner_pad[hash->hash->block_len + 1];
211 unsigned char outer_pad[hash->hash->block_len + 1];
212 unsigned char hvalue[hash->hash->hash_len];
213 unsigned char mac[128];
217 SILC_LOG_DEBUG(("Making HMAC for message"));
219 hash_context = silc_calloc(1, hash->hash->context_len());
221 memset(inner_pad, 0, sizeof(inner_pad));
222 memset(outer_pad, 0, sizeof(outer_pad));
224 /* If the key length is more than block size of the hash function, the
226 if (key_len > hash->hash->block_len) {
227 silc_hash_make(hash, key, key_len, hvalue);
229 key_len = hash->hash->hash_len;
232 /* Copy the key into the pads */
233 memcpy(inner_pad, key, key_len);
234 memcpy(outer_pad, key, key_len);
236 /* XOR the key with pads */
237 for (i = 0; i < hash->hash->block_len; i++) {
238 inner_pad[i] ^= 0x36;
239 outer_pad[i] ^= 0x5c;
242 /* Do the HMAC transform (too bad I can't do make_hash directly, sigh) */
243 hash->hash->init(hash_context);
244 hash->hash->update(hash_context, inner_pad, hash->hash->block_len);
245 hash->hash->update(hash_context, data, data_len);
246 hash->hash->final(hash_context, mac);
247 hash->hash->init(hash_context);
248 hash->hash->update(hash_context, outer_pad, hash->hash->block_len);
249 hash->hash->update(hash_context, mac, hash->hash->hash_len);
250 hash->hash->final(hash_context, mac);
251 memcpy(return_hash, mac, hmac->hmac->len);
252 memset(mac, 0, sizeof(mac));
253 silc_free(hash_context);
256 /* Create the HMAC. This is thee make_hmac function pointer. This
257 uses the internal key set with silc_hmac_set_key. */
259 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
260 unsigned int data_len, unsigned char *return_hash,
261 unsigned int *return_len)
263 silc_hmac_make_internal(hmac, data, data_len, hmac->key,
264 hmac->key_len, return_hash);
266 *return_len = hmac->hmac->len;
269 /* Creates HMAC just as above except that this doesn't use the internal
270 key. The key is sent as argument to the function. */
272 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
273 unsigned int data_len,
274 unsigned char *key, unsigned int key_len,
275 unsigned char *return_hash,
276 unsigned int *return_len)
278 silc_hmac_make_internal(hmac, data, data_len, key, key_len, return_hash);
280 *return_len = hmac->hmac->len;
283 /* Creates the HMAC just as above except that the hash value is truncated
284 to the truncated_len sent as argument. NOTE: One should not truncate to
285 less than half of the length of original hash value. However, this
286 routine allows these dangerous truncations. */
288 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
289 unsigned int data_len,
290 unsigned int truncated_len,
291 unsigned char *return_hash)
293 unsigned char hvalue[hmac->hash->hash->hash_len];
295 silc_hmac_make_internal(hmac, data, data_len,
296 hmac->key, hmac->key_len, hvalue);
297 memcpy(return_hash, hvalue, truncated_len);
298 memset(hvalue, 0, sizeof(hvalue));