Add support for parameterized SipHash
[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    <jeanphilippe.aumasson@gmail.com>
16    Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
17
18    To the extent possible under law, the author(s) have dedicated all copyright
19    and related and neighboring rights to this software to the public domain
20    worldwide. This software is distributed without any warranty.
21
22    You should have received a copy of the CC0 Public Domain Dedication along
23    with
24    this software. If not, see
25    <http://creativecommons.org/publicdomain/zero/1.0/>.
26  */
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <openssl/crypto.h>
31
32 #include "internal/siphash.h"
33 #include "siphash_local.h"
34
35 /* default: SipHash-2-4 */
36 #define SIPHASH_C_ROUNDS 2
37 #define SIPHASH_D_ROUNDS 4
38
39 #define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
40
41 #define U32TO8_LE(p, v)                                                        \
42     (p)[0] = (uint8_t)((v));                                                   \
43     (p)[1] = (uint8_t)((v) >> 8);                                              \
44     (p)[2] = (uint8_t)((v) >> 16);                                             \
45     (p)[3] = (uint8_t)((v) >> 24);
46
47 #define U64TO8_LE(p, v)                                                        \
48     U32TO8_LE((p), (uint32_t)((v)));                                           \
49     U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
50
51 #define U8TO64_LE(p)                                                           \
52     (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) |                        \
53      ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) |                 \
54      ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) |                 \
55      ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
56
57 #define SIPROUND                                                               \
58     do {                                                                       \
59         v0 += v1;                                                              \
60         v1 = ROTL(v1, 13);                                                     \
61         v1 ^= v0;                                                              \
62         v0 = ROTL(v0, 32);                                                     \
63         v2 += v3;                                                              \
64         v3 = ROTL(v3, 16);                                                     \
65         v3 ^= v2;                                                              \
66         v0 += v3;                                                              \
67         v3 = ROTL(v3, 21);                                                     \
68         v3 ^= v0;                                                              \
69         v2 += v1;                                                              \
70         v1 = ROTL(v1, 17);                                                     \
71         v1 ^= v2;                                                              \
72         v2 = ROTL(v2, 32);                                                     \
73     } while (0)
74
75 size_t SipHash_ctx_size(void)
76 {
77     return sizeof(SIPHASH);
78 }
79
80 size_t SipHash_hash_size(SIPHASH *ctx)
81 {
82     return ctx->hash_size;
83 }
84
85 /* hash_size = crounds = drounds = 0 means SipHash24 with 16-byte output */
86 int SipHash_Init(SIPHASH *ctx, const unsigned char *k, int hash_size, int crounds, int drounds)
87 {
88     uint64_t k0 = U8TO64_LE(k);
89     uint64_t k1 = U8TO64_LE(k + 8);
90
91     if (hash_size == 0)
92         hash_size = SIPHASH_MAX_DIGEST_SIZE;
93     else if (hash_size != SIPHASH_MIN_DIGEST_SIZE &&
94              hash_size != SIPHASH_MAX_DIGEST_SIZE)
95         return 0;
96
97     if (drounds == 0)
98         drounds = SIPHASH_D_ROUNDS;
99     if (crounds == 0)
100         crounds = SIPHASH_C_ROUNDS;
101
102     ctx->crounds = crounds;
103     ctx->drounds = drounds;
104     ctx->hash_size = hash_size;
105
106     ctx->len = 0;
107     ctx->total_inlen = 0;
108
109     ctx->v0 = 0x736f6d6570736575ULL ^ k0;
110     ctx->v1 = 0x646f72616e646f6dULL ^ k1;
111     ctx->v2 = 0x6c7967656e657261ULL ^ k0;
112     ctx->v3 = 0x7465646279746573ULL ^ k1;
113
114     if (ctx->hash_size == SIPHASH_MAX_DIGEST_SIZE)
115         ctx->v1 ^= 0xee;
116
117     return 1;
118 }
119
120 void SipHash_Update(SIPHASH *ctx, const unsigned char *in, size_t inlen)
121 {
122     uint64_t m;
123     const uint8_t *end;
124     int left;
125     int i;
126     uint64_t v0 = ctx->v0;
127     uint64_t v1 = ctx->v1;
128     uint64_t v2 = ctx->v2;
129     uint64_t v3 = ctx->v3;
130
131     ctx->total_inlen += inlen;
132
133     if (ctx->len) {
134         /* deal with leavings */
135         size_t available = SIPHASH_BLOCK_SIZE - ctx->len;
136
137         /* not enough to fill leavings */
138         if (inlen < available) {
139             memcpy(&ctx->leavings[ctx->len], in, inlen);
140             ctx->len += inlen;
141             return;
142         }
143
144         /* copy data into leavings and reduce input */
145         memcpy(&ctx->leavings[ctx->len], in, available);
146         inlen -= available;
147         in += available;
148
149         /* process leavings */
150         m = U8TO64_LE(ctx->leavings);
151         v3 ^= m;
152         for (i = 0; i < ctx->crounds; ++i)
153             SIPROUND;
154         v0 ^= m;
155     }
156     left = inlen & (SIPHASH_BLOCK_SIZE-1); /* gets put into leavings */
157     end = in + inlen - left;
158
159     for (; in != end; in += 8) {
160         m = U8TO64_LE(in);
161         v3 ^= m;
162         for (i = 0; i < ctx->crounds; ++i)
163             SIPROUND;
164         v0 ^= m;
165     }
166
167     /* save leavings and other ctx */
168     if (left)
169         memcpy(ctx->leavings, end, left);
170     ctx->len = left;
171
172     ctx->v0 = v0;
173     ctx->v1 = v1;
174     ctx->v2 = v2;
175     ctx->v3 = v3;
176 }
177
178 int SipHash_Final(SIPHASH *ctx, unsigned char *out, size_t outlen)
179 {
180     /* finalize hash */
181     int i;
182     uint64_t b = ctx->total_inlen << 56;
183     uint64_t v0 = ctx->v0;
184     uint64_t v1 = ctx->v1;
185     uint64_t v2 = ctx->v2;
186     uint64_t v3 = ctx->v3;
187
188     if (outlen != (size_t)ctx->hash_size)
189         return 0;
190
191     switch (ctx->len) {
192     case 7:
193         b |= ((uint64_t)ctx->leavings[6]) << 48;
194     case 6:
195         b |= ((uint64_t)ctx->leavings[5]) << 40;
196     case 5:
197         b |= ((uint64_t)ctx->leavings[4]) << 32;
198     case 4:
199         b |= ((uint64_t)ctx->leavings[3]) << 24;
200     case 3:
201         b |= ((uint64_t)ctx->leavings[2]) << 16;
202     case 2:
203         b |= ((uint64_t)ctx->leavings[1]) <<  8;
204     case 1:
205         b |= ((uint64_t)ctx->leavings[0]);
206     case 0:
207         break;
208     }
209
210     v3 ^= b;
211     for (i = 0; i < ctx->crounds; ++i)
212         SIPROUND;
213     v0 ^= b;
214     if (ctx->hash_size == SIPHASH_MAX_DIGEST_SIZE)
215         v2 ^= 0xee;
216     else
217         v2 ^= 0xff;
218     for (i = 0; i < ctx->drounds; ++i)
219         SIPROUND;
220     b = v0 ^ v1 ^ v2  ^ v3;
221     U64TO8_LE(out, b);
222     if (ctx->hash_size == SIPHASH_MIN_DIGEST_SIZE)
223         return 1;
224     v1 ^= 0xdd;
225     for (i = 0; i < ctx->drounds; ++i)
226         SIPROUND;
227     b = v0 ^ v1 ^ v2  ^ v3;
228     U64TO8_LE(out + 8, b);
229     return 1;
230 }