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 /* Registers a new HMAC into the SILC. This function is used at the
28    initialization of the SILC. */
29
30 int silc_hmac_register(SilcHmacObject *hmac)
31 {
32   SilcHmacObject *new;
33
34   SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
35
36   new = silc_calloc(1, sizeof(*new));
37   new->name = strdup(hmac->name);
38   new->len = hmac->len;
39
40   /* Add to list */
41   if (silc_hmac_list == NULL)
42     silc_hmac_list = silc_dlist_init();
43   silc_dlist_add(silc_hmac_list, new);
44
45   return TRUE;
46 }
47
48 /* Unregister a HMAC from the SILC. */
49
50 int silc_hmac_unregister(SilcHmacObject *hmac)
51 {
52   SilcHmacObject *entry;
53
54   SILC_LOG_DEBUG(("Unregistering HMAC"));
55
56   if (!silc_hmac_list)
57     return FALSE;
58
59   silc_dlist_start(silc_hmac_list);
60   while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
61     if (entry == hmac) {
62       silc_dlist_del(silc_hmac_list, entry);
63
64       if (silc_dlist_count(silc_hmac_list) == 0) {
65         silc_dlist_uninit(silc_hmac_list);
66         silc_hmac_list = NULL;
67       }
68
69       return TRUE;
70     }
71   }
72
73   return FALSE;
74 }
75
76 /* Allocates a new SilcHmac object of name of `name'.  The `hash' may
77    be provided as argument.  If provided it is used as the hash function
78    of the HMAC.  If it is NULL then the hash function is allocated and
79    the name of the hash algorithm is derived from the `name'. */
80
81 int silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
82 {
83   SilcHmacObject *entry;
84
85   SILC_LOG_DEBUG(("Allocating new HMAC"));
86
87   /* Allocate the new object */
88   *new_hmac = silc_calloc(1, sizeof(**new_hmac));
89
90   if (!hash) {
91     char *tmp = strdup(name), *hname;
92
93     hname = tmp;
94     if (strchr(hname, '-'))
95       hname = strchr(hname, '-') + 1;
96     if (strchr(hname, '-'))
97       *strchr(hname, '-') = '\0';
98
99     if (!silc_hash_alloc(hname, &hash)) {
100       silc_free(tmp);
101       return FALSE;
102     }
103
104     (*new_hmac)->allocated_hash = TRUE;
105     silc_free(tmp);
106   }
107
108   (*new_hmac)->hash = hash;
109
110   if (silc_hmac_list) {
111     silc_dlist_start(silc_hmac_list);
112     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
113       if (!strcmp(entry->name, name)) {
114         (*new_hmac)->hmac = entry; 
115         return TRUE;
116       }
117     }
118   }
119
120   return FALSE;
121 }
122
123 /* Free's the SilcHmac object. */
124
125 void silc_hmac_free(SilcHmac hmac)
126 {
127   if (hmac) {
128     if (hmac->allocated_hash)
129       silc_hash_free(hmac->hash);
130     silc_free(hmac);
131   }
132 }
133
134 /* Returns the length of the MAC that the HMAC will produce. */
135
136 unsigned int silc_hmac_len(SilcHmac hmac)
137 {
138   return hmac->hmac->len;
139 }
140
141 /* Returns TRUE if HMAC `name' is supported. */
142
143 int silc_hmac_is_supported(const char *name)
144 {
145   SilcHmacObject *entry;
146
147   if (!name)
148     return FALSE;
149   
150   if (silc_hmac_list) {
151     silc_dlist_start(silc_hmac_list);
152     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
153       if (!strcmp(entry->name, name))
154         return TRUE;
155     }
156   }
157
158   return FALSE;
159 }
160
161 /* Returns comma separated list of supported HMACs. */
162
163 char *silc_hmac_get_supported()
164 {
165   SilcHmacObject *entry;
166   char *list = NULL;
167   int len;
168
169   len = 0;
170   if (silc_hmac_list) {
171     silc_dlist_start(silc_hmac_list);
172     while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
173       len += strlen(entry->name);
174       list = silc_realloc(list, len + 1);
175       
176       memcpy(list + (len - strlen(entry->name)), 
177              entry->name, strlen(entry->name));
178       memcpy(list + len, ",", 1);
179       len++;
180     }
181   }
182
183   list[len - 1] = 0;
184
185   return list;
186 }
187
188 /* Sets the HMAC key used in the HMAC creation */
189
190 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
191                        unsigned int key_len)
192 {
193   hmac->key = silc_calloc(key_len, sizeof(unsigned char));
194   hmac->key_len = key_len;
195   memcpy(hmac->key, key, key_len);
196 }
197
198 /* Creates the HMAC. The created keyed hash value is returned to 
199    return_hash argument. */
200
201 void silc_hmac_make_internal(SilcHmac hmac, unsigned char *data,
202                              unsigned int data_len, unsigned char *key,
203                              unsigned int key_len, unsigned char *return_hash)
204 {
205   SilcHash hash = hmac->hash;
206   unsigned char inner_pad[hash->hash->block_len + 1];
207   unsigned char outer_pad[hash->hash->block_len + 1];
208   unsigned char hvalue[hash->hash->hash_len];
209   unsigned char mac[128];
210   void *hash_context;
211   int i;
212
213   SILC_LOG_DEBUG(("Making HMAC for message"));
214
215   hash_context = silc_calloc(1, hash->hash->context_len());
216
217   memset(inner_pad, 0, sizeof(inner_pad));
218   memset(outer_pad, 0, sizeof(outer_pad));
219
220   /* If the key length is more than block size of the hash function, the
221      key is hashed. */
222   if (key_len > hash->hash->block_len) {
223     silc_hash_make(hash, key, key_len, hvalue);
224     key = hvalue;
225     key_len = hash->hash->hash_len;
226   }
227
228   /* Copy the key into the pads */
229   memcpy(inner_pad, key, key_len);
230   memcpy(outer_pad, key, key_len);
231
232   /* XOR the key with pads */
233   for (i = 0; i < hash->hash->block_len; i++) {
234     inner_pad[i] ^= 0x36;
235     outer_pad[i] ^= 0x5c;
236   }
237
238   /* Do the HMAC transform (too bad I can't do make_hash directly, sigh) */
239   hash->hash->init(hash_context);
240   hash->hash->update(hash_context, inner_pad, hash->hash->block_len);
241   hash->hash->update(hash_context, data, data_len);
242   hash->hash->final(hash_context, mac);
243   hash->hash->init(hash_context);
244   hash->hash->update(hash_context, outer_pad, hash->hash->block_len);
245   hash->hash->update(hash_context, mac, hash->hash->hash_len);
246   hash->hash->final(hash_context, mac);
247   memcpy(return_hash, mac, hmac->hmac->len);
248   memset(mac, 0, sizeof(mac));
249 }
250
251 /* Create the HMAC. This is thee make_hmac function pointer.  This
252    uses the internal key set with silc_hmac_set_key. */
253
254 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
255                     unsigned int data_len, unsigned char *return_hash,
256                     unsigned int *return_len)
257 {
258   silc_hmac_make_internal(hmac, data, data_len, hmac->key, 
259                           hmac->key_len, return_hash);
260   if (return_len)
261     *return_len = hmac->hmac->len;
262 }
263
264 /* Creates HMAC just as above except that this doesn't use the internal
265    key. The key is sent as argument to the function. */
266
267 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
268                              unsigned int data_len, 
269                              unsigned char *key, unsigned int key_len,
270                              unsigned char *return_hash,
271                              unsigned int *return_len)
272 {
273   silc_hmac_make_internal(hmac, data, data_len, key, key_len, return_hash);
274   if (return_len)
275     *return_len = hmac->hmac->len;
276 }
277
278 /* Creates the HMAC just as above except that the hash value is truncated
279    to the truncated_len sent as argument. NOTE: One should not truncate to
280    less than half of the length of original hash value. However, this 
281    routine allows these dangerous truncations. */
282
283 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
284                               unsigned int data_len,
285                               unsigned int truncated_len,
286                               unsigned char *return_hash)
287 {
288   unsigned char hvalue[hmac->hash->hash->hash_len];
289
290   silc_hmac_make_internal(hmac, data, data_len, 
291                           hmac->key, hmac->key_len, hvalue);
292   memcpy(return_hash, hvalue, truncated_len);
293   memset(hvalue, 0, sizeof(hvalue));
294 }