]>
Commit | Line | Data |
---|---|---|
3f5616d7 | 1 | /* |
fecb3aae | 2 | * Copyright 2017-2022 The OpenSSL Project Authors. All Rights Reserved. |
3f5616d7 | 3 | * |
13414827 | 4 | * Licensed under the Apache License 2.0 (the "License"). You may not use |
3f5616d7 TS |
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 | |
e3713c36 | 15 | Copyright (c) 2012-2014 Daniel J. Bernstein |
3f5616d7 TS |
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 | |
e3713c36 | 22 | with this software. If not, see |
3f5616d7 TS |
23 | <http://creativecommons.org/publicdomain/zero/1.0/>. |
24 | */ | |
25 | ||
26 | #include <stdlib.h> | |
27 | #include <string.h> | |
28 | #include <openssl/crypto.h> | |
29 | ||
25f2138b | 30 | #include "crypto/siphash.h" |
3f5616d7 | 31 | |
3f5616d7 TS |
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 | ||
d74f23d2 RL |
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 | ||
42503613 RL |
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 | } | |
d74f23d2 RL |
105 | return 1; |
106 | } | |
107 | ||
3f5616d7 | 108 | /* hash_size = crounds = drounds = 0 means SipHash24 with 16-byte output */ |
d74f23d2 | 109 | int SipHash_Init(SIPHASH *ctx, const unsigned char *k, int crounds, int drounds) |
3f5616d7 TS |
110 | { |
111 | uint64_t k0 = U8TO64_LE(k); | |
112 | uint64_t k1 = U8TO64_LE(k + 8); | |
113 | ||
d74f23d2 RL |
114 | /* If the hash size wasn't set, i.e. is zero */ |
115 | ctx->hash_size = siphash_adjust_hash_size(ctx->hash_size); | |
3f5616d7 TS |
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; | |
3f5616d7 TS |
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; | |
ae7d90a1 | 144 | unsigned int i; |
3f5616d7 TS |
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 */ | |
ae7d90a1 | 200 | unsigned int i; |
3f5616d7 TS |
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 | ||
650b142c | 207 | if (ctx->crounds == 0 || outlen == 0 || outlen != (size_t)ctx->hash_size) |
3f5616d7 TS |
208 | return 0; |
209 | ||
210 | switch (ctx->len) { | |
211 | case 7: | |
212 | b |= ((uint64_t)ctx->leavings[6]) << 48; | |
018fcbec | 213 | /* fall thru */ |
3f5616d7 TS |
214 | case 6: |
215 | b |= ((uint64_t)ctx->leavings[5]) << 40; | |
018fcbec | 216 | /* fall thru */ |
3f5616d7 TS |
217 | case 5: |
218 | b |= ((uint64_t)ctx->leavings[4]) << 32; | |
018fcbec | 219 | /* fall thru */ |
3f5616d7 TS |
220 | case 4: |
221 | b |= ((uint64_t)ctx->leavings[3]) << 24; | |
018fcbec | 222 | /* fall thru */ |
3f5616d7 TS |
223 | case 3: |
224 | b |= ((uint64_t)ctx->leavings[2]) << 16; | |
018fcbec | 225 | /* fall thru */ |
3f5616d7 TS |
226 | case 2: |
227 | b |= ((uint64_t)ctx->leavings[1]) << 8; | |
018fcbec | 228 | /* fall thru */ |
3f5616d7 TS |
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 | } |