hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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 };