xref: /openssl/crypto/siphash/siphash.c (revision fecb3aae)
1 /*
2  * Copyright 2017-2022 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 
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 
SipHash_ctx_size(void)68 size_t SipHash_ctx_size(void)
69 {
70     return sizeof(SIPHASH);
71 }
72 
SipHash_hash_size(SIPHASH * ctx)73 size_t SipHash_hash_size(SIPHASH *ctx)
74 {
75     return ctx->hash_size;
76 }
77 
siphash_adjust_hash_size(size_t hash_size)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 
SipHash_set_hash_size(SIPHASH * ctx,size_t hash_size)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 
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     }
105     return 1;
106 }
107 
108 /* hash_size = crounds = drounds = 0 means SipHash24 with 16-byte output */
SipHash_Init(SIPHASH * ctx,const unsigned char * k,int crounds,int drounds)109 int SipHash_Init(SIPHASH *ctx, const unsigned char *k, int crounds, int drounds)
110 {
111     uint64_t k0 = U8TO64_LE(k);
112     uint64_t k1 = U8TO64_LE(k + 8);
113 
114     /* If the hash size wasn't set, i.e. is zero */
115     ctx->hash_size = siphash_adjust_hash_size(ctx->hash_size);
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;
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 
SipHash_Update(SIPHASH * ctx,const unsigned char * in,size_t inlen)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;
144     unsigned int i;
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 
SipHash_Final(SIPHASH * ctx,unsigned char * out,size_t outlen)197 int SipHash_Final(SIPHASH *ctx, unsigned char *out, size_t outlen)
198 {
199     /* finalize hash */
200     unsigned int i;
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 
207     if (ctx->crounds == 0 || outlen == 0 || outlen != (size_t)ctx->hash_size)
208         return 0;
209 
210     switch (ctx->len) {
211     case 7:
212         b |= ((uint64_t)ctx->leavings[6]) << 48;
213         /* fall thru */
214     case 6:
215         b |= ((uint64_t)ctx->leavings[5]) << 40;
216         /* fall thru */
217     case 5:
218         b |= ((uint64_t)ctx->leavings[4]) << 32;
219         /* fall thru */
220     case 4:
221         b |= ((uint64_t)ctx->leavings[3]) << 24;
222         /* fall thru */
223     case 3:
224         b |= ((uint64_t)ctx->leavings[2]) << 16;
225         /* fall thru */
226     case 2:
227         b |= ((uint64_t)ctx->leavings[1]) <<  8;
228         /* fall thru */
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 }
256