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