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