code wiki / (root) / nx_trust_store_load_from_certdata_e2e_test.nx

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}