From 2b6317a9e26c2673be3928a20374f1637967dfe1 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 24 Apr 2023 08:17:08 +0000 Subject: [PATCH] Chrono dep made optional --- matter/src/cert/asn1_writer.rs | 50 ++++++++++++++------------ matter/src/cert/mod.rs | 60 ++++++++++++++++++++----------- matter/src/cert/printer.rs | 29 +++++---------- matter/src/core.rs | 14 +++++++- matter/src/secure_channel/case.rs | 24 +++++++++---- matter/src/secure_channel/core.rs | 12 +++++-- matter/src/transport/mgr.rs | 16 +++++++-- matter/src/utils/epoch.rs | 47 ++++++++++++++++++++++++ matter/tests/common/im_engine.rs | 8 +++-- 9 files changed, 181 insertions(+), 79 deletions(-) diff --git a/matter/src/cert/asn1_writer.rs b/matter/src/cert/asn1_writer.rs index 675546a..b6f4ab7 100644 --- a/matter/src/cert/asn1_writer.rs +++ b/matter/src/cert/asn1_writer.rs @@ -16,10 +16,11 @@ */ use super::{CertConsumer, MAX_DEPTH}; -use crate::error::Error; -use chrono::{Datelike, TimeZone, Utc}; // TODO -use core::fmt::Write; -use log::warn; +use crate::{ + error::Error, + utils::epoch::{UtcCalendar, MATTER_EPOCH_SECS}, +}; +use core::{fmt::Write, time::Duration}; #[derive(Debug)] pub struct ASN1Writer<'a> { @@ -261,31 +262,34 @@ impl<'a> CertConsumer for ASN1Writer<'a> { self.write_str(0x06, oid) } - fn utctime(&mut self, _tag: &str, epoch: u32) -> Result<(), Error> { - let mut matter_epoch = Utc - .with_ymd_and_hms(2000, 1, 1, 0, 0, 0) - .unwrap() - .timestamp(); + fn utctime(&mut self, _tag: &str, epoch: u32, utc_calendar: UtcCalendar) -> Result<(), Error> { + let matter_epoch = MATTER_EPOCH_SECS + epoch as u64; - matter_epoch += epoch as i64; + let dt = utc_calendar(Duration::from_secs(matter_epoch as _)); - let dt = match Utc.timestamp_opt(matter_epoch, 0) { - chrono::LocalResult::None => return Err(Error::InvalidTime), - chrono::LocalResult::Single(s) => s, - chrono::LocalResult::Ambiguous(_, a) => { - warn!("Ambiguous time for epoch {epoch}; returning latest timestamp: {a}"); - a - } - }; + let mut time_str: heapless::String<32> = heapless::String::<32>::new(); - if dt.year() >= 2050 { + if dt.year >= 2050 { // If year is >= 2050, ASN.1 requires it to be Generalised Time - let mut time_str = heapless::String::<32>::new(); - write!(&mut time_str, "{}Z", dt.format("%Y%m%d%H%M%S")).unwrap(); + write!( + &mut time_str, + "{:04}{:02}{:02}{:02}{:02}{:02}Z", + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second + ) + .unwrap(); self.write_str(0x18, time_str.as_bytes()) } else { - let mut time_str = heapless::String::<32>::new(); - write!(&mut time_str, "{}Z", dt.format("%y%m%d%H%M%S")).unwrap(); + write!( + &mut time_str, + "{:02}{:02}{:02}{:02}{:02}{:02}Z", + dt.year % 100, + dt.month, + dt.day, + dt.hour, + dt.minute, + dt.second + ) + .unwrap(); self.write_str(0x17, time_str.as_bytes()) } } diff --git a/matter/src/cert/mod.rs b/matter/src/cert/mod.rs index 918737a..7af1867 100644 --- a/matter/src/cert/mod.rs +++ b/matter/src/cert/mod.rs @@ -21,7 +21,7 @@ use crate::{ crypto::KeyPair, error::Error, tlv::{self, FromTLV, OctetStr, TLVArray, TLVElement, TLVWriter, TagType, ToTLV}, - utils::writebuf::WriteBuf, + utils::{epoch::UtcCalendar, writebuf::WriteBuf}, }; use log::error; use num_derive::FromPrimitive; @@ -617,17 +617,21 @@ impl<'a> Cert<'a> { Ok(wb.as_slice().len()) } - pub fn as_asn1(&self, buf: &mut [u8]) -> Result { + pub fn as_asn1(&self, buf: &mut [u8], utc_calendar: UtcCalendar) -> Result { let mut w = ASN1Writer::new(buf); - self.encode(&mut w)?; + self.encode(&mut w, Some(utc_calendar))?; Ok(w.as_slice().len()) } - pub fn verify_chain_start(&self) -> CertVerifier { - CertVerifier::new(self) + pub fn verify_chain_start(&self, utc_calendar: UtcCalendar) -> CertVerifier { + CertVerifier::new(self, utc_calendar) } - fn encode(&self, w: &mut dyn CertConsumer) -> Result<(), Error> { + fn encode( + &self, + w: &mut dyn CertConsumer, + utc_calendar: Option, + ) -> Result<(), Error> { w.start_seq("")?; w.start_ctx("Version:", 0)?; @@ -646,8 +650,10 @@ impl<'a> Cert<'a> { self.issuer.encode("Issuer:", w)?; w.start_seq("Validity:")?; - w.utctime("Not Before:", self.not_before)?; - w.utctime("Not After:", self.not_after)?; + if let Some(utc_calendar) = utc_calendar { + w.utctime("Not Before:", self.not_before, utc_calendar)?; + w.utctime("Not After:", self.not_after, utc_calendar)?; + } w.end_seq()?; self.subject.encode("Subject:", w)?; @@ -679,7 +685,7 @@ impl<'a> fmt::Display for Cert<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut printer = CertPrinter::new(f); let _ = self - .encode(&mut printer) + .encode(&mut printer, None) .map_err(|e| error!("Error decoding certificate: {}", e)); // Signature is not encoded by the Cert Decoder writeln!(f, "Signature: {:x?}", self.get_signature()) @@ -688,11 +694,12 @@ impl<'a> fmt::Display for Cert<'a> { pub struct CertVerifier<'a> { cert: &'a Cert<'a>, + utc_calendar: UtcCalendar, } impl<'a> CertVerifier<'a> { - pub fn new(cert: &'a Cert) -> Self { - Self { cert } + pub fn new(cert: &'a Cert, utc_calendar: UtcCalendar) -> Self { + Self { cert, utc_calendar } } pub fn add_cert(self, parent: &'a Cert) -> Result, Error> { @@ -700,7 +707,7 @@ impl<'a> CertVerifier<'a> { return Err(Error::InvalidAuthKey); } let mut asn1 = [0u8; MAX_ASN1_CERT_SIZE]; - let len = self.cert.as_asn1(&mut asn1)?; + let len = self.cert.as_asn1(&mut asn1, self.utc_calendar)?; let asn1 = &asn1[..len]; let k = KeyPair::new_from_public(parent.get_pubkey())?; @@ -713,7 +720,7 @@ impl<'a> CertVerifier<'a> { })?; // TODO: other validation checks - Ok(CertVerifier::new(parent)) + Ok(CertVerifier::new(parent, self.utc_calendar)) } pub fn finalise(self) -> Result<(), Error> { @@ -740,7 +747,7 @@ pub trait CertConsumer { fn start_ctx(&mut self, tag: &str, id: u8) -> Result<(), Error>; fn end_ctx(&mut self) -> Result<(), Error>; fn oid(&mut self, tag: &str, oid: &[u8]) -> Result<(), Error>; - fn utctime(&mut self, tag: &str, epoch: u32) -> Result<(), Error>; + fn utctime(&mut self, tag: &str, epoch: u32, utc_calendar: UtcCalendar) -> Result<(), Error>; } const MAX_DEPTH: usize = 10; @@ -758,36 +765,44 @@ mod tests { use crate::tlv::{self, FromTLV, TLVWriter, TagType, ToTLV}; use crate::utils::writebuf::WriteBuf; + #[cfg(feature = "std")] #[test] fn test_asn1_encode_success() { { let mut asn1_buf = [0u8; 1000]; let c = Cert::new(&test_vectors::CHIP_CERT_INPUT1).unwrap(); - let len = c.as_asn1(&mut asn1_buf).unwrap(); + let len = c + .as_asn1(&mut asn1_buf, crate::utils::epoch::sys_utc_calendar) + .unwrap(); assert_eq!(&test_vectors::ASN1_OUTPUT1, &asn1_buf[..len]); } { let mut asn1_buf = [0u8; 1000]; let c = Cert::new(&test_vectors::CHIP_CERT_INPUT2).unwrap(); - let len = c.as_asn1(&mut asn1_buf).unwrap(); + let len = c + .as_asn1(&mut asn1_buf, crate::utils::epoch::sys_utc_calendar) + .unwrap(); assert_eq!(&test_vectors::ASN1_OUTPUT2, &asn1_buf[..len]); } { let mut asn1_buf = [0u8; 1000]; let c = Cert::new(&test_vectors::CHIP_CERT_TXT_IN_DN).unwrap(); - let len = c.as_asn1(&mut asn1_buf).unwrap(); + let len = c + .as_asn1(&mut asn1_buf, crate::utils::epoch::sys_utc_calendar) + .unwrap(); assert_eq!(&test_vectors::ASN1_OUTPUT_TXT_IN_DN, &asn1_buf[..len]); } } + #[cfg(feature = "std")] #[test] fn test_verify_chain_success() { let noc = Cert::new(&test_vectors::NOC1_SUCCESS).unwrap(); let icac = Cert::new(&test_vectors::ICAC1_SUCCESS).unwrap(); let rca = Cert::new(&test_vectors::RCA1_SUCCESS).unwrap(); - let a = noc.verify_chain_start(); + let a = noc.verify_chain_start(crate::utils::epoch::sys_utc_calendar); a.add_cert(&icac) .unwrap() .add_cert(&rca) @@ -796,31 +811,34 @@ mod tests { .unwrap(); } + #[cfg(feature = "std")] #[test] fn test_verify_chain_incomplete() { // The chain doesn't lead up to a self-signed certificate let noc = Cert::new(&test_vectors::NOC1_SUCCESS).unwrap(); let icac = Cert::new(&test_vectors::ICAC1_SUCCESS).unwrap(); - let a = noc.verify_chain_start(); + let a = noc.verify_chain_start(crate::utils::epoch::sys_utc_calendar); assert_eq!( Err(Error::InvalidAuthKey), a.add_cert(&icac).unwrap().finalise() ); } + #[cfg(feature = "std")] #[test] fn test_auth_key_chain_incorrect() { let noc = Cert::new(&test_vectors::NOC1_AUTH_KEY_FAIL).unwrap(); let icac = Cert::new(&test_vectors::ICAC1_SUCCESS).unwrap(); - let a = noc.verify_chain_start(); + let a = noc.verify_chain_start(crate::utils::epoch::sys_utc_calendar); assert_eq!(Err(Error::InvalidAuthKey), a.add_cert(&icac).map(|_| ())); } + #[cfg(feature = "std")] #[test] fn test_cert_corrupted() { let noc = Cert::new(&test_vectors::NOC1_CORRUPT_CERT).unwrap(); let icac = Cert::new(&test_vectors::ICAC1_SUCCESS).unwrap(); - let a = noc.verify_chain_start(); + let a = noc.verify_chain_start(crate::utils::epoch::sys_utc_calendar); assert_eq!(Err(Error::InvalidSignature), a.add_cert(&icac).map(|_| ())); } diff --git a/matter/src/cert/printer.rs b/matter/src/cert/printer.rs index b933607..ae07957 100644 --- a/matter/src/cert/printer.rs +++ b/matter/src/cert/printer.rs @@ -16,10 +16,11 @@ */ use super::{CertConsumer, MAX_DEPTH}; -use crate::error::Error; -use chrono::{TimeZone, Utc}; -use core::fmt; -use log::warn; +use crate::{ + error::Error, + utils::epoch::{UtcCalendar, MATTER_EPOCH_SECS}, +}; +use core::{fmt, time::Duration}; pub struct CertPrinter<'a, 'b> { level: usize, @@ -122,24 +123,12 @@ impl<'a, 'b> CertConsumer for CertPrinter<'a, 'b> { } Ok(()) } - fn utctime(&mut self, tag: &str, epoch: u32) -> Result<(), Error> { - let mut matter_epoch = Utc - .with_ymd_and_hms(2000, 1, 1, 0, 0, 0) - .unwrap() - .timestamp(); + fn utctime(&mut self, tag: &str, epoch: u32, utc_calendar: UtcCalendar) -> Result<(), Error> { + let matter_epoch = MATTER_EPOCH_SECS + epoch as u64; - matter_epoch += epoch as i64; + let dt = utc_calendar(Duration::from_secs(matter_epoch as _)); - let dt = match Utc.timestamp_opt(matter_epoch, 0) { - chrono::LocalResult::None => return Err(Error::InvalidTime), - chrono::LocalResult::Single(s) => s, - chrono::LocalResult::Ambiguous(_, a) => { - warn!("Ambiguous time for epoch {epoch}; returning latest timestamp: {a}"); - a - } - }; - - let _ = writeln!(self.f, "{} {} {}", SPACE[self.level], tag, dt); + let _ = writeln!(self.f, "{} {} {:?}", SPACE[self.level], tag, dt); Ok(()) } } diff --git a/matter/src/core.rs b/matter/src/core.rs index 1c3eb25..0b555cc 100644 --- a/matter/src/core.rs +++ b/matter/src/core.rs @@ -26,7 +26,10 @@ use crate::{ pairing::{print_pairing_code_and_qr, DiscoveryCapabilities}, secure_channel::{pake::PaseMgr, spake2p::VerifierData}, transport::udp::MATTER_PORT, - utils::{epoch::Epoch, rand::Rand}, + utils::{ + epoch::{Epoch, UtcCalendar}, + rand::Rand, + }, }; /// Device Commissioning Data @@ -46,6 +49,7 @@ pub struct Matter<'a> { pub mdns_mgr: RefCell>, pub epoch: Epoch, pub rand: Rand, + pub utc_calendar: UtcCalendar, pub dev_det: &'a BasicInfoConfig<'a>, } @@ -61,6 +65,7 @@ impl<'a> Matter<'a> { mdns: &'a mut dyn Mdns, epoch: Epoch, rand: Rand, + utc_calendar: UtcCalendar, ) -> Self { Self { fabric_mgr: RefCell::new(FabricMgr::new()), @@ -76,6 +81,7 @@ impl<'a> Matter<'a> { )), epoch, rand, + utc_calendar, dev_det, } } @@ -150,3 +156,9 @@ impl<'a> Borrow for Matter<'a> { &self.rand } } + +impl<'a> Borrow for Matter<'a> { + fn borrow(&self) -> &UtcCalendar { + &self.utc_calendar + } +} diff --git a/matter/src/secure_channel/case.rs b/matter/src/secure_channel/case.rs index a722dae..4ffb6b2 100644 --- a/matter/src/secure_channel/case.rs +++ b/matter/src/secure_channel/case.rs @@ -33,7 +33,7 @@ use crate::{ queue::{Msg, WorkQ}, session::{CaseDetails, CloneData, NocCatIds, SessionMode}, }, - utils::{rand::Rand, writebuf::WriteBuf}, + utils::{epoch::UtcCalendar, rand::Rand, writebuf::WriteBuf}, }; #[derive(PartialEq)] @@ -71,11 +71,16 @@ impl CaseSession { pub struct Case<'a> { fabric_mgr: &'a RefCell, rand: Rand, + utc_calendar: UtcCalendar, } impl<'a> Case<'a> { - pub fn new(fabric_mgr: &'a RefCell, rand: Rand) -> Self { - Self { fabric_mgr, rand } + pub fn new(fabric_mgr: &'a RefCell, rand: Rand, utc_calendar: UtcCalendar) -> Self { + Self { + fabric_mgr, + rand, + utc_calendar, + } } pub fn casesigma3_handler(&mut self, ctx: &mut ProtoCtx) -> Result { @@ -126,7 +131,9 @@ impl<'a> Case<'a> { if let Some(icac) = d.initiator_icac { initiator_icac = Some(Cert::new(icac.0)?); } - if let Err(e) = Case::validate_certs(fabric, &initiator_noc, &initiator_icac) { + if let Err(e) = + Case::validate_certs(fabric, &initiator_noc, &initiator_icac, self.utc_calendar) + { error!("Certificate Chain doesn't match: {}", e); common::create_sc_status_report(ctx.tx, common::SCStatusCodes::InvalidParameter, None)?; ctx.exch_ctx.exch.close(); @@ -332,8 +339,13 @@ impl<'a> Case<'a> { Ok(()) } - fn validate_certs(fabric: &Fabric, noc: &Cert, icac: &Option) -> Result<(), Error> { - let mut verifier = noc.verify_chain_start(); + fn validate_certs( + fabric: &Fabric, + noc: &Cert, + icac: &Option, + utc_calendar: UtcCalendar, + ) -> Result<(), Error> { + let mut verifier = noc.verify_chain_start(utc_calendar); if fabric.get_fabric_id() != noc.get_fabric_id()? { return Err(Error::Invalid); diff --git a/matter/src/secure_channel/core.rs b/matter/src/secure_channel/core.rs index 5ca1804..be806a7 100644 --- a/matter/src/secure_channel/core.rs +++ b/matter/src/secure_channel/core.rs @@ -18,8 +18,13 @@ use core::cell::RefCell; use crate::{ - error::*, fabric::FabricMgr, mdns::MdnsMgr, secure_channel::common::*, tlv, - transport::proto_ctx::ProtoCtx, utils::rand::Rand, + error::*, + fabric::FabricMgr, + mdns::MdnsMgr, + secure_channel::common::*, + tlv, + transport::proto_ctx::ProtoCtx, + utils::{epoch::UtcCalendar, rand::Rand}, }; use log::{error, info}; use num; @@ -41,9 +46,10 @@ impl<'a> SecureChannel<'a> { fabric_mgr: &'a RefCell, mdns: &'a RefCell>, rand: Rand, + utc_calendar: UtcCalendar, ) -> Self { SecureChannel { - case: Case::new(fabric_mgr, rand), + case: Case::new(fabric_mgr, rand, utc_calendar), pase, mdns, } diff --git a/matter/src/transport/mgr.rs b/matter/src/transport/mgr.rs index 349cfde..2ada942 100644 --- a/matter/src/transport/mgr.rs +++ b/matter/src/transport/mgr.rs @@ -29,7 +29,7 @@ use crate::secure_channel::common::PROTO_ID_SECURE_CHANNEL; use crate::secure_channel::core::SecureChannel; use crate::transport::mrp::ReliableMessage; use crate::transport::{exchange, packet::Packet}; -use crate::utils::epoch::Epoch; +use crate::utils::epoch::{Epoch, UtcCalendar}; use crate::utils::rand::Rand; use super::proto_ctx::ProtoCtx; @@ -167,13 +167,23 @@ pub struct TransportMgr<'a> { impl<'a> TransportMgr<'a> { pub fn new< - T: Borrow> + Borrow> + Borrow + Borrow, + T: Borrow> + + Borrow> + + Borrow + + Borrow + + Borrow, >( matter: &'a T, mdns_mgr: &'a RefCell>, ) -> Self { Self::wrap( - SecureChannel::new(matter.borrow(), matter.borrow(), mdns_mgr, *matter.borrow()), + SecureChannel::new( + matter.borrow(), + matter.borrow(), + mdns_mgr, + *matter.borrow(), + *matter.borrow(), + ), *matter.borrow(), *matter.borrow(), ) diff --git a/matter/src/utils/epoch.rs b/matter/src/utils/epoch.rs index 999cdf3..7d08bfe 100644 --- a/matter/src/utils/epoch.rs +++ b/matter/src/utils/epoch.rs @@ -2,13 +2,60 @@ use core::time::Duration; pub type Epoch = fn() -> Duration; +pub type UtcCalendar = fn(Duration) -> UtcDate; + +pub const MATTER_EPOCH_SECS: u64 = 946684800; // Seconds from 1970/01/01 00:00:00 till 2000/01/01 00:00:00 UTC + +#[derive(Default, Debug, Clone, Eq, PartialEq)] +pub struct UtcDate { + pub year: u16, + pub month: u8, // 1 - 12 + pub day: u8, // 1 - 31 + pub hour: u8, // 0 - 23 + pub minute: u8, + pub second: u8, + pub millis: u16, +} + pub fn dummy_epoch() -> Duration { Duration::from_secs(0) } +pub fn dummy_utc_calendar(_duration: Duration) -> UtcDate { + Default::default() +} + #[cfg(feature = "std")] pub fn sys_epoch() -> Duration { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() } + +#[cfg(feature = "std")] +pub fn sys_utc_calendar(duration: Duration) -> UtcDate { + use chrono::{Datelike, TimeZone, Timelike}; + use log::warn; + + let dt = match chrono::Utc.timestamp_opt(duration.as_secs() as _, duration.subsec_nanos()) { + chrono::LocalResult::None => panic!("Invalid time"), + chrono::LocalResult::Single(s) => s, + chrono::LocalResult::Ambiguous(_, a) => { + warn!( + "Ambiguous time for epoch {:?}; returning latest timestamp: {a}", + duration + ); + a + } + }; + + UtcDate { + year: dt.year() as _, + month: dt.month() as _, + day: dt.day() as _, + hour: dt.hour() as _, + minute: dt.minute() as _, + second: dt.second() as _, + millis: (dt.nanosecond() / 1000) as _, + } +} diff --git a/matter/tests/common/im_engine.rs b/matter/tests/common/im_engine.rs index 116ad50..4cdbf04 100644 --- a/matter/tests/common/im_engine.rs +++ b/matter/tests/common/im_engine.rs @@ -49,7 +49,11 @@ use matter::{ proto_ctx::ProtoCtx, session::{CaseDetails, CloneData, NocCatIds, SessionMgr, SessionMode}, }, - utils::{epoch::sys_epoch, rand::dummy_rand, writebuf::WriteBuf}, + utils::{ + epoch::{sys_epoch, sys_utc_calendar}, + rand::dummy_rand, + writebuf::WriteBuf, + }, Matter, }; use std::net::{Ipv4Addr, SocketAddr}; @@ -105,7 +109,7 @@ impl<'a> ImInput<'a> { pub type DmHandler<'a> = handler_chain_type!(OnOffCluster, EchoCluster, DescriptorCluster, EchoCluster | RootEndpointHandler<'a>); pub fn matter(mdns: &mut dyn Mdns) -> Matter<'_> { - Matter::new(&BASIC_INFO, mdns, sys_epoch, dummy_rand) + Matter::new(&BASIC_INFO, mdns, sys_epoch, dummy_rand, sys_utc_calendar) } /// An Interaction Model Engine to facilitate easy testing