5e740df5c9baebe29d528b3997e38ee321dc05f9
[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(const 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(&(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     return TRUE;
164   }
165
166   return FALSE;
167 }
168
169 /* Free's the SilcHash object */
170
171 void silc_hash_free(SilcHash hash)
172 {
173   if (hash) {
174     silc_free(hash->context);
175     silc_free(hash);
176   }
177 }
178
179 /* Returns the length of the hash digest. */
180
181 SilcUInt32 silc_hash_len(SilcHash hash)
182 {
183   return hash->hash->hash_len;
184 }
185
186 /* Returns TRUE if hash algorithm `name' is supported. */
187
188 bool silc_hash_is_supported(const unsigned char *name)
189 {
190 #ifndef SILC_EPOC
191   SilcHashObject *entry;
192
193   if (silc_hash_list) {
194     silc_dlist_start(silc_hash_list);
195     while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
196       if (!strcmp(entry->name, name))
197         return TRUE;
198     }
199   }
200 #else
201   {
202     int i;
203     for (i = 0; silc_default_hash[i].name; i++)
204       if (!strcmp(silc_default_hash[i].name, name))
205         return TRUE;
206   }
207 #endif /* SILC_EPOC */
208   return FALSE;
209 }
210
211 /* Returns comma separated list of supported hash functions. */
212
213 char *silc_hash_get_supported(void)
214 {
215   SilcHashObject *entry;
216   char *list = NULL;
217   int len = 0;
218
219 #ifndef SILC_EPOC
220   if (silc_hash_list) {
221     silc_dlist_start(silc_hash_list);
222     while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
223       len += strlen(entry->name);
224       list = silc_realloc(list, len + 1);
225       
226       memcpy(list + (len - strlen(entry->name)), 
227              entry->name, strlen(entry->name));
228       memcpy(list + len, ",", 1);
229       len++;
230     }
231   }
232 #else
233   {
234     int i;
235     for (i = 0; silc_default_hash[i].name; i++) {
236       entry = (SilcHashObject *)&(silc_default_hash[i]);
237       len += strlen(entry->name);
238       list = silc_realloc(list, len + 1);
239       
240       memcpy(list + (len - strlen(entry->name)), 
241              entry->name, strlen(entry->name));
242       memcpy(list + len, ",", 1);
243       len++;
244     }
245   }
246 #endif /* SILC_EPOC */
247
248   list[len - 1] = 0;
249
250   return list;
251 }
252
253 /* Creates the hash value and returns it to the return_hash argument. */
254
255 void silc_hash_make(SilcHash hash, const unsigned char *data, 
256                     SilcUInt32 len, unsigned char *return_hash)
257 {
258   hash->hash->init(hash->context);
259   hash->hash->update(hash->context, (unsigned char *)data, len);
260   hash->hash->final(hash->context, return_hash);
261 }
262
263 /* Creates fingerprint of the data. If `hash' is NULL SHA1 is used as
264    default hash function. The returned fingerprint must be free's by the
265    caller. */
266
267 char *silc_hash_fingerprint(SilcHash hash, const unsigned char *data,
268                             SilcUInt32 data_len)
269 {
270   SilcHash new_hash = NULL;
271   unsigned char h[32];
272   char *ret;
273
274   if (!hash) {
275     silc_hash_alloc("sha1", &new_hash);
276     hash = new_hash;
277   }
278
279   silc_hash_make(hash, data, data_len, h);
280   ret = silc_fingerprint(h, hash->hash->hash_len);
281
282   if (new_hash != NULL)
283     silc_hash_free(new_hash);
284   return ret;
285 }
286
287 static const char vo[]= "aeiouy";
288 static const char co[]= "bcdfghklmnprstvzx";
289
290 /* Creates a babbleprint (Bubble Babble Encoding, developed by Antti
291    Huima (draft-huima-babble-01.txt)), by first computing real fingerprint
292    using `hash' or if NULL, then using SHA1, and then encoding the
293    fingerprint to the babbleprint. */
294
295 char *silc_hash_babbleprint(SilcHash hash, const unsigned char *data,
296                             SilcUInt32 data_len)
297 {
298   SilcHash new_hash = NULL;
299   char *babbleprint;
300   unsigned char hval[32];
301   unsigned int a, b, c, d, e, check;
302   int i, k, out_len;
303
304   if (!hash) {
305     silc_hash_alloc("sha1", &new_hash);
306     hash = new_hash;
307   }
308
309   /* Take fingerprint */
310   silc_hash_make(hash, data, data_len, hval);
311
312   /* Encode babbleprint */
313   out_len = (((hash->hash->hash_len + 1) / 2) + 1) * 6;
314   babbleprint = silc_calloc(out_len, sizeof(*babbleprint));
315   babbleprint[0] = co[16];
316
317   check = 1;
318   for (i = 0, k = 1; i < hash->hash->hash_len - 1; i += 2, k += 6) { 
319     a = (((hval[i] >> 6) & 3) + check) % 6;
320     b = (hval[i] >> 2) & 15;
321     c = ((hval[i] & 3) + (check / 6)) % 6;
322     d = (hval[i + 1] >> 4) & 15;
323     e = hval[i + 1] & 15;
324     
325     check = ((check * 5) + (hval[i] * 7) + hval[i + 1]) % 36;
326     
327     babbleprint[k + 0] = vo[a];
328     babbleprint[k + 1] = co[b];
329     babbleprint[k + 2] = vo[c];
330     babbleprint[k + 3] = co[d];
331     babbleprint[k + 4] = '-';
332     babbleprint[k + 5] = co[e];
333   }
334
335   if ((hash->hash->hash_len % 2) != 0) {
336     a = (((hval[i] >> 6) & 3) + check) % 6;
337     b = (hval[i] >> 2) & 15;
338     c = ((hval[i] & 3) + (check / 6)) % 6;
339     babbleprint[k + 0] = vo[a];
340     babbleprint[k + 1] = co[b];
341     babbleprint[k + 2] = vo[c];
342   } else { 
343     a = check % 6;
344     b = 16;
345     c = check / 6;
346     babbleprint[k + 0] = vo[a];
347     babbleprint[k + 1] = co[b];
348     babbleprint[k + 2] = vo[c];
349   }
350   babbleprint[k + 3] = co[16];
351
352   if (new_hash != NULL)
353     silc_hash_free(new_hash);
354   return babbleprint;
355 }