1 | /*
|
---|
2 | * Copyright 2017-2021 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 |
|
---|
32 | #define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
|
---|
33 |
|
---|
34 | #define U32TO8_LE(p, v) \
|
---|
35 | (p)[0] = (uint8_t)((v)); \
|
---|
36 | (p)[1] = (uint8_t)((v) >> 8); \
|
---|
37 | (p)[2] = (uint8_t)((v) >> 16); \
|
---|
38 | (p)[3] = (uint8_t)((v) >> 24);
|
---|
39 |
|
---|
40 | #define U64TO8_LE(p, v) \
|
---|
41 | U32TO8_LE((p), (uint32_t)((v))); \
|
---|
42 | U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
|
---|
43 |
|
---|
44 | #define U8TO64_LE(p) \
|
---|
45 | (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \
|
---|
46 | ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
|
---|
47 | ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
|
---|
48 | ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
|
---|
49 |
|
---|
50 | #define SIPROUND \
|
---|
51 | do { \
|
---|
52 | v0 += v1; \
|
---|
53 | v1 = ROTL(v1, 13); \
|
---|
54 | v1 ^= v0; \
|
---|
55 | v0 = ROTL(v0, 32); \
|
---|
56 | v2 += v3; \
|
---|
57 | v3 = ROTL(v3, 16); \
|
---|
58 | v3 ^= v2; \
|
---|
59 | v0 += v3; \
|
---|
60 | v3 = ROTL(v3, 21); \
|
---|
61 | v3 ^= v0; \
|
---|
62 | v2 += v1; \
|
---|
63 | v1 = ROTL(v1, 17); \
|
---|
64 | v1 ^= v2; \
|
---|
65 | v2 = ROTL(v2, 32); \
|
---|
66 | } while (0)
|
---|
67 |
|
---|
68 | size_t SipHash_ctx_size(void)
|
---|
69 | {
|
---|
70 | return sizeof(SIPHASH);
|
---|
71 | }
|
---|
72 |
|
---|
73 | size_t SipHash_hash_size(SIPHASH *ctx)
|
---|
74 | {
|
---|
75 | return ctx->hash_size;
|
---|
76 | }
|
---|
77 |
|
---|
78 | static size_t siphash_adjust_hash_size(size_t hash_size)
|
---|
79 | {
|
---|
80 | if (hash_size == 0)
|
---|
81 | hash_size = SIPHASH_MAX_DIGEST_SIZE;
|
---|
82 | return hash_size;
|
---|
83 | }
|
---|
84 |
|
---|
85 | int SipHash_set_hash_size(SIPHASH *ctx, size_t hash_size)
|
---|
86 | {
|
---|
87 | hash_size = siphash_adjust_hash_size(hash_size);
|
---|
88 | if (hash_size != SIPHASH_MIN_DIGEST_SIZE
|
---|
89 | && hash_size != SIPHASH_MAX_DIGEST_SIZE)
|
---|
90 | return 0;
|
---|
91 |
|
---|
92 | /*
|
---|
93 | * It's possible that the key was set first. If the hash size changes,
|
---|
94 | * we need to adjust v1 (see SipHash_Init().
|
---|
95 | */
|
---|
96 |
|
---|
97 | /* Start by adjusting the stored size, to make things easier */
|
---|
98 | ctx->hash_size = siphash_adjust_hash_size(ctx->hash_size);
|
---|
99 |
|
---|
100 | /* Now, adjust ctx->v1 if the old and the new size differ */
|
---|
101 | if ((size_t)ctx->hash_size != hash_size) {
|
---|
102 | ctx->v1 ^= 0xee;
|
---|
103 | ctx->hash_size = hash_size;
|
---|
104 | }
|
---|
105 | return 1;
|
---|
106 | }
|
---|
107 |
|
---|
108 | /* hash_size = crounds = drounds = 0 means SipHash24 with 16-byte output */
|
---|
109 | int SipHash_Init(SIPHASH *ctx, const unsigned char *k, int crounds, int drounds)
|
---|
110 | {
|
---|
111 | uint64_t k0 = U8TO64_LE(k);
|
---|
112 | uint64_t k1 = U8TO64_LE(k + 8);
|
---|
113 |
|
---|
114 | /* If the hash size wasn't set, i.e. is zero */
|
---|
115 | ctx->hash_size = siphash_adjust_hash_size(ctx->hash_size);
|
---|
116 |
|
---|
117 | if (drounds == 0)
|
---|
118 | drounds = SIPHASH_D_ROUNDS;
|
---|
119 | if (crounds == 0)
|
---|
120 | crounds = SIPHASH_C_ROUNDS;
|
---|
121 |
|
---|
122 | ctx->crounds = crounds;
|
---|
123 | ctx->drounds = drounds;
|
---|
124 |
|
---|
125 | ctx->len = 0;
|
---|
126 | ctx->total_inlen = 0;
|
---|
127 |
|
---|
128 | ctx->v0 = 0x736f6d6570736575ULL ^ k0;
|
---|
129 | ctx->v1 = 0x646f72616e646f6dULL ^ k1;
|
---|
130 | ctx->v2 = 0x6c7967656e657261ULL ^ k0;
|
---|
131 | ctx->v3 = 0x7465646279746573ULL ^ k1;
|
---|
132 |
|
---|
133 | if (ctx->hash_size == SIPHASH_MAX_DIGEST_SIZE)
|
---|
134 | ctx->v1 ^= 0xee;
|
---|
135 |
|
---|
136 | return 1;
|
---|
137 | }
|
---|
138 |
|
---|
139 | void SipHash_Update(SIPHASH *ctx, const unsigned char *in, size_t inlen)
|
---|
140 | {
|
---|
141 | uint64_t m;
|
---|
142 | const uint8_t *end;
|
---|
143 | int left;
|
---|
144 | unsigned int i;
|
---|
145 | uint64_t v0 = ctx->v0;
|
---|
146 | uint64_t v1 = ctx->v1;
|
---|
147 | uint64_t v2 = ctx->v2;
|
---|
148 | uint64_t v3 = ctx->v3;
|
---|
149 |
|
---|
150 | ctx->total_inlen += inlen;
|
---|
151 |
|
---|
152 | if (ctx->len) {
|
---|
153 | /* deal with leavings */
|
---|
154 | size_t available = SIPHASH_BLOCK_SIZE - ctx->len;
|
---|
155 |
|
---|
156 | /* not enough to fill leavings */
|
---|
157 | if (inlen < available) {
|
---|
158 | memcpy(&ctx->leavings[ctx->len], in, inlen);
|
---|
159 | ctx->len += inlen;
|
---|
160 | return;
|
---|
161 | }
|
---|
162 |
|
---|
163 | /* copy data into leavings and reduce input */
|
---|
164 | memcpy(&ctx->leavings[ctx->len], in, available);
|
---|
165 | inlen -= available;
|
---|
166 | in += available;
|
---|
167 |
|
---|
168 | /* process leavings */
|
---|
169 | m = U8TO64_LE(ctx->leavings);
|
---|
170 | v3 ^= m;
|
---|
171 | for (i = 0; i < ctx->crounds; ++i)
|
---|
172 | SIPROUND;
|
---|
173 | v0 ^= m;
|
---|
174 | }
|
---|
175 | left = inlen & (SIPHASH_BLOCK_SIZE-1); /* gets put into leavings */
|
---|
176 | end = in + inlen - left;
|
---|
177 |
|
---|
178 | for (; in != end; in += 8) {
|
---|
179 | m = U8TO64_LE(in);
|
---|
180 | v3 ^= m;
|
---|
181 | for (i = 0; i < ctx->crounds; ++i)
|
---|
182 | SIPROUND;
|
---|
183 | v0 ^= m;
|
---|
184 | }
|
---|
185 |
|
---|
186 | /* save leavings and other ctx */
|
---|
187 | if (left)
|
---|
188 | memcpy(ctx->leavings, end, left);
|
---|
189 | ctx->len = left;
|
---|
190 |
|
---|
191 | ctx->v0 = v0;
|
---|
192 | ctx->v1 = v1;
|
---|
193 | ctx->v2 = v2;
|
---|
194 | ctx->v3 = v3;
|
---|
195 | }
|
---|
196 |
|
---|
197 | int SipHash_Final(SIPHASH *ctx, unsigned char *out, size_t outlen)
|
---|
198 | {
|
---|
199 | /* finalize hash */
|
---|
200 | unsigned int i;
|
---|
201 | uint64_t b = ctx->total_inlen << 56;
|
---|
202 | uint64_t v0 = ctx->v0;
|
---|
203 | uint64_t v1 = ctx->v1;
|
---|
204 | uint64_t v2 = ctx->v2;
|
---|
205 | uint64_t v3 = ctx->v3;
|
---|
206 |
|
---|
207 | if (outlen != (size_t)ctx->hash_size)
|
---|
208 | return 0;
|
---|
209 |
|
---|
210 | switch (ctx->len) {
|
---|
211 | case 7:
|
---|
212 | b |= ((uint64_t)ctx->leavings[6]) << 48;
|
---|
213 | /* fall thru */
|
---|
214 | case 6:
|
---|
215 | b |= ((uint64_t)ctx->leavings[5]) << 40;
|
---|
216 | /* fall thru */
|
---|
217 | case 5:
|
---|
218 | b |= ((uint64_t)ctx->leavings[4]) << 32;
|
---|
219 | /* fall thru */
|
---|
220 | case 4:
|
---|
221 | b |= ((uint64_t)ctx->leavings[3]) << 24;
|
---|
222 | /* fall thru */
|
---|
223 | case 3:
|
---|
224 | b |= ((uint64_t)ctx->leavings[2]) << 16;
|
---|
225 | /* fall thru */
|
---|
226 | case 2:
|
---|
227 | b |= ((uint64_t)ctx->leavings[1]) << 8;
|
---|
228 | /* fall thru */
|
---|
229 | case 1:
|
---|
230 | b |= ((uint64_t)ctx->leavings[0]);
|
---|
231 | case 0:
|
---|
232 | break;
|
---|
233 | }
|
---|
234 |
|
---|
235 | v3 ^= b;
|
---|
236 | for (i = 0; i < ctx->crounds; ++i)
|
---|
237 | SIPROUND;
|
---|
238 | v0 ^= b;
|
---|
239 | if (ctx->hash_size == SIPHASH_MAX_DIGEST_SIZE)
|
---|
240 | v2 ^= 0xee;
|
---|
241 | else
|
---|
242 | v2 ^= 0xff;
|
---|
243 | for (i = 0; i < ctx->drounds; ++i)
|
---|
244 | SIPROUND;
|
---|
245 | b = v0 ^ v1 ^ v2 ^ v3;
|
---|
246 | U64TO8_LE(out, b);
|
---|
247 | if (ctx->hash_size == SIPHASH_MIN_DIGEST_SIZE)
|
---|
248 | return 1;
|
---|
249 | v1 ^= 0xdd;
|
---|
250 | for (i = 0; i < ctx->drounds; ++i)
|
---|
251 | SIPROUND;
|
---|
252 | b = v0 ^ v1 ^ v2 ^ v3;
|
---|
253 | U64TO8_LE(out + 8, b);
|
---|
254 | return 1;
|
---|
255 | }
|
---|