1/*	$OpenBSD: siphash.c,v 1.8 2019/01/20 03:53:47 bcook Exp $ */
2
3/*-
4 * Copyright (c) 2013 Andre Oppermann <andre@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote
16 *    products derived from this software without specific prior written
17 *    permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32/*
33 * SipHash is a family of PRFs SipHash-c-d where the integer parameters c and d
34 * are the number of compression rounds and the number of finalization rounds.
35 * A compression round is identical to a finalization round and this round
36 * function is called SipRound.  Given a 128-bit key k and a (possibly empty)
37 * byte string m, SipHash-c-d returns a 64-bit value SipHash-c-d(k; m).
38 *
39 * Implemented from the paper "SipHash: a fast short-input PRF", 2012.09.18,
40 * by Jean-Philippe Aumasson and Daniel J. Bernstein,
41 * Permanent Document ID b9a943a805fbfc6fde808af9fc0ecdfa
42 * https://131002.net/siphash/siphash.pdf
43 * https://131002.net/siphash/
44 */
45
46#include <endian.h>
47#include <stdint.h>
48#include <string.h>
49#include <siphash.h>
50
51static void	SipHash_CRounds(SIPHASH_CTX *, int);
52static void	SipHash_Rounds(SIPHASH_CTX *, int);
53
54void
55SipHash_Init(SIPHASH_CTX *ctx, const SIPHASH_KEY *key)
56{
57	uint64_t k0, k1;
58
59	k0 = le64toh(key->k0);
60	k1 = le64toh(key->k1);
61
62	ctx->v[0] = 0x736f6d6570736575ULL ^ k0;
63	ctx->v[1] = 0x646f72616e646f6dULL ^ k1;
64	ctx->v[2] = 0x6c7967656e657261ULL ^ k0;
65	ctx->v[3] = 0x7465646279746573ULL ^ k1;
66
67	memset(ctx->buf, 0, sizeof(ctx->buf));
68	ctx->bytes = 0;
69}
70DEF_WEAK(SipHash_Init);
71
72void
73SipHash_Update(SIPHASH_CTX *ctx, int rc, int rf, const void *src, size_t len)
74{
75	const uint8_t *ptr = src;
76	size_t left, used;
77
78	if (len == 0)
79		return;
80
81	used = ctx->bytes % sizeof(ctx->buf);
82	ctx->bytes += len;
83
84	if (used > 0) {
85		left = sizeof(ctx->buf) - used;
86
87		if (len >= left) {
88			memcpy(&ctx->buf[used], ptr, left);
89			SipHash_CRounds(ctx, rc);
90			len -= left;
91			ptr += left;
92		} else {
93			memcpy(&ctx->buf[used], ptr, len);
94			return;
95		}
96	}
97
98	while (len >= sizeof(ctx->buf)) {
99		memcpy(ctx->buf, ptr, sizeof(ctx->buf));
100		SipHash_CRounds(ctx, rc);
101		len -= sizeof(ctx->buf);
102		ptr += sizeof(ctx->buf);
103	}
104
105	if (len > 0)
106		memcpy(ctx->buf, ptr, len);
107}
108DEF_WEAK(SipHash_Update);
109
110void
111SipHash_Final(void *dst, SIPHASH_CTX *ctx, int rc, int rf)
112{
113	uint64_t r;
114
115	r = htole64(SipHash_End(ctx, rc, rf));
116	memcpy(dst, &r, sizeof r);
117}
118DEF_WEAK(SipHash_Final);
119
120uint64_t
121SipHash_End(SIPHASH_CTX *ctx, int rc, int rf)
122{
123	uint64_t r;
124	size_t left, used;
125
126	used = ctx->bytes % sizeof(ctx->buf);
127	left = sizeof(ctx->buf) - used;
128	memset(&ctx->buf[used], 0, left - 1);
129	ctx->buf[7] = ctx->bytes;
130
131	SipHash_CRounds(ctx, rc);
132	ctx->v[2] ^= 0xff;
133	SipHash_Rounds(ctx, rf);
134
135	r = (ctx->v[0] ^ ctx->v[1]) ^ (ctx->v[2] ^ ctx->v[3]);
136	explicit_bzero(ctx, sizeof(*ctx));
137	return (r);
138}
139DEF_WEAK(SipHash_End);
140
141uint64_t
142SipHash(const SIPHASH_KEY *key, int rc, int rf, const void *src, size_t len)
143{
144	SIPHASH_CTX ctx;
145
146	SipHash_Init(&ctx, key);
147	SipHash_Update(&ctx, rc, rf, src, len);
148	return (SipHash_End(&ctx, rc, rf));
149}
150DEF_WEAK(SipHash);
151
152#define SIP_ROTL(x, b) ((x) << (b)) | ( (x) >> (64 - (b)))
153
154static void
155SipHash_Rounds(SIPHASH_CTX *ctx, int rounds)
156{
157	while (rounds--) {
158		ctx->v[0] += ctx->v[1];
159		ctx->v[2] += ctx->v[3];
160		ctx->v[1] = SIP_ROTL(ctx->v[1], 13);
161		ctx->v[3] = SIP_ROTL(ctx->v[3], 16);
162
163		ctx->v[1] ^= ctx->v[0];
164		ctx->v[3] ^= ctx->v[2];
165		ctx->v[0] = SIP_ROTL(ctx->v[0], 32);
166
167		ctx->v[2] += ctx->v[1];
168		ctx->v[0] += ctx->v[3];
169		ctx->v[1] = SIP_ROTL(ctx->v[1], 17);
170		ctx->v[3] = SIP_ROTL(ctx->v[3], 21);
171
172		ctx->v[1] ^= ctx->v[2];
173		ctx->v[3] ^= ctx->v[0];
174		ctx->v[2] = SIP_ROTL(ctx->v[2], 32);
175	}
176}
177
178static void
179SipHash_CRounds(SIPHASH_CTX *ctx, int rounds)
180{
181	uint64_t m = le64toh(*(uint64_t *)ctx->buf);
182
183	ctx->v[3] ^= m;
184	SipHash_Rounds(ctx, rounds);
185	ctx->v[0] ^= m;
186}
187