109 lines
2.9 KiB
Rust
109 lines
2.9 KiB
Rust
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<u8>,
|
|
}
|
|
|
|
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<u64> {
|
|
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: <https://www.iana.org/assignments/rpki/rpki.xhtml>
|
|
/// 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",
|
|
];
|