hare

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

sha512.ha (9466B)


      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 export type variant = enum {
     11 	SHA384,
     12 	SHA512,
     13 	SHA512_224,
     14 	SHA512_256,
     15 };
     16 
     17 // The size, in bytes, of a SHA-512 checksum.
     18 export def SZ: size = 64;
     19 
     20 // The size, in bytes, of a SHA-512/224 checksum.
     21 export def SZ224: size = 28;
     22 
     23 // The size, in bytes, of a SHA-512/256 checksum.
     24 export def SZ256: size = 32;
     25 
     26 // The size, in bytes, of a SHA-384 checksum.
     27 export def SZ384: size = 48;
     28 
     29 // The internal block size.
     30 export def BLOCKSZ: size = 128;
     31 
     32 def chunk: size = BLOCKSZ;
     33 def init0: u64 = 0x6a09e667f3bcc908;
     34 def init1: u64 = 0xbb67ae8584caa73b;
     35 def init2: u64 = 0x3c6ef372fe94f82b;
     36 def init3: u64 = 0xa54ff53a5f1d36f1;
     37 def init4: u64 = 0x510e527fade682d1;
     38 def init5: u64 = 0x9b05688c2b3e6c1f;
     39 def init6: u64 = 0x1f83d9abfb41bd6b;
     40 def init7: u64 = 0x5be0cd19137e2179;
     41 def init0_224: u64 = 0x8c3d37c819544da2;
     42 def init1_224: u64 = 0x73e1996689dcd4d6;
     43 def init2_224: u64 = 0x1dfab7ae32ff9c82;
     44 def init3_224: u64 = 0x679dd514582f9fcf;
     45 def init4_224: u64 = 0x0f6d2b697bd44da8;
     46 def init5_224: u64 = 0x77e36f7304c48942;
     47 def init6_224: u64 = 0x3f9d85a86a1d36c8;
     48 def init7_224: u64 = 0x1112e6ad91d692a1;
     49 def init0_256: u64 = 0x22312194fc2bf72c;
     50 def init1_256: u64 = 0x9f555fa3c84c64c2;
     51 def init2_256: u64 = 0x2393b86b6f53b151;
     52 def init3_256: u64 = 0x963877195940eabd;
     53 def init4_256: u64 = 0x96283ee2a88effe3;
     54 def init5_256: u64 = 0xbe5e1e2553863992;
     55 def init6_256: u64 = 0x2b0199fc2c85b8aa;
     56 def init7_256: u64 = 0x0eb72ddc81c52ca2;
     57 def init0_384: u64 = 0xcbbb9d5dc1059ed8;
     58 def init1_384: u64 = 0x629a292a367cd507;
     59 def init2_384: u64 = 0x9159015a3070dd17;
     60 def init3_384: u64 = 0x152fecd8f70e5939;
     61 def init4_384: u64 = 0x67332667ffc00b31;
     62 def init5_384: u64 = 0x8eb44a8768581511;
     63 def init6_384: u64 = 0xdb0c2e0d64f98fa7;
     64 def init7_384: u64 = 0x47b5481dbefa4fa4;
     65 
     66 export type digest = struct {
     67 	hash::hash,
     68 	h: [8]u64,
     69 	x: [chunk]u8,
     70 	nx: size,
     71 	ln: size,
     72 	var: variant,
     73 };
     74 
     75 // Creates a [[hash::hash]] which computes a SHA-512 hash. If this function is
     76 // used to hash sensitive information, the caller should call [[hash::close]] to
     77 // erase sensitive data from memory after use; if not, the use of
     78 // [[hash::close]] is optional.
     79 export fn sha512() digest = init(variant::SHA512, SZ);
     80 
     81 // Creates a [[hash::hash]] which computes a SHA-512/224 hash. If this function
     82 // is used to hash sensitive information, the caller should call [[hash::close]]
     83 // to erase sensitive data from memory after use; if not, the use of
     84 // [[hash::close]] is optional.
     85 export fn sha512_224() digest = init(variant::SHA512_224, SZ224);
     86 
     87 // Creates a [[hash::hash]] which computes a SHA-512/256 hash. If this function
     88 // is used to hash sensitive information, the caller should call [[hash::close]]
     89 // to erase sensitive data from memory after use; if not, the use of
     90 // [[hash::close]] is optional.
     91 export fn sha512_256() digest = init(variant::SHA512_256, SZ256);
     92 
     93 // Creates a [[hash::hash]] which computes a SHA-384 hash. If this function is
     94 // used to hash sensitive information, the caller should call [[hash::close]] to
     95 // erase sensitive data from memory after use; if not, the use of
     96 // [[hash::close]] is optional.
     97 export fn sha384() digest = init(variant::SHA384, SZ384);
     98 
     99 const sha512_vtable: io::vtable = io::vtable {
    100 	writer = &write,
    101 	closer = &close,
    102 	...
    103 };
    104 
    105 // Internal initialization function
    106 fn init(var: variant, sz: size) digest = {
    107 	let sha = digest {
    108 		stream = &sha512_vtable,
    109 		sum = &sum,
    110 		reset = &reset,
    111 		sz = sz,
    112 		bsz = chunk,
    113 		var = var,
    114 		...
    115 	};
    116 	hash::reset(&sha);
    117 	return sha;
    118 };
    119 
    120 fn write(st: *io::stream, buf: const []u8) (size | io::error) = {
    121 	let h = st: *digest;
    122 	let b: []u8 = buf;
    123 	let nn = len(buf);
    124 
    125 	h.ln += nn;
    126 
    127 	if (h.nx > 0) {
    128 		// Compute how many bytes can be copied into h.x
    129 		let r = len(h.x) - h.nx;
    130 		let n = if (nn > r) r else nn;
    131 		h.x[h.nx..h.nx + n] = b[..n];
    132 		h.nx += n;
    133 		if (h.nx == chunk) {
    134 			block(h, h.x[..]);
    135 			h.nx = 0;
    136 		};
    137 		b = b[n..];
    138 	};
    139 	if (len(b) >= chunk) {
    140 		let n = len(b) & ~(chunk - 1);
    141 		block(h, b[..n]);
    142 		b = b[n..];
    143 	};
    144 	if (len(b) > 0) {
    145 		let n = len(b);
    146 		h.x[..n] = b[..];
    147 		h.nx = n;
    148 	};
    149 	return nn;
    150 };
    151 
    152 fn sum(h: *hash::hash, buf: []u8) void = {
    153 	let d = h: *digest;
    154 	let copy = *d;
    155 	let d = &copy;
    156 	defer hash::close(d);
    157 
    158 	// Padding. Add a 1 bit and 0 bits until 112 bytes mod 128
    159 	let ln = d.ln;
    160 	let tmp: [chunk]u8 = [0x80, 0...];
    161 	if ((ln % 128) < 112) {
    162 		const n = 112 - (ln % 128);
    163 		write(d, tmp[..n])!;
    164 	} else {
    165 		const n = 128 + 112 - (ln % 128);
    166 		write(d, tmp[..n])!;
    167 	};
    168 
    169 	// Length in bits
    170 	ln <<= 3;
    171 	endian::beputu64(tmp, 0u64); // upper 64 bits are always zero
    172 	endian::beputu64(tmp[8..], ln : u64);
    173 	write(d, tmp[..16])!;
    174 
    175 	assert(d.nx == 0);
    176 
    177 	let dig: [SZ]u8 = [0...];
    178 	endian::beputu64(dig[0..], d.h[0]);
    179 	endian::beputu64(dig[8..], d.h[1]);
    180 	endian::beputu64(dig[16..], d.h[2]);
    181 	endian::beputu64(dig[24..], d.h[3]);
    182 	endian::beputu64(dig[32..], d.h[4]);
    183 	endian::beputu64(dig[40..], d.h[5]);
    184 	if (d.var != variant::SHA384) {
    185 		endian::beputu64(dig[48..], d.h[6]);
    186 		endian::beputu64(dig[56..], d.h[7]);
    187 	};
    188 
    189 	// We only copy the necessary bytes from fixed-size array into the
    190 	// returned slice. The size is already found in the inner hash struct.
    191 	buf[..d.sz] = dig[..d.sz];
    192 };
    193 
    194 fn reset(h: *hash::hash) void = {
    195 	let d = h: *digest;
    196 	switch (d.var) {
    197 	case variant::SHA384 =>
    198 		d.h[0] = init0_384;
    199 		d.h[1] = init1_384;
    200 		d.h[2] = init2_384;
    201 		d.h[3] = init3_384;
    202 		d.h[4] = init4_384;
    203 		d.h[5] = init5_384;
    204 		d.h[6] = init6_384;
    205 		d.h[7] = init7_384;
    206 	case variant::SHA512_224 =>
    207 		d.h[0] = init0_224;
    208 		d.h[1] = init1_224;
    209 		d.h[2] = init2_224;
    210 		d.h[3] = init3_224;
    211 		d.h[4] = init4_224;
    212 		d.h[5] = init5_224;
    213 		d.h[6] = init6_224;
    214 		d.h[7] = init7_224;
    215 	case variant::SHA512_256 =>
    216 		d.h[0] = init0_256;
    217 		d.h[1] = init1_256;
    218 		d.h[2] = init2_256;
    219 		d.h[3] = init3_256;
    220 		d.h[4] = init4_256;
    221 		d.h[5] = init5_256;
    222 		d.h[6] = init6_256;
    223 		d.h[7] = init7_256;
    224 	case =>
    225 		d.h[0] = init0;
    226 		d.h[1] = init1;
    227 		d.h[2] = init2;
    228 		d.h[3] = init3;
    229 		d.h[4] = init4;
    230 		d.h[5] = init5;
    231 		d.h[6] = init6;
    232 		d.h[7] = init7;
    233 	};
    234 	d.nx = 0;
    235 	d.ln = 0;
    236 };
    237 
    238 const k: [_]u64 = [
    239 	0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
    240 	0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
    241 	0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
    242 	0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
    243 	0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
    244 	0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
    245 	0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
    246 	0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
    247 	0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
    248 	0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
    249 	0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
    250 	0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
    251 	0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
    252 	0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
    253 	0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
    254 	0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
    255 	0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
    256 	0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
    257 	0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
    258 	0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817,
    259 ];
    260 
    261 fn block(h: *digest, p: []u8) void = {
    262 	let w: [80]u64 = [0...];
    263 
    264 	let h0 = h.h[0];
    265 	let h1 = h.h[1];
    266 	let h2 = h.h[2];
    267 	let h3 = h.h[3];
    268 	let h4 = h.h[4];
    269 	let h5 = h.h[5];
    270 	let h6 = h.h[6];
    271 	let h7 = h.h[7];
    272 
    273 	for (len(p) >= chunk; p = p[chunk..]) {
    274 		for (let i = 0z; i < 16; i += 1) {
    275 			let j = i * 8;
    276 			w[i] = p[j]: u64 << 56
    277 				| p[j+1]: u64 << 48
    278 				| p[j+2]: u64 << 40
    279 				| p[j+3]: u64 << 32
    280 				| p[j+4]: u64 << 24
    281 				| p[j+5]: u64 << 16
    282 				| p[j+6]: u64 << 8
    283 				| p[j+7]: u64;
    284 		};
    285 		for (let i = 16z; i < 80; i += 1) {
    286 			let v1 = w[i - 2];
    287 			let t1 = math::rotr64(v1, 19) ^ math::rotr64(v1, 61) ^ (v1 >> 6);
    288 			let v2 = w[i - 15];
    289 			let t2 = math::rotr64(v2, 1) ^ math::rotr64(v2, 8) ^ (v2 >> 7);
    290 
    291 			w[i] = t1 + w[i - 7] + t2 + w[i - 16];
    292 		};
    293 
    294 		let a = h0;
    295 		let b = h1;
    296 		let c = h2;
    297 		let d = h3;
    298 		let e = h4;
    299 		let f = h5;
    300 		let g = h6;
    301 		let h = h7;
    302 
    303 		for (let i = 0z; i < 80; i += 1) {
    304 			let t1 = h + (math::rotr64(e, 14)
    305 				^ math::rotr64(e, 18)
    306 				^ math::rotr64(e, 41))
    307 				+ ((e & f) ^ (~e & g))
    308 				+ k[i] + w[i];
    309 
    310 			let t2 = (math::rotr64(a, 28)
    311 				^ math::rotr64(a, 34)
    312 				^ math::rotr64(a, 39))
    313 				+ ((a & b) ^ (a & c) ^ (b & c));
    314 
    315 			h = g;
    316 			g = f;
    317 			f = e;
    318 			e = d + t1;
    319 			d = c;
    320 			c = b;
    321 			b = a;
    322 			a = t1 + t2;
    323 		};
    324 		h0 += a;
    325 		h1 += b;
    326 		h2 += c;
    327 		h3 += d;
    328 		h4 += e;
    329 		h5 += f;
    330 		h6 += g;
    331 		h7 += h;
    332 	};
    333 	h.h[0] = h0;
    334 	h.h[1] = h1;
    335 	h.h[2] = h2;
    336 	h.h[3] = h3;
    337 	h.h[4] = h4;
    338 	h.h[5] = h5;
    339 	h.h[6] = h6;
    340 	h.h[7] = h7;
    341 };
    342 
    343 fn close(stream: *io::stream) (void | io::error) = {
    344 	let s = stream: *digest;
    345 	bytes::zero((s.h[..]: *[*]u8)[..len(s.h) * size(u32)]);
    346 	bytes::zero(s.x);
    347 };