nx_hmac_sha384_wasm.nx source
↩ module page · 339 lines · 14221 B
1// nx_hmac_sha384_wasm.nx -- HMAC-SHA-384 (RFC 2104 + 4231) for WAT target.
2//
3// SHA-384 is SHA-512 with a different IV (FIPS 180-4 §5.3.4) and
4// truncation of the 8-word state to the first 6 words (384 bits = 48 B).
5// Block size remains B=128 -- so HMAC-SHA-384 follows the same outer/inner
6// construction as HMAC-SHA-512, with output truncated.
7//
8// Used in the TLS 1.3 ciphersuite TLS_AES_256_GCM_SHA384.
9//
10// API:
11// nx_hmac_sha384_one_shot(key_ptr, key_len, msg_ptr, msg_len,
12// scratch_ptr, out_ptr) -> i64
13// scratch_ptr -- >= 2048 bytes
14// out_ptr -- 48 bytes
15//
16// Verified against RFC 4231 §4 SHA-384 test cases.
17//
18// license_tier: INDEPENDENT_REDERIVE
19// genealogy_id: international-research-sources/ietf/rfc_2104 + rfc_4231
20// lineage_id: nishi_hmac_sha384_wasm_q1
21// safe_shift_audit: rotr-style (shift exponent = nn, mask exponent = 64-nn; differs -> safe)
22
23const W_OFF: i64 = 200
24const HMAC_B: i64 = 128
25
26func _rotr64(x: i64, n: i64) -> i64 {
27 let nn: i64 = n & 63
28 let low_mask: i64 = (1 << (64 - nn)) - 1
29 let lo: i64 = (x >> nn) & low_mask
30 let hi: i64 = x << (64 - nn)
31 return lo | hi
32}
33
34func _be64_read(buf: *u8, off: i64) -> i64 {
35 let b0: i64 = buf[off]
36 let b1: i64 = buf[off + 1]
37 let b2: i64 = buf[off + 2]
38 let b3: i64 = buf[off + 3]
39 let b4: i64 = buf[off + 4]
40 let b5: i64 = buf[off + 5]
41 let b6: i64 = buf[off + 6]
42 let b7: i64 = buf[off + 7]
43 return (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) |
44 (b4 << 24) | (b5 << 16) | (b6 << 8) | b7
45}
46func _be64_write(buf: *u8, off: i64, v: i64) -> i64 {
47 buf[off] = (v >> 56) & 0xFF
48 buf[off + 1] = (v >> 48) & 0xFF
49 buf[off + 2] = (v >> 40) & 0xFF
50 buf[off + 3] = (v >> 32) & 0xFF
51 buf[off + 4] = (v >> 24) & 0xFF
52 buf[off + 5] = (v >> 16) & 0xFF
53 buf[off + 6] = (v >> 8) & 0xFF
54 buf[off + 7] = v & 0xFF
55 return 0
56}
57
58func _i64_le_get(buf: *u8, idx: i64) -> i64 {
59 let off: i64 = idx * 8
60 return (buf[off] as i64) | ((buf[off + 1] as i64) << 8) |
61 ((buf[off + 2] as i64) << 16) | ((buf[off + 3] as i64) << 24) |
62 ((buf[off + 4] as i64) << 32) | ((buf[off + 5] as i64) << 40) |
63 ((buf[off + 6] as i64) << 48) | ((buf[off + 7] as i64) << 56)
64}
65func _i64_le_set(buf: *u8, idx: i64, v: i64) -> i64 {
66 let off: i64 = idx * 8
67 buf[off] = v & 0xFF
68 buf[off + 1] = (v >> 8) & 0xFF
69 buf[off + 2] = (v >> 16) & 0xFF
70 buf[off + 3] = (v >> 24) & 0xFF
71 buf[off + 4] = (v >> 32) & 0xFF
72 buf[off + 5] = (v >> 40) & 0xFF
73 buf[off + 6] = (v >> 48) & 0xFF
74 buf[off + 7] = (v >> 56) & 0xFF
75 return 0
76}
77func _h_get(ctx: *u8, i: i64) -> i64 {
78 let off: i64 = 128 + i * 8
79 return (ctx[off] as i64) | ((ctx[off + 1] as i64) << 8) |
80 ((ctx[off + 2] as i64) << 16) | ((ctx[off + 3] as i64) << 24) |
81 ((ctx[off + 4] as i64) << 32) | ((ctx[off + 5] as i64) << 40) |
82 ((ctx[off + 6] as i64) << 48) | ((ctx[off + 7] as i64) << 56)
83}
84func _h_set(ctx: *u8, i: i64, v: i64) -> i64 {
85 let off: i64 = 128 + i * 8
86 ctx[off] = v & 0xFF
87 ctx[off + 1] = (v >> 8) & 0xFF
88 ctx[off + 2] = (v >> 16) & 0xFF
89 ctx[off + 3] = (v >> 24) & 0xFF
90 ctx[off + 4] = (v >> 32) & 0xFF
91 ctx[off + 5] = (v >> 40) & 0xFF
92 ctx[off + 6] = (v >> 48) & 0xFF
93 ctx[off + 7] = (v >> 56) & 0xFF
94 return 0
95}
96
97func _sha512_k(i: i64) -> i64 {
98 if i == 0 { return 0x428a2f98d728ae22 } if i == 1 { return 0x7137449123ef65cd }
99 if i == 2 { return 0xb5c0fbcfec4d3b2f } if i == 3 { return 0xe9b5dba58189dbbc }
100 if i == 4 { return 0x3956c25bf348b538 } if i == 5 { return 0x59f111f1b605d019 }
101 if i == 6 { return 0x923f82a4af194f9b } if i == 7 { return 0xab1c5ed5da6d8118 }
102 if i == 8 { return 0xd807aa98a3030242 } if i == 9 { return 0x12835b0145706fbe }
103 if i == 10 { return 0x243185be4ee4b28c } if i == 11 { return 0x550c7dc3d5ffb4e2 }
104 if i == 12 { return 0x72be5d74f27b896f } if i == 13 { return 0x80deb1fe3b1696b1 }
105 if i == 14 { return 0x9bdc06a725c71235 } if i == 15 { return 0xc19bf174cf692694 }
106 if i == 16 { return 0xe49b69c19ef14ad2 } if i == 17 { return 0xefbe4786384f25e3 }
107 if i == 18 { return 0x0fc19dc68b8cd5b5 } if i == 19 { return 0x240ca1cc77ac9c65 }
108 if i == 20 { return 0x2de92c6f592b0275 } if i == 21 { return 0x4a7484aa6ea6e483 }
109 if i == 22 { return 0x5cb0a9dcbd41fbd4 } if i == 23 { return 0x76f988da831153b5 }
110 if i == 24 { return 0x983e5152ee66dfab } if i == 25 { return 0xa831c66d2db43210 }
111 if i == 26 { return 0xb00327c898fb213f } if i == 27 { return 0xbf597fc7beef0ee4 }
112 if i == 28 { return 0xc6e00bf33da88fc2 } if i == 29 { return 0xd5a79147930aa725 }
113 if i == 30 { return 0x06ca6351e003826f } if i == 31 { return 0x142929670a0e6e70 }
114 if i == 32 { return 0x27b70a8546d22ffc } if i == 33 { return 0x2e1b21385c26c926 }
115 if i == 34 { return 0x4d2c6dfc5ac42aed } if i == 35 { return 0x53380d139d95b3df }
116 if i == 36 { return 0x650a73548baf63de } if i == 37 { return 0x766a0abb3c77b2a8 }
117 if i == 38 { return 0x81c2c92e47edaee6 } if i == 39 { return 0x92722c851482353b }
118 if i == 40 { return 0xa2bfe8a14cf10364 } if i == 41 { return 0xa81a664bbc423001 }
119 if i == 42 { return 0xc24b8b70d0f89791 } if i == 43 { return 0xc76c51a30654be30 }
120 if i == 44 { return 0xd192e819d6ef5218 } if i == 45 { return 0xd69906245565a910 }
121 if i == 46 { return 0xf40e35855771202a } if i == 47 { return 0x106aa07032bbd1b8 }
122 if i == 48 { return 0x19a4c116b8d2d0c8 } if i == 49 { return 0x1e376c085141ab53 }
123 if i == 50 { return 0x2748774cdf8eeb99 } if i == 51 { return 0x34b0bcb5e19b48a8 }
124 if i == 52 { return 0x391c0cb3c5c95a63 } if i == 53 { return 0x4ed8aa4ae3418acb }
125 if i == 54 { return 0x5b9cca4f7763e373 } if i == 55 { return 0x682e6ff3d6b2b8a3 }
126 if i == 56 { return 0x748f82ee5defb2fc } if i == 57 { return 0x78a5636f43172f60 }
127 if i == 58 { return 0x84c87814a1f0ab72 } if i == 59 { return 0x8cc702081a6439ec }
128 if i == 60 { return 0x90befffa23631e28 } if i == 61 { return 0xa4506cebde82bde9 }
129 if i == 62 { return 0xbef9a3f7b2c67915 } if i == 63 { return 0xc67178f2e372532b }
130 if i == 64 { return 0xca273eceea26619c } if i == 65 { return 0xd186b8c721c0c207 }
131 if i == 66 { return 0xeada7dd6cde0eb1e } if i == 67 { return 0xf57d4f7fee6ed178 }
132 if i == 68 { return 0x06f067aa72176fba } if i == 69 { return 0x0a637dc5a2c898a6 }
133 if i == 70 { return 0x113f9804bef90dae } if i == 71 { return 0x1b710b35131c471b }
134 if i == 72 { return 0x28db77f523047d84 } if i == 73 { return 0x32caab7b40c72493 }
135 if i == 74 { return 0x3c9ebe0a15c9bebc } if i == 75 { return 0x431d67c49c100d4c }
136 if i == 76 { return 0x4cc5d4becb3e42b6 } if i == 77 { return 0x597f299cfc657e2a }
137 if i == 78 { return 0x5fcb6fab3ad6faec }
138 return 0x6c44198c4a475817
139}
140
141func _sha512_compress(ctx: *u8) -> i64 {
142 var i: i64 = 0
143 while i < 16 {
144 let w: i64 = _be64_read(ctx, i * 8)
145 _i64_le_set(ctx, (W_OFF / 8) + i, w)
146 i = i + 1
147 }
148 i = 16
149 while i < 80 {
150 let w2: i64 = _i64_le_get(ctx, (W_OFF / 8) + i - 2)
151 let w15: i64 = _i64_le_get(ctx, (W_OFF / 8) + i - 15)
152 let w7: i64 = _i64_le_get(ctx, (W_OFF / 8) + i - 7)
153 let w16: i64 = _i64_le_get(ctx, (W_OFF / 8) + i - 16)
154 let w2_shr6: i64 = (w2 >> 6) & 0x03ffffffffffffff
155 let w15_shr7: i64 = (w15 >> 7) & 0x01ffffffffffffff
156 let s0: i64 = _rotr64(w15, 1) ^ _rotr64(w15, 8) ^ w15_shr7
157 let s1: i64 = _rotr64(w2, 19) ^ _rotr64(w2, 61) ^ w2_shr6
158 let wi: i64 = w16 + s0 + w7 + s1
159 _i64_le_set(ctx, (W_OFF / 8) + i, wi)
160 i = i + 1
161 }
162 var a: i64 = _h_get(ctx, 0)
163 var b: i64 = _h_get(ctx, 1)
164 var c: i64 = _h_get(ctx, 2)
165 var d: i64 = _h_get(ctx, 3)
166 var e: i64 = _h_get(ctx, 4)
167 var f: i64 = _h_get(ctx, 5)
168 var g: i64 = _h_get(ctx, 6)
169 var h: i64 = _h_get(ctx, 7)
170 i = 0
171 while i < 80 {
172 let w: i64 = _i64_le_get(ctx, (W_OFF / 8) + i)
173 let s1: i64 = _rotr64(e, 14) ^ _rotr64(e, 18) ^ _rotr64(e, 41)
174 let ch: i64 = (e & f) ^ ((~e) & g)
175 let temp1: i64 = h + s1 + ch + _sha512_k(i) + w
176 let s0: i64 = _rotr64(a, 28) ^ _rotr64(a, 34) ^ _rotr64(a, 39)
177 let maj: i64 = (a & b) ^ (a & c) ^ (b & c)
178 let temp2: i64 = s0 + maj
179 h = g; g = f; f = e
180 e = d + temp1
181 d = c; c = b; b = a
182 a = temp1 + temp2
183 i = i + 1
184 }
185 _h_set(ctx, 0, _h_get(ctx, 0) + a)
186 _h_set(ctx, 1, _h_get(ctx, 1) + b)
187 _h_set(ctx, 2, _h_get(ctx, 2) + c)
188 _h_set(ctx, 3, _h_get(ctx, 3) + d)
189 _h_set(ctx, 4, _h_get(ctx, 4) + e)
190 _h_set(ctx, 5, _h_get(ctx, 5) + f)
191 _h_set(ctx, 6, _h_get(ctx, 6) + g)
192 _h_set(ctx, 7, _h_get(ctx, 7) + h)
193 return 0
194}
195
196func _sha512_init(ctx: *u8) -> i64 {
197 // SHA-384 IV per FIPS 180-4 §5.3.4 (drives this whole module into
198 // SHA-384 mode; the SHA-512 compression+pad logic below is unchanged).
199 _h_set(ctx, 0, 0xcbbb9d5dc1059ed8)
200 _h_set(ctx, 1, 0x629a292a367cd507)
201 _h_set(ctx, 2, 0x9159015a3070dd17)
202 _h_set(ctx, 3, 0x152fecd8f70e5939)
203 _h_set(ctx, 4, 0x67332667ffc00b31)
204 _h_set(ctx, 5, 0x8eb44a8768581511)
205 _h_set(ctx, 6, 0xdb0c2e0d64f98fa7)
206 _h_set(ctx, 7, 0x47b5481dbefa4fa4)
207 return 0
208}
209
210func _sha512_one_shot(in_ptr: *u8, in_len: i64, ctx: *u8, out_ptr: *u8) -> i64 {
211 _sha512_init(ctx)
212 var idx: i64 = 0
213 var bit_len: i64 = 0
214 var i: i64 = 0
215 while i < in_len {
216 ctx[idx] = in_ptr[i]
217 idx = idx + 1
218 bit_len = bit_len + 8
219 if idx == 128 {
220 _sha512_compress(ctx)
221 idx = 0
222 }
223 i = i + 1
224 }
225 ctx[idx] = 0x80
226 idx = idx + 1
227 if idx > 112 {
228 while idx < 128 { ctx[idx] = 0; idx = idx + 1 }
229 _sha512_compress(ctx)
230 idx = 0
231 }
232 while idx < 112 { ctx[idx] = 0; idx = idx + 1 }
233 ctx[112] = 0; ctx[113] = 0; ctx[114] = 0; ctx[115] = 0
234 ctx[116] = 0; ctx[117] = 0; ctx[118] = 0; ctx[119] = 0
235 ctx[120] = (bit_len >> 56) & 0xFF
236 ctx[121] = (bit_len >> 48) & 0xFF
237 ctx[122] = (bit_len >> 40) & 0xFF
238 ctx[123] = (bit_len >> 32) & 0xFF
239 ctx[124] = (bit_len >> 24) & 0xFF
240 ctx[125] = (bit_len >> 16) & 0xFF
241 ctx[126] = (bit_len >> 8) & 0xFF
242 ctx[127] = bit_len & 0xFF
243 _sha512_compress(ctx)
244 var k: i64 = 0
245 while k < 8 {
246 _be64_write(out_ptr, k * 8, _h_get(ctx, k))
247 k = k + 1
248 }
249 return 0
250}
251
252// ===== HMAC-SHA-512 =====
253//
254// scratch layout (>= 2048 bytes):
255// 0..127 : K' (block-sized key buffer)
256// 128..255 : K' XOR ipad
257// 256..383 : K' XOR opad
258// 384..447 : inner-hash output (64 bytes)
259// 512..1431 : SHA-512 ctx scratch (920 bytes; needs 128 + 64 + 8 + 640 = 840)
260// 1536..1727 : opad_block || inner_hash concat for outer (192 bytes)
261func nx_hmac_sha384_one_shot(key_ptr: *u8, key_len: i64,
262 msg_ptr: *u8, msg_len: i64,
263 scratch_ptr: *u8, out_ptr: *u8) -> i64 {
264 let kprime: *u8 = scratch_ptr
265 var j: i64 = 0
266 while j < HMAC_B { kprime[j] = 0; j = j + 1 }
267 if key_len > HMAC_B {
268 let h_ctx: *u8 = (scratch_ptr as i64 + 512) as *u8
269 _sha512_one_shot(key_ptr, key_len, h_ctx, kprime)
270 } else {
271 var ki: i64 = 0
272 while ki < key_len { kprime[ki] = key_ptr[ki]; ki = ki + 1 }
273 }
274 let ipad_blk: *u8 = (scratch_ptr as i64 + 128) as *u8
275 var ii: i64 = 0
276 while ii < HMAC_B { ipad_blk[ii] = kprime[ii] ^ 0x36; ii = ii + 1 }
277 let opad_blk: *u8 = (scratch_ptr as i64 + 256) as *u8
278 var oi: i64 = 0
279 while oi < HMAC_B { opad_blk[oi] = kprime[oi] ^ 0x5c; oi = oi + 1 }
280 // Inner hash: streaming SHA-512 of (ipad || msg).
281 let inner_ctx: *u8 = (scratch_ptr as i64 + 512) as *u8
282 _sha512_init(inner_ctx)
283 var inner_idx: i64 = 0
284 var inner_bits: i64 = 0
285 var ip: i64 = 0
286 while ip < HMAC_B {
287 inner_ctx[inner_idx] = ipad_blk[ip]
288 inner_idx = inner_idx + 1
289 inner_bits = inner_bits + 8
290 if inner_idx == 128 { _sha512_compress(inner_ctx); inner_idx = 0 }
291 ip = ip + 1
292 }
293 var mp: i64 = 0
294 while mp < msg_len {
295 inner_ctx[inner_idx] = msg_ptr[mp]
296 inner_idx = inner_idx + 1
297 inner_bits = inner_bits + 8
298 if inner_idx == 128 { _sha512_compress(inner_ctx); inner_idx = 0 }
299 mp = mp + 1
300 }
301 inner_ctx[inner_idx] = 0x80
302 inner_idx = inner_idx + 1
303 if inner_idx > 112 {
304 while inner_idx < 128 { inner_ctx[inner_idx] = 0; inner_idx = inner_idx + 1 }
305 _sha512_compress(inner_ctx)
306 inner_idx = 0
307 }
308 while inner_idx < 112 { inner_ctx[inner_idx] = 0; inner_idx = inner_idx + 1 }
309 inner_ctx[112] = 0; inner_ctx[113] = 0; inner_ctx[114] = 0; inner_ctx[115] = 0
310 inner_ctx[116] = 0; inner_ctx[117] = 0; inner_ctx[118] = 0; inner_ctx[119] = 0
311 inner_ctx[120] = (inner_bits >> 56) & 0xFF
312 inner_ctx[121] = (inner_bits >> 48) & 0xFF
313 inner_ctx[122] = (inner_bits >> 40) & 0xFF
314 inner_ctx[123] = (inner_bits >> 32) & 0xFF
315 inner_ctx[124] = (inner_bits >> 24) & 0xFF
316 inner_ctx[125] = (inner_bits >> 16) & 0xFF
317 inner_ctx[126] = (inner_bits >> 8) & 0xFF
318 inner_ctx[127] = inner_bits & 0xFF
319 _sha512_compress(inner_ctx)
320 // SHA-384 truncates to first 6 H-words = 48 bytes.
321 let inner_out: *u8 = (scratch_ptr as i64 + 384) as *u8
322 var dk: i64 = 0
323 while dk < 6 {
324 _be64_write(inner_out, dk * 8, _h_get(inner_ctx, dk))
325 dk = dk + 1
326 }
327 // Outer hash: SHA-384(opad_blk || inner_out). _sha512_one_shot writes
328 // 8 H-words = 64 bytes into a temp; we copy the first 48 to out_ptr.
329 let outer_in: *u8 = (scratch_ptr as i64 + 1536) as *u8
330 var op: i64 = 0
331 while op < HMAC_B { outer_in[op] = opad_blk[op]; op = op + 1 }
332 var ohi: i64 = 0
333 while ohi < 48 { outer_in[HMAC_B + ohi] = inner_out[ohi]; ohi = ohi + 1 }
334 let outer_tmp: *u8 = (scratch_ptr as i64 + 1792) as *u8 // 64-byte scratch
335 _sha512_one_shot(outer_in, HMAC_B + 48, inner_ctx, outer_tmp)
336 var tk: i64 = 0
337 while tk < 48 { out_ptr[tk] = outer_tmp[tk]; tk = tk + 1 }
338 return 0
339}