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