hare

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

blake2b.ha (4674B)


      1 // License: MPL-2.0
      2 // (c) 2021-2022 Armin Preiml <apreiml@strohwolke.at>
      3 // (c) 2021 Drew DeVault <sir@cmpwn.com>
      4 // (c) 2021 Eyal Sawady <ecs@d2evs.net>
      5 use bytes;
      6 use crypto::math;
      7 use endian;
      8 use hash;
      9 use io;
     10 
     11 def R1: int = 32;
     12 def R2: int = 24;
     13 def R3: int = 16;
     14 def R4: int = 63;
     15 def r: u64 = 12;
     16 def BSIZE: size = 128;
     17 
     18 const iv: [8]u64 = [
     19        0x6A09E667F3BCC908, 0xBB67AE8584CAA73B,
     20        0x3C6EF372FE94F82B, 0xA54FF53A5F1D36F1,
     21        0x510E527FADE682D1, 0x9B05688C2B3E6C1F,
     22        0x1F83D9ABFB41BD6B, 0x5BE0CD19137E2179,
     23 ];
     24 
     25 const sigma: [12][16]u8 = [
     26 	[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
     27 	[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
     28 	[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
     29 	[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
     30 	[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
     31 	[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
     32 	[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
     33 	[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
     34 	[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
     35 	[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
     36 	[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
     37 	[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
     38 ];
     39 
     40 export type digest = struct {
     41 	hash::hash,
     42 	h: [8]u64,
     43 	tlow: u64,
     44 	thigh: u64,
     45 	block: [BSIZE]u8,
     46 	blocklen: size,
     47 };
     48 
     49 const blake2b_vtable: io::vtable = io::vtable {
     50 	writer = &write,
     51 	closer = &close,
     52 	...
     53 };
     54 
     55 // Creates a [[hash::hash]] which computes a BLAKE2b hash with a given key and a
     56 // given hash size. The size must be between 1 and 64, inclusive. If this
     57 // function is used to hash sensitive information, the caller should call
     58 // [[hash::close]] to erase sensitive data from memory after use; if not, the
     59 // use of [[hash::close]] is optional. This function does not provide reset
     60 // functionality and calling [[hash::reset]] on it will terminate execution.
     61 export fn blake2b(key: []u8, sz: size) digest = {
     62 	assert(1 <= sz);
     63 	assert(sz <= 64);
     64 	assert(len(key) <= 64);
     65 	let h = iv;
     66 	h[0] ^= 0x01010000 ^ (len(key) << 8) ^ sz;
     67 	let keyblock: [BSIZE]u8 = [0...];
     68 	keyblock[..len(key)] = key;
     69 	return digest {
     70 		stream = &blake2b_vtable,
     71 		sum = &sum,
     72 		sz = sz,
     73 		h = h,
     74 		tlow = 0,
     75 		thigh = 0,
     76 		block = keyblock,
     77 		blocklen = if (len(key) > 0) BSIZE else 0,
     78 		...
     79 	};
     80 };
     81 
     82 fn write(st: *io::stream, buf: const []u8) (size | io::error) = {
     83 	if (len(buf) == 0) return 0z;
     84 	let h = st: *digest;
     85 	let length = len(buf);
     86 	let buf = buf[..];
     87 
     88 	for (h.blocklen + len(buf) > BSIZE) {
     89 		const n = BSIZE - h.blocklen;
     90 		h.block[h.blocklen..h.blocklen + n] = buf[..n];
     91 		buf = buf[n..];
     92 
     93 		h.tlow += BSIZE;
     94 		if (h.tlow < n: u64) h.thigh += 1;
     95 
     96 		compress(&h.h, &h.block, h.tlow, h.thigh, false);
     97 		h.blocklen = 0;
     98 	};
     99 	h.block[h.blocklen..h.blocklen + len(buf)] = buf;
    100 	h.blocklen += len(buf);
    101 	return length;
    102 };
    103 
    104 fn sum(h: *hash::hash, buf: []u8) void = {
    105 	let h = h: *digest;
    106 	let copy = *h;
    107 	let h = &copy;
    108 	defer hash::close(h);
    109 
    110 	h.tlow += h.blocklen;
    111 	if (h.tlow < h.blocklen: u64) h.thigh += 1;
    112 
    113 	// Padding
    114 	let tmp: [BSIZE]u8 = [0...];
    115 	h.block[h.blocklen..BSIZE] = tmp[..BSIZE - h.blocklen];
    116 	h.blocklen = BSIZE;
    117 
    118 	compress(&h.h, &h.block, h.tlow, h.thigh, true);
    119 
    120 	for (let i = 0z; i < h.sz / 8; i += 1) {
    121 		endian::leputu64(buf[i * 8..i * 8 + 8], h.h[i]);
    122 	};
    123 };
    124 
    125 // Compression function F
    126 fn compress(h: *[8]u64, b: *[BSIZE]u8, tlow: u64, thigh: u64, f: bool) void = {
    127 	let v: [16]u64 = [0...];
    128 	v[..8] = h[..];
    129 	v[8..] = iv;
    130 	v[12] ^= tlow;
    131 	v[13] ^= thigh;
    132 	if (f) v[14] = ~v[14];
    133 	let m: [16]u64 = [0...];
    134 	for (let i = 0z; i < len(m); i += 1) {
    135 		m[i] = endian::legetu64(b[i * 8..i * 8 + 8]);
    136 	};
    137 	for (let i = 0u64; i < r; i += 1) {
    138 		const s = &sigma[i];
    139 		mix(&v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
    140 		mix(&v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
    141 		mix(&v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
    142 		mix(&v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
    143 
    144 		mix(&v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
    145 		mix(&v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
    146 		mix(&v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
    147 		mix(&v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
    148 	};
    149 
    150 	for (let i = 0; i < 8; i += 1) {
    151 		h[i] ^= v[i] ^ v[i + 8];
    152 	};
    153 };
    154 
    155 // Mixing function G
    156 fn mix(v: *[16]u64, a: size, b: size, c: size, d: size, x: u64, y: u64) void = {
    157 	v[a] = v[a] + v[b] + x;
    158 	v[d] = math::rotr64(v[d] ^ v[a], R1);
    159 	v[c] = v[c] + v[d];
    160 	v[b] = math::rotr64(v[b] ^ v[c], R2);
    161 	v[a] = v[a] + v[b] + y;
    162 	v[d] = math::rotr64(v[d] ^ v[a], R3);
    163 	v[c] = v[c] + v[d];
    164 	v[b] = math::rotr64(v[b] ^ v[c], R4);
    165 };
    166 
    167 fn close(stream: *io::stream) (void | io::error) = {
    168 	let s = stream: *digest;
    169 	bytes::zero((s.h[..]: *[*]u8)[..len(s.h) * size(u32)]);
    170 	bytes::zero(s.block);
    171 };