updates.
[silc.git] / lib / silccrypt / silchmac.c
1 /*
2
3   silchmac.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 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; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "silcincludes.h"
23
24 /* List of dynamically registered HMACs. */
25 SilcDList silc_hmac_list = NULL;
26
27 /* Default hmacs for silc_hmac_register_default(). */
28 SilcHmacObject silc_default_hmacs[] =
29 {
30   { "hmac-sha1-96", 12 },
31   { "hmac-md5-96", 12 },
32   { "hmac-sha1", 20 },
33   { "hmac-md5", 16 },
34
35   { NULL, 0 }
36 };
37
38 /* Registers a new HMAC into the SILC. This function is used at the
39    initialization of the SILC. */
40
41 bool silc_hmac_register(SilcHmacObject *hmac)
42 {
43   SilcHmacObject *new;
44
45   SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
46
47   new = silc_calloc(1, sizeof(*new));
48   new->name = strdup(hmac->name);
49   new->len = hmac->len;
50
51   /* Add to list */
52   if (silc_hmac_list == NULL)
53     silc_hmac_list = silc_dlist_init();
54   silc_dlist_add(silc_hmac_list, new);
55
56   return TRUE;
57 }
58
59 /* Unregister a HMAC from the SILC. */
60
61 bool silc_hmac_unregister(SilcHmacObject *hmac)
62 {
63   SilcHmacObject *entry;
64
65   SILC_LOG_DEBUG(("Unregistering HMAC"));
66
67   if (!silc_hmac_list)
68     return FALSE;
69
70   silc_dlist_start(silc_hmac_list);
71   while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
72     if (hmac == SILC_ALL_HMACS || entry == hmac) {
73       silc_dlist_del(silc_hmac_list, entry);
74
75       if (silc_dlist_count(silc_hmac_list) == 0) {
76         silc_dlist_uninit(silc_hmac_list);
77         silc_hmac_list = NULL;
78       }
79
80       return TRUE;
81     }
82   }
83
84   return FALSE;
85 }
86
87 /* Function that registers all the default hmacs (all builtin ones). 
88    The application may use this to register the default hmacs if
89    specific hmacs in any specific order is not wanted. */
90
91 bool silc_hmac_register_default(void)
92 {
93   int i;
94
95   for (i = 0; silc_default_hmacs[i].name; i++)
96     silc_hmac_register(&(silc_default_hmacs[i]));
97
98   return TRUE;
99 }
100
101 /* Allocates a new SilcHmac object of name of `name'.  The `hash' may
102    be provided as argument.  If provided it is used as the hash function
103    of the HMAC.  If it is NULL then the hash function is allocated and
104    the name of the hash algorithm is derived from the `name'. */
105
106 bool silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
107 {
108   SilcHmacObject *entry;
109
110   SILC_LOG_DEBUG(("Allocating new HMAC"));
111
112   /* Allocate the new object */
113   *new_hmac = silc_calloc(1, sizeof(**new_hmac));
114
115   if (!hash) {
116     char *tmp = strdup(name), *hname;
117
118     hname = tmp;
119     if (strchr(hname, '-'))
120       hname = strchr(hname, '-') + 1;
121     if (strchr(hname, '-'))
122       *strchr(hname, '-') = '\0';
123
124     if (!silc_hash_alloc(hname, &hash)) {
125       silc_free(tmp);
126       return FALSE;
127     }
128
129     (*new_hmac)->allocated_hash = TRUE;
130     silc_free(tmp);
131   }
132
133   (*new_hmac)->hash = hash;
134
135   if (silc_hmac_list) {
136     silc_dlist_start(silc_hmac_list);
137     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
138       if (!strcmp(entry->name, name)) {
139         (*new_hmac)->hmac = entry; 
140         return TRUE;
141       }
142     }
143   }
144
145   return FALSE;
146 }
147
148 /* Free's the SilcHmac object. */
149
150 void silc_hmac_free(SilcHmac hmac)
151 {
152   if (hmac) {
153     if (hmac->allocated_hash)
154       silc_hash_free(hmac->hash);
155     silc_free(hmac);
156   }
157 }
158
159 /* Returns the length of the MAC that the HMAC will produce. */
160
161 uint32 silc_hmac_len(SilcHmac hmac)
162 {
163   return hmac->hmac->len;
164 }
165
166 /* Returns TRUE if HMAC `name' is supported. */
167
168 bool silc_hmac_is_supported(const char *name)
169 {
170   SilcHmacObject *entry;
171
172   if (!name)
173     return FALSE;
174   
175   if (silc_hmac_list) {
176     silc_dlist_start(silc_hmac_list);
177     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
178       if (!strcmp(entry->name, name))
179         return TRUE;
180     }
181   }
182
183   return FALSE;
184 }
185
186 /* Returns comma separated list of supported HMACs. */
187
188 char *silc_hmac_get_supported()
189 {
190   SilcHmacObject *entry;
191   char *list = NULL;
192   int len;
193
194   len = 0;
195   if (silc_hmac_list) {
196     silc_dlist_start(silc_hmac_list);
197     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
198       len += strlen(entry->name);
199       list = silc_realloc(list, len + 1);
200       
201       memcpy(list + (len - strlen(entry->name)), 
202              entry->name, strlen(entry->name));
203       memcpy(list + len, ",", 1);
204       len++;
205     }
206   }
207
208   list[len - 1] = 0;
209
210   return list;
211 }
212
213 /* Sets the HMAC key used in the HMAC creation */
214
215 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
216                        uint32 key_len)
217 {
218   if (hmac->key) {
219     memset(hmac->key, 0, hmac->key_len);
220     silc_free(hmac->key);
221   }
222   hmac->key = silc_calloc(key_len, sizeof(unsigned char));
223   hmac->key_len = key_len;
224   memcpy(hmac->key, key, key_len);
225 }
226
227 /* Creates the HMAC. The created keyed hash value is returned to 
228    return_hash argument. */
229
230 void silc_hmac_make_internal(SilcHmac hmac, unsigned char *data,
231                              uint32 data_len, unsigned char *key,
232                              uint32 key_len, unsigned char *return_hash)
233 {
234   SilcHash hash = hmac->hash;
235   unsigned char inner_pad[hash->hash->block_len + 1];
236   unsigned char outer_pad[hash->hash->block_len + 1];
237   unsigned char hvalue[hash->hash->hash_len];
238   unsigned char mac[128];
239   void *hash_context;
240   int i;
241
242   SILC_LOG_DEBUG(("Making HMAC for message"));
243
244   hash_context = silc_calloc(1, hash->hash->context_len());
245
246   memset(inner_pad, 0, sizeof(inner_pad));
247   memset(outer_pad, 0, sizeof(outer_pad));
248
249   /* If the key length is more than block size of the hash function, the
250      key is hashed. */
251   if (key_len > hash->hash->block_len) {
252     silc_hash_make(hash, key, key_len, hvalue);
253     key = hvalue;
254     key_len = hash->hash->hash_len;
255   }
256
257   /* Copy the key into the pads */
258   memcpy(inner_pad, key, key_len);
259   memcpy(outer_pad, key, key_len);
260
261   /* XOR the key with pads */
262   for (i = 0; i < hash->hash->block_len; i++) {
263     inner_pad[i] ^= 0x36;
264     outer_pad[i] ^= 0x5c;
265   }
266
267   /* Do the HMAC transform (too bad I can't do make_hash directly, sigh) */
268   hash->hash->init(hash_context);
269   hash->hash->update(hash_context, inner_pad, hash->hash->block_len);
270   hash->hash->update(hash_context, data, data_len);
271   hash->hash->final(hash_context, mac);
272   hash->hash->init(hash_context);
273   hash->hash->update(hash_context, outer_pad, hash->hash->block_len);
274   hash->hash->update(hash_context, mac, hash->hash->hash_len);
275   hash->hash->final(hash_context, mac);
276   memcpy(return_hash, mac, hmac->hmac->len);
277   memset(mac, 0, sizeof(mac));
278   silc_free(hash_context);
279 }
280
281 /* Create the HMAC. This is thee make_hmac function pointer.  This
282    uses the internal key set with silc_hmac_set_key. */
283
284 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
285                     uint32 data_len, unsigned char *return_hash,
286                     uint32 *return_len)
287 {
288   silc_hmac_make_internal(hmac, data, data_len, hmac->key, 
289                           hmac->key_len, return_hash);
290   if (return_len)
291     *return_len = hmac->hmac->len;
292 }
293
294 /* Creates HMAC just as above except that this doesn't use the internal
295    key. The key is sent as argument to the function. */
296
297 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
298                              uint32 data_len, 
299                              unsigned char *key, uint32 key_len,
300                              unsigned char *return_hash,
301                              uint32 *return_len)
302 {
303   silc_hmac_make_internal(hmac, data, data_len, key, key_len, return_hash);
304   if (return_len)
305     *return_len = hmac->hmac->len;
306 }
307
308 /* Creates the HMAC just as above except that the hash value is truncated
309    to the truncated_len sent as argument. NOTE: One should not truncate to
310    less than half of the length of original hash value. However, this 
311    routine allows these dangerous truncations. */
312
313 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
314                               uint32 data_len,
315                               uint32 truncated_len,
316                               unsigned char *return_hash)
317 {
318   unsigned char hvalue[hmac->hash->hash->hash_len];
319
320   silc_hmac_make_internal(hmac, data, data_len, 
321                           hmac->key, hmac->key_len, hvalue);
322   memcpy(return_hash, hvalue, truncated_len);
323   memset(hvalue, 0, sizeof(hvalue));
324 }