hare

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

sha1.ha (5177B)


      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 Kiƫd Llaentenn <kiedtl@tilde.team>
      5 // (c) 2021 Thomas Bracht Laumann Jespersen <t@laumann.xyz>
      6 use hash;
      7 use io;
      8 use bytes;
      9 use crypto::math;
     10 use endian;
     11 
     12 // The size, in bytes, of a SHA-1 digest.
     13 export def SIZE: size = 20;
     14 
     15 export def BLOCKSIZE: size = 64;
     16 
     17 def chunk: size = 64;
     18 def init0: u32 = 0x67452301;
     19 def init1: u32 = 0xEFCDAB89;
     20 def init2: u32 = 0x98BADCFE;
     21 def init3: u32 = 0x10325476;
     22 def init4: u32 = 0xC3D2E1F0;
     23 
     24 export type state = struct {
     25 	hash::hash,
     26 	h: [5]u32,
     27 	x: [chunk]u8,
     28 	nx: size,
     29 	ln: size,
     30 };
     31 
     32 const sha1_vtable: io::vtable = io::vtable {
     33 	writer = &write,
     34 	closer = &close,
     35 	...
     36 };
     37 
     38 // Creates a [[hash::hash]] which computes a SHA-1 hash. Note that this
     39 // algorithm is no longer considered secure. Where possible, applications are
     40 // encouraged to use [[crypto::sha256]] or [[crypto::sha512]] instead. If this
     41 // function is used to hash sensitive information, the caller should call
     42 // [[hash::close]] to erase sensitive data from memory after use; if not, the
     43 // use of [[hash::close]] is optional.
     44 export fn sha1() state = {
     45 	let sha = state {
     46 		stream = &sha1_vtable,
     47 		sum = &sum,
     48 		reset = &reset,
     49 		sz = SIZE,
     50 		bsz = BLOCKSIZE,
     51 		...
     52 	};
     53 	hash::reset(&sha);
     54 	return sha;
     55 };
     56 
     57 fn write(st: *io::stream, buf: const []u8) (size | io::error) = {
     58 	let h = st: *state;
     59 	let b: []u8 = buf;
     60 	let nn = len(buf);
     61 
     62 	h.ln += nn;
     63 
     64 	if (h.nx > 0) {
     65 		// Compute how many bytes can be copied into h.x
     66 		let r = len(h.x) - h.nx;
     67 		let n = if (nn > r) r else nn;
     68 		h.x[h.nx..h.nx + n] = b[..n];
     69 		h.nx += n;
     70 		if (h.nx == chunk) {
     71 			block(h, h.x[..]);
     72 			h.nx = 0;
     73 		};
     74 		b = b[n..];
     75 	};
     76 	if (len(b) >= chunk) {
     77 		let n = len(b) & ~(chunk - 1);
     78 		block(h, b[..n]);
     79 		b = b[n..];
     80 	};
     81 	if (len(b) > 0) {
     82 		let n = len(b);
     83 		h.x[..n] = b[..n];
     84 		h.nx = n;
     85 	};
     86 	return nn;
     87 };
     88 
     89 fn reset(h: *hash::hash) void = {
     90 	let h = h: *state;
     91 	h.h[0] = init0;
     92 	h.h[1] = init1;
     93 	h.h[2] = init2;
     94 	h.h[3] = init3;
     95 	h.h[4] = init4;
     96 	h.nx = 0;
     97 	h.ln = 0;
     98 };
     99 
    100 fn sum(h: *hash::hash, buf: []u8) void = {
    101 	let h = h: *state;
    102 	let copy = *h;
    103 	let h = &copy;
    104 	defer hash::close(h);
    105 
    106 	// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
    107 	let ln = h.ln;
    108 	let tmp: [64]u8 = [0x80, 0...];
    109 	const pad = if ((ln % 64z) < 56z) 56z - ln % 64z
    110 		else 64 + 56z - ln % 64z;
    111 	write(h, tmp[..pad])!;
    112 
    113 	// Length in bits.
    114 	ln <<= 3;
    115 	endian::beputu64(tmp, ln: u64);
    116 	write(h, tmp[..8])!;
    117 
    118 	assert(h.nx == 0);
    119 
    120 	// Where we write the digest
    121 	endian::beputu32(buf[0..], h.h[0]);
    122 	endian::beputu32(buf[4..], h.h[1]);
    123 	endian::beputu32(buf[8..], h.h[2]);
    124 	endian::beputu32(buf[12..], h.h[3]);
    125 	endian::beputu32(buf[16..], h.h[4]);
    126 };
    127 
    128 let K0: u32 = 0x5A827999;
    129 let K1: u32 = 0x6ED9EBA1;
    130 let K2: u32 = 0x8F1BBCDC;
    131 let K3: u32 = 0xCA62C1D6;
    132 
    133 // A generic, pure Hare version of the SHA-1 block step
    134 fn block(h: *state, p: []u8) void = {
    135 	let w: [16]u32 = [0...];
    136 
    137 	let h0 = h.h[0];
    138 	let h1 = h.h[1];
    139 	let h2 = h.h[2];
    140 	let h3 = h.h[3];
    141 	let h4 = h.h[4];
    142 
    143 	for (len(p) >= chunk) {
    144 		for (let i = 0z; i < 16; i += 1) {
    145 			let j = i * 4;
    146 			w[i] = p[j]: u32 << 24
    147 				| p[j+1]: u32 << 16
    148 				| p[j+2]: u32 << 8
    149 				| p[j+3]: u32;
    150 		};
    151 		let a = h0;
    152 		let b = h1;
    153 		let c = h2;
    154 		let d = h3;
    155 		let e = h4;
    156 
    157 		// Each of the four 20-iteration rounds differs only in the
    158 		// computation of f and the choice of Ki for i=0..5
    159 		let i = 0z;
    160 		for (i < 16; i += 1) {
    161 			let f = (b & c) | (~b & d);
    162 			let t = math::rotl32(a, 5) + f + e + w[i & 0xf] + K0;
    163 			// The order matters here!
    164 			e = d; d = c; c = math::rotl32(b, 30); b = a; a = t;
    165 		};
    166 		for (i < 20; i += 1) {
    167 			let tmp = w[(i - 3) & 0xf]
    168 				^ w[(i - 8) & 0xf]
    169 				^ w[(i - 14) & 0xf]
    170 				^ w[i & 0xf];
    171 			w[i & 0xf] = tmp << 1 | tmp >> 31;
    172 
    173 			let f = (b & c) | (~b & d);
    174 			let t = math::rotl32(a, 5) + f + e + w[i & 0xf] + K0;
    175 			e = d; d = c; c = math::rotl32(b, 30); b = a; a = t;
    176 		};
    177 		for (i < 40; i += 1) {
    178 			let tmp = w[(i - 3) & 0xf]
    179 				^ w[(i - 8) & 0xf]
    180 				^ w[(i - 14) & 0xf]
    181 				^ w[i & 0xf];
    182 			w[i & 0xf] = tmp << 1 | tmp >> 31;
    183 
    184 			let f = b ^ c ^ d;
    185 			let t = math::rotl32(a, 5) + f + e + w[i & 0xf] + K1;
    186 			e = d; d = c; c = math::rotl32(b, 30); b = a; a = t;
    187 		};
    188 		for (i < 60; i += 1) {
    189 			let tmp = w[(i - 3) & 0xf]
    190 				^ w[(i - 8) & 0xf]
    191 				^ w[(i - 14) & 0xf]
    192 				^ w[i & 0xf];
    193 			w[i & 0xf] = tmp << 1 | tmp >> 31;
    194 
    195 			let f = ((b | c) & d) | (b & c);
    196 			let t = math::rotl32(a, 5) + f + e + w[i & 0xf] + K2;
    197 			e = d; d = c; c = math::rotl32(b, 30); b = a; a = t;
    198 		};
    199 		for (i < 80; i += 1) {
    200 			let tmp = w[(i - 3) & 0xf]
    201 				^ w[(i - 8) & 0xf]
    202 				^ w[(i - 14) & 0xf]
    203 				^ w[i & 0xf];
    204 			w[i & 0xf] = tmp << 1 | tmp >> 31;
    205 
    206 			let f = b ^ c ^ d;
    207 			let t = math::rotl32(a, 5) + f + e + w[i & 0xf] + K3;
    208 			e = d; d = c; c = math::rotl32(b, 30); b = a; a = t;
    209 		};
    210 
    211 		h0 += a;
    212 		h1 += b;
    213 		h2 += c;
    214 		h3 += d;
    215 		h4 += e;
    216 
    217 		p = p[chunk..];
    218 	};
    219 
    220 	h.h[0] = h0;
    221 	h.h[1] = h1;
    222 	h.h[2] = h2;
    223 	h.h[3] = h3;
    224 	h.h[4] = h4;
    225 };
    226 
    227 fn close(stream: *io::stream) (void | io::error) = {
    228 	let s = stream: *state;
    229 	bytes::zero((s.h[..]: *[*]u8)[..len(s.h) * size(u32)]);
    230 	bytes::zero(s.x);
    231 };