use std::sync::Arc;
use matter_cert::MatterTime;
use matter_commissioning::{issue_noc, FabricRecord, NocRng, VerifiedCsr};
use matter_crypto::{RingSigner, Signer};
use crate::error::Error;
use crate::state::{CommissionerIdentity, FabricEntry};
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct FabricConfig {
pub fabric_id: u64,
pub rcac_id: u64,
pub commissioner_node_id: u64,
pub validity: (MatterTime, MatterTime),
}
impl FabricConfig {
#[must_use]
pub fn new(
fabric_id: u64,
rcac_id: u64,
commissioner_node_id: u64,
validity: (MatterTime, MatterTime),
) -> Self {
Self {
fabric_id,
rcac_id,
commissioner_node_id,
validity,
}
}
}
pub fn create_fabric(cfg: &FabricConfig, rng: &dyn NocRng) -> Result<FabricEntry, Error> {
let (root_signer, rcac_pkcs8) =
RingSigner::generate().map_err(|e| Error::Signer(e.to_string()))?;
let root_arc: Arc<dyn Signer> = Arc::new(root_signer);
let fabric_record = FabricRecord::new_root_only(
cfg.fabric_id,
root_arc,
cfg.validity.0,
cfg.validity.1,
cfg.rcac_id,
rng,
)?;
let (comm_signer, comm_pkcs8) =
RingSigner::generate().map_err(|e| Error::Signer(e.to_string()))?;
let comm_public_key = comm_signer.public_key().clone();
let verified = VerifiedCsr {
public_key: comm_public_key,
};
let noc = issue_noc(
&fabric_record,
&verified,
cfg.commissioner_node_id,
&[], cfg.validity,
rng,
)?;
Ok(FabricEntry {
fabric_id: cfg.fabric_id,
ipk: fabric_record.identity_protection_key,
rcac_cert: fabric_record.root_cert.clone(),
rcac_pkcs8,
commissioner: CommissionerIdentity {
node_id: cfg.commissioner_node_id,
operational_pkcs8: comm_pkcs8,
noc,
},
devices: Vec::new(),
group_keys: Vec::new(),
outbound_group_counter: 0,
icd_clients: Vec::new(),
})
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)] mod tests {
use super::*;
use matter_commissioning::SystemNocRng;
fn sample_cfg() -> FabricConfig {
FabricConfig::new(
0xDEAD_BEEF_0000_0001,
1,
0x0000_0000_0000_0001,
(
MatterTime::from_unix_secs(1_700_000_000),
MatterTime::NO_EXPIRY,
),
)
}
#[test]
fn new_constructor_sets_all_fields() {
let cfg = FabricConfig::new(
7,
9,
3,
(MatterTime::from_unix_secs(1), MatterTime::NO_EXPIRY),
);
assert_eq!(cfg.fabric_id, 7);
assert_eq!(cfg.rcac_id, 9);
assert_eq!(cfg.commissioner_node_id, 3);
assert_eq!(cfg.validity.0, MatterTime::from_unix_secs(1));
}
#[test]
fn creates_fabric_with_no_devices() {
let fabric = create_fabric(&sample_cfg(), &SystemNocRng).expect("create");
assert_eq!(fabric.fabric_id, 0xDEAD_BEEF_0000_0001);
assert_eq!(fabric.commissioner.node_id, 1);
assert!(fabric.devices.is_empty());
assert!(!fabric.rcac_pkcs8.is_empty());
assert!(!fabric.commissioner.operational_pkcs8.is_empty());
}
#[test]
fn commissioner_noc_is_signed_by_the_rcac() {
let fabric = create_fabric(&sample_cfg(), &SystemNocRng).expect("create");
let rcac_key = fabric.rcac_cert.public_key();
fabric
.commissioner
.noc
.verify_signed_by(rcac_key)
.expect("commissioner NOC must verify under the RCAC");
}
#[test]
fn commissioner_signer_matches_persisted_noc_key() {
let fabric = create_fabric(&sample_cfg(), &SystemNocRng).expect("create");
let signer = fabric.commissioner_signer().expect("reload signer");
assert_eq!(
signer.public_key().as_bytes(),
fabric.commissioner.noc.public_key().as_bytes(),
"persisted op key must match the NOC subject public key"
);
}
}