aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/crypto/siphash24.c
blob: d8418944fcaf7910ec7d2146c9ecba1c2233dbce (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/* Copyright (C) 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */

#include "siphash24.h"

#include <linux/kernel.h>

#define ROTL(x,b) (u64)(((x) << (b)) | ((x) >> (64 - (b))))
#define U8TO64(p) le64_to_cpu(*(__le64 *)(p))

#define SIPROUND \
	do { \
	v0 += v1; v1 = ROTL(v1, 13); v1 ^= v0; v0 = ROTL(v0, 32); \
	v2 += v3; v3 = ROTL(v3, 16); v3 ^= v2; \
	v0 += v3; v3 = ROTL(v3, 21); v3 ^= v0; \
	v2 += v1; v1 = ROTL(v1, 17); v1 ^= v2; v2 = ROTL(v2, 32); \
	} while(0)

__attribute__((optimize("unroll-loops")))
u64 siphash24(const u8 *data, size_t len, const u8 key[SIPHASH24_KEY_LEN])
{
	u64 v0 = 0x736f6d6570736575ULL;
	u64 v1 = 0x646f72616e646f6dULL;
	u64 v2 = 0x6c7967656e657261ULL;
	u64 v3 = 0x7465646279746573ULL;
	u64 b;
	u64 k0 = U8TO64(key);
	u64 k1 = U8TO64(key + sizeof(u64));
	u64 m;
	const u8 *end = data + len - (len % sizeof(u64));
	const u8 left = len & (sizeof(u64) - 1);
	b = ((u64)len) << 56;
	v3 ^= k1;
	v2 ^= k0;
	v1 ^= k1;
	v0 ^= k0;
	for (; data != end; data += sizeof(u64)) {
		m = U8TO64(data);
		v3 ^= m;
		SIPROUND;
		SIPROUND;
		v0 ^= m;
	}
	switch (left) {
		case 7: b |= ((u64)data[6]) << 48;
		case 6: b |= ((u64)data[5]) << 40;
		case 5: b |= ((u64)data[4]) << 32;
		case 4: b |= ((u64)data[3]) << 24;
		case 3: b |= ((u64)data[2]) << 16;
		case 2: b |= ((u64)data[1]) <<  8;
		case 1: b |= ((u64)data[0]); break;
		case 0: break;
	}
	v3 ^= b;
	SIPROUND;
	SIPROUND;
	v0 ^= b;
	v2 ^= 0xff;
	SIPROUND;
	SIPROUND;
	SIPROUND;
	SIPROUND;
	b = (v0 ^ v1) ^ (v2 ^ v3);
	return (__force u64)cpu_to_le64(b);
}

#include "../selftest/siphash24.h"