1use core::mem::MaybeUninit;
19use core::num::NonZeroU8;
20
21use cfg_if::cfg_if;
22use heapless::String;
23
24use crate::acl::{self, AccessReq, AclEntry, AuthMode};
25use crate::cert::{CertRef, MAX_CERT_TLV_LEN};
26use crate::crypto::{
27 CanonAeadKeyRef, CanonPkcPublicKeyRef, CanonPkcSecretKey, CanonPkcSecretKeyRef, Crypto,
28 CryptoSensitive, Digest, Hash, Kdf, PKC_CANON_PUBLIC_KEY_LEN,
29};
30use crate::dm::Privilege;
31use crate::error::{Error, ErrorCode};
32use crate::group_keys::KeySet;
33use crate::persist::{KvBlobStore, KvBlobStoreAccess, Persist, FABRIC_KEYS_START};
34#[cfg(feature = "groups")]
35use crate::tlv::Skippable;
36use crate::tlv::{FromTLV, TLVElement, ToTLV};
37use crate::transport::network::MatterLocalService;
38use crate::utils::init::{init, Init, InitMaybeUninit, IntoFallibleInit};
39use crate::utils::storage::Vec;
40
41const COMPRESSED_FABRIC_ID_LEN: usize = 8;
42
43#[cfg(feature = "groups")]
49mod groups {
50 use core::str::FromStr;
51
52 use cfg_if::cfg_if;
53
54 use heapless::String;
55
56 use crate::dm::clusters::decl::groupcast::MulticastAddrPolicyEnum;
57 use crate::error::{Error, ErrorCode};
58 use crate::group_keys::GroupKeySet;
59 use crate::tlv::{FromTLV, ToTLV};
60 use crate::utils::init::{init, Init, InitDefault};
61 use crate::utils::storage::Vec;
62
63 cfg_if! {
64 if #[cfg(feature = "max-group-keys-per-fabric-5")] {
65 pub const MAX_GROUP_KEYS_PER_FABRIC: usize = 5;
67 } else if #[cfg(feature = "max-group-keys-per-fabric-4")] {
68 pub const MAX_GROUP_KEYS_PER_FABRIC: usize = 4;
70 } else if #[cfg(feature = "max-group-keys-per-fabric-3")] {
71 pub const MAX_GROUP_KEYS_PER_FABRIC: usize = 3;
73 } else if #[cfg(feature = "max-group-keys-per-fabric-2")] {
74 pub const MAX_GROUP_KEYS_PER_FABRIC: usize = 2;
76 } else { pub const MAX_GROUP_KEYS_PER_FABRIC: usize = 3;
79 }
80 }
81
82 pub const MAX_GROUP_NAME_LEN: usize = 16;
84
85 cfg_if! {
86 if #[cfg(feature = "max-groups-per-fabric-32")] {
87 pub const MAX_GROUPS_PER_FABRIC: usize = 32;
89 } else if #[cfg(feature = "max-groups-per-fabric-16")] {
90 pub const MAX_GROUPS_PER_FABRIC: usize = 16;
92 } else if #[cfg(feature = "max-groups-per-fabric-12")] {
93 pub const MAX_GROUPS_PER_FABRIC: usize = 12;
95 } else if #[cfg(feature = "max-groups-per-fabric-8")] {
96 pub const MAX_GROUPS_PER_FABRIC: usize = 9;
98 } else if #[cfg(feature = "max-groups-per-fabric-7")] {
99 pub const MAX_GROUPS_PER_FABRIC: usize = 7;
101 } else if #[cfg(feature = "max-groups-per-fabric-6")] {
102 pub const MAX_GROUPS_PER_FABRIC: usize = 6;
104 } else if #[cfg(feature = "max-groups-per-fabric-5")] {
105 pub const MAX_GROUPS_PER_FABRIC: usize = 5;
107 } else if #[cfg(feature = "max-groups-per-fabric-4")] {
108 pub const MAX_GROUPS_PER_FABRIC: usize = 4;
110 } else { pub const MAX_GROUPS_PER_FABRIC: usize = 4;
113 }
114 }
115
116 cfg_if! {
117 if #[cfg(feature = "max-group-endpoints-per-fabric-5")] {
118 pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 5;
120 } else if #[cfg(feature = "max-group-endpoints-per-fabric-4")] {
121 pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 4;
123 } else if #[cfg(feature = "max-group-endpoints-per-fabric-3")] {
124 pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 3;
126 } else if #[cfg(feature = "max-group-endpoints-per-fabric-2")] {
127 pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 2;
129 } else if #[cfg(feature = "max-group-endpoints-per-fabric-1")] {
130 pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 1;
132 } else { pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 3;
135 }
136 }
137
138 #[derive(Debug, FromTLV, ToTLV)]
140 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
141 pub struct GroupEndpointMapping {
142 pub group_id: u16,
143 pub endpoints: Vec<u16, GROUP_ENDPOINTS_PER_FABRIC>,
144 pub group_name: String<MAX_GROUP_NAME_LEN>,
145 pub has_aux_acl: Option<bool>,
152 pub mcast_policy: Option<MulticastAddrPolicyEnum>,
160 }
161
162 impl GroupEndpointMapping {
163 pub fn has_aux_acl(&self) -> bool {
166 self.has_aux_acl.unwrap_or(false)
167 }
168
169 pub fn effective_mcast_policy(&self) -> MulticastAddrPolicyEnum {
172 self.mcast_policy
173 .unwrap_or(MulticastAddrPolicyEnum::PerGroup)
174 }
175
176 pub fn groupcast_managed(&self) -> bool {
179 self.mcast_policy.is_some()
180 }
181 }
182
183 #[derive(Debug, Clone, Default, FromTLV, ToTLV)]
185 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
186 pub struct GroupKeyMapping {
187 pub group_id: u16,
188 pub group_key_set_id: u16,
189 }
190
191 #[derive(Debug, FromTLV, ToTLV)]
192 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
193 pub struct Groups {
194 key_sets: Vec<GroupKeySet, MAX_GROUP_KEYS_PER_FABRIC>,
196 key_map: Vec<GroupKeyMapping, MAX_GROUPS_PER_FABRIC>,
198 endpoint_mapping: Vec<GroupEndpointMapping, MAX_GROUPS_PER_FABRIC>,
200 }
201
202 impl Groups {
203 pub(crate) const fn new() -> Self {
204 Self {
205 key_sets: Vec::new(),
206 key_map: Vec::new(),
207 endpoint_mapping: Vec::new(),
208 }
209 }
210
211 pub(crate) fn init() -> impl Init<Self> {
212 init!(Self {
213 key_sets <- Vec::init(),
214 key_map <- Vec::init(),
215 endpoint_mapping <- Vec::init(),
216 })
217 }
218
219 pub fn key_set_iter(&self) -> impl Iterator<Item = &GroupKeySet> {
221 self.key_sets.iter()
222 }
223
224 pub fn key_set_get(&self, id: u16) -> Option<&GroupKeySet> {
226 self.key_sets.iter().find(|e| e.group_key_set_id == id)
227 }
228
229 pub fn key_set_add(&mut self, entry: GroupKeySet) -> Result<(), Error> {
231 if let Some(existing) = self
232 .key_sets
233 .iter_mut()
234 .find(|e| e.group_key_set_id == entry.group_key_set_id)
235 {
236 *existing = entry;
237 } else {
238 self.key_sets
239 .push(entry)
240 .map_err(|_| ErrorCode::ResourceExhausted)?;
241 }
242 Ok(())
243 }
244
245 pub fn key_set_remove(&mut self, id: u16) -> Result<(), Error> {
247 let before = self.key_sets.len();
248 self.key_sets.retain(|e| e.group_key_set_id != id);
249 let removed = self.key_sets.len() < before;
250
251 self.key_map_remove_by_key_set(id);
252
253 if removed {
255 Ok(())
256 } else {
257 Err(Error::new(ErrorCode::NotFound))
258 }
259 }
260
261 pub fn key_map_add(&mut self, entry: GroupKeyMapping) -> Result<(), Error> {
262 self.key_map.push(entry).map_err(|_| ErrorCode::Failure)?;
263
264 Ok(())
265 }
266
267 pub fn key_map_iter(&self) -> impl Iterator<Item = &GroupKeyMapping> {
269 self.key_map.iter()
270 }
271
272 pub fn key_map_replace(
274 &mut self,
275 entries: impl Iterator<Item = GroupKeyMapping>,
276 ) -> Result<(), Error> {
277 self.key_map.clear();
278 for entry in entries {
279 self.key_map
280 .push(entry)
281 .map_err(|_| ErrorCode::ResourceExhausted)?;
282 }
283 Ok(())
284 }
285
286 pub fn key_map_remove_by_key_set(&mut self, key_set_id: u16) {
288 self.key_map.retain(|e| e.group_key_set_id != key_set_id);
289 }
290
291 pub fn iter(&self) -> impl Iterator<Item = &GroupEndpointMapping> {
293 self.endpoint_mapping.iter()
294 }
295
296 pub fn get(&self, group_id: u16) -> Option<&GroupEndpointMapping> {
298 self.endpoint_mapping
299 .iter()
300 .find(|e| e.group_id == group_id)
301 }
302
303 pub fn get_mut(&mut self, group_id: u16) -> Option<&mut GroupEndpointMapping> {
305 self.endpoint_mapping
306 .iter_mut()
307 .find(|e| e.group_id == group_id)
308 }
309
310 pub fn add(
313 &mut self,
314 endpoint_id: u16,
315 group_id: u16,
316 group_name: &str,
317 ) -> Result<bool, Error> {
318 let entry = if let Some(entry) = self
319 .endpoint_mapping
320 .iter_mut()
321 .find(|e| e.group_id == group_id)
322 {
323 entry
324 } else {
325 self.endpoint_mapping
326 .push(GroupEndpointMapping {
327 group_id,
328 endpoints: Vec::new(),
329 group_name: String::from_str(group_name)
330 .map_err(|_| ErrorCode::ConstraintError)?,
331 has_aux_acl: None,
332 mcast_policy: None,
333 })
334 .map_err(|_| ErrorCode::ResourceExhausted)?;
335 unwrap!(self.endpoint_mapping.last_mut())
336 };
337
338 entry.group_name.clear();
340 entry
341 .group_name
342 .push_str(group_name)
343 .map_err(|_| ErrorCode::ConstraintError)?;
344
345 if entry.endpoints.contains(&endpoint_id) {
346 return Ok(true);
347 }
348
349 entry
350 .endpoints
351 .push(endpoint_id)
352 .map_err(|_| ErrorCode::ResourceExhausted)?;
353
354 Ok(false)
355 }
356
357 pub fn remove(&mut self, endpoint_id: u16, group_id: Option<u16>) -> bool {
360 let mut removed = false;
361
362 for entry in self.endpoint_mapping.iter_mut() {
363 if group_id.is_some_and(|id| id != entry.group_id) {
364 continue;
365 }
366 let before = entry.endpoints.len();
367 entry.endpoints.retain(|&ep| ep != endpoint_id);
368 if entry.endpoints.len() < before {
369 removed = true;
370 }
371 }
372
373 self.endpoint_mapping
378 .retain(|e| !e.endpoints.is_empty() || e.groupcast_managed());
379
380 removed
381 }
382
383 pub fn groupcast_join(
399 &mut self,
400 group_id: u16,
401 endpoints: &[u16],
402 replace: bool,
403 mcast_policy: Option<MulticastAddrPolicyEnum>,
404 ) -> Result<(), Error> {
405 let entry = if let Some(entry) = self
406 .endpoint_mapping
407 .iter_mut()
408 .find(|e| e.group_id == group_id)
409 {
410 entry
411 } else {
412 self.endpoint_mapping
413 .push(GroupEndpointMapping {
414 group_id,
415 endpoints: Vec::new(),
416 group_name: String::new(),
417 has_aux_acl: Some(false),
418 mcast_policy: Some(
419 mcast_policy.unwrap_or(MulticastAddrPolicyEnum::IanaAddr),
420 ),
421 })
422 .map_err(|_| ErrorCode::ResourceExhausted)?;
423 unwrap!(self.endpoint_mapping.last_mut())
424 };
425
426 if entry.mcast_policy.is_none() {
430 entry.mcast_policy = Some(MulticastAddrPolicyEnum::PerGroup);
431 }
432
433 if let Some(mcast_policy) = mcast_policy {
434 entry.mcast_policy = Some(mcast_policy);
435 }
436
437 if replace {
438 entry.endpoints.clear();
439 }
440
441 for endpoint in endpoints {
442 if !entry.endpoints.contains(endpoint) {
443 entry
444 .endpoints
445 .push(*endpoint)
446 .map_err(|_| ErrorCode::ResourceExhausted)?;
447 }
448 }
449
450 Ok(())
451 }
452
453 pub fn groupcast_remove(&mut self, group_id: u16) -> bool {
455 let before = self.endpoint_mapping.len();
456 self.endpoint_mapping.retain(|e| e.group_id != group_id);
457
458 before != self.endpoint_mapping.len()
459 }
460
461 pub fn set_has_aux_acl(&mut self, group_id: u16, has_aux_acl: bool) -> bool {
464 let Some(entry) = self
465 .endpoint_mapping
466 .iter_mut()
467 .find(|e| e.group_id == group_id)
468 else {
469 return false;
470 };
471
472 let changed = entry.has_aux_acl() != has_aux_acl;
473 entry.has_aux_acl = Some(has_aux_acl);
474
475 changed
476 }
477
478 pub fn group_count(&self) -> usize {
480 self.endpoint_mapping.len()
481 }
482
483 pub fn key_map_get(&self, group_id: u16) -> Option<u16> {
485 self.key_map
486 .iter()
487 .find(|e| e.group_id == group_id)
488 .map(|e| e.group_key_set_id)
489 }
490
491 pub fn key_map_set_group(&mut self, group_id: u16, key_set_id: u16) -> Result<(), Error> {
494 if let Some(entry) = self.key_map.iter_mut().find(|e| e.group_id == group_id) {
495 entry.group_key_set_id = key_set_id;
496 return Ok(());
497 }
498
499 self.key_map
500 .push(GroupKeyMapping {
501 group_id,
502 group_key_set_id: key_set_id,
503 })
504 .map_err(|_| ErrorCode::ResourceExhausted.into())
505 }
506
507 pub fn key_map_remove_group(&mut self, group_id: u16) {
509 self.key_map.retain(|e| e.group_id != group_id);
510 }
511 }
512
513 impl Default for Groups {
514 fn default() -> Self {
515 Self::new()
516 }
517 }
518
519 impl InitDefault for Groups {
520 fn init_default() -> impl Init<Self> {
521 Self::init()
522 }
523 }
524}
525
526#[cfg(feature = "groups")]
527pub use groups::*;
528
529#[derive(Debug, ToTLV, FromTLV)]
531#[cfg_attr(feature = "defmt", derive(defmt::Format))]
532pub struct Fabric {
533 fab_idx: NonZeroU8,
535 node_id: u64,
537 fabric_id: u64,
539 vendor_id: u16,
541 compressed_fabric_id: u64,
543 secret_key: CanonPkcSecretKey,
545 root_ca: Vec<u8, { MAX_CERT_TLV_LEN }>,
554 icac_or_vvsc: Vec<u8, { MAX_CERT_TLV_LEN }>,
561 vvsc_set: bool,
564 noc: Vec<u8, { MAX_CERT_TLV_LEN }>,
566 ipk: KeySet,
568 label: String<32>,
570 acl: Vec<AclEntry, { acl::MAX_ACL_ENTRIES_PER_FABRIC }>,
572 #[cfg(feature = "groups")]
574 #[tagval(13)]
575 groups: Skippable<Groups>,
576 #[tagval(14)]
581 vid_verification_statement: Vec<u8, VID_VERIFICATION_STATEMENT_LEN>,
582}
583
584pub const VID_VERIFICATION_STATEMENT_LEN: usize = 85;
588
589impl Fabric {
590 fn init(fab_idx: NonZeroU8) -> impl Init<Self> {
599 #[cfg(feature = "groups")]
603 let r = init!(Self {
604 fab_idx,
605 node_id: 0,
606 fabric_id: 0,
607 vendor_id: 0,
608 compressed_fabric_id: 0,
609 secret_key <- CanonPkcSecretKey::init(),
610 root_ca <- Vec::init(),
611 icac_or_vvsc <- Vec::init(),
612 vvsc_set: false,
613 noc <- Vec::init(),
614 ipk <- KeySet::init(),
615 label: String::new(),
616 acl <- Vec::init(),
617 vid_verification_statement <- Vec::init(),
618 groups <- Skippable::init_default(),
619 });
620
621 #[cfg(not(feature = "groups"))]
622 let r = init!(Self {
623 fab_idx,
624 node_id: 0,
625 fabric_id: 0,
626 vendor_id: 0,
627 compressed_fabric_id: 0,
628 secret_key <- CanonPkcSecretKey::init(),
629 root_ca <- Vec::init(),
630 icac_or_vvsc <- Vec::init(),
631 vvsc_set: false,
632 noc <- Vec::init(),
633 ipk <- KeySet::init(),
634 label: String::new(),
635 acl <- Vec::init(),
636 vid_verification_statement <- Vec::init(),
637 });
638 r
639 }
640
641 #[allow(clippy::too_many_arguments)]
653 fn update<C: Crypto>(
654 &mut self,
655 crypto: C,
656 root_ca: Option<&[u8]>,
657 noc: &[u8],
658 icac: &[u8],
659 secret_key: CanonPkcSecretKeyRef<'_>,
660 epoch_key: Option<CanonAeadKeyRef<'_>>,
661 vendor_id: Option<u16>,
662 case_admin_subject: Option<u64>,
663 ) -> Result<(), Error> {
664 if let Some(root_ca) = root_ca {
665 self.root_ca.clear();
666 self.root_ca
667 .extend_from_slice(root_ca)
668 .map_err(|_| ErrorCode::BufferTooSmall)?;
669 }
670 self.icac_or_vvsc.clear();
674 self.icac_or_vvsc
675 .extend_from_slice(icac)
676 .map_err(|_| ErrorCode::BufferTooSmall)?;
677 self.vvsc_set = false;
678 self.noc.clear();
679 self.noc
680 .extend_from_slice(noc)
681 .map_err(|_| ErrorCode::BufferTooSmall)?;
682
683 let root_cert = CertRef::new(TLVElement::new(self.root_ca.as_slice()));
684 let noc_cert = CertRef::new(TLVElement::new(noc));
685
686 self.node_id = noc_cert.get_node_id()?;
687 self.fabric_id = noc_cert.get_fabric_id()?;
688 self.compressed_fabric_id = Self::compute_compressed_fabric_id(
689 &crypto,
690 root_cert.pubkey()?.try_into()?,
691 self.fabric_id,
692 );
693
694 if let Some(epoch_key) = epoch_key {
695 self.ipk
696 .update(&crypto, epoch_key, &self.compressed_fabric_id)?;
697 }
698
699 if let Some(vendor_id) = vendor_id {
700 self.vendor_id = vendor_id;
701 }
702
703 if let Some(case_admin_subject) = case_admin_subject {
704 self.acl.clear();
705 self.acl.push_init(
706 AclEntry::init(None, Privilege::ADMIN, AuthMode::Case)
707 .into_fallible()
708 .chain(|e| {
709 e.fab_idx = Some(self.fab_idx);
710 e.add_subject(case_admin_subject)
711 }),
712 || ErrorCode::ResourceExhausted.into(),
713 )?;
714 }
715
716 self.secret_key.load(secret_key);
717
718 Ok(())
719 }
720
721 pub fn mdns_service(&self) -> Option<MatterLocalService> {
722 self.mdns_service_for(self.node_id)
723 }
724
725 pub fn mdns_service_for(&self, node_id: u64) -> Option<MatterLocalService> {
726 (!self.noc.is_empty()).then_some(MatterLocalService::Commissioned {
727 compressed_fabric_id: self.compressed_fabric_id,
728 node_id,
729 })
730 }
731
732 pub fn is_dest_id<C: Crypto>(
734 &self,
735 crypto: C,
736 random: &[u8],
737 target: &[u8],
738 ) -> Result<(), Error> {
739 let mut mac = crypto.hmac(self.ipk.op_key())?;
740
741 mac.update(random)?;
742 mac.update(CertRef::new(TLVElement::new(self.root_ca())).pubkey()?)?;
743
744 mac.update(&self.fabric_id.to_le_bytes())?;
745 mac.update(&self.node_id.to_le_bytes())?;
746
747 let mut id = MaybeUninit::<Hash>::uninit(); let id = id.init_with(Hash::init());
749 mac.finish(id)?;
750 if id.access() == target {
751 Ok(())
752 } else {
753 Err(ErrorCode::NotFound.into())
754 }
755 }
756
757 pub fn compute_dest_id<C: Crypto>(
766 &self,
767 crypto: C,
768 random: &[u8],
769 target_node_id: u64,
770 out: &mut Hash,
771 ) -> Result<(), Error> {
772 let mut mac = crypto.hmac(self.ipk.op_key())?;
773
774 mac.update(random)?;
775 mac.update(CertRef::new(TLVElement::new(self.root_ca())).pubkey()?)?;
776 mac.update(&self.fabric_id.to_le_bytes())?;
777 mac.update(&target_node_id.to_le_bytes())?;
778
779 mac.finish(out)?;
780 Ok(())
781 }
782
783 pub fn secret_key(&self) -> CanonPkcSecretKeyRef<'_> {
785 self.secret_key.reference()
786 }
787
788 pub fn node_id(&self) -> u64 {
790 self.node_id
791 }
792
793 pub fn fabric_id(&self) -> u64 {
795 self.fabric_id
796 }
797
798 pub fn fab_idx(&self) -> NonZeroU8 {
800 self.fab_idx
801 }
802
803 pub fn compressed_fabric_id(&self) -> u64 {
805 self.compressed_fabric_id
806 }
807
808 pub fn vendor_id(&self) -> u16 {
810 self.vendor_id
811 }
812
813 pub fn label(&self) -> &str {
815 &self.label
816 }
817
818 pub fn root_ca(&self) -> &[u8] {
822 &self.root_ca
823 }
824
825 pub fn icac(&self) -> &[u8] {
834 if self.vvsc_set {
835 &[]
836 } else {
837 &self.icac_or_vvsc
838 }
839 }
840
841 pub fn noc(&self) -> &[u8] {
843 &self.noc
844 }
845
846 pub fn ipk(&self) -> &KeySet {
848 &self.ipk
849 }
850
851 #[cfg(feature = "groups")]
854 pub fn groups(&self) -> &Groups {
855 self.groups.value()
856 }
857
858 #[cfg(feature = "groups")]
861 pub fn groups_mut(&mut self) -> &mut Groups {
862 self.groups.value_mut()
863 }
864
865 pub fn vvsc(&self) -> &[u8] {
871 if self.vvsc_set {
872 &self.icac_or_vvsc
873 } else {
874 &[]
875 }
876 }
877
878 pub fn vid_verification_statement(&self) -> &[u8] {
882 &self.vid_verification_statement
883 }
884
885 pub fn set_vid_verification(
893 &mut self,
894 vendor_id: Option<u16>,
895 vid_verification_statement: Option<&[u8]>,
896 vvsc: Option<&[u8]>,
897 ) -> Result<(), Error> {
898 if let Some(vid) = vendor_id {
899 self.vendor_id = vid;
900 }
901
902 if let Some(vvs) = vid_verification_statement {
903 self.vid_verification_statement.clear();
904 self.vid_verification_statement
905 .extend_from_slice(vvs)
906 .map_err(|_| ErrorCode::BufferTooSmall)?;
907 }
908
909 if let Some(v) = vvsc {
910 if !v.is_empty() {
917 self.icac_or_vvsc.clear();
918 self.icac_or_vvsc
919 .extend_from_slice(v)
920 .map_err(|_| ErrorCode::BufferTooSmall)?;
921 self.vvsc_set = true;
922 } else if self.vvsc_set {
923 self.icac_or_vvsc.clear();
924 self.vvsc_set = false;
925 }
926 }
927
928 Ok(())
929 }
930
931 pub fn acl_iter(&self) -> impl Iterator<Item = &AclEntry> {
933 self.acl.iter()
934 }
935
936 pub fn acl_add(&mut self, mut entry: AclEntry) -> Result<usize, Error> {
940 if entry.auth_mode() == AuthMode::Pase {
941 Err(ErrorCode::ConstraintError)?;
943 }
944
945 entry.fab_idx = Some(self.fab_idx);
947
948 self.acl
949 .push(entry)
950 .map_err(|_| ErrorCode::ResourceExhausted)?;
951
952 Ok(self.acl.len() - 1)
953 }
954
955 pub fn acl_add_init<I>(&mut self, init: I) -> Result<usize, Error>
959 where
960 I: Init<AclEntry, Error>,
961 {
962 self.acl
968 .push_init(init, || ErrorCode::ResourceExhausted.into())?;
969
970 let idx = self.acl.len() - 1;
971 let entry = &mut self.acl[idx];
972
973 entry.fab_idx = Some(self.fab_idx);
975
976 Ok(idx)
977 }
978
979 pub fn acl_update(&mut self, idx: usize, mut entry: AclEntry) -> Result<(), Error> {
981 if self.acl.len() <= idx {
982 return Err(ErrorCode::NotFound.into());
983 }
984
985 entry.fab_idx = Some(self.fab_idx);
987
988 self.acl[idx] = entry;
989
990 Ok(())
991 }
992
993 pub fn acl_update_init<I>(&mut self, idx: usize, init: I) -> Result<(), Error>
995 where
996 I: Init<AclEntry, Error>,
997 {
998 if self.acl.len() <= idx {
999 return Err(ErrorCode::NotFound.into());
1000 }
1001
1002 let mut entry = MaybeUninit::uninit();
1004 let entry = entry.try_init_with(init)?.clone();
1005
1006 self.acl[idx] = entry;
1007
1008 self.acl[idx].fab_idx = Some(self.fab_idx);
1010
1011 Ok(())
1012 }
1013
1014 pub fn acl_remove(&mut self, idx: usize) -> Result<(), Error> {
1016 if self.acl.len() <= idx {
1017 return Err(ErrorCode::NotFound.into());
1018 }
1019
1020 self.acl.remove(idx);
1021
1022 Ok(())
1023 }
1024
1025 pub fn acl_remove_all(&mut self) {
1027 self.acl.clear();
1029 }
1030
1031 fn allow(&self, req: &AccessReq, aux_acl_enabled: bool) -> bool {
1037 for e in &self.acl {
1038 if e.allow(req, aux_acl_enabled) {
1039 return true;
1040 }
1041 }
1042
1043 debug!(
1044 "ACL Disallow for subjects {} fab idx {}",
1045 req.accessor().subjects(),
1046 req.accessor().fab_idx
1047 );
1048
1049 false
1050 }
1051
1052 pub(crate) fn compute_compressed_fabric_id<C: Crypto>(
1054 crypto: C,
1055 root_pubkey: CanonPkcPublicKeyRef<'_>,
1056 fabric_id: u64,
1057 ) -> u64 {
1058 const COMPRESSED_FABRIC_ID_INFO: &[u8; 16] = &[
1059 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x46, 0x61, 0x62, 0x72,
1060 0x69, 0x63,
1061 ];
1062
1063 let mut compressed_fabric_id = CryptoSensitive::<{ COMPRESSED_FABRIC_ID_LEN }>::new();
1064 unwrap!(unwrap!(crypto.kdf()).expand(
1065 &fabric_id.to_be_bytes(),
1066 root_pubkey.split::<1, { PKC_CANON_PUBLIC_KEY_LEN - 1 }>().1,
1067 COMPRESSED_FABRIC_ID_INFO,
1068 &mut compressed_fabric_id,
1069 ));
1070
1071 u64::from_be_bytes(*compressed_fabric_id.access())
1072 }
1073}
1074
1075cfg_if! {
1076 if #[cfg(feature = "max-fabrics-32")] {
1077 pub const MAX_FABRICS: usize = 32;
1079 } else if #[cfg(feature = "max-fabrics-16")] {
1080 pub const MAX_FABRICS: usize = 16;
1082 } else if #[cfg(feature = "max-fabrics-8")] {
1083 pub const MAX_FABRICS: usize = 8;
1085 } else if #[cfg(feature = "max-fabrics-7")] {
1086 pub const MAX_FABRICS: usize = 7;
1088 } else if #[cfg(feature = "max-fabrics-6")] {
1089 pub const MAX_FABRICS: usize = 6;
1091 } else { pub const MAX_FABRICS: usize = 5;
1094 }
1095}
1096
1097pub struct Fabrics {
1099 fabrics: Vec<Fabric, MAX_FABRICS>,
1100}
1101
1102impl Default for Fabrics {
1103 fn default() -> Self {
1104 Self::new()
1105 }
1106}
1107
1108impl Fabrics {
1109 #[inline(always)]
1111 pub const fn new() -> Self {
1112 Self {
1113 fabrics: Vec::new(),
1114 }
1115 }
1116
1117 pub fn init() -> impl Init<Self> {
1119 init!(Self {
1120 fabrics <- Vec::init(),
1121 })
1122 }
1123
1124 pub fn reset(&mut self) {
1126 self.fabrics.clear();
1127 }
1128
1129 pub fn reset_persist<S: KvBlobStore>(
1135 &mut self,
1136 mut store: S,
1137 buf: &mut [u8],
1138 ) -> Result<(), Error> {
1139 self.reset();
1140
1141 for idx in 1..=255u8 {
1142 store.remove(FABRIC_KEYS_START + idx as u16, buf)?;
1143 }
1144
1145 info!("Removed all fabrics from storage");
1146
1147 Ok(())
1148 }
1149
1150 pub fn load_persist<S: KvBlobStore>(
1156 &mut self,
1157 mut store: S,
1158 buf: &mut [u8],
1159 ) -> Result<(), Error> {
1160 self.reset();
1161
1162 for fab_idx in 1..=255u8 {
1163 self.add_load(fab_idx, &mut store, buf)?;
1164 }
1165
1166 Ok(())
1167 }
1168
1169 pub(crate) fn add_load<S: KvBlobStore>(
1170 &mut self,
1171 fab_idx: u8,
1172 mut store: S,
1173 buf: &mut [u8],
1174 ) -> Result<(), Error> {
1175 if let Some(data) = store.load(FABRIC_KEYS_START + fab_idx as u16, buf)? {
1176 self.fabrics
1177 .push_init(Fabric::init_from_tlv(TLVElement::new(data)), || {
1178 ErrorCode::ResourceExhausted.into()
1179 })?;
1180
1181 let fabric = unwrap!(self.fabrics.last());
1182
1183 info!(
1184 "Loaded fabric {} with ID {:x} from storage",
1185 fabric.fab_idx(),
1186 fabric.compressed_fabric_id()
1187 );
1188 }
1189
1190 Ok(())
1191 }
1192
1193 pub fn add_with_post_init<F>(&mut self, post_init: F) -> Result<&mut Fabric, Error>
1199 where
1200 F: FnOnce(&mut Fabric) -> Result<(), Error>,
1201 {
1202 let max_fab_idx = self
1203 .iter()
1204 .map(|fabric| fabric.fab_idx().get())
1205 .max()
1206 .unwrap_or(0);
1207 let fab_idx = unwrap!(NonZeroU8::new(if max_fab_idx < u8::MAX - 1 {
1208 max_fab_idx + 1
1210 } else {
1211 let Some(fab_idx) = (1..u8::MAX)
1213 .find(|fab_idx| self.iter().all(|fabric| fabric.fab_idx().get() != *fab_idx))
1214 else {
1215 return Err(ErrorCode::ResourceExhausted.into());
1216 };
1217
1218 fab_idx
1219 })); self.fabrics.push_init(
1222 Fabric::init(fab_idx)
1223 .into_fallible::<Error>()
1224 .chain(post_init),
1225 || ErrorCode::ResourceExhausted.into(),
1226 )?;
1227
1228 let fabric = unwrap!(self.fabrics.last_mut());
1229
1230 Ok(fabric)
1231 }
1232
1233 #[allow(clippy::too_many_arguments)]
1237 pub fn add<C: Crypto>(
1238 &mut self,
1239 crypto: C,
1240 secret_key: CanonPkcSecretKeyRef<'_>,
1241 root_ca: &[u8],
1242 noc: &[u8],
1243 icac: &[u8],
1244 epoch_key: Option<CanonAeadKeyRef<'_>>,
1245 vendor_id: u16,
1246 case_admin_subject: u64,
1247 ) -> Result<&mut Fabric, Error> {
1248 self.add_with_post_init(|fabric| {
1249 fabric.update(
1250 crypto,
1251 Some(root_ca),
1252 noc,
1253 icac,
1254 secret_key,
1255 epoch_key,
1256 Some(vendor_id),
1257 Some(case_admin_subject),
1258 )
1259 })
1260 }
1261
1262 pub fn update<C: Crypto>(
1273 &mut self,
1274 crypto: C,
1275 fab_idx: NonZeroU8,
1276 secret_key: CanonPkcSecretKeyRef<'_>,
1277 noc: &[u8],
1278 icac: &[u8],
1279 ) -> Result<&mut Fabric, Error> {
1280 let fabric = self.fabric_mut(fab_idx)?;
1281
1282 fabric.update(crypto, None, noc, icac, secret_key, None, None, None)?;
1283
1284 Ok(fabric)
1285 }
1286
1287 pub fn update_label(&mut self, fab_idx: NonZeroU8, label: &str) -> Result<&mut Fabric, Error> {
1288 if self.iter().any(|fabric| {
1289 fabric.fab_idx != fab_idx && !fabric.label.is_empty() && fabric.label == label
1290 }) {
1291 return Err(ErrorCode::Invalid.into());
1292 }
1293
1294 let fabric = self.fabric_mut(fab_idx)?;
1295 fabric.label.clear();
1296 fabric
1297 .label
1298 .push_str(label)
1299 .map_err(|_| ErrorCode::ConstraintError)?;
1300
1301 Ok(fabric)
1302 }
1303
1304 pub fn remove(&mut self, fab_idx: NonZeroU8) -> Result<(), Error> {
1306 let _ = self.fabric(fab_idx)?;
1307
1308 self.fabrics.retain(|fabric| fabric.fab_idx != fab_idx);
1309
1310 Ok(())
1311 }
1312
1313 pub fn get_by_dest_id<C: Crypto>(
1315 &self,
1316 crypto: C,
1317 random: &[u8],
1318 target: &[u8],
1319 ) -> Option<&Fabric> {
1320 self.iter()
1321 .find(|fabric| fabric.is_dest_id(&crypto, random, target).is_ok())
1322 }
1323
1324 pub fn get(&self, fab_idx: NonZeroU8) -> Option<&Fabric> {
1326 self.iter().find(|fabric| fabric.fab_idx == fab_idx)
1327 }
1328
1329 pub fn get_mut(&mut self, fab_idx: NonZeroU8) -> Option<&mut Fabric> {
1331 self.fabrics
1333 .iter_mut()
1334 .find(|fabric| fabric.fab_idx == fab_idx)
1335 }
1336
1337 pub fn iter(&self) -> impl Iterator<Item = &Fabric> {
1339 self.fabrics.iter()
1340 }
1341
1342 pub fn fabric(&self, fab_idx: NonZeroU8) -> Result<&Fabric, Error> {
1346 self.get(fab_idx).ok_or(ErrorCode::NotFound.into())
1347 }
1348
1349 pub fn fabric_mut(&mut self, fab_idx: NonZeroU8) -> Result<&mut Fabric, Error> {
1353 self.get_mut(fab_idx).ok_or(ErrorCode::NotFound.into())
1354 }
1355
1356 pub fn allow(&self, req: &AccessReq, aux_acl_enabled: bool) -> bool {
1362 if req.accessor().auth_mode() == Some(AuthMode::Pase) {
1384 return true;
1385 }
1386
1387 let Ok(fab_idx) = req.accessor().fab_idx() else {
1388 return false;
1389 };
1390
1391 let Some(fabric) = self.get(fab_idx) else {
1392 return false;
1393 };
1394
1395 fabric.allow(req, aux_acl_enabled)
1396 }
1397}
1398
1399pub struct FabricPersist<S>(Persist<S>);
1401
1402impl<S> FabricPersist<S>
1403where
1404 S: KvBlobStoreAccess,
1405{
1406 pub const fn new(kvb: S) -> Self {
1408 Self(Persist::new(kvb))
1409 }
1410
1411 pub fn persist_mut(&mut self) -> &mut Persist<S> {
1413 &mut self.0
1414 }
1415
1416 pub fn store(&mut self, fabric: &Fabric) -> Result<(), Error> {
1418 self.0
1419 .store_tlv(FABRIC_KEYS_START + fabric.fab_idx().get() as u16, fabric)
1420 }
1421
1422 pub fn remove(&mut self, fab_idx: NonZeroU8) -> Result<(), Error> {
1424 self.0.remove(FABRIC_KEYS_START + fab_idx.get() as u16)
1425 }
1426
1427 pub fn run(self) -> Result<(), Error> {
1430 self.0.run()
1431 }
1432}
1433
1434#[cfg(test)]
1435mod tests {
1436 use core::mem::MaybeUninit;
1437
1438 use crate::cert::gen::{CertGenerator, CertType, IssuerDN, SubjectDN, Validity};
1439 use crate::cert::MAX_CERT_TLV_AND_ASN1_LEN;
1440 use crate::crypto::test_only_crypto;
1441 use crate::crypto::{
1442 CanonAeadKeyRef, CanonPkcSecretKey, Crypto, Hash, PublicKey, SecretKey, SigningSecretKey,
1443 AEAD_CANON_KEY_LEN,
1444 };
1445 use crate::utils::init::InitMaybeUninit;
1446
1447 use core::num::NonZeroU8;
1448
1449 use super::{Fabric, Fabrics};
1450
1451 #[test]
1458 fn fabric_tlv_tag_layout_is_stable() {
1459 use crate::tlv::{TLVElement, TLVTag, ToTLV};
1460 use crate::utils::init::InitMaybeUninit;
1461 use crate::utils::storage::WriteBuf;
1462
1463 let mut fabric = core::mem::MaybeUninit::<Fabric>::uninit();
1464 let fabric = fabric.init_with(Fabric::init(unwrap!(NonZeroU8::new(1))));
1465
1466 let mut buf = [0u8; 512];
1467 let mut wb = WriteBuf::new(&mut buf);
1468 fabric.to_tlv(&TLVTag::Anonymous, &mut wb).unwrap();
1469 let len = wb.get_tail();
1470
1471 let root = TLVElement::new(&buf[..len]).structure().unwrap();
1472
1473 assert!(
1478 !root.find_ctx(12).unwrap().is_empty(),
1479 "acl must stay at TLV tag 12"
1480 );
1481
1482 assert!(
1484 !root.find_ctx(14).unwrap().is_empty(),
1485 "vid_verification_statement must stay at TLV tag 14"
1486 );
1487
1488 #[cfg(feature = "groups")]
1491 assert!(
1492 !root.find_ctx(13).unwrap().is_empty(),
1493 "groups must be at TLV tag 13 when compiled in"
1494 );
1495 #[cfg(not(feature = "groups"))]
1496 assert!(
1497 root.find_ctx(13).unwrap().is_empty(),
1498 "no field should occupy tag 13 when groups is compiled out"
1499 );
1500 }
1501
1502 #[test]
1509 fn test_compute_dest_id_matches_is_dest_id() {
1510 let crypto = test_only_crypto();
1511
1512 let fabric_id: u64 = 1;
1513 let rcac_id: u64 = 1;
1514 let node_id: u64 = 100;
1515
1516 let rcac_secret_key = crypto.generate_secret_key().unwrap();
1518 let mut rcac_pubkey_canon = crate::crypto::CanonPkcPublicKey::new();
1519 rcac_secret_key
1520 .pub_key()
1521 .unwrap()
1522 .write_canon(&mut rcac_pubkey_canon)
1523 .unwrap();
1524
1525 let validity = Validity {
1526 not_before: 0,
1527 not_after: 0,
1528 };
1529
1530 let mut rcac_buf = [0u8; MAX_CERT_TLV_AND_ASN1_LEN];
1531 let rcac_len = CertGenerator::new(&mut rcac_buf)
1532 .generate(
1533 &crypto,
1534 CertType::Rcac,
1535 &[0x01],
1536 validity,
1537 SubjectDN {
1538 node_id: None,
1539 fabric_id: Some(fabric_id),
1540 cat_ids: &[],
1541 ca_id: Some(rcac_id),
1542 },
1543 IssuerDN {
1544 ca_id: None,
1545 fabric_id: None,
1546 is_rcac: false,
1547 },
1548 rcac_pubkey_canon.reference(),
1549 None,
1550 &rcac_secret_key,
1551 )
1552 .unwrap();
1553
1554 let noc_secret_key = crypto.generate_secret_key().unwrap();
1556 let mut noc_pubkey_canon = crate::crypto::CanonPkcPublicKey::new();
1557 noc_secret_key
1558 .pub_key()
1559 .unwrap()
1560 .write_canon(&mut noc_pubkey_canon)
1561 .unwrap();
1562
1563 let mut noc_secret_key_canon = CanonPkcSecretKey::new();
1564 noc_secret_key
1565 .write_canon(&mut noc_secret_key_canon)
1566 .unwrap();
1567
1568 let mut noc_buf = [0u8; MAX_CERT_TLV_AND_ASN1_LEN];
1569 let noc_len = CertGenerator::new(&mut noc_buf)
1570 .generate(
1571 &crypto,
1572 CertType::Noc,
1573 &[0x02],
1574 validity,
1575 SubjectDN {
1576 node_id: Some(node_id),
1577 fabric_id: Some(fabric_id),
1578 cat_ids: &[],
1579 ca_id: None,
1580 },
1581 IssuerDN {
1582 ca_id: Some(rcac_id),
1583 fabric_id: Some(fabric_id),
1584 is_rcac: true,
1585 },
1586 noc_pubkey_canon.reference(),
1587 Some(rcac_pubkey_canon.reference()),
1588 &rcac_secret_key,
1589 )
1590 .unwrap();
1591
1592 let epoch_key = [0x5a_u8; AEAD_CANON_KEY_LEN];
1594 let mut fabrics = Fabrics::new();
1595 fabrics
1596 .add(
1597 &crypto,
1598 noc_secret_key_canon.reference(),
1599 &rcac_buf[..rcac_len],
1600 &noc_buf[..noc_len],
1601 &[], Some(CanonAeadKeyRef::new(&epoch_key)),
1603 0x8000,
1604 node_id,
1605 )
1606 .expect("Fabrics::add should succeed");
1607
1608 let fab_idx = core::num::NonZeroU8::new(1).unwrap();
1609 let fabric = fabrics
1610 .get(fab_idx)
1611 .expect("fabric at index 1 should exist");
1612
1613 let random = [0xABu8; 32];
1614
1615 let mut dest_id = MaybeUninit::<Hash>::uninit();
1617 let dest_id = dest_id.init_with(Hash::init());
1618 fabric
1619 .compute_dest_id(&crypto, &random, fabric.node_id(), dest_id)
1620 .expect("compute_dest_id should not fail");
1621
1622 fabric
1624 .is_dest_id(&crypto, &random, dest_id.access())
1625 .expect("is_dest_id should accept hash produced by compute_dest_id");
1626 }
1627}