xref: /freebsd/sys/crypto/siphash/siphash.c (revision 882a725bdfc71b9fc3b70cbcc230327528f3635e)
16856398eSAndre Oppermann /*-
26856398eSAndre Oppermann  * Copyright (c) 2013 Andre Oppermann <andre@FreeBSD.org>
36856398eSAndre Oppermann  * All rights reserved.
46856398eSAndre Oppermann  *
56856398eSAndre Oppermann  * Redistribution and use in source and binary forms, with or without
66856398eSAndre Oppermann  * modification, are permitted provided that the following conditions
76856398eSAndre Oppermann  * are met:
86856398eSAndre Oppermann  * 1. Redistributions of source code must retain the above copyright
96856398eSAndre Oppermann  *    notice, this list of conditions and the following disclaimer.
106856398eSAndre Oppermann  * 2. Redistributions in binary form must reproduce the above copyright
116856398eSAndre Oppermann  *    notice, this list of conditions and the following disclaimer in the
126856398eSAndre Oppermann  *    documentation and/or other materials provided with the distribution.
136856398eSAndre Oppermann  * 3. The name of the author may not be used to endorse or promote
146856398eSAndre Oppermann  *    products derived from this software without specific prior written
156856398eSAndre Oppermann  *    permission.
166856398eSAndre Oppermann  *
176856398eSAndre Oppermann  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
186856398eSAndre Oppermann  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
196856398eSAndre Oppermann  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
206856398eSAndre Oppermann  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
216856398eSAndre Oppermann  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
226856398eSAndre Oppermann  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
236856398eSAndre Oppermann  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
246856398eSAndre Oppermann  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
256856398eSAndre Oppermann  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
266856398eSAndre Oppermann  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
276856398eSAndre Oppermann  * SUCH DAMAGE.
286856398eSAndre Oppermann  */
296856398eSAndre Oppermann 
306856398eSAndre Oppermann /*
316856398eSAndre Oppermann  * SipHash is a family of PRFs SipHash-c-d where the integer parameters c and d
326856398eSAndre Oppermann  * are the number of compression rounds and the number of finalization rounds.
336856398eSAndre Oppermann  * A compression round is identical to a finalization round and this round
346856398eSAndre Oppermann  * function is called SipRound.  Given a 128-bit key k and a (possibly empty)
356856398eSAndre Oppermann  * byte string m, SipHash-c-d returns a 64-bit value SipHash-c-d(k; m).
366856398eSAndre Oppermann  *
376856398eSAndre Oppermann  * Implemented from the paper "SipHash: a fast short-input PRF", 2012.09.18,
386856398eSAndre Oppermann  * by Jean-Philippe Aumasson and Daniel J. Bernstein,
396856398eSAndre Oppermann  * Permanent Document ID b9a943a805fbfc6fde808af9fc0ecdfa
406856398eSAndre Oppermann  * https://131002.net/siphash/siphash.pdf
416856398eSAndre Oppermann  * https://131002.net/siphash/
426856398eSAndre Oppermann  */
436856398eSAndre Oppermann 
446856398eSAndre Oppermann #include <sys/param.h>
456856398eSAndre Oppermann #include <sys/types.h>
466856398eSAndre Oppermann #include <sys/systm.h>
476856398eSAndre Oppermann #include <sys/libkern.h>
486856398eSAndre Oppermann #include <sys/endian.h>
496856398eSAndre Oppermann 
506856398eSAndre Oppermann #include <crypto/siphash/siphash.h>
516856398eSAndre Oppermann 
526856398eSAndre Oppermann static void	SipRounds(SIPHASH_CTX *ctx, int final);
536856398eSAndre Oppermann 
546856398eSAndre Oppermann void
556856398eSAndre Oppermann SipHash_InitX(SIPHASH_CTX *ctx, int rc, int rf)
566856398eSAndre Oppermann {
576856398eSAndre Oppermann 
586856398eSAndre Oppermann 	ctx->v[0] = 0x736f6d6570736575ull;
596856398eSAndre Oppermann 	ctx->v[1] = 0x646f72616e646f6dull;
606856398eSAndre Oppermann 	ctx->v[2] = 0x6c7967656e657261ull;
616856398eSAndre Oppermann 	ctx->v[3] = 0x7465646279746573ull;
626856398eSAndre Oppermann 	ctx->buf.b64 = 0;
636856398eSAndre Oppermann 	ctx->bytes = 0;
646856398eSAndre Oppermann 	ctx->buflen = 0;
656856398eSAndre Oppermann 	ctx->rounds_compr = rc;
666856398eSAndre Oppermann 	ctx->rounds_final = rf;
676856398eSAndre Oppermann 	ctx->initialized = 1;
686856398eSAndre Oppermann }
696856398eSAndre Oppermann 
706856398eSAndre Oppermann void
71571ebf76SConrad Meyer SipHash_SetKey(SIPHASH_CTX *ctx, const uint8_t key[static SIPHASH_KEY_LENGTH])
726856398eSAndre Oppermann {
736856398eSAndre Oppermann 	uint64_t k[2];
746856398eSAndre Oppermann 
756856398eSAndre Oppermann 	KASSERT(ctx->v[0] == 0x736f6d6570736575ull &&
766856398eSAndre Oppermann 	    ctx->initialized == 1,
776856398eSAndre Oppermann 	    ("%s: context %p not properly initialized", __func__, ctx));
786856398eSAndre Oppermann 
796856398eSAndre Oppermann 	k[0] = le64dec(&key[0]);
806856398eSAndre Oppermann 	k[1] = le64dec(&key[8]);
816856398eSAndre Oppermann 
826856398eSAndre Oppermann 	ctx->v[0] ^= k[0];
836856398eSAndre Oppermann 	ctx->v[1] ^= k[1];
846856398eSAndre Oppermann 	ctx->v[2] ^= k[0];
856856398eSAndre Oppermann 	ctx->v[3] ^= k[1];
866856398eSAndre Oppermann 
876856398eSAndre Oppermann 	ctx->initialized = 2;
886856398eSAndre Oppermann }
896856398eSAndre Oppermann 
906856398eSAndre Oppermann static size_t
916856398eSAndre Oppermann SipBuf(SIPHASH_CTX *ctx, const uint8_t **src, size_t len, int final)
926856398eSAndre Oppermann {
936856398eSAndre Oppermann 	size_t x = 0;
946856398eSAndre Oppermann 
95*882a725bSMark O'Donovan 	/* handle hashing 0 length buffer - needed for test vectors */
96*882a725bSMark O'Donovan 	if (len == 0 && final == 0)
97*882a725bSMark O'Donovan 		return (0);
986856398eSAndre Oppermann 
99*882a725bSMark O'Donovan 	if (final) {
100*882a725bSMark O'Donovan 		KASSERT(len == 0, ("%s: invalid len param", __func__));
101*882a725bSMark O'Donovan 		ctx->buf.b8[7] = (uint8_t)ctx->bytes;
102*882a725bSMark O'Donovan 	} else {
103*882a725bSMark O'Donovan 		KASSERT((len > 0) && src && *src,
104*882a725bSMark O'Donovan 			("%s: invalid parameters", __func__));
1056856398eSAndre Oppermann 		x = MIN(len, sizeof(ctx->buf.b64) - ctx->buflen);
1066856398eSAndre Oppermann 		bcopy(*src, &ctx->buf.b8[ctx->buflen], x);
1076856398eSAndre Oppermann 		ctx->buflen += x;
1086856398eSAndre Oppermann 		*src += x;
109*882a725bSMark O'Donovan 	}
1106856398eSAndre Oppermann 
1116856398eSAndre Oppermann 	if (ctx->buflen == 8 || final) {
1126856398eSAndre Oppermann 		ctx->v[3] ^= le64toh(ctx->buf.b64);
1136856398eSAndre Oppermann 		SipRounds(ctx, 0);
1146856398eSAndre Oppermann 		ctx->v[0] ^= le64toh(ctx->buf.b64);
1156856398eSAndre Oppermann 		ctx->buf.b64 = 0;
1166856398eSAndre Oppermann 		ctx->buflen = 0;
1176856398eSAndre Oppermann 	}
1186856398eSAndre Oppermann 	return (x);
1196856398eSAndre Oppermann }
1206856398eSAndre Oppermann 
1216856398eSAndre Oppermann void
1226856398eSAndre Oppermann SipHash_Update(SIPHASH_CTX *ctx, const void *src, size_t len)
1236856398eSAndre Oppermann {
124bf0354c0SAndre Oppermann 	uint64_t m;
125bf0354c0SAndre Oppermann 	const uint64_t *p;
1266856398eSAndre Oppermann 	const uint8_t *s;
1276856398eSAndre Oppermann 	size_t rem;
1286856398eSAndre Oppermann 
1296856398eSAndre Oppermann 	KASSERT(ctx->initialized == 2,
1306856398eSAndre Oppermann 	    ("%s: context %p not properly initialized", __func__, ctx));
1316856398eSAndre Oppermann 
1326856398eSAndre Oppermann 	s = src;
1336856398eSAndre Oppermann 	ctx->bytes += len;
1346856398eSAndre Oppermann 
1356856398eSAndre Oppermann 	/*
1366856398eSAndre Oppermann 	 * Push length smaller than block size into buffer or
1376856398eSAndre Oppermann 	 * fill up the buffer if there is already something
1386856398eSAndre Oppermann 	 * in it.
1396856398eSAndre Oppermann 	 */
1406856398eSAndre Oppermann 	if (ctx->buflen > 0 || len < 8)
1416856398eSAndre Oppermann 		len -= SipBuf(ctx, &s, len, 0);
1426856398eSAndre Oppermann 	if (len == 0)
1436856398eSAndre Oppermann 		return;
1446856398eSAndre Oppermann 
1456856398eSAndre Oppermann 	rem = len & 0x7;
1466856398eSAndre Oppermann 	len >>= 3;
1476856398eSAndre Oppermann 
1486856398eSAndre Oppermann 	/* Optimze for 64bit aligned/unaligned access. */
1496856398eSAndre Oppermann 	if (((uintptr_t)s & 0x7) == 0) {
150bf0354c0SAndre Oppermann 		for (p = (const uint64_t *)s; len > 0; len--, p++) {
1516856398eSAndre Oppermann 			m = le64toh(*p);
1526856398eSAndre Oppermann 			ctx->v[3] ^= m;
1536856398eSAndre Oppermann 			SipRounds(ctx, 0);
1546856398eSAndre Oppermann 			ctx->v[0] ^= m;
1556856398eSAndre Oppermann 		}
156bf0354c0SAndre Oppermann 		s = (const uint8_t *)p;
1576856398eSAndre Oppermann 	} else {
1586856398eSAndre Oppermann 		for (; len > 0; len--, s += 8) {
1596856398eSAndre Oppermann 			m = le64dec(s);
1606856398eSAndre Oppermann 			ctx->v[3] ^= m;
1616856398eSAndre Oppermann 			SipRounds(ctx, 0);
1626856398eSAndre Oppermann 			ctx->v[0] ^= m;
1636856398eSAndre Oppermann 		}
1646856398eSAndre Oppermann 	}
1656856398eSAndre Oppermann 
1666856398eSAndre Oppermann 	/* Push remainder into buffer. */
1676856398eSAndre Oppermann 	if (rem > 0)
1686856398eSAndre Oppermann 		(void)SipBuf(ctx, &s, rem, 0);
1696856398eSAndre Oppermann }
1706856398eSAndre Oppermann 
1716856398eSAndre Oppermann void
172571ebf76SConrad Meyer SipHash_Final(uint8_t dst[static SIPHASH_DIGEST_LENGTH], SIPHASH_CTX *ctx)
1736856398eSAndre Oppermann {
1746856398eSAndre Oppermann 	uint64_t r;
1756856398eSAndre Oppermann 
1766856398eSAndre Oppermann 	KASSERT(ctx->initialized == 2,
1776856398eSAndre Oppermann 	    ("%s: context %p not properly initialized", __func__, ctx));
1786856398eSAndre Oppermann 
1796856398eSAndre Oppermann 	r = SipHash_End(ctx);
1806856398eSAndre Oppermann 	le64enc(dst, r);
1816856398eSAndre Oppermann }
1826856398eSAndre Oppermann 
1836856398eSAndre Oppermann uint64_t
1846856398eSAndre Oppermann SipHash_End(SIPHASH_CTX *ctx)
1856856398eSAndre Oppermann {
1866856398eSAndre Oppermann 	uint64_t r;
1876856398eSAndre Oppermann 
1886856398eSAndre Oppermann 	KASSERT(ctx->initialized == 2,
1896856398eSAndre Oppermann 	    ("%s: context %p not properly initialized", __func__, ctx));
1906856398eSAndre Oppermann 
1916856398eSAndre Oppermann 	SipBuf(ctx, NULL, 0, 1);
1926856398eSAndre Oppermann 	ctx->v[2] ^= 0xff;
1936856398eSAndre Oppermann 	SipRounds(ctx, 1);
1946856398eSAndre Oppermann 	r = (ctx->v[0] ^ ctx->v[1]) ^ (ctx->v[2] ^ ctx->v[3]);
1956856398eSAndre Oppermann 
1966856398eSAndre Oppermann 	bzero(ctx, sizeof(*ctx));
1976856398eSAndre Oppermann 	return (r);
1986856398eSAndre Oppermann }
1996856398eSAndre Oppermann 
2006856398eSAndre Oppermann uint64_t
201571ebf76SConrad Meyer SipHashX(SIPHASH_CTX *ctx, int rc, int rf,
202571ebf76SConrad Meyer     const uint8_t key[static SIPHASH_KEY_LENGTH], const void *src, size_t len)
2036856398eSAndre Oppermann {
2046856398eSAndre Oppermann 
2056856398eSAndre Oppermann 	SipHash_InitX(ctx, rc, rf);
2066856398eSAndre Oppermann 	SipHash_SetKey(ctx, key);
2076856398eSAndre Oppermann 	SipHash_Update(ctx, src, len);
2086856398eSAndre Oppermann 
2096856398eSAndre Oppermann 	return (SipHash_End(ctx));
2106856398eSAndre Oppermann }
2116856398eSAndre Oppermann 
2126856398eSAndre Oppermann #define SIP_ROTL(x, b)	(uint64_t)(((x) << (b)) | ( (x) >> (64 - (b))))
2136856398eSAndre Oppermann 
2146856398eSAndre Oppermann static void
2156856398eSAndre Oppermann SipRounds(SIPHASH_CTX *ctx, int final)
2166856398eSAndre Oppermann {
2176856398eSAndre Oppermann 	int rounds;
2186856398eSAndre Oppermann 
2196856398eSAndre Oppermann 	if (!final)
2206856398eSAndre Oppermann 		rounds = ctx->rounds_compr;
2216856398eSAndre Oppermann 	else
2226856398eSAndre Oppermann 		rounds = ctx->rounds_final;
2236856398eSAndre Oppermann 
2246856398eSAndre Oppermann 	while (rounds--) {
2256856398eSAndre Oppermann 		ctx->v[0] += ctx->v[1];
2266856398eSAndre Oppermann 		ctx->v[2] += ctx->v[3];
2276856398eSAndre Oppermann 		ctx->v[1] = SIP_ROTL(ctx->v[1], 13);
2286856398eSAndre Oppermann 		ctx->v[3] = SIP_ROTL(ctx->v[3], 16);
2296856398eSAndre Oppermann 
2306856398eSAndre Oppermann 		ctx->v[1] ^= ctx->v[0];
2316856398eSAndre Oppermann 		ctx->v[3] ^= ctx->v[2];
2326856398eSAndre Oppermann 		ctx->v[0] = SIP_ROTL(ctx->v[0], 32);
2336856398eSAndre Oppermann 
2346856398eSAndre Oppermann 		ctx->v[2] += ctx->v[1];
2356856398eSAndre Oppermann 		ctx->v[0] += ctx->v[3];
2366856398eSAndre Oppermann 		ctx->v[1] = SIP_ROTL(ctx->v[1], 17);
2376856398eSAndre Oppermann 		ctx->v[3] = SIP_ROTL(ctx->v[3], 21);
2386856398eSAndre Oppermann 
2396856398eSAndre Oppermann 		ctx->v[1] ^= ctx->v[2];
2406856398eSAndre Oppermann 		ctx->v[3] ^= ctx->v[0];
2416856398eSAndre Oppermann 		ctx->v[2] = SIP_ROTL(ctx->v[2], 32);
2426856398eSAndre Oppermann 	}
2436856398eSAndre Oppermann }
2446856398eSAndre Oppermann 
245