code wiki / (root) / nx_hmac_sha384.nx

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}