Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 29 additions & 22 deletions rs-matter/src/cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use num::FromPrimitive;
use num_derive::FromPrimitive;

use crate::crypto::{Crypto, PublicKey};
use crate::dm::clusters::time_sync::UtcTime;
use crate::error::{Error, ErrorCode};
use crate::tlv::{FromTLV, Octets, TLVArray, TLVElement, TLVList, ToTLV};
use crate::utils::epoch::MATTER_CERT_DOESNT_EXPIRE;
Expand Down Expand Up @@ -714,9 +715,9 @@ impl<'a> CertRef<'a> {
pub fn verify_chain_start<C: Crypto>(
&'a self,
crypto: C,
lkg_utc_secs: u64,
utc_time: UtcTime,
) -> CertVerifier<'a, C> {
CertVerifier::new(self, crypto, lkg_utc_secs)
CertVerifier::new(self, crypto, utc_time)
}

fn encode(&self, w: &mut dyn CertConsumer) -> Result<(), Error> {
Expand Down Expand Up @@ -792,20 +793,15 @@ impl fmt::Display for CertRef<'_> {
pub struct CertVerifier<'a, C> {
cert: &'a CertRef<'a>,
crypto: C,
/// Last-Known-Good UTC Time as Matter-epoch seconds, snapshot at
/// the start of chain verification. Per Matter Core spec §3.5.6
/// this is the time value used against cert `NotBefore`/`NotAfter`
/// when no live trusted real-time-clock is available — and the
/// stored LKG (§3.5.6.1) is exactly that.
lkg_utc_secs: u64,
utc_time: UtcTime,
}

impl<'a, C: Crypto> CertVerifier<'a, C> {
pub fn new(cert: &'a CertRef<'a>, crypto: C, lkg_utc_secs: u64) -> Self {
pub fn new(cert: &'a CertRef<'a>, crypto: C, utc_time: UtcTime) -> Self {
Self {
cert,
crypto,
lkg_utc_secs,
utc_time,
}
}

Expand Down Expand Up @@ -841,13 +837,23 @@ impl<'a, C: Crypto> CertVerifier<'a, C> {
// §6.5.5.
let not_before = self.cert.not_before()? as u64;
let not_after = self.cert.not_after()?;
if self.lkg_utc_secs < not_before
|| (not_after != 0 && self.lkg_utc_secs > not_after as u64)
{

if not_after > 0 && self.utc_time.any_secs() > not_after as u64 {
Err(ErrorCode::InvalidTime)?;
}

Ok(CertVerifier::new(parent, self.crypto, self.lkg_utc_secs))
if let Some(secs) = self.utc_time.reliable_secs() {
// Only check the NotBefore if we have a reliable UTC time;
// if we have only the Last-Known-Good time, then we may be
// in a gap between the LKG time and the real current time,
// and we don't want to reject certs that were generated _after_
// our clock lost sync
if secs < not_before {
Err(ErrorCode::InvalidTime)?;
}
}

Ok(CertVerifier::new(parent, self.crypto, self.utc_time))
}

pub fn finalise(self, buf: &mut [u8]) -> Result<(), Error> {
Expand Down Expand Up @@ -888,6 +894,7 @@ pub trait CertConsumer {
#[cfg(test)]
mod tests {
use crate::crypto::test_only_crypto;
use crate::dm::clusters::time_sync::UtcTime;
use crate::error::ErrorCode;
use crate::tlv::{FromTLV, TLVElement, TagType, ToTLV};
use crate::utils::storage::WriteBuf;
Expand Down Expand Up @@ -924,8 +931,8 @@ mod tests {
let noc = CertRef::new(TLVElement::new(NOC1_SUCCESS));
let icac = CertRef::new(TLVElement::new(ICAC1_SUCCESS));
let rca = CertRef::new(TLVElement::new(RCA1_SUCCESS));
let lkg = unwrap!(noc.not_before()) as u64;
let a = noc.verify_chain_start(test_only_crypto(), lkg);
let time = UtcTime::Reliable(unwrap!(noc.not_before()) as u64 * 1_000_000);
let a = noc.verify_chain_start(test_only_crypto(), time);
unwrap!(
unwrap!(unwrap!(a.add_cert(&icac, &mut buf)).add_cert(&rca, &mut buf))
.finalise(&mut buf)
Expand All @@ -939,8 +946,8 @@ mod tests {
let mut buf = [0; 1000];
let noc = CertRef::new(TLVElement::new(NOC1_SUCCESS));
let icac = CertRef::new(TLVElement::new(ICAC1_SUCCESS));
let lkg = unwrap!(noc.not_before()) as u64;
let a = noc.verify_chain_start(test_only_crypto(), lkg);
let time = UtcTime::Reliable(unwrap!(noc.not_before()) as u64 * 1_000_000);
let a = noc.verify_chain_start(test_only_crypto(), time);
assert_eq!(
Err(ErrorCode::InvalidAuthKey),
unwrap!(a.add_cert(&icac, &mut buf))
Expand All @@ -954,7 +961,7 @@ mod tests {
let mut buf = [0; 1000];
let noc = CertRef::new(TLVElement::new(NOC1_AUTH_KEY_FAIL));
let icac = CertRef::new(TLVElement::new(ICAC1_SUCCESS));
let a = noc.verify_chain_start(test_only_crypto(), 0);
let a = noc.verify_chain_start(test_only_crypto(), UtcTime::Reliable(0));
assert_eq!(
Err(ErrorCode::InvalidAuthKey),
a.add_cert(&icac, &mut buf)
Expand All @@ -969,8 +976,8 @@ mod tests {
let noc = CertRef::new(TLVElement::new(NOC_NOT_AFTER_ZERO));
let rca = CertRef::new(TLVElement::new(RCA_FOR_NOC_NOT_AFTER_ZERO));

let lkg = unwrap!(noc.not_before()) as u64;
let v = noc.verify_chain_start(test_only_crypto(), lkg);
let time = UtcTime::Reliable(unwrap!(noc.not_before()) as u64 * 1_000_000);
let v = noc.verify_chain_start(test_only_crypto(), time);
let v = unwrap!(v.add_cert(&rca, &mut buf));
unwrap!(v.finalise(&mut buf));
}
Expand All @@ -980,7 +987,7 @@ mod tests {
let mut buf = [0; 1000];
let noc = CertRef::new(TLVElement::new(NOC1_CORRUPT_CERT));
let icac = CertRef::new(TLVElement::new(ICAC1_SUCCESS));
let a = noc.verify_chain_start(test_only_crypto(), 0);
let a = noc.verify_chain_start(test_only_crypto(), UtcTime::Reliable(0));
assert_eq!(
Err(ErrorCode::InvalidSignature),
a.add_cert(&icac, &mut buf)
Expand Down
6 changes: 5 additions & 1 deletion rs-matter/src/cert/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ mod tests {
use crate::{
cert::{MAX_CERT_TLV_AND_ASN1_LEN, MAX_CERT_TLV_LEN},
crypto::{test_only_crypto, CanonPkcPublicKey, PublicKey, SigningSecretKey},
dm::clusters::time_sync::UtcTime,
};

use super::*;
Expand Down Expand Up @@ -711,7 +712,10 @@ mod tests {
let cert = CertRef::new(crate::tlv::TLVElement::new(&cert_buf[..len]));
let mut scratch = [0u8; MAX_CERT_TLV_AND_ASN1_LEN];
let res = cert
.verify_chain_start(&crypto, VALID_FOREVER.not_before as _)
.verify_chain_start(
&crypto,
UtcTime::Reliable(VALID_FOREVER.not_before as u64 * 1_000_000),
)
.finalise(&mut scratch);
assert!(
res.is_ok(),
Expand Down
18 changes: 14 additions & 4 deletions rs-matter/src/dm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ use crate::im::{
use crate::persist::KvBlobStoreAccess;
use crate::respond::ExchangeHandler;
use crate::tlv::{get_root_node_struct, FromTLV, Nullable, TLVElement, TLVTag, TLVWrite, ToTLV};
use crate::transport::exchange::{Exchange, MAX_EXCHANGE_RX_BUF_SIZE, MAX_EXCHANGE_TX_BUF_SIZE};
use crate::transport::exchange::{
Exchange, ExchangeId, MAX_EXCHANGE_RX_BUF_SIZE, MAX_EXCHANGE_TX_BUF_SIZE,
};
use crate::utils::select::Coalesce;
use crate::utils::storage::pooled::BufferAccess;
use crate::utils::storage::WriteBuf;
Expand Down Expand Up @@ -206,22 +208,30 @@ where
loop {
Timer::after_secs(CHECK_INTERVAL_SECS).await;

self.check_timeouts()?;
self.check_timeouts(None)?;
}
}

fn check_timeouts(&self) -> Result<(), Error> {
fn check_timeouts(&self, exch_id: Option<ExchangeId>) -> Result<(), Error> {
let mut notify_mdns = || self.matter.notify_mdns_changed();
let mut notify_change =
|endpt_id, clust_id| self.notify_cluster_changed(endpt_id, clust_id);

self.matter.with_state(|state| {
let expire_sess_id = exch_id.and_then(|exch_id| {
state
.sessions
.get(exch_id.session_id())
.map(|sess| sess.id())
});

// Disarm the failsafe on timeout
state.failsafe.check_failsafe_timeout(
&mut state.fabrics,
&mut state.sessions,
&self.networks,
&self.kv,
expire_sess_id,
&mut notify_mdns,
&mut notify_change,
)?;
Expand Down Expand Up @@ -265,7 +275,7 @@ where
None
};

self.check_timeouts()?;
self.check_timeouts(Some(exchange.id()))?;

// TODO: Handle the cases where we receive a timeout request
// before read and subscribe. This is probably not allowed.
Expand Down
15 changes: 8 additions & 7 deletions rs-matter/src/dm/clusters/gen_diag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use core::net::{Ipv4Addr, Ipv6Addr};
use crate::dm::{ArrayAttributeRead, Cluster, Dataver, InvokeContext, ReadContext};
use crate::error::{Error, ErrorCode};
use crate::tlv::{Nullable, Octets, TLVBuilder, TLVBuilderParent};
use crate::utils::epoch::MATTER_EPOCH_SECS;
use crate::utils::sync::DynBase;
use crate::with;

Expand Down Expand Up @@ -297,13 +298,13 @@ impl ClusterHandler for GenDiagHandler<'_> {
// (we do) or when its `UTCTime` attribute is null. We read the
// Matter-wide current UTC time directly from `Matter::utc_time`
// and convert (Matter-epoch microseconds) → POSIX-epoch ms.
let posix_time_ms = match ctx.matter().with_state(|state| state.rtc.utc_time()) {
Some(utc_us) => Nullable::some(
(utc_us / 1000)
.saturating_add(crate::utils::epoch::MATTER_EPOCH_SECS.saturating_mul(1000)),
),
None => Nullable::none(),
};
let posix_time_ms = Nullable::new(
ctx.matter()
.with_state(|state| state.rtc.utc_time())
.reliable()
.map(|us| us / 1000)
.map(|ms| ms.saturating_add(MATTER_EPOCH_SECS.saturating_mul(1000))),
);

response
.system_time_ms(system_time_ms)?
Expand Down
11 changes: 4 additions & 7 deletions rs-matter/src/dm/clusters/noc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ impl ClusterHandler for NocHandler {
// `SetUTCTime` yet, this is the firmware build timestamp.
// Read directly from the already-held `state` — re-entering
// `Matter::with_state` here would deadlock the inner mutex.
let epoch = (state.rtc.last_known_utc_time() / 1_000_000) as u32;
let epoch = state.rtc.utc_time().any_secs() as u32;

let mut signature = MaybeUninit::uninit();
let signature = signature.init_with(CanonPkcSignature::init()); // TODO MEDIUM BUFFER
Expand Down Expand Up @@ -501,12 +501,11 @@ impl ClusterHandler for NocHandler {
let status = NodeOperationalCertStatusEnum::map(GenCommHandler::with_armed_failsafe(
&ctx,
|state, mut notify_mdns| {
let lkg_utc_secs = state.rtc.utc_time_best_effort() / 1_000_000;
let sess = ctx.exchange().id().session(&mut state.sessions);

let fabric = state.failsafe.add_noc(
ctx.crypto(),
lkg_utc_secs,
state.rtc.utc_time(),
&mut state.fabrics,
sess.get_session_mode(),
request.admin_vendor_id()?,
Expand Down Expand Up @@ -592,12 +591,11 @@ impl ClusterHandler for NocHandler {
let status = NodeOperationalCertStatusEnum::map(GenCommHandler::with_armed_failsafe(
&ctx,
|state, notify_mdns| {
let lkg_utc_secs = state.rtc.utc_time_best_effort() / 1_000_000;
let sess = ctx.exchange().id().session(&mut state.sessions);

state.failsafe.update_noc(
ctx.crypto(),
lkg_utc_secs,
state.rtc.utc_time(),
&mut state.fabrics,
sess.get_session_mode(),
icac,
Expand Down Expand Up @@ -775,12 +773,11 @@ impl ClusterHandler for NocHandler {
let mut buf = [0u8; crate::cert::MAX_CERT_ASN1_LEN];

GenCommHandler::with_armed_failsafe(&ctx, |state, _| {
let lkg_utc_secs = state.rtc.utc_time_best_effort() / 1_000_000;
let sess = ctx.exchange().id().session(&mut state.sessions);

state.failsafe.add_trusted_root_cert(
ctx.crypto(),
lkg_utc_secs,
state.rtc.utc_time(),
sess.get_session_mode(),
request.root_ca_certificate()?.0,
&mut buf,
Expand Down
Loading
Loading