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