rwhashtab.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. #include <errno.h>
  2. #include <stdint.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include "crypto_entropy.h"
  6. #include "ctassert.h"
  7. #include "sysendian.h"
  8. #include "warnp.h"
  9. #include "crypto.h"
  10. #include "rwhashtab.h"
  11. /*
  12. * We use le64dec to generate a size_t from an array of bytes; make sure
  13. * that this is enough.
  14. */
  15. CTASSERT(sizeof(size_t) <= sizeof(uint64_t));
  16. /**
  17. * Structure used to store RW hash table state.
  18. */
  19. struct rwhashtab_internal {
  20. /* Hash table parameters */
  21. size_t cursize; /* Size of table; must be a power of 2 */
  22. size_t numentries; /* Number of entries, <= 0.75 * cursize */
  23. void ** ht; /* Table of cursize pointers to records */
  24. /* Where to find keys within records */
  25. size_t keyoffset;
  26. size_t keylength;
  27. /* Used for hashing */
  28. uint8_t randbuf[32]; /* Prefix used for hashing */
  29. };
  30. static int rwhashtab_enlarge(RWHASHTAB * H);
  31. static size_t rwhashtab_search(RWHASHTAB * H, const uint8_t * key);
  32. /* Enlarge the table by a factor of 2, and rehash. */
  33. static int
  34. rwhashtab_enlarge(RWHASHTAB * H)
  35. {
  36. void ** htnew, ** htold;
  37. void * rec;
  38. size_t newsize, oldsize;
  39. size_t htposold, htposnew;
  40. /*
  41. * Compute new size, and make sure the new table size in bytes
  42. * doesn't overflow.
  43. */
  44. newsize = H->cursize * 2;
  45. if (newsize > SIZE_MAX / sizeof(void *)) {
  46. errno = ENOMEM;
  47. return (-1);
  48. }
  49. /* Allocate and zero the new space. */
  50. if ((htnew = malloc(newsize * sizeof(void *))) == NULL)
  51. return (-1);
  52. for (htposnew = 0; htposnew < newsize; htposnew++)
  53. htnew[htposnew] = NULL;
  54. /* Attach new space to hash table. */
  55. htold = H->ht;
  56. H->ht = htnew;
  57. oldsize = H->cursize;
  58. H->cursize = newsize;
  59. /* Rehash into new table. */
  60. for (htposold = 0; htposold < oldsize; htposold++) {
  61. rec = htold[htposold];
  62. if (rec != NULL) {
  63. htposnew = rwhashtab_search(H,
  64. (uint8_t *)rec + H->keyoffset);
  65. H->ht[htposnew] = rec;
  66. }
  67. }
  68. /* Free now-unused memory. */
  69. free(htold);
  70. /* Success! */
  71. return (0);
  72. }
  73. /*
  74. * Search for a record within ht[size], using hashing parameters from H.
  75. * Return the position of the record or of the first NULL found.
  76. */
  77. static size_t
  78. rwhashtab_search(RWHASHTAB * H, const uint8_t * key)
  79. {
  80. size_t htpos;
  81. uint8_t hashbuf[32];
  82. /* Compute the hash of the record key. */
  83. if (crypto_hash_data_2(CRYPTO_KEY_HMAC_SHA256, H->randbuf, 32,
  84. key, H->keylength, hashbuf)) {
  85. warn0("Programmer error: "
  86. "SHA256 should never fail");
  87. abort();
  88. }
  89. /* Compute starting hash location. */
  90. htpos = le64dec(hashbuf) & (H->cursize - 1);
  91. /*
  92. * Search. This is not an endless loop since the table isn't
  93. * allowed to be full.
  94. */
  95. do {
  96. /* Is the space empty? */
  97. if (H->ht[htpos] == NULL)
  98. return (htpos);
  99. /* Do we have the right key? */
  100. if (memcmp((uint8_t *)(H->ht[htpos]) + H->keyoffset,
  101. key, H->keylength) == 0)
  102. return (htpos);
  103. /* Move to the next table entry. */
  104. htpos = (htpos + 1) & (H->cursize - 1);
  105. } while (1);
  106. }
  107. /**
  108. * rwhashtab_init(keyoffset, keylength):
  109. * Create an empty hash ${table} for storing records which contain keys of
  110. * length ${keylength} bytes starting at offset ${keyoffset} from the start
  111. * of each record. Return NULL on failure.
  112. */
  113. RWHASHTAB *
  114. rwhashtab_init(size_t keyoffset, size_t keylength)
  115. {
  116. RWHASHTAB * H;
  117. size_t i;
  118. /* Sanity check. */
  119. if (keylength == 0)
  120. return (NULL);
  121. H = malloc(sizeof(*H));
  122. if (H == NULL)
  123. return (NULL);
  124. H->cursize = 4;
  125. H->numentries = 0;
  126. H->keyoffset = keyoffset;
  127. H->keylength = keylength;
  128. /* Get some entropy for the keyed hash function. */
  129. if (crypto_entropy_read(H->randbuf, 32)) {
  130. free(H);
  131. return (NULL);
  132. }
  133. /* Allocate space for pointers to records. */
  134. H->ht = malloc(H->cursize * sizeof(void *));
  135. if (H->ht == NULL) {
  136. free(H);
  137. return (NULL);
  138. }
  139. /* All of the entries are empty. */
  140. for (i = 0; i < H->cursize; i++)
  141. H->ht[i] = NULL;
  142. return (H);
  143. }
  144. /**
  145. * rwhashtab_getsize(table):
  146. * Return the number of entries in the ${table}.
  147. */
  148. size_t
  149. rwhashtab_getsize(RWHASHTAB * H)
  150. {
  151. /* Just extract the value and return it. */
  152. return (H->numentries);
  153. }
  154. /**
  155. * rwhashtab_insert(table, record):
  156. * Insert the provided ${record} into the hash ${table}. Return (-1) on error,
  157. * 0 on success, and 1 if the table already contains a record with the same
  158. * key.
  159. */
  160. int
  161. rwhashtab_insert(RWHASHTAB * H, void * rec)
  162. {
  163. int rc;
  164. size_t htpos;
  165. /*
  166. * Does the table need to be enlarged? Technically we should check
  167. * this after searching to see if the key is already in the table;
  168. * but then we'd need to search again to find the new insertion
  169. * location after enlarging, which would add unnecessary complexity.
  170. * Doing it this way just means that we might enlarge the table one
  171. * insert sooner than necessary.
  172. */
  173. if (H->numentries >= H->cursize - (H->cursize >> 2)) {
  174. rc = rwhashtab_enlarge(H);
  175. if (rc)
  176. return (rc);
  177. }
  178. /*
  179. * Search for the record, to see if it is already present and/or
  180. * where it should be inserted.
  181. */
  182. htpos = rwhashtab_search(H, (uint8_t *)rec + H->keyoffset);
  183. /* Already present? */
  184. if (H->ht[htpos] != NULL)
  185. return (1);
  186. /* Insert the record. */
  187. H->ht[htpos] = rec;
  188. H->numentries += 1;
  189. /* Success! */
  190. return (0);
  191. }
  192. /**
  193. * rwhashtab_read(table, key):
  194. * Return a pointer to the record in the ${table} with the specified ${key}, or
  195. * NULL if no such record exists.
  196. */
  197. void *
  198. rwhashtab_read(RWHASHTAB * H, const uint8_t * key)
  199. {
  200. size_t htpos;
  201. /* Search. */
  202. htpos = rwhashtab_search(H, key);
  203. /* Return the record, or NULL if not present. */
  204. return (H->ht[htpos]);
  205. }
  206. /**
  207. * rwhashtab_foreach(table, func, cookie):
  208. * Call ${func(record, cookie)} for each ${record} in the hash ${table}. Stop
  209. * the iteration early if ${func} returns a non-zero value; return 0 or the
  210. * non-zero value returned by ${func}.
  211. */
  212. int
  213. rwhashtab_foreach(RWHASHTAB * H, int func(void *, void *), void * cookie)
  214. {
  215. size_t htpos;
  216. int rc;
  217. for (htpos = 0; htpos < H->cursize; htpos++) {
  218. if (H->ht[htpos] != NULL) {
  219. rc = func(H->ht[htpos], cookie);
  220. if (rc)
  221. return (rc);
  222. }
  223. }
  224. /* Success! */
  225. return (0);
  226. }
  227. /**
  228. * rwhashtab_free(table):
  229. * Free the hash ${table}.
  230. */
  231. void
  232. rwhashtab_free(RWHASHTAB * H)
  233. {
  234. /* Behave consistently with free(NULL). */
  235. if (H == NULL)
  236. return;
  237. /* Free everything. */
  238. free(H->ht);
  239. free(H);
  240. }