nx_hmac_sha384.nx source
↩ module page · 94 lines · 3222 B
1// hmac_sha384.nx -- HMAC-SHA-384 (RFC 2104 / FIPS 198-1).
2//
3// license_tier: INDEPENDENT_REDERIVE
4// genealogy_id: international-research-sources/nist/fips_198_1
5//
6// Parallel to hmac.nx but keyed on SHA-384 instead of SHA-256.
7// Required for TLS 1.3 cipher suites that negotiate SHA-384 in
8// the key schedule (e.g. TLS_AES_256_GCM_SHA384).
9//
10// Differences from hmac.nx:
11// - Block size B = 128 bytes (SHA-384 / SHA-512 block), not 64.
12// - Hash output = 48 bytes (SHA-384), not 32.
13// - Key-pre-hash uses sha384_digest for oversized keys.
14//
15// Key processing (RFC 2104) remains identical in structure:
16// K' = H(K) if len(K) > B
17// K' = K || zeros if len(K) < B
18// K' = K if len(K) == B
19// ipad / opad bytes XOR'd against K' block.
20
21// nx_safety_envelope:
22// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
23// sil_target: SIL1
24// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
25// verdict: NOT_YET_EVALUATED
26
27import "nx_syscalls.nx"
28import "nx_sha512.nx"
29
30const HMAC384_BLOCK: i64 = 128
31const HMAC384_HASH: i64 = 48
32const IPAD_384: i64 = 0x36
33const OPAD_384: i64 = 0x5C
34
35func hmac_sha384(key: *u8, key_len: i64, msg: *u8, msg_len: i64,
36 out: *u8) -> i64 {
37 // Step 1: derive K'. HMAC384_BLOCK-sized zero-padded buffer.
38 let kp: *u8 = sys_mmap(HMAC384_BLOCK)
39 var i: i64 = 0
40 while i < HMAC384_BLOCK { kp[i] = 0; i = i + 1 }
41 if key_len > HMAC384_BLOCK {
42 sha384_digest(key, key_len, kp)
43 } else {
44 var j: i64 = 0
45 while j < key_len { kp[j] = key[j]; j = j + 1 }
46 }
47
48 // Step 2: inner pad + hash.
49 let inner_key: *u8 = sys_mmap(HMAC384_BLOCK)
50 let ii_raw: *u8 = sys_mmap(512)
51 let ii: *Sha512 = ii_raw as *Sha512
52 sha384_init(ii)
53 var b: i64 = 0
54 while b < HMAC384_BLOCK {
55 inner_key[b] = kp[b] ^ IPAD_384
56 b = b + 1
57 }
58 sha512_update(ii, inner_key, HMAC384_BLOCK)
59 sha512_update(ii, msg, msg_len)
60 let inner_digest_full: *u8 = sys_mmap(64)
61 sha512_final(ii, inner_digest_full)
62 let inner_digest: *u8 = inner_digest_full // first 48 bytes = SHA-384 digest
63
64 // Step 3: outer pad + hash.
65 let outer_key: *u8 = sys_mmap(HMAC384_BLOCK)
66 let oi_raw: *u8 = sys_mmap(512)
67 let oi: *Sha512 = oi_raw as *Sha512
68 sha384_init(oi)
69 b = 0
70 while b < HMAC384_BLOCK {
71 outer_key[b] = kp[b] ^ OPAD_384
72 b = b + 1
73 }
74 sha512_update(oi, outer_key, HMAC384_BLOCK)
75 sha512_update(oi, inner_digest, HMAC384_HASH)
76 // Emit 64 bytes full then truncate to first 48.
77 let out_full: *u8 = sys_mmap(64)
78 sha512_final(oi, out_full)
79 var k: i64 = 0
80 while k < HMAC384_HASH { out[k] = out_full[k]; k = k + 1 }
81 return 0
82}
83
84// Compile-only smoke. RFC 4231 test case 1: key=0x0b*20,
85// data="Hi There" -> afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59cfaea9ea9076ede7f4af152e8b2fa9cb6
86func main() -> i64 {
87 let key: *u8 = sys_mmap(20)
88 let msg: *u8 = "Hi There"
89 let tag: *u8 = sys_mmap(48)
90 var i: i64 = 0
91 while i < 20 { key[i] = 0x0B; i = i + 1 }
92 hmac_sha384(key, 20, msg, 8, tag)
93 return tag[0] as i64
94}