use x509_parser::asn1_rs::Tag; use x509_parser::x509::AlgorithmIdentifier; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Asn1TimeEncoding { UtcTime, GeneralizedTime, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Asn1TimeUtc { pub utc: time::OffsetDateTime, pub encoding: Asn1TimeEncoding, } impl Asn1TimeUtc { /// Validate Time encoding rules (RFC 5280): years 1950-2049 use UTCTime, /// other years use GeneralizedTime. pub fn validate_encoding_rfc5280( &self, field: &'static str, ) -> Result<(), InvalidTimeEncodingError> { let year = self.utc.year(); let expected = if year <= 2049 { Asn1TimeEncoding::UtcTime } else { Asn1TimeEncoding::GeneralizedTime }; if self.encoding != expected { return Err(InvalidTimeEncodingError { field, year, encoding: self.encoding, }); } Ok(()) } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigUnsigned { /// Minimal big-endian bytes. For zero, this is `[0]`. pub bytes_be: Vec, } impl BigUnsigned { pub fn from_biguint(n: &der_parser::num_bigint::BigUint) -> Self { let mut bytes = n.to_bytes_be(); if bytes.is_empty() { bytes.push(0); } Self { bytes_be: bytes } } pub fn to_hex_upper(&self) -> String { hex::encode_upper(&self.bytes_be) } pub fn to_u64(&self) -> Option { if self.bytes_be.len() > 8 { return None; } let mut value: u64 = 0; for &b in &self.bytes_be { value = (value << 8) | (b as u64); } Some(value) } } #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] #[error("{field} time encoding invalid for year {year}: got {encoding:?}")] pub struct InvalidTimeEncodingError { pub field: &'static str, pub year: i32, pub encoding: Asn1TimeEncoding, } pub fn asn1_time_to_model(t: x509_parser::time::ASN1Time) -> Asn1TimeUtc { let encoding = if t.is_utctime() { Asn1TimeEncoding::UtcTime } else { Asn1TimeEncoding::GeneralizedTime }; Asn1TimeUtc { utc: t.to_datetime(), encoding, } } pub fn algorithm_params_absent_or_null(sig: &AlgorithmIdentifier<'_>) -> bool { match sig.parameters.as_ref() { None => true, Some(p) if p.tag() == Tag::Null => true, Some(_p) => false, } } /// Filename extensions registered in IANA "RPKI Repository Name Schemes". /// /// Source: /// Snapshot date: 2026-01-28. /// /// Notes: /// - Includes entries marked TEMPORARY/DEPRECATED by IANA (e.g., `asa`, `gbr`). pub const IANA_RPKI_REPOSITORY_FILENAME_EXTENSIONS: &[&str] = &[ "asa", "cer", "crl", "gbr", "mft", "roa", "sig", "tak", ];