hare

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

blake2b.ha (4551B)


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