Skip to content

Commit 4a89f35

Browse files
committed
[Support] Import SipHash c reference implementation.
This brings the unmodified SipHash reference implementation: https://github.com/veorq/SipHash which has been very graciously licensed under our llvm license (Apache-2.0 WITH LLVM-exception) by Jean-Philippe Aumasson. SipHash is a lightweight hash function optimized for speed on short messages. We use it as part of the AArch64 ptrauth ABI (in arm64e and ELF PAuth) to generate discriminators based on language identifiers and mangled names. This commit brings the reference implementation (siphash.c@f26d35e) unmodified, as SipHash.cpp. Next, we will integrate it properly into libSupport, with a wrapping API suited for the ptrauth use-case.
1 parent 572b4e7 commit 4a89f35

File tree

3 files changed

+314
-0
lines changed

3 files changed

+314
-0
lines changed

llvm/lib/Support/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ endif()
127127

128128
add_subdirectory(BLAKE3)
129129

130+
# Temporarily ignore SipHash.cpp before we fully integrate it into LLVMSupport.
131+
set(LLVM_OPTIONAL_SOURCES SipHash.cpp)
132+
130133
add_llvm_component_library(LLVMSupport
131134
ABIBreak.cpp
132135
AMDGPUMetadata.cpp

llvm/lib/Support/README.md.SipHash

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# SipHash
2+
3+
[![License:
4+
CC0-1.0](https://licensebuttons.net/l/zero/1.0/80x15.png)](http://creativecommons.org/publicdomain/zero/1.0/)
5+
6+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7+
8+
9+
SipHash is a family of pseudorandom functions (PRFs) optimized for speed on short messages.
10+
This is the reference C code of SipHash: portable, simple, optimized for clarity and debugging.
11+
12+
SipHash was designed in 2012 by [Jean-Philippe Aumasson](https://aumasson.jp)
13+
and [Daniel J. Bernstein](https://cr.yp.to) as a defense against [hash-flooding
14+
DoS attacks](https://aumasson.jp/siphash/siphashdos_29c3_slides.pdf).
15+
16+
SipHash is:
17+
18+
* *Simpler and faster* on short messages than previous cryptographic
19+
algorithms, such as MACs based on universal hashing.
20+
21+
* *Competitive in performance* with insecure non-cryptographic algorithms, such as [fhhash](https://github.com/cbreeden/fxhash).
22+
23+
* *Cryptographically secure*, with no sign of weakness despite multiple [cryptanalysis](https://eprint.iacr.org/2019/865) [projects](https://eprint.iacr.org/2019/865) by leading cryptographers.
24+
25+
* *Battle-tested*, with successful integration in OSs (Linux kernel, OpenBSD,
26+
FreeBSD, FreeRTOS), languages (Perl, Python, Ruby, etc.), libraries (OpenSSL libcrypto,
27+
Sodium, etc.) and applications (Wireguard, Redis, etc.).
28+
29+
As a secure pseudorandom function (a.k.a. keyed hash function), SipHash can also be used as a secure message authentication code (MAC).
30+
But SipHash is *not a hash* in the sense of general-purpose key-less hash function such as BLAKE3 or SHA-3.
31+
SipHash should therefore always be used with a secret key in order to be secure.
32+
33+
34+
## Variants
35+
36+
The default SipHash is *SipHash-2-4*: it takes a 128-bit key, does 2 compression
37+
rounds, 4 finalization rounds, and returns a 64-bit tag.
38+
39+
Variants can use a different number of rounds. For example, we proposed *SipHash-4-8* as a conservative version.
40+
41+
The following versions are not described in the paper but were designed and analyzed to fulfill applications' needs:
42+
43+
* *SipHash-128* returns a 128-bit tag instead of 64-bit. Versions with specified number of rounds are SipHash-2-4-128, SipHash4-8-128, and so on.
44+
45+
* *HalfSipHash* works with 32-bit words instead of 64-bit, takes a 64-bit key,
46+
and returns 32-bit or 64-bit tags. For example, HalfSipHash-2-4-32 has 2
47+
compression rounds, 4 finalization rounds, and returns a 32-bit tag.
48+
49+
50+
## Security
51+
52+
(Half)SipHash-*c*-*d* with *c* ≥ 2 and *d* ≥ 4 is expected to provide the maximum PRF
53+
security for any function with the same key and output size.
54+
55+
The standard PRF security goal allow the attacker access to the output of SipHash on messages chosen adaptively by the attacker.
56+
57+
Security is limited by the key size (128 bits for SipHash), such that
58+
attackers searching 2<sup>*s*</sup> keys have chance 2<sup>*s*−128</sup> of finding
59+
the SipHash key.
60+
Security is also limited by the output size. In particular, when
61+
SipHash is used as a MAC, an attacker who blindly tries 2<sup>*s*</sup> tags will
62+
succeed with probability 2<sup>*s*-*t*</sup>, if *t* is that tag's bit size.
63+
64+
65+
## Research
66+
67+
* [Research paper](https://www.aumasson.jp/siphash/siphash.pdf) "SipHash: a fast short-input PRF" (accepted at INDOCRYPT 2012)
68+
* [Slides](https://cr.yp.to/talks/2012.12.12/slides.pdf) of the presentation of SipHash at INDOCRYPT 2012 (Bernstein)
69+
* [Slides](https://www.aumasson.jp/siphash/siphash_slides.pdf) of the presentation of SipHash at the DIAC workshop (Aumasson)
70+
71+
72+
## Usage
73+
74+
Running
75+
76+
```sh
77+
make
78+
```
79+
80+
will build tests for
81+
82+
* SipHash-2-4-64
83+
* SipHash-2-4-128
84+
* HalfSipHash-2-4-32
85+
* HalfSipHash-2-4-64
86+
87+
88+
```C
89+
./test
90+
```
91+
92+
verifies 64 test vectors, and
93+
94+
```C
95+
./debug
96+
```
97+
98+
does the same and prints intermediate values.
99+
100+
The code can be adapted to implement SipHash-*c*-*d*, the version of SipHash
101+
with *c* compression rounds and *d* finalization rounds, by defining `cROUNDS`
102+
or `dROUNDS` when compiling. This can be done with `-D` command line arguments
103+
to many compilers such as below.
104+
105+
```sh
106+
gcc -Wall --std=c99 -DcROUNDS=2 -DdROUNDS=4 siphash.c halfsiphash.c test.c -o test
107+
```
108+
109+
The `makefile` also takes *c* and *d* rounds values as parameters.
110+
111+
```sh
112+
make cROUNDS=2 dROUNDS=4
113+
```
114+
115+
Obviously, if the number of rounds is modified then the test vectors
116+
won't verify.
117+
118+
## Intellectual property
119+
120+
This code is copyright (c) 2014-2023 Jean-Philippe Aumasson, Daniel J.
121+
Bernstein. It is multi-licensed under
122+
123+
* [CC0](./LICENCE_CC0)
124+
* [MIT](./LICENSE_MIT).
125+
* [Apache 2.0 with LLVM exceptions](./LICENSE_A2LLVM).
126+

llvm/lib/Support/SipHash.cpp

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
SipHash reference C implementation
3+
4+
Copyright (c) 2012-2022 Jean-Philippe Aumasson
5+
6+
Copyright (c) 2012-2014 Daniel J. Bernstein <[email protected]>
7+
8+
To the extent possible under law, the author(s) have dedicated all copyright
9+
and related and neighboring rights to this software to the public domain
10+
worldwide. This software is distributed without any warranty.
11+
12+
You should have received a copy of the CC0 Public Domain Dedication along
13+
with
14+
this software. If not, see
15+
<http://creativecommons.org/publicdomain/zero/1.0/>.
16+
*/
17+
18+
#include "siphash.h"
19+
#include <assert.h>
20+
#include <stddef.h>
21+
#include <stdint.h>
22+
23+
/* default: SipHash-2-4 */
24+
#ifndef cROUNDS
25+
#define cROUNDS 2
26+
#endif
27+
#ifndef dROUNDS
28+
#define dROUNDS 4
29+
#endif
30+
31+
#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
32+
33+
#define U32TO8_LE(p, v) \
34+
(p)[0] = (uint8_t)((v)); \
35+
(p)[1] = (uint8_t)((v) >> 8); \
36+
(p)[2] = (uint8_t)((v) >> 16); \
37+
(p)[3] = (uint8_t)((v) >> 24);
38+
39+
#define U64TO8_LE(p, v) \
40+
U32TO8_LE((p), (uint32_t)((v))); \
41+
U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
42+
43+
#define U8TO64_LE(p) \
44+
(((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \
45+
((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
46+
((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
47+
((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
48+
49+
#define SIPROUND \
50+
do { \
51+
v0 += v1; \
52+
v1 = ROTL(v1, 13); \
53+
v1 ^= v0; \
54+
v0 = ROTL(v0, 32); \
55+
v2 += v3; \
56+
v3 = ROTL(v3, 16); \
57+
v3 ^= v2; \
58+
v0 += v3; \
59+
v3 = ROTL(v3, 21); \
60+
v3 ^= v0; \
61+
v2 += v1; \
62+
v1 = ROTL(v1, 17); \
63+
v1 ^= v2; \
64+
v2 = ROTL(v2, 32); \
65+
} while (0)
66+
67+
#ifdef DEBUG_SIPHASH
68+
#include <stdio.h>
69+
70+
#define TRACE \
71+
do { \
72+
printf("(%3zu) v0 %016" PRIx64 "\n", inlen, v0); \
73+
printf("(%3zu) v1 %016" PRIx64 "\n", inlen, v1); \
74+
printf("(%3zu) v2 %016" PRIx64 "\n", inlen, v2); \
75+
printf("(%3zu) v3 %016" PRIx64 "\n", inlen, v3); \
76+
} while (0)
77+
#else
78+
#define TRACE
79+
#endif
80+
81+
/*
82+
Computes a SipHash value
83+
*in: pointer to input data (read-only)
84+
inlen: input data length in bytes (any size_t value)
85+
*k: pointer to the key data (read-only), must be 16 bytes
86+
*out: pointer to output data (write-only), outlen bytes must be allocated
87+
outlen: length of the output in bytes, must be 8 or 16
88+
*/
89+
int siphash(const void *in, const size_t inlen, const void *k, uint8_t *out,
90+
const size_t outlen) {
91+
92+
const unsigned char *ni = (const unsigned char *)in;
93+
const unsigned char *kk = (const unsigned char *)k;
94+
95+
assert((outlen == 8) || (outlen == 16));
96+
uint64_t v0 = UINT64_C(0x736f6d6570736575);
97+
uint64_t v1 = UINT64_C(0x646f72616e646f6d);
98+
uint64_t v2 = UINT64_C(0x6c7967656e657261);
99+
uint64_t v3 = UINT64_C(0x7465646279746573);
100+
uint64_t k0 = U8TO64_LE(kk);
101+
uint64_t k1 = U8TO64_LE(kk + 8);
102+
uint64_t m;
103+
int i;
104+
const unsigned char *end = ni + inlen - (inlen % sizeof(uint64_t));
105+
const int left = inlen & 7;
106+
uint64_t b = ((uint64_t)inlen) << 56;
107+
v3 ^= k1;
108+
v2 ^= k0;
109+
v1 ^= k1;
110+
v0 ^= k0;
111+
112+
if (outlen == 16)
113+
v1 ^= 0xee;
114+
115+
for (; ni != end; ni += 8) {
116+
m = U8TO64_LE(ni);
117+
v3 ^= m;
118+
119+
TRACE;
120+
for (i = 0; i < cROUNDS; ++i)
121+
SIPROUND;
122+
123+
v0 ^= m;
124+
}
125+
126+
switch (left) {
127+
case 7:
128+
b |= ((uint64_t)ni[6]) << 48;
129+
/* FALLTHRU */
130+
case 6:
131+
b |= ((uint64_t)ni[5]) << 40;
132+
/* FALLTHRU */
133+
case 5:
134+
b |= ((uint64_t)ni[4]) << 32;
135+
/* FALLTHRU */
136+
case 4:
137+
b |= ((uint64_t)ni[3]) << 24;
138+
/* FALLTHRU */
139+
case 3:
140+
b |= ((uint64_t)ni[2]) << 16;
141+
/* FALLTHRU */
142+
case 2:
143+
b |= ((uint64_t)ni[1]) << 8;
144+
/* FALLTHRU */
145+
case 1:
146+
b |= ((uint64_t)ni[0]);
147+
break;
148+
case 0:
149+
break;
150+
}
151+
152+
v3 ^= b;
153+
154+
TRACE;
155+
for (i = 0; i < cROUNDS; ++i)
156+
SIPROUND;
157+
158+
v0 ^= b;
159+
160+
if (outlen == 16)
161+
v2 ^= 0xee;
162+
else
163+
v2 ^= 0xff;
164+
165+
TRACE;
166+
for (i = 0; i < dROUNDS; ++i)
167+
SIPROUND;
168+
169+
b = v0 ^ v1 ^ v2 ^ v3;
170+
U64TO8_LE(out, b);
171+
172+
if (outlen == 8)
173+
return 0;
174+
175+
v1 ^= 0xdd;
176+
177+
TRACE;
178+
for (i = 0; i < dROUNDS; ++i)
179+
SIPROUND;
180+
181+
b = v0 ^ v1 ^ v2 ^ v3;
182+
U64TO8_LE(out + 8, b);
183+
184+
return 0;
185+
}

0 commit comments

Comments
 (0)