72fd5198b8993c7971ed92743c2969499e98e96b
[openssl.git] / crypto / siphash / siphash.c
1 /*
2  * Copyright 2017 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 /* hash_size = crounds = drounds = 0 means SipHash24 with 16-byte output */
84 int SipHash_Init(SIPHASH *ctx, const unsigned char *k, int hash_size, int crounds, int drounds)
85 {
86     uint64_t k0 = U8TO64_LE(k);
87     uint64_t k1 = U8TO64_LE(k + 8);
88
89     if (hash_size == 0)
90         hash_size = SIPHASH_MAX_DIGEST_SIZE;
91     else if (hash_size != SIPHASH_MIN_DIGEST_SIZE &&
92              hash_size != SIPHASH_MAX_DIGEST_SIZE)
93         return 0;
94
95     if (drounds == 0)
96         drounds = SIPHASH_D_ROUNDS;
97     if (crounds == 0)
98         crounds = SIPHASH_C_ROUNDS;
99
100     ctx->crounds = crounds;
101     ctx->drounds = drounds;
102     ctx->hash_size = hash_size;
103
104     ctx->len = 0;
105     ctx->total_inlen = 0;
106
107     ctx->v0 = 0x736f6d6570736575ULL ^ k0;
108     ctx->v1 = 0x646f72616e646f6dULL ^ k1;
109     ctx->v2 = 0x6c7967656e657261ULL ^ k0;
110     ctx->v3 = 0x7465646279746573ULL ^ k1;
111
112     if (ctx->hash_size == SIPHASH_MAX_DIGEST_SIZE)
113         ctx->v1 ^= 0xee;
114
115     return 1;
116 }
117
118 void SipHash_Update(SIPHASH *ctx, const unsigned char *in, size_t inlen)
119 {
120     uint64_t m;
121     const uint8_t *end;
122     int left;
123     int i;
124     uint64_t v0 = ctx->v0;
125     uint64_t v1 = ctx->v1;
126     uint64_t v2 = ctx->v2;
127     uint64_t v3 = ctx->v3;
128
129     ctx->total_inlen += inlen;
130
131     if (ctx->len) {
132         /* deal with leavings */
133         size_t available = SIPHASH_BLOCK_SIZE - ctx->len;
134
135         /* not enough to fill leavings */
136         if (inlen < available) {
137             memcpy(&ctx->leavings[ctx->len], in, inlen);
138             ctx->len += inlen;
139             return;
140         }
141
142         /* copy data into leavings and reduce input */
143         memcpy(&ctx->leavings[ctx->len], in, available);
144         inlen -= available;
145         in += available;
146
147         /* process leavings */
148         m = U8TO64_LE(ctx->leavings);
149         v3 ^= m;
150         for (i = 0; i < ctx->crounds; ++i)
151             SIPROUND;
152         v0 ^= m;
153     }
154     left = inlen & (SIPHASH_BLOCK_SIZE-1); /* gets put into leavings */
155     end = in + inlen - left;
156
157     for (; in != end; in += 8) {
158         m = U8TO64_LE(in);
159         v3 ^= m;
160         for (i = 0; i < ctx->crounds; ++i)
161             SIPROUND;
162         v0 ^= m;
163     }
164
165     /* save leavings and other ctx */
166     if (left)
167         memcpy(ctx->leavings, end, left);
168     ctx->len = left;
169
170     ctx->v0 = v0;
171     ctx->v1 = v1;
172     ctx->v2 = v2;
173     ctx->v3 = v3;
174 }
175
176 int SipHash_Final(SIPHASH *ctx, unsigned char *out, size_t outlen)
177 {
178     /* finalize hash */
179     int i;
180     uint64_t b = ctx->total_inlen << 56;
181     uint64_t v0 = ctx->v0;
182     uint64_t v1 = ctx->v1;
183     uint64_t v2 = ctx->v2;
184     uint64_t v3 = ctx->v3;
185
186     if (outlen != (size_t)ctx->hash_size)
187         return 0;
188
189     switch (ctx->len) {
190     case 7:
191         b |= ((uint64_t)ctx->leavings[6]) << 48;
192         /* fall thru */
193     case 6:
194         b |= ((uint64_t)ctx->leavings[5]) << 40;
195         /* fall thru */
196     case 5:
197         b |= ((uint64_t)ctx->leavings[4]) << 32;
198         /* fall thru */
199     case 4:
200         b |= ((uint64_t)ctx->leavings[3]) << 24;
201         /* fall thru */
202     case 3:
203         b |= ((uint64_t)ctx->leavings[2]) << 16;
204         /* fall thru */
205     case 2:
206         b |= ((uint64_t)ctx->leavings[1]) <<  8;
207         /* fall thru */
208     case 1:
209         b |= ((uint64_t)ctx->leavings[0]);
210     case 0:
211         break;
212     }
213
214     v3 ^= b;
215     for (i = 0; i < ctx->crounds; ++i)
216         SIPROUND;
217     v0 ^= b;
218     if (ctx->hash_size == SIPHASH_MAX_DIGEST_SIZE)
219         v2 ^= 0xee;
220     else
221         v2 ^= 0xff;
222     for (i = 0; i < ctx->drounds; ++i)
223         SIPROUND;
224     b = v0 ^ v1 ^ v2  ^ v3;
225     U64TO8_LE(out, b);
226     if (ctx->hash_size == SIPHASH_MIN_DIGEST_SIZE)
227         return 1;
228     v1 ^= 0xdd;
229     for (i = 0; i < ctx->drounds; ++i)
230         SIPROUND;
231     b = v0 ^ v1 ^ v2  ^ v3;
232     U64TO8_LE(out + 8, b);
233     return 1;
234 }