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