nx_tls13_kdf_test.nx source
↩ module page · 119 lines · 5767 B
1// nx_tls13_kdf_test.nx -- RFC 8446 §7.1 + RFC 8448 §3 KAT for the
2// TLS 1.3 HKDF-Expand-Label + Derive-Secret wrappers.
3//
4// Three verification steps:
5//
6// A. HkdfLabel byte construction for the canonical first-call
7// ("derived" label, empty-message SHA-256 hash as context,
8// length 32). Verifies the structure layout exactly matches
9// RFC 8446 §7.1's `struct HkdfLabel`.
10//
11// B. RFC 8448 §3 early_secret = HKDF-Extract(salt=0, IKM=0^32)
12// = 33ad0a1c607ec03b09e6cd9893680ce210adf300aa1f2660e1b22e10f170f92a
13// (the first secret in TLS 1.3's 5-stage key schedule, in the
14// no-PSK case which is what 99% of fresh TLS handshakes use).
15//
16// C. RFC 8448 §3 derived = Derive-Secret(early_secret, "derived", empty)
17// = 6f2615a108c702c5678f54fc9dbab69716c076189c48250cebeac3576c3611ba
18// (the secret that gets HKDF-Extract'd with ECDHE output to
19// produce the handshake_secret).
20//
21// The empty SHA-256 hash is hardcoded as the well-known constant
22// e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
23// to keep this test focused on the KDF logic (the SHA-256 module
24// has its own KAT).
25//
26// expect_exit: 0
27// license_tier: ORIGINAL
28
29import "nx_syscalls.nx"
30import "nx_hkdf.nx"
31import "nx_tls13_kdf.nx"
32
33func main() -> i64 {
34 // ---- The "derived" label = 7 bytes ----
35 let label: *u8 = sys_mmap(16)
36 label[0]=0x64; label[1]=0x65; label[2]=0x72; label[3]=0x69 // "deri"
37 label[4]=0x76; label[5]=0x65; label[6]=0x64 // "ved"
38
39 // ---- Empty SHA-256 hash (well-known constant) ----
40 let empty_hash: *u8 = sys_mmap(64)
41 empty_hash[0]=0xe3; empty_hash[1]=0xb0; empty_hash[2]=0xc4; empty_hash[3]=0x42
42 empty_hash[4]=0x98; empty_hash[5]=0xfc; empty_hash[6]=0x1c; empty_hash[7]=0x14
43 empty_hash[8]=0x9a; empty_hash[9]=0xfb; empty_hash[10]=0xf4; empty_hash[11]=0xc8
44 empty_hash[12]=0x99; empty_hash[13]=0x6f; empty_hash[14]=0xb9; empty_hash[15]=0x24
45 empty_hash[16]=0x27; empty_hash[17]=0xae; empty_hash[18]=0x41; empty_hash[19]=0xe4
46 empty_hash[20]=0x64; empty_hash[21]=0x9b; empty_hash[22]=0x93; empty_hash[23]=0x4c
47 empty_hash[24]=0xa4; empty_hash[25]=0x95; empty_hash[26]=0x99; empty_hash[27]=0x1b
48 empty_hash[28]=0x78; empty_hash[29]=0x52; empty_hash[30]=0xb8; empty_hash[31]=0x55
49
50 // ---- Test A: HkdfLabel byte construction ----
51 let info: *u8 = sys_mmap(128)
52 let info_len: i64 = tls13_build_hkdf_label(32, label, 7, empty_hash, 32, info)
53 // Expected total = 2 (length) + 1 (label_len) + 6 ("tls13 ") + 7 ("derived")
54 // + 1 (context_len) + 32 (empty hash) = 49 bytes
55 if info_len != 49 { return 1 }
56 if (info[0] & 0xff) != 0x00 { return 2 } // length hi
57 if (info[1] & 0xff) != 0x20 { return 3 } // length lo = 32
58 if (info[2] & 0xff) != 0x0d { return 4 } // label total = 13
59 if (info[3] & 0xff) != 0x74 { return 5 } // 't' of "tls13 "
60 if (info[4] & 0xff) != 0x6c { return 6 } // 'l'
61 if (info[5] & 0xff) != 0x73 { return 7 } // 's'
62 if (info[6] & 0xff) != 0x31 { return 8 } // '1'
63 if (info[7] & 0xff) != 0x33 { return 9 } // '3'
64 if (info[8] & 0xff) != 0x20 { return 10 } // ' '
65 if (info[9] & 0xff) != 0x64 { return 11 } // 'd' of "derived"
66 if (info[15] & 0xff) != 0x64 { return 12 } // last 'd' of "derived"
67 if (info[16] & 0xff) != 0x20 { return 13 } // context_len = 32
68 if (info[17] & 0xff) != 0xe3 { return 14 } // first byte of empty hash
69 if (info[48] & 0xff) != 0x55 { return 15 } // last byte of empty hash
70
71 // ---- Test B: RFC 8448 §3 early_secret ----
72 let zeros: *u8 = sys_mmap(64)
73 let early_secret: *u8 = sys_mmap(64)
74 // hkdf_extract with salt_len=0 auto-fills with HashLen zeros.
75 hkdf_extract(zeros, 0, zeros, 32, early_secret)
76 // Expected: 33ad0a1c607ec03b09e6cd9893680ce210adf300aa1f2660e1b22e10f170f92a
77 if (early_secret[0] & 0xff) != 0x33 { return 20 }
78 if (early_secret[1] & 0xff) != 0xad { return 21 }
79 if (early_secret[2] & 0xff) != 0x0a { return 22 }
80 if (early_secret[3] & 0xff) != 0x1c { return 23 }
81 if (early_secret[7] & 0xff) != 0x3b { return 24 }
82 if (early_secret[15] & 0xff) != 0xe2 { return 25 }
83 if (early_secret[23] & 0xff) != 0x60 { return 26 }
84 if (early_secret[31] & 0xff) != 0x2a { return 27 }
85
86 // ---- Test C: Derive-Secret(early_secret, "derived", empty) ----
87 let derived: *u8 = sys_mmap(64)
88 let v: i64 = tls13_derive_secret(
89 early_secret, label, 7, empty_hash, 32, derived
90 )
91 if v != NX_TLS13_KDF_VERDICT_OK { return 30 }
92 // Expected: 6f2615a108c702c5 678f54fc9dbab697 16c076189c48250c ebeac3576c3611ba
93 if (derived[0] & 0xff) != 0x6f { return 31 }
94 if (derived[1] & 0xff) != 0x26 { return 32 }
95 if (derived[2] & 0xff) != 0x15 { return 33 }
96 if (derived[3] & 0xff) != 0xa1 { return 34 }
97 if (derived[7] & 0xff) != 0xc5 { return 35 }
98 if (derived[15] & 0xff) != 0x97 { return 36 }
99 if (derived[23] & 0xff) != 0x0c { return 37 }
100 if (derived[30] & 0xff) != 0x11 { return 38 }
101 if (derived[31] & 0xff) != 0xba { return 39 }
102
103 // ---- Bounds: label too long rejected ----
104 let big_label: *u8 = sys_mmap(512)
105 var bl: i64 = 0
106 while bl < 250 {
107 big_label[bl] = 0x41
108 bl = bl + 1
109 }
110 let bad: i64 = tls13_build_hkdf_label(32, big_label, 250, empty_hash, 32, info)
111 if bad != 0 - NX_TLS13_KDF_VERDICT_LABEL_TOO_LONG { return 50 }
112
113 // ---- Verdict gate ----
114 if nx_tls13_kdf_verdict_is_valid(NX_TLS13_KDF_VERDICT_OK) != 1 { return 60 }
115 if nx_tls13_kdf_verdict_is_valid(NX_TLS13_KDF_VERDICT_N) != 0 { return 61 }
116 if nx_tls13_kdf_verdict_is_valid(0 - 1) != 0 { return 62 }
117
118 return 0
119}