1253208Sandre/*-
2253208Sandre * Copyright (c) 2013 Andre Oppermann <andre@FreeBSD.org>
3253208Sandre * All rights reserved.
4253208Sandre *
5253208Sandre * Redistribution and use in source and binary forms, with or without
6253208Sandre * modification, are permitted provided that the following conditions
7253208Sandre * are met:
8253208Sandre * 1. Redistributions of source code must retain the above copyright
9253208Sandre *    notice, this list of conditions and the following disclaimer.
10253208Sandre * 2. Redistributions in binary form must reproduce the above copyright
11253208Sandre *    notice, this list of conditions and the following disclaimer in the
12253208Sandre *    documentation and/or other materials provided with the distribution.
13253208Sandre * 3. The name of the author may not be used to endorse or promote
14253208Sandre *    products derived from this software without specific prior written
15253208Sandre *    permission.
16253208Sandre *
17253208Sandre * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18253208Sandre * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19253208Sandre * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20253208Sandre * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21253208Sandre * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22253208Sandre * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23253208Sandre * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24253208Sandre * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25253208Sandre * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26253208Sandre * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27253208Sandre * SUCH DAMAGE.
28253208Sandre */
29253208Sandre
30253208Sandre/*
31253208Sandre * SipHash is a family of PRFs SipHash-c-d where the integer parameters c and d
32253208Sandre * are the number of compression rounds and the number of finalization rounds.
33253208Sandre * A compression round is identical to a finalization round and this round
34253208Sandre * function is called SipRound.  Given a 128-bit key k and a (possibly empty)
35253208Sandre * byte string m, SipHash-c-d returns a 64-bit value SipHash-c-d(k; m).
36253208Sandre *
37253208Sandre * Implemented from the paper "SipHash: a fast short-input PRF", 2012.09.18,
38253208Sandre * by Jean-Philippe Aumasson and Daniel J. Bernstein,
39253208Sandre * Permanent Document ID b9a943a805fbfc6fde808af9fc0ecdfa
40253208Sandre * https://131002.net/siphash/siphash.pdf
41253208Sandre * https://131002.net/siphash/
42253208Sandre */
43253208Sandre
44253208Sandre#include <sys/cdefs.h>
45253208Sandre__FBSDID("$FreeBSD$");
46253208Sandre
47253208Sandre#include <sys/param.h>
48253208Sandre#include <sys/types.h>
49253208Sandre#include <sys/systm.h>
50253208Sandre#include <sys/libkern.h>
51253208Sandre#include <sys/endian.h>
52253208Sandre
53253208Sandre#include <crypto/siphash/siphash.h>
54253208Sandre
55253208Sandrestatic void	SipRounds(SIPHASH_CTX *ctx, int final);
56253208Sandre
57253208Sandrevoid
58253208SandreSipHash_InitX(SIPHASH_CTX *ctx, int rc, int rf)
59253208Sandre{
60253208Sandre
61253208Sandre	ctx->v[0] = 0x736f6d6570736575ull;
62253208Sandre	ctx->v[1] = 0x646f72616e646f6dull;
63253208Sandre	ctx->v[2] = 0x6c7967656e657261ull;
64253208Sandre	ctx->v[3] = 0x7465646279746573ull;
65253208Sandre	ctx->buf.b64 = 0;
66253208Sandre	ctx->bytes = 0;
67253208Sandre	ctx->buflen = 0;
68253208Sandre	ctx->rounds_compr = rc;
69253208Sandre	ctx->rounds_final = rf;
70253208Sandre	ctx->initialized = 1;
71253208Sandre}
72253208Sandre
73253208Sandrevoid
74253208SandreSipHash_SetKey(SIPHASH_CTX *ctx, const uint8_t key[16])
75253208Sandre{
76253208Sandre	uint64_t k[2];
77253208Sandre
78253208Sandre	KASSERT(ctx->v[0] == 0x736f6d6570736575ull &&
79253208Sandre	    ctx->initialized == 1,
80253208Sandre	    ("%s: context %p not properly initialized", __func__, ctx));
81253208Sandre
82253208Sandre	k[0] = le64dec(&key[0]);
83253208Sandre	k[1] = le64dec(&key[8]);
84253208Sandre
85253208Sandre	ctx->v[0] ^= k[0];
86253208Sandre	ctx->v[1] ^= k[1];
87253208Sandre	ctx->v[2] ^= k[0];
88253208Sandre	ctx->v[3] ^= k[1];
89253208Sandre
90253208Sandre	ctx->initialized = 2;
91253208Sandre}
92253208Sandre
93253208Sandrestatic size_t
94253208SandreSipBuf(SIPHASH_CTX *ctx, const uint8_t **src, size_t len, int final)
95253208Sandre{
96253208Sandre	size_t x = 0;
97253208Sandre
98253208Sandre	KASSERT((!final && len > 0) || (final && len == 0),
99253208Sandre	    ("%s: invalid parameters", __func__));
100253208Sandre
101253208Sandre	if (!final) {
102253208Sandre		x = MIN(len, sizeof(ctx->buf.b64) - ctx->buflen);
103253208Sandre		bcopy(*src, &ctx->buf.b8[ctx->buflen], x);
104253208Sandre		ctx->buflen += x;
105253208Sandre		*src += x;
106253208Sandre	} else
107253208Sandre		ctx->buf.b8[7] = (uint8_t)ctx->bytes;
108253208Sandre
109253208Sandre	if (ctx->buflen == 8 || final) {
110253208Sandre		ctx->v[3] ^= le64toh(ctx->buf.b64);
111253208Sandre		SipRounds(ctx, 0);
112253208Sandre		ctx->v[0] ^= le64toh(ctx->buf.b64);
113253208Sandre		ctx->buf.b64 = 0;
114253208Sandre		ctx->buflen = 0;
115253208Sandre	}
116253208Sandre	return (x);
117253208Sandre}
118253208Sandre
119253208Sandrevoid
120253208SandreSipHash_Update(SIPHASH_CTX *ctx, const void *src, size_t len)
121253208Sandre{
122253214Sandre	uint64_t m;
123253214Sandre	const uint64_t *p;
124253208Sandre	const uint8_t *s;
125253208Sandre	size_t rem;
126253208Sandre
127253208Sandre	KASSERT(ctx->initialized == 2,
128253208Sandre	    ("%s: context %p not properly initialized", __func__, ctx));
129253208Sandre
130253208Sandre	s = src;
131253208Sandre	ctx->bytes += len;
132253208Sandre
133253208Sandre	/*
134253208Sandre	 * Push length smaller than block size into buffer or
135253208Sandre	 * fill up the buffer if there is already something
136253208Sandre	 * in it.
137253208Sandre	 */
138253208Sandre	if (ctx->buflen > 0 || len < 8)
139253208Sandre		len -= SipBuf(ctx, &s, len, 0);
140253208Sandre	if (len == 0)
141253208Sandre		return;
142253208Sandre
143253208Sandre	rem = len & 0x7;
144253208Sandre	len >>= 3;
145253208Sandre
146253208Sandre	/* Optimze for 64bit aligned/unaligned access. */
147253208Sandre	if (((uintptr_t)s & 0x7) == 0) {
148253214Sandre		for (p = (const uint64_t *)s; len > 0; len--, p++) {
149253208Sandre			m = le64toh(*p);
150253208Sandre			ctx->v[3] ^= m;
151253208Sandre			SipRounds(ctx, 0);
152253208Sandre			ctx->v[0] ^= m;
153253208Sandre		}
154253214Sandre		s = (const uint8_t *)p;
155253208Sandre	} else {
156253208Sandre		for (; len > 0; len--, s += 8) {
157253208Sandre			m = le64dec(s);
158253208Sandre			ctx->v[3] ^= m;
159253208Sandre			SipRounds(ctx, 0);
160253208Sandre			ctx->v[0] ^= m;
161253208Sandre		}
162253208Sandre	}
163253208Sandre
164253208Sandre	/* Push remainder into buffer. */
165253208Sandre	if (rem > 0)
166253208Sandre		(void)SipBuf(ctx, &s, rem, 0);
167253208Sandre}
168253208Sandre
169253208Sandrevoid
170253208SandreSipHash_Final(void *dst, SIPHASH_CTX *ctx)
171253208Sandre{
172253208Sandre	uint64_t r;
173253208Sandre
174253208Sandre	KASSERT(ctx->initialized == 2,
175253208Sandre	    ("%s: context %p not properly initialized", __func__, ctx));
176253208Sandre
177253208Sandre	r = SipHash_End(ctx);
178253208Sandre	le64enc(dst, r);
179253208Sandre}
180253208Sandre
181253208Sandreuint64_t
182253208SandreSipHash_End(SIPHASH_CTX *ctx)
183253208Sandre{
184253208Sandre	uint64_t r;
185253208Sandre
186253208Sandre	KASSERT(ctx->initialized == 2,
187253208Sandre	    ("%s: context %p not properly initialized", __func__, ctx));
188253208Sandre
189253208Sandre	SipBuf(ctx, NULL, 0, 1);
190253208Sandre	ctx->v[2] ^= 0xff;
191253208Sandre	SipRounds(ctx, 1);
192253208Sandre	r = (ctx->v[0] ^ ctx->v[1]) ^ (ctx->v[2] ^ ctx->v[3]);
193253208Sandre
194253208Sandre	bzero(ctx, sizeof(*ctx));
195253208Sandre	return (r);
196253208Sandre}
197253208Sandre
198253208Sandreuint64_t
199253208SandreSipHashX(SIPHASH_CTX *ctx, int rc, int rf, const uint8_t key[16],
200253208Sandre    const void *src, size_t len)
201253208Sandre{
202253208Sandre
203253208Sandre	SipHash_InitX(ctx, rc, rf);
204253208Sandre	SipHash_SetKey(ctx, key);
205253208Sandre	SipHash_Update(ctx, src, len);
206253208Sandre
207253208Sandre	return (SipHash_End(ctx));
208253208Sandre}
209253208Sandre
210253208Sandre#define SIP_ROTL(x, b)	(uint64_t)(((x) << (b)) | ( (x) >> (64 - (b))))
211253208Sandre
212253208Sandrestatic void
213253208SandreSipRounds(SIPHASH_CTX *ctx, int final)
214253208Sandre{
215253208Sandre	int rounds;
216253208Sandre
217253208Sandre	if (!final)
218253208Sandre		rounds = ctx->rounds_compr;
219253208Sandre	else
220253208Sandre		rounds = ctx->rounds_final;
221253208Sandre
222253208Sandre	while (rounds--) {
223253208Sandre		ctx->v[0] += ctx->v[1];
224253208Sandre		ctx->v[2] += ctx->v[3];
225253208Sandre		ctx->v[1] = SIP_ROTL(ctx->v[1], 13);
226253208Sandre		ctx->v[3] = SIP_ROTL(ctx->v[3], 16);
227253208Sandre
228253208Sandre		ctx->v[1] ^= ctx->v[0];
229253208Sandre		ctx->v[3] ^= ctx->v[2];
230253208Sandre		ctx->v[0] = SIP_ROTL(ctx->v[0], 32);
231253208Sandre
232253208Sandre		ctx->v[2] += ctx->v[1];
233253208Sandre		ctx->v[0] += ctx->v[3];
234253208Sandre		ctx->v[1] = SIP_ROTL(ctx->v[1], 17);
235253208Sandre		ctx->v[3] = SIP_ROTL(ctx->v[3], 21);
236253208Sandre
237253208Sandre		ctx->v[1] ^= ctx->v[2];
238253208Sandre		ctx->v[3] ^= ctx->v[0];
239253208Sandre		ctx->v[2] = SIP_ROTL(ctx->v[2], 32);
240253208Sandre	}
241253208Sandre}
242253208Sandre
243