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