1*0fca6ea1SDimitry Andric //===--- SipHash.cpp - An ABI-stable string hash --------------------------===// 2*0fca6ea1SDimitry Andric // 3*0fca6ea1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0fca6ea1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0fca6ea1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0fca6ea1SDimitry Andric // 7*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 8*0fca6ea1SDimitry Andric // 9*0fca6ea1SDimitry Andric // This file implements an ABI-stable string hash based on SipHash, used to 10*0fca6ea1SDimitry Andric // compute ptrauth discriminators. 11*0fca6ea1SDimitry Andric // 12*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 13*0fca6ea1SDimitry Andric 14*0fca6ea1SDimitry Andric #include "llvm/Support/SipHash.h" 15*0fca6ea1SDimitry Andric #include "llvm/ADT/ArrayRef.h" 16*0fca6ea1SDimitry Andric #include "llvm/ADT/StringExtras.h" 17*0fca6ea1SDimitry Andric #include "llvm/ADT/StringRef.h" 18*0fca6ea1SDimitry Andric #include "llvm/Support/Compiler.h" 19*0fca6ea1SDimitry Andric #include "llvm/Support/Debug.h" 20*0fca6ea1SDimitry Andric #include "llvm/Support/Endian.h" 21*0fca6ea1SDimitry Andric #include <cstdint> 22*0fca6ea1SDimitry Andric 23*0fca6ea1SDimitry Andric using namespace llvm; 24*0fca6ea1SDimitry Andric using namespace support; 25*0fca6ea1SDimitry Andric 26*0fca6ea1SDimitry Andric #define DEBUG_TYPE "llvm-siphash" 27*0fca6ea1SDimitry Andric 28*0fca6ea1SDimitry Andric // Lightly adapted from the SipHash reference C implementation: 29*0fca6ea1SDimitry Andric // https://github.com/veorq/SipHash 30*0fca6ea1SDimitry Andric // by Jean-Philippe Aumasson and Daniel J. Bernstein 31*0fca6ea1SDimitry Andric 32*0fca6ea1SDimitry Andric #define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) 33*0fca6ea1SDimitry Andric 34*0fca6ea1SDimitry Andric #define SIPROUND \ 35*0fca6ea1SDimitry Andric do { \ 36*0fca6ea1SDimitry Andric v0 += v1; \ 37*0fca6ea1SDimitry Andric v1 = ROTL(v1, 13); \ 38*0fca6ea1SDimitry Andric v1 ^= v0; \ 39*0fca6ea1SDimitry Andric v0 = ROTL(v0, 32); \ 40*0fca6ea1SDimitry Andric v2 += v3; \ 41*0fca6ea1SDimitry Andric v3 = ROTL(v3, 16); \ 42*0fca6ea1SDimitry Andric v3 ^= v2; \ 43*0fca6ea1SDimitry Andric v0 += v3; \ 44*0fca6ea1SDimitry Andric v3 = ROTL(v3, 21); \ 45*0fca6ea1SDimitry Andric v3 ^= v0; \ 46*0fca6ea1SDimitry Andric v2 += v1; \ 47*0fca6ea1SDimitry Andric v1 = ROTL(v1, 17); \ 48*0fca6ea1SDimitry Andric v1 ^= v2; \ 49*0fca6ea1SDimitry Andric v2 = ROTL(v2, 32); \ 50*0fca6ea1SDimitry Andric } while (0) 51*0fca6ea1SDimitry Andric 52*0fca6ea1SDimitry Andric namespace { 53*0fca6ea1SDimitry Andric 54*0fca6ea1SDimitry Andric /// Computes a SipHash value 55*0fca6ea1SDimitry Andric /// 56*0fca6ea1SDimitry Andric /// \param in: pointer to input data (read-only) 57*0fca6ea1SDimitry Andric /// \param inlen: input data length in bytes (any size_t value) 58*0fca6ea1SDimitry Andric /// \param k: reference to the key data 16-byte array (read-only) 59*0fca6ea1SDimitry Andric /// \returns output data, must be 8 or 16 bytes 60*0fca6ea1SDimitry Andric /// 61*0fca6ea1SDimitry Andric template <int cROUNDS, int dROUNDS, size_t outlen> 62*0fca6ea1SDimitry Andric void siphash(const unsigned char *in, uint64_t inlen, 63*0fca6ea1SDimitry Andric const unsigned char (&k)[16], unsigned char (&out)[outlen]) { 64*0fca6ea1SDimitry Andric 65*0fca6ea1SDimitry Andric const unsigned char *ni = (const unsigned char *)in; 66*0fca6ea1SDimitry Andric const unsigned char *kk = (const unsigned char *)k; 67*0fca6ea1SDimitry Andric 68*0fca6ea1SDimitry Andric static_assert(outlen == 8 || outlen == 16, "result should be 8 or 16 bytes"); 69*0fca6ea1SDimitry Andric 70*0fca6ea1SDimitry Andric uint64_t v0 = UINT64_C(0x736f6d6570736575); 71*0fca6ea1SDimitry Andric uint64_t v1 = UINT64_C(0x646f72616e646f6d); 72*0fca6ea1SDimitry Andric uint64_t v2 = UINT64_C(0x6c7967656e657261); 73*0fca6ea1SDimitry Andric uint64_t v3 = UINT64_C(0x7465646279746573); 74*0fca6ea1SDimitry Andric uint64_t k0 = endian::read64le(kk); 75*0fca6ea1SDimitry Andric uint64_t k1 = endian::read64le(kk + 8); 76*0fca6ea1SDimitry Andric uint64_t m; 77*0fca6ea1SDimitry Andric int i; 78*0fca6ea1SDimitry Andric const unsigned char *end = ni + inlen - (inlen % sizeof(uint64_t)); 79*0fca6ea1SDimitry Andric const int left = inlen & 7; 80*0fca6ea1SDimitry Andric uint64_t b = ((uint64_t)inlen) << 56; 81*0fca6ea1SDimitry Andric v3 ^= k1; 82*0fca6ea1SDimitry Andric v2 ^= k0; 83*0fca6ea1SDimitry Andric v1 ^= k1; 84*0fca6ea1SDimitry Andric v0 ^= k0; 85*0fca6ea1SDimitry Andric 86*0fca6ea1SDimitry Andric if (outlen == 16) 87*0fca6ea1SDimitry Andric v1 ^= 0xee; 88*0fca6ea1SDimitry Andric 89*0fca6ea1SDimitry Andric for (; ni != end; ni += 8) { 90*0fca6ea1SDimitry Andric m = endian::read64le(ni); 91*0fca6ea1SDimitry Andric v3 ^= m; 92*0fca6ea1SDimitry Andric 93*0fca6ea1SDimitry Andric for (i = 0; i < cROUNDS; ++i) 94*0fca6ea1SDimitry Andric SIPROUND; 95*0fca6ea1SDimitry Andric 96*0fca6ea1SDimitry Andric v0 ^= m; 97*0fca6ea1SDimitry Andric } 98*0fca6ea1SDimitry Andric 99*0fca6ea1SDimitry Andric switch (left) { 100*0fca6ea1SDimitry Andric case 7: 101*0fca6ea1SDimitry Andric b |= ((uint64_t)ni[6]) << 48; 102*0fca6ea1SDimitry Andric LLVM_FALLTHROUGH; 103*0fca6ea1SDimitry Andric case 6: 104*0fca6ea1SDimitry Andric b |= ((uint64_t)ni[5]) << 40; 105*0fca6ea1SDimitry Andric LLVM_FALLTHROUGH; 106*0fca6ea1SDimitry Andric case 5: 107*0fca6ea1SDimitry Andric b |= ((uint64_t)ni[4]) << 32; 108*0fca6ea1SDimitry Andric LLVM_FALLTHROUGH; 109*0fca6ea1SDimitry Andric case 4: 110*0fca6ea1SDimitry Andric b |= ((uint64_t)ni[3]) << 24; 111*0fca6ea1SDimitry Andric LLVM_FALLTHROUGH; 112*0fca6ea1SDimitry Andric case 3: 113*0fca6ea1SDimitry Andric b |= ((uint64_t)ni[2]) << 16; 114*0fca6ea1SDimitry Andric LLVM_FALLTHROUGH; 115*0fca6ea1SDimitry Andric case 2: 116*0fca6ea1SDimitry Andric b |= ((uint64_t)ni[1]) << 8; 117*0fca6ea1SDimitry Andric LLVM_FALLTHROUGH; 118*0fca6ea1SDimitry Andric case 1: 119*0fca6ea1SDimitry Andric b |= ((uint64_t)ni[0]); 120*0fca6ea1SDimitry Andric break; 121*0fca6ea1SDimitry Andric case 0: 122*0fca6ea1SDimitry Andric break; 123*0fca6ea1SDimitry Andric } 124*0fca6ea1SDimitry Andric 125*0fca6ea1SDimitry Andric v3 ^= b; 126*0fca6ea1SDimitry Andric 127*0fca6ea1SDimitry Andric for (i = 0; i < cROUNDS; ++i) 128*0fca6ea1SDimitry Andric SIPROUND; 129*0fca6ea1SDimitry Andric 130*0fca6ea1SDimitry Andric v0 ^= b; 131*0fca6ea1SDimitry Andric 132*0fca6ea1SDimitry Andric if (outlen == 16) 133*0fca6ea1SDimitry Andric v2 ^= 0xee; 134*0fca6ea1SDimitry Andric else 135*0fca6ea1SDimitry Andric v2 ^= 0xff; 136*0fca6ea1SDimitry Andric 137*0fca6ea1SDimitry Andric for (i = 0; i < dROUNDS; ++i) 138*0fca6ea1SDimitry Andric SIPROUND; 139*0fca6ea1SDimitry Andric 140*0fca6ea1SDimitry Andric b = v0 ^ v1 ^ v2 ^ v3; 141*0fca6ea1SDimitry Andric endian::write64le(out, b); 142*0fca6ea1SDimitry Andric 143*0fca6ea1SDimitry Andric if (outlen == 8) 144*0fca6ea1SDimitry Andric return; 145*0fca6ea1SDimitry Andric 146*0fca6ea1SDimitry Andric v1 ^= 0xdd; 147*0fca6ea1SDimitry Andric 148*0fca6ea1SDimitry Andric for (i = 0; i < dROUNDS; ++i) 149*0fca6ea1SDimitry Andric SIPROUND; 150*0fca6ea1SDimitry Andric 151*0fca6ea1SDimitry Andric b = v0 ^ v1 ^ v2 ^ v3; 152*0fca6ea1SDimitry Andric endian::write64le(out + 8, b); 153*0fca6ea1SDimitry Andric } 154*0fca6ea1SDimitry Andric 155*0fca6ea1SDimitry Andric } // end anonymous namespace 156*0fca6ea1SDimitry Andric 157*0fca6ea1SDimitry Andric void llvm::getSipHash_2_4_64(ArrayRef<uint8_t> In, const uint8_t (&K)[16], 158*0fca6ea1SDimitry Andric uint8_t (&Out)[8]) { 159*0fca6ea1SDimitry Andric siphash<2, 4>(In.data(), In.size(), K, Out); 160*0fca6ea1SDimitry Andric } 161*0fca6ea1SDimitry Andric 162*0fca6ea1SDimitry Andric void llvm::getSipHash_2_4_128(ArrayRef<uint8_t> In, const uint8_t (&K)[16], 163*0fca6ea1SDimitry Andric uint8_t (&Out)[16]) { 164*0fca6ea1SDimitry Andric siphash<2, 4>(In.data(), In.size(), K, Out); 165*0fca6ea1SDimitry Andric } 166*0fca6ea1SDimitry Andric 167*0fca6ea1SDimitry Andric /// Compute an ABI-stable 16-bit hash of the given string. 168*0fca6ea1SDimitry Andric uint16_t llvm::getPointerAuthStableSipHash(StringRef Str) { 169*0fca6ea1SDimitry Andric static const uint8_t K[16] = {0xb5, 0xd4, 0xc9, 0xeb, 0x79, 0x10, 0x4a, 0x79, 170*0fca6ea1SDimitry Andric 0x6f, 0xec, 0x8b, 0x1b, 0x42, 0x87, 0x81, 0xd4}; 171*0fca6ea1SDimitry Andric 172*0fca6ea1SDimitry Andric uint8_t RawHashBytes[8]; 173*0fca6ea1SDimitry Andric getSipHash_2_4_64(arrayRefFromStringRef(Str), K, RawHashBytes); 174*0fca6ea1SDimitry Andric uint64_t RawHash = endian::read64le(RawHashBytes); 175*0fca6ea1SDimitry Andric 176*0fca6ea1SDimitry Andric // Produce a non-zero 16-bit discriminator. 177*0fca6ea1SDimitry Andric uint16_t Discriminator = (RawHash % 0xFFFF) + 1; 178*0fca6ea1SDimitry Andric LLVM_DEBUG( 179*0fca6ea1SDimitry Andric dbgs() << "ptrauth stable hash discriminator: " << utostr(Discriminator) 180*0fca6ea1SDimitry Andric << " (0x" 181*0fca6ea1SDimitry Andric << utohexstr(Discriminator, /*Lowercase=*/false, /*Width=*/4) 182*0fca6ea1SDimitry Andric << ")" 183*0fca6ea1SDimitry Andric << " of: " << Str << "\n"); 184*0fca6ea1SDimitry Andric return Discriminator; 185*0fca6ea1SDimitry Andric } 186