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