1use core::mem::MaybeUninit;
19use core::num::NonZeroU8;
20use core::str::FromStr;
21
22use cfg_if::cfg_if;
23use heapless::String;
24
25use crate::acl::{self, AccessReq, AclEntry, AuthMode};
26use crate::cert::{CertRef, MAX_CERT_TLV_LEN};
27use crate::crypto::{
28 CanonAeadKeyRef, CanonPkcPublicKeyRef, CanonPkcSecretKey, CanonPkcSecretKeyRef, Crypto,
29 CryptoSensitive, Digest, Hash, Kdf, PKC_CANON_PUBLIC_KEY_LEN,
30};
31use crate::dm::Privilege;
32use crate::error::{Error, ErrorCode};
33use crate::group_keys::{GroupKeySet, KeySet};
34use crate::persist::{KvBlobStore, KvBlobStoreAccess, Persist, FABRIC_KEYS_START};
35use crate::tlv::{FromTLV, TLVElement, ToTLV};
36use crate::transport::network::MatterLocalService;
37use crate::utils::init::{init, Init, InitMaybeUninit, IntoFallibleInit};
38use crate::utils::storage::Vec;
39
40const COMPRESSED_FABRIC_ID_LEN: usize = 8;
41
42cfg_if! {
43 if #[cfg(feature = "max-group-keys-per-fabric-5")] {
44 pub const MAX_GROUP_KEYS_PER_FABRIC: usize = 5;
46 } else if #[cfg(feature = "max-group-keys-per-fabric-4")] {
47 pub const MAX_GROUP_KEYS_PER_FABRIC: usize = 4;
49 } else if #[cfg(feature = "max-group-keys-per-fabric-3")] {
50 pub const MAX_GROUP_KEYS_PER_FABRIC: usize = 3;
52 } else if #[cfg(feature = "max-group-keys-per-fabric-2")] {
53 pub const MAX_GROUP_KEYS_PER_FABRIC: usize = 2;
55 } else {
56 pub const MAX_GROUP_KEYS_PER_FABRIC: usize = 0;
58 }
59}
60
61pub const MAX_GROUP_NAME_LEN: usize = 16;
63
64cfg_if! {
65 if #[cfg(feature = "max-groups-per-fabric-32")] {
66 pub const MAX_GROUPS_PER_FABRIC: usize = 32;
68 } else if #[cfg(feature = "max-groups-per-fabric-16")] {
69 pub const MAX_GROUPS_PER_FABRIC: usize = 16;
71 } else if #[cfg(feature = "max-groups-per-fabric-12")] {
72 pub const MAX_GROUPS_PER_FABRIC: usize = 12;
74 } else if #[cfg(feature = "max-groups-per-fabric-8")] {
75 pub const MAX_GROUPS_PER_FABRIC: usize = 9;
77 } else if #[cfg(feature = "max-groups-per-fabric-7")] {
78 pub const MAX_GROUPS_PER_FABRIC: usize = 7;
80 } else if #[cfg(feature = "max-groups-per-fabric-6")] {
81 pub const MAX_GROUPS_PER_FABRIC: usize = 6;
83 } else if #[cfg(feature = "max-groups-per-fabric-5")] {
84 pub const MAX_GROUPS_PER_FABRIC: usize = 5;
86 } else if #[cfg(feature = "max-groups-per-fabric-4")] {
87 pub const MAX_GROUPS_PER_FABRIC: usize = 4;
89 } else {
90 pub const MAX_GROUPS_PER_FABRIC: usize = 0;
92 }
93}
94
95cfg_if! {
96 if #[cfg(feature = "max-group-endpoints-per-fabric-5")] {
97 pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 5;
99 } else if #[cfg(feature = "max-group-endpoints-per-fabric-4")] {
100 pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 4;
102 } else if #[cfg(feature = "max-group-endpoints-per-fabric-3")] {
103 pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 3;
105 } else if #[cfg(feature = "max-group-endpoints-per-fabric-2")] {
106 pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 2;
108 } else if #[cfg(feature = "max-group-endpoints-per-fabric-1")] {
109 pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 1;
111 } else {
112 pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 0;
114 }
115}
116
117#[derive(Debug, FromTLV, ToTLV)]
119#[cfg_attr(feature = "defmt", derive(defmt::Format))]
120pub struct GroupEndpointMapping {
121 pub group_id: u16,
122 pub endpoints: Vec<u16, GROUP_ENDPOINTS_PER_FABRIC>,
123 pub group_name: String<MAX_GROUP_NAME_LEN>,
124}
125
126#[derive(Debug, Clone, Default, FromTLV, ToTLV)]
128#[cfg_attr(feature = "defmt", derive(defmt::Format))]
129pub struct GroupKeyMapping {
130 pub group_id: u16,
131 pub group_key_set_id: u16,
132}
133
134#[derive(Debug, FromTLV, ToTLV)]
135#[cfg_attr(feature = "defmt", derive(defmt::Format))]
136pub struct Groups {
137 key_sets: Vec<GroupKeySet, MAX_GROUP_KEYS_PER_FABRIC>,
139 key_map: Vec<GroupKeyMapping, MAX_GROUPS_PER_FABRIC>,
141 endpoint_mapping: Vec<GroupEndpointMapping, MAX_GROUPS_PER_FABRIC>,
143}
144
145impl Groups {
146 fn init() -> impl Init<Self> {
147 init!(Self {
148 key_sets <- Vec::init(),
149 key_map <- Vec::init(),
150 endpoint_mapping <- Vec::init(),
151 })
152 }
153
154 pub fn key_set_iter(&self) -> impl Iterator<Item = &GroupKeySet> {
156 self.key_sets.iter()
157 }
158
159 pub fn key_set_get(&self, id: u16) -> Option<&GroupKeySet> {
161 self.key_sets.iter().find(|e| e.group_key_set_id == id)
162 }
163
164 pub fn key_set_add(&mut self, entry: GroupKeySet) -> Result<(), Error> {
166 if let Some(existing) = self
167 .key_sets
168 .iter_mut()
169 .find(|e| e.group_key_set_id == entry.group_key_set_id)
170 {
171 *existing = entry;
172 } else {
173 self.key_sets
174 .push(entry)
175 .map_err(|_| ErrorCode::ResourceExhausted)?;
176 }
177 Ok(())
178 }
179
180 pub fn key_set_remove(&mut self, id: u16) -> Result<(), Error> {
182 let before = self.key_sets.len();
183 self.key_sets.retain(|e| e.group_key_set_id != id);
184 let removed = self.key_sets.len() < before;
185
186 self.key_map_remove_by_key_set(id);
187
188 if removed {
190 Ok(())
191 } else {
192 Err(Error::new(ErrorCode::NotFound))
193 }
194 }
195
196 pub fn key_map_add(&mut self, entry: GroupKeyMapping) -> Result<(), Error> {
197 self.key_map.push(entry).map_err(|_| ErrorCode::Failure)?;
198
199 Ok(())
200 }
201
202 pub fn key_map_iter(&self) -> impl Iterator<Item = &GroupKeyMapping> {
204 self.key_map.iter()
205 }
206
207 pub fn key_map_replace(
209 &mut self,
210 entries: impl Iterator<Item = GroupKeyMapping>,
211 ) -> Result<(), Error> {
212 self.key_map.clear();
213 for entry in entries {
214 self.key_map
215 .push(entry)
216 .map_err(|_| ErrorCode::ResourceExhausted)?;
217 }
218 Ok(())
219 }
220
221 pub fn key_map_remove_by_key_set(&mut self, key_set_id: u16) {
223 self.key_map.retain(|e| e.group_key_set_id != key_set_id);
224 }
225
226 pub fn iter(&self) -> impl Iterator<Item = &GroupEndpointMapping> {
228 self.endpoint_mapping.iter()
229 }
230
231 pub fn get(&self, group_id: u16) -> Option<&GroupEndpointMapping> {
233 self.endpoint_mapping
234 .iter()
235 .find(|e| e.group_id == group_id)
236 }
237
238 pub fn add(
241 &mut self,
242 endpoint_id: u16,
243 group_id: u16,
244 group_name: &str,
245 ) -> Result<bool, Error> {
246 let entry = if let Some(entry) = self
247 .endpoint_mapping
248 .iter_mut()
249 .find(|e| e.group_id == group_id)
250 {
251 entry
252 } else {
253 self.endpoint_mapping
254 .push(GroupEndpointMapping {
255 group_id,
256 endpoints: Vec::new(),
257 group_name: unwrap!(String::from_str(group_name)),
258 })
259 .map_err(|_| ErrorCode::ResourceExhausted)?;
260 unwrap!(self.endpoint_mapping.last_mut())
261 };
262
263 entry.group_name.clear();
265 unwrap!(entry.group_name.push_str(group_name));
266
267 if entry.endpoints.contains(&endpoint_id) {
268 return Ok(true);
269 }
270
271 entry
272 .endpoints
273 .push(endpoint_id)
274 .map_err(|_| ErrorCode::ResourceExhausted)?;
275
276 Ok(false)
277 }
278
279 pub fn remove(&mut self, endpoint_id: u16, group_id: Option<u16>) -> bool {
282 let mut removed = false;
283
284 for entry in self.endpoint_mapping.iter_mut() {
285 if group_id.is_some_and(|id| id != entry.group_id) {
286 continue;
287 }
288 let before = entry.endpoints.len();
289 entry.endpoints.retain(|&ep| ep != endpoint_id);
290 if entry.endpoints.len() < before {
291 removed = true;
292 }
293 }
294
295 self.endpoint_mapping.retain(|e| !e.endpoints.is_empty());
297
298 removed
299 }
300}
301
302#[derive(Debug, ToTLV, FromTLV)]
304#[cfg_attr(feature = "defmt", derive(defmt::Format))]
305pub struct Fabric {
306 fab_idx: NonZeroU8,
308 node_id: u64,
310 fabric_id: u64,
312 vendor_id: u16,
314 compressed_fabric_id: u64,
316 secret_key: CanonPkcSecretKey,
318 root_ca: Vec<u8, { MAX_CERT_TLV_LEN }>,
327 icac_or_vvsc: Vec<u8, { MAX_CERT_TLV_LEN }>,
334 vvsc_set: bool,
337 noc: Vec<u8, { MAX_CERT_TLV_LEN }>,
339 ipk: KeySet,
341 label: String<32>,
343 acl: Vec<AclEntry, { acl::MAX_ACL_ENTRIES_PER_FABRIC }>,
345 groups: Groups,
347 vid_verification_statement: Vec<u8, VID_VERIFICATION_STATEMENT_LEN>,
352}
353
354pub const VID_VERIFICATION_STATEMENT_LEN: usize = 85;
358
359impl Fabric {
360 fn init(fab_idx: NonZeroU8) -> impl Init<Self> {
369 init!(Self {
370 fab_idx,
371 node_id: 0,
372 fabric_id: 0,
373 vendor_id: 0,
374 compressed_fabric_id: 0,
375 secret_key <- CanonPkcSecretKey::init(),
376 root_ca <- Vec::init(),
377 icac_or_vvsc <- Vec::init(),
378 vvsc_set: false,
379 noc <- Vec::init(),
380 ipk <- KeySet::init(),
381 label: String::new(),
382 acl <- Vec::init(),
383 groups <- Groups::init(),
384 vid_verification_statement <- Vec::init(),
385 })
386 }
387
388 #[allow(clippy::too_many_arguments)]
400 fn update<C: Crypto>(
401 &mut self,
402 crypto: C,
403 root_ca: Option<&[u8]>,
404 noc: &[u8],
405 icac: &[u8],
406 secret_key: CanonPkcSecretKeyRef<'_>,
407 epoch_key: Option<CanonAeadKeyRef<'_>>,
408 vendor_id: Option<u16>,
409 case_admin_subject: Option<u64>,
410 ) -> Result<(), Error> {
411 if let Some(root_ca) = root_ca {
412 self.root_ca.clear();
413 self.root_ca
414 .extend_from_slice(root_ca)
415 .map_err(|_| ErrorCode::BufferTooSmall)?;
416 }
417 self.icac_or_vvsc.clear();
421 self.icac_or_vvsc
422 .extend_from_slice(icac)
423 .map_err(|_| ErrorCode::BufferTooSmall)?;
424 self.vvsc_set = false;
425 self.noc.clear();
426 self.noc
427 .extend_from_slice(noc)
428 .map_err(|_| ErrorCode::BufferTooSmall)?;
429
430 let root_cert = CertRef::new(TLVElement::new(self.root_ca.as_slice()));
431 let noc_cert = CertRef::new(TLVElement::new(noc));
432
433 self.node_id = noc_cert.get_node_id()?;
434 self.fabric_id = noc_cert.get_fabric_id()?;
435 self.compressed_fabric_id = Self::compute_compressed_fabric_id(
436 &crypto,
437 root_cert.pubkey()?.try_into()?,
438 self.fabric_id,
439 );
440
441 if let Some(epoch_key) = epoch_key {
442 self.ipk
443 .update(&crypto, epoch_key, &self.compressed_fabric_id)?;
444 }
445
446 if let Some(vendor_id) = vendor_id {
447 self.vendor_id = vendor_id;
448 }
449
450 if let Some(case_admin_subject) = case_admin_subject {
451 self.acl.clear();
452 self.acl.push_init(
453 AclEntry::init(None, Privilege::ADMIN, AuthMode::Case)
454 .into_fallible()
455 .chain(|e| {
456 e.fab_idx = Some(self.fab_idx);
457 e.add_subject(case_admin_subject)
458 }),
459 || ErrorCode::ResourceExhausted.into(),
460 )?;
461 }
462
463 self.secret_key.load(secret_key);
464
465 Ok(())
466 }
467
468 pub fn mdns_service(&self) -> Option<MatterLocalService> {
469 self.mdns_service_for(self.node_id)
470 }
471
472 pub fn mdns_service_for(&self, node_id: u64) -> Option<MatterLocalService> {
473 (!self.noc.is_empty()).then_some(MatterLocalService::Commissioned {
474 compressed_fabric_id: self.compressed_fabric_id,
475 node_id,
476 })
477 }
478
479 pub fn is_dest_id<C: Crypto>(
481 &self,
482 crypto: C,
483 random: &[u8],
484 target: &[u8],
485 ) -> Result<(), Error> {
486 let mut mac = crypto.hmac(self.ipk.op_key())?;
487
488 mac.update(random)?;
489 mac.update(CertRef::new(TLVElement::new(self.root_ca())).pubkey()?)?;
490
491 mac.update(&self.fabric_id.to_le_bytes())?;
492 mac.update(&self.node_id.to_le_bytes())?;
493
494 let mut id = MaybeUninit::<Hash>::uninit(); let id = id.init_with(Hash::init());
496 mac.finish(id)?;
497 if id.access() == target {
498 Ok(())
499 } else {
500 Err(ErrorCode::NotFound.into())
501 }
502 }
503
504 pub fn compute_dest_id<C: Crypto>(
513 &self,
514 crypto: C,
515 random: &[u8],
516 target_node_id: u64,
517 out: &mut Hash,
518 ) -> Result<(), Error> {
519 let mut mac = crypto.hmac(self.ipk.op_key())?;
520
521 mac.update(random)?;
522 mac.update(CertRef::new(TLVElement::new(self.root_ca())).pubkey()?)?;
523 mac.update(&self.fabric_id.to_le_bytes())?;
524 mac.update(&target_node_id.to_le_bytes())?;
525
526 mac.finish(out)?;
527 Ok(())
528 }
529
530 pub fn secret_key(&self) -> CanonPkcSecretKeyRef<'_> {
532 self.secret_key.reference()
533 }
534
535 pub fn node_id(&self) -> u64 {
537 self.node_id
538 }
539
540 pub fn fabric_id(&self) -> u64 {
542 self.fabric_id
543 }
544
545 pub fn fab_idx(&self) -> NonZeroU8 {
547 self.fab_idx
548 }
549
550 pub fn compressed_fabric_id(&self) -> u64 {
552 self.compressed_fabric_id
553 }
554
555 pub fn vendor_id(&self) -> u16 {
557 self.vendor_id
558 }
559
560 pub fn label(&self) -> &str {
562 &self.label
563 }
564
565 pub fn root_ca(&self) -> &[u8] {
569 &self.root_ca
570 }
571
572 pub fn icac(&self) -> &[u8] {
581 if self.vvsc_set {
582 &[]
583 } else {
584 &self.icac_or_vvsc
585 }
586 }
587
588 pub fn noc(&self) -> &[u8] {
590 &self.noc
591 }
592
593 pub fn ipk(&self) -> &KeySet {
595 &self.ipk
596 }
597
598 pub fn groups(&self) -> &Groups {
600 &self.groups
601 }
602
603 pub fn groups_mut(&mut self) -> &mut Groups {
605 &mut self.groups
606 }
607
608 pub fn vvsc(&self) -> &[u8] {
614 if self.vvsc_set {
615 &self.icac_or_vvsc
616 } else {
617 &[]
618 }
619 }
620
621 pub fn vid_verification_statement(&self) -> &[u8] {
625 &self.vid_verification_statement
626 }
627
628 pub fn set_vid_verification(
636 &mut self,
637 vendor_id: Option<u16>,
638 vid_verification_statement: Option<&[u8]>,
639 vvsc: Option<&[u8]>,
640 ) -> Result<(), Error> {
641 if let Some(vid) = vendor_id {
642 self.vendor_id = vid;
643 }
644
645 if let Some(vvs) = vid_verification_statement {
646 self.vid_verification_statement.clear();
647 self.vid_verification_statement
648 .extend_from_slice(vvs)
649 .map_err(|_| ErrorCode::BufferTooSmall)?;
650 }
651
652 if let Some(v) = vvsc {
653 if !v.is_empty() {
660 self.icac_or_vvsc.clear();
661 self.icac_or_vvsc
662 .extend_from_slice(v)
663 .map_err(|_| ErrorCode::BufferTooSmall)?;
664 self.vvsc_set = true;
665 } else if self.vvsc_set {
666 self.icac_or_vvsc.clear();
667 self.vvsc_set = false;
668 }
669 }
670
671 Ok(())
672 }
673
674 pub fn acl_iter(&self) -> impl Iterator<Item = &AclEntry> {
676 self.acl.iter()
677 }
678
679 pub fn acl_add(&mut self, mut entry: AclEntry) -> Result<usize, Error> {
683 if entry.auth_mode() == AuthMode::Pase {
684 Err(ErrorCode::ConstraintError)?;
686 }
687
688 entry.fab_idx = Some(self.fab_idx);
690
691 self.acl
692 .push(entry)
693 .map_err(|_| ErrorCode::ResourceExhausted)?;
694
695 Ok(self.acl.len() - 1)
696 }
697
698 pub fn acl_add_init<I>(&mut self, init: I) -> Result<usize, Error>
702 where
703 I: Init<AclEntry, Error>,
704 {
705 self.acl
711 .push_init(init, || ErrorCode::ResourceExhausted.into())?;
712
713 let idx = self.acl.len() - 1;
714 let entry = &mut self.acl[idx];
715
716 entry.fab_idx = Some(self.fab_idx);
718
719 Ok(idx)
720 }
721
722 pub fn acl_update(&mut self, idx: usize, mut entry: AclEntry) -> Result<(), Error> {
724 if self.acl.len() <= idx {
725 return Err(ErrorCode::NotFound.into());
726 }
727
728 entry.fab_idx = Some(self.fab_idx);
730
731 self.acl[idx] = entry;
732
733 Ok(())
734 }
735
736 pub fn acl_update_init<I>(&mut self, idx: usize, init: I) -> Result<(), Error>
738 where
739 I: Init<AclEntry, Error>,
740 {
741 if self.acl.len() <= idx {
742 return Err(ErrorCode::NotFound.into());
743 }
744
745 let mut entry = MaybeUninit::uninit();
747 let entry = entry.try_init_with(init)?.clone();
748
749 self.acl[idx] = entry;
750
751 self.acl[idx].fab_idx = Some(self.fab_idx);
753
754 Ok(())
755 }
756
757 pub fn acl_remove(&mut self, idx: usize) -> Result<(), Error> {
759 if self.acl.len() <= idx {
760 return Err(ErrorCode::NotFound.into());
761 }
762
763 self.acl.remove(idx);
764
765 Ok(())
766 }
767
768 pub fn acl_remove_all(&mut self) {
770 self.acl.clear();
772 }
773
774 fn allow(&self, req: &AccessReq) -> bool {
778 for e in &self.acl {
779 if e.allow(req) {
780 return true;
781 }
782 }
783
784 debug!(
785 "ACL Disallow for subjects {} fab idx {}",
786 req.accessor().subjects(),
787 req.accessor().fab_idx
788 );
789
790 false
791 }
792
793 pub(crate) fn compute_compressed_fabric_id<C: Crypto>(
795 crypto: C,
796 root_pubkey: CanonPkcPublicKeyRef<'_>,
797 fabric_id: u64,
798 ) -> u64 {
799 const COMPRESSED_FABRIC_ID_INFO: &[u8; 16] = &[
800 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x46, 0x61, 0x62, 0x72,
801 0x69, 0x63,
802 ];
803
804 let mut compressed_fabric_id = CryptoSensitive::<{ COMPRESSED_FABRIC_ID_LEN }>::new();
805 unwrap!(unwrap!(crypto.kdf()).expand(
806 &fabric_id.to_be_bytes(),
807 root_pubkey.split::<1, { PKC_CANON_PUBLIC_KEY_LEN - 1 }>().1,
808 COMPRESSED_FABRIC_ID_INFO,
809 &mut compressed_fabric_id,
810 ));
811
812 u64::from_be_bytes(*compressed_fabric_id.access())
813 }
814}
815
816cfg_if! {
817 if #[cfg(feature = "max-fabrics-32")] {
818 pub const MAX_FABRICS: usize = 32;
820 } else if #[cfg(feature = "max-fabrics-16")] {
821 pub const MAX_FABRICS: usize = 16;
823 } else if #[cfg(feature = "max-fabrics-8")] {
824 pub const MAX_FABRICS: usize = 8;
826 } else if #[cfg(feature = "max-fabrics-7")] {
827 pub const MAX_FABRICS: usize = 7;
829 } else if #[cfg(feature = "max-fabrics-6")] {
830 pub const MAX_FABRICS: usize = 6;
832 } else { pub const MAX_FABRICS: usize = 5;
835 }
836}
837
838pub struct Fabrics {
840 fabrics: Vec<Fabric, MAX_FABRICS>,
841}
842
843impl Default for Fabrics {
844 fn default() -> Self {
845 Self::new()
846 }
847}
848
849impl Fabrics {
850 #[inline(always)]
852 pub const fn new() -> Self {
853 Self {
854 fabrics: Vec::new(),
855 }
856 }
857
858 pub fn init() -> impl Init<Self> {
860 init!(Self {
861 fabrics <- Vec::init(),
862 })
863 }
864
865 pub fn reset(&mut self) {
867 self.fabrics.clear();
868 }
869
870 pub fn reset_persist<S: KvBlobStore>(
876 &mut self,
877 mut store: S,
878 buf: &mut [u8],
879 ) -> Result<(), Error> {
880 self.reset();
881
882 for idx in 1..=255u8 {
883 store.remove(FABRIC_KEYS_START + idx as u16, buf)?;
884 }
885
886 info!("Removed all fabrics from storage");
887
888 Ok(())
889 }
890
891 pub fn load_persist<S: KvBlobStore>(
897 &mut self,
898 mut store: S,
899 buf: &mut [u8],
900 ) -> Result<(), Error> {
901 self.reset();
902
903 for fab_idx in 1..=255u8 {
904 self.add_load(fab_idx, &mut store, buf)?;
905 }
906
907 Ok(())
908 }
909
910 pub(crate) fn add_load<S: KvBlobStore>(
911 &mut self,
912 fab_idx: u8,
913 mut store: S,
914 buf: &mut [u8],
915 ) -> Result<(), Error> {
916 if let Some(data) = store.load(FABRIC_KEYS_START + fab_idx as u16, buf)? {
917 self.fabrics
918 .push_init(Fabric::init_from_tlv(TLVElement::new(data)), || {
919 ErrorCode::ResourceExhausted.into()
920 })?;
921
922 let fabric = unwrap!(self.fabrics.last());
923
924 info!(
925 "Loaded fabric {} with ID {:x} from storage",
926 fabric.fab_idx(),
927 fabric.compressed_fabric_id()
928 );
929 }
930
931 Ok(())
932 }
933
934 pub fn add_with_post_init<F>(&mut self, post_init: F) -> Result<&mut Fabric, Error>
940 where
941 F: FnOnce(&mut Fabric) -> Result<(), Error>,
942 {
943 let max_fab_idx = self
944 .iter()
945 .map(|fabric| fabric.fab_idx().get())
946 .max()
947 .unwrap_or(0);
948 let fab_idx = unwrap!(NonZeroU8::new(if max_fab_idx < u8::MAX - 1 {
949 max_fab_idx + 1
951 } else {
952 let Some(fab_idx) = (1..u8::MAX)
954 .find(|fab_idx| self.iter().all(|fabric| fabric.fab_idx().get() != *fab_idx))
955 else {
956 return Err(ErrorCode::ResourceExhausted.into());
957 };
958
959 fab_idx
960 })); self.fabrics.push_init(
963 Fabric::init(fab_idx)
964 .into_fallible::<Error>()
965 .chain(post_init),
966 || ErrorCode::ResourceExhausted.into(),
967 )?;
968
969 let fabric = unwrap!(self.fabrics.last_mut());
970
971 Ok(fabric)
972 }
973
974 #[allow(clippy::too_many_arguments)]
978 pub fn add<C: Crypto>(
979 &mut self,
980 crypto: C,
981 secret_key: CanonPkcSecretKeyRef<'_>,
982 root_ca: &[u8],
983 noc: &[u8],
984 icac: &[u8],
985 epoch_key: Option<CanonAeadKeyRef<'_>>,
986 vendor_id: u16,
987 case_admin_subject: u64,
988 ) -> Result<&mut Fabric, Error> {
989 self.add_with_post_init(|fabric| {
990 fabric.update(
991 crypto,
992 Some(root_ca),
993 noc,
994 icac,
995 secret_key,
996 epoch_key,
997 Some(vendor_id),
998 Some(case_admin_subject),
999 )
1000 })
1001 }
1002
1003 pub fn update<C: Crypto>(
1014 &mut self,
1015 crypto: C,
1016 fab_idx: NonZeroU8,
1017 secret_key: CanonPkcSecretKeyRef<'_>,
1018 noc: &[u8],
1019 icac: &[u8],
1020 ) -> Result<&mut Fabric, Error> {
1021 let fabric = self.fabric_mut(fab_idx)?;
1022
1023 fabric.update(crypto, None, noc, icac, secret_key, None, None, None)?;
1024
1025 Ok(fabric)
1026 }
1027
1028 pub fn update_label(&mut self, fab_idx: NonZeroU8, label: &str) -> Result<&mut Fabric, Error> {
1029 if self.iter().any(|fabric| {
1030 fabric.fab_idx != fab_idx && !fabric.label.is_empty() && fabric.label == label
1031 }) {
1032 return Err(ErrorCode::Invalid.into());
1033 }
1034
1035 let fabric = self.fabric_mut(fab_idx)?;
1036 fabric.label.clear();
1037 fabric
1038 .label
1039 .push_str(label)
1040 .map_err(|_| ErrorCode::ConstraintError)?;
1041
1042 Ok(fabric)
1043 }
1044
1045 pub fn remove(&mut self, fab_idx: NonZeroU8) -> Result<(), Error> {
1047 let _ = self.fabric(fab_idx)?;
1048
1049 self.fabrics.retain(|fabric| fabric.fab_idx != fab_idx);
1050
1051 Ok(())
1052 }
1053
1054 pub fn get_by_dest_id<C: Crypto>(
1056 &self,
1057 crypto: C,
1058 random: &[u8],
1059 target: &[u8],
1060 ) -> Option<&Fabric> {
1061 self.iter()
1062 .find(|fabric| fabric.is_dest_id(&crypto, random, target).is_ok())
1063 }
1064
1065 pub fn get(&self, fab_idx: NonZeroU8) -> Option<&Fabric> {
1067 self.iter().find(|fabric| fabric.fab_idx == fab_idx)
1068 }
1069
1070 pub fn get_mut(&mut self, fab_idx: NonZeroU8) -> Option<&mut Fabric> {
1072 self.fabrics
1074 .iter_mut()
1075 .find(|fabric| fabric.fab_idx == fab_idx)
1076 }
1077
1078 pub fn iter(&self) -> impl Iterator<Item = &Fabric> {
1080 self.fabrics.iter()
1081 }
1082
1083 pub fn fabric(&self, fab_idx: NonZeroU8) -> Result<&Fabric, Error> {
1087 self.get(fab_idx).ok_or(ErrorCode::NotFound.into())
1088 }
1089
1090 pub fn fabric_mut(&mut self, fab_idx: NonZeroU8) -> Result<&mut Fabric, Error> {
1094 self.get_mut(fab_idx).ok_or(ErrorCode::NotFound.into())
1095 }
1096
1097 pub fn allow(&self, req: &AccessReq) -> bool {
1100 if req.accessor().auth_mode() == Some(AuthMode::Pase) {
1122 return true;
1123 }
1124
1125 let Ok(fab_idx) = req.accessor().fab_idx() else {
1126 return false;
1127 };
1128
1129 let Some(fabric) = self.get(fab_idx) else {
1130 return false;
1131 };
1132
1133 fabric.allow(req)
1134 }
1135}
1136
1137pub struct FabricPersist<S>(Persist<S>);
1139
1140impl<S> FabricPersist<S>
1141where
1142 S: KvBlobStoreAccess,
1143{
1144 pub const fn new(kvb: S) -> Self {
1146 Self(Persist::new(kvb))
1147 }
1148
1149 pub fn persist_mut(&mut self) -> &mut Persist<S> {
1151 &mut self.0
1152 }
1153
1154 pub fn store(&mut self, fabric: &Fabric) -> Result<(), Error> {
1156 self.0
1157 .store_tlv(FABRIC_KEYS_START + fabric.fab_idx().get() as u16, fabric)
1158 }
1159
1160 pub fn remove(&mut self, fab_idx: NonZeroU8) -> Result<(), Error> {
1162 self.0.remove(FABRIC_KEYS_START + fab_idx.get() as u16)
1163 }
1164
1165 pub fn run(self) -> Result<(), Error> {
1168 self.0.run()
1169 }
1170}
1171
1172#[cfg(test)]
1173mod tests {
1174 use core::mem::MaybeUninit;
1175
1176 use crate::cert::gen::{CertGenerator, CertType, IssuerDN, SubjectDN, Validity};
1177 use crate::cert::MAX_CERT_TLV_AND_ASN1_LEN;
1178 use crate::crypto::test_only_crypto;
1179 use crate::crypto::{
1180 CanonAeadKeyRef, CanonPkcSecretKey, Crypto, Hash, PublicKey, SecretKey, SigningSecretKey,
1181 AEAD_CANON_KEY_LEN,
1182 };
1183 use crate::utils::init::InitMaybeUninit;
1184
1185 use super::Fabrics;
1186
1187 #[test]
1194 fn test_compute_dest_id_matches_is_dest_id() {
1195 let crypto = test_only_crypto();
1196
1197 let fabric_id: u64 = 1;
1198 let rcac_id: u64 = 1;
1199 let node_id: u64 = 100;
1200
1201 let rcac_secret_key = crypto.generate_secret_key().unwrap();
1203 let mut rcac_pubkey_canon = crate::crypto::CanonPkcPublicKey::new();
1204 rcac_secret_key
1205 .pub_key()
1206 .unwrap()
1207 .write_canon(&mut rcac_pubkey_canon)
1208 .unwrap();
1209
1210 let validity = Validity {
1211 not_before: 0,
1212 not_after: 0,
1213 };
1214
1215 let mut rcac_buf = [0u8; MAX_CERT_TLV_AND_ASN1_LEN];
1216 let rcac_len = CertGenerator::new(&mut rcac_buf)
1217 .generate(
1218 &crypto,
1219 CertType::Rcac,
1220 &[0x01],
1221 validity,
1222 SubjectDN {
1223 node_id: None,
1224 fabric_id: Some(fabric_id),
1225 cat_ids: &[],
1226 ca_id: Some(rcac_id),
1227 },
1228 IssuerDN {
1229 ca_id: None,
1230 fabric_id: None,
1231 is_rcac: false,
1232 },
1233 rcac_pubkey_canon.reference(),
1234 None,
1235 &rcac_secret_key,
1236 )
1237 .unwrap();
1238
1239 let noc_secret_key = crypto.generate_secret_key().unwrap();
1241 let mut noc_pubkey_canon = crate::crypto::CanonPkcPublicKey::new();
1242 noc_secret_key
1243 .pub_key()
1244 .unwrap()
1245 .write_canon(&mut noc_pubkey_canon)
1246 .unwrap();
1247
1248 let mut noc_secret_key_canon = CanonPkcSecretKey::new();
1249 noc_secret_key
1250 .write_canon(&mut noc_secret_key_canon)
1251 .unwrap();
1252
1253 let mut noc_buf = [0u8; MAX_CERT_TLV_AND_ASN1_LEN];
1254 let noc_len = CertGenerator::new(&mut noc_buf)
1255 .generate(
1256 &crypto,
1257 CertType::Noc,
1258 &[0x02],
1259 validity,
1260 SubjectDN {
1261 node_id: Some(node_id),
1262 fabric_id: Some(fabric_id),
1263 cat_ids: &[],
1264 ca_id: None,
1265 },
1266 IssuerDN {
1267 ca_id: Some(rcac_id),
1268 fabric_id: Some(fabric_id),
1269 is_rcac: true,
1270 },
1271 noc_pubkey_canon.reference(),
1272 Some(rcac_pubkey_canon.reference()),
1273 &rcac_secret_key,
1274 )
1275 .unwrap();
1276
1277 let epoch_key = [0x5a_u8; AEAD_CANON_KEY_LEN];
1279 let mut fabrics = Fabrics::new();
1280 fabrics
1281 .add(
1282 &crypto,
1283 noc_secret_key_canon.reference(),
1284 &rcac_buf[..rcac_len],
1285 &noc_buf[..noc_len],
1286 &[], Some(CanonAeadKeyRef::new(&epoch_key)),
1288 0x8000,
1289 node_id,
1290 )
1291 .expect("Fabrics::add should succeed");
1292
1293 let fab_idx = core::num::NonZeroU8::new(1).unwrap();
1294 let fabric = fabrics
1295 .get(fab_idx)
1296 .expect("fabric at index 1 should exist");
1297
1298 let random = [0xABu8; 32];
1299
1300 let mut dest_id = MaybeUninit::<Hash>::uninit();
1302 let dest_id = dest_id.init_with(Hash::init());
1303 fabric
1304 .compute_dest_id(&crypto, &random, fabric.node_id(), dest_id)
1305 .expect("compute_dest_id should not fail");
1306
1307 fabric
1309 .is_dest_id(&crypto, &random, dest_id.access())
1310 .expect("is_dest_id should accept hash produced by compute_dest_id");
1311 }
1312}