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