nx_tls13_client_dispatch_with_validation.nx source
↩ module page · 124 lines · 5002 B
1// nx_tls13_client_dispatch_with_validation.nx -- TLS 1.3 client
2// dispatcher variant that enforces cert chain validation against
3// a TrustStore at the WAIT_CERT state.
4//
5// Phase 0b §L sub-4 of the TLS client arc. Wraps the existing
6// tls13_client_dispatch_handshake_message (commit pre-dating this
7// session, with the "accept ANY cert" STUB intentionally documented
8// in its preamble) with the cert-validation BRIDGE shipped this
9// session (nx_tls13_client_validate_certificate, commit c5ddbf81).
10//
11// Wrapper protocol:
12// - For state != WAIT_CERT: delegate unchanged to the underlying
13// dispatcher (transcript update + state transition unchanged).
14// - For state == WAIT_CERT: call the bridge FIRST against the
15// Certificate message bytes. If validation fails, return
16// CERT_VERIFY_FAIL (no transcript update, no state advance --
17// handshake stalls until caller closes the connection).
18// If validation succeeds, delegate to the underlying
19// dispatcher (which advances state to WAIT_CV).
20//
21// Per Cardinal 19 (API contract stability): the existing
22// dispatcher signature is UNCHANGED. Callers without a
23// TrustStore (e.g. the loopback test that uses a self-signed
24// throwaway) keep working in the legacy "accept any" mode. The
25// real Web PKI client uses this variant.
26//
27// Per Cardinal 12 (defensive at boundaries): cert validation
28// happens BEFORE the underlying dispatcher's transcript update,
29// so a rejected cert doesn't poison the transcript hash.
30//
31// Public API:
32// tls13_client_dispatch_with_validation(
33// state, msg, msg_len,
34// server_hs_traffic_secret,
35// transcript_state,
36// out_new_state,
37// val_ctx: *TlsValidationContext
38// ) -> verdict
39// nx_tls13_dwv_verdict_is_valid(v) -> 0|1
40//
41// Sealed verdict enum (separate namespace to keep dispatcher's
42// existing 6-verdict surface stable):
43// NX_TLS13_DWV_OK
44// NX_TLS13_DWV_BAD_MSG_TYPE -- delegated from dispatcher
45// NX_TLS13_DWV_BAD_FORMAT -- delegated from dispatcher
46// NX_TLS13_DWV_SF_BAD_MAC -- delegated from dispatcher
47// NX_TLS13_DWV_BAD_STATE -- delegated from dispatcher
48// NX_TLS13_DWV_CERT_VERIFY_FAIL -- THE new verdict: bridge rejected
49//
50// license_tier: INDEPENDENT_REDERIVE
51// genealogy_id: international-research-sources/ietf/rfc_8446 + rfc_5280
52// lineage_id: nishi_tls13_client_dispatch_with_validation_q10
53
54// nx_safety_envelope:
55// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
56// sil_target: SIL1
57// evidence: [bulk_applied_2026-05-19, tls13-dispatcher-with-validation]
58// verdict: NOT_YET_EVALUATED
59
60import "nx_syscalls.nx"
61import "nx_tls13_client.nx"
62import "nx_tls13_client_validate_certificate.nx"
63
64const NX_TLS13_DWV_OK: i64 = 1
65const NX_TLS13_DWV_BAD_MSG_TYPE: i64 = 2
66const NX_TLS13_DWV_BAD_FORMAT: i64 = 3
67const NX_TLS13_DWV_SF_BAD_MAC: i64 = 4
68const NX_TLS13_DWV_BAD_STATE: i64 = 5
69const NX_TLS13_DWV_CERT_VERIFY_FAIL: i64 = 6
70const NX_TLS13_DWV_VERDICT_N: i64 = 7
71
72func nx_tls13_dwv_verdict_is_valid(v: i64) -> i64 {
73 if v < NX_TLS13_DWV_OK { return 0 }
74 if v >= NX_TLS13_DWV_VERDICT_N { return 0 }
75 return 1
76}
77
78// Map an existing dispatcher verdict into our wrapper namespace.
79// One-to-one preservation; the new verdict CERT_VERIFY_FAIL is
80// added by the WAIT_CERT branch only.
81func _map_dispatcher_verdict(v: i64) -> i64 {
82 if v == NX_TLS13_CLIENT_VERDICT_OK { return NX_TLS13_DWV_OK }
83 if v == NX_TLS13_CLIENT_VERDICT_BAD_MSG_TYPE { return NX_TLS13_DWV_BAD_MSG_TYPE }
84 if v == NX_TLS13_CLIENT_VERDICT_BAD_FORMAT { return NX_TLS13_DWV_BAD_FORMAT }
85 if v == NX_TLS13_CLIENT_VERDICT_SF_BAD_MAC { return NX_TLS13_DWV_SF_BAD_MAC }
86 if v == NX_TLS13_CLIENT_VERDICT_BAD_STATE { return NX_TLS13_DWV_BAD_STATE }
87 return NX_TLS13_DWV_BAD_FORMAT // unknown -> conservative fallback
88}
89
90// Dispatch one decrypted handshake message with cert validation
91// enforced at WAIT_CERT. See preamble for protocol details.
92func tls13_client_dispatch_with_validation(
93 state: i64,
94 msg: *u8, msg_len: i64,
95 server_hs_traffic_secret: *u8,
96 transcript_state: *u8,
97 out_new_state: *i64,
98 val_ctx: *TlsValidationContext
99) -> i64 {
100 if state == NX_TLS13_CSTATE_WAIT_CERT {
101 // Validate BEFORE delegating: keeps transcript clean if
102 // we reject.
103 let cv: i64 = nx_tls13_client_validate_certificate(
104 msg, msg_len, val_ctx
105 )
106 if cv != NX_TLS13_CLIENT_CV_OK {
107 return NX_TLS13_DWV_CERT_VERIFY_FAIL
108 }
109 }
110
111 let d: i64 = tls13_client_dispatch_handshake_message(
112 state, msg, msg_len,
113 server_hs_traffic_secret,
114 transcript_state,
115 out_new_state
116 )
117 return _map_dispatcher_verdict(d)
118}
119
120// Compile-only smoke. Real KAT in
121// nx_tls13_client_dispatch_with_validation_test.nx.
122func main() -> i64 {
123 return 0
124}