1 /*
2
3 silchmac.c
4
5 Author: Pekka Riikonen <priikone@silcnet.org>
6
7 Copyright (C) 1999 - 2006 Pekka Riikonen
8
9 The contents of this file are subject to one of the Licenses specified
10 in the COPYING file; You may not use this file except in compliance
11 with the License.
12
13 The software distributed under the License is distributed on an "AS IS"
14 basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15 KIND, either expressed or implied. See the COPYING file for more
16 information.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22
23 /* HMAC context */
24 struct SilcHmacStruct {
25 SilcHmacObject *hmac;
26 SilcHash hash;
27 unsigned char inner_pad[64];
28 unsigned char outer_pad[64];
29 unsigned char *key;
30 unsigned int key_len : 31;
31 unsigned int allocated_hash : 1; /* TRUE if the hash was allocated */
32 };
33
34 #ifndef SILC_SYMBIAN
35 /* List of dynamically registered HMACs. */
36 SilcDList silc_hmac_list = NULL;
37 #endif /* SILC_SYMBIAN */
38
39 /* Default hmacs for silc_hmac_register_default(). */
40 const SilcHmacObject silc_default_hmacs[] =
41 {
42 { "hmac-sha256-96", 12 },
43 { "hmac-sha1-96", 12 },
44 { "hmac-md5-96", 12 },
45 { "hmac-sha256", 32 },
46 { "hmac-sha1", 20 },
47 { "hmac-md5", 16 },
48
49 { NULL, 0 }
50 };
51
silc_hmac_init_internal(SilcHmac hmac,unsigned char * key,SilcUInt32 key_len)52 static void silc_hmac_init_internal(SilcHmac hmac, unsigned char *key,
53 SilcUInt32 key_len)
54 {
55 SilcHash hash = hmac->hash;
56 SilcUInt32 block_len;
57 unsigned char hvalue[SILC_HASH_MAXLEN];
58 int i;
59
60 memset(hmac->inner_pad, 0, sizeof(hmac->inner_pad));
61 memset(hmac->outer_pad, 0, sizeof(hmac->outer_pad));
62
63 block_len = silc_hash_block_len(hash);
64
65 /* If the key length is more than block size of the hash function, the
66 key is hashed. */
67 if (key_len > block_len) {
68 silc_hash_make(hash, key, key_len, hvalue);
69 key = hvalue;
70 key_len = silc_hash_len(hash);
71 }
72
73 /* Copy the key into the pads */
74 memcpy(hmac->inner_pad, key, key_len);
75 memcpy(hmac->outer_pad, key, key_len);
76
77 /* XOR the key with pads */
78 for (i = 0; i < block_len; i++) {
79 hmac->inner_pad[i] ^= 0x36;
80 hmac->outer_pad[i] ^= 0x5c;
81 }
82 }
83
84 /* Registers a new HMAC into the SILC. This function is used at the
85 initialization of the SILC. */
86
silc_hmac_register(const SilcHmacObject * hmac)87 SilcBool silc_hmac_register(const SilcHmacObject *hmac)
88 {
89 #ifndef SILC_SYMBIAN
90 SilcHmacObject *new;
91
92 SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
93
94 /* Check for existing */
95 if (silc_hmac_list) {
96 SilcHmacObject *entry;
97 silc_dlist_start(silc_hmac_list);
98 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
99 if (!strcmp(entry->name, hmac->name))
100 return FALSE;
101 }
102 }
103
104 new = silc_calloc(1, sizeof(*new));
105 if (!new)
106 return FALSE;
107 new->name = strdup(hmac->name);
108 new->len = hmac->len;
109
110 /* Add to list */
111 if (silc_hmac_list == NULL)
112 silc_hmac_list = silc_dlist_init();
113 silc_dlist_add(silc_hmac_list, new);
114
115 #endif /* SILC_SYMBIAN */
116 return TRUE;
117 }
118
119 /* Unregister a HMAC from the SILC. */
120
silc_hmac_unregister(SilcHmacObject * hmac)121 SilcBool silc_hmac_unregister(SilcHmacObject *hmac)
122 {
123 #ifndef SILC_SYMBIAN
124 SilcHmacObject *entry;
125
126 SILC_LOG_DEBUG(("Unregistering HMAC"));
127
128 if (!silc_hmac_list)
129 return FALSE;
130
131 silc_dlist_start(silc_hmac_list);
132 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
133 if (hmac == SILC_ALL_HMACS || entry == hmac) {
134 silc_dlist_del(silc_hmac_list, entry);
135 silc_free(entry->name);
136 silc_free(entry);
137
138 if (silc_dlist_count(silc_hmac_list) == 0) {
139 silc_dlist_uninit(silc_hmac_list);
140 silc_hmac_list = NULL;
141 }
142
143 return TRUE;
144 }
145 }
146
147 #endif /* SILC_SYMBIAN */
148 return FALSE;
149 }
150
151 /* Function that registers all the default hmacs (all builtin ones).
152 The application may use this to register the default hmacs if
153 specific hmacs in any specific order is not wanted. */
154
silc_hmac_register_default(void)155 SilcBool silc_hmac_register_default(void)
156 {
157 #ifndef SILC_SYMBIAN
158 int i;
159
160 for (i = 0; silc_default_hmacs[i].name; i++)
161 silc_hmac_register(&(silc_default_hmacs[i]));
162
163 #endif /* SILC_SYMBIAN */
164 return TRUE;
165 }
166
silc_hmac_unregister_all(void)167 SilcBool silc_hmac_unregister_all(void)
168 {
169 #ifndef SILC_SYMBIAN
170 SilcHmacObject *entry;
171
172 if (!silc_hmac_list)
173 return FALSE;
174
175 silc_dlist_start(silc_hmac_list);
176 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
177 silc_hmac_unregister(entry);
178 if (!silc_hmac_list)
179 break;
180 }
181 #endif /* SILC_SYMBIAN */
182 return TRUE;
183 }
184
185 /* Allocates a new SilcHmac object of name of `name'. The `hash' may
186 be provided as argument. If provided it is used as the hash function
187 of the HMAC. If it is NULL then the hash function is allocated and
188 the name of the hash algorithm is derived from the `name'. */
189
silc_hmac_alloc(const char * name,SilcHash hash,SilcHmac * new_hmac)190 SilcBool silc_hmac_alloc(const char *name, SilcHash hash, SilcHmac *new_hmac)
191 {
192 SILC_LOG_DEBUG(("Allocating new HMAC"));
193
194 /* Allocate the new object */
195 *new_hmac = silc_calloc(1, sizeof(**new_hmac));
196 if (!(*new_hmac))
197 return FALSE;
198
199 if (!hash) {
200 char *tmp = strdup(name), *hname;
201
202 hname = tmp;
203 if (strchr(hname, '-'))
204 hname = strchr(hname, '-') + 1;
205 if (strchr(hname, '-'))
206 *strchr(hname, '-') = '\0';
207
208 if (!silc_hash_alloc(hname, &hash)) {
209 silc_free(tmp);
210 silc_free(*new_hmac);
211 *new_hmac = NULL;
212 return FALSE;
213 }
214
215 (*new_hmac)->allocated_hash = TRUE;
216 silc_free(tmp);
217 }
218
219 (*new_hmac)->hash = hash;
220
221 #ifndef SILC_SYMBIAN
222 if (silc_hmac_list) {
223 SilcHmacObject *entry;
224 silc_dlist_start(silc_hmac_list);
225 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
226 if (!strcmp(entry->name, name)) {
227 (*new_hmac)->hmac = entry;
228 return TRUE;
229 }
230 }
231 }
232 #else
233 {
234 /* On EPOC which don't have globals we check our constant hash list. */
235 int i;
236 for (i = 0; silc_default_hmacs[i].name; i++) {
237 if (!strcmp(silc_default_hmacs[i].name, name)) {
238 (*new_hmac)->hmac = (SilcHmacObject *)&(silc_default_hmacs[i]);
239 return TRUE;
240 }
241 }
242 }
243 #endif /* SILC_SYMBIAN */
244
245 silc_free(*new_hmac);
246 *new_hmac = NULL;
247 return FALSE;
248 }
249
250 /* Free's the SilcHmac object. */
251
silc_hmac_free(SilcHmac hmac)252 void silc_hmac_free(SilcHmac hmac)
253 {
254 if (hmac) {
255 if (hmac->allocated_hash)
256 silc_hash_free(hmac->hash);
257
258 if (hmac->key) {
259 memset(hmac->key, 0, hmac->key_len);
260 silc_free(hmac->key);
261 }
262
263 silc_free(hmac);
264 }
265 }
266
267 /* Returns the length of the MAC that the HMAC will produce. */
268
silc_hmac_len(SilcHmac hmac)269 SilcUInt32 silc_hmac_len(SilcHmac hmac)
270 {
271 return hmac->hmac->len;
272 }
273
274 /* Get hash context */
275
silc_hmac_get_hash(SilcHmac hmac)276 SilcHash silc_hmac_get_hash(SilcHmac hmac)
277 {
278 return hmac->hash;
279 }
280
281 /* Return name of hmac */
282
silc_hmac_get_name(SilcHmac hmac)283 const char *silc_hmac_get_name(SilcHmac hmac)
284 {
285 return hmac->hmac->name;
286 }
287
288 /* Returns TRUE if HMAC `name' is supported. */
289
silc_hmac_is_supported(const char * name)290 SilcBool silc_hmac_is_supported(const char *name)
291 {
292 #ifndef SILC_SYMBIAN
293 SilcHmacObject *entry;
294
295 if (!name)
296 return FALSE;
297
298 if (silc_hmac_list) {
299 silc_dlist_start(silc_hmac_list);
300 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
301 if (!strcmp(entry->name, name))
302 return TRUE;
303 }
304 }
305 #else
306 {
307 int i;
308 for (i = 0; silc_default_hmacs[i].name; i++)
309 if (!strcmp(silc_default_hmacs[i].name, name))
310 return TRUE;
311 }
312 #endif /* SILC_SYMBIAN */
313 return FALSE;
314 }
315
316 /* Returns comma separated list of supported HMACs. */
317
silc_hmac_get_supported()318 char *silc_hmac_get_supported()
319 {
320 SilcHmacObject *entry;
321 char *list = NULL;
322 int len = 0;
323
324 #ifndef SILC_SYMBIAN
325 if (silc_hmac_list) {
326 silc_dlist_start(silc_hmac_list);
327 while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
328 len += strlen(entry->name);
329 list = silc_realloc(list, len + 1);
330
331 memcpy(list + (len - strlen(entry->name)),
332 entry->name, strlen(entry->name));
333 memcpy(list + len, ",", 1);
334 len++;
335 }
336 }
337 #else
338 {
339 int i;
340 for (i = 0; silc_default_hmacs[i].name; i++) {
341 entry = (SilcHmacObject *)&(silc_default_hmacs[i]);
342 len += strlen(entry->name);
343 list = silc_realloc(list, len + 1);
344
345 memcpy(list + (len - strlen(entry->name)),
346 entry->name, strlen(entry->name));
347 memcpy(list + len, ",", 1);
348 len++;
349 }
350 }
351 #endif /* SILC_SYMBIAN */
352
353 if (list)
354 list[len - 1] = 0;
355
356 return list;
357 }
358
359 /* Sets the HMAC key used in the HMAC creation */
360
silc_hmac_set_key(SilcHmac hmac,const unsigned char * key,SilcUInt32 key_len)361 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
362 SilcUInt32 key_len)
363 {
364 if (hmac->key) {
365 memset(hmac->key, 0, hmac->key_len);
366 silc_free(hmac->key);
367 }
368 hmac->key = silc_malloc(key_len);
369 if (!hmac->key)
370 return;
371 hmac->key_len = key_len;
372 memcpy(hmac->key, key, key_len);
373 }
374
375 /* Return HMAC key */
376
silc_hmac_get_key(SilcHmac hmac,SilcUInt32 * key_len)377 const unsigned char *silc_hmac_get_key(SilcHmac hmac, SilcUInt32 *key_len)
378 {
379 if (key_len)
380 *key_len = hmac->key_len;
381 return (const unsigned char *)hmac->key;
382 }
383
384 /* Create the HMAC. This is thee make_hmac function pointer. This
385 uses the internal key set with silc_hmac_set_key. */
386
silc_hmac_make(SilcHmac hmac,unsigned char * data,SilcUInt32 data_len,unsigned char * return_hash,SilcUInt32 * return_len)387 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
388 SilcUInt32 data_len, unsigned char *return_hash,
389 SilcUInt32 *return_len)
390 {
391 SILC_LOG_DEBUG(("Making HMAC for message"));
392
393 silc_hmac_init(hmac);
394 silc_hmac_update(hmac, data, data_len);
395 silc_hmac_final(hmac, return_hash, return_len);
396 }
397
398 /* Creates HMAC just as above except that this doesn't use the internal
399 key. The key is sent as argument to the function. */
400
silc_hmac_make_with_key(SilcHmac hmac,unsigned char * data,SilcUInt32 data_len,unsigned char * key,SilcUInt32 key_len,unsigned char * return_hash,SilcUInt32 * return_len)401 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
402 SilcUInt32 data_len,
403 unsigned char *key, SilcUInt32 key_len,
404 unsigned char *return_hash,
405 SilcUInt32 *return_len)
406 {
407 SILC_LOG_DEBUG(("Making HMAC for message"));
408
409 silc_hmac_init_with_key(hmac, key, key_len);
410 silc_hmac_update(hmac, data, data_len);
411 silc_hmac_final(hmac, return_hash, return_len);
412 }
413
414 /* Creates the HMAC just as above except that the hash value is truncated
415 to the truncated_len sent as argument. NOTE: One should not truncate to
416 less than half of the length of original hash value. However, this
417 routine allows these dangerous truncations. */
418
silc_hmac_make_truncated(SilcHmac hmac,unsigned char * data,SilcUInt32 data_len,SilcUInt32 truncated_len,unsigned char * return_hash)419 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
420 SilcUInt32 data_len,
421 SilcUInt32 truncated_len,
422 unsigned char *return_hash)
423 {
424 unsigned char hvalue[SILC_HASH_MAXLEN];
425
426 SILC_LOG_DEBUG(("Making HMAC for message"));
427
428 silc_hmac_init(hmac);
429 silc_hmac_update(hmac, data, data_len);
430 silc_hmac_final(hmac, return_hash, NULL);
431 memcpy(return_hash, hvalue, truncated_len);
432 memset(hvalue, 0, sizeof(hvalue));
433 }
434
435 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
436
silc_hmac_init(SilcHmac hmac)437 void silc_hmac_init(SilcHmac hmac)
438 {
439 silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
440 }
441
442 /* Same as above but with specific key */
443
silc_hmac_init_with_key(SilcHmac hmac,const unsigned char * key,SilcUInt32 key_len)444 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
445 SilcUInt32 key_len)
446 {
447 SilcHash hash = hmac->hash;
448 silc_hmac_init_internal(hmac, (unsigned char *)key, key_len);
449 silc_hash_init(hash);
450 silc_hash_update(hash, hmac->inner_pad, silc_hash_block_len(hash));
451 }
452
453 /* Add data to be used in the MAC computation. */
454
silc_hmac_update(SilcHmac hmac,const unsigned char * data,SilcUInt32 data_len)455 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
456 SilcUInt32 data_len)
457 {
458 SilcHash hash = hmac->hash;
459 silc_hash_update(hash, data, data_len);
460 }
461
462 /* Compute the final MAC. */
463
silc_hmac_final(SilcHmac hmac,unsigned char * return_hash,SilcUInt32 * return_len)464 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
465 SilcUInt32 *return_len)
466 {
467 SilcHash hash = hmac->hash;
468 unsigned char mac[SILC_HASH_MAXLEN];
469
470 silc_hash_final(hash, mac);
471 silc_hash_init(hash);
472 silc_hash_update(hash, hmac->outer_pad, silc_hash_block_len(hash));
473 silc_hash_update(hash, mac, silc_hash_len(hash));
474 silc_hash_final(hash, mac);
475 memcpy(return_hash, mac, hmac->hmac->len);
476 memset(mac, 0, sizeof(mac));
477
478 if (return_len)
479 *return_len = hmac->hmac->len;
480 }
481