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