6293cea6757176f65c1dbf0a3042ccb9c50b4b2a
[silc.git] / lib / silccrypt / silchmac.c
1 /*
2
3   silchmac.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1999 - 2001 Pekka Riikonen
8
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.
12
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.
17
18 */
19 /* $Id$ */
20
21 #include "silcincludes.h"
22
23 /* HMAC context */
24 struct SilcHmacStruct {
25   SilcHmacObject *hmac;
26   SilcHash hash;
27   bool allocated_hash;          /* TRUE if the hash was allocated */
28
29   unsigned char *key;
30   uint32 key_len;
31
32   unsigned char inner_pad[64];
33   unsigned char outer_pad[64];
34   void *hash_context;
35
36   /* XXX backwards thingy for 0.5.x MAC computation.
37      XXX Remove in 0.7.x */
38   bool backwards_support;
39 };
40
41 /* List of dynamically registered HMACs. */
42 SilcDList silc_hmac_list = NULL;
43
44 /* Default hmacs for silc_hmac_register_default(). */
45 SilcHmacObject silc_default_hmacs[] =
46 {
47   { "hmac-sha1-96", 12 },
48   { "hmac-md5-96", 12 },
49   { "hmac-sha1", 20 },
50   { "hmac-md5", 16 },
51
52   { NULL, 0 }
53 };
54
55 static void silc_hmac_init_internal(SilcHmac hmac, unsigned char *key,
56                                     uint32 key_len)
57 {
58   SilcHash hash = hmac->hash;
59   unsigned char hvalue[20];
60   int i;
61
62   memset(hmac->inner_pad, 0, sizeof(hmac->inner_pad));
63   memset(hmac->outer_pad, 0, sizeof(hmac->outer_pad));
64
65   /* If the key length is more than block size of the hash function, the
66      key is hashed. */
67   if (key_len > hash->hash->block_len) {
68     silc_hash_make(hash, key, key_len, hvalue);
69     key = hvalue;
70     key_len = hash->hash->hash_len;
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 < hash->hash->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
87 bool silc_hmac_register(SilcHmacObject *hmac)
88 {
89   SilcHmacObject *new;
90
91   SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
92
93   new = silc_calloc(1, sizeof(*new));
94   new->name = strdup(hmac->name);
95   new->len = hmac->len;
96
97   /* Add to list */
98   if (silc_hmac_list == NULL)
99     silc_hmac_list = silc_dlist_init();
100   silc_dlist_add(silc_hmac_list, new);
101
102   return TRUE;
103 }
104
105 /* Unregister a HMAC from the SILC. */
106
107 bool silc_hmac_unregister(SilcHmacObject *hmac)
108 {
109   SilcHmacObject *entry;
110
111   SILC_LOG_DEBUG(("Unregistering HMAC"));
112
113   if (!silc_hmac_list)
114     return FALSE;
115
116   silc_dlist_start(silc_hmac_list);
117   while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
118     if (hmac == SILC_ALL_HMACS || entry == hmac) {
119       silc_dlist_del(silc_hmac_list, entry);
120
121       if (silc_dlist_count(silc_hmac_list) == 0) {
122         silc_dlist_uninit(silc_hmac_list);
123         silc_hmac_list = NULL;
124       }
125
126       return TRUE;
127     }
128   }
129
130   return FALSE;
131 }
132
133 /* Function that registers all the default hmacs (all builtin ones). 
134    The application may use this to register the default hmacs if
135    specific hmacs in any specific order is not wanted. */
136
137 bool silc_hmac_register_default(void)
138 {
139   int i;
140
141   for (i = 0; silc_default_hmacs[i].name; i++)
142     silc_hmac_register(&(silc_default_hmacs[i]));
143
144   return TRUE;
145 }
146
147 /* Allocates a new SilcHmac object of name of `name'.  The `hash' may
148    be provided as argument.  If provided it is used as the hash function
149    of the HMAC.  If it is NULL then the hash function is allocated and
150    the name of the hash algorithm is derived from the `name'. */
151
152 bool silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
153 {
154   SilcHmacObject *entry;
155
156   SILC_LOG_DEBUG(("Allocating new HMAC"));
157
158   /* Allocate the new object */
159   *new_hmac = silc_calloc(1, sizeof(**new_hmac));
160
161   if (!hash) {
162     char *tmp = strdup(name), *hname;
163
164     hname = tmp;
165     if (strchr(hname, '-'))
166       hname = strchr(hname, '-') + 1;
167     if (strchr(hname, '-'))
168       *strchr(hname, '-') = '\0';
169
170     if (!silc_hash_alloc(hname, &hash)) {
171       silc_free(tmp);
172       silc_free(*new_hmac);
173       *new_hmac = NULL;
174       return FALSE;
175     }
176
177     (*new_hmac)->allocated_hash = TRUE;
178     silc_free(tmp);
179   }
180
181   (*new_hmac)->hash = hash;
182
183   if (silc_hmac_list) {
184     silc_dlist_start(silc_hmac_list);
185     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
186       if (!strcmp(entry->name, name)) {
187         (*new_hmac)->hmac = entry; 
188         return TRUE;
189       }
190     }
191   }
192
193   silc_free(*new_hmac);
194   *new_hmac = NULL;
195   return FALSE;
196 }
197
198 /* Free's the SilcHmac object. */
199
200 void silc_hmac_free(SilcHmac hmac)
201 {
202   if (hmac) {
203     if (hmac->allocated_hash)
204       silc_hash_free(hmac->hash);
205
206     if (hmac->key) {
207       memset(hmac->key, 0, hmac->key_len);
208       silc_free(hmac->key);
209     }
210
211     silc_free(hmac->hash_context);
212     silc_free(hmac);
213   }
214 }
215
216 /* Returns the length of the MAC that the HMAC will produce. */
217
218 uint32 silc_hmac_len(SilcHmac hmac)
219 {
220   return hmac->hmac->len;
221 }
222
223 /* Get hash context */
224
225 SilcHash silc_hmac_get_hash(SilcHmac hmac)
226 {
227   return hmac->hash;
228 }
229
230 /* Return name of hmac */
231
232 const char *silc_hmac_get_name(SilcHmac hmac)
233 {
234   return hmac->hmac->name;
235 }
236
237 /* Returns TRUE if HMAC `name' is supported. */
238
239 bool silc_hmac_is_supported(const char *name)
240 {
241   SilcHmacObject *entry;
242
243   if (!name)
244     return FALSE;
245   
246   if (silc_hmac_list) {
247     silc_dlist_start(silc_hmac_list);
248     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
249       if (!strcmp(entry->name, name))
250         return TRUE;
251     }
252   }
253
254   return FALSE;
255 }
256
257 /* Returns comma separated list of supported HMACs. */
258
259 char *silc_hmac_get_supported()
260 {
261   SilcHmacObject *entry;
262   char *list = NULL;
263   int len;
264
265   len = 0;
266   if (silc_hmac_list) {
267     silc_dlist_start(silc_hmac_list);
268     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
269       len += strlen(entry->name);
270       list = silc_realloc(list, len + 1);
271       
272       memcpy(list + (len - strlen(entry->name)), 
273              entry->name, strlen(entry->name));
274       memcpy(list + len, ",", 1);
275       len++;
276     }
277     list[len - 1] = 0;
278   }
279
280   return list;
281 }
282
283 /* Sets the HMAC key used in the HMAC creation */
284
285 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
286                        uint32 key_len)
287 {
288   if (hmac->key) {
289     memset(hmac->key, 0, hmac->key_len);
290     silc_free(hmac->key);
291   }
292   hmac->key = silc_calloc(key_len, sizeof(unsigned char));
293   hmac->key_len = key_len;
294   memcpy(hmac->key, key, key_len);
295 }
296
297 /* Create the HMAC. This is thee make_hmac function pointer.  This
298    uses the internal key set with silc_hmac_set_key. */
299
300 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
301                     uint32 data_len, unsigned char *return_hash,
302                     uint32 *return_len)
303 {
304   SILC_LOG_DEBUG(("Making HMAC for message"));
305
306   silc_hmac_init(hmac);
307   silc_hmac_update(hmac, data, data_len);
308   silc_hmac_final(hmac, return_hash, return_len);
309 }
310
311 /* Creates HMAC just as above except that this doesn't use the internal
312    key. The key is sent as argument to the function. */
313
314 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
315                              uint32 data_len, 
316                              unsigned char *key, uint32 key_len,
317                              unsigned char *return_hash,
318                              uint32 *return_len)
319 {
320   SILC_LOG_DEBUG(("Making HMAC for message"));
321
322   silc_hmac_init_with_key(hmac, key, key_len);
323   silc_hmac_update(hmac, data, data_len);
324   silc_hmac_final(hmac, return_hash, return_len);
325 }
326
327 /* Creates the HMAC just as above except that the hash value is truncated
328    to the truncated_len sent as argument. NOTE: One should not truncate to
329    less than half of the length of original hash value. However, this 
330    routine allows these dangerous truncations. */
331
332 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
333                               uint32 data_len,
334                               uint32 truncated_len,
335                               unsigned char *return_hash)
336 {
337   unsigned char hvalue[20];
338
339   SILC_LOG_DEBUG(("Making HMAC for message"));
340
341   silc_hmac_init(hmac);
342   silc_hmac_update(hmac, data, data_len);
343   silc_hmac_final(hmac, return_hash, NULL);
344   memcpy(return_hash, hvalue, truncated_len);
345   memset(hvalue, 0, sizeof(hvalue));
346 }
347
348 /* Init HMAC for silc_hmac_update and silc_hmac_final. */
349
350 void silc_hmac_init(SilcHmac hmac)
351 {
352   silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
353 }
354
355 /* Same as above but with specific key */
356
357 void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
358                              uint32 key_len)
359 {
360   SilcHash hash = hmac->hash;
361
362   silc_hmac_init_internal(hmac, hmac->key, hmac->key_len);
363
364   if (!hmac->hash_context)
365     hmac->hash_context = silc_calloc(1, hash->hash->context_len());
366
367   hash->hash->init(hmac->hash_context);
368   hash->hash->update(hmac->hash_context, hmac->inner_pad, 
369                      hash->hash->block_len);
370 }
371
372 /* Add data to be used in the MAC computation. */
373
374 void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
375                       uint32 data_len)
376 {
377   SilcHash hash = hmac->hash;
378   hash->hash->update(hmac->hash_context, (unsigned char *)data, data_len);
379 }
380
381 /* Compute the final MAC. */
382
383 void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
384                      uint32 *return_len)
385 {
386   SilcHash hash = hmac->hash;
387   unsigned char mac[20];
388
389   hash->hash->final(hmac->hash_context, mac);
390   hash->hash->init(hmac->hash_context);
391   hash->hash->update(hmac->hash_context, hmac->outer_pad, 
392                      hash->hash->block_len);
393   hash->hash->update(hmac->hash_context, mac, hash->hash->hash_len);
394   hash->hash->final(hmac->hash_context, mac);
395   memcpy(return_hash, mac, hmac->hmac->len);
396   memset(mac, 0, sizeof(mac));
397
398   if (return_len)
399     *return_len = hmac->hmac->len;
400 }
401
402 void silc_hmac_set_b(SilcHmac hmac)
403 {
404   hmac->backwards_support = TRUE;
405 }
406
407 bool silc_hmac_get_b(SilcHmac hmac)
408 {
409   return hmac->backwards_support;
410 }