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