From ee08980dc3b798514ff261813c95e9b04dd06fa7 Mon Sep 17 00:00:00 2001 From: William Durand Date: Thu, 11 Apr 2024 11:02:03 +0200 Subject: [PATCH] Expose certificate end dates (#7) --- src/xpi/signatures.rs | 55 ++++++++++++++++++++--- tests/fixtures/line-staging-cas-cur.xpi | Bin 0 -> 8044 bytes tests/xpi_test.rs | 56 +++++++++++++++++++++++- 3 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 tests/fixtures/line-staging-cas-cur.xpi diff --git a/src/xpi/signatures.rs b/src/xpi/signatures.rs index ba2932c..0998ab3 100644 --- a/src/xpi/signatures.rs +++ b/src/xpi/signatures.rs @@ -1,10 +1,11 @@ use super::cose_ish::CoseSign; use cms::cert::{ + x509, x509::{ attr::AttributeTypeAndValue, certificate::TbsCertificateInner, der::{ - asn1::{PrintableStringRef, TeletexStringRef, Utf8StringRef}, + asn1::{GeneralizedTime, PrintableStringRef, TeletexStringRef, UtcTime, Utf8StringRef}, Decode, Encode, Tag, Tagged, }, Certificate, @@ -17,16 +18,57 @@ use const_oid::db::{ rfc4519::{COMMON_NAME, ORGANIZATIONAL_UNIT_NAME}, rfc5912::{ID_SHA_1, ID_SHA_256}, }; -use serde::Serialize; +use serde::{Serialize, Serializer}; use std::convert::{From, TryInto}; -use std::{fmt, io, io::Read}; +use std::{fmt, io, io::Read, time::Duration}; use zip::ZipArchive; +#[derive(Debug, PartialEq)] +/// Represents a date in a certificate. +pub struct Date(x509::time::Time); + +impl Date { + pub fn utc_time_from_duration(duration: Duration) -> Self { + Date(x509::time::Time::from( + UtcTime::from_unix_duration(duration).expect("failed to make UtcTime"), + )) + } + + pub fn generalized_time_from_duration(duration: Duration) -> Self { + Date(x509::time::Time::from( + GeneralizedTime::from_unix_duration(duration).expect("failed to make GeneralizedTime"), + )) + } +} + +impl Default for Date { + fn default() -> Self { + Date::generalized_time_from_duration(Duration::ZERO) + } +} + +impl Serialize for Date { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s = format!("{}", self.0); + serializer.serialize_str(&s) + } +} + +impl fmt::Display for Date { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + #[derive(Default, Serialize)] /// Represents some of the information found in a certificate. pub struct CertificateInfo { pub common_name: String, pub organizational_unit: String, + pub end_date: Date, } impl CertificateInfo { @@ -73,6 +115,7 @@ impl From<&TbsCertificateInner> for CertificateInfo { CertificateInfo { common_name, organizational_unit, + end_date: Date(tbs_cert.validity.not_after), } } } @@ -81,8 +124,10 @@ impl fmt::Display for CertificateInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "Common Name (CN): {}\n Organizational Unit (OU): {}", - self.common_name, self.organizational_unit + "Common Name (CN): {}\n \ + Organizational Unit (OU): {}\n \ + End Date : {}", + self.common_name, self.organizational_unit, self.end_date ) } } diff --git a/tests/fixtures/line-staging-cas-cur.xpi b/tests/fixtures/line-staging-cas-cur.xpi new file mode 100644 index 0000000000000000000000000000000000000000..f09bcd98b27fcb96a834f5466048cc4f373d7842 GIT binary patch literal 8044 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 n-T?d^;ICEsR~Pay{|&%1`&Ls#MEd<1d%tJI9R) literal 0 HcmV?d00001 diff --git a/tests/xpi_test.rs b/tests/xpi_test.rs index 90d9352..3bdfa60 100644 --- a/tests/xpi_test.rs +++ b/tests/xpi_test.rs @@ -1,5 +1,6 @@ use std::io::Cursor; -use xpidump::{RecommendationState, Signature, SignatureKind, XPI}; +use std::time::Duration; +use xpidump::{Date, RecommendationState, Signature, SignatureKind, XPI}; use zip::ZipArchive; fn assert_signature(signature: &Signature, kind: SignatureKind, is_staging: bool, algorithm: &str) { @@ -45,7 +46,16 @@ fn test_prod_regular_addon() { false, "SHA-1", ); + assert_eq!( + Date::utc_time_from_duration(Duration::from_secs(1743724800)), + xpi.signatures.pkcs7.certificates[0].end_date + ); + assert_signature(&xpi.signatures.cose, SignatureKind::Regular, false, "ES256"); + assert_eq!( + Date::utc_time_from_duration(Duration::from_secs(1743724800)), + xpi.signatures.cose.certificates[0].end_date + ); } #[test] @@ -72,6 +82,10 @@ fn test_prod_old_regular_addon() { "{6AC85730-7D0F-4de0-B3FA-21142DD85326}", xpi.signatures.pkcs7.certificates[1].common_name ); + assert_eq!( + Date::utc_time_from_duration(Duration::from_secs(1741996362)), + xpi.signatures.pkcs7.certificates[0].end_date + ); assert!(!xpi.signatures.cose.exists()); } @@ -229,3 +243,43 @@ fn test_unsigned_addon() { assert!(!xpi.is_recommended()); assert!(!xpi.signatures.has_signatures()); } + +#[test] +fn test_staging_line_extension() { + let bytes = include_bytes!("fixtures/line-staging-cas-cur.xpi"); + let reader = Cursor::new(bytes); + let mut archive = ZipArchive::new(reader).unwrap(); + + let xpi = XPI::new(&mut archive); + + assert!(xpi.manifest.exists()); + assert!(xpi.is_recommended()); + assert_eq!( + "{0cdc308b-4c2a-497d-916a-164d602ed358}", + xpi.manifest.id.expect("expect add-on ID") + ); + assert_eq!( + "109.2", + xpi.manifest.version.expect("expect add-on version") + ); + + assert_signature(&xpi.signatures.pkcs7, SignatureKind::Regular, true, "SHA-1"); + assert_eq!( + Date::utc_time_from_duration(Duration::from_secs(1741910400)), + xpi.signatures.pkcs7.certificates[0].end_date + ); + assert_eq!( + Date::utc_time_from_duration(Duration::from_secs(2026818980)), + xpi.signatures.pkcs7.certificates[1].end_date + ); + + assert_signature(&xpi.signatures.cose, SignatureKind::Regular, true, "ES256"); + assert_eq!( + Date::utc_time_from_duration(Duration::from_secs(1741910400)), + xpi.signatures.cose.certificates[0].end_date + ); + assert_eq!( + Date::utc_time_from_duration(Duration::from_secs(2026818980)), + xpi.signatures.cose.certificates[1].end_date + ); +}