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