1 // Copyright 2015-2017 the openage authors. See copying.md for legal info.
2 
3 #include <array>
4 #include <limits.h>
5 
6 #include "hash.h"
7 #include "misc.h"
8 
9 namespace openage {
10 namespace util {
11 
hash_combine(size_t hash1,size_t hash2)12 size_t hash_combine(size_t hash1, size_t hash2) {
13 	// Taken from http://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine
14 	return hash1 ^ (hash2 + 0x9e3779b9 + ((hash1 << 6) + (hash1 >> 2)));
15 }
16 
17 
Siphash(std::array<uint8_t,16> key)18 Siphash::Siphash(std::array<uint8_t, 16> key) {
19 	set_key(key);
20 }
21 
22 
set_key(std::array<uint8_t,16> key)23 Siphash& Siphash::set_key(std::array<uint8_t, 16> key) {
24 	this->key[0] = array8_to_uint64(key.data(), 8, false);
25 	this->key[1] = array8_to_uint64(key.data() + 8, 8, false);
26 
27 	return *this;
28 }
29 
30 
31 /**
32  * Siphash implementation
33  *
34  * https://131002.net/siphash/
35  */
hash(const uint64_t key[2],const uint8_t * data,size_t len_data)36 uint64_t hash(const uint64_t key[2], const uint8_t *data, size_t len_data) {
37 
38 	static auto sipround = [](uint64_t *v) {
39 		v[0] += v[1];
40 		v[1] = rol<uint64_t, 13>(v[1]);
41 		v[1] ^= v[0];
42 		v[0] = rol<uint64_t, 32>(v[0]);
43 		v[2] += v[3];
44 		v[3] = rol<uint64_t, 16>(v[3]);
45 		v[3] ^= v[2];
46 		v[0] += v[3];
47 		v[3] = rol<uint64_t, 21>(v[3]);
48 		v[3] ^= v[0];
49 		v[2] += v[1];
50 		v[1] = rol<uint64_t, 17>(v[1]);
51 		v[1] ^= v[2];
52 		v[2] = rol<uint64_t, 32>(v[2]);
53 	};
54 
55 	// (len_data mod 256) ending to append later at the end of the data
56 	uint64_t ending = static_cast<uint64_t>(len_data & 0xff) << 56;
57 
58 	// Initialization
59 
60 	uint64_t v[4];
61 	// These values were taken from the paper: https://131002.net/siphash/siphash.pdf
62 	v[0] = key[0] ^ 0x736f6d6570736575ull;
63 	v[1] = key[1] ^ 0x646f72616e646f6dull;
64 	v[2] = key[0] ^ 0x6c7967656e657261ull;
65 	v[3] = key[1] ^ 0x7465646279746573ull;
66 
67 	// Compression
68 
69 	{
70 		size_t rem_bytes{len_data};
71 		const uint8_t *idx{data}; // Points to the start of the current block being copied
72 		uint64_t block;
73 		bool finished{false};
74 
75 		do {
76 			if (rem_bytes < 8) {
77 				block = array8_to_uint64(idx, rem_bytes, false);
78 				block |= ending;
79 				finished = true;
80 			}
81 			else {
82 				block = array8_to_uint64(idx, 8, false);
83 			}
84 
85 			v[3] ^= block;
86 			sipround(v);
87 			sipround(v);
88 			v[0] ^= block;
89 
90 			rem_bytes -= 8;
91 			idx += 8;
92 		} while (!finished);
93 	}
94 
95 	// Finalization
96 
97 	v[2] ^= 0xff;
98 	sipround(v);
99 	sipround(v);
100 	sipround(v);
101 	sipround(v);
102 
103 	return v[0] ^ v[1] ^ v[2] ^ v[3];
104 }
105 
106 
digest(const uint8_t * data,size_t count)107 uint64_t Siphash::digest(const uint8_t *data, size_t count) {
108 	return hash(key, data, count);
109 }
110 
111 
digest(uint64_t value)112 uint64_t Siphash::digest(uint64_t value) {
113 	std::vector<uint8_t> data = uint64_to_array8(value, true);
114 	return hash(key, data.data(), 8);
115 }
116 
117 
118 }} // openage::util
119