8bb7339336e98188d45ec00925a4c02b0527d3f5
[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 /* List of all hash functions in SILC. You can dynamically add new hash
28    functions into the list. At the initialization of SILC this list is 
29    filled with the configured hash functions. */
30 struct SilcHashListStruct {
31   SilcHashObject *hash;
32   struct SilcHashListStruct *next;
33 };
34
35 /* List of dynamically registered hash functions. */
36 struct SilcHashListStruct *silc_hash_list = NULL;
37
38 /* Statically declared list of hash functions. */
39 SilcHashObject silc_hash_builtin_list[] = 
40 {
41   { "md5", 16, 64, silc_md5_init, silc_md5_update, silc_md5_final,
42     silc_md5_transform, silc_md5_context_len },
43   { "sha1", 20, 64, silc_sha1_init, silc_sha1_update, silc_sha1_final,
44     silc_sha1_transform, silc_sha1_context_len },
45
46   { NULL, 0, 0, NULL, NULL, NULL, NULL, NULL }
47 };
48
49 /* Registers a ned hash function into the SILC. This function is used at
50    the initialization of the SILC. */
51
52 int silc_hash_register(SilcHashObject *hash)
53 {
54   struct SilcHashListStruct *new, *h;
55
56   SILC_LOG_DEBUG(("Registering new hash function"));
57
58   new = silc_calloc(1, sizeof(*new));
59   new->hash = silc_calloc(1, sizeof(*new->hash));
60
61   /* Set the pointers */
62   new->hash->name = silc_calloc(1, strlen(hash->name));
63   memcpy(new->hash->name, hash->name, strlen(hash->name));
64   new->hash->hash_len = hash->hash_len;
65   new->hash->block_len = hash->block_len;
66   new->hash->init = hash->init;
67   new->hash->update = hash->update;
68   new->hash->final = hash->final;
69   new->hash->context_len = hash->context_len;
70   new->next = NULL;
71
72   /* Add the new hash function to the list */
73   if (!silc_hash_list) {
74     silc_hash_list = new;
75     return TRUE;
76   }
77
78   h = silc_hash_list;
79   while (h) {
80     if (!h->next) {
81       h->next = new;
82       break;
83     }
84     h = h->next;
85   }
86
87   return TRUE;
88 }
89
90 /* Unregister a hash function from the SILC. */
91
92 int silc_hash_unregister(SilcHashObject *hash)
93 {
94   struct SilcHashListStruct *h, *tmp;
95
96   SILC_LOG_DEBUG(("Unregistering hash function"));
97
98   h = silc_hash_list;
99
100   /* Unregister all hash functions */
101   if (hash == SILC_ALL_HASH_FUNCTIONS) {
102     /* Unregister all ciphers */
103     while (h) {
104       tmp = h->next;
105       silc_free(h->hash->name);
106       silc_free(h);
107       h = tmp;
108     }
109
110     return TRUE;
111   }
112
113   /* Unregister the hash function */
114   if (h->hash == hash) {
115     tmp = h->next;
116     silc_free(h->hash->name);
117     silc_free(h);
118     silc_hash_list = tmp;
119
120     return TRUE;
121   }
122
123   while (h) {
124     if (h->next->hash == hash) {
125       tmp = h->next->next;
126       silc_free(h->hash->name);
127       silc_free(h);
128       h->next = tmp;
129       return TRUE;
130     }
131
132     h = h->next;
133   }
134
135   return FALSE;
136 }
137
138 /* Allocates a new SilcHash object. New object is returned into new_hash
139    argument. */
140
141 int silc_hash_alloc(const unsigned char *name, SilcHash *new_hash)
142 {
143   struct SilcHashListStruct *h;
144   int i;
145   
146   SILC_LOG_DEBUG(("Allocating new hash object"));
147
148   /* Allocate the new object */
149   *new_hash = silc_calloc(1, sizeof(**new_hash));
150
151   if (silc_hash_list) {
152     h = silc_hash_list;
153     while (h) {
154       if (!strcmp(h->hash->name, name))
155         break;
156       h = h->next;
157     }
158
159     if (!h)
160       goto check_builtin;
161
162     /* Set the pointers */
163     (*new_hash)->hash = h->hash;
164     (*new_hash)->context = silc_calloc(1, h->hash->context_len());
165     (*new_hash)->make_hash = silc_hash_make;
166
167     return TRUE;
168   }
169
170  check_builtin:
171   for (i = 0; silc_hash_builtin_list[i].name; i++)
172     if (!strcmp(silc_hash_builtin_list[i].name, name))
173       break;
174
175   if (silc_hash_builtin_list[i].name == NULL) {
176     silc_free(*new_hash);
177     return FALSE;
178   }
179   
180   /* Set the pointers */
181   (*new_hash)->hash = &silc_hash_builtin_list[i];
182   (*new_hash)->context = silc_calloc(1, (*new_hash)->hash->context_len());
183   (*new_hash)->make_hash = silc_hash_make;
184   
185   return TRUE;
186 }
187
188 /* Free's the SilcHash object */
189
190 void silc_hash_free(SilcHash hash)
191 {
192   if (hash) {
193     silc_free(hash->context);
194     silc_free(hash);
195   }
196 }
197
198 /* Returns TRUE if hash algorithm `name' is supported. */
199
200 int silc_hash_is_supported(const unsigned char *name)
201 {
202   struct SilcHashListStruct *h;
203   int i;
204   
205   if (silc_hash_list) {
206     h = silc_hash_list;
207
208     while (h) {
209       if (!strcmp(h->hash->name, name))
210         return TRUE;
211       h = h->next;
212     }
213   }
214
215   for (i = 0; silc_hash_builtin_list[i].name; i++)
216     if (!strcmp(silc_hash_builtin_list[i].name, name))
217       return TRUE;
218
219   return FALSE;
220 }
221
222 /* Returns comma separated list of supported hash functions. */
223
224 char *silc_hash_get_supported()
225 {
226   char *list = NULL;
227   int i, len;
228   struct SilcHashListStruct *h;
229
230   len = 0;
231   if (silc_hash_list) {
232     h = silc_hash_list;
233
234     while (h) {
235       len += strlen(h->hash->name);
236       list = silc_realloc(list, len + 1);
237       
238       memcpy(list + (len - strlen(h->hash->name)), 
239              h->hash->name, strlen(h->hash->name));
240       memcpy(list + len, ",", 1);
241       len++;
242       
243       h = h->next;
244     }
245   }
246
247   for (i = 0; silc_hash_builtin_list[i].name; i++) {
248     len += strlen(silc_hash_builtin_list[i].name);
249     list = silc_realloc(list, len + 1);
250     
251     memcpy(list + (len - strlen(silc_hash_builtin_list[i].name)), 
252            silc_hash_builtin_list[i].name, 
253            strlen(silc_hash_builtin_list[i].name));
254     memcpy(list + len, ",", 1);
255     len++;
256   }
257
258   list[len - 1] = 0;
259
260   return list;
261 }
262
263 /* Creates the hash value and returns it to the return_hash argument. */
264
265 void silc_hash_make(SilcHash hash, const unsigned char *data, 
266                     unsigned int len, unsigned char *return_hash)
267 {
268   hash->hash->init(hash->context);
269   hash->hash->update(hash->context, (unsigned char *)data, len);
270   hash->hash->final(hash->context, return_hash);
271 }
272
273 /* Creates fingerprint of the data. If `hash' is NULL SHA1 is used as
274    default hash function. The returned fingerprint must be free's by the
275    caller. */
276
277 char *silc_hash_fingerprint(SilcHash hash, const unsigned char *data,
278                             unsigned int data_len)
279 {
280   char fingerprint[64], *cp;
281   unsigned char h[32];
282   int i;
283
284   if (!hash)
285     silc_hash_alloc("sha1", &hash);
286
287   silc_hash_make(hash, data, data_len, h);
288   
289   memset(fingerprint, 0, sizeof(fingerprint));
290   cp = fingerprint;
291   for (i = 0; i < hash->hash->hash_len; i++) {
292     snprintf(cp, sizeof(fingerprint), "%02X", h[i]);
293     cp += 2;
294     
295     if ((i + 1) % 2 == 0)
296       snprintf(cp++, sizeof(fingerprint), " ");
297
298     if ((i + 1) % 10 == 0)
299       snprintf(cp++, sizeof(fingerprint), " ");
300   }
301   
302   return strdup(fingerprint);
303 }