Home | History | Annotate | Line # | Download | only in isc
      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 (at) gmail.com> Copyright (c) 2012-2014 Daniel J. Bernstein
     29  * <djb (at) 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 
     91 void
     92 isc_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 
    178 void
    179 isc_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