code wiki / (root) / nx_x509_validity_test.nx

nx_x509_validity_test.nx source

↩ module page · 216 lines · 9736 B

1// nx_x509_validity_test.nx -- KAT for the X.509 validity period 2// parser + range-check. Exercises both ASN.1 Time encodings 3// (UTCTime 0x17 and GeneralizedTime 0x18), the OK / NOT_YET_VALID / 4// EXPIRED verdicts, plus error paths for bad tag, bad length, 5// inverted range, and missing validity. 6// 7// expect_exit: 0 8// license_tier: ORIGINAL 9 10import "nx_syscalls.nx" 11import "nx_asn1.nx" 12import "nx_x509.nx" 13import "nx_x509_validity.nx" 14 15// ---- Helpers ------------------------------------------------------ 16 17// Write a UTCTime TLV "YYMMDDhhmmssZ" into buf at off. yyyy is the 18// full 4-digit year; we encode as 2-digit modulo (RFC 5280: years 19// 1950..2049). Returns bytes written (15 = 2-byte TLV header + 20// 13-byte content). 21func emit_utctime(buf: *u8, off: i64, 22 yyyy: i64, mo: i64, d: i64, 23 h: i64, mi: i64, s: i64) -> i64 { 24 buf[off + 0] = 0x17 as u8 // ASN1_UTC_TIME 25 buf[off + 1] = 13 as u8 26 let yy: i64 = yyyy - 2000 27 buf[off + 2] = (0x30 + (yy / 10)) as u8 28 buf[off + 3] = (0x30 + (yy % 10)) as u8 29 buf[off + 4] = (0x30 + (mo / 10)) as u8 30 buf[off + 5] = (0x30 + (mo % 10)) as u8 31 buf[off + 6] = (0x30 + (d / 10)) as u8 32 buf[off + 7] = (0x30 + (d % 10)) as u8 33 buf[off + 8] = (0x30 + (h / 10)) as u8 34 buf[off + 9] = (0x30 + (h % 10)) as u8 35 buf[off + 10] = (0x30 + (mi / 10)) as u8 36 buf[off + 11] = (0x30 + (mi % 10)) as u8 37 buf[off + 12] = (0x30 + (s / 10)) as u8 38 buf[off + 13] = (0x30 + (s % 10)) as u8 39 buf[off + 14] = 0x5A as u8 // 'Z' 40 return 15 41} 42 43// Write a GeneralizedTime TLV "YYYYMMDDhhmmssZ" into buf at off. 44// Returns bytes written (17 = 2-byte TLV header + 15-byte content). 45func emit_gentime(buf: *u8, off: i64, 46 yyyy: i64, mo: i64, d: i64, 47 h: i64, mi: i64, s: i64) -> i64 { 48 buf[off + 0] = 0x18 as u8 // ASN1_GENERALIZED 49 buf[off + 1] = 15 as u8 50 buf[off + 2] = (0x30 + (yyyy / 1000)) as u8 51 buf[off + 3] = (0x30 + ((yyyy / 100) % 10)) as u8 52 buf[off + 4] = (0x30 + ((yyyy / 10) % 10)) as u8 53 buf[off + 5] = (0x30 + (yyyy % 10)) as u8 54 buf[off + 6] = (0x30 + (mo / 10)) as u8 55 buf[off + 7] = (0x30 + (mo % 10)) as u8 56 buf[off + 8] = (0x30 + (d / 10)) as u8 57 buf[off + 9] = (0x30 + (d % 10)) as u8 58 buf[off + 10] = (0x30 + (h / 10)) as u8 59 buf[off + 11] = (0x30 + (h % 10)) as u8 60 buf[off + 12] = (0x30 + (mi / 10)) as u8 61 buf[off + 13] = (0x30 + (mi % 10)) as u8 62 buf[off + 14] = (0x30 + (s / 10)) as u8 63 buf[off + 15] = (0x30 + (s % 10)) as u8 64 buf[off + 16] = 0x5A as u8 // 'Z' 65 return 17 66} 67 68func main() -> i64 { 69 // Known epochs for date-arithmetic spot-checks (computed via 70 // Hinnant days_from_civil + 86400 sec/day): 71 // 2023-01-01T00:00:00Z = 1672531200 72 // 2026-01-01T00:00:00Z = 1767225600 73 // 2024-06-15T12:00:00Z = 1718452800 (within [2023, 2026]) 74 // 2022-06-01T00:00:00Z = 1654041600 (BEFORE 2023) 75 // 2027-01-01T00:00:00Z = 1798761600 (AFTER 2026) 76 let NB_EPOCH: i64 = 1672531200 77 let NA_EPOCH: i64 = 1767225600 78 let NOW_OK: i64 = 1718452800 79 let NOW_EARLY: i64 = 1654041600 80 let NOW_LATE: i64 = 1798761600 81 82 let buf: *u8 = sys_mmap(256) 83 let cert_raw: *u8 = sys_mmap(256) 84 let cert: *X509Cert = cert_raw as *X509Cert 85 86 // Initialise all X509Cert fields (manual construction; no parser 87 // call here -- we test the validity primitive in isolation). 88 cert.tbs_off = 0; cert.tbs_len = 0 89 cert.serial_off = 0; cert.serial_len = 0 90 cert.sig_alg_off = 0; cert.sig_alg_len = 0 91 cert.spki_off = 0; cert.spki_len = 0 92 cert.sig_off = 0; cert.sig_len = 0 93 cert.pubkey_off = 0; cert.pubkey_len = 0 94 cert.pubkey_alg_off = 0; cert.pubkey_alg_len = 0 95 cert.validity_off = 0; cert.validity_len = 0 96 cert.extensions_off = 0 - 1 97 cert.extensions_len = 0 98 99 // ---- Test A: UTCTime + UTCTime, both valid ---- 100 let n1: i64 = emit_utctime(buf, 0, 2023, 1, 1, 0, 0, 0) 101 let n2: i64 = emit_utctime(buf, n1, 2026, 1, 1, 0, 0, 0) 102 cert.validity_off = 0 103 cert.validity_len = n1 + n2 // 15 + 15 = 30 104 105 let nb_p: *i64 = sys_mmap(16) as *i64 106 let na_p: *i64 = sys_mmap(16) as *i64 107 let v1: i64 = x509_validity_get(buf, cert, nb_p, na_p) 108 if v1 != NX_X509_VALID_OK { return 1 } 109 if *nb_p != NB_EPOCH { return 2 } 110 if *na_p != NA_EPOCH { return 3 } 111 112 // ---- Test B: now within range -> OK ---- 113 if x509_validity_check(buf, cert, NOW_OK) != NX_X509_VALID_OK { return 4 } 114 115 // ---- Test C: now before notBefore -> NOT_YET_VALID ---- 116 if x509_validity_check(buf, cert, NOW_EARLY) != NX_X509_VALID_NOT_YET_VALID { return 5 } 117 118 // ---- Test D: now after notAfter -> EXPIRED ---- 119 if x509_validity_check(buf, cert, NOW_LATE) != NX_X509_VALID_EXPIRED { return 6 } 120 121 // ---- Test E: edge cases at exact boundaries ---- 122 if x509_validity_check(buf, cert, NB_EPOCH) != NX_X509_VALID_OK { return 7 } 123 if x509_validity_check(buf, cert, NA_EPOCH) != NX_X509_VALID_OK { return 8 } 124 if x509_validity_check(buf, cert, NB_EPOCH - 1) != NX_X509_VALID_NOT_YET_VALID { return 9 } 125 if x509_validity_check(buf, cert, NA_EPOCH + 1) != NX_X509_VALID_EXPIRED { return 10 } 126 127 // ---- Test F: GeneralizedTime + GeneralizedTime, valid ---- 128 let n3: i64 = emit_gentime(buf, 0, 2023, 1, 1, 0, 0, 0) 129 let n4: i64 = emit_gentime(buf, n3, 2026, 1, 1, 0, 0, 0) 130 cert.validity_off = 0 131 cert.validity_len = n3 + n4 // 17 + 17 = 34 132 let v6: i64 = x509_validity_get(buf, cert, nb_p, na_p) 133 if v6 != NX_X509_VALID_OK { return 11 } 134 if *nb_p != NB_EPOCH { return 12 } 135 if *na_p != NA_EPOCH { return 13 } 136 if x509_validity_check(buf, cert, NOW_OK) != NX_X509_VALID_OK { return 14 } 137 138 // ---- Test G: mixed UTCTime + GeneralizedTime ---- 139 let n5a: i64 = emit_utctime(buf, 0, 2023, 1, 1, 0, 0, 0) 140 let n5b: i64 = emit_gentime(buf, n5a, 2026, 1, 1, 0, 0, 0) 141 cert.validity_off = 0 142 cert.validity_len = n5a + n5b // 15 + 17 = 32 143 let v7: i64 = x509_validity_get(buf, cert, nb_p, na_p) 144 if v7 != NX_X509_VALID_OK { return 15 } 145 if *nb_p != NB_EPOCH { return 16 } 146 if *na_p != NA_EPOCH { return 17 } 147 148 // ---- Test H: inverted range (notBefore > notAfter) rejected ---- 149 let n6a: i64 = emit_utctime(buf, 0, 2026, 1, 1, 0, 0, 0) 150 let n6b: i64 = emit_utctime(buf, n6a, 2023, 1, 1, 0, 0, 0) 151 cert.validity_off = 0 152 cert.validity_len = n6a + n6b // 30 153 let v8: i64 = x509_validity_get(buf, cert, nb_p, na_p) 154 if v8 != NX_X509_VALID_INVERTED { return 18 } 155 156 // ---- Test I: bad ASN.1 tag rejected ---- 157 let n7a: i64 = emit_utctime(buf, 0, 2023, 1, 1, 0, 0, 0) 158 let n7b: i64 = emit_utctime(buf, n7a, 2026, 1, 1, 0, 0, 0) 159 buf[0] = 0x99 as u8 // mangle first tag 160 cert.validity_off = 0 161 cert.validity_len = n7a + n7b 162 let v9: i64 = x509_validity_get(buf, cert, nb_p, na_p) 163 if v9 != NX_X509_VALID_BAD_TAG { return 19 } 164 165 // ---- Test J: bad inner length rejected ---- 166 let n8a: i64 = emit_utctime(buf, 0, 2023, 1, 1, 0, 0, 0) 167 let n8b: i64 = emit_utctime(buf, n8a, 2026, 1, 1, 0, 0, 0) 168 buf[1] = 99 as u8 // claim 99-byte UTCTime 169 cert.validity_off = 0 170 cert.validity_len = n8a + n8b 171 let v10: i64 = x509_validity_get(buf, cert, nb_p, na_p) 172 if v10 != NX_X509_VALID_BAD_LENGTH { return 20 } 173 174 // ---- Test K: long-form length rejected (no Time uses long form) ---- 175 let n9a: i64 = emit_utctime(buf, 0, 2023, 1, 1, 0, 0, 0) 176 let n9b: i64 = emit_utctime(buf, n9a, 2026, 1, 1, 0, 0, 0) 177 buf[1] = 0x82 as u8 // long-form indicator 178 cert.validity_off = 0 179 cert.validity_len = n9a + n9b 180 let v11: i64 = x509_validity_get(buf, cert, nb_p, na_p) 181 if v11 != NX_X509_VALID_BAD_LENGTH { return 21 } 182 183 // ---- Test L: empty validity body rejected ---- 184 cert.validity_off = 0 185 cert.validity_len = 0 186 let v12: i64 = x509_validity_get(buf, cert, nb_p, na_p) 187 if v12 != NX_X509_VALID_NO_VALIDITY { return 22 } 188 189 // ---- Test M: validity body too short for two TLVs ---- 190 let n10a: i64 = emit_utctime(buf, 0, 2023, 1, 1, 0, 0, 0) 191 cert.validity_off = 0 192 cert.validity_len = n10a + 1 // 16: room for first + 1 leftover 193 let v13: i64 = x509_validity_get(buf, cert, nb_p, na_p) 194 if v13 != NX_X509_VALID_BAD_FORMAT { return 23 } 195 196 // ---- Test N: trailing bytes (over-long body) rejected ---- 197 let n11a: i64 = emit_utctime(buf, 0, 2023, 1, 1, 0, 0, 0) 198 let n11b: i64 = emit_utctime(buf, n11a, 2026, 1, 1, 0, 0, 0) 199 cert.validity_off = 0 200 cert.validity_len = n11a + n11b + 3 // 3 extra trailing bytes 201 let v14: i64 = x509_validity_get(buf, cert, nb_p, na_p) 202 if v14 != NX_X509_VALID_BAD_FORMAT { return 24 } 203 204 // ---- Test O: verdict gate ---- 205 if nx_x509_validity_verdict_is_valid(NX_X509_VALID_OK) != 1 { return 25 } 206 if nx_x509_validity_verdict_is_valid(NX_X509_VALID_NOT_YET_VALID) != 1 { return 26 } 207 if nx_x509_validity_verdict_is_valid(NX_X509_VALID_EXPIRED) != 1 { return 27 } 208 if nx_x509_validity_verdict_is_valid(NX_X509_VALID_BAD_FORMAT) != 1 { return 28 } 209 if nx_x509_validity_verdict_is_valid(NX_X509_VALID_INVERTED) != 1 { return 29 } 210 if nx_x509_validity_verdict_is_valid(NX_X509_VALID_VERDICT_N) != 0 { return 30 } 211 if nx_x509_validity_verdict_is_valid(0) != 0 { return 31 } 212 if nx_x509_validity_verdict_is_valid(0 - 1) != 0 { return 32 } 213 if nx_x509_validity_verdict_is_valid(999) != 0 { return 33 } 214 215 return 0 216}