1577c3f11SAhmed Bougacha //===--- SipHash.cpp - An ABI-stable string hash --------------------------===//
2577c3f11SAhmed Bougacha //
3577c3f11SAhmed Bougacha // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4577c3f11SAhmed Bougacha // See https://llvm.org/LICENSE.txt for license information.
5577c3f11SAhmed Bougacha // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6577c3f11SAhmed Bougacha //
7577c3f11SAhmed Bougacha //===----------------------------------------------------------------------===//
8*61069bd5SAhmed Bougacha //
9*61069bd5SAhmed Bougacha // This file implements an ABI-stable string hash based on SipHash, used to
10*61069bd5SAhmed Bougacha // compute ptrauth discriminators.
11*61069bd5SAhmed Bougacha //
12*61069bd5SAhmed Bougacha //===----------------------------------------------------------------------===//
13cfbed2c0SAhmed Bougacha
14577c3f11SAhmed Bougacha #include "llvm/Support/SipHash.h"
15577c3f11SAhmed Bougacha #include "llvm/ADT/ArrayRef.h"
16*61069bd5SAhmed Bougacha #include "llvm/ADT/StringExtras.h"
17*61069bd5SAhmed Bougacha #include "llvm/ADT/StringRef.h"
18577c3f11SAhmed Bougacha #include "llvm/Support/Compiler.h"
19*61069bd5SAhmed Bougacha #include "llvm/Support/Debug.h"
20577c3f11SAhmed Bougacha #include "llvm/Support/Endian.h"
21577c3f11SAhmed Bougacha #include <cstdint>
22cfbed2c0SAhmed Bougacha
23577c3f11SAhmed Bougacha using namespace llvm;
24577c3f11SAhmed Bougacha using namespace support;
25cfbed2c0SAhmed Bougacha
26*61069bd5SAhmed Bougacha #define DEBUG_TYPE "llvm-siphash"
27*61069bd5SAhmed Bougacha
28577c3f11SAhmed Bougacha // Lightly adapted from the SipHash reference C implementation:
29577c3f11SAhmed Bougacha // https://github.com/veorq/SipHash
30577c3f11SAhmed Bougacha // by Jean-Philippe Aumasson and Daniel J. Bernstein
31cfbed2c0SAhmed Bougacha
32cfbed2c0SAhmed Bougacha #define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
33cfbed2c0SAhmed Bougacha
34cfbed2c0SAhmed Bougacha #define SIPROUND \
35cfbed2c0SAhmed Bougacha do { \
36cfbed2c0SAhmed Bougacha v0 += v1; \
37cfbed2c0SAhmed Bougacha v1 = ROTL(v1, 13); \
38cfbed2c0SAhmed Bougacha v1 ^= v0; \
39cfbed2c0SAhmed Bougacha v0 = ROTL(v0, 32); \
40cfbed2c0SAhmed Bougacha v2 += v3; \
41cfbed2c0SAhmed Bougacha v3 = ROTL(v3, 16); \
42cfbed2c0SAhmed Bougacha v3 ^= v2; \
43cfbed2c0SAhmed Bougacha v0 += v3; \
44cfbed2c0SAhmed Bougacha v3 = ROTL(v3, 21); \
45cfbed2c0SAhmed Bougacha v3 ^= v0; \
46cfbed2c0SAhmed Bougacha v2 += v1; \
47cfbed2c0SAhmed Bougacha v1 = ROTL(v1, 17); \
48cfbed2c0SAhmed Bougacha v1 ^= v2; \
49cfbed2c0SAhmed Bougacha v2 = ROTL(v2, 32); \
50cfbed2c0SAhmed Bougacha } while (0)
51cfbed2c0SAhmed Bougacha
52577c3f11SAhmed Bougacha namespace {
53cfbed2c0SAhmed Bougacha
54577c3f11SAhmed Bougacha /// Computes a SipHash value
55577c3f11SAhmed Bougacha ///
56577c3f11SAhmed Bougacha /// \param in: pointer to input data (read-only)
57577c3f11SAhmed Bougacha /// \param inlen: input data length in bytes (any size_t value)
58577c3f11SAhmed Bougacha /// \param k: reference to the key data 16-byte array (read-only)
59577c3f11SAhmed Bougacha /// \returns output data, must be 8 or 16 bytes
60577c3f11SAhmed Bougacha ///
61577c3f11SAhmed Bougacha template <int cROUNDS, int dROUNDS, size_t outlen>
siphash(const unsigned char * in,uint64_t inlen,const unsigned char (& k)[16],unsigned char (& out)[outlen])62577c3f11SAhmed Bougacha void siphash(const unsigned char *in, uint64_t inlen,
63577c3f11SAhmed Bougacha const unsigned char (&k)[16], unsigned char (&out)[outlen]) {
64cfbed2c0SAhmed Bougacha
65cfbed2c0SAhmed Bougacha const unsigned char *ni = (const unsigned char *)in;
66cfbed2c0SAhmed Bougacha const unsigned char *kk = (const unsigned char *)k;
67cfbed2c0SAhmed Bougacha
68577c3f11SAhmed Bougacha static_assert(outlen == 8 || outlen == 16, "result should be 8 or 16 bytes");
69577c3f11SAhmed Bougacha
70cfbed2c0SAhmed Bougacha uint64_t v0 = UINT64_C(0x736f6d6570736575);
71cfbed2c0SAhmed Bougacha uint64_t v1 = UINT64_C(0x646f72616e646f6d);
72cfbed2c0SAhmed Bougacha uint64_t v2 = UINT64_C(0x6c7967656e657261);
73cfbed2c0SAhmed Bougacha uint64_t v3 = UINT64_C(0x7465646279746573);
74577c3f11SAhmed Bougacha uint64_t k0 = endian::read64le(kk);
75577c3f11SAhmed Bougacha uint64_t k1 = endian::read64le(kk + 8);
76cfbed2c0SAhmed Bougacha uint64_t m;
77cfbed2c0SAhmed Bougacha int i;
78cfbed2c0SAhmed Bougacha const unsigned char *end = ni + inlen - (inlen % sizeof(uint64_t));
79cfbed2c0SAhmed Bougacha const int left = inlen & 7;
80cfbed2c0SAhmed Bougacha uint64_t b = ((uint64_t)inlen) << 56;
81cfbed2c0SAhmed Bougacha v3 ^= k1;
82cfbed2c0SAhmed Bougacha v2 ^= k0;
83cfbed2c0SAhmed Bougacha v1 ^= k1;
84cfbed2c0SAhmed Bougacha v0 ^= k0;
85cfbed2c0SAhmed Bougacha
86cfbed2c0SAhmed Bougacha if (outlen == 16)
87cfbed2c0SAhmed Bougacha v1 ^= 0xee;
88cfbed2c0SAhmed Bougacha
89cfbed2c0SAhmed Bougacha for (; ni != end; ni += 8) {
90577c3f11SAhmed Bougacha m = endian::read64le(ni);
91cfbed2c0SAhmed Bougacha v3 ^= m;
92cfbed2c0SAhmed Bougacha
93cfbed2c0SAhmed Bougacha for (i = 0; i < cROUNDS; ++i)
94cfbed2c0SAhmed Bougacha SIPROUND;
95cfbed2c0SAhmed Bougacha
96cfbed2c0SAhmed Bougacha v0 ^= m;
97cfbed2c0SAhmed Bougacha }
98cfbed2c0SAhmed Bougacha
99cfbed2c0SAhmed Bougacha switch (left) {
100cfbed2c0SAhmed Bougacha case 7:
101cfbed2c0SAhmed Bougacha b |= ((uint64_t)ni[6]) << 48;
102577c3f11SAhmed Bougacha LLVM_FALLTHROUGH;
103cfbed2c0SAhmed Bougacha case 6:
104cfbed2c0SAhmed Bougacha b |= ((uint64_t)ni[5]) << 40;
105577c3f11SAhmed Bougacha LLVM_FALLTHROUGH;
106cfbed2c0SAhmed Bougacha case 5:
107cfbed2c0SAhmed Bougacha b |= ((uint64_t)ni[4]) << 32;
108577c3f11SAhmed Bougacha LLVM_FALLTHROUGH;
109cfbed2c0SAhmed Bougacha case 4:
110cfbed2c0SAhmed Bougacha b |= ((uint64_t)ni[3]) << 24;
111577c3f11SAhmed Bougacha LLVM_FALLTHROUGH;
112cfbed2c0SAhmed Bougacha case 3:
113cfbed2c0SAhmed Bougacha b |= ((uint64_t)ni[2]) << 16;
114577c3f11SAhmed Bougacha LLVM_FALLTHROUGH;
115cfbed2c0SAhmed Bougacha case 2:
116cfbed2c0SAhmed Bougacha b |= ((uint64_t)ni[1]) << 8;
117577c3f11SAhmed Bougacha LLVM_FALLTHROUGH;
118cfbed2c0SAhmed Bougacha case 1:
119cfbed2c0SAhmed Bougacha b |= ((uint64_t)ni[0]);
120cfbed2c0SAhmed Bougacha break;
121cfbed2c0SAhmed Bougacha case 0:
122cfbed2c0SAhmed Bougacha break;
123cfbed2c0SAhmed Bougacha }
124cfbed2c0SAhmed Bougacha
125cfbed2c0SAhmed Bougacha v3 ^= b;
126cfbed2c0SAhmed Bougacha
127cfbed2c0SAhmed Bougacha for (i = 0; i < cROUNDS; ++i)
128cfbed2c0SAhmed Bougacha SIPROUND;
129cfbed2c0SAhmed Bougacha
130cfbed2c0SAhmed Bougacha v0 ^= b;
131cfbed2c0SAhmed Bougacha
132cfbed2c0SAhmed Bougacha if (outlen == 16)
133cfbed2c0SAhmed Bougacha v2 ^= 0xee;
134cfbed2c0SAhmed Bougacha else
135cfbed2c0SAhmed Bougacha v2 ^= 0xff;
136cfbed2c0SAhmed Bougacha
137cfbed2c0SAhmed Bougacha for (i = 0; i < dROUNDS; ++i)
138cfbed2c0SAhmed Bougacha SIPROUND;
139cfbed2c0SAhmed Bougacha
140cfbed2c0SAhmed Bougacha b = v0 ^ v1 ^ v2 ^ v3;
141577c3f11SAhmed Bougacha endian::write64le(out, b);
142cfbed2c0SAhmed Bougacha
143cfbed2c0SAhmed Bougacha if (outlen == 8)
144577c3f11SAhmed Bougacha return;
145cfbed2c0SAhmed Bougacha
146cfbed2c0SAhmed Bougacha v1 ^= 0xdd;
147cfbed2c0SAhmed Bougacha
148cfbed2c0SAhmed Bougacha for (i = 0; i < dROUNDS; ++i)
149cfbed2c0SAhmed Bougacha SIPROUND;
150cfbed2c0SAhmed Bougacha
151cfbed2c0SAhmed Bougacha b = v0 ^ v1 ^ v2 ^ v3;
152577c3f11SAhmed Bougacha endian::write64le(out + 8, b);
153577c3f11SAhmed Bougacha }
154cfbed2c0SAhmed Bougacha
155577c3f11SAhmed Bougacha } // end anonymous namespace
156577c3f11SAhmed Bougacha
getSipHash_2_4_64(ArrayRef<uint8_t> In,const uint8_t (& K)[16],uint8_t (& Out)[8])157577c3f11SAhmed Bougacha void llvm::getSipHash_2_4_64(ArrayRef<uint8_t> In, const uint8_t (&K)[16],
158577c3f11SAhmed Bougacha uint8_t (&Out)[8]) {
159577c3f11SAhmed Bougacha siphash<2, 4>(In.data(), In.size(), K, Out);
160577c3f11SAhmed Bougacha }
161577c3f11SAhmed Bougacha
getSipHash_2_4_128(ArrayRef<uint8_t> In,const uint8_t (& K)[16],uint8_t (& Out)[16])162577c3f11SAhmed Bougacha void llvm::getSipHash_2_4_128(ArrayRef<uint8_t> In, const uint8_t (&K)[16],
163577c3f11SAhmed Bougacha uint8_t (&Out)[16]) {
164577c3f11SAhmed Bougacha siphash<2, 4>(In.data(), In.size(), K, Out);
165cfbed2c0SAhmed Bougacha }
166*61069bd5SAhmed Bougacha
167*61069bd5SAhmed Bougacha /// Compute an ABI-stable 16-bit hash of the given string.
getPointerAuthStableSipHash(StringRef Str)168*61069bd5SAhmed Bougacha uint16_t llvm::getPointerAuthStableSipHash(StringRef Str) {
169*61069bd5SAhmed Bougacha static const uint8_t K[16] = {0xb5, 0xd4, 0xc9, 0xeb, 0x79, 0x10, 0x4a, 0x79,
170*61069bd5SAhmed Bougacha 0x6f, 0xec, 0x8b, 0x1b, 0x42, 0x87, 0x81, 0xd4};
171*61069bd5SAhmed Bougacha
172*61069bd5SAhmed Bougacha uint8_t RawHashBytes[8];
173*61069bd5SAhmed Bougacha getSipHash_2_4_64(arrayRefFromStringRef(Str), K, RawHashBytes);
174*61069bd5SAhmed Bougacha uint64_t RawHash = endian::read64le(RawHashBytes);
175*61069bd5SAhmed Bougacha
176*61069bd5SAhmed Bougacha // Produce a non-zero 16-bit discriminator.
177*61069bd5SAhmed Bougacha uint16_t Discriminator = (RawHash % 0xFFFF) + 1;
178*61069bd5SAhmed Bougacha LLVM_DEBUG(
179*61069bd5SAhmed Bougacha dbgs() << "ptrauth stable hash discriminator: " << utostr(Discriminator)
180*61069bd5SAhmed Bougacha << " (0x"
181*61069bd5SAhmed Bougacha << utohexstr(Discriminator, /*Lowercase=*/false, /*Width=*/4)
182*61069bd5SAhmed Bougacha << ")"
183*61069bd5SAhmed Bougacha << " of: " << Str << "\n");
184*61069bd5SAhmed Bougacha return Discriminator;
185*61069bd5SAhmed Bougacha }
186