hare

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

sha256.ha (5094B)


      1 // License: MPL-2.0
      2 // (c) 2022 Armin Preiml <apreiml@strohwolke.at>
      3 // (c) 2021 Drew DeVault <sir@cmpwn.com>
      4 // (c) 2021 Kiƫd Llaentenn <kiedtl@tilde.team>
      5 use crypto::math;
      6 use bytes;
      7 use endian;
      8 use hash;
      9 use io;
     10 
     11 // The size, in bytes, of a SHA-256 digest.
     12 export def SIZE: size = 32;
     13 
     14 // The internal block size.
     15 export def BLOCKSIZE: size = 64;
     16 
     17 // This is loosely based on the Go implementation
     18 def init0: u32 = 0x6A09E667;
     19 def init1: u32 = 0xBB67AE85;
     20 def init2: u32 = 0x3C6EF372;
     21 def init3: u32 = 0xA54FF53A;
     22 def init4: u32 = 0x510E527F;
     23 def init5: u32 = 0x9B05688C;
     24 def init6: u32 = 0x1F83D9AB;
     25 def init7: u32 = 0x5BE0CD19;
     26 
     27 const k: [_]u32 = [
     28 	0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
     29 	0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
     30 	0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
     31 	0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
     32 	0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
     33 	0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
     34 	0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
     35 	0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
     36 	0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
     37 	0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
     38 	0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
     39 ];
     40 
     41 export type state = struct {
     42 	hash::hash,
     43 	h: [8]u32,
     44 	x: [BLOCKSIZE]u8,
     45 	nx: size,
     46 	ln: size,
     47 };
     48 
     49 const sha256_vtable: io::vtable = io::vtable {
     50 	writer = &write,
     51 	closer = &close,
     52 	...
     53 };
     54 
     55 // Creates a [[hash::hash]] which computes a SHA-256 hash. If this function is
     56 // used to hash sensitive information, the caller should call [[hash::close]] to
     57 // erase sensitive data from memory after use; if not, the use of
     58 // [[hash::close]] is optional.
     59 export fn sha256() state = {
     60 	let sha = state {
     61 		stream = &sha256_vtable,
     62 		sum = &sum,
     63 		reset = &reset,
     64 		sz = SIZE,
     65 		bsz = BLOCKSIZE,
     66 		...
     67 	};
     68 	hash::reset(&sha);
     69 	return sha;
     70 };
     71 
     72 fn reset(h: *hash::hash) void = {
     73 	let h = h: *state;
     74 	h.h[0] = init0;
     75 	h.h[1] = init1;
     76 	h.h[2] = init2;
     77 	h.h[3] = init3;
     78 	h.h[4] = init4;
     79 	h.h[5] = init5;
     80 	h.h[6] = init6;
     81 	h.h[7] = init7;
     82 	h.nx = 0;
     83 	h.ln = 0;
     84 };
     85 
     86 fn write(st: *io::stream, buf: const []u8) (size | io::error) = {
     87 	let h = st: *state;
     88 	let b: []u8 = buf;
     89 	let n = len(b);
     90 	h.ln += n;
     91 	if (h.nx > 0) {
     92 		let n = if (len(b) > len(h.x) - h.nx) {
     93 			yield len(h.x) - h.nx;
     94 		} else len(b);
     95 		h.x[h.nx..h.nx + n] = b[..n];
     96 		h.nx += n;
     97 		if (h.nx == BLOCKSIZE) {
     98 			block(h, h.x[..]);
     99 			h.nx = 0;
    100 		};
    101 		b = b[n..];
    102 	};
    103 	if (len(b) >= BLOCKSIZE) {
    104 		let n = len(b) & ~(BLOCKSIZE - 1);
    105 		block(h, b[..n]);
    106 		b = b[n..];
    107 	};
    108 	if (len(b) > 0) {
    109 		let n = len(b);
    110 		h.x[..n] = b[..n];
    111 		h.nx = n;
    112 	};
    113 	return n;
    114 };
    115 
    116 fn sum(h: *hash::hash, buf: []u8) void = {
    117 	let h = h: *state;
    118 	let copy = *h;
    119 	let h = &copy;
    120 	defer hash::close(h);
    121 
    122 	// Add padding
    123 	let ln = h.ln;
    124 	let tmp: [64]u8 = [0...];
    125 	tmp[0] = 0x80;
    126 	const n = if ((ln % 64z) < 56z) 56z - ln % 64z
    127 		else 64z + 56z - ln % 64z;
    128 	write(h, tmp[..n])!;
    129 
    130 	ln <<= 3;
    131 	endian::beputu64(tmp, ln: u64);
    132 	write(h, tmp[..8])!;
    133 
    134 	assert(h.nx == 0);
    135 
    136 	endian::beputu32(buf[0..], h.h[0]);
    137 	endian::beputu32(buf[4..], h.h[1]);
    138 	endian::beputu32(buf[8..], h.h[2]);
    139 	endian::beputu32(buf[12..], h.h[3]);
    140 	endian::beputu32(buf[16..], h.h[4]);
    141 	endian::beputu32(buf[20..], h.h[5]);
    142 	endian::beputu32(buf[24..], h.h[6]);
    143 	endian::beputu32(buf[28..], h.h[7]);
    144 };
    145 
    146 // TODO: Rewrite me in assembly
    147 fn block(h: *state, buf: []u8) void = {
    148 	let w: [64]u32 = [0...];
    149 	let h0 = h.h[0], h1 = h.h[1], h2 = h.h[2], h3 = h.h[3],
    150 		h4 = h.h[4], h5 = h.h[5], h6 = h.h[6], h7 = h.h[7];
    151 	for (len(buf) >= BLOCKSIZE) {
    152 		for (let i = 0; i < 16; i += 1) {
    153 			let j = i * 4;
    154 			w[i] = buf[j]: u32 << 24
    155 				| buf[j+1]: u32 << 16
    156 				| buf[j+2]: u32 << 8
    157 				| buf[j+3]: u32;
    158 		};
    159 
    160 		for (let i = 16; i < 64; i += 1) {
    161 			let v1 = w[i - 2];
    162 			let t1 = (math::rotr32(v1, 17))
    163 				^ (math::rotr32(v1, 19))
    164 				^ (v1 >> 10);
    165 			let v2 = w[i - 15];
    166 			let t2 = (math::rotr32(v2, 7))
    167 				^ (math::rotr32(v2, 18))
    168 				^ (v2 >> 3);
    169 			w[i] = t1 + w[i - 7] + t2 + w[i - 16];
    170 		};
    171 
    172 		let a = h0, b = h1, c = h2, d = h3,
    173 			e = h4, f = h5, g = h6, h = h7;
    174 		for (let i = 0; i < 64; i += 1) {
    175 			let t1 = h + ((math::rotr32(e, 6))
    176 				^ (math::rotr32(e, 11))
    177 				^ (math::rotr32(e, 25)))
    178 				+ ((e & f) ^ (~e & g)) + k[i] + w[i];
    179 
    180 			let t2 = ((math::rotr32(a, 2))
    181 				^ (math::rotr32(a, 13))
    182 				^ (math::rotr32(a, 22)))
    183 				+ ((a & b) ^ (a & c) ^ (b & c));
    184 			h = g;
    185 			g = f;
    186 			f = e;
    187 			e = d + t1;
    188 			d = c;
    189 			c = b;
    190 			b = a;
    191 			a = t1 + t2;
    192 		};
    193 
    194 		h0 += a;
    195 		h1 += b;
    196 		h2 += c;
    197 		h3 += d;
    198 		h4 += e;
    199 		h5 += f;
    200 		h6 += g;
    201 		h7 += h;
    202 
    203 		buf = buf[BLOCKSIZE..];
    204 	};
    205 
    206 	h.h[0] = h0;
    207 	h.h[1] = h1;
    208 	h.h[2] = h2;
    209 	h.h[3] = h3;
    210 	h.h[4] = h4;
    211 	h.h[5] = h5;
    212 	h.h[6] = h6;
    213 	h.h[7] = h7;
    214 };
    215 
    216 fn close(stream: *io::stream) (void | io::error) = {
    217 	let s = stream: *state;
    218 	bytes::zero((s.h[..]: *[*]u8)[..len(s.h) * size(u32)]);
    219 	bytes::zero(s.x);
    220 };