From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ismael Luceno <iluceno@suse.de>
Date: Tue, 18 Jun 2024 13:22:52 +0200
Subject: [PATCH] Import hastable implementation

Imported from upstream commit 175655b771fd ("Fix case insensitive matching
in isc_ht hash table implementation", 2024-02-11).

This also pulls in hash32 wrappers, and following headers:
- isc/ascii.h
- isc/endian.h
- isc/siphash.h

Needed by the patch for CVE-2023-4408.

Signed-off-by: Ismael Luceno <iluceno@suse.de>
---
 lib/isc/Makefile.in           |    4 
 lib/isc/hash.c                |   54 +++
 lib/isc/ht.c                  |  577 ++++++++++++++++++++++++++++++++++++++++++
 lib/isc/include/isc/ascii.h   |  188 +++++++++++++
 lib/isc/include/isc/endian.h  |  264 +++++++++++++++++++
 lib/isc/include/isc/hash.h    |   21 +
 lib/isc/include/isc/siphash.h |  442 ++++++++++++++++++++++++++++++++
 7 files changed, 1548 insertions(+), 2 deletions(-)

Index: b/lib/isc/Makefile.in
===================================================================
--- a/lib/isc/Makefile.in
+++ b/lib/isc/Makefile.in
@@ -53,7 +53,7 @@ OBJS =		@ISC_EXTRA_OBJS@ \
 		bitstring.@O@ buffer.@O@ bufferlist.@O@ commandline.@O@ \
 		counter.@O@ error.@O@ event.@O@ \
 		hash.@O@ heap.@O@ hex.@O@ hmacmd5.@O@ hmacsha.@O@ \
-		httpd.@O@ inet_aton.@O@ iterated_hash.@O@ \
+		ascii.@O@ ht.@O@ httpd.@O@ inet_aton.@O@ iterated_hash.@O@ \
 		lex.@O@ lfsr.@O@ lib.@O@ log.@O@ \
 		md5.@O@ mem.@O@ mutexblock.@O@ \
 		netaddr.@O@ netscope.@O@ pool.@O@ ondestroy.@O@ \
@@ -71,7 +71,7 @@ SRCS =		@ISC_EXTRA_SRCS@ \
 		assertions.c backtrace.c base32.c base64.c bitstring.c \
 		buffer.c bufferlist.c commandline.c counter.c \
 		error.c event.c hash.c ht.c heap.c hex.c hmacmd5.c hmacsha.c \
-		httpd.c inet_aton.c iterated_hash.c \
+		ascii.c ht.c httpd.c inet_aton.c iterated_hash.c \
 		lex.c lfsr.c lib.c log.c \
 		md5.c mem.c mutexblock.c \
 		netaddr.c netscope.c pool.c ondestroy.c \
Index: b/lib/isc/ht.c
===================================================================
--- /dev/null
+++ b/lib/isc/ht.c
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <isc/ascii.h>
+#include <isc/hash.h>
+#include <isc/ht.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/result.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#define STRUCT_FLEX_SIZE(pointer, member, count) \
+        (sizeof(*(pointer)) + sizeof(*(pointer)->member) * (count))
+
+typedef struct isc_ht_node isc_ht_node_t;
+
+#define ISC_HT_MAGIC	 ISC_MAGIC('H', 'T', 'a', 'b')
+#define ISC_HT_VALID(ht) ISC_MAGIC_VALID(ht, ISC_HT_MAGIC)
+
+#define HT_NO_BITS    0
+#define HT_MIN_BITS   1
+#define HT_MAX_BITS   32
+#define HT_OVERCOMMIT 3
+
+#define HT_NEXTTABLE(idx)      ((idx == 0) ? 1 : 0)
+#define TRY_NEXTTABLE(idx, ht) (idx == ht->hindex && rehashing_in_progress(ht))
+
+#define GOLDEN_RATIO_32 0x61C88647
+
+#define HASHSIZE(bits) (UINT64_C(1) << (bits))
+
+struct isc_ht_node {
+	void *value;
+	isc_ht_node_t *next;
+	uint32_t hashval;
+	size_t keysize;
+	unsigned char key[];
+};
+
+struct isc_ht {
+	unsigned int magic;
+	isc_mem_t *mctx;
+	size_t count;
+	bool case_sensitive;
+	size_t size[2];
+	uint8_t hashbits[2];
+	isc_ht_node_t **table[2];
+	uint8_t hindex;
+	uint32_t hiter; /* rehashing iterator */
+};
+
+struct isc_ht_iter {
+	isc_ht_t *ht;
+	size_t i;
+	uint8_t hindex;
+	isc_ht_node_t *cur;
+};
+
+static isc_ht_node_t *
+isc__ht_find(const isc_ht_t *ht, const unsigned char *key,
+	     const uint32_t keysize, const uint32_t hashval, const uint8_t idx);
+static void
+isc__ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+	    const uint32_t hashval, const uint8_t idx, void *value);
+static isc_result_t
+isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+	       const uint32_t hashval, const uint8_t idx);
+
+static uint32_t
+rehash_bits(isc_ht_t *ht, size_t newcount);
+
+static void
+hashtable_new(isc_ht_t *ht, const uint8_t idx, const uint8_t bits);
+static void
+hashtable_free(isc_ht_t *ht, const uint8_t idx);
+static void
+hashtable_rehash(isc_ht_t *ht, uint32_t newbits);
+static void
+hashtable_rehash_one(isc_ht_t *ht);
+static void
+maybe_rehash(isc_ht_t *ht, size_t newcount);
+
+static isc_result_t
+isc__ht_iter_next(isc_ht_iter_t *it);
+
+static bool
+isc__ht_node_match(isc_ht_node_t *node, const uint32_t hashval,
+		   const uint8_t *key, uint32_t keysize, bool case_sensitive) {
+	return (node->hashval == hashval && node->keysize == keysize &&
+		(case_sensitive
+			 ? (memcmp(node->key, key, keysize) == 0)
+			 : (isc_ascii_lowerequal(node->key, key, keysize))));
+}
+
+static uint32_t
+hash_32(uint32_t val, unsigned int bits) {
+	REQUIRE(bits <= HT_MAX_BITS);
+	/* High bits are more random. */
+	return (val * GOLDEN_RATIO_32 >> (32 - bits));
+}
+
+static bool
+rehashing_in_progress(const isc_ht_t *ht) {
+	return (ht->table[HT_NEXTTABLE(ht->hindex)] != NULL);
+}
+
+static bool
+hashtable_is_overcommited(isc_ht_t *ht) {
+	return (ht->count >= (ht->size[ht->hindex] * HT_OVERCOMMIT));
+}
+
+static uint32_t
+rehash_bits(isc_ht_t *ht, size_t newcount) {
+	uint32_t newbits = ht->hashbits[ht->hindex];
+
+	while (newcount >= HASHSIZE(newbits) && newbits <= HT_MAX_BITS) {
+		newbits += 1;
+	}
+
+	return (newbits);
+}
+
+/*
+ * Rebuild the hashtable to reduce the load factor
+ */
+static void
+hashtable_rehash(isc_ht_t *ht, uint32_t newbits) {
+	uint8_t oldindex = ht->hindex;
+	uint32_t oldbits = ht->hashbits[oldindex];
+	uint8_t newindex = HT_NEXTTABLE(oldindex);
+
+	REQUIRE(ht->hashbits[oldindex] >= HT_MIN_BITS);
+	REQUIRE(ht->hashbits[oldindex] <= HT_MAX_BITS);
+	REQUIRE(ht->table[oldindex] != NULL);
+
+	REQUIRE(newbits <= HT_MAX_BITS);
+	REQUIRE(ht->hashbits[newindex] == HT_NO_BITS);
+	REQUIRE(ht->table[newindex] == NULL);
+
+	REQUIRE(newbits > oldbits);
+
+	hashtable_new(ht, newindex, newbits);
+
+	ht->hindex = newindex;
+
+	hashtable_rehash_one(ht);
+}
+
+static void
+hashtable_rehash_one(isc_ht_t *ht) {
+	isc_ht_node_t **newtable = ht->table[ht->hindex];
+	uint32_t oldsize = ht->size[HT_NEXTTABLE(ht->hindex)];
+	isc_ht_node_t **oldtable = ht->table[HT_NEXTTABLE(ht->hindex)];
+	isc_ht_node_t *node = NULL;
+	isc_ht_node_t *nextnode;
+
+	/* Find first non-empty node */
+	while (ht->hiter < oldsize && oldtable[ht->hiter] == NULL) {
+		ht->hiter++;
+	}
+
+	/* Rehashing complete */
+	if (ht->hiter == oldsize) {
+		hashtable_free(ht, HT_NEXTTABLE(ht->hindex));
+		ht->hiter = 0;
+		return;
+	}
+
+	/* Move the first non-empty node from old hashtable to new hashtable */
+	for (node = oldtable[ht->hiter]; node != NULL; node = nextnode) {
+		uint32_t hash = hash_32(node->hashval,
+					ht->hashbits[ht->hindex]);
+		nextnode = node->next;
+		node->next = newtable[hash];
+		newtable[hash] = node;
+	}
+
+	oldtable[ht->hiter] = NULL;
+
+	ht->hiter++;
+}
+
+static void
+maybe_rehash(isc_ht_t *ht, size_t newcount) {
+	uint32_t newbits = rehash_bits(ht, newcount);
+
+	if (ht->hashbits[ht->hindex] < newbits && newbits <= HT_MAX_BITS) {
+		hashtable_rehash(ht, newbits);
+	}
+}
+
+static void
+hashtable_new(isc_ht_t *ht, const uint8_t idx, const uint8_t bits) {
+	REQUIRE(ht->hashbits[idx] == HT_NO_BITS);
+	REQUIRE(ht->table[idx] == NULL);
+	REQUIRE(bits >= HT_MIN_BITS);
+	REQUIRE(bits <= HT_MAX_BITS);
+
+	ht->hashbits[idx] = bits;
+	ht->size[idx] = HASHSIZE(ht->hashbits[idx]);
+
+	size_t ht_raw_size = ht->size[idx] * sizeof(isc_ht_node_t *);
+	ht->table[idx] = isc_mem_get(ht->mctx, ht_raw_size);
+	if (ht->table[idx])
+		memset(ht->table[idx], 0, ht_raw_size);
+}
+
+static void
+hashtable_free(isc_ht_t *ht, const uint8_t idx) {
+	size_t i;
+	for (i = 0; i < ht->size[idx]; i++) {
+		isc_ht_node_t *node = ht->table[idx][i];
+		while (node != NULL) {
+			isc_ht_node_t *next = node->next;
+			ht->count--;
+			isc_mem_put(ht->mctx, node,
+				    sizeof(*node) + node->keysize);
+			node = next;
+		}
+	}
+
+	isc_mem_put(ht->mctx, ht->table[idx],
+		    ht->size[idx] * sizeof(isc_ht_node_t *));
+
+	ht->hashbits[idx] = HT_NO_BITS;
+	ht->table[idx] = NULL;
+}
+
+void
+isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits,
+	    unsigned int options) {
+	isc_ht_t *ht = NULL;
+	bool case_sensitive = ((options & ISC_HT_CASE_INSENSITIVE) == 0);
+
+	REQUIRE(htp != NULL && *htp == NULL);
+	REQUIRE(mctx != NULL);
+	REQUIRE(bits >= 1 && bits <= HT_MAX_BITS);
+
+	ht = isc_mem_get(mctx, sizeof(*ht));
+	*ht = (isc_ht_t){
+		.case_sensitive = case_sensitive,
+	};
+
+	isc_mem_attach(mctx, &ht->mctx);
+
+	hashtable_new(ht, 0, bits);
+
+	ht->magic = ISC_HT_MAGIC;
+
+	*htp = ht;
+}
+
+void
+isc_ht_destroy(isc_ht_t **htp) {
+	isc_ht_t *ht;
+	size_t i;
+
+	REQUIRE(htp != NULL);
+	REQUIRE(ISC_HT_VALID(*htp));
+
+	ht = *htp;
+	*htp = NULL;
+	ht->magic = 0;
+
+	for (i = 0; i <= 1; i++) {
+		if (ht->table[i] != NULL) {
+			hashtable_free(ht, i);
+		}
+	}
+
+	INSIST(ht->count == 0);
+
+	isc_mem_putanddetach(&ht->mctx, ht, sizeof(*ht));
+}
+
+static void
+isc__ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+	    const uint32_t hashval, const uint8_t idx, void *value) {
+	isc_ht_node_t *node;
+	uint32_t hash;
+
+	hash = hash_32(hashval, ht->hashbits[idx]);
+
+	node = isc_mem_get(ht->mctx, STRUCT_FLEX_SIZE(node, key, keysize));
+	*node = (isc_ht_node_t){
+		.keysize = keysize,
+		.hashval = hashval,
+		.next = ht->table[idx][hash],
+		.value = value,
+	};
+
+	memmove(node->key, key, keysize);
+
+	ht->count++;
+	ht->table[idx][hash] = node;
+}
+
+isc_result_t
+isc_ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+	   void *value) {
+	uint32_t hashval;
+
+	REQUIRE(ISC_HT_VALID(ht));
+	REQUIRE(key != NULL && keysize > 0);
+
+	if (rehashing_in_progress(ht)) {
+		/* Rehash in progress */
+		hashtable_rehash_one(ht);
+	} else if (hashtable_is_overcommited(ht)) {
+		/* Rehash requested */
+		maybe_rehash(ht, ht->count);
+	}
+
+	hashval = isc_hash32(key, keysize, ht->case_sensitive);
+
+	if (isc__ht_find(ht, key, keysize, hashval, ht->hindex) != NULL) {
+		return (ISC_R_EXISTS);
+	}
+
+	isc__ht_add(ht, key, keysize, hashval, ht->hindex, value);
+
+	return (ISC_R_SUCCESS);
+}
+
+static isc_ht_node_t *
+isc__ht_find(const isc_ht_t *ht, const unsigned char *key,
+	     const uint32_t keysize, const uint32_t hashval,
+	     const uint8_t idx) {
+	uint32_t hash;
+	uint8_t findex = idx;
+	isc_ht_node_t *node;
+
+nexttable:
+	hash = hash_32(hashval, ht->hashbits[findex]);
+	for (node = ht->table[findex][hash]; node != NULL;
+	     node = node->next)
+	{
+		if (isc__ht_node_match(node, hashval, key, keysize,
+				       ht->case_sensitive))
+		{
+			return (node);
+		}
+	}
+	if (TRY_NEXTTABLE(findex, ht)) {
+		/*
+		 * Rehashing in progress, check the other table
+		 */
+		findex = HT_NEXTTABLE(findex);
+		goto nexttable;
+	}
+
+	return (NULL);
+}
+
+isc_result_t
+isc_ht_find(const isc_ht_t *ht, const unsigned char *key,
+	    const uint32_t keysize, void **valuep) {
+	uint32_t hashval;
+	isc_ht_node_t *node;
+
+	REQUIRE(ISC_HT_VALID(ht));
+	REQUIRE(key != NULL && keysize > 0);
+	REQUIRE(valuep == NULL || *valuep == NULL);
+
+	hashval = isc_hash32(key, keysize, ht->case_sensitive);
+
+	node = isc__ht_find(ht, key, keysize, hashval, ht->hindex);
+	if (node == NULL)
+		return (ISC_R_NOTFOUND);
+
+	if (valuep)
+		*valuep = node->value;
+	return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+	       const uint32_t hashval, const uint8_t idx) {
+	isc_ht_node_t *prev = NULL, *node;
+	uint32_t hash;
+
+	hash = hash_32(hashval, ht->hashbits[idx]);
+
+	for (node = ht->table[idx][hash]; node != NULL;
+	     prev = node, node = node->next)
+	{
+		if (isc__ht_node_match(node, hashval, key, keysize,
+				       ht->case_sensitive))
+		{
+			if (prev == NULL) {
+				ht->table[idx][hash] = node->next;
+			} else {
+				prev->next = node->next;
+			}
+			isc_mem_put(ht->mctx, node,
+				    STRUCT_FLEX_SIZE(node, key, node->keysize));
+			ht->count--;
+
+			return (ISC_R_SUCCESS);
+		}
+	}
+
+	return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+isc_ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize) {
+	uint32_t hashval;
+	uint8_t hindex;
+	isc_result_t result;
+
+	REQUIRE(ISC_HT_VALID(ht));
+	REQUIRE(key != NULL && keysize > 0);
+
+	if (rehashing_in_progress(ht)) {
+		/* Rehash in progress */
+		hashtable_rehash_one(ht);
+	}
+
+	hindex = ht->hindex;
+	hashval = isc_hash32(key, keysize, ht->case_sensitive);
+nexttable:
+	result = isc__ht_delete(ht, key, keysize, hashval, hindex);
+
+	if (result == ISC_R_NOTFOUND && TRY_NEXTTABLE(hindex, ht)) {
+		/*
+		 * Rehashing in progress, check the other table
+		 */
+		hindex = HT_NEXTTABLE(hindex);
+		goto nexttable;
+	}
+
+	return (result);
+}
+
+void
+isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) {
+	isc_ht_iter_t *it;
+
+	REQUIRE(ISC_HT_VALID(ht));
+	REQUIRE(itp != NULL && *itp == NULL);
+
+	it = isc_mem_get(ht->mctx, sizeof(isc_ht_iter_t));
+	*it = (isc_ht_iter_t){
+		.ht = ht,
+		.hindex = ht->hindex,
+	};
+
+	*itp = it;
+}
+
+void
+isc_ht_iter_destroy(isc_ht_iter_t **itp) {
+	isc_ht_iter_t *it;
+	isc_ht_t *ht;
+
+	REQUIRE(itp != NULL && *itp != NULL);
+
+	it = *itp;
+	*itp = NULL;
+	ht = it->ht;
+	isc_mem_put(ht->mctx, it, sizeof(*it));
+}
+
+isc_result_t
+isc_ht_iter_first(isc_ht_iter_t *it) {
+	isc_ht_t *ht;
+
+	REQUIRE(it != NULL);
+
+	ht = it->ht;
+
+	it->hindex = ht->hindex;
+	it->i = 0;
+
+	return (isc__ht_iter_next(it));
+}
+
+static isc_result_t
+isc__ht_iter_next(isc_ht_iter_t *it) {
+	isc_ht_t *ht = it->ht;
+
+	while (it->i < ht->size[it->hindex] &&
+	       ht->table[it->hindex][it->i] == NULL)
+	{
+		it->i++;
+	}
+
+	if (it->i < ht->size[it->hindex]) {
+		it->cur = ht->table[it->hindex][it->i];
+
+		return (ISC_R_SUCCESS);
+	}
+
+	if (TRY_NEXTTABLE(it->hindex, ht)) {
+		it->hindex = HT_NEXTTABLE(it->hindex);
+		it->i = 0;
+		return (isc__ht_iter_next(it));
+	}
+
+	return (ISC_R_NOMORE);
+}
+
+isc_result_t
+isc_ht_iter_next(isc_ht_iter_t *it) {
+	REQUIRE(it != NULL);
+	REQUIRE(it->cur != NULL);
+
+	it->cur = it->cur->next;
+
+	if (it->cur != NULL) {
+		return (ISC_R_SUCCESS);
+	}
+
+	it->i++;
+
+	return (isc__ht_iter_next(it));
+}
+
+isc_result_t
+isc_ht_iter_delcurrent_next(isc_ht_iter_t *it) {
+	isc_result_t result = ISC_R_SUCCESS;
+	isc_ht_node_t *dnode = NULL;
+	uint8_t dindex;
+	isc_ht_t *ht;
+	isc_result_t dresult;
+
+	REQUIRE(it != NULL);
+	REQUIRE(it->cur != NULL);
+
+	ht = it->ht;
+	dnode = it->cur;
+	dindex = it->hindex;
+
+	result = isc_ht_iter_next(it);
+
+	dresult = isc__ht_delete(ht, dnode->key, dnode->keysize, dnode->hashval,
+				 dindex);
+	INSIST(dresult == ISC_R_SUCCESS);
+
+	return (result);
+}
+
+void
+isc_ht_iter_current(isc_ht_iter_t *it, void **valuep) {
+	REQUIRE(it != NULL);
+	REQUIRE(it->cur != NULL);
+	REQUIRE(valuep != NULL && *valuep == NULL);
+
+	*valuep = it->cur->value;
+}
+
+void
+isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key,
+		       size_t *keysize) {
+	REQUIRE(it != NULL);
+	REQUIRE(it->cur != NULL);
+	REQUIRE(key != NULL && *key == NULL);
+
+	*key = it->cur->key;
+	*keysize = it->cur->keysize;
+}
+
+size_t
+isc_ht_count(const isc_ht_t *ht) {
+	REQUIRE(ISC_HT_VALID(ht));
+
+	return (ht->count);
+}
Index: b/lib/isc/ascii.c
===================================================================
--- /dev/null
+++ b/lib/isc/ascii.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <isc/ascii.h>
+
+const uint8_t isc__ascii_tolower[256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+	0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+	0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+	0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
+	0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+	0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
+	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
+	0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+	0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,
+	0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb,
+	0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3,
+	0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,
+	0xfc, 0xfd, 0xfe, 0xff
+};
+
+const uint8_t isc__ascii_toupper[256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+	0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+	0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+	0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53,
+	0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+	0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
+	0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+	0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
+	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
+	0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+	0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,
+	0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb,
+	0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3,
+	0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,
+	0xfc, 0xfd, 0xfe, 0xff
+};
Index: b/lib/isc/include/isc/ascii.h
===================================================================
--- /dev/null
+++ b/lib/isc/include/isc/ascii.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <isc/endian.h>
+
+/*
+ * ASCII case conversion
+ */
+extern const uint8_t isc__ascii_tolower[256];
+extern const uint8_t isc__ascii_toupper[256];
+
+/*
+ * Wrappers so we don't have to cast all over the place like <ctype.h>
+ */
+#define isc_ascii_tolower(c) isc__ascii_tolower[(uint8_t)(c)]
+#define isc_ascii_toupper(c) isc__ascii_toupper[(uint8_t)(c)]
+
+/*
+ * A variant tolower() implementation with no memory accesses,
+ * for use when the compiler is able to autovectorize.
+ */
+static inline uint8_t
+isc__ascii_tolower1(uint8_t c) {
+	return (c + ('a' - 'A') * ('A' <= c && c <= 'Z'));
+}
+
+/*
+ * Copy `len` bytes from `src` to `dst`, converting to lower case.
+ */
+static inline void
+isc_ascii_lowercopy(uint8_t *dst, const uint8_t *src, unsigned int len) {
+	while (len-- > 0) {
+		*dst++ = isc__ascii_tolower1(*src++);
+	}
+}
+
+/*
+ * Convert a string to lower case in place
+ */
+static inline void
+isc_ascii_strtolower(char *str) {
+	isc_ascii_lowercopy((uint8_t *)str, (uint8_t *)str,
+			    (unsigned int)strlen(str));
+}
+
+/*
+ * Convert 8 bytes to lower case, using SWAR tricks (SIMD within a register).
+ * Based on "Hacker's Delight" by Henry S. Warren, "searching for a value in a
+ * given range", p. 95. Eight bytes is wider than many labels in DNS names, so
+ * it does not seem worth dealing with the portability issues of wide vector
+ * registers. If there was a vector string load instruction (analogous to
+ * memove() below) the balance might be different.
+ */
+static inline uint64_t
+isc_ascii_tolower8(uint64_t octets) {
+	/*
+	 * Multiply a single-byte constant by `all_bytes` to replicate
+	 * it to all eight bytes in a word.
+	 */
+	uint64_t all_bytes = 0x0101010101010101;
+	/*
+	 * Clear the top bit of each byte to make space for a per-byte flag.
+	 */
+	uint64_t heptets = octets & (0x7F * all_bytes);
+	/*
+	 * We will need to avoid going wrong if our flag bits were originally
+	 * set, and clear calculation leftovers in our non-flag bits
+	 */
+	uint64_t is_ascii = ~octets & (0x80 * all_bytes);
+	/*
+	 * To compare a heptet to `N`, we can add `0x7F - N` so that carry
+	 * propagation will set the flag when our heptet is greater than `N`
+	 */
+	uint64_t is_gt_Z = heptets + (0x7F - 'Z') * all_bytes;
+	/*
+	 * Add one for greater-than-or-equal comparison
+	 */
+	uint64_t is_ge_A = heptets + (0x80 - 'A') * all_bytes;
+	/*
+	 * Now we have what we need to identify the ascii uppercase bytes
+	 */
+	uint64_t is_upper = (is_ge_A ^ is_gt_Z) & is_ascii;
+	/*
+	 * Move the is_upper flag bits to bit 0x20 (which is 'a' - 'A')
+	 * and use them to adjust each byte as required
+	 */
+	return (octets | (is_upper >> 2));
+}
+
+/*
+ * Same, but 4 bytes at a time, used by isc_halfsiphash24()
+ */
+static inline uint32_t
+isc_ascii_tolower4(uint32_t octets) {
+	uint32_t all_bytes = 0x01010101;
+	uint32_t heptets = octets & (0x7F * all_bytes);
+	uint32_t is_ascii = ~octets & (0x80 * all_bytes);
+	uint32_t is_gt_Z = heptets + (0x7F - 'Z') * all_bytes;
+	uint32_t is_ge_A = heptets + (0x80 - 'A') * all_bytes;
+	uint32_t is_upper = (is_ge_A ^ is_gt_Z) & is_ascii;
+	return (octets | (is_upper >> 2));
+}
+
+/*
+ * Helper function to do an unaligned load of 8 bytes in host byte order
+ */
+static inline uint64_t
+isc__ascii_load8(const uint8_t *ptr) {
+	uint64_t bytes = 0;
+	memmove(&bytes, ptr, sizeof(bytes));
+	return (bytes);
+}
+
+/*
+ * Compare `len` bytes at `a` and `b` for case-insensitive equality
+ */
+static inline bool
+isc_ascii_lowerequal(const uint8_t *a, const uint8_t *b, unsigned int len) {
+	uint64_t a8 = 0, b8 = 0;
+	while (len >= 8) {
+		a8 = isc_ascii_tolower8(isc__ascii_load8(a));
+		b8 = isc_ascii_tolower8(isc__ascii_load8(b));
+		if (a8 != b8) {
+			return (false);
+		}
+		len -= 8;
+		a += 8;
+		b += 8;
+	}
+	while (len-- > 0) {
+		if (isc_ascii_tolower(*a++) != isc_ascii_tolower(*b++)) {
+			return (false);
+		}
+	}
+	return (true);
+}
+
+/*
+ * Compare `len` bytes at `a` and `b` for case-insensitive order.
+ * Unlike the previous functions (which do not need to care about byte
+ * order) here we need to ensure the comparisons are lexicographic,
+ * i.e. they treat the strings as big-endian numbers.
+ */
+static inline int
+isc_ascii_lowercmp(const uint8_t *a, const uint8_t *b, unsigned int len) {
+	uint64_t a8 = 0, b8 = 0;
+	while (len >= 8) {
+		a8 = isc_ascii_tolower8(htobe64(isc__ascii_load8(a)));
+		b8 = isc_ascii_tolower8(htobe64(isc__ascii_load8(b)));
+		if (a8 != b8) {
+			goto ret;
+		}
+		len -= 8;
+		a += 8;
+		b += 8;
+	}
+	while (len-- > 0) {
+		a8 = isc_ascii_tolower(*a++);
+		b8 = isc_ascii_tolower(*b++);
+		if (a8 != b8) {
+			goto ret;
+		}
+	}
+ret:
+	if (a8 < b8) {
+		return (-1);
+	}
+	if (a8 > b8) {
+		return (+1);
+	}
+	return (0);
+}
Index: b/lib/isc/include/isc/endian.h
===================================================================
--- /dev/null
+++ b/lib/isc/include/isc/endian.h
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
+	defined(__OpenBSD__) || defined(__bsdi__)
+
+#include <sys/endian.h>
+
+/*
+ * Recent BSDs should have [bl]e{16,32,64}toh() defined in <sys/endian.h>.
+ * Older ones might not, but these should have the alternatively named
+ * [bl]etoh{16,32,64}() functions defined.
+ */
+#ifndef be16toh
+#define be16toh(x) betoh16(x)
+#define le16toh(x) letoh16(x)
+#define be32toh(x) betoh32(x)
+#define le32toh(x) letoh32(x)
+#define be64toh(x) betoh64(x)
+#define le64toh(x) letoh64(x)
+#endif /* !be16toh */
+
+#elif defined __APPLE__
+
+/*
+ * macOS has its own byte-swapping routines, so use these.
+ */
+
+#include <libkern/OSByteOrder.h>
+
+#define htobe16(x) OSSwapHostToBigInt16(x)
+#define htole16(x) OSSwapHostToLittleInt16(x)
+#define be16toh(x) OSSwapBigToHostInt16(x)
+#define le16toh(x) OSSwapLittleToHostInt16(x)
+
+#define htobe32(x) OSSwapHostToBigInt32(x)
+#define htole32(x) OSSwapHostToLittleInt32(x)
+#define be32toh(x) OSSwapBigToHostInt32(x)
+#define le32toh(x) OSSwapLittleToHostInt32(x)
+
+#define htobe64(x) OSSwapHostToBigInt64(x)
+#define htole64(x) OSSwapHostToLittleInt64(x)
+#define be64toh(x) OSSwapBigToHostInt64(x)
+#define le64toh(x) OSSwapLittleToHostInt64(x)
+
+#elif defined(sun) || defined(__sun) || defined(__SVR4)
+
+/*
+ * For Solaris, rely on the fallback definitions below, though use
+ * Solaris-specific versions of bswap_{16,32,64}().
+ */
+
+#include <sys/byteorder.h>
+
+#define bswap_16(x) BSWAP_16(x)
+#define bswap_32(x) BSWAP_32(x)
+#define bswap_64(x) BSWAP_64(x)
+
+#elif defined(__ANDROID__) || defined(__CYGWIN__) || defined(__GNUC__) || \
+	defined(__GNU__)
+
+#include <byteswap.h>
+#include <endian.h>
+
+#else /* if defined(__DragonFly__) || defined(__FreeBSD__) || \
+       * defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) */
+
+#endif /* Specific platform support */
+
+/*
+ * Fallback definitions.
+ */
+
+#include <inttypes.h>
+
+#ifndef bswap_16
+#define bswap_16(x)                                   \
+	((uint16_t)((((uint16_t)(x) & 0xff00) >> 8) | \
+		    (((uint16_t)(x) & 0x00ff) << 8)))
+#endif /* !bswap_16 */
+
+#ifndef bswap_32
+#define bswap_32(x)                                        \
+	((uint32_t)((((uint32_t)(x) & 0xff000000) >> 24) | \
+		    (((uint32_t)(x) & 0x00ff0000) >> 8) |  \
+		    (((uint32_t)(x) & 0x0000ff00) << 8) |  \
+		    (((uint32_t)(x) & 0x000000ff) << 24)))
+#endif /* !bswap_32 */
+
+#ifndef bswap_64
+#define bswap_64(x)                                                   \
+	((uint64_t)((((uint64_t)(x) & 0xff00000000000000ULL) >> 56) | \
+		    (((uint64_t)(x) & 0x00ff000000000000ULL) >> 40) | \
+		    (((uint64_t)(x) & 0x0000ff0000000000ULL) >> 24) | \
+		    (((uint64_t)(x) & 0x000000ff00000000ULL) >> 8) |  \
+		    (((uint64_t)(x) & 0x00000000ff000000ULL) << 8) |  \
+		    (((uint64_t)(x) & 0x0000000000ff0000ULL) << 24) | \
+		    (((uint64_t)(x) & 0x000000000000ff00ULL) << 40) | \
+		    (((uint64_t)(x) & 0x00000000000000ffULL) << 56)))
+#endif /* !bswap_64 */
+
+#ifndef htobe16
+#if WORDS_BIGENDIAN
+
+#define htobe16(x) (x)
+#define htole16(x) bswap_16(x)
+#define be16toh(x) (x)
+#define le16toh(x) bswap_16(x)
+
+#else /* WORDS_BIGENDIAN */
+
+#define htobe16(x) bswap_16(x)
+#define htole16(x) (x)
+#define be16toh(x) bswap_16(x)
+#define le16toh(x) (x)
+
+#endif /* WORDS_BIGENDIAN */
+#endif /* !htobe16 */
+
+#ifndef htobe32
+#if WORDS_BIGENDIAN
+
+#define htobe32(x) (x)
+#define htole32(x) bswap_32(x)
+#define be32toh(x) (x)
+#define le32toh(x) bswap_32(x)
+
+#else /* WORDS_BIGENDIAN */
+
+#define htobe32(x) bswap_32(x)
+#define htole32(x) (x)
+#define be32toh(x) bswap_32(x)
+#define le32toh(x) (x)
+
+#endif /* WORDS_BIGENDIAN */
+#endif /* !htobe32 */
+
+#ifndef htobe64
+#if WORDS_BIGENDIAN
+
+#define htobe64(x) (x)
+#define htole64(x) bswap_64(x)
+#define be64toh(x) (x)
+#define le64toh(x) bswap_64(x)
+
+#else /* WORDS_BIGENDIAN */
+
+#define htobe64(x) bswap_64(x)
+#define htole64(x) (x)
+#define be64toh(x) bswap_64(x)
+#define le64toh(x) (x)
+
+#endif /* WORDS_BIGENDIAN */
+#endif /* !htobe64 */
+
+/*
+ * Macros to convert uint8_t arrays to integers.
+ */
+
+/* Low-Endian */
+
+#define ISC_U16TO8_LE(p, v)      \
+	(p)[0] = (uint8_t)((v)); \
+	(p)[1] = (uint8_t)((v) >> 8);
+
+#define ISC_U8TO16_LE(p) (((uint16_t)((p)[0])) | ((uint16_t)((p)[1]) << 8))
+
+#define ISC_U32TO8_LE(p, v)            \
+	(p)[0] = (uint8_t)((v));       \
+	(p)[1] = (uint8_t)((v) >> 8);  \
+	(p)[2] = (uint8_t)((v) >> 16); \
+	(p)[3] = (uint8_t)((v) >> 24);
+
+#define ISC_U8TO32_LE(p)                                    \
+	(((uint32_t)((p)[0])) | ((uint32_t)((p)[1]) << 8) | \
+	 ((uint32_t)((p)[2]) << 16) | ((uint32_t)((p)[3]) << 24))
+
+#define ISC_U48TO8_LE(p, v)            \
+	(p)[0] = (uint8_t)((v));       \
+	(p)[1] = (uint8_t)((v) >> 8);  \
+	(p)[2] = (uint8_t)((v) >> 16); \
+	(p)[3] = (uint8_t)((v) >> 24); \
+	(p)[4] = (uint8_t)((v) >> 32); \
+	(p)[5] = (uint8_t)((v) >> 40);
+
+#define ISC_U8TO48_LE(p)                                           \
+	(((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) |        \
+	 ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
+	 ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40))
+
+#define ISC_U64TO8_LE(p, v)            \
+	(p)[0] = (uint8_t)((v));       \
+	(p)[1] = (uint8_t)((v) >> 8);  \
+	(p)[2] = (uint8_t)((v) >> 16); \
+	(p)[3] = (uint8_t)((v) >> 24); \
+	(p)[4] = (uint8_t)((v) >> 32); \
+	(p)[5] = (uint8_t)((v) >> 40); \
+	(p)[6] = (uint8_t)((v) >> 48); \
+	(p)[7] = (uint8_t)((v) >> 56);
+
+#define ISC_U8TO64_LE(p)                                           \
+	(((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) |        \
+	 ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
+	 ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
+	 ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
+
+/* Big-Endian */
+
+#define ISC_U16TO8_BE(p, v)           \
+	(p)[0] = (uint8_t)((v) >> 8); \
+	(p)[1] = (uint8_t)((v));
+
+#define ISC_U8TO16_BE(p) (((uint16_t)((p)[0]) << 8) | ((uint16_t)((p)[1])))
+
+#define ISC_U32TO8_BE(p, v)            \
+	(p)[0] = (uint8_t)((v) >> 24); \
+	(p)[1] = (uint8_t)((v) >> 16); \
+	(p)[2] = (uint8_t)((v) >> 8);  \
+	(p)[3] = (uint8_t)((v));
+
+#define ISC_U8TO32_BE(p)                                           \
+	(((uint32_t)((p)[0]) << 24) | ((uint32_t)((p)[1]) << 16) | \
+	 ((uint32_t)((p)[2]) << 8) | ((uint32_t)((p)[3])))
+
+#define ISC_U48TO8_BE(p, v)            \
+	(p)[0] = (uint8_t)((v) >> 40); \
+	(p)[1] = (uint8_t)((v) >> 32); \
+	(p)[2] = (uint8_t)((v) >> 24); \
+	(p)[3] = (uint8_t)((v) >> 16); \
+	(p)[4] = (uint8_t)((v) >> 8);  \
+	(p)[5] = (uint8_t)((v));
+
+#define ISC_U8TO48_BE(p)                                           \
+	(((uint64_t)((p)[0]) << 40) | ((uint64_t)((p)[1]) << 32) | \
+	 ((uint64_t)((p)[2]) << 24) | ((uint64_t)((p)[3]) << 16) | \
+	 ((uint64_t)((p)[4]) << 8) | ((uint64_t)((p)[5])))
+
+#define ISC_U64TO8_BE(p, v)            \
+	(p)[0] = (uint8_t)((v) >> 56); \
+	(p)[1] = (uint8_t)((v) >> 48); \
+	(p)[2] = (uint8_t)((v) >> 40); \
+	(p)[3] = (uint8_t)((v) >> 32); \
+	(p)[4] = (uint8_t)((v) >> 24); \
+	(p)[5] = (uint8_t)((v) >> 16); \
+	(p)[6] = (uint8_t)((v) >> 8);  \
+	(p)[7] = (uint8_t)((v));
+
+#define ISC_U8TO64_BE(p)                                           \
+	(((uint64_t)((p)[0]) << 56) | ((uint64_t)((p)[1]) << 48) | \
+	 ((uint64_t)((p)[2]) << 40) | ((uint64_t)((p)[3]) << 32) | \
+	 ((uint64_t)((p)[4]) << 24) | ((uint64_t)((p)[5]) << 16) | \
+	 ((uint64_t)((p)[6]) << 8) | ((uint64_t)((p)[7])))
Index: b/lib/isc/hash.c
===================================================================
--- a/lib/isc/hash.c
+++ b/lib/isc/hash.c
@@ -66,6 +66,7 @@ if advised of the possibility of such da
 #include <isc/once.h>
 #include <isc/random.h>
 #include <isc/refcount.h>
+#include <isc/siphash.h>
 #include <isc/string.h>
 #include <isc/util.h>
 
@@ -99,6 +100,8 @@ struct isc_hash {
 	hash_random_t	*rndvector; /*%< random vector for universal hashing */
 };
 
+static uint8_t isc_hash_key[16];
+
 static isc_mutex_t createlock;
 static isc_once_t once = ISC_ONCE_INIT;
 static isc_hash_t *hash = NULL;
@@ -287,6 +290,12 @@ isc_hash_init(void) {
 	INSIST(hash != NULL && VALID_HASH(hash));
 
 	isc_hash_ctxinit(hash);
+
+	isc_result_t result;
+	result = isc_entropy_getdata(hash->entropy,
+				     isc_hash_key, sizeof(isc_hash_key),
+				     NULL, 0);
+	INSIST(result == ISC_R_SUCCESS);
 }
 
 void
@@ -403,3 +412,48 @@ isc_hash_calc(const unsigned char *key,
 
 	return (hash_calc(hash, key, keylen, case_sensitive));
 }
+
+
+void
+isc_hash32_init(isc_hash32_t *state) {
+	isc_halfsiphash24_init(state, isc_hash_key);
+}
+
+void
+isc_hash32_hash(isc_hash32_t *state, const void *data,
+		const size_t length, const bool case_sensitive) {
+	REQUIRE(length == 0 || data != NULL);
+
+	isc_halfsiphash24_hash(state, data, length, case_sensitive);
+}
+
+uint32_t
+isc_hash32_finalize(isc_hash32_t *state) {
+	uint32_t hval;
+
+	isc_halfsiphash24_finalize(state, (uint8_t *)&hval);
+
+	return (hval);
+}
+
+void
+isc_hash64_init(isc_hash64_t *state) {
+	isc_siphash24_init(state, isc_hash_key);
+}
+
+void
+isc_hash64_hash(isc_hash64_t *state, const void *data,
+		const size_t length, const bool case_sensitive) {
+	REQUIRE(length == 0 || data != NULL);
+
+	isc_siphash24_hash(state, data, length, case_sensitive);
+}
+
+uint64_t
+isc_hash64_finalize(isc_hash64_t *state) {
+	uint64_t hval;
+
+	isc_siphash24_finalize(state, (uint8_t *)&hval);
+
+	return (hval);
+}
Index: b/lib/isc/include/isc/hash.h
===================================================================
--- a/lib/isc/include/isc/hash.h
+++ b/lib/isc/include/isc/hash.h
@@ -74,6 +74,7 @@
  *** Imports
  ***/
 
+#include <isc/siphash.h>
 #include <isc/types.h>
 
 /***
@@ -180,6 +181,26 @@ isc_hash_calc(const unsigned char *key,
  */
 /*@}*/
 
+
+typedef struct isc_halfsiphash24 isc_hash32_t;
+typedef struct isc_siphash24 isc_hash64_t;
+
+void
+isc_hash32_init(isc_hash32_t *state);
+void
+isc_hash32_hash(isc_hash32_t *state, const void *data,
+		const size_t length, const bool case_sensitive);
+uint32_t
+isc_hash32_finalize(isc_hash32_t *state);
+static inline uint32_t
+isc_hash32(const void *data, const size_t length, const bool case_sensitive) {
+	isc_hash32_t state;
+	isc_hash32_init(&state);
+	isc_hash32_hash(&state, data, length, case_sensitive);
+	return (isc_hash32_finalize(&state));
+}
+
+
 ISC_LANG_ENDDECLS
 
 #endif /* ISC_HASH_H */
Index: b/lib/isc/include/isc/siphash.h
===================================================================
--- /dev/null
+++ b/lib/isc/include/isc/siphash.h
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * The implementation is based on SipHash reference C implementation by
+ *
+ * Copyright (c) 2012-2016 Jean-Philippe Aumasson
+ * <jeanphilippe.aumasson@gmail.com> Copyright (c) 2012-2014 Daniel J. Bernstein
+ * <djb@cr.yp.to>
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.  You should
+ * have received a copy of the CC0 Public Domain Dedication along with this
+ * software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+/*! \file isc/siphash.h */
+
+#pragma once
+
+#include <isc/ascii.h>
+#include <isc/endian.h>
+#include <isc/lang.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+
+#ifndef FALLTHROUGH
+#define FALLTHROUGH do {} while (0) /* FALLTHROUGH */
+#endif
+
+#define ISC_SIPHASH24_KEY_LENGTH 128 / 8
+#define ISC_SIPHASH24_TAG_LENGTH 64 / 8
+
+#define ISC_HALFSIPHASH24_KEY_LENGTH 64 / 8
+#define ISC_HALFSIPHASH24_TAG_LENGTH 32 / 8
+
+#define cROUNDS 2
+#define dROUNDS 4
+
+#define ROTATE64(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
+
+#define HALF_ROUND64(a, b, c, d, s, t) \
+	a += b;                        \
+	c += d;                        \
+	b = ROTATE64(b, s) ^ a;        \
+	d = ROTATE64(d, t) ^ c;        \
+	a = ROTATE64(a, 32);
+
+#define FULL_ROUND64(v0, v1, v2, v3)          \
+	HALF_ROUND64(v0, v1, v2, v3, 13, 16); \
+	HALF_ROUND64(v2, v1, v0, v3, 17, 21);
+
+#define SIPROUND FULL_ROUND64
+
+#define ROTATE32(x, b) (uint32_t)(((x) << (b)) | ((x) >> (32 - (b))))
+
+#define HALF_ROUND32(a, b, c, d, s, t) \
+	a += b;                        \
+	c += d;                        \
+	b = ROTATE32(b, s) ^ a;        \
+	d = ROTATE32(d, t) ^ c;        \
+	a = ROTATE32(a, 16);
+
+#define FULL_ROUND32(v0, v1, v2, v3)        \
+	HALF_ROUND32(v0, v1, v2, v3, 5, 8); \
+	HALF_ROUND32(v2, v1, v0, v3, 13, 7);
+
+#define HALFSIPROUND FULL_ROUND32
+
+#define U8TO32_ONE(case_sensitive, byte) \
+	(uint32_t)(case_sensitive ? byte : isc__ascii_tolower1(byte))
+
+#define U8TO64_ONE(case_sensitive, byte) \
+	(uint64_t)(case_sensitive ? byte : isc__ascii_tolower1(byte))
+
+ISC_LANG_BEGINDECLS
+
+typedef struct isc_siphash24 {
+	uint64_t k0;
+	uint64_t k1;
+
+	uint64_t v0;
+	uint64_t v1;
+	uint64_t v2;
+	uint64_t v3;
+
+	uint64_t b;
+
+	size_t inlen;
+} isc_siphash24_t;
+
+typedef struct isc_halfsiphash24 {
+	uint32_t k0;
+	uint32_t k1;
+
+	uint32_t v0;
+	uint32_t v1;
+	uint32_t v2;
+	uint32_t v3;
+
+	uint32_t b;
+
+	size_t inlen;
+} isc_halfsiphash24_t;
+
+static inline void
+isc_siphash24_init(isc_siphash24_t *state, const uint8_t *k) {
+	REQUIRE(k != NULL);
+
+	uint64_t k0 = ISC_U8TO64_LE(k);
+	uint64_t k1 = ISC_U8TO64_LE(k + 8);
+
+	*state = (isc_siphash24_t){
+		.k0 = k0,
+		.k1 = k1,
+		.v0 = UINT64_C(0x736f6d6570736575) ^ k0,
+		.v1 = UINT64_C(0x646f72616e646f6d) ^ k1,
+		.v2 = UINT64_C(0x6c7967656e657261) ^ k0,
+		.v3 = UINT64_C(0x7465646279746573) ^ k1,
+	};
+}
+
+static inline void
+isc_siphash24_one(isc_siphash24_t *state, const uint64_t m) {
+	state->v3 ^= m;
+
+	size_t i;
+	for (i = 0; i < cROUNDS; ++i) {
+		SIPROUND(state->v0, state->v1, state->v2, state->v3);
+	}
+
+	state->v0 ^= m;
+}
+
+static inline void
+isc_siphash24_hash(isc_siphash24_t *state, const uint8_t *in,
+		   const size_t inlen, const bool case_sensitive) {
+	REQUIRE(inlen == 0 || in != NULL);
+
+	if (in == NULL || inlen == 0) {
+		return;
+	}
+
+	size_t	  len = inlen;
+	const int right = state->inlen & 7;
+
+	switch (right) {
+	case 0:
+		break;
+	case 1:
+		state->b |= U8TO64_ONE(case_sensitive, in[0]) << 8;
+		state->inlen++;
+		++in;
+
+		if (--len == 0) {
+			return;
+		}
+		FALLTHROUGH;
+	case 2:
+		state->b |= U8TO64_ONE(case_sensitive, in[0]) << 16;
+		state->inlen++;
+		++in;
+
+		if (--len == 0) {
+			return;
+		}
+		FALLTHROUGH;
+	case 3:
+		state->b |= U8TO64_ONE(case_sensitive, in[0]) << 24;
+		state->inlen++;
+		++in;
+
+		if (--len == 0) {
+			return;
+		}
+		FALLTHROUGH;
+	case 4:
+		state->b |= U8TO64_ONE(case_sensitive, in[0]) << 32;
+		state->inlen++;
+		++in;
+
+		if (--len == 0) {
+			return;
+		}
+		FALLTHROUGH;
+	case 5:
+		state->b |= U8TO64_ONE(case_sensitive, in[0]) << 40;
+		state->inlen++;
+		++in;
+
+		if (--len == 0) {
+			return;
+		}
+		FALLTHROUGH;
+	case 6:
+		state->b |= U8TO64_ONE(case_sensitive, in[0]) << 48;
+		state->inlen++;
+		++in;
+
+		if (--len == 0) {
+			return;
+		}
+		FALLTHROUGH;
+	case 7:
+		state->b |= U8TO64_ONE(case_sensitive, in[0]) << 56;
+		state->inlen++;
+		++in;
+
+		isc_siphash24_one(state, state->b);
+		state->b = 0; /* consumed */
+
+		if (--len == 0) {
+			return;
+		}
+		break;
+	default:
+		break;
+	}
+
+	const uint8_t *end = in + len - (len % sizeof(uint64_t));
+	const size_t   left = len & 7;
+
+	for (; in != end; in += 8) {
+		uint64_t m = case_sensitive
+				     ? ISC_U8TO64_LE(in)
+				     : isc_ascii_tolower8(ISC_U8TO64_LE(in));
+
+		isc_siphash24_one(state, m);
+	}
+
+	INSIST(state->b == 0);
+	switch (left) {
+	case 7:
+		state->b |= U8TO64_ONE(case_sensitive, in[6]) << 48;
+		FALLTHROUGH;
+	case 6:
+		state->b |= U8TO64_ONE(case_sensitive, in[5]) << 40;
+		FALLTHROUGH;
+	case 5:
+		state->b |= U8TO64_ONE(case_sensitive, in[4]) << 32;
+		FALLTHROUGH;
+	case 4:
+		state->b |= U8TO64_ONE(case_sensitive, in[3]) << 24;
+		FALLTHROUGH;
+	case 3:
+		state->b |= U8TO64_ONE(case_sensitive, in[2]) << 16;
+		FALLTHROUGH;
+	case 2:
+		state->b |= U8TO64_ONE(case_sensitive, in[1]) << 8;
+		FALLTHROUGH;
+	case 1:
+		state->b |= U8TO64_ONE(case_sensitive, in[0]);
+		FALLTHROUGH;
+	case 0:
+		break;
+	default:
+		break;
+	}
+
+	state->inlen += len;
+}
+
+static inline void
+isc_siphash24_finalize(isc_siphash24_t *state, uint8_t *out) {
+	REQUIRE(out != NULL);
+
+	uint64_t b = ((uint64_t)state->inlen) << 56 | state->b;
+
+	isc_siphash24_one(state, b);
+
+	state->v2 ^= 0xff;
+
+	size_t i;
+	for (i = 0; i < dROUNDS; ++i) {
+		SIPROUND(state->v0, state->v1, state->v2, state->v3);
+	}
+
+	b = state->v0 ^ state->v1 ^ state->v2 ^ state->v3;
+
+	ISC_U64TO8_LE(out, b);
+}
+
+static inline void
+isc_siphash24(const uint8_t *key, const uint8_t *in, const size_t inlen,
+	      bool case_sensitive, uint8_t *out) {
+	isc_siphash24_t state;
+	isc_siphash24_init(&state, key);
+	isc_siphash24_hash(&state, in, inlen, case_sensitive);
+	isc_siphash24_finalize(&state, out);
+}
+
+static inline void
+isc_halfsiphash24_init(isc_halfsiphash24_t *state, const uint8_t *k) {
+	REQUIRE(k != NULL);
+
+	uint32_t k0 = ISC_U8TO32_LE(k);
+	uint32_t k1 = ISC_U8TO32_LE(k + 4);
+
+	*state = (isc_halfsiphash24_t){
+		.k0 = k0,
+		.k1 = k1,
+		.v0 = UINT32_C(0x00000000) ^ k0,
+		.v1 = UINT32_C(0x00000000) ^ k1,
+		.v2 = UINT32_C(0x6c796765) ^ k0,
+		.v3 = UINT32_C(0x74656462) ^ k1,
+	};
+}
+
+static inline void
+isc_halfsiphash24_one(isc_halfsiphash24_t *state, const uint32_t m) {
+	state->v3 ^= m;
+
+	size_t i;
+	for (i = 0; i < cROUNDS; ++i) {
+		HALFSIPROUND(state->v0, state->v1, state->v2, state->v3);
+	}
+
+	state->v0 ^= m;
+}
+
+static inline void
+isc_halfsiphash24_hash(isc_halfsiphash24_t *state, const uint8_t *in,
+		       const size_t inlen, const bool case_sensitive) {
+	REQUIRE(inlen == 0 || in != NULL);
+
+	if (in == NULL || inlen == 0) {
+		return;
+	}
+
+	size_t	  len = inlen;
+	const int right = state->inlen & 3;
+
+	switch (right) {
+	case 0:
+		break;
+	case 1:
+		state->b |= U8TO32_ONE(case_sensitive, in[0]) << 8;
+		state->inlen++;
+		++in;
+
+		if (--len == 0) {
+			return;
+		}
+		FALLTHROUGH;
+	case 2:
+		state->b |= U8TO32_ONE(case_sensitive, in[0]) << 16;
+		state->inlen++;
+		++in;
+
+		if (--len == 0) {
+			return;
+		}
+		FALLTHROUGH;
+	case 3:
+		state->b |= U8TO32_ONE(case_sensitive, in[0]) << 24;
+		state->inlen++;
+		++in;
+
+		isc_halfsiphash24_one(state, state->b);
+		state->b = 0; /* consumed */
+
+		if (--len == 0) {
+			return;
+		}
+		break;
+	default:
+		break;
+	}
+
+	const uint8_t *end = in + len - (len % sizeof(uint32_t));
+	const int      left = len & 3;
+
+	for (; in != end; in += 4) {
+		uint32_t m = case_sensitive
+				     ? ISC_U8TO32_LE(in)
+				     : isc_ascii_tolower4(ISC_U8TO32_LE(in));
+
+		isc_halfsiphash24_one(state, m);
+	}
+
+	INSIST(state->b == 0);
+	switch (left) {
+	case 3:
+		state->b |= U8TO32_ONE(case_sensitive, in[2]) << 16;
+		FALLTHROUGH;
+	case 2:
+		state->b |= U8TO32_ONE(case_sensitive, in[1]) << 8;
+		FALLTHROUGH;
+	case 1:
+		state->b |= U8TO32_ONE(case_sensitive, in[0]);
+		FALLTHROUGH;
+	case 0:
+		break;
+	default:
+		break;
+	}
+
+	state->inlen += len;
+}
+
+static inline void
+isc_halfsiphash24_finalize(isc_halfsiphash24_t *state, uint8_t *out) {
+	REQUIRE(out != NULL);
+
+	uint32_t b = ((uint32_t)state->inlen) << 24 | state->b;
+
+	isc_halfsiphash24_one(state, b);
+
+	state->v2 ^= 0xff;
+
+	size_t i;
+	for (i = 0; i < dROUNDS; ++i) {
+		HALFSIPROUND(state->v0, state->v1, state->v2, state->v3);
+	}
+
+	b = state->v1 ^ state->v3;
+	ISC_U32TO8_LE(out, b);
+}
+
+static inline void
+isc_halfsiphash24(const uint8_t *k, const uint8_t *in, const size_t inlen,
+		  bool case_sensitive, uint8_t *out) {
+	isc_halfsiphash24_t state;
+
+	isc_halfsiphash24_init(&state, k);
+	isc_halfsiphash24_hash(&state, in, inlen, case_sensitive);
+	isc_halfsiphash24_finalize(&state, out);
+}
+
+ISC_LANG_ENDDECLS
