1 // Written in the D programming language. 2 3 /** 4 This class provides an implementation of the SipHash cryptographic function. 5 Implementation is based on the following code (by Károly Lőrentey): https://github.com/attaswift/SipHash/blob/master/SipHash/SipHasher.swift 6 7 Copyright: LightHouse Software, 2021 8 License: $(HTTP https://github.com/aquaratixc/ESL-License, Experimental Software License 1.0). 9 Authors: Oleg Bakharev, 10 Ilya Pertsev 11 12 See also: 13 https://131002.net/siphash, https://github.com/attaswift/SipHash/blob/master/SipHash/SipHasher.swift 14 */ 15 module styx2000.extrautil.siphash; 16 17 /** 18 The class provides functionality to initialize the internal state of the SipHash hash function and generate a hash for byte arrays. 19 Params: 20 NUMBER_OF_COMPRESS_ROUNDS = Number of compression rounds (default: 2) 21 NUMBER_OF_FINALIZATION_ROUNDS = Number of finalization rounds (default: 4) 22 */ 23 class SipHash(ubyte NUMBER_OF_COMPRESS_ROUNDS = 2, ubyte NUMBER_OF_FINALIZATION_ROUNDS = 4) 24 { 25 private 26 { 27 /// Secret key: 0 - low part of key, 1 - high of key 28 ulong key0, key1; 29 30 /// Internal state 31 ulong v0 = 0x736f6d6570736575; 32 ulong v1 = 0x646f72616e646f6d; 33 ulong v2 = 0x6c7967656e657261; 34 ulong v3 = 0x7465646279746573; 35 36 ulong pendingBytes = 0; 37 ulong pendingByteCount = 0; 38 long byteCount = 0; 39 } 40 41 private 42 { 43 /** 44 Constructs (restores) a unsigned long value (little-endian order of bytes in value) from the passed pointer to ubyte array. 45 For internal use. 46 Params: 47 ptr = Pointer to unsigned byte array. 48 49 */ 50 ulong _toLEUlong(ubyte* ptr) @system @nogc 51 { 52 ulong h = 0x0000000000000000; 53 54 h |= (cast(ulong) ptr[0]); 55 h |= (cast(ulong) ptr[1]) << 8; 56 h |= (cast(ulong) ptr[2]) << 16; 57 h |= (cast(ulong) ptr[3]) << 24; 58 h |= (cast(ulong) ptr[4]) << 32; 59 h |= (cast(ulong) ptr[5]) << 40; 60 h |= (cast(ulong) ptr[6]) << 48; 61 h |= (cast(ulong) ptr[7]) << 56; 62 63 return h; 64 } 65 66 /** 67 Rotated shift of unsigned long value. 68 For internal use. 69 Params: 70 value = Unsigned long value. 71 amount = Number of position to shift. 72 73 */ 74 ulong _rotateLeft(ulong value, ulong amount) @nogc 75 { 76 return (value << amount) | (value >> (64 - amount)); 77 } 78 79 /** 80 One SipHash round. 81 For internal use. 82 83 */ 84 void _round() @nogc 85 { 86 v0 += v1; 87 v1 = _rotateLeft(v1, 13); 88 v1 ^= v0; 89 v0 = _rotateLeft(v0, 32); 90 v2 += v3; 91 v3 = _rotateLeft(v3, 16); 92 v3 ^= v2; 93 v0 += v3; 94 v3 = _rotateLeft(v3, 21); 95 v3 ^= v0; 96 v2 += v1; 97 v1 = _rotateLeft(v1, 17); 98 v1 ^= v2; 99 v2 = _rotateLeft(v2, 32); 100 } 101 102 103 /** 104 Compress one word. 105 For internal use. 106 Params: 107 m = Unsigned long value (word for compressing). 108 109 */ 110 void _compress(ulong m) @nogc 111 { 112 v3 ^= m; 113 for (ubyte i = 0; i < NUMBER_OF_COMPRESS_ROUNDS; i++) 114 { 115 _round; 116 } 117 v0 ^= m; 118 } 119 120 121 /** 122 Finalization procedure.Needed for ending of hashing. 123 For internal use. 124 125 */ 126 ulong _finalize() @nogc 127 { 128 pendingBytes |= (cast(ulong) byteCount) << 56; 129 byteCount = -1; 130 131 _compress(pendingBytes); 132 133 v2 ^= 0xff; 134 135 for (ubyte i = 0; i < NUMBER_OF_FINALIZATION_ROUNDS; i++) 136 { 137 _round; 138 } 139 140 return v0 ^ v1 ^ v2 ^ v3; 141 } 142 } 143 144 /** 145 Default constructor. 146 Initializes the internal state of the function with a random 128-bit key (split into two 64-bit parts). 147 148 */ 149 this() 150 { 151 import std.random : Random, uniform, unpredictableSeed; 152 153 auto rng = Random(unpredictableSeed); 154 155 this(uniform(0UL, ulong.max, rng), uniform(0UL, ulong.max, rng)); 156 } 157 158 /** 159 Basic constructor. 160 Initializes the internal state with a 128-bit key, split into two 64-bit parts - the low-order and high-order parts of the key. 161 Returns: 162 key0 = low part of key. 163 key1 = high part of key. 164 165 */ 166 this(ulong key0, ulong key1) 167 { 168 key0 = key0; 169 key1 = key1; 170 171 v0 ^= key0; 172 v1 ^= key1; 173 v2 ^= key0; 174 v3 ^= key1; 175 } 176 177 /** 178 Append bytes to internal hash-function state. 179 Params: 180 buffer = Unsigned byte array. 181 182 */ 183 void append(ubyte[] buffer...) 184 { 185 import std.algorithm : min; 186 187 auto i = 0; 188 if (pendingByteCount > 0) 189 { 190 ulong readCount = min(buffer.length, 8 - pendingByteCount); 191 ulong m = 0; 192 switch (readCount) 193 { 194 case 7: 195 m |= cast(ulong)(buffer[6]) << 48; 196 goto case; 197 case 6: 198 m |= cast(ulong)(buffer[5]) << 40; 199 goto case; 200 case 5: 201 m |= cast(ulong)(buffer[4]) << 32; 202 goto case; 203 case 4: 204 m |= cast(ulong)(buffer[3]) << 24; 205 goto case; 206 case 3: 207 m |= cast(ulong)(buffer[2]) << 16; 208 goto case; 209 case 2: 210 m |= cast(ulong)(buffer[1]) << 8; 211 goto case; 212 case 1: 213 m |= cast(ulong)(buffer[0]); 214 break; 215 default: 216 break; 217 } 218 219 pendingBytes |= m << cast(ulong)(pendingByteCount << 3); 220 pendingByteCount += readCount; 221 i += readCount; 222 223 if (pendingByteCount == 8) 224 { 225 _compress(pendingBytes); 226 pendingBytes = 0; 227 pendingByteCount = 0; 228 } 229 } 230 231 ulong left = (buffer.length - i) & 7; 232 ulong end = (buffer.length - i) - left; 233 234 while (i < end) 235 { 236 ulong m = 0; 237 auto ptr = buffer[i .. i + 8].ptr; 238 _compress(_toLEUlong(ptr)); 239 i += 8; 240 } 241 242 switch (left) 243 { 244 case 7: 245 pendingBytes |= cast(ulong)(buffer[i + 6]) << 48; 246 goto case; 247 case 6: 248 pendingBytes |= cast(ulong)(buffer[i + 5]) << 40; 249 goto case; 250 case 5: 251 pendingBytes |= cast(ulong)(buffer[i + 4]) << 32; 252 goto case; 253 case 4: 254 pendingBytes |= cast(ulong)(buffer[i + 3]) << 24; 255 goto case; 256 case 3: 257 pendingBytes |= cast(ulong)(buffer[i + 2]) << 16; 258 goto case; 259 case 2: 260 pendingBytes |= cast(ulong)(buffer[i + 1]) << 8; 261 goto case; 262 case 1: 263 pendingBytes |= cast(ulong)(buffer[i]); 264 break; 265 default: 266 break; 267 } 268 269 pendingByteCount = left; 270 271 byteCount += buffer.length; 272 } 273 274 /** 275 Perform finalization of hash-function and returns hash. 276 Returns: 277 64-bit hash of passed bytes 278 279 */ 280 ulong finalize() 281 { 282 return _finalize(); 283 } 284 } 285 286 /** 287 Hashes a byte stream with the specified cryptographic key 288 Params: 289 bytes = Array of unsigned bytes. 290 key = Array of two ulong for low and high parts of 128-bit key (default: zero key) 291 292 Typical usage: 293 ---- 294 auto hash = hash8([0x65, 0x67, 0x67, 0x00]); 295 ---- 296 */ 297 auto hash8(ubyte[] bytes, ulong[2] key = [0UL, 0UL]) 298 { 299 SipHash!(2, 4) sh = new SipHash!(2, 4)(key[0], key[1]); 300 301 sh.append(bytes); 302 303 return sh.finalize; 304 } 305 306 /** 307 Hashes a string with the specified cryptographic key 308 Params: 309 string = String for hashing. 310 key = Array of two ulong for low and high parts of 128-bit key (default: zero key) 311 312 Typical usage: 313 ---- 314 auto hash = hash8(`Sample`); 315 ---- 316 */ 317 auto hash8(string s, ulong[2] key = [0UL, 0UL]) 318 { 319 SipHash!(2, 4) sh = new SipHash!(2, 4)(key[0], key[1]); 320 321 sh.append((cast(ubyte[]) s)); 322 323 return sh.finalize; 324 }