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