1/*
2 * Copyright 2017-2018 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10/* Based on https://131002.net/siphash C reference implementation */
11/*
12 SipHash reference C implementation
13
14 Copyright (c) 2012-2016 Jean-Philippe Aumasson
15 Copyright (c) 2012-2014 Daniel J. Bernstein
16
17 To the extent possible under law, the author(s) have dedicated all copyright
18 and related and neighboring rights to this software to the public domain
19 worldwide. This software is distributed without any warranty.
20
21 You should have received a copy of the CC0 Public Domain Dedication along
22 with this software. If not, see
23 <http://creativecommons.org/publicdomain/zero/1.0/>.
24 */
25
26#include <stdlib.h>
27#include <string.h>
28#include <openssl/crypto.h>
29
30#include "crypto/siphash.h"
31#include "siphash_local.h"
32
33/* default: SipHash-2-4 */
34#define SIPHASH_C_ROUNDS 2
35#define SIPHASH_D_ROUNDS 4
36
37#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
38
39#define U32TO8_LE(p, v) \
40 (p)[0] = (uint8_t)((v)); \
41 (p)[1] = (uint8_t)((v) >> 8); \
42 (p)[2] = (uint8_t)((v) >> 16); \
43 (p)[3] = (uint8_t)((v) >> 24);
44
45#define U64TO8_LE(p, v) \
46 U32TO8_LE((p), (uint32_t)((v))); \
47 U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
48
49#define U8TO64_LE(p) \
50 (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \
51 ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
52 ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
53 ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
54
55#define SIPROUND \
56 do { \
57 v0 += v1; \
58 v1 = ROTL(v1, 13); \
59 v1 ^= v0; \
60 v0 = ROTL(v0, 32); \
61 v2 += v3; \
62 v3 = ROTL(v3, 16); \
63 v3 ^= v2; \
64 v0 += v3; \
65 v3 = ROTL(v3, 21); \
66 v3 ^= v0; \
67 v2 += v1; \
68 v1 = ROTL(v1, 17); \
69 v1 ^= v2; \
70 v2 = ROTL(v2, 32); \
71 } while (0)
72
73size_t SipHash_ctx_size(void)
74{
75 return sizeof(SIPHASH);
76}
77
78size_t SipHash_hash_size(SIPHASH *ctx)
79{
80 return ctx->hash_size;
81}
82
83static size_t siphash_adjust_hash_size(size_t hash_size)
84{
85 if (hash_size == 0)
86 hash_size = SIPHASH_MAX_DIGEST_SIZE;
87 return hash_size;
88}
89
90int SipHash_set_hash_size(SIPHASH *ctx, size_t hash_size)
91{
92 hash_size = siphash_adjust_hash_size(hash_size);
93 if (hash_size != SIPHASH_MIN_DIGEST_SIZE
94 && hash_size != SIPHASH_MAX_DIGEST_SIZE)
95 return 0;
96
97 /*
98 * It's possible that the key was set first. If the hash size changes,
99 * we need to adjust v1 (see SipHash_Init().
100 */
101
102 /* Start by adjusting the stored size, to make things easier */
103 ctx->hash_size = siphash_adjust_hash_size(ctx->hash_size);
104
105 /* Now, adjust ctx->v1 if the old and the new size differ */
106 if ((size_t)ctx->hash_size != hash_size) {
107 ctx->v1 ^= 0xee;
108 ctx->hash_size = hash_size;
109 }
110 return 1;
111}
112
113/* hash_size = crounds = drounds = 0 means SipHash24 with 16-byte output */
114int SipHash_Init(SIPHASH *ctx, const unsigned char *k, int crounds, int drounds)
115{
116 uint64_t k0 = U8TO64_LE(k);
117 uint64_t k1 = U8TO64_LE(k + 8);
118
119 /* If the hash size wasn't set, i.e. is zero */
120 ctx->hash_size = siphash_adjust_hash_size(ctx->hash_size);
121
122 if (drounds == 0)
123 drounds = SIPHASH_D_ROUNDS;
124 if (crounds == 0)
125 crounds = SIPHASH_C_ROUNDS;
126
127 ctx->crounds = crounds;
128 ctx->drounds = drounds;
129
130 ctx->len = 0;
131 ctx->total_inlen = 0;
132
133 ctx->v0 = 0x736f6d6570736575ULL ^ k0;
134 ctx->v1 = 0x646f72616e646f6dULL ^ k1;
135 ctx->v2 = 0x6c7967656e657261ULL ^ k0;
136 ctx->v3 = 0x7465646279746573ULL ^ k1;
137
138 if (ctx->hash_size == SIPHASH_MAX_DIGEST_SIZE)
139 ctx->v1 ^= 0xee;
140
141 return 1;
142}
143
144void SipHash_Update(SIPHASH *ctx, const unsigned char *in, size_t inlen)
145{
146 uint64_t m;
147 const uint8_t *end;
148 int left;
149 int i;
150 uint64_t v0 = ctx->v0;
151 uint64_t v1 = ctx->v1;
152 uint64_t v2 = ctx->v2;
153 uint64_t v3 = ctx->v3;
154
155 ctx->total_inlen += inlen;
156
157 if (ctx->len) {
158 /* deal with leavings */
159 size_t available = SIPHASH_BLOCK_SIZE - ctx->len;
160
161 /* not enough to fill leavings */
162 if (inlen < available) {
163 memcpy(&ctx->leavings[ctx->len], in, inlen);
164 ctx->len += inlen;
165 return;
166 }
167
168 /* copy data into leavings and reduce input */
169 memcpy(&ctx->leavings[ctx->len], in, available);
170 inlen -= available;
171 in += available;
172
173 /* process leavings */
174 m = U8TO64_LE(ctx->leavings);
175 v3 ^= m;
176 for (i = 0; i < ctx->crounds; ++i)
177 SIPROUND;
178 v0 ^= m;
179 }
180 left = inlen & (SIPHASH_BLOCK_SIZE-1); /* gets put into leavings */
181 end = in + inlen - left;
182
183 for (; in != end; in += 8) {
184 m = U8TO64_LE(in);
185 v3 ^= m;
186 for (i = 0; i < ctx->crounds; ++i)
187 SIPROUND;
188 v0 ^= m;
189 }
190
191 /* save leavings and other ctx */
192 if (left)
193 memcpy(ctx->leavings, end, left);
194 ctx->len = left;
195
196 ctx->v0 = v0;
197 ctx->v1 = v1;
198 ctx->v2 = v2;
199 ctx->v3 = v3;
200}
201
202int SipHash_Final(SIPHASH *ctx, unsigned char *out, size_t outlen)
203{
204 /* finalize hash */
205 int i;
206 uint64_t b = ctx->total_inlen << 56;
207 uint64_t v0 = ctx->v0;
208 uint64_t v1 = ctx->v1;
209 uint64_t v2 = ctx->v2;
210 uint64_t v3 = ctx->v3;
211
212 if (outlen != (size_t)ctx->hash_size)
213 return 0;
214
215 switch (ctx->len) {
216 case 7:
217 b |= ((uint64_t)ctx->leavings[6]) << 48;
218 /* fall thru */
219 case 6:
220 b |= ((uint64_t)ctx->leavings[5]) << 40;
221 /* fall thru */
222 case 5:
223 b |= ((uint64_t)ctx->leavings[4]) << 32;
224 /* fall thru */
225 case 4:
226 b |= ((uint64_t)ctx->leavings[3]) << 24;
227 /* fall thru */
228 case 3:
229 b |= ((uint64_t)ctx->leavings[2]) << 16;
230 /* fall thru */
231 case 2:
232 b |= ((uint64_t)ctx->leavings[1]) << 8;
233 /* fall thru */
234 case 1:
235 b |= ((uint64_t)ctx->leavings[0]);
236 case 0:
237 break;
238 }
239
240 v3 ^= b;
241 for (i = 0; i < ctx->crounds; ++i)
242 SIPROUND;
243 v0 ^= b;
244 if (ctx->hash_size == SIPHASH_MAX_DIGEST_SIZE)
245 v2 ^= 0xee;
246 else
247 v2 ^= 0xff;
248 for (i = 0; i < ctx->drounds; ++i)
249 SIPROUND;
250 b = v0 ^ v1 ^ v2 ^ v3;
251 U64TO8_LE(out, b);
252 if (ctx->hash_size == SIPHASH_MIN_DIGEST_SIZE)
253 return 1;
254 v1 ^= 0xdd;
255 for (i = 0; i < ctx->drounds; ++i)
256 SIPROUND;
257 b = v0 ^ v1 ^ v2 ^ v3;
258 U64TO8_LE(out + 8, b);
259 return 1;
260}
261