Added stacktrace support with --enable-stack-trace option.
[silc.git] / lib / silccrypt / silchash.c
1 /*
2
3   silchash.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2002 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; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silcincludes.h"
22
23 #include "md5.h"
24 #include "sha1.h"
25
26 /* The main SILC hash structure. */
27 struct SilcHashStruct {
28   SilcHashObject *hash;
29   void *context;
30 };
31
32 #ifndef SILC_EPOC
33 /* List of dynamically registered hash functions. */
34 SilcDList silc_hash_list = NULL;
35 #endif /* SILC_EPOC */
36
37 /* Default hash functions for silc_hash_register_default(). */
38 const SilcHashObject silc_default_hash[] = 
39 {
40   { "sha1", 20, 64, silc_sha1_init, silc_sha1_update, silc_sha1_final,
41     silc_sha1_transform, silc_sha1_context_len },
42   { "md5", 16, 64, silc_md5_init, silc_md5_update, silc_md5_final,
43     silc_md5_transform, silc_md5_context_len },
44
45   { NULL, 0, 0, NULL, NULL, NULL, NULL, NULL }
46 };
47
48 /* Registers a new hash function into the SILC. This function is used at
49    the initialization of the SILC. */
50
51 bool silc_hash_register(const SilcHashObject *hash)
52 {
53 #ifndef SILC_EPOC
54   SilcHashObject *new;
55
56   SILC_LOG_DEBUG(("Registering new hash function `%s'", hash->name));
57
58   /* Check for existing */
59   if (silc_hash_list) {
60     SilcHashObject *entry;
61     silc_dlist_start(silc_hash_list);
62     while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
63       if (!strcmp(entry->name, hash->name))
64         return FALSE;
65     }
66   }
67
68   new = silc_calloc(1, sizeof(*new));
69   new->name = strdup(hash->name);
70   new->hash_len = hash->hash_len;
71   new->block_len = hash->block_len;
72   new->init = hash->init;
73   new->update = hash->update;
74   new->final = hash->final;
75   new->transform = hash->transform;
76   new->context_len = hash->context_len;
77
78   /* Add to list */
79   if (silc_hash_list == NULL)
80     silc_hash_list = silc_dlist_init();
81   silc_dlist_add(silc_hash_list, new);
82
83 #endif /* SILC_EPOC */
84   return TRUE;
85 }
86
87 /* Unregister a hash function from the SILC. */
88
89 bool silc_hash_unregister(SilcHashObject *hash)
90 {
91 #ifndef SILC_EPOC
92   SilcHashObject *entry;
93
94   SILC_LOG_DEBUG(("Unregistering hash function"));
95
96   if (!silc_hash_list)
97     return FALSE;
98
99   silc_dlist_start(silc_hash_list);
100   while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
101     if (hash == SILC_ALL_HASH_FUNCTIONS || entry == hash) {
102       silc_dlist_del(silc_hash_list, entry);
103       silc_free(entry->name);
104       silc_free(entry);
105
106       if (silc_dlist_count(silc_hash_list) == 0) {
107         silc_dlist_uninit(silc_hash_list);
108         silc_hash_list = NULL;
109       }
110
111       return TRUE;
112     }
113   }
114
115 #endif /* SILC_EPOC */
116   return FALSE;
117 }
118
119 /* Function that registers all the default hash funcs (all builtin ones). 
120    The application may use this to register the default hash funcs if
121    specific hash funcs in any specific order is not wanted. */
122
123 bool silc_hash_register_default(void)
124 {
125 #ifndef SILC_EPOC
126   int i;
127
128   for (i = 0; silc_default_hash[i].name; i++)
129     silc_hash_register(&(silc_default_hash[i]));
130
131 #endif /* SILC_EPOC */
132   return TRUE;
133 }
134
135 bool silc_hash_unregister_all(void)
136 {
137 #ifndef SILC_EPOC
138   SilcHashObject *entry;
139
140   if (!silc_hash_list)
141     return FALSE;
142
143   silc_dlist_start(silc_hash_list);
144   while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
145     silc_hash_unregister(entry);
146     if (!silc_hash_list)
147       break;
148   }
149 #endif /* SILC_EPOC */
150   return TRUE;
151 }
152
153 /* Allocates a new SilcHash object. New object is returned into new_hash
154    argument. */
155
156 bool silc_hash_alloc(const unsigned char *name, SilcHash *new_hash)
157 {
158   SilcHashObject *entry = NULL;
159   
160   SILC_LOG_DEBUG(("Allocating new hash object"));
161
162 #ifndef SILC_EPOC
163   if (silc_hash_list) {
164     silc_dlist_start(silc_hash_list);
165     while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
166       if (!strcmp(entry->name, name))
167         break;
168     }
169   }
170 #else
171   {
172     /* On EPOC which don't have globals we check our constant hash list. */
173     int i;
174     for (i = 0; silc_default_hash[i].name; i++) {
175       if (!strcmp(silc_default_hash[i].name, name)) {
176         entry = (SilcHashObject *)&(silc_default_hash[i]);
177         break;
178       }
179     }
180   }
181 #endif /* SILC_EPOC */
182
183   if (entry) {
184     *new_hash = silc_calloc(1, sizeof(**new_hash));
185     (*new_hash)->hash = entry;
186     (*new_hash)->context = silc_calloc(1, entry->context_len());
187     return TRUE;
188   }
189
190   return FALSE;
191 }
192
193 /* Free's the SilcHash object */
194
195 void silc_hash_free(SilcHash hash)
196 {
197   if (hash) {
198     silc_free(hash->context);
199     silc_free(hash);
200   }
201 }
202
203 /* Returns the length of the hash digest. */
204
205 SilcUInt32 silc_hash_len(SilcHash hash)
206 {
207   return hash->hash->hash_len;
208 }
209
210 /* Returns the block lenght of the hash. */
211
212 SilcUInt32 silc_hash_block_len(SilcHash hash)
213 {
214   return hash->hash->block_len;
215 }
216
217 /* Returns the name of the hash function */
218
219 const char *silc_hash_get_name(SilcHash hash)
220 {
221   return hash->hash->name;
222 }
223
224 /* Returns TRUE if hash algorithm `name' is supported. */
225
226 bool silc_hash_is_supported(const unsigned char *name)
227 {
228 #ifndef SILC_EPOC
229   SilcHashObject *entry;
230
231   if (silc_hash_list) {
232     silc_dlist_start(silc_hash_list);
233     while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
234       if (!strcmp(entry->name, name))
235         return TRUE;
236     }
237   }
238 #else
239   {
240     int i;
241     for (i = 0; silc_default_hash[i].name; i++)
242       if (!strcmp(silc_default_hash[i].name, name))
243         return TRUE;
244   }
245 #endif /* SILC_EPOC */
246   return FALSE;
247 }
248
249 /* Returns comma separated list of supported hash functions. */
250
251 char *silc_hash_get_supported(void)
252 {
253   SilcHashObject *entry;
254   char *list = NULL;
255   int len = 0;
256
257 #ifndef SILC_EPOC
258   if (silc_hash_list) {
259     silc_dlist_start(silc_hash_list);
260     while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
261       len += strlen(entry->name);
262       list = silc_realloc(list, len + 1);
263       
264       memcpy(list + (len - strlen(entry->name)), 
265              entry->name, strlen(entry->name));
266       memcpy(list + len, ",", 1);
267       len++;
268     }
269   }
270 #else
271   {
272     int i;
273     for (i = 0; silc_default_hash[i].name; i++) {
274       entry = (SilcHashObject *)&(silc_default_hash[i]);
275       len += strlen(entry->name);
276       list = silc_realloc(list, len + 1);
277       
278       memcpy(list + (len - strlen(entry->name)), 
279              entry->name, strlen(entry->name));
280       memcpy(list + len, ",", 1);
281       len++;
282     }
283   }
284 #endif /* SILC_EPOC */
285
286   list[len - 1] = 0;
287
288   return list;
289 }
290
291 /* Creates the hash value and returns it to the return_hash argument. */
292
293 void silc_hash_make(SilcHash hash, const unsigned char *data, 
294                     SilcUInt32 len, unsigned char *return_hash)
295 {
296   silc_hash_init(hash);
297   silc_hash_update(hash, data, len);
298   silc_hash_final(hash, return_hash);
299 }
300
301 void silc_hash_init(SilcHash hash)
302 {
303   hash->hash->init(hash->context);
304 }
305
306 void silc_hash_update(SilcHash hash, const unsigned char *data,
307                       SilcUInt32 data_len)
308 {
309   hash->hash->update(hash->context, (unsigned char *)data, data_len);
310 }
311
312 void silc_hash_final(SilcHash hash, unsigned char *return_hash)
313 {
314   hash->hash->final(hash->context, return_hash);
315 }
316
317 void silc_hash_transform(SilcHash hash, SilcUInt32 *state,
318                          const unsigned char *data)
319 {
320   hash->hash->transform(state, data);
321 }
322
323 /* Creates fingerprint of the data. If `hash' is NULL SHA1 is used as
324    default hash function. The returned fingerprint must be freed by the
325    caller. */
326
327 char *silc_hash_fingerprint(SilcHash hash, const unsigned char *data,
328                             SilcUInt32 data_len)
329 {
330   SilcHash new_hash = NULL;
331   unsigned char h[32];
332   char *ret;
333
334   if (!hash) {
335     silc_hash_alloc("sha1", &new_hash);
336     hash = new_hash;
337   }
338
339   silc_hash_make(hash, data, data_len, h);
340   ret = silc_fingerprint(h, hash->hash->hash_len);
341
342   if (new_hash != NULL)
343     silc_hash_free(new_hash);
344   return ret;
345 }
346
347 static const char vo[]= "aeiouy";
348 static const char co[]= "bcdfghklmnprstvzx";
349
350 /* Creates a babbleprint (Bubble Babble Encoding, developed by Antti
351    Huima (draft-huima-babble-01.txt)), by first computing real fingerprint
352    using `hash' or if NULL, then using SHA1, and then encoding the
353    fingerprint to the babbleprint. */
354
355 char *silc_hash_babbleprint(SilcHash hash, const unsigned char *data,
356                             SilcUInt32 data_len)
357 {
358   SilcHash new_hash = NULL;
359   char *babbleprint;
360   unsigned char hval[32];
361   unsigned int a, b, c, d, e, check;
362   int i, k, out_len;
363
364   if (!hash) {
365     silc_hash_alloc("sha1", &new_hash);
366     hash = new_hash;
367   }
368
369   /* Take fingerprint */
370   silc_hash_make(hash, data, data_len, hval);
371
372   /* Encode babbleprint */
373   out_len = (((hash->hash->hash_len + 1) / 2) + 1) * 6;
374   babbleprint = silc_calloc(out_len, sizeof(*babbleprint));
375   babbleprint[0] = co[16];
376
377   check = 1;
378   for (i = 0, k = 1; i < hash->hash->hash_len - 1; i += 2, k += 6) { 
379     a = (((hval[i] >> 6) & 3) + check) % 6;
380     b = (hval[i] >> 2) & 15;
381     c = ((hval[i] & 3) + (check / 6)) % 6;
382     d = (hval[i + 1] >> 4) & 15;
383     e = hval[i + 1] & 15;
384     
385     check = ((check * 5) + (hval[i] * 7) + hval[i + 1]) % 36;
386     
387     babbleprint[k + 0] = vo[a];
388     babbleprint[k + 1] = co[b];
389     babbleprint[k + 2] = vo[c];
390     babbleprint[k + 3] = co[d];
391     babbleprint[k + 4] = '-';
392     babbleprint[k + 5] = co[e];
393   }
394
395   if ((hash->hash->hash_len % 2) != 0) {
396     a = (((hval[i] >> 6) & 3) + check) % 6;
397     b = (hval[i] >> 2) & 15;
398     c = ((hval[i] & 3) + (check / 6)) % 6;
399     babbleprint[k + 0] = vo[a];
400     babbleprint[k + 1] = co[b];
401     babbleprint[k + 2] = vo[c];
402   } else { 
403     a = check % 6;
404     b = 16;
405     c = check / 6;
406     babbleprint[k + 0] = vo[a];
407     babbleprint[k + 1] = co[b];
408     babbleprint[k + 2] = vo[c];
409   }
410   babbleprint[k + 3] = co[16];
411
412   if (new_hash != NULL)
413     silc_hash_free(new_hash);
414   return babbleprint;
415 }