Chrono dep made optional

This commit is contained in:
ivmarkov 2023-04-24 08:17:08 +00:00
parent 0b807f03a6
commit 2b6317a9e2
9 changed files with 181 additions and 79 deletions

View file

@ -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())
}
}

View file

@ -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<usize, Error> {
pub fn as_asn1(&self, buf: &mut [u8], utc_calendar: UtcCalendar) -> Result<usize, Error> {
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<UtcCalendar>,
) -> 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<CertVerifier<'a>, 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(|_| ()));
}

View file

@ -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(())
}
}

View file

@ -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<MdnsMgr<'a>>,
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<Rand> for Matter<'a> {
&self.rand
}
}
impl<'a> Borrow<UtcCalendar> for Matter<'a> {
fn borrow(&self) -> &UtcCalendar {
&self.utc_calendar
}
}

View file

@ -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<FabricMgr>,
rand: Rand,
utc_calendar: UtcCalendar,
}
impl<'a> Case<'a> {
pub fn new(fabric_mgr: &'a RefCell<FabricMgr>, rand: Rand) -> Self {
Self { fabric_mgr, rand }
pub fn new(fabric_mgr: &'a RefCell<FabricMgr>, rand: Rand, utc_calendar: UtcCalendar) -> Self {
Self {
fabric_mgr,
rand,
utc_calendar,
}
}
pub fn casesigma3_handler(&mut self, ctx: &mut ProtoCtx) -> Result<bool, Error> {
@ -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<Cert>) -> Result<(), Error> {
let mut verifier = noc.verify_chain_start();
fn validate_certs(
fabric: &Fabric,
noc: &Cert,
icac: &Option<Cert>,
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);

View file

@ -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<FabricMgr>,
mdns: &'a RefCell<MdnsMgr<'a>>,
rand: Rand,
utc_calendar: UtcCalendar,
) -> Self {
SecureChannel {
case: Case::new(fabric_mgr, rand),
case: Case::new(fabric_mgr, rand, utc_calendar),
pase,
mdns,
}

View file

@ -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<RefCell<FabricMgr>> + Borrow<RefCell<PaseMgr>> + Borrow<Epoch> + Borrow<Rand>,
T: Borrow<RefCell<FabricMgr>>
+ Borrow<RefCell<PaseMgr>>
+ Borrow<Epoch>
+ Borrow<Rand>
+ Borrow<UtcCalendar>,
>(
matter: &'a T,
mdns_mgr: &'a RefCell<MdnsMgr<'a>>,
) -> 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(),
)

View file

@ -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 _,
}
}

View file

@ -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