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