Added SILC Public Key version 2 support (V= identifier support).
[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);
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 /* Function that registers all the default hash funcs (all builtin ones).
137    The application may use this to register the default hash funcs if
138    specific hash funcs in any specific order is not wanted. */
139
140 SilcBool silc_hash_register_default(void)
141 {
142 #ifndef SILC_SYMBIAN
143   int i;
144
145   for (i = 0; silc_default_hash[i].name; i++)
146     silc_hash_register(&(silc_default_hash[i]));
147
148 #endif /* SILC_SYMBIAN */
149   return TRUE;
150 }
151
152 SilcBool silc_hash_unregister_all(void)
153 {
154 #ifndef SILC_SYMBIAN
155   SilcHashObject *entry;
156
157   if (!silc_hash_list)
158     return FALSE;
159
160   silc_dlist_start(silc_hash_list);
161   while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
162     silc_hash_unregister(entry);
163     if (!silc_hash_list)
164       break;
165   }
166 #endif /* SILC_SYMBIAN */
167   return TRUE;
168 }
169
170 /* Allocates a new SilcHash object. New object is returned into new_hash
171    argument. */
172
173 SilcBool silc_hash_alloc(const unsigned char *name, SilcHash *new_hash)
174 {
175   SilcHashObject *entry = NULL;
176
177   SILC_LOG_DEBUG(("Allocating new hash %s", name));
178
179 #ifndef SILC_SYMBIAN
180   if (silc_hash_list) {
181     silc_dlist_start(silc_hash_list);
182     while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
183       if (!strcmp(entry->name, name))
184         break;
185     }
186   }
187 #else
188   {
189     /* On EPOC which don't have globals we check our constant hash list. */
190     int i;
191     for (i = 0; silc_default_hash[i].name; i++) {
192       if (!strcmp(silc_default_hash[i].name, name)) {
193         entry = (SilcHashObject *)&(silc_default_hash[i]);
194         break;
195       }
196     }
197   }
198 #endif /* SILC_SYMBIAN */
199
200   if (entry) {
201     *new_hash = silc_calloc(1, sizeof(**new_hash));
202     if (!(*new_hash))
203       return FALSE;
204     (*new_hash)->hash = entry;
205     (*new_hash)->context = silc_calloc(1, entry->context_len());
206     if (!(*new_hash)->context) {
207       silc_free(*new_hash);
208       return FALSE;
209     }
210     return TRUE;
211   }
212
213   return FALSE;
214 }
215
216 /* Allocate hash by OID string */
217
218 SilcBool silc_hash_alloc_by_oid(const char *oid, SilcHash *new_hash)
219 {
220   SilcHashObject *entry = NULL;
221
222   SILC_LOG_DEBUG(("Allocating new hash %s", oid));
223
224 #ifndef SILC_SYMBIAN
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 #else
233   {
234     /* On EPOC which don't have globals we check our constant hash list. */
235     int i;
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 #endif /* SILC_SYMBIAN */
244
245   if (entry) {
246     *new_hash = silc_calloc(1, sizeof(**new_hash));
247     if (!(*new_hash))
248       return FALSE;
249     (*new_hash)->hash = entry;
250     (*new_hash)->context = silc_calloc(1, entry->context_len());
251     if (!(*new_hash)->context) {
252       silc_free(*new_hash);
253       return FALSE;
254     }
255     return TRUE;
256   }
257
258   return FALSE;
259 }
260
261 /* Free's the SilcHash object */
262
263 void silc_hash_free(SilcHash hash)
264 {
265   if (hash) {
266     silc_free(hash->context);
267     silc_free(hash);
268   }
269 }
270
271 /* Returns the length of the hash digest. */
272
273 SilcUInt32 silc_hash_len(SilcHash hash)
274 {
275   return hash->hash->hash_len;
276 }
277
278 /* Returns the block lenght of the hash. */
279
280 SilcUInt32 silc_hash_block_len(SilcHash hash)
281 {
282   return hash->hash->block_len;
283 }
284
285 /* Returns the name of the hash function */
286
287 const char *silc_hash_get_name(SilcHash hash)
288 {
289   return hash->hash->name;
290 }
291
292 /* Returns hash OID string */
293
294 const char *silc_hash_get_oid(SilcHash hash)
295 {
296   return hash->hash->oid;
297 }
298
299 /* Returns TRUE if hash algorithm `name' is supported. */
300
301 SilcBool silc_hash_is_supported(const unsigned char *name)
302 {
303 #ifndef SILC_SYMBIAN
304   SilcHashObject *entry;
305
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 #else
314   {
315     int i;
316     for (i = 0; silc_default_hash[i].name; i++)
317       if (!strcmp(silc_default_hash[i].name, name))
318         return TRUE;
319   }
320 #endif /* SILC_SYMBIAN */
321   return FALSE;
322 }
323
324 /* Returns comma separated list of supported hash functions. */
325
326 char *silc_hash_get_supported(void)
327 {
328   SilcHashObject *entry;
329   char *list = NULL;
330   int len = 0;
331
332 #ifndef SILC_SYMBIAN
333   if (silc_hash_list) {
334     silc_dlist_start(silc_hash_list);
335     while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
336       len += strlen(entry->name);
337       list = silc_realloc(list, len + 1);
338
339       memcpy(list + (len - strlen(entry->name)),
340              entry->name, strlen(entry->name));
341       memcpy(list + len, ",", 1);
342       len++;
343     }
344   }
345 #else
346   {
347     int i;
348     for (i = 0; silc_default_hash[i].name; i++) {
349       entry = (SilcHashObject *)&(silc_default_hash[i]);
350       len += strlen(entry->name);
351       list = silc_realloc(list, len + 1);
352
353       memcpy(list + (len - strlen(entry->name)),
354              entry->name, strlen(entry->name));
355       memcpy(list + len, ",", 1);
356       len++;
357     }
358   }
359 #endif /* SILC_SYMBIAN */
360
361   list[len - 1] = 0;
362
363   return list;
364 }
365
366 /* Creates the hash value and returns it to the return_hash argument. */
367
368 void silc_hash_make(SilcHash hash, const unsigned char *data,
369                     SilcUInt32 len, unsigned char *return_hash)
370 {
371   silc_hash_init(hash);
372   silc_hash_update(hash, data, len);
373   silc_hash_final(hash, return_hash);
374 }
375
376 void silc_hash_init(SilcHash hash)
377 {
378   hash->hash->init(hash->context);
379 }
380
381 void silc_hash_update(SilcHash hash, const unsigned char *data,
382                       SilcUInt32 data_len)
383 {
384   hash->hash->update(hash->context, (unsigned char *)data, data_len);
385 }
386
387 void silc_hash_final(SilcHash hash, unsigned char *return_hash)
388 {
389   hash->hash->final(hash->context, return_hash);
390 }
391
392 void silc_hash_transform(SilcHash hash, SilcUInt32 *state,
393                          const unsigned char *data)
394 {
395   hash->hash->transform(state, data);
396 }
397
398 /* Creates fingerprint of the data. If `hash' is NULL SHA1 is used as
399    default hash function. The returned fingerprint must be freed by the
400    caller. */
401
402 char *silc_hash_fingerprint(SilcHash hash, const unsigned char *data,
403                             SilcUInt32 data_len)
404 {
405   SilcHash new_hash = NULL;
406   unsigned char h[32];
407   char *ret;
408
409   if (!hash) {
410     if (!silc_hash_alloc("sha1", &new_hash))
411       return NULL;
412     hash = new_hash;
413   }
414
415   silc_hash_make(hash, data, data_len, h);
416   ret = silc_fingerprint(h, hash->hash->hash_len);
417
418   if (new_hash != NULL)
419     silc_hash_free(new_hash);
420   return ret;
421 }
422
423 static const char vo[]= "aeiouy";
424 static const char co[]= "bcdfghklmnprstvzx";
425
426 /* Creates a babbleprint (Bubble Babble Encoding, developed by Antti
427    Huima (draft-huima-babble-01.txt)), by first computing real fingerprint
428    using `hash' or if NULL, then using SHA1, and then encoding the
429    fingerprint to the babbleprint. */
430
431 char *silc_hash_babbleprint(SilcHash hash, const unsigned char *data,
432                             SilcUInt32 data_len)
433 {
434   SilcHash new_hash = NULL;
435   char *babbleprint;
436   unsigned char hval[32];
437   unsigned int a, b, c, d, e, check;
438   int i, k, out_len;
439
440   if (!hash) {
441     if (!silc_hash_alloc("sha1", &new_hash))
442       return NULL;
443     hash = new_hash;
444   }
445
446   /* Take fingerprint */
447   silc_hash_make(hash, data, data_len, hval);
448
449   /* Encode babbleprint */
450   out_len = (((hash->hash->hash_len + 1) / 2) + 1) * 6;
451   babbleprint = silc_calloc(out_len, sizeof(*babbleprint));
452   if (!babbleprint) {
453     silc_hash_free(new_hash);
454     return NULL;
455   }
456   babbleprint[0] = co[16];
457
458   check = 1;
459   for (i = 0, k = 1; i < hash->hash->hash_len - 1; i += 2, k += 6) {
460     a = (((hval[i] >> 6) & 3) + check) % 6;
461     b = (hval[i] >> 2) & 15;
462     c = ((hval[i] & 3) + (check / 6)) % 6;
463     d = (hval[i + 1] >> 4) & 15;
464     e = hval[i + 1] & 15;
465
466     check = ((check * 5) + (hval[i] * 7) + hval[i + 1]) % 36;
467
468     babbleprint[k + 0] = vo[a];
469     babbleprint[k + 1] = co[b];
470     babbleprint[k + 2] = vo[c];
471     babbleprint[k + 3] = co[d];
472     babbleprint[k + 4] = '-';
473     babbleprint[k + 5] = co[e];
474   }
475
476   if ((hash->hash->hash_len % 2) != 0) {
477     a = (((hval[i] >> 6) & 3) + check) % 6;
478     b = (hval[i] >> 2) & 15;
479     c = ((hval[i] & 3) + (check / 6)) % 6;
480     babbleprint[k + 0] = vo[a];
481     babbleprint[k + 1] = co[b];
482     babbleprint[k + 2] = vo[c];
483   } else {
484     a = check % 6;
485     b = 16;
486     c = check / 6;
487     babbleprint[k + 0] = vo[a];
488     babbleprint[k + 1] = co[b];
489     babbleprint[k + 2] = vo[c];
490   }
491   babbleprint[k + 3] = co[16];
492
493   if (new_hash != NULL)
494     silc_hash_free(new_hash);
495   return babbleprint;
496 }