]> git.ipfire.org Git - thirdparty/openssl.git/blob - crypto/siphash/siphash.c
Update copyright year
[thirdparty/openssl.git] / crypto / siphash / siphash.c
1 /*
2 * Copyright 2017-2018 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the OpenSSL license (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 "internal/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
73 size_t SipHash_ctx_size(void)
74 {
75 return sizeof(SIPHASH);
76 }
77
78 size_t SipHash_hash_size(SIPHASH *ctx)
79 {
80 return ctx->hash_size;
81 }
82
83 static 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
90 int 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 ctx->hash_size = hash_size;
98 return 1;
99 }
100
101 /* hash_size = crounds = drounds = 0 means SipHash24 with 16-byte output */
102 int SipHash_Init(SIPHASH *ctx, const unsigned char *k, int crounds, int drounds)
103 {
104 uint64_t k0 = U8TO64_LE(k);
105 uint64_t k1 = U8TO64_LE(k + 8);
106
107 /* If the hash size wasn't set, i.e. is zero */
108 ctx->hash_size = siphash_adjust_hash_size(ctx->hash_size);
109
110 if (drounds == 0)
111 drounds = SIPHASH_D_ROUNDS;
112 if (crounds == 0)
113 crounds = SIPHASH_C_ROUNDS;
114
115 ctx->crounds = crounds;
116 ctx->drounds = drounds;
117
118 ctx->len = 0;
119 ctx->total_inlen = 0;
120
121 ctx->v0 = 0x736f6d6570736575ULL ^ k0;
122 ctx->v1 = 0x646f72616e646f6dULL ^ k1;
123 ctx->v2 = 0x6c7967656e657261ULL ^ k0;
124 ctx->v3 = 0x7465646279746573ULL ^ k1;
125
126 if (ctx->hash_size == SIPHASH_MAX_DIGEST_SIZE)
127 ctx->v1 ^= 0xee;
128
129 return 1;
130 }
131
132 void SipHash_Update(SIPHASH *ctx, const unsigned char *in, size_t inlen)
133 {
134 uint64_t m;
135 const uint8_t *end;
136 int left;
137 int i;
138 uint64_t v0 = ctx->v0;
139 uint64_t v1 = ctx->v1;
140 uint64_t v2 = ctx->v2;
141 uint64_t v3 = ctx->v3;
142
143 ctx->total_inlen += inlen;
144
145 if (ctx->len) {
146 /* deal with leavings */
147 size_t available = SIPHASH_BLOCK_SIZE - ctx->len;
148
149 /* not enough to fill leavings */
150 if (inlen < available) {
151 memcpy(&ctx->leavings[ctx->len], in, inlen);
152 ctx->len += inlen;
153 return;
154 }
155
156 /* copy data into leavings and reduce input */
157 memcpy(&ctx->leavings[ctx->len], in, available);
158 inlen -= available;
159 in += available;
160
161 /* process leavings */
162 m = U8TO64_LE(ctx->leavings);
163 v3 ^= m;
164 for (i = 0; i < ctx->crounds; ++i)
165 SIPROUND;
166 v0 ^= m;
167 }
168 left = inlen & (SIPHASH_BLOCK_SIZE-1); /* gets put into leavings */
169 end = in + inlen - left;
170
171 for (; in != end; in += 8) {
172 m = U8TO64_LE(in);
173 v3 ^= m;
174 for (i = 0; i < ctx->crounds; ++i)
175 SIPROUND;
176 v0 ^= m;
177 }
178
179 /* save leavings and other ctx */
180 if (left)
181 memcpy(ctx->leavings, end, left);
182 ctx->len = left;
183
184 ctx->v0 = v0;
185 ctx->v1 = v1;
186 ctx->v2 = v2;
187 ctx->v3 = v3;
188 }
189
190 int SipHash_Final(SIPHASH *ctx, unsigned char *out, size_t outlen)
191 {
192 /* finalize hash */
193 int i;
194 uint64_t b = ctx->total_inlen << 56;
195 uint64_t v0 = ctx->v0;
196 uint64_t v1 = ctx->v1;
197 uint64_t v2 = ctx->v2;
198 uint64_t v3 = ctx->v3;
199
200 if (outlen != (size_t)ctx->hash_size)
201 return 0;
202
203 switch (ctx->len) {
204 case 7:
205 b |= ((uint64_t)ctx->leavings[6]) << 48;
206 /* fall thru */
207 case 6:
208 b |= ((uint64_t)ctx->leavings[5]) << 40;
209 /* fall thru */
210 case 5:
211 b |= ((uint64_t)ctx->leavings[4]) << 32;
212 /* fall thru */
213 case 4:
214 b |= ((uint64_t)ctx->leavings[3]) << 24;
215 /* fall thru */
216 case 3:
217 b |= ((uint64_t)ctx->leavings[2]) << 16;
218 /* fall thru */
219 case 2:
220 b |= ((uint64_t)ctx->leavings[1]) << 8;
221 /* fall thru */
222 case 1:
223 b |= ((uint64_t)ctx->leavings[0]);
224 case 0:
225 break;
226 }
227
228 v3 ^= b;
229 for (i = 0; i < ctx->crounds; ++i)
230 SIPROUND;
231 v0 ^= b;
232 if (ctx->hash_size == SIPHASH_MAX_DIGEST_SIZE)
233 v2 ^= 0xee;
234 else
235 v2 ^= 0xff;
236 for (i = 0; i < ctx->drounds; ++i)
237 SIPROUND;
238 b = v0 ^ v1 ^ v2 ^ v3;
239 U64TO8_LE(out, b);
240 if (ctx->hash_size == SIPHASH_MIN_DIGEST_SIZE)
241 return 1;
242 v1 ^= 0xdd;
243 for (i = 0; i < ctx->drounds; ++i)
244 SIPROUND;
245 b = v0 ^ v1 ^ v2 ^ v3;
246 U64TO8_LE(out + 8, b);
247 return 1;
248 }