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