cwist
Modern C web framework with libttak runtime
Loading...
Searching...
No Matches
siphash.c
Go to the documentation of this file.
1#include <stdint.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <fcntl.h>
6#ifdef CWIST_BAREMETAL
7#include <cwist/sys/port.h>
8#endif
9#include <stdio.h>
10#include <string.h>
11#include <time.h>
13
19/* Left-rotate a 64-bit integer by 'b' bits */
20#ifndef ROTL
21#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
22#endif
23
28static void sipround(uint64_t *v0, uint64_t *v1, uint64_t *v2, uint64_t *v3) {
29 *v0 += *v1;
30 *v1 = ROTL(*v1, 13);
31 *v1 ^= *v0;
32 *v0 = ROTL(*v0, 32);
33 *v2 += *v3;
34 *v3 = ROTL(*v3, 16);
35 *v3 ^= *v2;
36 *v0 += *v3;
37 *v3 = ROTL(*v3, 21);
38 *v3 ^= *v0;
39 *v2 += *v1;
40 *v1 = ROTL(*v1, 17);
41 *v1 ^= *v2;
42 *v2 = ROTL(*v2, 32);
43}
44
52uint64_t siphash24(const void *src, size_t len, const uint8_t key[16]) {
53 const uint8_t *m = (const uint8_t *)src;
54 uint64_t k0, k1;
55
56 /* Load the 16-byte key into two 64-bit integers */
57 memcpy(&k0, key, 8);
58 memcpy(&k1, key + 8, 8);
59
60 /* 1. Initialization: Mix secret keys with fixed magic constants */
61 uint64_t v0 = k0 ^ 0x736f6d6570736575ULL;
62 uint64_t v1 = k1 ^ 0x646f72616e646f6dULL;
63 uint64_t v2 = k0 ^ 0x6c7967656e657261ULL;
64 uint64_t v3 = k1 ^ 0x7465646279746573ULL;
65
66 uint64_t b = ((uint64_t)len) << 56;
67 const uint8_t *end = m + (len - (len % 8));
69
70 /* 2. Compression: Process the input in 8-byte blocks */
71 for (; m < end; m += 8) {
72 memcpy(&mi, m, 8);
73 v3 ^= mi;
74 sipround(&v0, &v1, &v2, &v3); /* Round 1 */
75 sipround(&v0, &v1, &v2, &v3); /* Round 2 */
76 v0 ^= mi;
77 }
78
79 /* 3. Final Block & Padding: Handle the remaining 0-7 bytes */
80 uint64_t t = 0;
81 switch (len % 8) {
82 /* fall through */
83 case 7: t |= ((uint64_t)m[6]) << 48;
84 /* fall through */
85 case 6: t |= ((uint64_t)m[5]) << 40;
86 /* fall through */
87 case 5: t |= ((uint64_t)m[4]) << 32;
88 /* fall through */
89 case 4: t |= ((uint64_t)m[3]) << 24;
90 /* fall through */
91 case 3: t |= ((uint64_t)m[2]) << 16;
92 /* fall through */
93 case 2: t |= ((uint64_t)m[1]) << 8;
94 /* fall through */
95 case 1: t |= ((uint64_t)m[0]);
96 }
97
98 /* Mix length and final partial block into the state */
99 v3 ^= (b | t);
100 sipround(&v0, &v1, &v2, &v3);
101 sipround(&v0, &v1, &v2, &v3);
102 v0 ^= (b | t);
103
104 /* 4. Finalization: 4 additional rounds for security */
105 v2 ^= 0xff;
106 for (int i = 0; i < 4; ++i) sipround(&v0, &v1, &v2, &v3);
107
108 return v0 ^ v1 ^ v2 ^ v3;
109}
110
111static const uint8_t CHOE_ORTHO_PRIMARY[4][4] = {
112 {0, 1, 2, 3}, {1, 0, 3, 2}, {2, 3, 0, 1}, {3, 2, 1, 0}};
113
114static const uint8_t CHOE_ORTHO_SECONDARY[4][4] = {
115 {0, 1, 2, 3}, {2, 3, 0, 1}, {3, 2, 1, 0}, {1, 0, 3, 2}};
116
123static inline uint64_t rotl64(uint64_t v, unsigned int r) {
124 r &= 63U;
125 return (v << r) | (v >> ((64U - r) & 63U));
126}
127
138static void cwist_entropy_fill(uint8_t *buf, size_t len) {
139#ifdef CWIST_BAREMETAL
140 if (cwist_port_entropy(buf, len) != 0) abort();
141#else
142 size_t filled = 0;
143 int fd = open("/dev/urandom", O_RDONLY);
144 if (fd >= 0) {
145 while (filled < len) {
146 ssize_t n = read(fd, buf + filled, len - filled);
147 if (n <= 0) break;
148 filled += (size_t)n;
149 }
150 close(fd);
151 }
152 if (filled < len) {
153 struct timespec ts = {0};
155 /* WASI preview1 has no getppid(); the fallback entropy degrades to
156 * pid + clock only, which is acceptable for a last-resort mixer. */
157#if defined(CWIST_PLATFORM_NO_PROC)
158/* WASI preview1 has no getpid(); the fallback entropy degrades to pure
159 * clock mixing, which is acceptable for a last-resort mixer. */
160 uint64_t fallbacks[2] = {(uint64_t)ts.tv_nsec, (uint64_t)ts.tv_sec ^ (uint64_t)ts.tv_nsec};
161#else
162 uint64_t fallbacks[2] = {(uint64_t)ts.tv_nsec ^ (uint64_t)getpid(),
163 (uint64_t)ts.tv_sec ^ (uint64_t)getppid()};
164#endif
165 srand((unsigned int)(fallbacks[0] ^ fallbacks[1]));
166 size_t idx = 0;
167 while (filled + idx < len) {
168 uint8_t val = (uint8_t)(fallbacks[idx % 2] >> ((idx * 13) & 63));
169 val ^= (uint8_t)(rand() & 0xff);
170 buf[filled + idx] = val;
171 idx++;
172 }
173 }
174#endif
175}
176
183static uint8_t gusuryak_cell(uint8_t a, uint8_t b) {
184 uint8_t row = a & 0x3;
185 uint8_t col = b & 0x3;
186 uint8_t primary = CHOE_ORTHO_PRIMARY[row][col];
187 uint8_t secondary = CHOE_ORTHO_SECONDARY[col][row];
188 return (uint8_t)((primary << 2) | secondary);
189}
190
196static void gusuryak_mix(const uint8_t in[16], uint8_t out[16]) {
197 /* Following Choe Seok-jeong (Gusuryak), treat 16 bytes as a 4x4 Latin board. */
198 uint64_t hi = 0x9e3779b185ebca87ULL;
199 uint64_t lo = 0xc2b2ae3d27d4eb4fULL;
200 for (size_t row = 0; row < 4; ++row) {
201 for (size_t col = 0; col < 4; ++col) {
202 size_t idx = row * 4 + col;
203 uint8_t base = in[idx];
204 uint8_t partner = in[((col + 1) % 4) + ((row + 1) % 4) * 4];
205 uint8_t orth = gusuryak_cell(base, partner);
206 uint64_t delta = ((uint64_t)base << 32) | ((uint64_t)orth << 24) |
207 ((uint64_t)row << 12) | ((uint64_t)col << 4) |
208 (uint64_t)gusuryak_cell(partner, base);
209 hi ^= rotl64(delta ^ hi, (unsigned int)((row * 13 + col * 7) & 63));
210 lo += rotl64(delta + lo, (unsigned int)((row * 11 + col * 5) & 63));
211 }
212 }
213 hi ^= rotl64(lo, 17);
214 lo ^= rotl64(hi, 41);
215 memcpy(out, &hi, 8);
216 memcpy(out + 8, &lo, 8);
217}
218
224 uint8_t raw[16];
225 cwist_entropy_fill(raw, sizeof(raw));
226 gusuryak_mix(raw, key);
227 if (key[0] == 0 && key[8] == 0) {
228 /* keep RFC compatibility but avoid all-zero keys */
229 uint64_t tweak = 0xA54FF53A5F1D36F1ULL;
230 for (size_t i = 0; i < 16; ++i) {
231 key[i] ^= (uint8_t)(tweak >> ((i % 8) * 8));
232 }
233 }
234}
235
236/* Do not leak the helper macro into later bodies of the amalgamated unity
237 * build (issue #429); sqlite3.c defines its own ROTL. */
238#undef ROTL
size_t cap
Definition otel.c:56
Target capability macros shared by all CWIST sources.
Hooks a bare-metal firmware supplies to CWIST (issue #447).
int cwist_port_entropy(void *buf, size_t len)
Fill a buffer with cryptographically secure random bytes.
uint64_t siphash24(const void *src, size_t len, const uint8_t key[16])
Compute SipHash-2-4 over a byte range using a 16-byte secret key.
Definition siphash.c:52
#define ROTL(x, b)
Definition siphash.c:21
void cwist_generate_hash_seed(uint8_t key[16])
Generate a non-trivial 16-byte key suitable for SipHash table seeding.
Definition siphash.c:223