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