hare

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

sha1.ha (5058B)


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