Static analyzer bug fixes
[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   if (list)
363     list[len - 1] = 0;
364
365   return list;
366 }
367
368 /* Creates the hash value and returns it to the return_hash argument. */
369
370 void silc_hash_make(SilcHash hash, const unsigned char *data,
371                     SilcUInt32 len, unsigned char *return_hash)
372 {
373   silc_hash_init(hash);
374   silc_hash_update(hash, data, len);
375   silc_hash_final(hash, return_hash);
376 }
377
378 void silc_hash_init(SilcHash hash)
379 {
380   hash->hash->init(hash->context);
381 }
382
383 void silc_hash_update(SilcHash hash, const unsigned char *data,
384                       SilcUInt32 data_len)
385 {
386   hash->hash->update(hash->context, (unsigned char *)data, data_len);
387 }
388
389 void silc_hash_final(SilcHash hash, unsigned char *return_hash)
390 {
391   hash->hash->final(hash->context, return_hash);
392 }
393
394 void silc_hash_transform(SilcHash hash, SilcUInt32 *state,
395                          const unsigned char *data)
396 {
397   hash->hash->transform(state, data);
398 }
399
400 /* Creates fingerprint of the data. If `hash' is NULL SHA1 is used as
401    default hash function. The returned fingerprint must be freed by the
402    caller. */
403
404 char *silc_hash_fingerprint(SilcHash hash, const unsigned char *data,
405                             SilcUInt32 data_len)
406 {
407   SilcHash new_hash = NULL;
408   unsigned char h[32];
409   char *ret;
410
411   if (!hash) {
412     if (!silc_hash_alloc("sha1", &new_hash))
413       return NULL;
414     hash = new_hash;
415   }
416
417   silc_hash_make(hash, data, data_len, h);
418   ret = silc_fingerprint(h, hash->hash->hash_len);
419
420   if (new_hash != NULL)
421     silc_hash_free(new_hash);
422   return ret;
423 }
424
425 static const char vo[]= "aeiouy";
426 static const char co[]= "bcdfghklmnprstvzx";
427
428 /* Creates a babbleprint (Bubble Babble Encoding, developed by Antti
429    Huima (draft-huima-babble-01.txt)), by first computing real fingerprint
430    using `hash' or if NULL, then using SHA1, and then encoding the
431    fingerprint to the babbleprint. */
432
433 char *silc_hash_babbleprint(SilcHash hash, const unsigned char *data,
434                             SilcUInt32 data_len)
435 {
436   SilcHash new_hash = NULL;
437   char *babbleprint;
438   unsigned char hval[32];
439   unsigned int a, b, c, d, e, check;
440   int i, k, out_len;
441
442   if (!hash) {
443     if (!silc_hash_alloc("sha1", &new_hash))
444       return NULL;
445     hash = new_hash;
446   }
447
448   /* Take fingerprint */
449   silc_hash_make(hash, data, data_len, hval);
450
451   /* Encode babbleprint */
452   out_len = (((hash->hash->hash_len + 1) / 2) + 1) * 6;
453   babbleprint = silc_calloc(out_len, sizeof(*babbleprint));
454   if (!babbleprint) {
455     silc_hash_free(new_hash);
456     return NULL;
457   }
458   babbleprint[0] = co[16];
459
460   check = 1;
461   for (i = 0, k = 1; i < hash->hash->hash_len - 1; i += 2, k += 6) {
462     a = (((hval[i] >> 6) & 3) + check) % 6;
463     b = (hval[i] >> 2) & 15;
464     c = ((hval[i] & 3) + (check / 6)) % 6;
465     d = (hval[i + 1] >> 4) & 15;
466     e = hval[i + 1] & 15;
467
468     check = ((check * 5) + (hval[i] * 7) + hval[i + 1]) % 36;
469
470     babbleprint[k + 0] = vo[a];
471     babbleprint[k + 1] = co[b];
472     babbleprint[k + 2] = vo[c];
473     babbleprint[k + 3] = co[d];
474     babbleprint[k + 4] = '-';
475     babbleprint[k + 5] = co[e];
476   }
477
478   if ((hash->hash->hash_len % 2) != 0) {
479     a = (((hval[i] >> 6) & 3) + check) % 6;
480     b = (hval[i] >> 2) & 15;
481     c = ((hval[i] & 3) + (check / 6)) % 6;
482     babbleprint[k + 0] = vo[a];
483     babbleprint[k + 1] = co[b];
484     babbleprint[k + 2] = vo[c];
485   } else {
486     a = check % 6;
487     b = 16;
488     c = check / 6;
489     babbleprint[k + 0] = vo[a];
490     babbleprint[k + 1] = co[b];
491     babbleprint[k + 2] = vo[c];
492   }
493   babbleprint[k + 3] = co[16];
494
495   if (new_hash != NULL)
496     silc_hash_free(new_hash);
497   return babbleprint;
498 }