nx_x509_pubkey_rsa.nx source
↩ module page · 131 lines · 4357 B
1// nx_x509_pubkey_rsa.nx -- extract an RSA-2048 public key (n, e) from
2// an X.509 SubjectPublicKeyInfo.
3//
4// Parallel to nx_x509_pubkey_ec.nx; reads the cert.pubkey_off/_len
5// bytes (the BIT STRING contents, with the unused-bits byte already
6// stripped by x509_parse). The contents are a DER-encoded
7//
8// RSAPublicKey ::= SEQUENCE {
9// modulus INTEGER, -- n (2048-bit unsigned)
10// publicExponent INTEGER -- e (typically 65537)
11// }
12//
13// Public API:
14// nx_x509_pubkey_extract_rsa(buf, cert, out_n_2048, out_e_p) -> verdict
15//
16// Writes:
17// out_n_2048: 64-limb LE U2048 modulus
18// *out_e_p: i64 public exponent
19//
20// Sealed verdict:
21// NX_X509_PUBKEY_RSA_OK
22// NX_X509_PUBKEY_RSA_BAD_SEQUENCE outer SEQUENCE missing/malformed
23// NX_X509_PUBKEY_RSA_BAD_N n INTEGER missing/out-of-range
24// NX_X509_PUBKEY_RSA_BAD_E e INTEGER missing/oversized
25//
26// Per Cardinals 9, 12, 23.
27//
28// license_tier: INDEPENDENT_REDERIVE
29// genealogy_id: international-research-sources/ietf/rfc_8017 + rfc_5280
30// lineage_id: nishi_x509_pubkey_rsa_q10
31
32// nx_safety_envelope:
33// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
34// sil_target: SIL1
35// evidence: [bulk_applied_2026-05-20, x509-rsa-pubkey-extract]
36// verdict: NOT_YET_EVALUATED
37
38import "nx_syscalls.nx"
39import "nx_asn1.nx"
40import "nx_u2048.nx"
41import "nx_x509.nx"
42
43const NX_X509_PUBKEY_RSA_OK: i64 = 1
44const NX_X509_PUBKEY_RSA_BAD_SEQUENCE: i64 = 2
45const NX_X509_PUBKEY_RSA_BAD_N: i64 = 3
46const NX_X509_PUBKEY_RSA_BAD_E: i64 = 4
47const NX_X509_PUBKEY_RSA_VERDICT_N: i64 = 5
48
49func nx_x509_pubkey_rsa_verdict_is_valid(v: i64) -> i64 {
50 if v < NX_X509_PUBKEY_RSA_OK { return 0 }
51 if v >= NX_X509_PUBKEY_RSA_VERDICT_N { return 0 }
52 return 1
53}
54
55// Extract RSA pubkey from cert's SPKI.
56func nx_x509_pubkey_extract_rsa(buf: *u8, cert: *X509Cert,
57 out_n: *i64, out_e: *i64) -> i64 {
58 let pubkey_off: i64 = cert.pubkey_off
59 let pubkey_len: i64 = cert.pubkey_len
60
61 let c_raw: *u8 = sys_mmap(32)
62 let c: *Asn1Cursor = c_raw as *Asn1Cursor
63 c.pos = pubkey_off
64 c.end = pubkey_off + pubkey_len
65
66 // Outer SEQUENCE
67 let seq_len_p: *i64 = sys_mmap(16) as *i64
68 let rc1: i64 = asn1_expect_tag(buf, c, ASN1_SEQUENCE, seq_len_p)
69 if rc1 < 0 { return NX_X509_PUBKEY_RSA_BAD_SEQUENCE }
70 let seq_end: i64 = c.pos + *seq_len_p
71 if seq_end > c.end { return NX_X509_PUBKEY_RSA_BAD_SEQUENCE }
72
73 // n INTEGER
74 let n_len_p: *i64 = sys_mmap(16) as *i64
75 let rc2: i64 = asn1_expect_tag(buf, c, ASN1_INTEGER, n_len_p)
76 if rc2 < 0 { return NX_X509_PUBKEY_RSA_BAD_N }
77 let n_off: i64 = c.pos
78 let n_len: i64 = *n_len_p
79 c.pos = c.pos + n_len
80 if c.pos > seq_end { return NX_X509_PUBKEY_RSA_BAD_N }
81
82 // DER may include a leading 0x00 sign byte if MSB set.
83 var skip: i64 = 0
84 var mag_len: i64 = n_len
85 if n_len == 257 {
86 if (buf[n_off] & 0xff) != 0 { return NX_X509_PUBKEY_RSA_BAD_N }
87 skip = 1
88 mag_len = 256
89 }
90 if mag_len > 256 { return NX_X509_PUBKEY_RSA_BAD_N }
91 if mag_len < 1 { return NX_X509_PUBKEY_RSA_BAD_N }
92
93 // Zero-pad up to 256 bytes BE then load as u2048.
94 let padded: *u8 = sys_mmap(256)
95 var i: i64 = 0
96 while i < 256 { padded[i] = 0; i = i + 1 }
97 let pad_off: i64 = 256 - mag_len
98 i = 0
99 while i < mag_len {
100 padded[pad_off + i] = buf[n_off + skip + i]
101 i = i + 1
102 }
103 u2048_load_be(out_n, padded)
104
105 // e INTEGER (small; we accept up to 8 bytes magnitude)
106 let e_len_p: *i64 = sys_mmap(16) as *i64
107 let rc3: i64 = asn1_expect_tag(buf, c, ASN1_INTEGER, e_len_p)
108 if rc3 < 0 { return NX_X509_PUBKEY_RSA_BAD_E }
109 let e_off: i64 = c.pos
110 let e_len: i64 = *e_len_p
111 c.pos = c.pos + e_len
112 if c.pos > seq_end { return NX_X509_PUBKEY_RSA_BAD_E }
113 if e_len < 1 { return NX_X509_PUBKEY_RSA_BAD_E }
114 if e_len > 8 { return NX_X509_PUBKEY_RSA_BAD_E }
115
116 var e_val: i64 = 0
117 var skip_e: i64 = 0
118 if (buf[e_off] & 0xff) == 0 && e_len > 1 { skip_e = 1 }
119 i = skip_e
120 while i < e_len {
121 e_val = (e_val << 8) | (buf[e_off + i] & 0xff)
122 i = i + 1
123 }
124 *out_e = e_val
125
126 return NX_X509_PUBKEY_RSA_OK
127}
128
129func main() -> i64 {
130 return 0
131}