matter_controller/
fabric.rs1use std::sync::Arc;
7
8use matter_cert::MatterTime;
9use matter_commissioning::{issue_noc, FabricRecord, NocRng, VerifiedCsr};
10use matter_crypto::{RingSigner, Signer};
11
12use crate::error::Error;
13use crate::state::{CommissionerIdentity, FabricEntry};
14
15#[derive(Debug, Clone)]
21#[non_exhaustive]
22pub struct FabricConfig {
23 pub fabric_id: u64,
25 pub rcac_id: u64,
27 pub commissioner_node_id: u64,
29 pub validity: (MatterTime, MatterTime),
31}
32
33impl FabricConfig {
34 #[must_use]
40 pub fn new(
41 fabric_id: u64,
42 rcac_id: u64,
43 commissioner_node_id: u64,
44 validity: (MatterTime, MatterTime),
45 ) -> Self {
46 Self {
47 fabric_id,
48 rcac_id,
49 commissioner_node_id,
50 validity,
51 }
52 }
53}
54
55pub fn create_fabric(cfg: &FabricConfig, rng: &dyn NocRng) -> Result<FabricEntry, Error> {
66 let (root_signer, rcac_pkcs8) =
68 RingSigner::generate().map_err(|e| Error::Signer(e.to_string()))?;
69 let root_arc: Arc<dyn Signer> = Arc::new(root_signer);
70 let fabric_record = FabricRecord::new_root_only(
71 cfg.fabric_id,
72 root_arc,
73 cfg.validity.0,
74 cfg.validity.1,
75 cfg.rcac_id,
76 rng,
77 )?;
78
79 let (comm_signer, comm_pkcs8) =
81 RingSigner::generate().map_err(|e| Error::Signer(e.to_string()))?;
82 let comm_public_key = comm_signer.public_key().clone();
83
84 let verified = VerifiedCsr {
89 public_key: comm_public_key,
90 };
91 let noc = issue_noc(
92 &fabric_record,
93 &verified,
94 cfg.commissioner_node_id,
95 &[], cfg.validity,
97 rng,
98 )?;
99
100 Ok(FabricEntry {
101 fabric_id: cfg.fabric_id,
102 ipk: fabric_record.identity_protection_key,
103 rcac_cert: fabric_record.root_cert.clone(),
104 rcac_pkcs8,
105 commissioner: CommissionerIdentity {
106 node_id: cfg.commissioner_node_id,
107 operational_pkcs8: comm_pkcs8,
108 noc,
109 },
110 devices: Vec::new(),
111 group_keys: Vec::new(),
112 outbound_group_counter: 0,
113 icd_clients: Vec::new(),
114 })
115}
116
117#[cfg(test)]
118#[allow(clippy::unwrap_used, clippy::expect_used)] mod tests {
120 use super::*;
121 use matter_commissioning::SystemNocRng;
122
123 fn sample_cfg() -> FabricConfig {
124 FabricConfig::new(
125 0xDEAD_BEEF_0000_0001,
126 1,
127 0x0000_0000_0000_0001,
128 (
129 MatterTime::from_unix_secs(1_700_000_000),
130 MatterTime::NO_EXPIRY,
131 ),
132 )
133 }
134
135 #[test]
136 fn new_constructor_sets_all_fields() {
137 let cfg = FabricConfig::new(
140 7,
141 9,
142 3,
143 (MatterTime::from_unix_secs(1), MatterTime::NO_EXPIRY),
144 );
145 assert_eq!(cfg.fabric_id, 7);
146 assert_eq!(cfg.rcac_id, 9);
147 assert_eq!(cfg.commissioner_node_id, 3);
148 assert_eq!(cfg.validity.0, MatterTime::from_unix_secs(1));
149 }
150
151 #[test]
152 fn creates_fabric_with_no_devices() {
153 let fabric = create_fabric(&sample_cfg(), &SystemNocRng).expect("create");
154 assert_eq!(fabric.fabric_id, 0xDEAD_BEEF_0000_0001);
155 assert_eq!(fabric.commissioner.node_id, 1);
156 assert!(fabric.devices.is_empty());
157 assert!(!fabric.rcac_pkcs8.is_empty());
158 assert!(!fabric.commissioner.operational_pkcs8.is_empty());
159 }
160
161 #[test]
162 fn commissioner_noc_is_signed_by_the_rcac() {
163 let fabric = create_fabric(&sample_cfg(), &SystemNocRng).expect("create");
164 let rcac_key = fabric.rcac_cert.public_key();
165 fabric
166 .commissioner
167 .noc
168 .verify_signed_by(rcac_key)
169 .expect("commissioner NOC must verify under the RCAC");
170 }
171
172 #[test]
173 fn commissioner_signer_matches_persisted_noc_key() {
174 let fabric = create_fabric(&sample_cfg(), &SystemNocRng).expect("create");
177 let signer = fabric.commissioner_signer().expect("reload signer");
178 assert_eq!(
179 signer.public_key().as_bytes(),
180 fabric.commissioner.noc.public_key().as_bytes(),
181 "persisted op key must match the NOC subject public key"
182 );
183 }
184}