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       silc_free(*new_hmac);
127       *new_hmac = NULL;
128       return FALSE;
129     }
130
131     (*new_hmac)->allocated_hash = TRUE;
132     silc_free(tmp);
133   }
134
135   (*new_hmac)->hash = hash;
136
137   if (silc_hmac_list) {
138     silc_dlist_start(silc_hmac_list);
139     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
140       if (!strcmp(entry->name, name)) {
141         (*new_hmac)->hmac = entry; 
142         return TRUE;
143       }
144     }
145   }
146
147   silc_free(*new_hmac);
148   *new_hmac = NULL;
149   return FALSE;
150 }
151
152 /* Free's the SilcHmac object. */
153
154 void silc_hmac_free(SilcHmac hmac)
155 {
156   if (hmac) {
157     if (hmac->allocated_hash)
158       silc_hash_free(hmac->hash);
159     silc_free(hmac);
160   }
161 }
162
163 /* Returns the length of the MAC that the HMAC will produce. */
164
165 uint32 silc_hmac_len(SilcHmac hmac)
166 {
167   return hmac->hmac->len;
168 }
169
170 /* Returns TRUE if HMAC `name' is supported. */
171
172 bool silc_hmac_is_supported(const char *name)
173 {
174   SilcHmacObject *entry;
175
176   if (!name)
177     return FALSE;
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         return TRUE;
184     }
185   }
186
187   return FALSE;
188 }
189
190 /* Returns comma separated list of supported HMACs. */
191
192 char *silc_hmac_get_supported()
193 {
194   SilcHmacObject *entry;
195   char *list = NULL;
196   int len;
197
198   len = 0;
199   if (silc_hmac_list) {
200     silc_dlist_start(silc_hmac_list);
201     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
202       len += strlen(entry->name);
203       list = silc_realloc(list, len + 1);
204       
205       memcpy(list + (len - strlen(entry->name)), 
206              entry->name, strlen(entry->name));
207       memcpy(list + len, ",", 1);
208       len++;
209     }
210     list[len - 1] = 0;
211   }
212
213   return list;
214 }
215
216 /* Sets the HMAC key used in the HMAC creation */
217
218 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
219                        uint32 key_len)
220 {
221   if (hmac->key) {
222     memset(hmac->key, 0, hmac->key_len);
223     silc_free(hmac->key);
224   }
225   hmac->key = silc_calloc(key_len, sizeof(unsigned char));
226   hmac->key_len = key_len;
227   memcpy(hmac->key, key, key_len);
228 }
229
230 /* Creates the HMAC. The created keyed hash value is returned to 
231    return_hash argument. */
232
233 void silc_hmac_make_internal(SilcHmac hmac, unsigned char *data,
234                              uint32 data_len, unsigned char *key,
235                              uint32 key_len, unsigned char *return_hash)
236 {
237   SilcHash hash = hmac->hash;
238   unsigned char inner_pad[64];
239   unsigned char outer_pad[64];
240   unsigned char hvalue[20], mac[20];
241   void *hash_context;
242   int i;
243
244   SILC_LOG_DEBUG(("Making HMAC for message"));
245
246   hash_context = silc_calloc(1, hash->hash->context_len());
247
248   memset(inner_pad, 0, sizeof(inner_pad));
249   memset(outer_pad, 0, sizeof(outer_pad));
250
251   /* If the key length is more than block size of the hash function, the
252      key is hashed. */
253   if (key_len > hash->hash->block_len) {
254     silc_hash_make(hash, key, key_len, hvalue);
255     key = hvalue;
256     key_len = hash->hash->hash_len;
257   }
258
259   /* Copy the key into the pads */
260   memcpy(inner_pad, key, key_len);
261   memcpy(outer_pad, key, key_len);
262
263   /* XOR the key with pads */
264   for (i = 0; i < hash->hash->block_len; i++) {
265     inner_pad[i] ^= 0x36;
266     outer_pad[i] ^= 0x5c;
267   }
268
269   /* Do the HMAC transform (too bad I can't do make_hash directly, sigh) */
270   hash->hash->init(hash_context);
271   hash->hash->update(hash_context, inner_pad, hash->hash->block_len);
272   hash->hash->update(hash_context, data, data_len);
273   hash->hash->final(hash_context, mac);
274   hash->hash->init(hash_context);
275   hash->hash->update(hash_context, outer_pad, hash->hash->block_len);
276   hash->hash->update(hash_context, mac, hash->hash->hash_len);
277   hash->hash->final(hash_context, mac);
278   memcpy(return_hash, mac, hmac->hmac->len);
279   memset(mac, 0, sizeof(mac));
280   silc_free(hash_context);
281 }
282
283 /* Create the HMAC. This is thee make_hmac function pointer.  This
284    uses the internal key set with silc_hmac_set_key. */
285
286 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
287                     uint32 data_len, unsigned char *return_hash,
288                     uint32 *return_len)
289 {
290   silc_hmac_make_internal(hmac, data, data_len, hmac->key, 
291                           hmac->key_len, return_hash);
292   if (return_len)
293     *return_len = hmac->hmac->len;
294 }
295
296 /* Creates HMAC just as above except that this doesn't use the internal
297    key. The key is sent as argument to the function. */
298
299 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
300                              uint32 data_len, 
301                              unsigned char *key, uint32 key_len,
302                              unsigned char *return_hash,
303                              uint32 *return_len)
304 {
305   silc_hmac_make_internal(hmac, data, data_len, key, key_len, return_hash);
306   if (return_len)
307     *return_len = hmac->hmac->len;
308 }
309
310 /* Creates the HMAC just as above except that the hash value is truncated
311    to the truncated_len sent as argument. NOTE: One should not truncate to
312    less than half of the length of original hash value. However, this 
313    routine allows these dangerous truncations. */
314
315 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
316                               uint32 data_len,
317                               uint32 truncated_len,
318                               unsigned char *return_hash)
319 {
320   unsigned char hvalue[20];
321
322   silc_hmac_make_internal(hmac, data, data_len, 
323                           hmac->key, hmac->key_len, hvalue);
324   memcpy(return_hash, hvalue, truncated_len);
325   memset(hvalue, 0, sizeof(hvalue));
326 }