nx_trust_store_load_from_certdata_e2e_test.nx source
↩ module page · 250 lines · 10252 B
1// nx_trust_store_load_from_certdata_e2e_test.nx -- REAL
2// END-TO-END happy-path KAT for the boot-time loader.
3//
4// Unlike the verdict-only sibling test, this one exercises the
5// FULL composed pipeline against a real on-disk file:
6//
7// 1. Build a real 169-byte minimal Ed25519 v1 DER cert in memory
8// 2. Wrap it in Mozilla NSS certdata.txt PKCS#11 text format
9// with proper \NNN multiline-octal escape encoding of the DER
10// 3. Write the certdata.txt content to a real temp file via
11// sys_openat_wr + sys_write
12// 4. Call nx_trust_store_load_from_certdata against the path
13// 5. Verify the returned *TrustStore has the cert populated
14//
15// Stands as evidence against [[feedback-no-false-ok-substrate-
16// honesty-audit]] -- the verdict-only smoke shipped earlier only
17// hit error paths and could not have detected a bug in the happy-
18// path composition. This test would catch a bug ANYWHERE in:
19// sys_read_file
20// -> nx_nss_certdata_parse (octal escape decoder, state machine)
21// -> trust_store_alloc
22// -> nx_x509_trust_store_load (wire-format walker + x509_parse +
23// trust_store_add)
24//
25// expect_exit: 0
26// license_tier: ORIGINAL
27
28import "nx_syscalls.nx"
29import "nx_x509.nx"
30import "nx_x509_trust_store.nx"
31import "nx_trust_store_load_from_certdata.nx"
32
33// ---- DER cert builder (copy of build_min_v1_cert from sibling test) ----
34
35func emit_ed25519_algid(buf: *u8, off: i64) -> i64 {
36 buf[off + 0] = 0x30 as u8
37 buf[off + 1] = 5 as u8
38 buf[off + 2] = 0x06 as u8
39 buf[off + 3] = 3 as u8
40 buf[off + 4] = 0x2B as u8
41 buf[off + 5] = 0x65 as u8
42 buf[off + 6] = 0x70 as u8
43 return 7
44}
45
46func emit_utctime(buf: *u8, off: i64,
47 yyyy: i64, mo: i64, d: i64,
48 h: i64, mi: i64, s: i64) -> i64 {
49 buf[off + 0] = 0x17 as u8
50 buf[off + 1] = 13 as u8
51 let yy: i64 = yyyy - 2000
52 buf[off + 2] = (0x30 + (yy / 10)) as u8
53 buf[off + 3] = (0x30 + (yy % 10)) as u8
54 buf[off + 4] = (0x30 + (mo / 10)) as u8
55 buf[off + 5] = (0x30 + (mo % 10)) as u8
56 buf[off + 6] = (0x30 + (d / 10)) as u8
57 buf[off + 7] = (0x30 + (d % 10)) as u8
58 buf[off + 8] = (0x30 + (h / 10)) as u8
59 buf[off + 9] = (0x30 + (h % 10)) as u8
60 buf[off + 10] = (0x30 + (mi / 10)) as u8
61 buf[off + 11] = (0x30 + (mi % 10)) as u8
62 buf[off + 12] = (0x30 + (s / 10)) as u8
63 buf[off + 13] = (0x30 + (s % 10)) as u8
64 buf[off + 14] = 0x5A as u8
65 return 15
66}
67
68func build_min_v1_cert(buf: *u8, off: i64, serial_byte: i64) -> i64 {
69 let SPKI_BODY: i64 = 7 + 35
70 let SPKI_TOTAL: i64 = 2 + SPKI_BODY
71 let VAL_BODY: i64 = 30
72 let VAL_TOTAL: i64 = 2 + VAL_BODY
73 let TBS_BODY: i64 = 3 + 7 + 2 + VAL_TOTAL + 2 + SPKI_TOTAL
74 let SIG_TOTAL: i64 = 67
75 let OUTER_BODY: i64 = (2 + TBS_BODY) + 7 + SIG_TOTAL
76
77 var o: i64 = off
78 buf[o] = 0x30 as u8; o = o + 1
79 buf[o] = 0x81 as u8; o = o + 1
80 buf[o] = OUTER_BODY as u8; o = o + 1
81 buf[o] = 0x30 as u8; o = o + 1
82 buf[o] = TBS_BODY as u8; o = o + 1
83 buf[o] = 0x02 as u8; buf[o+1] = 0x01 as u8; buf[o+2] = serial_byte as u8; o = o + 3
84 o = o + emit_ed25519_algid(buf, o)
85 buf[o] = 0x30 as u8; buf[o+1] = 0x00 as u8; o = o + 2
86 buf[o] = 0x30 as u8; o = o + 1
87 buf[o] = VAL_BODY as u8; o = o + 1
88 o = o + emit_utctime(buf, o, 2023, 1, 1, 0, 0, 0)
89 o = o + emit_utctime(buf, o, 2026, 1, 1, 0, 0, 0)
90 buf[o] = 0x30 as u8; buf[o+1] = 0x00 as u8; o = o + 2
91 buf[o] = 0x30 as u8; o = o + 1
92 buf[o] = SPKI_BODY as u8; o = o + 1
93 o = o + emit_ed25519_algid(buf, o)
94 buf[o] = 0x03 as u8; o = o + 1
95 buf[o] = 0x21 as u8; o = o + 1
96 buf[o] = 0x00 as u8; o = o + 1
97 var ki: i64 = 0
98 while ki < 32 { buf[o + ki] = 0x00 as u8; ki = ki + 1 }
99 o = o + 32
100 o = o + emit_ed25519_algid(buf, o)
101 buf[o] = 0x03 as u8; o = o + 1
102 buf[o] = 0x41 as u8; o = o + 1
103 buf[o] = 0x00 as u8; o = o + 1
104 var si: i64 = 0
105 while si < 64 { buf[o + si] = 0xAA as u8; si = si + 1 }
106 o = o + 64
107 return o - off
108}
109
110// ---- certdata.txt text emitter ----
111
112// Copy n bytes from src into buf at off.
113func emit_bytes(buf: *u8, off: i64, src: *u8, n: i64) -> i64 {
114 var i: i64 = 0
115 while i < n {
116 buf[off + i] = src[i]
117 i = i + 1
118 }
119 return n
120}
121
122// Emit a single byte as \NNN where NNN is 3 octal digits.
123// Each cert byte becomes 4 chars. Per Mozilla NSS multiline-octal
124// format used in certdata.txt (CKA_VALUE MULTILINE_OCTAL).
125func emit_octal_escape(buf: *u8, off: i64, b: i64) -> i64 {
126 buf[off + 0] = 0x5C as u8 // '\'
127 let d2: i64 = (b >> 6) & 0x7
128 let d1: i64 = (b >> 3) & 0x7
129 let d0: i64 = b & 0x7
130 buf[off + 1] = (0x30 + d2) as u8
131 buf[off + 2] = (0x30 + d1) as u8
132 buf[off + 3] = (0x30 + d0) as u8
133 return 4
134}
135
136// Emit a single literal byte at offset; helper for short strings.
137func emit_u8(buf: *u8, off: i64, b: i64) -> i64 {
138 buf[off] = b as u8
139 return 1
140}
141
142func main() -> i64 {
143 // ---- Step 1: build the real 169-byte Ed25519 DER cert ----
144 let der: *u8 = sys_mmap(256)
145 let der_n: i64 = build_min_v1_cert(der, 0, 0x42)
146 if der_n != 169 { return 1 }
147
148 // ---- Step 2: build certdata.txt text in memory ----
149 let text: *u8 = sys_mmap(8192)
150 var p: i64 = 0
151
152 // Line 1: "CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE\n"
153 let line1: *u8 = sys_mmap(64)
154 var li: i64 = 0
155 // C K A _ C L A S S
156 line1[li]=0x43; li=li+1; line1[li]=0x4B; li=li+1; line1[li]=0x41; li=li+1
157 line1[li]=0x5F; li=li+1; line1[li]=0x43; li=li+1; line1[li]=0x4C; li=li+1
158 line1[li]=0x41; li=li+1; line1[li]=0x53; li=li+1; line1[li]=0x53; li=li+1
159 line1[li]=0x20; li=li+1
160 // C K _ O B J E C T _ C L A S S
161 line1[li]=0x43; li=li+1; line1[li]=0x4B; li=li+1; line1[li]=0x5F; li=li+1
162 line1[li]=0x4F; li=li+1; line1[li]=0x42; li=li+1; line1[li]=0x4A; li=li+1
163 line1[li]=0x45; li=li+1; line1[li]=0x43; li=li+1; line1[li]=0x54; li=li+1
164 line1[li]=0x5F; li=li+1; line1[li]=0x43; li=li+1; line1[li]=0x4C; li=li+1
165 line1[li]=0x41; li=li+1; line1[li]=0x53; li=li+1; line1[li]=0x53; li=li+1
166 line1[li]=0x20; li=li+1
167 // C K O _ C E R T I F I C A T E
168 line1[li]=0x43; li=li+1; line1[li]=0x4B; li=li+1; line1[li]=0x4F; li=li+1
169 line1[li]=0x5F; li=li+1; line1[li]=0x43; li=li+1; line1[li]=0x45; li=li+1
170 line1[li]=0x52; li=li+1; line1[li]=0x54; li=li+1; line1[li]=0x49; li=li+1
171 line1[li]=0x46; li=li+1; line1[li]=0x49; li=li+1; line1[li]=0x43; li=li+1
172 line1[li]=0x41; li=li+1; line1[li]=0x54; li=li+1; line1[li]=0x45; li=li+1
173 line1[li]=0x0A; li=li+1 // \n
174 p = p + emit_bytes(text, p, line1, li)
175
176 // Line 2: "CKA_VALUE MULTILINE_OCTAL\n"
177 let line2: *u8 = sys_mmap(64)
178 var l2: i64 = 0
179 // CKA_VALUE
180 line2[l2]=0x43; l2=l2+1; line2[l2]=0x4B; l2=l2+1; line2[l2]=0x41; l2=l2+1
181 line2[l2]=0x5F; l2=l2+1; line2[l2]=0x56; l2=l2+1; line2[l2]=0x41; l2=l2+1
182 line2[l2]=0x4C; l2=l2+1; line2[l2]=0x55; l2=l2+1; line2[l2]=0x45; l2=l2+1
183 line2[l2]=0x20; l2=l2+1
184 // MULTILINE_OCTAL
185 line2[l2]=0x4D; l2=l2+1; line2[l2]=0x55; l2=l2+1; line2[l2]=0x4C; l2=l2+1
186 line2[l2]=0x54; l2=l2+1; line2[l2]=0x49; l2=l2+1; line2[l2]=0x4C; l2=l2+1
187 line2[l2]=0x49; l2=l2+1; line2[l2]=0x4E; l2=l2+1; line2[l2]=0x45; l2=l2+1
188 line2[l2]=0x5F; l2=l2+1; line2[l2]=0x4F; l2=l2+1; line2[l2]=0x43; l2=l2+1
189 line2[l2]=0x54; l2=l2+1; line2[l2]=0x41; l2=l2+1; line2[l2]=0x4C; l2=l2+1
190 line2[l2]=0x0A; l2=l2+1 // \n
191 p = p + emit_bytes(text, p, line2, l2)
192
193 // Lines 3..N: \NNN escapes for the 169 DER bytes.
194 // Format each byte as \NNN; group up to 16 per line + \n.
195 var bi: i64 = 0
196 var on_line: i64 = 0
197 while bi < der_n {
198 p = p + emit_octal_escape(text, p, der[bi] as i64)
199 bi = bi + 1
200 on_line = on_line + 1
201 if on_line >= 16 {
202 p = p + emit_u8(text, p, 0x0A) // \n
203 on_line = 0
204 }
205 }
206 if on_line > 0 { p = p + emit_u8(text, p, 0x0A) }
207
208 // END\n
209 p = p + emit_u8(text, p, 0x45) // E
210 p = p + emit_u8(text, p, 0x4E) // N
211 p = p + emit_u8(text, p, 0x44) // D
212 p = p + emit_u8(text, p, 0x0A) // \n
213
214 let text_len: i64 = p
215
216 // ---- Step 3: write the certdata content to a real temp file ----
217 let path: *u8 = sys_mmap(64)
218 var pi: i64 = 0
219 // /tmp/nx_certdata_e2e.txt
220 path[pi]=0x2F; pi=pi+1 // /
221 path[pi]=0x74; pi=pi+1; path[pi]=0x6D; pi=pi+1; path[pi]=0x70; pi=pi+1 // tmp
222 path[pi]=0x2F; pi=pi+1 // /
223 path[pi]=0x6E; pi=pi+1; path[pi]=0x78; pi=pi+1; path[pi]=0x5F; pi=pi+1 // nx_
224 path[pi]=0x63; pi=pi+1; path[pi]=0x65; pi=pi+1; path[pi]=0x72; pi=pi+1
225 path[pi]=0x74; pi=pi+1; path[pi]=0x64; pi=pi+1; path[pi]=0x61; pi=pi+1
226 path[pi]=0x74; pi=pi+1; path[pi]=0x61; pi=pi+1 // certdata
227 path[pi]=0x5F; pi=pi+1 // _
228 path[pi]=0x65; pi=pi+1; path[pi]=0x32; pi=pi+1; path[pi]=0x65; pi=pi+1 // e2e
229 path[pi]=0x2E; pi=pi+1; path[pi]=0x74; pi=pi+1; path[pi]=0x78; pi=pi+1
230 path[pi]=0x74; pi=pi+1 // .txt
231 path[pi]=0 as u8 // NUL
232
233 let fd: i64 = sys_openat_wr(path, 0x1A4) // 0o644
234 if fd < 0 { return 2 }
235 let wn: i64 = sys_write(fd, text, text_len)
236 if wn != text_len { return 3 }
237 sys_close(fd)
238
239 // ---- Step 4: call the primitive against the real file ----
240 let r: i64 = nx_trust_store_load_from_certdata(path, 8, 65536)
241 // Positive return = *TrustStore as i64. Negative = -verdict.
242 if r <= 0 { return 100 - r } // surface the verdict
243
244 // ---- Step 5: verify the returned store ----
245 let store: *TrustStore = r as *TrustStore
246 let n: i64 = trust_store_count(store)
247 if n != 1 { return 200 + n } // surface actual count
248
249 return 0
250}