xref: /openbsd/lib/libcrypto/lhash/lhash.c (revision 343bd8e2)
1 /* $OpenBSD: lhash.c,v 1.28 2024/07/14 14:32:45 jsing Exp $ */
2 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
3  * All rights reserved.
4  *
5  * This package is an SSL implementation written
6  * by Eric Young (eay@cryptsoft.com).
7  * The implementation was written so as to conform with Netscapes SSL.
8  *
9  * This library is free for commercial and non-commercial use as long as
10  * the following conditions are aheared to.  The following conditions
11  * apply to all code found in this distribution, be it the RC4, RSA,
12  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
13  * included with this distribution is covered by the same copyright terms
14  * except that the holder is Tim Hudson (tjh@cryptsoft.com).
15  *
16  * Copyright remains Eric Young's, and as such any Copyright notices in
17  * the code are not to be removed.
18  * If this package is used in a product, Eric Young should be given attribution
19  * as the author of the parts of the library used.
20  * This can be in the form of a textual message at program startup or
21  * in documentation (online or textual) provided with the package.
22  *
23  * Redistribution and use in source and binary forms, with or without
24  * modification, are permitted provided that the following conditions
25  * are met:
26  * 1. Redistributions of source code must retain the copyright
27  *    notice, this list of conditions and the following disclaimer.
28  * 2. Redistributions in binary form must reproduce the above copyright
29  *    notice, this list of conditions and the following disclaimer in the
30  *    documentation and/or other materials provided with the distribution.
31  * 3. All advertising materials mentioning features or use of this software
32  *    must display the following acknowledgement:
33  *    "This product includes cryptographic software written by
34  *     Eric Young (eay@cryptsoft.com)"
35  *    The word 'cryptographic' can be left out if the rouines from the library
36  *    being used are not cryptographic related :-).
37  * 4. If you include any Windows specific code (or a derivative thereof) from
38  *    the apps directory (application code) you must include an acknowledgement:
39  *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
40  *
41  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
42  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  *
53  * The licence and distribution terms for any publically available version or
54  * derivative of this code cannot be changed.  i.e. this code cannot simply be
55  * copied and put under another distribution licence
56  * [including the GNU Public Licence.]
57  */
58 
59 #include <stdio.h>
60 #include <string.h>
61 #include <stdlib.h>
62 
63 #include <openssl/opensslconf.h>
64 
65 #include <openssl/crypto.h>
66 #include <openssl/lhash.h>
67 
68 #undef MIN_NODES
69 #define MIN_NODES	16
70 #define UP_LOAD		(2*LH_LOAD_MULT) /* load times 256  (default 2) */
71 #define DOWN_LOAD	(LH_LOAD_MULT)   /* load times 256  (default 1) */
72 
73 typedef struct lhash_node_st {
74 	void *data;
75 	struct lhash_node_st *next;
76 #ifndef OPENSSL_NO_HASH_COMP
77 	unsigned long hash;
78 #endif
79 } LHASH_NODE;
80 
81 struct lhash_st {
82 	LHASH_NODE **b;
83 	LHASH_COMP_FN_TYPE comp;
84 	LHASH_HASH_FN_TYPE hash;
85 	unsigned int num_nodes;
86 	unsigned int num_alloc_nodes;
87 	unsigned int p;
88 	unsigned int pmax;
89 	unsigned long up_load; /* load times 256 */
90 	unsigned long down_load; /* load times 256 */
91 	unsigned long num_items;
92 
93 	int error;
94 } /* _LHASH */;
95 
96 static void
expand(_LHASH * lh)97 expand(_LHASH *lh)
98 {
99 	LHASH_NODE **n, **n1, **n2, *np;
100 	unsigned int p, i, j;
101 	unsigned long hash, nni;
102 
103 	lh->num_nodes++;
104 	p = (int)lh->p++;
105 	n1 = &(lh->b[p]);
106 	n2 = &(lh->b[p + (int)lh->pmax]);
107 	*n2 = NULL;        /* 27/07/92 - eay - undefined pointer bug */
108 	nni = lh->num_alloc_nodes;
109 
110 	for (np = *n1; np != NULL; ) {
111 #ifndef OPENSSL_NO_HASH_COMP
112 		hash = np->hash;
113 #else
114 		hash = lh->hash(np->data);
115 #endif
116 		if ((hash % nni) != p) { /* move it */
117 			*n1 = (*n1)->next;
118 			np->next= *n2;
119 			*n2 = np;
120 		} else
121 			n1 = &((*n1)->next);
122 		np= *n1;
123 	}
124 
125 	if ((lh->p) >= lh->pmax) {
126 		j = (int)lh->num_alloc_nodes * 2;
127 		n = reallocarray(lh->b, j, sizeof(LHASH_NODE *));
128 		if (n == NULL) {
129 /*			fputs("realloc error in lhash", stderr); */
130 			lh->error++;
131 			lh->p = 0;
132 			return;
133 		}
134 		/* else */
135 		for (i = (int)lh->num_alloc_nodes; i < j; i++)/* 26/02/92 eay */
136 			n[i] = NULL;			  /* 02/03/92 eay */
137 		lh->pmax = lh->num_alloc_nodes;
138 		lh->num_alloc_nodes = j;
139 		lh->p = 0;
140 		lh->b = n;
141 	}
142 }
143 
144 static void
contract(_LHASH * lh)145 contract(_LHASH *lh)
146 {
147 	LHASH_NODE **n, *n1, *np;
148 
149 	np = lh->b[lh->p + lh->pmax - 1];
150 	lh->b[lh->p+lh->pmax - 1] = NULL; /* 24/07-92 - eay - weird but :-( */
151 	if (lh->p == 0) {
152 		n = reallocarray(lh->b, lh->pmax, sizeof(LHASH_NODE *));
153 		if (n == NULL) {
154 /*			fputs("realloc error in lhash", stderr); */
155 			lh->error++;
156 			return;
157 		}
158 		lh->num_alloc_nodes /= 2;
159 		lh->pmax /= 2;
160 		lh->p = lh->pmax - 1;
161 		lh->b = n;
162 	} else
163 		lh->p--;
164 
165 	lh->num_nodes--;
166 
167 	n1 = lh->b[(int)lh->p];
168 	if (n1 == NULL)
169 		lh->b[(int)lh->p] = np;
170 	else {
171 		while (n1->next != NULL)
172 			n1 = n1->next;
173 		n1->next = np;
174 	}
175 }
176 
177 static LHASH_NODE **
getrn(_LHASH * lh,const void * data,unsigned long * rhash)178 getrn(_LHASH *lh, const void *data, unsigned long *rhash)
179 {
180 	LHASH_NODE **ret, *n1;
181 	unsigned long hash, nn;
182 	LHASH_COMP_FN_TYPE cf;
183 
184 	hash = (*(lh->hash))(data);
185 	*rhash = hash;
186 
187 	nn = hash % lh->pmax;
188 	if (nn < lh->p)
189 		nn = hash % lh->num_alloc_nodes;
190 
191 	cf = lh->comp;
192 	ret = &(lh->b[(int)nn]);
193 	for (n1 = *ret; n1 != NULL; n1 = n1->next) {
194 #ifndef OPENSSL_NO_HASH_COMP
195 		if (n1->hash != hash) {
196 			ret = &(n1->next);
197 			continue;
198 		}
199 #endif
200 		if (cf(n1->data, data) == 0)
201 			break;
202 		ret = &(n1->next);
203 	}
204 	return (ret);
205 }
206 
207 _LHASH *
lh_new(LHASH_HASH_FN_TYPE h,LHASH_COMP_FN_TYPE c)208 lh_new(LHASH_HASH_FN_TYPE h, LHASH_COMP_FN_TYPE c)
209 {
210 	_LHASH *ret;
211 
212 	if ((ret = calloc(1, sizeof(_LHASH))) == NULL)
213 		return NULL;
214 	if ((ret->b = calloc(MIN_NODES, sizeof(LHASH_NODE *))) == NULL) {
215 		free(ret);
216 		return NULL;
217 	}
218 	ret->comp = ((c == NULL) ? (LHASH_COMP_FN_TYPE)strcmp : c);
219 	ret->hash = ((h == NULL) ? (LHASH_HASH_FN_TYPE)lh_strhash : h);
220 	ret->num_nodes = MIN_NODES / 2;
221 	ret->num_alloc_nodes = MIN_NODES;
222 	ret->pmax = MIN_NODES / 2;
223 	ret->up_load = UP_LOAD;
224 	ret->down_load = DOWN_LOAD;
225 
226 	return (ret);
227 }
228 LCRYPTO_ALIAS(lh_new);
229 
230 void
lh_free(_LHASH * lh)231 lh_free(_LHASH *lh)
232 {
233 	unsigned int i;
234 	LHASH_NODE *n, *nn;
235 
236 	if (lh == NULL)
237 		return;
238 
239 	for (i = 0; i < lh->num_nodes; i++) {
240 		n = lh->b[i];
241 		while (n != NULL) {
242 			nn = n->next;
243 			free(n);
244 			n = nn;
245 		}
246 	}
247 	free(lh->b);
248 	free(lh);
249 }
250 LCRYPTO_ALIAS(lh_free);
251 
252 int
lh_error(_LHASH * lh)253 lh_error(_LHASH *lh)
254 {
255 	return lh->error;
256 }
257 LCRYPTO_ALIAS(lh_error);
258 
259 void *
lh_insert(_LHASH * lh,void * data)260 lh_insert(_LHASH *lh, void *data)
261 {
262 	unsigned long hash;
263 	LHASH_NODE *nn, **rn;
264 	void *ret;
265 
266 	lh->error = 0;
267 	if (lh->up_load <= (lh->num_items * LH_LOAD_MULT / lh->num_nodes))
268 		expand(lh);
269 
270 	rn = getrn(lh, data, &hash);
271 
272 	if (*rn == NULL) {
273 		if ((nn = malloc(sizeof(LHASH_NODE))) == NULL) {
274 			lh->error++;
275 			return (NULL);
276 		}
277 		nn->data = data;
278 		nn->next = NULL;
279 #ifndef OPENSSL_NO_HASH_COMP
280 		nn->hash = hash;
281 #endif
282 		*rn = nn;
283 		ret = NULL;
284 		lh->num_items++;
285 	}
286 	else /* replace same key */
287 	{
288 		ret = (*rn)->data;
289 		(*rn)->data = data;
290 	}
291 	return (ret);
292 }
293 LCRYPTO_ALIAS(lh_insert);
294 
295 void *
lh_delete(_LHASH * lh,const void * data)296 lh_delete(_LHASH *lh, const void *data)
297 {
298 	unsigned long hash;
299 	LHASH_NODE *nn, **rn;
300 	void *ret;
301 
302 	lh->error = 0;
303 	rn = getrn(lh, data, &hash);
304 
305 	if (*rn == NULL) {
306 		return (NULL);
307 	} else {
308 		nn= *rn;
309 		*rn = nn->next;
310 		ret = nn->data;
311 		free(nn);
312 	}
313 
314 	lh->num_items--;
315 	if ((lh->num_nodes > MIN_NODES) &&
316 	    (lh->down_load >= (lh->num_items * LH_LOAD_MULT / lh->num_nodes)))
317 		contract(lh);
318 
319 	return (ret);
320 }
321 LCRYPTO_ALIAS(lh_delete);
322 
323 void *
lh_retrieve(_LHASH * lh,const void * data)324 lh_retrieve(_LHASH *lh, const void *data)
325 {
326 	unsigned long hash;
327 	LHASH_NODE **rn;
328 	void *ret;
329 
330 	lh->error = 0;
331 	rn = getrn(lh, data, &hash);
332 
333 	if (*rn == NULL) {
334 		return (NULL);
335 	} else {
336 		ret = (*rn)->data;
337 	}
338 	return (ret);
339 }
340 LCRYPTO_ALIAS(lh_retrieve);
341 
342 static void
doall_util_fn(_LHASH * lh,int use_arg,LHASH_DOALL_FN_TYPE func,LHASH_DOALL_ARG_FN_TYPE func_arg,void * arg)343 doall_util_fn(_LHASH *lh, int use_arg, LHASH_DOALL_FN_TYPE func,
344     LHASH_DOALL_ARG_FN_TYPE func_arg, void *arg)
345 {
346 	LHASH_NODE *a, *n;
347 	int down_load;
348 	int i;
349 
350 	if (lh == NULL)
351 		return;
352 
353 	/*
354 	 * Disable contraction of the hash while walking, as some consumers use
355 	 * it to delete hash entries. A better option would be to snapshot the
356 	 * hash, making it insert safe as well.
357 	 */
358 	down_load = lh->down_load;
359 	lh->down_load = 0;
360 
361 	/* reverse the order so we search from 'top to bottom'
362 	 * We were having memory leaks otherwise */
363 	for (i = lh->num_nodes - 1; i >= 0; i--) {
364 		a = lh->b[i];
365 		while (a != NULL) {
366 			/* 28/05/91 - eay - n added so items can be deleted
367 			 * via lh_doall */
368 			/* 22/05/08 - ben - eh? since a is not passed,
369 			 * this should not be needed */
370 			n = a->next;
371 			if (use_arg)
372 				func_arg(a->data, arg);
373 			else
374 				func(a->data);
375 			a = n;
376 		}
377 	}
378 
379 	/* Restore down load factor and trigger contraction. */
380 	lh->down_load = down_load;
381 	if ((lh->num_nodes > MIN_NODES) &&
382 	    (lh->down_load >= (lh->num_items * LH_LOAD_MULT / lh->num_nodes)))
383 		contract(lh);
384 }
385 
386 void
lh_doall(_LHASH * lh,LHASH_DOALL_FN_TYPE func)387 lh_doall(_LHASH *lh, LHASH_DOALL_FN_TYPE func)
388 {
389 	doall_util_fn(lh, 0, func, (LHASH_DOALL_ARG_FN_TYPE)0, NULL);
390 }
391 LCRYPTO_ALIAS(lh_doall);
392 
393 void
lh_doall_arg(_LHASH * lh,LHASH_DOALL_ARG_FN_TYPE func,void * arg)394 lh_doall_arg(_LHASH *lh, LHASH_DOALL_ARG_FN_TYPE func, void *arg)
395 {
396 	doall_util_fn(lh, 1, (LHASH_DOALL_FN_TYPE)0, func, arg);
397 }
398 LCRYPTO_ALIAS(lh_doall_arg);
399 
400 /* The following hash seems to work very well on normal text strings
401  * no collisions on /usr/dict/words and it distributes on %2^n quite
402  * well, not as good as MD5, but still good.
403  */
404 unsigned long
lh_strhash(const char * c)405 lh_strhash(const char *c)
406 {
407 	unsigned long ret = 0;
408 	unsigned long n, v;
409 	unsigned int r;
410 
411 	if (c == NULL || *c == '\0')
412 		return ret;
413 
414 	n = 0x100;
415 	while (*c) {
416 		v = n | *c;
417 		n += 0x100;
418 		if ((r = ((v >> 2) ^ v) & 0x0f) != 0)
419 			ret = (ret << r) | (ret >> (32 - r));
420 		ret &= 0xFFFFFFFFUL;
421 		ret ^= v * v;
422 		c++;
423 	}
424 	return (ret >> 16) ^ ret;
425 }
426 LCRYPTO_ALIAS(lh_strhash);
427 
428 unsigned long
lh_num_items(const _LHASH * lh)429 lh_num_items(const _LHASH *lh)
430 {
431 	return lh ? lh->num_items : 0;
432 }
433 LCRYPTO_ALIAS(lh_num_items);
434