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