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     list[len - 1] = 0;
207   }
208
209   return list;
210 }
211
212 /* Sets the HMAC key used in the HMAC creation */
213
214 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
215                        uint32 key_len)
216 {
217   if (hmac->key) {
218     memset(hmac->key, 0, hmac->key_len);
219     silc_free(hmac->key);
220   }
221   hmac->key = silc_calloc(key_len, sizeof(unsigned char));
222   hmac->key_len = key_len;
223   memcpy(hmac->key, key, key_len);
224 }
225
226 /* Creates the HMAC. The created keyed hash value is returned to 
227    return_hash argument. */
228
229 void silc_hmac_make_internal(SilcHmac hmac, unsigned char *data,
230                              uint32 data_len, unsigned char *key,
231                              uint32 key_len, unsigned char *return_hash)
232 {
233   SilcHash hash = hmac->hash;
234   unsigned char inner_pad[64];
235   unsigned char outer_pad[64];
236   unsigned char hvalue[20], mac[20];
237   void *hash_context;
238   int i;
239
240   SILC_LOG_DEBUG(("Making HMAC for message"));
241
242   hash_context = silc_calloc(1, hash->hash->context_len());
243
244   memset(inner_pad, 0, sizeof(inner_pad));
245   memset(outer_pad, 0, sizeof(outer_pad));
246
247   /* If the key length is more than block size of the hash function, the
248      key is hashed. */
249   if (key_len > hash->hash->block_len) {
250     silc_hash_make(hash, key, key_len, hvalue);
251     key = hvalue;
252     key_len = hash->hash->hash_len;
253   }
254
255   /* Copy the key into the pads */
256   memcpy(inner_pad, key, key_len);
257   memcpy(outer_pad, key, key_len);
258
259   /* XOR the key with pads */
260   for (i = 0; i < hash->hash->block_len; i++) {
261     inner_pad[i] ^= 0x36;
262     outer_pad[i] ^= 0x5c;
263   }
264
265   /* Do the HMAC transform (too bad I can't do make_hash directly, sigh) */
266   hash->hash->init(hash_context);
267   hash->hash->update(hash_context, inner_pad, hash->hash->block_len);
268   hash->hash->update(hash_context, data, data_len);
269   hash->hash->final(hash_context, mac);
270   hash->hash->init(hash_context);
271   hash->hash->update(hash_context, outer_pad, hash->hash->block_len);
272   hash->hash->update(hash_context, mac, hash->hash->hash_len);
273   hash->hash->final(hash_context, mac);
274   memcpy(return_hash, mac, hmac->hmac->len);
275   memset(mac, 0, sizeof(mac));
276   silc_free(hash_context);
277 }
278
279 /* Create the HMAC. This is thee make_hmac function pointer.  This
280    uses the internal key set with silc_hmac_set_key. */
281
282 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
283                     uint32 data_len, unsigned char *return_hash,
284                     uint32 *return_len)
285 {
286   silc_hmac_make_internal(hmac, data, data_len, hmac->key, 
287                           hmac->key_len, return_hash);
288   if (return_len)
289     *return_len = hmac->hmac->len;
290 }
291
292 /* Creates HMAC just as above except that this doesn't use the internal
293    key. The key is sent as argument to the function. */
294
295 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
296                              uint32 data_len, 
297                              unsigned char *key, uint32 key_len,
298                              unsigned char *return_hash,
299                              uint32 *return_len)
300 {
301   silc_hmac_make_internal(hmac, data, data_len, key, key_len, return_hash);
302   if (return_len)
303     *return_len = hmac->hmac->len;
304 }
305
306 /* Creates the HMAC just as above except that the hash value is truncated
307    to the truncated_len sent as argument. NOTE: One should not truncate to
308    less than half of the length of original hash value. However, this 
309    routine allows these dangerous truncations. */
310
311 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
312                               uint32 data_len,
313                               uint32 truncated_len,
314                               unsigned char *return_hash)
315 {
316   unsigned char hvalue[20];
317
318   silc_hmac_make_internal(hmac, data, data_len, 
319                           hmac->key, hmac->key_len, hvalue);
320   memcpy(return_hash, hvalue, truncated_len);
321   memset(hvalue, 0, sizeof(hvalue));
322 }