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