20daa74d32b71fd8c296295a8ed9fba69d4c7d5e
[ccan] / siphash24.c
1 /* CC0 license (public domain) - see LICENSE file for details */
2 /* Based on CC0 reference implementation:
3  * https://github.com/veorq/SipHash c03e6bbf6613243bc30788912ad4afbc0b992d47
4  */
5 #include <ccan/crypto/siphash24/siphash24.h>
6 #include <ccan/endian/endian.h>
7 #include <stdbool.h>
8 #include <assert.h>
9 #include <string.h>
10
11 /* default: SipHash-2-4 */
12 #define cROUNDS 2
13 #define dROUNDS 4
14
15 #define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
16
17 #define SIPROUND(v)                                                     \
18         do {                                                            \
19                 v[0] += v[1];                                           \
20                 v[1] = ROTL(v[1], 13);                                  \
21                 v[1] ^= v[0];                                           \
22                 v[0] = ROTL(v[0], 32);                                  \
23                 v[2] += v[3];                                           \
24                 v[3] = ROTL(v[3], 16);                                  \
25                 v[3] ^= v[2];                                           \
26                 v[0] += v[3];                                           \
27                 v[3] = ROTL(v[3], 21);                                  \
28                 v[3] ^= v[0];                                           \
29                 v[2] += v[1];                                           \
30                 v[1] = ROTL(v[1], 17);                                  \
31                 v[1] ^= v[2];                                           \
32                 v[2] = ROTL(v[2], 32);                                  \
33         } while (0)
34
35 static void invalidate_siphash24(struct siphash24_ctx *ctx)
36 {
37         ctx->bytes = -1ULL;
38 }
39
40 static void check_siphash24(struct siphash24_ctx *ctx)
41 {
42         assert(ctx->bytes != -1ULL);
43 }
44
45 static bool alignment_ok(const void *p, size_t n)
46 {
47 #if HAVE_UNALIGNED_ACCESS
48         (void)p; (void)n;
49         return true;
50 #else
51         return ((size_t)p % n == 0);
52 #endif
53 }
54
55 static void add_64bits(uint64_t v[4], uint64_t in)
56 {
57         int i;
58         uint64_t m = cpu_to_le64(in);
59         v[3] ^= m;
60
61         for (i = 0; i < cROUNDS; ++i)
62                 SIPROUND(v);
63
64         v[0] ^= m;
65 }
66
67 static void add(struct siphash24_ctx *ctx, const void *p, size_t len)
68 {
69         const unsigned char *data = p;
70         size_t bufsize = ctx->bytes % sizeof(ctx->buf.u8);
71
72         if (bufsize + len >= sizeof(ctx->buf.u8)) {
73                 // Fill the buffer, and process it.
74                 memcpy(ctx->buf.u8 + bufsize, data,
75                        sizeof(ctx->buf.u8) - bufsize);
76                 ctx->bytes += sizeof(ctx->buf.u8) - bufsize;
77                 data += sizeof(ctx->buf.u8) - bufsize;
78                 len -= sizeof(ctx->buf.u8) - bufsize;
79                 add_64bits(ctx->v, ctx->buf.u64);
80                 bufsize = 0;
81         }
82
83         while (len >= sizeof(ctx->buf.u8)) {
84                 // Process full chunks directly from the source.
85                 if (alignment_ok(data, sizeof(uint64_t)))
86                         add_64bits(ctx->v, *(const uint64_t *)data);
87                 else {
88                         memcpy(ctx->buf.u8, data, sizeof(ctx->buf));
89                         add_64bits(ctx->v, ctx->buf.u64);
90                 }
91                 ctx->bytes += sizeof(ctx->buf.u8);
92                 data += sizeof(ctx->buf.u8);
93                 len -= sizeof(ctx->buf.u8);
94         }
95             
96         if (len) {
97                 // Fill the buffer with what remains.
98                 memcpy(ctx->buf.u8 + bufsize, data, len);
99                 ctx->bytes += len;
100         }
101 }
102
103 void siphash24_init(struct siphash24_ctx *ctx, const struct siphash_seed *seed)
104 {
105         struct siphash24_ctx init = SIPHASH24_INIT(0, 0);
106         *ctx = init;
107         ctx->v[0] ^= seed->u.u64[0];
108         ctx->v[1] ^= seed->u.u64[1];
109         ctx->v[2] ^= seed->u.u64[0];
110         ctx->v[3] ^= seed->u.u64[1];
111 }
112
113 void siphash24_update(struct siphash24_ctx *ctx, const void *p, size_t size)
114 {
115         check_siphash24(ctx);
116         add(ctx, p, size);
117 }
118
119 uint64_t siphash24_done(struct siphash24_ctx *ctx)
120 {
121         uint64_t b;
122         int i;
123
124         b = ctx->bytes << 56;
125
126         switch (ctx->bytes % 8) {
127         case 7:
128                 b |= ((uint64_t)ctx->buf.u8[6]) << 48;
129         case 6:
130                 b |= ((uint64_t)ctx->buf.u8[5]) << 40;
131         case 5:
132                 b |= ((uint64_t)ctx->buf.u8[4]) << 32;
133         case 4:
134                 b |= ((uint64_t)ctx->buf.u8[3]) << 24;
135         case 3:
136                 b |= ((uint64_t)ctx->buf.u8[2]) << 16;
137         case 2:
138                 b |= ((uint64_t)ctx->buf.u8[1]) << 8;
139         case 1:
140                 b |= ((uint64_t)ctx->buf.u8[0]);
141                 break;
142         case 0:
143                 break;
144         }
145
146         ctx->v[3] ^= b;
147
148         for (i = 0; i < cROUNDS; ++i)
149                 SIPROUND(ctx->v);
150
151         ctx->v[0] ^= b;
152
153         ctx->v[2] ^= 0xff;
154
155         for (i = 0; i < dROUNDS; ++i)
156                 SIPROUND(ctx->v);
157
158         b = ctx->v[0] ^ ctx->v[1] ^ ctx->v[2] ^ ctx->v[3];
159
160         invalidate_siphash24(ctx);
161         return cpu_to_le64(b);
162 }
163
164 uint64_t siphash24(const struct siphash_seed *seed, const void *p, size_t size)
165 {
166         struct siphash24_ctx ctx;
167
168         siphash24_init(&ctx, seed);
169         siphash24_update(&ctx, p, size);
170         return siphash24_done(&ctx);
171 }
172         
173 void siphash24_u8(struct siphash24_ctx *ctx, uint8_t v)
174 {
175         siphash24_update(ctx, &v, sizeof(v));
176 }
177
178 void siphash24_u16(struct siphash24_ctx *ctx, uint16_t v)
179 {
180         siphash24_update(ctx, &v, sizeof(v));
181 }
182
183 void siphash24_u32(struct siphash24_ctx *ctx, uint32_t v)
184 {
185         siphash24_update(ctx, &v, sizeof(v));
186 }
187
188 void siphash24_u64(struct siphash24_ctx *ctx, uint64_t v)
189 {
190         siphash24_update(ctx, &v, sizeof(v));
191 }
192
193 /* Add as little-endian */
194 void siphash24_le16(struct siphash24_ctx *ctx, uint16_t v)
195 {
196         leint16_t lev = cpu_to_le16(v);
197         siphash24_update(ctx, &lev, sizeof(lev));
198 }
199         
200 void siphash24_le32(struct siphash24_ctx *ctx, uint32_t v)
201 {
202         leint32_t lev = cpu_to_le32(v);
203         siphash24_update(ctx, &lev, sizeof(lev));
204 }
205         
206 void siphash24_le64(struct siphash24_ctx *ctx, uint64_t v)
207 {
208         leint64_t lev = cpu_to_le64(v);
209         siphash24_update(ctx, &lev, sizeof(lev));
210 }
211
212 /* Add as big-endian */
213 void siphash24_be16(struct siphash24_ctx *ctx, uint16_t v)
214 {
215         beint16_t bev = cpu_to_be16(v);
216         siphash24_update(ctx, &bev, sizeof(bev));
217 }
218         
219 void siphash24_be32(struct siphash24_ctx *ctx, uint32_t v)
220 {
221         beint32_t bev = cpu_to_be32(v);
222         siphash24_update(ctx, &bev, sizeof(bev));
223 }
224         
225 void siphash24_be64(struct siphash24_ctx *ctx, uint64_t v)
226 {
227         beint64_t bev = cpu_to_be64(v);
228         siphash24_update(ctx, &bev, sizeof(bev));
229 }