code wiki / (root) / nx_sha512_wasm.nx

nx_sha512_wasm.nx source

↩ module page · 285 lines · 12278 B

1// nx_sha512_wasm.nx -- SHA-512 (FIPS 180-4) self-contained for WAT target. 2// 3// Same algorithmic shape as SHA-256 but 64-bit words, 80 rounds, 4// different K + initial H + σ/Σ rotate amounts. NishiLang's i64 5// natively fits each word so no 32-bit masking dance. 6// 7// API for the embedder: 8// nx_sha512_one_shot(in_ptr, in_len, ctx_ptr, out_ptr) -> i64 9// in_ptr -- input bytes 10// in_len -- byte count 11// ctx_ptr -- caller-allocated >=1024-byte scratch (we use ~720) 12// out_ptr -- 64-byte digest destination 13// 14// Memory layout of ctx_ptr (caller need not zero): 15// bytes 0.. 127 : 128-byte partial block buffer (block size = 1024 bits) 16// bytes 128.. 191 : 64 bytes of H state (h0..h7 as 8x i64 LE) 17// bytes 192.. 199 : total bit length (i64) -- low 64 only; SHA-512 18// spec uses 128-bit length, but practical inputs 19// fit in 64 bits (16 EiB). We zero-pad the high 64. 20// bytes 200.. 919 : W[0..79] message schedule (80 i64s = 640 bytes) 21// 22// Verified against FIPS 180-4 + RFC 6234 test vectors. 23// 24// license_tier: INDEPENDENT_REDERIVE 25// genealogy_id: international-research-sources/nist/fips_180_4 26// lineage_id: nishi_sha512_wasm_q11 27 28const W_OFF: i64 = 200 29 30// Right-rotate 64-bit value by n bits. NishiLang i64 is two's-complement 31// 64-bit; we mask AFTER shifts to keep things explicit-positive. 32func _rotr64(x: i64, n: i64) -> i64 { 33 let nn: i64 = n & 63 34 // Unsigned shift right (no sign extend) by masking after shift_s. 35 let low_mask: i64 = (1 << (64 - nn)) - 1 // bits we're keeping after right-shift 36 let lo: i64 = (x >> nn) & low_mask 37 let hi: i64 = x << (64 - nn) 38 return lo | hi 39} 40 41// Read a 64-bit big-endian word from byte buffer at offset. 42func _be64_read(buf: *u8, off: i64) -> i64 { 43 let b0: i64 = buf[off] 44 let b1: i64 = buf[off + 1] 45 let b2: i64 = buf[off + 2] 46 let b3: i64 = buf[off + 3] 47 let b4: i64 = buf[off + 4] 48 let b5: i64 = buf[off + 5] 49 let b6: i64 = buf[off + 6] 50 let b7: i64 = buf[off + 7] 51 return (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | 52 (b4 << 24) | (b5 << 16) | (b6 << 8) | b7 53} 54 55// Write a 64-bit big-endian word to byte buffer at offset. 56func _be64_write(buf: *u8, off: i64, v: i64) -> i64 { 57 buf[off] = (v >> 56) & 0xFF 58 buf[off + 1] = (v >> 48) & 0xFF 59 buf[off + 2] = (v >> 40) & 0xFF 60 buf[off + 3] = (v >> 32) & 0xFF 61 buf[off + 4] = (v >> 24) & 0xFF 62 buf[off + 5] = (v >> 16) & 0xFF 63 buf[off + 6] = (v >> 8) & 0xFF 64 buf[off + 7] = v & 0xFF 65 return 0 66} 67 68// Read i64 limb at (base + idx*8) from a u8 buffer treated as packed i64 LE. 69func _i64_le_get(buf: *u8, idx: i64) -> i64 { 70 let off: i64 = idx * 8 71 return (buf[off] as i64) | 72 ((buf[off + 1] as i64) << 8) | 73 ((buf[off + 2] as i64) << 16) | 74 ((buf[off + 3] as i64) << 24) | 75 ((buf[off + 4] as i64) << 32) | 76 ((buf[off + 5] as i64) << 40) | 77 ((buf[off + 6] as i64) << 48) | 78 ((buf[off + 7] as i64) << 56) 79} 80func _i64_le_set(buf: *u8, idx: i64, v: i64) -> i64 { 81 let off: i64 = idx * 8 82 buf[off] = v & 0xFF 83 buf[off + 1] = (v >> 8) & 0xFF 84 buf[off + 2] = (v >> 16) & 0xFF 85 buf[off + 3] = (v >> 24) & 0xFF 86 buf[off + 4] = (v >> 32) & 0xFF 87 buf[off + 5] = (v >> 40) & 0xFF 88 buf[off + 6] = (v >> 48) & 0xFF 89 buf[off + 7] = (v >> 56) & 0xFF 90 return 0 91} 92 93// H state lives at ctx + 128; index by word 0..7. 94func _h_get(ctx: *u8, i: i64) -> i64 { 95 let off: i64 = 128 + i * 8 96 return (ctx[off] as i64) | 97 ((ctx[off + 1] as i64) << 8) | 98 ((ctx[off + 2] as i64) << 16) | 99 ((ctx[off + 3] as i64) << 24) | 100 ((ctx[off + 4] as i64) << 32) | 101 ((ctx[off + 5] as i64) << 40) | 102 ((ctx[off + 6] as i64) << 48) | 103 ((ctx[off + 7] as i64) << 56) 104} 105func _h_set(ctx: *u8, i: i64, v: i64) -> i64 { 106 let off: i64 = 128 + i * 8 107 ctx[off] = v & 0xFF 108 ctx[off + 1] = (v >> 8) & 0xFF 109 ctx[off + 2] = (v >> 16) & 0xFF 110 ctx[off + 3] = (v >> 24) & 0xFF 111 ctx[off + 4] = (v >> 32) & 0xFF 112 ctx[off + 5] = (v >> 40) & 0xFF 113 ctx[off + 6] = (v >> 48) & 0xFF 114 ctx[off + 7] = (v >> 56) & 0xFF 115 return 0 116} 117 118// K[0..79] from FIPS 180-4 §4.2.3 (first 64 bits of fractional parts 119// of cube roots of first 80 primes). 120func _sha512_k(i: i64) -> i64 { 121 if i == 0 { return 0x428a2f98d728ae22 } if i == 1 { return 0x7137449123ef65cd } 122 if i == 2 { return 0xb5c0fbcfec4d3b2f } if i == 3 { return 0xe9b5dba58189dbbc } 123 if i == 4 { return 0x3956c25bf348b538 } if i == 5 { return 0x59f111f1b605d019 } 124 if i == 6 { return 0x923f82a4af194f9b } if i == 7 { return 0xab1c5ed5da6d8118 } 125 if i == 8 { return 0xd807aa98a3030242 } if i == 9 { return 0x12835b0145706fbe } 126 if i == 10 { return 0x243185be4ee4b28c } if i == 11 { return 0x550c7dc3d5ffb4e2 } 127 if i == 12 { return 0x72be5d74f27b896f } if i == 13 { return 0x80deb1fe3b1696b1 } 128 if i == 14 { return 0x9bdc06a725c71235 } if i == 15 { return 0xc19bf174cf692694 } 129 if i == 16 { return 0xe49b69c19ef14ad2 } if i == 17 { return 0xefbe4786384f25e3 } 130 if i == 18 { return 0x0fc19dc68b8cd5b5 } if i == 19 { return 0x240ca1cc77ac9c65 } 131 if i == 20 { return 0x2de92c6f592b0275 } if i == 21 { return 0x4a7484aa6ea6e483 } 132 if i == 22 { return 0x5cb0a9dcbd41fbd4 } if i == 23 { return 0x76f988da831153b5 } 133 if i == 24 { return 0x983e5152ee66dfab } if i == 25 { return 0xa831c66d2db43210 } 134 if i == 26 { return 0xb00327c898fb213f } if i == 27 { return 0xbf597fc7beef0ee4 } 135 if i == 28 { return 0xc6e00bf33da88fc2 } if i == 29 { return 0xd5a79147930aa725 } 136 if i == 30 { return 0x06ca6351e003826f } if i == 31 { return 0x142929670a0e6e70 } 137 if i == 32 { return 0x27b70a8546d22ffc } if i == 33 { return 0x2e1b21385c26c926 } 138 if i == 34 { return 0x4d2c6dfc5ac42aed } if i == 35 { return 0x53380d139d95b3df } 139 if i == 36 { return 0x650a73548baf63de } if i == 37 { return 0x766a0abb3c77b2a8 } 140 if i == 38 { return 0x81c2c92e47edaee6 } if i == 39 { return 0x92722c851482353b } 141 if i == 40 { return 0xa2bfe8a14cf10364 } if i == 41 { return 0xa81a664bbc423001 } 142 if i == 42 { return 0xc24b8b70d0f89791 } if i == 43 { return 0xc76c51a30654be30 } 143 if i == 44 { return 0xd192e819d6ef5218 } if i == 45 { return 0xd69906245565a910 } 144 if i == 46 { return 0xf40e35855771202a } if i == 47 { return 0x106aa07032bbd1b8 } 145 if i == 48 { return 0x19a4c116b8d2d0c8 } if i == 49 { return 0x1e376c085141ab53 } 146 if i == 50 { return 0x2748774cdf8eeb99 } if i == 51 { return 0x34b0bcb5e19b48a8 } 147 if i == 52 { return 0x391c0cb3c5c95a63 } if i == 53 { return 0x4ed8aa4ae3418acb } 148 if i == 54 { return 0x5b9cca4f7763e373 } if i == 55 { return 0x682e6ff3d6b2b8a3 } 149 if i == 56 { return 0x748f82ee5defb2fc } if i == 57 { return 0x78a5636f43172f60 } 150 if i == 58 { return 0x84c87814a1f0ab72 } if i == 59 { return 0x8cc702081a6439ec } 151 if i == 60 { return 0x90befffa23631e28 } if i == 61 { return 0xa4506cebde82bde9 } 152 if i == 62 { return 0xbef9a3f7b2c67915 } if i == 63 { return 0xc67178f2e372532b } 153 if i == 64 { return 0xca273eceea26619c } if i == 65 { return 0xd186b8c721c0c207 } 154 if i == 66 { return 0xeada7dd6cde0eb1e } if i == 67 { return 0xf57d4f7fee6ed178 } 155 if i == 68 { return 0x06f067aa72176fba } if i == 69 { return 0x0a637dc5a2c898a6 } 156 if i == 70 { return 0x113f9804bef90dae } if i == 71 { return 0x1b710b35131c471b } 157 if i == 72 { return 0x28db77f523047d84 } if i == 73 { return 0x32caab7b40c72493 } 158 if i == 74 { return 0x3c9ebe0a15c9bebc } if i == 75 { return 0x431d67c49c100d4c } 159 if i == 76 { return 0x4cc5d4becb3e42b6 } if i == 77 { return 0x597f299cfc657e2a } 160 if i == 78 { return 0x5fcb6fab3ad6faec } 161 return 0x6c44198c4a475817 162} 163 164func _sha512_compress(ctx: *u8) -> i64 { 165 // Schedule: W[0..15] from block buffer (BE 64-bit words). 166 // W[i] for i=16..79 = σ1(W[i-2]) + W[i-7] + σ0(W[i-15]) + W[i-16] 167 // σ0(x) = ROTR(x,1) XOR ROTR(x,8) XOR SHR(x,7) 168 // σ1(x) = ROTR(x,19) XOR ROTR(x,61) XOR SHR(x,6) 169 var i: i64 = 0 170 while i < 16 { 171 let w: i64 = _be64_read(ctx, i * 8) 172 _i64_le_set(ctx, (W_OFF / 8) + i, w) 173 i = i + 1 174 } 175 i = 16 176 while i < 80 { 177 let w2: i64 = _i64_le_get(ctx, (W_OFF / 8) + i - 2) 178 let w15: i64 = _i64_le_get(ctx, (W_OFF / 8) + i - 15) 179 let w7: i64 = _i64_le_get(ctx, (W_OFF / 8) + i - 7) 180 let w16: i64 = _i64_le_get(ctx, (W_OFF / 8) + i - 16) 181 // For unsigned >> on i64, we mask the high bits ourselves. 182 let w2_shr6: i64 = (w2 >> 6) & 0x03ffffffffffffff 183 let w15_shr7: i64 = (w15 >> 7) & 0x01ffffffffffffff 184 let s0: i64 = _rotr64(w15, 1) ^ _rotr64(w15, 8) ^ w15_shr7 185 let s1: i64 = _rotr64(w2, 19) ^ _rotr64(w2, 61) ^ w2_shr6 186 let wi: i64 = w16 + s0 + w7 + s1 187 _i64_le_set(ctx, (W_OFF / 8) + i, wi) 188 i = i + 1 189 } 190 var a: i64 = _h_get(ctx, 0) 191 var b: i64 = _h_get(ctx, 1) 192 var c: i64 = _h_get(ctx, 2) 193 var d: i64 = _h_get(ctx, 3) 194 var e: i64 = _h_get(ctx, 4) 195 var f: i64 = _h_get(ctx, 5) 196 var g: i64 = _h_get(ctx, 6) 197 var h: i64 = _h_get(ctx, 7) 198 i = 0 199 while i < 80 { 200 let w: i64 = _i64_le_get(ctx, (W_OFF / 8) + i) 201 // S1(e) = ROTR(e,14) XOR ROTR(e,18) XOR ROTR(e,41) 202 let s1: i64 = _rotr64(e, 14) ^ _rotr64(e, 18) ^ _rotr64(e, 41) 203 // ch(e,f,g) = (e AND f) XOR (NOT e AND g) 204 let ch: i64 = (e & f) ^ ((~e) & g) 205 let temp1: i64 = h + s1 + ch + _sha512_k(i) + w 206 // S0(a) = ROTR(a,28) XOR ROTR(a,34) XOR ROTR(a,39) 207 let s0: i64 = _rotr64(a, 28) ^ _rotr64(a, 34) ^ _rotr64(a, 39) 208 let maj: i64 = (a & b) ^ (a & c) ^ (b & c) 209 let temp2: i64 = s0 + maj 210 h = g; g = f; f = e 211 e = d + temp1 212 d = c; c = b; b = a 213 a = temp1 + temp2 214 i = i + 1 215 } 216 _h_set(ctx, 0, _h_get(ctx, 0) + a) 217 _h_set(ctx, 1, _h_get(ctx, 1) + b) 218 _h_set(ctx, 2, _h_get(ctx, 2) + c) 219 _h_set(ctx, 3, _h_get(ctx, 3) + d) 220 _h_set(ctx, 4, _h_get(ctx, 4) + e) 221 _h_set(ctx, 5, _h_get(ctx, 5) + f) 222 _h_set(ctx, 6, _h_get(ctx, 6) + g) 223 _h_set(ctx, 7, _h_get(ctx, 7) + h) 224 return 0 225} 226 227// One-shot: hash `in_len` bytes into `out_ptr` (64 bytes). 228// Exported as `nx_sha512_one_shot`. 229func nx_sha512_one_shot(in_ptr: *u8, in_len: i64, ctx: *u8, out_ptr: *u8) -> i64 { 230 // Init H[0..7] from FIPS 180-4 §5.3.5 (first 64 bits of fractional 231 // parts of square roots of primes 2,3,5,7,11,13,17,19). 232 _h_set(ctx, 0, 0x6a09e667f3bcc908) 233 _h_set(ctx, 1, 0xbb67ae8584caa73b) 234 _h_set(ctx, 2, 0x3c6ef372fe94f82b) 235 _h_set(ctx, 3, 0xa54ff53a5f1d36f1) 236 _h_set(ctx, 4, 0x510e527fade682d1) 237 _h_set(ctx, 5, 0x9b05688c2b3e6c1f) 238 _h_set(ctx, 6, 0x1f83d9abfb41bd6b) 239 _h_set(ctx, 7, 0x5be0cd19137e2179) 240 241 var idx: i64 = 0 242 var bit_len: i64 = 0 243 var i: i64 = 0 244 while i < in_len { 245 ctx[idx] = in_ptr[i] 246 idx = idx + 1 247 bit_len = bit_len + 8 248 if idx == 128 { 249 _sha512_compress(ctx) 250 idx = 0 251 } 252 i = i + 1 253 } 254 // Padding: append 0x80, then zeros, then 128-bit big-endian length. 255 // Block size = 128 bytes; length field occupies last 16 bytes. 256 ctx[idx] = 0x80 257 idx = idx + 1 258 if idx > 112 { 259 while idx < 128 { ctx[idx] = 0; idx = idx + 1 } 260 _sha512_compress(ctx) 261 idx = 0 262 } 263 while idx < 112 { ctx[idx] = 0; idx = idx + 1 } 264 // High 64 bits of length = 0 (we don't support > 2^64 bits). 265 ctx[112] = 0; ctx[113] = 0; ctx[114] = 0; ctx[115] = 0 266 ctx[116] = 0; ctx[117] = 0; ctx[118] = 0; ctx[119] = 0 267 // Low 64 bits BE. 268 ctx[120] = (bit_len >> 56) & 0xFF 269 ctx[121] = (bit_len >> 48) & 0xFF 270 ctx[122] = (bit_len >> 40) & 0xFF 271 ctx[123] = (bit_len >> 32) & 0xFF 272 ctx[124] = (bit_len >> 24) & 0xFF 273 ctx[125] = (bit_len >> 16) & 0xFF 274 ctx[126] = (bit_len >> 8) & 0xFF 275 ctx[127] = bit_len & 0xFF 276 _sha512_compress(ctx) 277 278 // Emit H[0..7] as 8 big-endian 8-byte words = 64 bytes. 279 var k: i64 = 0 280 while k < 8 { 281 _be64_write(out_ptr, k * 8, _h_get(ctx, k)) 282 k = k + 1 283 } 284 return 0 285}