1/*	$NetBSD: siphash.c,v 1.1 2024/02/18 20:57:50 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16#include <inttypes.h>
17#include <string.h>
18#include <unistd.h>
19
20#include <isc/endian.h>
21#include <isc/siphash.h>
22#include <isc/util.h>
23
24/*
25 * The implementation is based on SipHash reference C implementation by
26 *
27 * Copyright (c) 2012-2016 Jean-Philippe Aumasson
28 * <jeanphilippe.aumasson@gmail.com> Copyright (c) 2012-2014 Daniel J. Bernstein
29 * <djb@cr.yp.to>
30 *
31 * To the extent possible under law, the author(s) have dedicated all copyright
32 * and related and neighboring rights to this software to the public domain
33 * worldwide. This software is distributed without any warranty.  You should
34 * have received a copy of the CC0 Public Domain Dedication along with this
35 * software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
36 */
37
38#define cROUNDS 2
39#define dROUNDS 4
40
41#define ROTATE64(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
42
43#define HALF_ROUND64(a, b, c, d, s, t) \
44	a += b;                        \
45	c += d;                        \
46	b = ROTATE64(b, s) ^ a;        \
47	d = ROTATE64(d, t) ^ c;        \
48	a = ROTATE64(a, 32);
49
50#define FULL_ROUND64(v0, v1, v2, v3)          \
51	HALF_ROUND64(v0, v1, v2, v3, 13, 16); \
52	HALF_ROUND64(v2, v1, v0, v3, 17, 21);
53
54#define SIPROUND FULL_ROUND64
55
56#define ROTATE32(x, b) (uint32_t)(((x) << (b)) | ((x) >> (32 - (b))))
57
58#define HALF_ROUND32(a, b, c, d, s, t) \
59	a += b;                        \
60	c += d;                        \
61	b = ROTATE32(b, s) ^ a;        \
62	d = ROTATE32(d, t) ^ c;        \
63	a = ROTATE32(a, 16);
64
65#define FULL_ROUND32(v0, v1, v2, v3)        \
66	HALF_ROUND32(v0, v1, v2, v3, 5, 8); \
67	HALF_ROUND32(v2, v1, v0, v3, 13, 7);
68
69#define HALFSIPROUND FULL_ROUND32
70
71#define U32TO8_LE(p, v)                \
72	(p)[0] = (uint8_t)((v));       \
73	(p)[1] = (uint8_t)((v) >> 8);  \
74	(p)[2] = (uint8_t)((v) >> 16); \
75	(p)[3] = (uint8_t)((v) >> 24);
76
77#define U8TO32_LE(p)                                        \
78	(((uint32_t)((p)[0])) | ((uint32_t)((p)[1]) << 8) | \
79	 ((uint32_t)((p)[2]) << 16) | ((uint32_t)((p)[3]) << 24))
80
81#define U64TO8_LE(p, v)                  \
82	U32TO8_LE((p), (uint32_t)((v))); \
83	U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
84
85#define U8TO64_LE(p)                                               \
86	(((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) |        \
87	 ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
88	 ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
89	 ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
90
91void
92isc_siphash24(const uint8_t *k, const uint8_t *in, const size_t inlen,
93	      uint8_t *out) {
94	REQUIRE(k != NULL);
95	REQUIRE(out != NULL);
96	REQUIRE(inlen == 0 || in != NULL);
97
98	uint64_t k0;
99	uint64_t k1;
100
101	memcpy(&k0, k, sizeof(k0));
102	memcpy(&k1, k + sizeof(k0), sizeof(k1));
103
104	k0 = le64toh(k0);
105	k1 = le64toh(k1);
106
107	uint64_t v0 = UINT64_C(0x736f6d6570736575) ^ k0;
108	uint64_t v1 = UINT64_C(0x646f72616e646f6d) ^ k1;
109	uint64_t v2 = UINT64_C(0x6c7967656e657261) ^ k0;
110	uint64_t v3 = UINT64_C(0x7465646279746573) ^ k1;
111
112	uint64_t b = ((uint64_t)inlen) << 56;
113
114	const uint8_t *end = (in == NULL)
115				     ? NULL
116				     : in + inlen - (inlen % sizeof(uint64_t));
117	const size_t left = inlen & 7;
118
119	for (; in != end; in += 8) {
120		uint64_t m = U8TO64_LE(in);
121
122		v3 ^= m;
123
124		for (size_t i = 0; i < cROUNDS; ++i) {
125			SIPROUND(v0, v1, v2, v3);
126		}
127
128		v0 ^= m;
129	}
130
131	switch (left) {
132	case 7:
133		b |= ((uint64_t)in[6]) << 48;
134		FALLTHROUGH;
135	case 6:
136		b |= ((uint64_t)in[5]) << 40;
137		FALLTHROUGH;
138	case 5:
139		b |= ((uint64_t)in[4]) << 32;
140		FALLTHROUGH;
141	case 4:
142		b |= ((uint64_t)in[3]) << 24;
143		FALLTHROUGH;
144	case 3:
145		b |= ((uint64_t)in[2]) << 16;
146		FALLTHROUGH;
147	case 2:
148		b |= ((uint64_t)in[1]) << 8;
149		FALLTHROUGH;
150	case 1:
151		b |= ((uint64_t)in[0]);
152		FALLTHROUGH;
153	case 0:
154		break;
155	default:
156		UNREACHABLE();
157	}
158
159	v3 ^= b;
160
161	for (size_t i = 0; i < cROUNDS; ++i) {
162		SIPROUND(v0, v1, v2, v3);
163	}
164
165	v0 ^= b;
166
167	v2 ^= 0xff;
168
169	for (size_t i = 0; i < dROUNDS; ++i) {
170		SIPROUND(v0, v1, v2, v3);
171	}
172
173	b = v0 ^ v1 ^ v2 ^ v3;
174
175	U64TO8_LE(out, b);
176}
177
178void
179isc_halfsiphash24(const uint8_t *k, const uint8_t *in, const size_t inlen,
180		  uint8_t *out) {
181	REQUIRE(k != NULL);
182	REQUIRE(out != NULL);
183	REQUIRE(inlen == 0 || in != NULL);
184
185	uint32_t k0 = U8TO32_LE(k);
186	uint32_t k1 = U8TO32_LE(k + 4);
187
188	uint32_t v0 = UINT32_C(0x00000000) ^ k0;
189	uint32_t v1 = UINT32_C(0x00000000) ^ k1;
190	uint32_t v2 = UINT32_C(0x6c796765) ^ k0;
191	uint32_t v3 = UINT32_C(0x74656462) ^ k1;
192
193	uint32_t b = ((uint32_t)inlen) << 24;
194
195	const uint8_t *end = (in == NULL)
196				     ? NULL
197				     : in + inlen - (inlen % sizeof(uint32_t));
198	const int left = inlen & 3;
199
200	for (; in != end; in += 4) {
201		uint32_t m = U8TO32_LE(in);
202		v3 ^= m;
203
204		for (size_t i = 0; i < cROUNDS; ++i) {
205			HALFSIPROUND(v0, v1, v2, v3);
206		}
207
208		v0 ^= m;
209	}
210
211	switch (left) {
212	case 3:
213		b |= ((uint32_t)in[2]) << 16;
214		FALLTHROUGH;
215	case 2:
216		b |= ((uint32_t)in[1]) << 8;
217		FALLTHROUGH;
218	case 1:
219		b |= ((uint32_t)in[0]);
220		FALLTHROUGH;
221	case 0:
222		break;
223	default:
224		UNREACHABLE();
225	}
226
227	v3 ^= b;
228
229	for (size_t i = 0; i < cROUNDS; ++i) {
230		HALFSIPROUND(v0, v1, v2, v3);
231	}
232
233	v0 ^= b;
234
235	v2 ^= 0xff;
236
237	for (size_t i = 0; i < dROUNDS; ++i) {
238		HALFSIPROUND(v0, v1, v2, v3);
239	}
240
241	b = v1 ^ v3;
242	U32TO8_LE(out, b);
243}
244