code wiki / (root) / nx_x509_pubkey_alg.nx

nx_x509_pubkey_alg.nx source

↩ module page · 187 lines · 7534 B

1// nx_x509_pubkey_alg.nx -- identify the public-key algorithm of a 2// SubjectPublicKeyInfo by inspecting its AlgorithmIdentifier bytes. 3// 4// Phase 0b §I.4 piece 4 of the chain-walker arc. Parallel to 5// nx_x509_sig_alg.nx (046d5dae) but reads from cert.pubkey_alg_off/_len 6// instead of cert.sig_alg_off/_len. 7// 8// Sealed enum: 9// NX_X509_PUBKEY_ALG_UNKNOWN (0) unrecognized / unsupported 10// NX_X509_PUBKEY_ALG_ED25519 (1) OID 1.3.101.112 (RFC 8410) 11// DER inner: 06 03 2B 65 70 12// NX_X509_PUBKEY_ALG_EC_P256 (2) OID 1.2.840.10045.2.1 (id-ecPublicKey) 13// with namedCurve OID 1.2.840.10045.3.1.7 14// (prime256v1) 15// 16// For EC pubkeys we MUST peek at the second OID (the namedCurve) 17// to distinguish P-256 from P-384 etc. Just checking 18// id-ecPublicKey would let a P-384 pubkey be mis-identified as 19// P-256, causing extract_ec_p256 to fail later with confusing 20// "BAD_LEN" rather than the precise "wrong curve" diagnosis. 21// 22// Per Cardinals 9 (single-responsibility -- identify, not extract), 23// 12 (defensive at boundaries -- bounded reads), and 23 (preamble 24// explains the namedCurve peek + why it matters). 25// 26// Public API: 27// nx_x509_pubkey_alg_identify(buf, off, len) -> alg-enum 28// nx_x509_pubkey_alg_is_supported(alg) -> 0|1 29// 30// license_tier: INDEPENDENT_REDERIVE 31// genealogy_id: international-research-sources/ietf/rfc_5280 + rfc_5480 + 32// ietf/rfc_8410 + sec_g/sec2_v1 33// lineage_id: nishi_x509_pubkey_alg_q10 34 35// nx_safety_envelope: 36// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 37// sil_target: SIL1 38// evidence: [bulk_applied_2026-05-19, x509-pubkey-alg-identify] 39// verdict: NOT_YET_EVALUATED 40 41import "nx_syscalls.nx" 42import "nx_asn1.nx" 43 44const NX_X509_PUBKEY_ALG_UNKNOWN: i64 = 0 45const NX_X509_PUBKEY_ALG_ED25519: i64 = 1 46const NX_X509_PUBKEY_ALG_EC_P256: i64 = 2 47const NX_X509_PUBKEY_ALG_EC_P384: i64 = 3 48// rsaEncryption -- OID 1.2.840.113549.1.1.1 (RFC 8017). 49// Used by RSA-2048+ pubkeys. Distinguished from the SIGNATURE 50// algorithms 1.2.840.113549.1.1.{5,11,12,13,14} -- here we only 51// recognise the encryption (key) algorithm at SPKI position. 52const NX_X509_PUBKEY_ALG_RSA: i64 = 4 53const NX_X509_PUBKEY_ALG_N: i64 = 5 54 55func nx_x509_pubkey_alg_is_supported(alg: i64) -> i64 { 56 if alg <= NX_X509_PUBKEY_ALG_UNKNOWN { return 0 } 57 if alg >= NX_X509_PUBKEY_ALG_N { return 0 } 58 return 1 59} 60 61// Returns 1 if (buf+oid_off) for oid_len bytes matches id-ecPublicKey 62// (1.2.840.10045.2.1). Content bytes: 2A 86 48 CE 3D 02 01 (7 bytes). 63func oid_is_id_ec_public_key(buf: *u8, off: i64, len: i64) -> i64 { 64 if len != 7 { return 0 } 65 if (buf[off] & 0xff) != 0x2A { return 0 } 66 if (buf[off+1] & 0xff) != 0x86 { return 0 } 67 if (buf[off+2] & 0xff) != 0x48 { return 0 } 68 if (buf[off+3] & 0xff) != 0xCE { return 0 } 69 if (buf[off+4] & 0xff) != 0x3D { return 0 } 70 if (buf[off+5] & 0xff) != 0x02 { return 0 } 71 if (buf[off+6] & 0xff) != 0x01 { return 0 } 72 return 1 73} 74 75// Returns 1 if (buf+oid_off) for oid_len bytes matches prime256v1 76// (1.2.840.10045.3.1.7). Content bytes: 2A 86 48 CE 3D 03 01 07 (8 bytes). 77func oid_is_prime256v1(buf: *u8, off: i64, len: i64) -> i64 { 78 if len != 8 { return 0 } 79 if (buf[off] & 0xff) != 0x2A { return 0 } 80 if (buf[off+1] & 0xff) != 0x86 { return 0 } 81 if (buf[off+2] & 0xff) != 0x48 { return 0 } 82 if (buf[off+3] & 0xff) != 0xCE { return 0 } 83 if (buf[off+4] & 0xff) != 0x3D { return 0 } 84 if (buf[off+5] & 0xff) != 0x03 { return 0 } 85 if (buf[off+6] & 0xff) != 0x01 { return 0 } 86 if (buf[off+7] & 0xff) != 0x07 { return 0 } 87 return 1 88} 89 90// Returns 1 if (buf+oid_off) for oid_len bytes matches secp384r1 91// (1.3.132.0.34). Content bytes: 2B 81 04 00 22 (5 bytes). 92func oid_is_secp384r1(buf: *u8, off: i64, len: i64) -> i64 { 93 if len != 5 { return 0 } 94 if (buf[off] & 0xff) != 0x2B { return 0 } 95 if (buf[off+1] & 0xff) != 0x81 { return 0 } 96 if (buf[off+2] & 0xff) != 0x04 { return 0 } 97 if (buf[off+3] & 0xff) != 0x00 { return 0 } 98 if (buf[off+4] & 0xff) != 0x22 { return 0 } 99 return 1 100} 101 102// Returns 1 if (buf+oid_off) for oid_len bytes matches rsaEncryption 103// (1.2.840.113549.1.1.1). Content bytes: 2A 86 48 86 F7 0D 01 01 01 (9 bytes). 104func oid_is_rsa_encryption(buf: *u8, off: i64, len: i64) -> i64 { 105 if len != 9 { return 0 } 106 if (buf[off] & 0xff) != 0x2A { return 0 } 107 if (buf[off+1] & 0xff) != 0x86 { return 0 } 108 if (buf[off+2] & 0xff) != 0x48 { return 0 } 109 if (buf[off+3] & 0xff) != 0x86 { return 0 } 110 if (buf[off+4] & 0xff) != 0xF7 { return 0 } 111 if (buf[off+5] & 0xff) != 0x0D { return 0 } 112 if (buf[off+6] & 0xff) != 0x01 { return 0 } 113 if (buf[off+7] & 0xff) != 0x01 { return 0 } 114 if (buf[off+8] & 0xff) != 0x01 { return 0 } 115 return 1 116} 117 118// Returns 1 if (buf+oid_off) for oid_len bytes matches Ed25519 119// (1.3.101.112). Content bytes: 2B 65 70 (3 bytes). 120func oid_is_ed25519(buf: *u8, off: i64, len: i64) -> i64 { 121 if len != 3 { return 0 } 122 if (buf[off] & 0xff) != 0x2B { return 0 } 123 if (buf[off+1] & 0xff) != 0x65 { return 0 } 124 if (buf[off+2] & 0xff) != 0x70 { return 0 } 125 return 1 126} 127 128// Identify the public-key algorithm at buf[off..off+len), where 129// the bytes are a SubjectPublicKeyInfo.algorithm AlgorithmIdentifier 130// SEQUENCE. Returns NX_X509_PUBKEY_ALG_* enum. 131func nx_x509_pubkey_alg_identify(buf: *u8, off: i64, len: i64) -> i64 { 132 if len < 4 { return NX_X509_PUBKEY_ALG_UNKNOWN } 133 134 let c_raw: *u8 = sys_mmap(32) 135 let c: *Asn1Cursor = c_raw as *Asn1Cursor 136 c.pos = off 137 c.end = off + len 138 139 let outer_len_p: *i64 = sys_mmap(16) as *i64 140 let rc1: i64 = asn1_expect_tag(buf, c, ASN1_SEQUENCE, outer_len_p) 141 if rc1 < 0 { return NX_X509_PUBKEY_ALG_UNKNOWN } 142 let inner_end: i64 = c.pos + *outer_len_p 143 if inner_end > c.end { return NX_X509_PUBKEY_ALG_UNKNOWN } 144 145 // First OID (the algorithm). 146 let oid_len_p: *i64 = sys_mmap(16) as *i64 147 let rc2: i64 = asn1_expect_tag(buf, c, ASN1_OID, oid_len_p) 148 if rc2 < 0 { return NX_X509_PUBKEY_ALG_UNKNOWN } 149 let oid_off: i64 = c.pos 150 let oid_len: i64 = *oid_len_p 151 if c.pos + oid_len > inner_end { return NX_X509_PUBKEY_ALG_UNKNOWN } 152 c.pos = c.pos + oid_len 153 154 // Case 1: Ed25519 (no parameters). 155 if oid_is_ed25519(buf, oid_off, oid_len) == 1 { 156 return NX_X509_PUBKEY_ALG_ED25519 157 } 158 159 // Case 3: rsaEncryption (parameters are NULL or absent; we don't 160 // probe them here). 161 if oid_is_rsa_encryption(buf, oid_off, oid_len) == 1 { 162 return NX_X509_PUBKEY_ALG_RSA 163 } 164 165 // Case 2: id-ecPublicKey -- requires namedCurve OID parameter to 166 // distinguish P-256 from P-384. 167 if oid_is_id_ec_public_key(buf, oid_off, oid_len) == 1 { 168 // Read the parameters: must be a namedCurve OID TLV 169 let curve_len_p: *i64 = sys_mmap(16) as *i64 170 let rc3: i64 = asn1_expect_tag(buf, c, ASN1_OID, curve_len_p) 171 if rc3 < 0 { return NX_X509_PUBKEY_ALG_UNKNOWN } 172 if oid_is_prime256v1(buf, c.pos, *curve_len_p) == 1 { 173 return NX_X509_PUBKEY_ALG_EC_P256 174 } 175 if oid_is_secp384r1(buf, c.pos, *curve_len_p) == 1 { 176 return NX_X509_PUBKEY_ALG_EC_P384 177 } 178 return NX_X509_PUBKEY_ALG_UNKNOWN 179 } 180 181 return NX_X509_PUBKEY_ALG_UNKNOWN 182} 183 184// Compile-only smoke. Real KAT in nx_x509_pubkey_alg_test.nx. 185func main() -> i64 { 186 return 0 187}