siphash.ha (2821B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use crypto::math::{rotl64}; 5 use endian::{leputu64, legetu64}; 6 use hash; 7 use io; 8 9 export type state = struct { 10 hash::hash, 11 v: [4]u64, 12 x: [CHUNKSZ]u8, 13 x_len: u8, 14 c: u8, 15 d: u8, 16 ln: size, 17 }; 18 19 def CHUNKSZ: size = 8; 20 21 const sip_vtable: io::vtable = io::vtable { 22 writer = &write, 23 closer = &close, 24 ... 25 }; 26 27 // Creates a [hash::hash] that computes SipHash-c-d with the given 16 byte key, 28 // where c denotes number of compression rounds and d denotes number of 29 // finalization rounds. Recommended values for c and d are 2 and 4 respectively. 30 // Calling [[hash::close]] on this function will erase its state information. 31 // This function does not provide reset functionality and calling 32 // [[hash::reset]] on it will terminate execution. 33 export fn siphash(c: u8, d: u8, key: *[16]u8) state = { 34 let h = state { 35 stream = &sip_vtable, 36 sum = &sum64_bytes, 37 sz = CHUNKSZ, 38 c = c, 39 d = d, 40 ... 41 }; 42 let s = legetu64(key[..8]); 43 h.v[0] = 0x736f6d6570736575 ^ s; 44 h.v[2] = 0x6c7967656e657261 ^ s; 45 let s = legetu64(key[8..]); 46 h.v[1] = 0x646f72616e646f6d ^ s; 47 h.v[3] = 0x7465646279746573 ^ s; 48 return h; 49 }; 50 51 fn write(s: *io::stream, buf: const []u8) (size | io::error) = { 52 let h = s: *state; 53 h.ln += len(buf); 54 let n = CHUNKSZ - h.x_len; 55 56 if (len(buf) < n) { 57 // not enough data to fill a chunk 58 h.x[h.x_len..h.x_len + len(buf)] = buf; 59 h.x_len += len(buf): u8; 60 return len(buf); 61 }; 62 63 h.x[h.x_len..] = buf[..n]; 64 let b = legetu64(h.x); 65 h.v[3] ^= b; 66 for (let i = 0u8; i < h.c; i += 1) { 67 round(h); 68 }; 69 h.v[0] ^= b; 70 71 let buf = buf[n..]; 72 for (len(buf) >= CHUNKSZ) { 73 let b = legetu64(buf); 74 h.v[3] ^= b; 75 for (let i = 0u8; i < h.c; i += 1) { 76 round(h); 77 }; 78 h.v[0] ^= b; 79 buf = buf[CHUNKSZ..]; 80 }; 81 82 h.x_len = len(buf): u8; 83 h.x[..h.x_len] = buf; 84 return len(buf); 85 }; 86 87 // Returns the sum as a u64 88 export fn sum(h: *state) u64 = { 89 let h = *h; 90 91 for (let i = h.x_len; i < 7; i += 1) { 92 h.x[i] = 0; 93 }; 94 h.x[7] = h.ln: u8; 95 96 let b = legetu64(h.x); 97 h.v[3] ^= b; 98 for (let i = 0u8; i < h.c; i += 1) { 99 round(&h); 100 }; 101 h.v[0] ^= b; 102 103 h.v[2] ^= 0xff; 104 for (let i = 0u8; i < h.d; i += 1) { 105 round(&h); 106 }; 107 return h.v[0] ^ h.v[1] ^ h.v[2] ^ h.v[3]; 108 }; 109 110 fn sum64_bytes(h: *hash::hash, buf: []u8) void = leputu64(buf, sum(h: *state)); 111 112 fn round(h: *state) void = { 113 h.v[0] += h.v[1]; 114 h.v[1] = rotl64(h.v[1], 13); 115 h.v[1] ^= h.v[0]; 116 h.v[0] = rotl64(h.v[0], 32); 117 h.v[2] += h.v[3]; 118 h.v[3] = rotl64(h.v[3], 16); 119 h.v[3] ^= h.v[2]; 120 h.v[0] += h.v[3]; 121 h.v[3] = rotl64(h.v[3], 21); 122 h.v[3] ^= h.v[0]; 123 h.v[2] += h.v[1]; 124 h.v[1] = rotl64(h.v[1], 17); 125 h.v[1] ^= h.v[2]; 126 h.v[2] = rotl64(h.v[2], 32); 127 }; 128 129 fn close(h: *io::stream) (void | io::error) = { 130 let h = h: *state; 131 h.v = [0...]; 132 h.x = [0...]; 133 h.ln = 0; 134 h.x_len = 0; 135 };