优化时间表示

This commit is contained in:
yuyr 2026-02-06 15:30:26 +08:00
parent a58e507f92
commit 7be865d7f1
4 changed files with 50 additions and 15 deletions

View File

@ -1,6 +1,8 @@
use x509_parser::asn1_rs::Tag; use x509_parser::asn1_rs::Tag;
use x509_parser::x509::AlgorithmIdentifier; use x509_parser::x509::AlgorithmIdentifier;
pub type UtcTime = time::OffsetDateTime;
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Asn1TimeEncoding { pub enum Asn1TimeEncoding {
UtcTime, UtcTime,
@ -9,7 +11,7 @@ pub enum Asn1TimeEncoding {
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Asn1TimeUtc { pub struct Asn1TimeUtc {
pub utc: time::OffsetDateTime, pub utc: UtcTime,
pub encoding: Asn1TimeEncoding, pub encoding: Asn1TimeEncoding,
} }

View File

@ -1,4 +1,4 @@
use crate::data_model::common::BigUnsigned; use crate::data_model::common::{BigUnsigned, UtcTime};
use crate::data_model::oid::{OID_CT_RPKI_MANIFEST, OID_SHA256}; use crate::data_model::oid::{OID_CT_RPKI_MANIFEST, OID_SHA256};
use crate::data_model::rc::ResourceCertificate; use crate::data_model::rc::ResourceCertificate;
use crate::data_model::signed_object::{ use crate::data_model::signed_object::{
@ -6,7 +6,6 @@ use crate::data_model::signed_object::{
}; };
use der_parser::ber::BerObjectContent; use der_parser::ber::BerObjectContent;
use der_parser::der::{DerObject, Tag, parse_der}; use der_parser::der::{DerObject, Tag, parse_der};
use time::OffsetDateTime;
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct ManifestObject { pub struct ManifestObject {
@ -26,8 +25,8 @@ pub struct ManifestObjectParsed {
pub struct ManifestEContent { pub struct ManifestEContent {
pub version: u32, pub version: u32,
pub manifest_number: BigUnsigned, pub manifest_number: BigUnsigned,
pub this_update: OffsetDateTime, pub this_update: UtcTime,
pub next_update: OffsetDateTime, pub next_update: UtcTime,
pub file_hash_alg: String, pub file_hash_alg: String,
pub files: Vec<FileAndHash>, pub files: Vec<FileAndHash>,
} }
@ -378,7 +377,7 @@ fn parse_manifest_number(obj: &DerObject<'_>) -> Result<BigUnsigned, ManifestPro
fn parse_generalized_time( fn parse_generalized_time(
obj: &DerObject<'_>, obj: &DerObject<'_>,
err: ManifestProfileError, err: ManifestProfileError,
) -> Result<OffsetDateTime, ManifestProfileError> { ) -> Result<UtcTime, ManifestProfileError> {
match &obj.content { match &obj.content {
BerObjectContent::GeneralizedTime(dt) => dt BerObjectContent::GeneralizedTime(dt) => dt
.to_datetime() .to_datetime()

View File

@ -1,12 +1,14 @@
use der_parser::ber::{BerObjectContent, Class}; use der_parser::ber::{BerObjectContent, Class};
use der_parser::der::{DerObject, Tag, parse_der}; use der_parser::der::{DerObject, Tag, parse_der};
use der_parser::num_bigint::BigUint; use der_parser::num_bigint::BigUint;
use time::OffsetDateTime;
use url::Url; use url::Url;
use x509_parser::asn1_rs::{Class as Asn1Class, Tag as Asn1Tag}; use x509_parser::asn1_rs::{Class as Asn1Class, Tag as Asn1Tag};
use x509_parser::extensions::ParsedExtension; use x509_parser::extensions::ParsedExtension;
use x509_parser::prelude::{FromDer, X509Certificate, X509Extension, X509Version}; use x509_parser::prelude::{FromDer, X509Certificate, X509Extension, X509Version};
use crate::data_model::common::{
Asn1TimeUtc, InvalidTimeEncodingError, UtcTime, asn1_time_to_model,
};
use crate::data_model::oid::{ use crate::data_model::oid::{
OID_AD_SIGNED_OBJECT, OID_AUTONOMOUS_SYS_IDS, OID_CP_IPADDR_ASNUMBER, OID_IP_ADDR_BLOCKS, OID_AD_SIGNED_OBJECT, OID_AUTONOMOUS_SYS_IDS, OID_CP_IPADDR_ASNUMBER, OID_IP_ADDR_BLOCKS,
OID_SHA256_WITH_RSA_ENCRYPTION, OID_SUBJECT_INFO_ACCESS, OID_SUBJECT_KEY_IDENTIFIER, OID_SHA256_WITH_RSA_ENCRYPTION, OID_SUBJECT_INFO_ACCESS, OID_SUBJECT_KEY_IDENTIFIER,
@ -41,8 +43,8 @@ pub struct RpkixTbsCertificate {
pub signature_algorithm: String, pub signature_algorithm: String,
pub issuer_dn: String, pub issuer_dn: String,
pub subject_dn: String, pub subject_dn: String,
pub validity_not_before: OffsetDateTime, pub validity_not_before: UtcTime,
pub validity_not_after: OffsetDateTime, pub validity_not_after: UtcTime,
/// DER encoding of SubjectPublicKeyInfo. /// DER encoding of SubjectPublicKeyInfo.
pub subject_public_key_info: Vec<u8>, pub subject_public_key_info: Vec<u8>,
pub extensions: RcExtensions, pub extensions: RcExtensions,
@ -68,8 +70,8 @@ pub struct ResourceCertificateParsed {
pub tbs_signature_algorithm: AlgorithmIdentifierValue, pub tbs_signature_algorithm: AlgorithmIdentifierValue,
pub issuer_dn: String, pub issuer_dn: String,
pub subject_dn: String, pub subject_dn: String,
pub validity_not_before: OffsetDateTime, pub validity_not_before: Asn1TimeUtc,
pub validity_not_after: OffsetDateTime, pub validity_not_after: Asn1TimeUtc,
/// DER encoding of SubjectPublicKeyInfo. /// DER encoding of SubjectPublicKeyInfo.
pub subject_public_key_info: Vec<u8>, pub subject_public_key_info: Vec<u8>,
pub extensions: RcExtensionsParsed, pub extensions: RcExtensionsParsed,
@ -326,6 +328,9 @@ pub enum ResourceCertificateParseError {
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum ResourceCertificateProfileError { pub enum ResourceCertificateProfileError {
#[error("{0}")]
InvalidTimeEncoding(#[from] InvalidTimeEncodingError),
#[error("certificate version must be v3 (RFC 5280 §4.1; RFC 6487 §4)")] #[error("certificate version must be v3 (RFC 5280 §4.1; RFC 6487 §4)")]
InvalidVersion, InvalidVersion,
@ -394,8 +399,8 @@ impl ResourceCertificate {
return Err(ResourceCertificateParseError::TrailingBytes(rem.len())); return Err(ResourceCertificateParseError::TrailingBytes(rem.len()));
} }
let validity_not_before = cert.validity().not_before.to_datetime(); let validity_not_before = asn1_time_to_model(cert.validity().not_before);
let validity_not_after = cert.validity().not_after.to_datetime(); let validity_not_after = asn1_time_to_model(cert.validity().not_after);
let subject_public_key_info = cert.tbs_certificate.subject_pki.raw.to_vec(); let subject_public_key_info = cert.tbs_certificate.subject_pki.raw.to_vec();
@ -444,6 +449,11 @@ impl ResourceCertificateParsed {
_ => return Err(ResourceCertificateProfileError::InvalidVersion), _ => return Err(ResourceCertificateProfileError::InvalidVersion),
}; };
self.validity_not_before
.validate_encoding_rfc5280("notBefore")?;
self.validity_not_after
.validate_encoding_rfc5280("notAfter")?;
if self.signature_algorithm != self.tbs_signature_algorithm { if self.signature_algorithm != self.tbs_signature_algorithm {
return Err(ResourceCertificateProfileError::SignatureAlgorithmMismatch); return Err(ResourceCertificateProfileError::SignatureAlgorithmMismatch);
} }
@ -469,8 +479,8 @@ impl ResourceCertificateParsed {
signature_algorithm: self.signature_algorithm.oid, signature_algorithm: self.signature_algorithm.oid,
issuer_dn: self.issuer_dn, issuer_dn: self.issuer_dn,
subject_dn: self.subject_dn, subject_dn: self.subject_dn,
validity_not_before: self.validity_not_before, validity_not_before: self.validity_not_before.utc,
validity_not_after: self.validity_not_after, validity_not_after: self.validity_not_after.utc,
subject_public_key_info: self.subject_public_key_info, subject_public_key_info: self.subject_public_key_info,
extensions, extensions,
}, },

View File

@ -0,0 +1,24 @@
use rpki::data_model::common::Asn1TimeEncoding;
use rpki::data_model::rc::{ResourceCertificate, ResourceCertificateProfileError};
#[test]
fn rc_validity_time_encoding_is_validated() {
let der = std::fs::read(
"tests/fixtures/repository/rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer",
)
.expect("read RC fixture");
let mut parsed = ResourceCertificate::parse_der(&der).expect("parse RC fixture");
parsed.validity_not_before.encoding = match parsed.validity_not_before.encoding {
Asn1TimeEncoding::UtcTime => Asn1TimeEncoding::GeneralizedTime,
Asn1TimeEncoding::GeneralizedTime => Asn1TimeEncoding::UtcTime,
};
match parsed.validate_profile() {
Ok(_rc) => panic!("expected time-encoding validation error"),
Err(ResourceCertificateProfileError::InvalidTimeEncoding(e)) => {
assert_eq!(e.field, "notBefore");
}
Err(e) => panic!("unexpected error: {e}"),
}
}