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 = © 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 };