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