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