updates.
[silc.git] / lib / silccrypt / silchash.c
1 /*
2
3   silchash.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2001 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; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "silcincludes.h"
23
24 #include "md5.h"
25 #include "sha1.h"
26
27 #ifndef SILC_EPOC
28 /* List of dynamically registered hash functions. */
29 SilcDList silc_hash_list = NULL;
30 #endif /* SILC_EPOC */
31
32 /* Default hash functions for silc_hash_register_default(). */
33 const SilcHashObject silc_default_hash[] = 
34 {
35   { "sha1", 20, 64, silc_sha1_init, silc_sha1_update, silc_sha1_final,
36     silc_sha1_transform, silc_sha1_context_len },
37   { "md5", 16, 64, silc_md5_init, silc_md5_update, silc_md5_final,
38     silc_md5_transform, silc_md5_context_len },
39
40   { NULL, 0, 0, NULL, NULL, NULL, NULL, NULL }
41 };
42
43 /* Registers a new hash function into the SILC. This function is used at
44    the initialization of the SILC. */
45
46 bool silc_hash_register(SilcHashObject *hash)
47 {
48 #ifndef SILC_EPOC
49   SilcHashObject *new;
50
51   SILC_LOG_DEBUG(("Registering new hash function `%s'", hash->name));
52
53   /* Check for existing */
54   if (silc_hash_list) {
55     SilcHashObject *entry;
56     silc_dlist_start(silc_hash_list);
57     while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
58       if (!strcmp(entry->name, hash->name))
59         return FALSE;
60     }
61   }
62
63   new = silc_calloc(1, sizeof(*new));
64   new->name = strdup(hash->name);
65   new->hash_len = hash->hash_len;
66   new->block_len = hash->block_len;
67   new->init = hash->init;
68   new->update = hash->update;
69   new->final = hash->final;
70   new->transform = hash->transform;
71   new->context_len = hash->context_len;
72
73   /* Add to list */
74   if (silc_hash_list == NULL)
75     silc_hash_list = silc_dlist_init();
76   silc_dlist_add(silc_hash_list, new);
77
78 #endif /* SILC_EPOC */
79   return TRUE;
80 }
81
82 /* Unregister a hash function from the SILC. */
83
84 bool silc_hash_unregister(SilcHashObject *hash)
85 {
86 #ifndef SILC_EPOC
87   SilcHashObject *entry;
88
89   SILC_LOG_DEBUG(("Unregistering hash function"));
90
91   if (!silc_hash_list)
92     return FALSE;
93
94   silc_dlist_start(silc_hash_list);
95   while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
96     if (hash == SILC_ALL_HASH_FUNCTIONS || entry == hash) {
97       silc_dlist_del(silc_hash_list, entry);
98
99       if (silc_dlist_count(silc_hash_list) == 0) {
100         silc_dlist_uninit(silc_hash_list);
101         silc_hash_list = NULL;
102       }
103
104       return TRUE;
105     }
106   }
107
108 #endif /* SILC_EPOC */
109   return FALSE;
110 }
111
112 /* Function that registers all the default hash funcs (all builtin ones). 
113    The application may use this to register the default hash funcs if
114    specific hash funcs in any specific order is not wanted. */
115
116 bool silc_hash_register_default(void)
117 {
118 #ifndef SILC_EPOC
119   int i;
120
121   for (i = 0; silc_default_hash[i].name; i++)
122     silc_hash_register((SilcHashObject *)&(silc_default_hash[i]));
123
124 #endif /* SILC_EPOC */
125   return TRUE;
126 }
127
128 /* Allocates a new SilcHash object. New object is returned into new_hash
129    argument. */
130
131 bool silc_hash_alloc(const unsigned char *name, SilcHash *new_hash)
132 {
133   SilcHashObject *entry = NULL;
134   
135   SILC_LOG_DEBUG(("Allocating new hash object"));
136
137 #ifndef SILC_EPOC
138   if (silc_hash_list) {
139     silc_dlist_start(silc_hash_list);
140     while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
141       if (!strcmp(entry->name, name))
142         break;
143     }
144   }
145 #else
146   {
147     /* On EPOC which don't have globals we check our constant hash list. */
148     int i;
149     for (i = 0; silc_default_hash[i].name; i++) {
150       if (!strcmp(silc_default_hash[i].name, name)) {
151         entry = (SilcHashObject *)&(silc_default_hash[i]);
152         break;
153       }
154     }
155   }
156 #endif /* SILC_EPOC */
157
158   if (entry) {
159     *new_hash = silc_calloc(1, sizeof(**new_hash));
160     (*new_hash)->hash = entry;
161     (*new_hash)->context = silc_calloc(1, entry->context_len());
162     (*new_hash)->make_hash = silc_hash_make;
163   }
164
165   return FALSE;
166 }
167
168 /* Free's the SilcHash object */
169
170 void silc_hash_free(SilcHash hash)
171 {
172   if (hash) {
173     silc_free(hash->context);
174     silc_free(hash);
175   }
176 }
177
178 /* Returns the length of the hash digest. */
179
180 SilcUInt32 silc_hash_len(SilcHash hash)
181 {
182   return hash->hash->hash_len;
183 }
184
185 /* Returns TRUE if hash algorithm `name' is supported. */
186
187 bool silc_hash_is_supported(const unsigned char *name)
188 {
189 #ifndef SILC_EPOC
190   SilcHashObject *entry;
191
192   if (silc_hash_list) {
193     silc_dlist_start(silc_hash_list);
194     while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
195       if (!strcmp(entry->name, name))
196         return TRUE;
197     }
198   }
199 #else
200   {
201     int i;
202     for (i = 0; silc_default_hash[i].name; i++)
203       if (!strcmp(silc_default_hash[i].name, name))
204         return TRUE;
205   }
206 #endif /* SILC_EPOC */
207   return FALSE;
208 }
209
210 /* Returns comma separated list of supported hash functions. */
211
212 char *silc_hash_get_supported(void)
213 {
214   SilcHashObject *entry;
215   char *list = NULL;
216   int len = 0;
217
218 #ifndef SILC_EPOC
219   if (silc_hash_list) {
220     silc_dlist_start(silc_hash_list);
221     while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
222       len += strlen(entry->name);
223       list = silc_realloc(list, len + 1);
224       
225       memcpy(list + (len - strlen(entry->name)), 
226              entry->name, strlen(entry->name));
227       memcpy(list + len, ",", 1);
228       len++;
229     }
230   }
231 #else
232   {
233     int i;
234     for (i = 0; silc_default_hash[i].name; i++) {
235       entry = (SilcHashObject *)&(silc_default_hash[i]);
236       len += strlen(entry->name);
237       list = silc_realloc(list, len + 1);
238       
239       memcpy(list + (len - strlen(entry->name)), 
240              entry->name, strlen(entry->name));
241       memcpy(list + len, ",", 1);
242       len++;
243     }
244   }
245 #endif /* SILC_EPOC */
246
247   list[len - 1] = 0;
248
249   return list;
250 }
251
252 /* Creates the hash value and returns it to the return_hash argument. */
253
254 void silc_hash_make(SilcHash hash, const unsigned char *data, 
255                     SilcUInt32 len, unsigned char *return_hash)
256 {
257   hash->hash->init(hash->context);
258   hash->hash->update(hash->context, (unsigned char *)data, len);
259   hash->hash->final(hash->context, return_hash);
260 }
261
262 /* Creates fingerprint of the data. If `hash' is NULL SHA1 is used as
263    default hash function. The returned fingerprint must be free's by the
264    caller. */
265
266 char *silc_hash_fingerprint(SilcHash hash, const unsigned char *data,
267                             SilcUInt32 data_len)
268 {
269   SilcHash new_hash = NULL;
270   unsigned char h[32];
271   char *ret;
272
273   if (!hash) {
274     silc_hash_alloc("sha1", &new_hash);
275     hash = new_hash;
276   }
277
278   silc_hash_make(hash, data, data_len, h);
279   ret = silc_fingerprint(h, hash->hash->hash_len);
280
281   if (new_hash != NULL)
282     silc_hash_free(new_hash);
283   return ret;
284 }
285
286 static const char vo[]= "aeiouy";
287 static const char co[]= "bcdfghklmnprstvzx";
288
289 /* Creates a babbleprint (Bubble Babble Encoding, developed by Antti
290    Huima (draft-huima-babble-01.txt)), by first computing real fingerprint
291    using `hash' or if NULL, then using SHA1, and then encoding the
292    fingerprint to the babbleprint. */
293
294 char *silc_hash_babbleprint(SilcHash hash, const unsigned char *data,
295                             SilcUInt32 data_len)
296 {
297   SilcHash new_hash = NULL;
298   char *babbleprint;
299   unsigned char hval[32];
300   unsigned int a, b, c, d, e, check;
301   int i, k, out_len;
302
303   if (!hash) {
304     silc_hash_alloc("sha1", &new_hash);
305     hash = new_hash;
306   }
307
308   /* Take fingerprint */
309   silc_hash_make(hash, data, data_len, hval);
310
311   /* Encode babbleprint */
312   out_len = (((hash->hash->hash_len + 1) / 2) + 1) * 6;
313   babbleprint = silc_calloc(out_len, sizeof(*babbleprint));
314   babbleprint[0] = co[16];
315
316   check = 1;
317   for (i = 0, k = 1; i < hash->hash->hash_len - 1; i += 2, k += 6) { 
318     a = (((hval[i] >> 6) & 3) + check) % 6;
319     b = (hval[i] >> 2) & 15;
320     c = ((hval[i] & 3) + (check / 6)) % 6;
321     d = (hval[i + 1] >> 4) & 15;
322     e = hval[i + 1] & 15;
323     
324     check = ((check * 5) + (hval[i] * 7) + hval[i + 1]) % 36;
325     
326     babbleprint[k + 0] = vo[a];
327     babbleprint[k + 1] = co[b];
328     babbleprint[k + 2] = vo[c];
329     babbleprint[k + 3] = co[d];
330     babbleprint[k + 4] = '-';
331     babbleprint[k + 5] = co[e];
332   }
333
334   if ((hash->hash->hash_len % 2) != 0) {
335     a = (((hval[i] >> 6) & 3) + check) % 6;
336     b = (hval[i] >> 2) & 15;
337     c = ((hval[i] & 3) + (check / 6)) % 6;
338     babbleprint[k + 0] = vo[a];
339     babbleprint[k + 1] = co[b];
340     babbleprint[k + 2] = vo[c];
341   } else { 
342     a = check % 6;
343     b = 16;
344     c = check / 6;
345     babbleprint[k + 0] = vo[a];
346     babbleprint[k + 1] = co[b];
347     babbleprint[k + 2] = vo[c];
348   }
349   babbleprint[k + 3] = co[16];
350
351   if (new_hash != NULL)
352     silc_hash_free(new_hash);
353   return babbleprint;
354 }