From f5b7da921a99c139e6df0bb0775e45f714cc9936 Mon Sep 17 00:00:00 2001 From: xuxt Date: Tue, 3 Feb 2026 10:01:32 +0800 Subject: [PATCH] =?UTF-8?q?RC=E8=AF=81=E4=B9=A6=E8=A7=A3=E6=9E=90=E9=83=A8?= =?UTF-8?q?=E5=88=86=E4=BB=A3=E7=A0=81=20(#2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: xiuting.xu Reviewed-on: http://git.nasp.fit/NASP/rpki/pulls/2 Reviewed-by: yuyr Co-authored-by: xuxt Co-committed-by: xuxt --- Cargo.toml | 3 + src/data_model/oids.rs | 3 + src/data_model/rc.rs | 900 +++++++++++++---------- src/data_model/resources/ip_resources.rs | 5 +- src/data_model/resources/mod.rs | 2 +- 5 files changed, 520 insertions(+), 393 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6928bc2..a15d38d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,6 @@ thiserror = "2.0.18" time = "0.3.45" x509-parser = { version = "0.18.0", features = ["verify"] } url = "2.5.8" +asn1-rs = "0.7.1" +asn1-rs-derive = "0.6.0" +asn1 = "0.23.0" diff --git a/src/data_model/oids.rs b/src/data_model/oids.rs index 08cd5b4..31e1121 100644 --- a/src/data_model/oids.rs +++ b/src/data_model/oids.rs @@ -11,3 +11,6 @@ pub const OID_AD_OCSP: &str = "1.3.6.1.5.5.7.48.1"; pub const OID_SUBJECT_INFO_ACCESS: &str = "1.3.6.1.5.5.7.1.11"; pub const OID_CERTIFICATE_POLICIES: &str = "2.5.29.32"; pub const OID_SHA256_WITH_RSA_ENCRYPTION: &str = "1.2.840.113549.1.1.11"; +pub const OID_IP_ADDRESS_BLOCKS: &str = "1.3.6.1.5.5.7.1.7"; +pub const OID_AS_IDENTIFIERS: &str = "1.3.6.1.5.5.7.1.8"; +pub const OID_RPKI_CP: &str = "1.3.6.1.5.5.7.14.2"; \ No newline at end of file diff --git a/src/data_model/rc.rs b/src/data_model/rc.rs index cc9e224..4dfdd3e 100644 --- a/src/data_model/rc.rs +++ b/src/data_model/rc.rs @@ -1,3 +1,4 @@ +use asn1::{parse, BitString}; use der_parser::asn1_rs::Tag; use der_parser::num_bigint::BigUint; use url::Url; @@ -5,9 +6,11 @@ use time::OffsetDateTime; use x509_parser::x509::AlgorithmIdentifier; use x509_parser::prelude::{Validity, KeyUsage, X509Certificate, FromDer, X509Version, X509Extension, ParsedExtension, - CRLDistributionPoints, DistributionPointName, GeneralName}; + DistributionPointName, GeneralName}; use crate::data_model::crl::CrlDecodeError; -use crate::data_model::resources::ip_resources::IPAddrBlocks; +use crate::data_model::resources::ip_resources::{Afi, IPAddrBlocks, IPAddress, IPAddressChoice, + IPAddressOrRange, IPAddressPrefix, IPAddressRange, + IPAddressFamily}; use crate::data_model::resources::as_resources::ASIdentifiers; use crate::data_model::oids; @@ -127,393 +130,510 @@ pub enum ResourceCertError { #[error("Empty AuthorityInfoAccess!")] EmptyAuthorityInfoAccess, + #[error("Certificate Policies must exists one policy")] + CertificatePoliciesTooMany, + + #[error("Certificate Policies invalid")] + CertificatePoliciesInvalid, + } -// impl ResourceCert{ -// pub fn from_der(cert_der: &[u8]) -> Result { -// let (rem, x509_rc) = X509Certificate::from_der(cert_der) -// .map_err(|e| ResourceCertError::ParseCert(e.to_string()))?; -// -// if !rem.is_empty() { -// return Err(ResourceCertError::TrailingBytes(rem.len())); -// } -// -// // 校验 -// parse_and_validate_cert(x509_rc) -// } -// -// -// -// } -// -// fn parse_and_validate_cert(x509_rc: X509Certificate) -> Result { -// ///逐个校验RC的内容, 如果有任何一个校验失败, 则返回错误 -// -// // 1. 版本号必须是V3 -// let version = match x509_rc.version() { -// X509Version::V3 => X509Version::V3, -// v => { -// return Err(ResourceCertError::InvalidVersion(v.0)); -// } -// }; -// -// // 2.校验签名算法 -// // 2.1. 校验外层的签名算法与里层的一致 -// let outer = &x509_rc.signature_algorithm; -// let inner = &x509_rc.tbs_certificate.signature; -// -// if outer.algorithm != inner.algorithm || outer.parameters != inner.parameters { -// return Err(ResourceCertError::SignatureAlgorithmMismatch); -// } -// //2.2 RPKI的签名算法必须是rsaWithSHA256 -// let signature_algorithm = &x509_rc.signature_algorithm; -// if signature_algorithm.algorithm.to_id_string() != oids::OID_SHA256_WITH_RSA_ENCRYPTION { -// return Err(ResourceCertError::UnsupportedSignatureAlgorithm); -// } -// validate_sig_params(signature_algorithm)?; -// -// // 3. 校验Validity -// let validity = x509_rc.validity(); -// validate_validity(validity, OffsetDateTime::now_utc())?; -// -// // 4. SubjectPublicKeyInfo -// let subject_public_key_info = x509_rc.tbs_certificate.subject_pki; -// -// let extensions = parse_and_validate_extensions(x509_rc.extensions())?; -// -// Ok(ResourceCert { -// cert_der: x509_rc.to_der().to_vec(), -// version: version.0, -// serial_number: x509_rc.serial(), -// signature_algorithm_oid: signature_algorithm.algorithm.to_id_string(), -// issuer_dn: x509_rc.issuer().to_string(), -// subject_dn: x509_rc.subject().to_string(), -// validity, -// subject_public_key_info: SubjectPublicKeyInfo { -// // algorithm_oid: x509_rc.tbs_certificate.subject_pki.algorithm.algorithm.to_id_string(), -// // subject_public_key: x509_rc.tbs_certificate.subject_pki.subject_public_key.unused_bits, -// }, -// extensions, -// }) -// -// -// } -// -// fn validate_sig_params(sig: &AlgorithmIdentifier<'_>) -> Result<(), CrlDecodeError> { -// match sig.parameters.as_ref() { -// None => Ok(()), -// Some(p) if p.tag() == Tag::Null => Ok(()), -// Some(_p) => Err(CrlDecodeError::InvalidSignatureAlgorithmParameters), -// } -// } -// -// fn validate_validity( -// validity: &Validity, -// now: OffsetDateTime, -// ) -> Result<(), ResourceCertError> { -// let not_before = validity.not_before.to_datetime(); -// let not_after = validity.not_after.to_datetime(); -// -// if not_after < not_before { -// return Err(ResourceCertError::InvalidValidityRange); -// } -// -// if now < not_before { -// return Err(ResourceCertError::NotYetValid); -// } -// -// if now > not_after { -// return Err(ResourceCertError::Expired); -// } -// -// Ok(()) -// } -// -// -// pub fn parse_and_validate_extensions( -// exts: &[X509Extension<'_>], -// ) -> Result { -// let mut basic_constraints = None; -// let mut ip_addr_blocks = None; -// let mut as_identifiers = None; -// let mut ski = None; -// let mut aki = None; -// let mut crl_dp = None; -// let mut aia = None; -// let mut sia = None; -// let mut key_usage = None; -// let mut extended_key_usage = None; -// let mut certificate_policies = None; -// -// for ext in exts { -// let oid = ext.oid.to_id_string(); -// let critical = ext.critical; -// match oid.as_str() { -// oids::OID_BASIC_CONSTRAINTS => { -// if basic_constraints.is_some() { -// return Err(ResourceCertError::DuplicateExtension("basicConstraints".into())); -// } -// if !critical { -// return Err(ResourceCertError::CriticalError("basicConstraints".into(), "critical".into())); -// } -// let bc = parse_basic_constraints(ext)?; -// basic_constraints = Some(bc); -// } -// oids::OID_SUBJECT_KEY_IDENTIFIER => { -// if ski.is_some() { -// return Err(ResourceCertError::DuplicateExtension("subjectKeyIdentifier".into())); -// } -// if critical { -// return Err(ResourceCertError::CriticalError("subjectKeyIdentifier".into(), "non-critical".into())); -// } -// let s = parse_subject_key_identifier(ext)?; -// ski = Some(s); -// } -// oids::OID_AUTHORITY_KEY_IDENTIFIER => { -// if aki.is_some() { -// return Err(ResourceCertError::DuplicateExtension("authorityKeyIdentifier".into())); -// } -// if critical { -// return Err(ResourceCertError::CriticalError("authorityKeyIdentifier".into(), "non-critical".into())); -// } -// let a = parse_authority_key_identifier(ext)?; -// aki = Some(a); -// } -// oids::OID_KEY_USAGE => { -// if key_usage.is_some() { -// return Err(ResourceCertError::DuplicateExtension("keyUsage".into())); -// } -// if !critical { -// return Err(ResourceCertError::CriticalError("keyUsage".into(), "critical".into())); -// } -// let ku = parse_key_usage(ext)?; -// key_usage = Some(ku); -// } -// oids::OID_EXTENDED_KEY_USAGE => { -// if extended_key_usage.is_some() { -// return Err(ResourceCertError::DuplicateExtension("extendedKeyUsage".into())); -// } -// if critical { -// return Err(ResourceCertError::CriticalError("extendedKeyUsage".into(), "non-critical".into())); -// } -// let eku = oids::OID_EXTENDED_KEY_USAGE; -// } -// oids::OID_CRL_DISTRIBUTION_POINTS => { -// if crl_dp.is_some() { -// return Err(ResourceCertError::DuplicateExtension("crlDistributionPoints".into())); -// } -// if critical { -// return Err(ResourceCertError::CriticalError("crlDistributionPoints".into(), "non-critical".into())); -// } -// let cdp = parse_crl_distribution_points(ext)?; -// crl_dp = Some(cdp); -// } -// oids::OID_AUTHORITY_INFO_ACCESS => { -// if aia.is_some() { -// return Err(ResourceCertError::DuplicateExtension("authorityInfoAccess".into())); -// } -// if critical { -// return Err(ResourceCertError::CriticalError("authorityInfoAccess".into(), "non-critical".into())); -// } -// let p_aia = parse_authority_info_access(ext)?; -// aia = Some(p_aia); -// } -// oids::OID_SUBJECT_INFO_ACCESS => { -// if sia.is_some() { -// return Err(ResourceCertError::DuplicateExtension("subjectInfoAccess".into())); -// } -// if critical { -// return Err(ResourceCertError::CriticalError("subjectInfoAccess".into(), "non-critical".into())); -// } -// let p_sia = parse_subject_info_access(ext)?; -// sia = Some(p_sia); -// } -// oids::OID_CERTIFICATE_POLICIES => { -// if certificate_policies.is_some() { -// return Err(ResourceCertError::DuplicateExtension("certificatePolicies".into())); -// } -// if !critical { -// return Err(ResourceCertError::CriticalError("certificatePolicies".into(), "critical".into())); -// } -// let p_cp = parse_certificate_policies(ext)?; -// certificate_policies = Some(p_cp); -// } -// } -// -// -// } -// Ok(RcExtension { -// basic_constraints, -// ip_addr_blocks, -// as_identifiers, -// subject_key_id: ski, -// authority_key_id: aki, -// crl_distribution_points: crl_dp, -// authority_info_access: aia, -// }) -// } -// -// fn parse_basic_constraints(ext: &X509Extension<'_>) -> Result { -// let ParsedExtension::BasicConstraints(bc) = ext.parsed_extension() else { -// return Err(ResourceCertError::ParseCert("basicConstraints parse failed".into())); -// }; -// Ok(bc.ca) -// } -// -// fn parse_subject_key_identifier(ext: &X509Extension<'_>) -> Result, ResourceCertError> { -// let ParsedExtension::SubjectKeyIdentifier(s) = ext.parsed_extension() else { -// return Err(ResourceCertError::ParseCert("subjectKeyIdentifier parse failed".into())); -// }; -// Ok(s.0.to_vec()) -// } -// -// fn parse_authority_key_identifier(ext: &X509Extension<'_>) -> Result, ResourceCertError> { -// let ParsedExtension::AuthorityKeyIdentifier(aki) = ext.parsed_extension() else { -// return Err(ResourceCertError::ParseCert("authorityKeyIdentifier parse failed".into())); -// }; -// let key_id = aki -// .key_identifier -// .as_ref() -// .ok_or(ResourceCertError::MissingParameter("key_identifier".into()))?; -// -// if aki.authority_cert_issuer.is_some() { -// return Err(ResourceCertError::UnexceptedParameter("authority_cert_issuer".into())); -// } -// if aki.authority_cert_serial.is_some() { -// return Err(ResourceCertError::UnexceptedParameter("authority_cert_serial".into())); -// } -// -// -// Ok(key_id.0.to_vec()) -// } -// -// fn parse_key_usage(ext: &X509Extension<'_>) -> Result { -// let ParsedExtension::KeyUsage(ku) = ext.parsed_extension() else { -// return Err(ResourceCertError::ParseCert("keyUsage parse failed".into())); -// }; -// Ok(ku.clone()) -// } -// -// fn parse_crl_distribution_points(ext: &X509Extension<'_>) -> Result, ResourceCertError> { -// let ParsedExtension::CRLDistributionPoints(cdp) = ext.parsed_extension() else { -// return Err(ResourceCertError::ParseCert("crlDistributionPoints parse failed".into())); -// }; -// let mut urls = Vec::new(); -// for point in cdp.points.iter() { -// if point.reasons.is_some() { -// return Err(ResourceCertError::UnexceptedParameter("reasons".into())); -// } -// if point.crl_issuer.is_some() { -// return Err(ResourceCertError::UnexceptedParameter("crl_issuer".into())); -// } -// -// let dp_name = point.distribution_point.as_ref() -// .ok_or(ResourceCertError::MissingParameter("distribution_point".into()))?; -// match dp_name { -// DistributionPointName::FullName(names) => { -// for name in names { -// match name { -// GeneralName::URI(uri) => { -// let url = Url::parse(uri) -// .map_err(|_| ResourceCertError::InvalidUri(uri.to_string()))?; -// urls.push(url); -// } -// _ => { -// return Err(ResourceCertError::UnsupportedGeneralName("distribution_point".into())); -// } -// } -// } -// -// } -// DistributionPointName::NameRelativeToCRLIssuer(_) => { -// return Err(ResourceCertError::UnsupportedCrlDistributionPoint); -// } -// } -// } -// if urls.is_empty() { -// return Err(ResourceCertError::MissingParameter("distribution_point".into())); -// } -// Ok(urls) -// } -// -// fn parse_authority_info_access( -// ext: &X509Extension<'_>, -// ) -> Result, ResourceCertError> { -// let ParsedExtension::AuthorityInfoAccess(aia) = ext.parsed_extension() else { -// return Err(ResourceCertError::ParseCert( -// "authorityInfoAccess parse failed".into(), -// )); -// }; -// -// let mut access_descriptions = Vec::new(); -// -// for access in &aia.accessdescs { -// let access_method_oid = access.access_method.to_id_string(); -// -// let uri = match &access.access_location { -// GeneralName::URI(uri) => uri, -// _ => { -// return Err(ResourceCertError::InvalidAccessLocationType); -// } -// }; -// -// let url = Url::parse(uri) -// .map_err(|_| ResourceCertError::InvalidUri(uri.to_string()))?; -// -// access_descriptions.push(AccessDescription { -// access_method_oid, -// access_location: url, -// }); -// } -// -// if access_descriptions.is_empty() { -// return Err(ResourceCertError::EmptyAuthorityInfoAccess); -// } -// -// Ok(access_descriptions) -// } -// -// fn parse_subject_info_access(ext: &X509Extension<'_>) -> Result, ResourceCertError> { -// let ParsedExtension::SubjectInfoAccess(sia) = ext.parsed_extension() else { -// return Err(ResourceCertError::ParseCert( -// "subjectInfoAccess parse failed".into(), -// )); -// }; -// let mut access_descriptions = Vec::new(); -// -// for access in &sia.accessdescs { -// let access_method_oid = access.access_method.to_id_string(); -// -// // accessLocation: MUST be URI in RPKI -// let uri = match &access.access_location { -// GeneralName::URI(uri) => uri, -// _ => { -// return Err(ResourceCertError::InvalidAccessLocationType); -// } -// }; -// -// let url = Url::parse(uri) -// .map_err(|_| ResourceCertError::InvalidUri(uri.to_string()))?; -// -// access_descriptions.push(AccessDescription { -// access_method_oid, -// access_location: url, -// }); -// } -// -// if access_descriptions.is_empty() { -// return Err(ResourceCertError::EmptyAuthorityInfoAccess); -// } -// -// Ok(access_descriptions) -// } -// -// fn parse_certificate_policies(ext: &X509Extension<'_>) -> Result, ResourceCertError> { -// let ParsedExtension::CertificatePolicies(cp) = ext.parsed_extension() else { -// return Err(ResourceCertError::ParseCert( -// "certificatePolicies parse failed".into(), -// )); -// }; -// let mut policies = Vec::new(); -// -// } +impl ResourceCert{ + pub fn from_der(cert_der: &[u8]) -> Result { + let (rem, x509_rc) = X509Certificate::from_der(cert_der) + .map_err(|e| ResourceCertError::ParseCert(e.to_string()))?; + + if !rem.is_empty() { + return Err(ResourceCertError::TrailingBytes(rem.len())); + } + + // 校验 + parse_and_validate_cert(x509_rc) + } + + + +} + +fn parse_and_validate_cert(x509_rc: X509Certificate) -> Result { + ///逐个校验RC的内容, 如果有任何一个校验失败, 则返回错误 + + // 1. 版本号必须是V3 + let version = match x509_rc.version() { + X509Version::V3 => X509Version::V3, + v => { + return Err(ResourceCertError::InvalidVersion(v.0)); + } + }; + + // 2.校验签名算法 + // 2.1. 校验外层的签名算法与里层的一致 + let outer = &x509_rc.signature_algorithm; + let inner = &x509_rc.tbs_certificate.signature; + + if outer.algorithm != inner.algorithm || outer.parameters != inner.parameters { + return Err(ResourceCertError::SignatureAlgorithmMismatch); + } + //2.2 RPKI的签名算法必须是rsaWithSHA256 + let signature_algorithm = &x509_rc.signature_algorithm; + if signature_algorithm.algorithm.to_id_string() != oids::OID_SHA256_WITH_RSA_ENCRYPTION { + return Err(ResourceCertError::UnsupportedSignatureAlgorithm); + } + validate_sig_params(signature_algorithm)?; + + // 3. 校验Validity + let validity = x509_rc.validity(); + validate_validity(validity, OffsetDateTime::now_utc())?; + + // 4. SubjectPublicKeyInfo + let subject_public_key_info = x509_rc.tbs_certificate.subject_pki; + + let extensions = parse_and_validate_extensions(x509_rc.extensions())?; + + // TODO + Ok(ResourceCert { + + }) + + +} + +fn validate_sig_params(sig: &AlgorithmIdentifier<'_>) -> Result<(), CrlDecodeError> { + match sig.parameters.as_ref() { + None => Ok(()), + Some(p) if p.tag() == Tag::Null => Ok(()), + Some(_p) => Err(CrlDecodeError::InvalidSignatureAlgorithmParameters), + } +} + +fn validate_validity( + validity: &Validity, + now: OffsetDateTime, +) -> Result<(), ResourceCertError> { + let not_before = validity.not_before.to_datetime(); + let not_after = validity.not_after.to_datetime(); + + if not_after < not_before { + return Err(ResourceCertError::InvalidValidityRange); + } + + if now < not_before { + return Err(ResourceCertError::NotYetValid); + } + + if now > not_after { + return Err(ResourceCertError::Expired); + } + + Ok(()) +} + + +pub fn parse_and_validate_extensions( + exts: &[X509Extension<'_>], +) -> Result { + let mut basic_constraints = None; + let mut ip_addr_blocks = None; + let mut as_identifiers = None; + let mut ski = None; + let mut aki = None; + let mut crl_dp = None; + let mut aia = None; + let mut sia = None; + let mut key_usage = None; + let mut extended_key_usage = None; + let mut certificate_policies = None; + + for ext in exts { + let oid = ext.oid.to_id_string(); + let critical = ext.critical; + match oid.as_str() { + oids::OID_BASIC_CONSTRAINTS => { + if basic_constraints.is_some() { + return Err(ResourceCertError::DuplicateExtension("basicConstraints".into())); + } + if !critical { + return Err(ResourceCertError::CriticalError("basicConstraints".into(), "critical".into())); + } + let bc = parse_basic_constraints(ext)?; + basic_constraints = Some(bc); + } + oids::OID_SUBJECT_KEY_IDENTIFIER => { + if ski.is_some() { + return Err(ResourceCertError::DuplicateExtension("subjectKeyIdentifier".into())); + } + if critical { + return Err(ResourceCertError::CriticalError("subjectKeyIdentifier".into(), "non-critical".into())); + } + let s = parse_subject_key_identifier(ext)?; + ski = Some(s); + } + oids::OID_AUTHORITY_KEY_IDENTIFIER => { + if aki.is_some() { + return Err(ResourceCertError::DuplicateExtension("authorityKeyIdentifier".into())); + } + if critical { + return Err(ResourceCertError::CriticalError("authorityKeyIdentifier".into(), "non-critical".into())); + } + let a = parse_authority_key_identifier(ext)?; + aki = Some(a); + } + oids::OID_KEY_USAGE => { + if key_usage.is_some() { + return Err(ResourceCertError::DuplicateExtension("keyUsage".into())); + } + if !critical { + return Err(ResourceCertError::CriticalError("keyUsage".into(), "critical".into())); + } + let ku = parse_key_usage(ext)?; + key_usage = Some(ku); + } + oids::OID_EXTENDED_KEY_USAGE => { + if extended_key_usage.is_some() { + return Err(ResourceCertError::DuplicateExtension("extendedKeyUsage".into())); + } + if critical { + return Err(ResourceCertError::CriticalError("extendedKeyUsage".into(), "non-critical".into())); + } + let eku = oids::OID_EXTENDED_KEY_USAGE; + } + oids::OID_CRL_DISTRIBUTION_POINTS => { + if crl_dp.is_some() { + return Err(ResourceCertError::DuplicateExtension("crlDistributionPoints".into())); + } + if critical { + return Err(ResourceCertError::CriticalError("crlDistributionPoints".into(), "non-critical".into())); + } + let cdp = parse_crl_distribution_points(ext)?; + crl_dp = Some(cdp); + } + oids::OID_AUTHORITY_INFO_ACCESS => { + if aia.is_some() { + return Err(ResourceCertError::DuplicateExtension("authorityInfoAccess".into())); + } + if critical { + return Err(ResourceCertError::CriticalError("authorityInfoAccess".into(), "non-critical".into())); + } + let p_aia = parse_authority_info_access(ext)?; + aia = Some(p_aia); + } + oids::OID_SUBJECT_INFO_ACCESS => { + if sia.is_some() { + return Err(ResourceCertError::DuplicateExtension("subjectInfoAccess".into())); + } + if critical { + return Err(ResourceCertError::CriticalError("subjectInfoAccess".into(), "non-critical".into())); + } + let p_sia = parse_subject_info_access(ext)?; + sia = Some(p_sia); + } + oids::OID_CERTIFICATE_POLICIES => { + if certificate_policies.is_some() { + return Err(ResourceCertError::DuplicateExtension("certificatePolicies".into())); + } + if !critical { + return Err(ResourceCertError::CriticalError("certificatePolicies".into(), "critical".into())); + } + let p_cp = parse_certificate_policies(ext)?; + certificate_policies = Some(p_cp); + } + oids::OID_IP_ADDRESS_BLOCKS => { + if ip_addr_blocks.is_some() { + return Err(ResourceCertError::DuplicateExtension("ipAddressBlocks".into())); + } + if !critical { + return Err(ResourceCertError::CriticalError("ipAddressBlocks".into(), "critical".into())); + } + let p_ip = parse_ip_address_blocks(ext)?; + ip_addr_blocks = Some(p_ip); + } + oids::OID_AS_IDENTIFIERS => { + if as_identifiers.is_some() { + return Err(ResourceCertError::DuplicateExtension("asIdentifiers".into())); + } + if !critical { + return Err(ResourceCertError::CriticalError("asIdentifiers".into(), "critical".into())); + } + let p_as = parse_as_identifiers(ext)?; + as_identifiers = Some(p_as); + } + } + } + + // TODO: + Ok(RcExtension { + + } +} + +fn parse_basic_constraints(ext: &X509Extension<'_>) -> Result { + let ParsedExtension::BasicConstraints(bc) = ext.parsed_extension() else { + return Err(ResourceCertError::ParseCert("basicConstraints parse failed".into())); + }; + Ok(bc.ca) +} + +fn parse_subject_key_identifier(ext: &X509Extension<'_>) -> Result, ResourceCertError> { + let ParsedExtension::SubjectKeyIdentifier(s) = ext.parsed_extension() else { + return Err(ResourceCertError::ParseCert("subjectKeyIdentifier parse failed".into())); + }; + Ok(s.0.to_vec()) +} + +fn parse_authority_key_identifier(ext: &X509Extension<'_>) -> Result, ResourceCertError> { + let ParsedExtension::AuthorityKeyIdentifier(aki) = ext.parsed_extension() else { + return Err(ResourceCertError::ParseCert("authorityKeyIdentifier parse failed".into())); + }; + let key_id = aki + .key_identifier + .as_ref() + .ok_or(ResourceCertError::MissingParameter("key_identifier".into()))?; + + if aki.authority_cert_issuer.is_some() { + return Err(ResourceCertError::UnexceptedParameter("authority_cert_issuer".into())); + } + if aki.authority_cert_serial.is_some() { + return Err(ResourceCertError::UnexceptedParameter("authority_cert_serial".into())); + } + + + Ok(key_id.0.to_vec()) +} + +fn parse_key_usage(ext: &X509Extension<'_>) -> Result { + let ParsedExtension::KeyUsage(ku) = ext.parsed_extension() else { + return Err(ResourceCertError::ParseCert("keyUsage parse failed".into())); + }; + Ok(ku.clone()) +} + +fn parse_crl_distribution_points(ext: &X509Extension<'_>) -> Result, ResourceCertError> { + let ParsedExtension::CRLDistributionPoints(cdp) = ext.parsed_extension() else { + return Err(ResourceCertError::ParseCert("crlDistributionPoints parse failed".into())); + }; + let mut urls = Vec::new(); + for point in cdp.points.iter() { + if point.reasons.is_some() { + return Err(ResourceCertError::UnexceptedParameter("reasons".into())); + } + if point.crl_issuer.is_some() { + return Err(ResourceCertError::UnexceptedParameter("crl_issuer".into())); + } + + let dp_name = point.distribution_point.as_ref() + .ok_or(ResourceCertError::MissingParameter("distribution_point".into()))?; + match dp_name { + DistributionPointName::FullName(names) => { + for name in names { + match name { + GeneralName::URI(uri) => { + let url = Url::parse(uri) + .map_err(|_| ResourceCertError::InvalidUri(uri.to_string()))?; + urls.push(url); + } + _ => { + return Err(ResourceCertError::UnsupportedGeneralName("distribution_point".into())); + } + } + } + + } + DistributionPointName::NameRelativeToCRLIssuer(_) => { + return Err(ResourceCertError::UnsupportedCrlDistributionPoint); + } + } + } + if urls.is_empty() { + return Err(ResourceCertError::MissingParameter("distribution_point".into())); + } + Ok(urls) +} + +fn parse_authority_info_access( + ext: &X509Extension<'_>, +) -> Result, ResourceCertError> { + let ParsedExtension::AuthorityInfoAccess(aia) = ext.parsed_extension() else { + return Err(ResourceCertError::ParseCert( + "authorityInfoAccess parse failed".into(), + )); + }; + + let mut access_descriptions = Vec::new(); + + for access in &aia.accessdescs { + let access_method_oid = access.access_method.to_id_string(); + + let uri = match &access.access_location { + GeneralName::URI(uri) => uri, + _ => { + return Err(ResourceCertError::InvalidAccessLocationType); + } + }; + + let url = Url::parse(uri) + .map_err(|_| ResourceCertError::InvalidUri(uri.to_string()))?; + + access_descriptions.push(AccessDescription { + access_method_oid, + access_location: url, + }); + } + + if access_descriptions.is_empty() { + return Err(ResourceCertError::EmptyAuthorityInfoAccess); + } + + Ok(access_descriptions) +} + +fn parse_subject_info_access(ext: &X509Extension<'_>) -> Result, ResourceCertError> { + let ParsedExtension::SubjectInfoAccess(sia) = ext.parsed_extension() else { + return Err(ResourceCertError::ParseCert( + "subjectInfoAccess parse failed".into(), + )); + }; + let mut access_descriptions = Vec::new(); + + for access in &sia.accessdescs { + let access_method_oid = access.access_method.to_id_string(); + + // accessLocation: MUST be URI in RPKI + let uri = match &access.access_location { + GeneralName::URI(uri) => uri, + _ => { + return Err(ResourceCertError::InvalidAccessLocationType); + } + }; + + let url = Url::parse(uri) + .map_err(|_| ResourceCertError::InvalidUri(uri.to_string()))?; + + access_descriptions.push(AccessDescription { + access_method_oid, + access_location: url, + }); + } + + if access_descriptions.is_empty() { + return Err(ResourceCertError::EmptyAuthorityInfoAccess); + } + + Ok(access_descriptions) +} + +fn parse_certificate_policies(ext: &X509Extension<'_>) -> Result, ResourceCertError> { + let ParsedExtension::CertificatePolicies(cp) = ext.parsed_extension() else { + return Err(ResourceCertError::ParseCert( + "certificatePolicies parse failed".into(), + )); + }; + let mut policies = Vec::new(); + if cp.len() > 1 { + return Err(ResourceCertError::CertificatePoliciesTooMany); + } + let policy_info = cp.first().unwrap(); + let policy_id = policy_info.policy_id.to_id_string(); + if policy_id != oids::OID_RPKI_CP { + return Err(ResourceCertError::CertificatePoliciesInvalid); + } + let policy_info = PolicyInformation{ + policy_oid: policy_id, + }; + policies.push(policy_info); + Ok(policies) +} + +fn bitstring_to_ip(b: &BitString, afi: &Afi) -> Result { + let bytes = b.as_bytes(); + let ip = match afi { + Afi::Ipv4 => { + if bytes.len() != 4 { return Err(ResourceCertError::ParseCert("IPv4 length mismatch".into())); } + u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as u128 + }, + Afi::Ipv6 => { + if bytes.len() != 16 { return Err(ResourceCertError::ParseCert("IPv6 length mismatch".into())); } + u128::from_be_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], bytes[6], bytes[7], + bytes[8], bytes[9], bytes[10], bytes[11], + bytes[12], bytes[13], bytes[14], bytes[15], + ]) + }, + }; + Ok(IPAddress(ip)) +} + +fn parse_ip_address_blocks(ext: &X509Extension<'_>) -> Result { + + let ip_blocks_der = ext.value; + + let ips = parse(ip_blocks_der, |p| { + // 顶层 SEQUENCE OF IPAddressFamily + let mut ip_families = Vec::new(); + p.read_sequence_of(|p2| { + // 每个 IPAddressFamily 是 SEQUENCE { addressFamily OCTET STRING, ipAddressChoice } + p2.read_sequence(|p3| { + let address_family_bytes = p3.read_element::<&[u8]>()?; + let afi = match address_family_bytes { + [0,1] => Afi::Ipv4, + [0,2] => Afi::Ipv6, + _ => return Err(asn1::ParseError::new(asn1::ParseErrorKind::InvalidValue)), + }; + + // 解析 IPAddressChoice + let ip_address_choice = { + let peek_tag = p3.peek_tag()?; + match peek_tag.tag_number() { + 5 => IPAddressChoice::Inherit, // NULL + 16 => { // SEQUENCE OF IPAddressOrRange + let ranges = p3.read_sequence_of(|p4| { + // 解析 IPAddressOrRange CHOICE + let peek = p4.peek_tag()?.tag_number(); + if peek == 16 { // SEQUENCE -> AddressPrefix + let (addr_bytes, prefix_len): (&[u8], u8) = p4.read_sequence(|p5| { + let addr = p5.read_element::()?.as_bytes(); + let prefix_len = addr.len() as u8 * 8; // 简化:用字节长度推前缀 + Ok((addr, prefix_len)) + })?; + Ok(IPAddressOrRange::AddressPrefix(IPAddressPrefix{ + address: bitstring_to_ip(addr_bytes, &afi)?, + prefix_length: prefix_len, + })) + } else { + // AddressRange + let (min_bytes, max_bytes) = p4.read_sequence(|p5| { + let min = p5.read_element::()?.as_bytes(); + let max = p5.read_element::()?.as_bytes(); + Ok((min, max)) + })?; + Ok(IPAddressOrRange::AddressRange(IPAddressRange{ + min: bitstring_to_ip(min_bytes, &afi)?, + max: bitstring_to_ip(max_bytes, &afi)?, + })) + } + })?; + IPAddressChoice::AddressOrRange(ranges) + } + _ => return Err(asn1::ParseError::new(asn1::ParseErrorKind::InvalidValue)), + } + }; + + ip_families.push(IPAddressFamily { + address_family: afi, + ip_address_choice, + }); + + Ok(()) + }) + })?; + Ok(IPAddrBlocks { ips: ip_families }) + }).map_err(|_| ResourceCertError::ParseCert("Failed to parse IPAddrBlocks DER".into()))?; + + Ok(ips) +} + +fn parse_as_identifiers(ext: &X509Extension<'_>) -> Result, ResourceCertError> { + let as_identifiers_der = ext.value; + // TODO: 解析 ASIdentifiers DER + +} \ No newline at end of file diff --git a/src/data_model/resources/ip_resources.rs b/src/data_model/resources/ip_resources.rs index 9590bf8..1e754bc 100644 --- a/src/data_model/resources/ip_resources.rs +++ b/src/data_model/resources/ip_resources.rs @@ -1,7 +1,7 @@ #[derive(Clone, Debug, PartialEq, Eq)] pub struct IPAddrBlocks { - ips: Vec + pub ips: Vec } @@ -43,4 +43,5 @@ pub struct IPAddressRange { } #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct IPAddress(u128); \ No newline at end of file +pub struct IPAddress(u128); + diff --git a/src/data_model/resources/mod.rs b/src/data_model/resources/mod.rs index 39284d6..46a9e49 100644 --- a/src/data_model/resources/mod.rs +++ b/src/data_model/resources/mod.rs @@ -1,3 +1,3 @@ pub(crate) mod ip_resources; pub(crate) mod as_resources; -pub mod resource; \ No newline at end of file +pub mod resource;