1/* $NetBSD: siphash.c,v 1.1 2024/02/18 20:57:50 christos Exp $ */ 2 3/* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16#include <inttypes.h> 17#include <string.h> 18#include <unistd.h> 19 20#include <isc/endian.h> 21#include <isc/siphash.h> 22#include <isc/util.h> 23 24/* 25 * The implementation is based on SipHash reference C implementation by 26 * 27 * Copyright (c) 2012-2016 Jean-Philippe Aumasson 28 * <jeanphilippe.aumasson@gmail.com> Copyright (c) 2012-2014 Daniel J. Bernstein 29 * <djb@cr.yp.to> 30 * 31 * To the extent possible under law, the author(s) have dedicated all copyright 32 * and related and neighboring rights to this software to the public domain 33 * worldwide. This software is distributed without any warranty. You should 34 * have received a copy of the CC0 Public Domain Dedication along with this 35 * software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. 36 */ 37 38#define cROUNDS 2 39#define dROUNDS 4 40 41#define ROTATE64(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) 42 43#define HALF_ROUND64(a, b, c, d, s, t) \ 44 a += b; \ 45 c += d; \ 46 b = ROTATE64(b, s) ^ a; \ 47 d = ROTATE64(d, t) ^ c; \ 48 a = ROTATE64(a, 32); 49 50#define FULL_ROUND64(v0, v1, v2, v3) \ 51 HALF_ROUND64(v0, v1, v2, v3, 13, 16); \ 52 HALF_ROUND64(v2, v1, v0, v3, 17, 21); 53 54#define SIPROUND FULL_ROUND64 55 56#define ROTATE32(x, b) (uint32_t)(((x) << (b)) | ((x) >> (32 - (b)))) 57 58#define HALF_ROUND32(a, b, c, d, s, t) \ 59 a += b; \ 60 c += d; \ 61 b = ROTATE32(b, s) ^ a; \ 62 d = ROTATE32(d, t) ^ c; \ 63 a = ROTATE32(a, 16); 64 65#define FULL_ROUND32(v0, v1, v2, v3) \ 66 HALF_ROUND32(v0, v1, v2, v3, 5, 8); \ 67 HALF_ROUND32(v2, v1, v0, v3, 13, 7); 68 69#define HALFSIPROUND FULL_ROUND32 70 71#define U32TO8_LE(p, v) \ 72 (p)[0] = (uint8_t)((v)); \ 73 (p)[1] = (uint8_t)((v) >> 8); \ 74 (p)[2] = (uint8_t)((v) >> 16); \ 75 (p)[3] = (uint8_t)((v) >> 24); 76 77#define U8TO32_LE(p) \ 78 (((uint32_t)((p)[0])) | ((uint32_t)((p)[1]) << 8) | \ 79 ((uint32_t)((p)[2]) << 16) | ((uint32_t)((p)[3]) << 24)) 80 81#define U64TO8_LE(p, v) \ 82 U32TO8_LE((p), (uint32_t)((v))); \ 83 U32TO8_LE((p) + 4, (uint32_t)((v) >> 32)); 84 85#define U8TO64_LE(p) \ 86 (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \ 87 ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \ 88 ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \ 89 ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56)) 90 91void 92isc_siphash24(const uint8_t *k, const uint8_t *in, const size_t inlen, 93 uint8_t *out) { 94 REQUIRE(k != NULL); 95 REQUIRE(out != NULL); 96 REQUIRE(inlen == 0 || in != NULL); 97 98 uint64_t k0; 99 uint64_t k1; 100 101 memcpy(&k0, k, sizeof(k0)); 102 memcpy(&k1, k + sizeof(k0), sizeof(k1)); 103 104 k0 = le64toh(k0); 105 k1 = le64toh(k1); 106 107 uint64_t v0 = UINT64_C(0x736f6d6570736575) ^ k0; 108 uint64_t v1 = UINT64_C(0x646f72616e646f6d) ^ k1; 109 uint64_t v2 = UINT64_C(0x6c7967656e657261) ^ k0; 110 uint64_t v3 = UINT64_C(0x7465646279746573) ^ k1; 111 112 uint64_t b = ((uint64_t)inlen) << 56; 113 114 const uint8_t *end = (in == NULL) 115 ? NULL 116 : in + inlen - (inlen % sizeof(uint64_t)); 117 const size_t left = inlen & 7; 118 119 for (; in != end; in += 8) { 120 uint64_t m = U8TO64_LE(in); 121 122 v3 ^= m; 123 124 for (size_t i = 0; i < cROUNDS; ++i) { 125 SIPROUND(v0, v1, v2, v3); 126 } 127 128 v0 ^= m; 129 } 130 131 switch (left) { 132 case 7: 133 b |= ((uint64_t)in[6]) << 48; 134 FALLTHROUGH; 135 case 6: 136 b |= ((uint64_t)in[5]) << 40; 137 FALLTHROUGH; 138 case 5: 139 b |= ((uint64_t)in[4]) << 32; 140 FALLTHROUGH; 141 case 4: 142 b |= ((uint64_t)in[3]) << 24; 143 FALLTHROUGH; 144 case 3: 145 b |= ((uint64_t)in[2]) << 16; 146 FALLTHROUGH; 147 case 2: 148 b |= ((uint64_t)in[1]) << 8; 149 FALLTHROUGH; 150 case 1: 151 b |= ((uint64_t)in[0]); 152 FALLTHROUGH; 153 case 0: 154 break; 155 default: 156 UNREACHABLE(); 157 } 158 159 v3 ^= b; 160 161 for (size_t i = 0; i < cROUNDS; ++i) { 162 SIPROUND(v0, v1, v2, v3); 163 } 164 165 v0 ^= b; 166 167 v2 ^= 0xff; 168 169 for (size_t i = 0; i < dROUNDS; ++i) { 170 SIPROUND(v0, v1, v2, v3); 171 } 172 173 b = v0 ^ v1 ^ v2 ^ v3; 174 175 U64TO8_LE(out, b); 176} 177 178void 179isc_halfsiphash24(const uint8_t *k, const uint8_t *in, const size_t inlen, 180 uint8_t *out) { 181 REQUIRE(k != NULL); 182 REQUIRE(out != NULL); 183 REQUIRE(inlen == 0 || in != NULL); 184 185 uint32_t k0 = U8TO32_LE(k); 186 uint32_t k1 = U8TO32_LE(k + 4); 187 188 uint32_t v0 = UINT32_C(0x00000000) ^ k0; 189 uint32_t v1 = UINT32_C(0x00000000) ^ k1; 190 uint32_t v2 = UINT32_C(0x6c796765) ^ k0; 191 uint32_t v3 = UINT32_C(0x74656462) ^ k1; 192 193 uint32_t b = ((uint32_t)inlen) << 24; 194 195 const uint8_t *end = (in == NULL) 196 ? NULL 197 : in + inlen - (inlen % sizeof(uint32_t)); 198 const int left = inlen & 3; 199 200 for (; in != end; in += 4) { 201 uint32_t m = U8TO32_LE(in); 202 v3 ^= m; 203 204 for (size_t i = 0; i < cROUNDS; ++i) { 205 HALFSIPROUND(v0, v1, v2, v3); 206 } 207 208 v0 ^= m; 209 } 210 211 switch (left) { 212 case 3: 213 b |= ((uint32_t)in[2]) << 16; 214 FALLTHROUGH; 215 case 2: 216 b |= ((uint32_t)in[1]) << 8; 217 FALLTHROUGH; 218 case 1: 219 b |= ((uint32_t)in[0]); 220 FALLTHROUGH; 221 case 0: 222 break; 223 default: 224 UNREACHABLE(); 225 } 226 227 v3 ^= b; 228 229 for (size_t i = 0; i < cROUNDS; ++i) { 230 HALFSIPROUND(v0, v1, v2, v3); 231 } 232 233 v0 ^= b; 234 235 v2 ^= 0xff; 236 237 for (size_t i = 0; i < dROUNDS; ++i) { 238 HALFSIPROUND(v0, v1, v2, v3); 239 } 240 241 b = v1 ^ v3; 242 U32TO8_LE(out, b); 243} 244