use core::mem::MaybeUninit;
use core::num::NonZeroU8;
use core::str::FromStr;
use cfg_if::cfg_if;
use heapless::String;
use crate::acl::{self, AccessReq, AclEntry, AuthMode};
use crate::cert::{CertRef, MAX_CERT_TLV_LEN};
use crate::crypto::{
CanonAeadKeyRef, CanonPkcPublicKeyRef, CanonPkcSecretKey, CanonPkcSecretKeyRef, Crypto,
CryptoSensitive, Digest, Hash, Kdf, PKC_CANON_PUBLIC_KEY_LEN,
};
use crate::dm::Privilege;
use crate::error::{Error, ErrorCode};
use crate::group_keys::{GroupKeySet, KeySet};
use crate::persist::{KvBlobStore, KvBlobStoreAccess, Persist, FABRIC_KEYS_START};
use crate::tlv::{FromTLV, TLVElement, ToTLV};
use crate::transport::network::MatterLocalService;
use crate::utils::init::{init, Init, InitMaybeUninit, IntoFallibleInit};
use crate::utils::storage::Vec;
const COMPRESSED_FABRIC_ID_LEN: usize = 8;
cfg_if! {
if #[cfg(feature = "max-group-keys-per-fabric-5")] {
pub const MAX_GROUP_KEYS_PER_FABRIC: usize = 5;
} else if #[cfg(feature = "max-group-keys-per-fabric-4")] {
pub const MAX_GROUP_KEYS_PER_FABRIC: usize = 4;
} else if #[cfg(feature = "max-group-keys-per-fabric-3")] {
pub const MAX_GROUP_KEYS_PER_FABRIC: usize = 3;
} else if #[cfg(feature = "max-group-keys-per-fabric-2")] {
pub const MAX_GROUP_KEYS_PER_FABRIC: usize = 2;
} else {
pub const MAX_GROUP_KEYS_PER_FABRIC: usize = 0;
}
}
pub const MAX_GROUP_NAME_LEN: usize = 16;
cfg_if! {
if #[cfg(feature = "max-groups-per-fabric-32")] {
pub const MAX_GROUPS_PER_FABRIC: usize = 32;
} else if #[cfg(feature = "max-groups-per-fabric-16")] {
pub const MAX_GROUPS_PER_FABRIC: usize = 16;
} else if #[cfg(feature = "max-groups-per-fabric-12")] {
pub const MAX_GROUPS_PER_FABRIC: usize = 12;
} else if #[cfg(feature = "max-groups-per-fabric-8")] {
pub const MAX_GROUPS_PER_FABRIC: usize = 9;
} else if #[cfg(feature = "max-groups-per-fabric-7")] {
pub const MAX_GROUPS_PER_FABRIC: usize = 7;
} else if #[cfg(feature = "max-groups-per-fabric-6")] {
pub const MAX_GROUPS_PER_FABRIC: usize = 6;
} else if #[cfg(feature = "max-groups-per-fabric-5")] {
pub const MAX_GROUPS_PER_FABRIC: usize = 5;
} else if #[cfg(feature = "max-groups-per-fabric-4")] {
pub const MAX_GROUPS_PER_FABRIC: usize = 4;
} else {
pub const MAX_GROUPS_PER_FABRIC: usize = 0;
}
}
cfg_if! {
if #[cfg(feature = "max-group-endpoints-per-fabric-5")] {
pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 5;
} else if #[cfg(feature = "max-group-endpoints-per-fabric-4")] {
pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 4;
} else if #[cfg(feature = "max-group-endpoints-per-fabric-3")] {
pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 3;
} else if #[cfg(feature = "max-group-endpoints-per-fabric-2")] {
pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 2;
} else if #[cfg(feature = "max-group-endpoints-per-fabric-1")] {
pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 1;
} else {
pub const GROUP_ENDPOINTS_PER_FABRIC: usize = 0;
}
}
#[derive(Debug, FromTLV, ToTLV)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct GroupEndpointMapping {
pub group_id: u16,
pub endpoints: Vec<u16, GROUP_ENDPOINTS_PER_FABRIC>,
pub group_name: String<MAX_GROUP_NAME_LEN>,
}
#[derive(Debug, Clone, Default, FromTLV, ToTLV)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct GroupKeyMapping {
pub group_id: u16,
pub group_key_set_id: u16,
}
#[derive(Debug, FromTLV, ToTLV)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Groups {
key_sets: Vec<GroupKeySet, MAX_GROUP_KEYS_PER_FABRIC>,
key_map: Vec<GroupKeyMapping, MAX_GROUPS_PER_FABRIC>,
endpoint_mapping: Vec<GroupEndpointMapping, MAX_GROUPS_PER_FABRIC>,
}
impl Groups {
fn init() -> impl Init<Self> {
init!(Self {
key_sets <- Vec::init(),
key_map <- Vec::init(),
endpoint_mapping <- Vec::init(),
})
}
pub fn key_set_iter(&self) -> impl Iterator<Item = &GroupKeySet> {
self.key_sets.iter()
}
pub fn key_set_get(&self, id: u16) -> Option<&GroupKeySet> {
self.key_sets.iter().find(|e| e.group_key_set_id == id)
}
pub fn key_set_add(&mut self, entry: GroupKeySet) -> Result<(), Error> {
if let Some(existing) = self
.key_sets
.iter_mut()
.find(|e| e.group_key_set_id == entry.group_key_set_id)
{
*existing = entry;
} else {
self.key_sets
.push(entry)
.map_err(|_| ErrorCode::ResourceExhausted)?;
}
Ok(())
}
pub fn key_set_remove(&mut self, id: u16) -> Result<(), Error> {
let before = self.key_sets.len();
self.key_sets.retain(|e| e.group_key_set_id != id);
let removed = self.key_sets.len() < before;
self.key_map_remove_by_key_set(id);
if removed {
Ok(())
} else {
Err(Error::new(ErrorCode::NotFound))
}
}
pub fn key_map_add(&mut self, entry: GroupKeyMapping) -> Result<(), Error> {
self.key_map.push(entry).map_err(|_| ErrorCode::Failure)?;
Ok(())
}
pub fn key_map_iter(&self) -> impl Iterator<Item = &GroupKeyMapping> {
self.key_map.iter()
}
pub fn key_map_replace(
&mut self,
entries: impl Iterator<Item = GroupKeyMapping>,
) -> Result<(), Error> {
self.key_map.clear();
for entry in entries {
self.key_map
.push(entry)
.map_err(|_| ErrorCode::ResourceExhausted)?;
}
Ok(())
}
pub fn key_map_remove_by_key_set(&mut self, key_set_id: u16) {
self.key_map.retain(|e| e.group_key_set_id != key_set_id);
}
pub fn iter(&self) -> impl Iterator<Item = &GroupEndpointMapping> {
self.endpoint_mapping.iter()
}
pub fn get(&self, group_id: u16) -> Option<&GroupEndpointMapping> {
self.endpoint_mapping
.iter()
.find(|e| e.group_id == group_id)
}
pub fn add(
&mut self,
endpoint_id: u16,
group_id: u16,
group_name: &str,
) -> Result<bool, Error> {
let entry = if let Some(entry) = self
.endpoint_mapping
.iter_mut()
.find(|e| e.group_id == group_id)
{
entry
} else {
self.endpoint_mapping
.push(GroupEndpointMapping {
group_id,
endpoints: Vec::new(),
group_name: unwrap!(String::from_str(group_name)),
})
.map_err(|_| ErrorCode::ResourceExhausted)?;
unwrap!(self.endpoint_mapping.last_mut())
};
entry.group_name.clear();
unwrap!(entry.group_name.push_str(group_name));
if entry.endpoints.contains(&endpoint_id) {
return Ok(true);
}
entry
.endpoints
.push(endpoint_id)
.map_err(|_| ErrorCode::ResourceExhausted)?;
Ok(false)
}
pub fn remove(&mut self, endpoint_id: u16, group_id: Option<u16>) -> bool {
let mut removed = false;
for entry in self.endpoint_mapping.iter_mut() {
if group_id.is_some_and(|id| id != entry.group_id) {
continue;
}
let before = entry.endpoints.len();
entry.endpoints.retain(|&ep| ep != endpoint_id);
if entry.endpoints.len() < before {
removed = true;
}
}
self.endpoint_mapping.retain(|e| !e.endpoints.is_empty());
removed
}
}
#[derive(Debug, ToTLV, FromTLV)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Fabric {
fab_idx: NonZeroU8,
node_id: u64,
fabric_id: u64,
vendor_id: u16,
compressed_fabric_id: u64,
secret_key: CanonPkcSecretKey,
root_ca: Vec<u8, { MAX_CERT_TLV_LEN }>,
icac_or_vvsc: Vec<u8, { MAX_CERT_TLV_LEN }>,
vvsc_set: bool,
noc: Vec<u8, { MAX_CERT_TLV_LEN }>,
ipk: KeySet,
label: String<32>,
acl: Vec<AclEntry, { acl::MAX_ACL_ENTRIES_PER_FABRIC }>,
groups: Groups,
vid_verification_statement: Vec<u8, VID_VERIFICATION_STATEMENT_LEN>,
}
pub const VID_VERIFICATION_STATEMENT_LEN: usize = 85;
impl Fabric {
fn init(fab_idx: NonZeroU8) -> impl Init<Self> {
init!(Self {
fab_idx,
node_id: 0,
fabric_id: 0,
vendor_id: 0,
compressed_fabric_id: 0,
secret_key <- CanonPkcSecretKey::init(),
root_ca <- Vec::init(),
icac_or_vvsc <- Vec::init(),
vvsc_set: false,
noc <- Vec::init(),
ipk <- KeySet::init(),
label: String::new(),
acl <- Vec::init(),
groups <- Groups::init(),
vid_verification_statement <- Vec::init(),
})
}
#[allow(clippy::too_many_arguments)]
fn update<C: Crypto>(
&mut self,
crypto: C,
root_ca: Option<&[u8]>,
noc: &[u8],
icac: &[u8],
secret_key: CanonPkcSecretKeyRef<'_>,
epoch_key: Option<CanonAeadKeyRef<'_>>,
vendor_id: Option<u16>,
case_admin_subject: Option<u64>,
) -> Result<(), Error> {
if let Some(root_ca) = root_ca {
self.root_ca.clear();
self.root_ca
.extend_from_slice(root_ca)
.map_err(|_| ErrorCode::BufferTooSmall)?;
}
self.icac_or_vvsc.clear();
self.icac_or_vvsc
.extend_from_slice(icac)
.map_err(|_| ErrorCode::BufferTooSmall)?;
self.vvsc_set = false;
self.noc.clear();
self.noc
.extend_from_slice(noc)
.map_err(|_| ErrorCode::BufferTooSmall)?;
let root_cert = CertRef::new(TLVElement::new(self.root_ca.as_slice()));
let noc_cert = CertRef::new(TLVElement::new(noc));
self.node_id = noc_cert.get_node_id()?;
self.fabric_id = noc_cert.get_fabric_id()?;
self.compressed_fabric_id = Self::compute_compressed_fabric_id(
&crypto,
root_cert.pubkey()?.try_into()?,
self.fabric_id,
);
if let Some(epoch_key) = epoch_key {
self.ipk
.update(&crypto, epoch_key, &self.compressed_fabric_id)?;
}
if let Some(vendor_id) = vendor_id {
self.vendor_id = vendor_id;
}
if let Some(case_admin_subject) = case_admin_subject {
self.acl.clear();
self.acl.push_init(
AclEntry::init(None, Privilege::ADMIN, AuthMode::Case)
.into_fallible()
.chain(|e| {
e.fab_idx = Some(self.fab_idx);
e.add_subject(case_admin_subject)
}),
|| ErrorCode::ResourceExhausted.into(),
)?;
}
self.secret_key.load(secret_key);
Ok(())
}
pub fn mdns_service(&self) -> Option<MatterLocalService> {
self.mdns_service_for(self.node_id)
}
pub fn mdns_service_for(&self, node_id: u64) -> Option<MatterLocalService> {
(!self.noc.is_empty()).then_some(MatterLocalService::Commissioned {
compressed_fabric_id: self.compressed_fabric_id,
node_id,
})
}
pub fn is_dest_id<C: Crypto>(
&self,
crypto: C,
random: &[u8],
target: &[u8],
) -> Result<(), Error> {
let mut mac = crypto.hmac(self.ipk.op_key())?;
mac.update(random)?;
mac.update(CertRef::new(TLVElement::new(self.root_ca())).pubkey()?)?;
mac.update(&self.fabric_id.to_le_bytes())?;
mac.update(&self.node_id.to_le_bytes())?;
let mut id = MaybeUninit::<Hash>::uninit(); let id = id.init_with(Hash::init());
mac.finish(id)?;
if id.access() == target {
Ok(())
} else {
Err(ErrorCode::NotFound.into())
}
}
pub fn compute_dest_id<C: Crypto>(
&self,
crypto: C,
random: &[u8],
target_node_id: u64,
out: &mut Hash,
) -> Result<(), Error> {
let mut mac = crypto.hmac(self.ipk.op_key())?;
mac.update(random)?;
mac.update(CertRef::new(TLVElement::new(self.root_ca())).pubkey()?)?;
mac.update(&self.fabric_id.to_le_bytes())?;
mac.update(&target_node_id.to_le_bytes())?;
mac.finish(out)?;
Ok(())
}
pub fn secret_key(&self) -> CanonPkcSecretKeyRef<'_> {
self.secret_key.reference()
}
pub fn node_id(&self) -> u64 {
self.node_id
}
pub fn fabric_id(&self) -> u64 {
self.fabric_id
}
pub fn fab_idx(&self) -> NonZeroU8 {
self.fab_idx
}
pub fn compressed_fabric_id(&self) -> u64 {
self.compressed_fabric_id
}
pub fn vendor_id(&self) -> u16 {
self.vendor_id
}
pub fn label(&self) -> &str {
&self.label
}
pub fn root_ca(&self) -> &[u8] {
&self.root_ca
}
pub fn icac(&self) -> &[u8] {
if self.vvsc_set {
&[]
} else {
&self.icac_or_vvsc
}
}
pub fn noc(&self) -> &[u8] {
&self.noc
}
pub fn ipk(&self) -> &KeySet {
&self.ipk
}
pub fn groups(&self) -> &Groups {
&self.groups
}
pub fn groups_mut(&mut self) -> &mut Groups {
&mut self.groups
}
pub fn vvsc(&self) -> &[u8] {
if self.vvsc_set {
&self.icac_or_vvsc
} else {
&[]
}
}
pub fn vid_verification_statement(&self) -> &[u8] {
&self.vid_verification_statement
}
pub fn set_vid_verification(
&mut self,
vendor_id: Option<u16>,
vid_verification_statement: Option<&[u8]>,
vvsc: Option<&[u8]>,
) -> Result<(), Error> {
if let Some(vid) = vendor_id {
self.vendor_id = vid;
}
if let Some(vvs) = vid_verification_statement {
self.vid_verification_statement.clear();
self.vid_verification_statement
.extend_from_slice(vvs)
.map_err(|_| ErrorCode::BufferTooSmall)?;
}
if let Some(v) = vvsc {
if !v.is_empty() {
self.icac_or_vvsc.clear();
self.icac_or_vvsc
.extend_from_slice(v)
.map_err(|_| ErrorCode::BufferTooSmall)?;
self.vvsc_set = true;
} else if self.vvsc_set {
self.icac_or_vvsc.clear();
self.vvsc_set = false;
}
}
Ok(())
}
pub fn acl_iter(&self) -> impl Iterator<Item = &AclEntry> {
self.acl.iter()
}
pub fn acl_add(&mut self, mut entry: AclEntry) -> Result<usize, Error> {
if entry.auth_mode() == AuthMode::Pase {
Err(ErrorCode::ConstraintError)?;
}
entry.fab_idx = Some(self.fab_idx);
self.acl
.push(entry)
.map_err(|_| ErrorCode::ResourceExhausted)?;
Ok(self.acl.len() - 1)
}
pub fn acl_add_init<I>(&mut self, init: I) -> Result<usize, Error>
where
I: Init<AclEntry, Error>,
{
self.acl
.push_init(init, || ErrorCode::ResourceExhausted.into())?;
let idx = self.acl.len() - 1;
let entry = &mut self.acl[idx];
entry.fab_idx = Some(self.fab_idx);
Ok(idx)
}
pub fn acl_update(&mut self, idx: usize, mut entry: AclEntry) -> Result<(), Error> {
if self.acl.len() <= idx {
return Err(ErrorCode::NotFound.into());
}
entry.fab_idx = Some(self.fab_idx);
self.acl[idx] = entry;
Ok(())
}
pub fn acl_update_init<I>(&mut self, idx: usize, init: I) -> Result<(), Error>
where
I: Init<AclEntry, Error>,
{
if self.acl.len() <= idx {
return Err(ErrorCode::NotFound.into());
}
let mut entry = MaybeUninit::uninit();
let entry = entry.try_init_with(init)?.clone();
self.acl[idx] = entry;
self.acl[idx].fab_idx = Some(self.fab_idx);
Ok(())
}
pub fn acl_remove(&mut self, idx: usize) -> Result<(), Error> {
if self.acl.len() <= idx {
return Err(ErrorCode::NotFound.into());
}
self.acl.remove(idx);
Ok(())
}
pub fn acl_remove_all(&mut self) {
self.acl.clear();
}
fn allow(&self, req: &AccessReq) -> bool {
for e in &self.acl {
if e.allow(req) {
return true;
}
}
debug!(
"ACL Disallow for subjects {} fab idx {}",
req.accessor().subjects(),
req.accessor().fab_idx
);
false
}
pub(crate) fn compute_compressed_fabric_id<C: Crypto>(
crypto: C,
root_pubkey: CanonPkcPublicKeyRef<'_>,
fabric_id: u64,
) -> u64 {
const COMPRESSED_FABRIC_ID_INFO: &[u8; 16] = &[
0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x46, 0x61, 0x62, 0x72,
0x69, 0x63,
];
let mut compressed_fabric_id = CryptoSensitive::<{ COMPRESSED_FABRIC_ID_LEN }>::new();
unwrap!(unwrap!(crypto.kdf()).expand(
&fabric_id.to_be_bytes(),
root_pubkey.split::<1, { PKC_CANON_PUBLIC_KEY_LEN - 1 }>().1,
COMPRESSED_FABRIC_ID_INFO,
&mut compressed_fabric_id,
));
u64::from_be_bytes(*compressed_fabric_id.access())
}
}
cfg_if! {
if #[cfg(feature = "max-fabrics-32")] {
pub const MAX_FABRICS: usize = 32;
} else if #[cfg(feature = "max-fabrics-16")] {
pub const MAX_FABRICS: usize = 16;
} else if #[cfg(feature = "max-fabrics-8")] {
pub const MAX_FABRICS: usize = 8;
} else if #[cfg(feature = "max-fabrics-7")] {
pub const MAX_FABRICS: usize = 7;
} else if #[cfg(feature = "max-fabrics-6")] {
pub const MAX_FABRICS: usize = 6;
} else { pub const MAX_FABRICS: usize = 5;
}
}
pub struct Fabrics {
fabrics: Vec<Fabric, MAX_FABRICS>,
}
impl Default for Fabrics {
fn default() -> Self {
Self::new()
}
}
impl Fabrics {
#[inline(always)]
pub const fn new() -> Self {
Self {
fabrics: Vec::new(),
}
}
pub fn init() -> impl Init<Self> {
init!(Self {
fabrics <- Vec::init(),
})
}
pub fn reset(&mut self) {
self.fabrics.clear();
}
pub fn reset_persist<S: KvBlobStore>(
&mut self,
mut store: S,
buf: &mut [u8],
) -> Result<(), Error> {
self.reset();
for idx in 1..=255u8 {
store.remove(FABRIC_KEYS_START + idx as u16, buf)?;
}
info!("Removed all fabrics from storage");
Ok(())
}
pub fn load_persist<S: KvBlobStore>(
&mut self,
mut store: S,
buf: &mut [u8],
) -> Result<(), Error> {
self.reset();
for fab_idx in 1..=255u8 {
self.add_load(fab_idx, &mut store, buf)?;
}
Ok(())
}
pub(crate) fn add_load<S: KvBlobStore>(
&mut self,
fab_idx: u8,
mut store: S,
buf: &mut [u8],
) -> Result<(), Error> {
if let Some(data) = store.load(FABRIC_KEYS_START + fab_idx as u16, buf)? {
self.fabrics
.push_init(Fabric::init_from_tlv(TLVElement::new(data)), || {
ErrorCode::ResourceExhausted.into()
})?;
let fabric = unwrap!(self.fabrics.last());
info!(
"Loaded fabric {} with ID {:x} from storage",
fabric.fab_idx(),
fabric.compressed_fabric_id()
);
}
Ok(())
}
pub fn add_with_post_init<F>(&mut self, post_init: F) -> Result<&mut Fabric, Error>
where
F: FnOnce(&mut Fabric) -> Result<(), Error>,
{
let max_fab_idx = self
.iter()
.map(|fabric| fabric.fab_idx().get())
.max()
.unwrap_or(0);
let fab_idx = unwrap!(NonZeroU8::new(if max_fab_idx < u8::MAX - 1 {
max_fab_idx + 1
} else {
let Some(fab_idx) = (1..u8::MAX)
.find(|fab_idx| self.iter().all(|fabric| fabric.fab_idx().get() != *fab_idx))
else {
return Err(ErrorCode::ResourceExhausted.into());
};
fab_idx
}));
self.fabrics.push_init(
Fabric::init(fab_idx)
.into_fallible::<Error>()
.chain(post_init),
|| ErrorCode::ResourceExhausted.into(),
)?;
let fabric = unwrap!(self.fabrics.last_mut());
Ok(fabric)
}
#[allow(clippy::too_many_arguments)]
pub fn add<C: Crypto>(
&mut self,
crypto: C,
secret_key: CanonPkcSecretKeyRef<'_>,
root_ca: &[u8],
noc: &[u8],
icac: &[u8],
epoch_key: Option<CanonAeadKeyRef<'_>>,
vendor_id: u16,
case_admin_subject: u64,
) -> Result<&mut Fabric, Error> {
self.add_with_post_init(|fabric| {
fabric.update(
crypto,
Some(root_ca),
noc,
icac,
secret_key,
epoch_key,
Some(vendor_id),
Some(case_admin_subject),
)
})
}
pub fn update<C: Crypto>(
&mut self,
crypto: C,
fab_idx: NonZeroU8,
secret_key: CanonPkcSecretKeyRef<'_>,
noc: &[u8],
icac: &[u8],
) -> Result<&mut Fabric, Error> {
let fabric = self.fabric_mut(fab_idx)?;
fabric.update(crypto, None, noc, icac, secret_key, None, None, None)?;
Ok(fabric)
}
pub fn update_label(&mut self, fab_idx: NonZeroU8, label: &str) -> Result<&mut Fabric, Error> {
if self.iter().any(|fabric| {
fabric.fab_idx != fab_idx && !fabric.label.is_empty() && fabric.label == label
}) {
return Err(ErrorCode::Invalid.into());
}
let fabric = self.fabric_mut(fab_idx)?;
fabric.label.clear();
fabric
.label
.push_str(label)
.map_err(|_| ErrorCode::ConstraintError)?;
Ok(fabric)
}
pub fn remove(&mut self, fab_idx: NonZeroU8) -> Result<(), Error> {
let _ = self.fabric(fab_idx)?;
self.fabrics.retain(|fabric| fabric.fab_idx != fab_idx);
Ok(())
}
pub fn get_by_dest_id<C: Crypto>(
&self,
crypto: C,
random: &[u8],
target: &[u8],
) -> Option<&Fabric> {
self.iter()
.find(|fabric| fabric.is_dest_id(&crypto, random, target).is_ok())
}
pub fn get(&self, fab_idx: NonZeroU8) -> Option<&Fabric> {
self.iter().find(|fabric| fabric.fab_idx == fab_idx)
}
pub fn get_mut(&mut self, fab_idx: NonZeroU8) -> Option<&mut Fabric> {
self.fabrics
.iter_mut()
.find(|fabric| fabric.fab_idx == fab_idx)
}
pub fn iter(&self) -> impl Iterator<Item = &Fabric> {
self.fabrics.iter()
}
pub fn fabric(&self, fab_idx: NonZeroU8) -> Result<&Fabric, Error> {
self.get(fab_idx).ok_or(ErrorCode::NotFound.into())
}
pub fn fabric_mut(&mut self, fab_idx: NonZeroU8) -> Result<&mut Fabric, Error> {
self.get_mut(fab_idx).ok_or(ErrorCode::NotFound.into())
}
pub fn allow(&self, req: &AccessReq) -> bool {
if req.accessor().auth_mode() == Some(AuthMode::Pase) {
return true;
}
let Ok(fab_idx) = req.accessor().fab_idx() else {
return false;
};
let Some(fabric) = self.get(fab_idx) else {
return false;
};
fabric.allow(req)
}
}
pub struct FabricPersist<S>(Persist<S>);
impl<S> FabricPersist<S>
where
S: KvBlobStoreAccess,
{
pub const fn new(kvb: S) -> Self {
Self(Persist::new(kvb))
}
pub fn persist_mut(&mut self) -> &mut Persist<S> {
&mut self.0
}
pub fn store(&mut self, fabric: &Fabric) -> Result<(), Error> {
self.0
.store_tlv(FABRIC_KEYS_START + fabric.fab_idx().get() as u16, fabric)
}
pub fn remove(&mut self, fab_idx: NonZeroU8) -> Result<(), Error> {
self.0.remove(FABRIC_KEYS_START + fab_idx.get() as u16)
}
pub fn run(self) -> Result<(), Error> {
self.0.run()
}
}
#[cfg(test)]
mod tests {
use core::mem::MaybeUninit;
use crate::cert::gen::{CertGenerator, CertType, IssuerDN, SubjectDN, Validity};
use crate::cert::MAX_CERT_TLV_AND_ASN1_LEN;
use crate::crypto::test_only_crypto;
use crate::crypto::{
CanonAeadKeyRef, CanonPkcSecretKey, Crypto, Hash, PublicKey, SecretKey, SigningSecretKey,
AEAD_CANON_KEY_LEN,
};
use crate::utils::init::InitMaybeUninit;
use super::Fabrics;
#[test]
fn test_compute_dest_id_matches_is_dest_id() {
let crypto = test_only_crypto();
let fabric_id: u64 = 1;
let rcac_id: u64 = 1;
let node_id: u64 = 100;
let rcac_secret_key = crypto.generate_secret_key().unwrap();
let mut rcac_pubkey_canon = crate::crypto::CanonPkcPublicKey::new();
rcac_secret_key
.pub_key()
.unwrap()
.write_canon(&mut rcac_pubkey_canon)
.unwrap();
let validity = Validity {
not_before: 0,
not_after: 0,
};
let mut rcac_buf = [0u8; MAX_CERT_TLV_AND_ASN1_LEN];
let rcac_len = CertGenerator::new(&mut rcac_buf)
.generate(
&crypto,
CertType::Rcac,
&[0x01],
validity,
SubjectDN {
node_id: None,
fabric_id: Some(fabric_id),
cat_ids: &[],
ca_id: Some(rcac_id),
},
IssuerDN {
ca_id: None,
fabric_id: None,
is_rcac: false,
},
rcac_pubkey_canon.reference(),
None,
&rcac_secret_key,
)
.unwrap();
let noc_secret_key = crypto.generate_secret_key().unwrap();
let mut noc_pubkey_canon = crate::crypto::CanonPkcPublicKey::new();
noc_secret_key
.pub_key()
.unwrap()
.write_canon(&mut noc_pubkey_canon)
.unwrap();
let mut noc_secret_key_canon = CanonPkcSecretKey::new();
noc_secret_key
.write_canon(&mut noc_secret_key_canon)
.unwrap();
let mut noc_buf = [0u8; MAX_CERT_TLV_AND_ASN1_LEN];
let noc_len = CertGenerator::new(&mut noc_buf)
.generate(
&crypto,
CertType::Noc,
&[0x02],
validity,
SubjectDN {
node_id: Some(node_id),
fabric_id: Some(fabric_id),
cat_ids: &[],
ca_id: None,
},
IssuerDN {
ca_id: Some(rcac_id),
fabric_id: Some(fabric_id),
is_rcac: true,
},
noc_pubkey_canon.reference(),
Some(rcac_pubkey_canon.reference()),
&rcac_secret_key,
)
.unwrap();
let epoch_key = [0x5a_u8; AEAD_CANON_KEY_LEN];
let mut fabrics = Fabrics::new();
fabrics
.add(
&crypto,
noc_secret_key_canon.reference(),
&rcac_buf[..rcac_len],
&noc_buf[..noc_len],
&[], Some(CanonAeadKeyRef::new(&epoch_key)),
0x8000,
node_id,
)
.expect("Fabrics::add should succeed");
let fab_idx = core::num::NonZeroU8::new(1).unwrap();
let fabric = fabrics
.get(fab_idx)
.expect("fabric at index 1 should exist");
let random = [0xABu8; 32];
let mut dest_id = MaybeUninit::<Hash>::uninit();
let dest_id = dest_id.init_with(Hash::init());
fabric
.compute_dest_id(&crypto, &random, fabric.node_id(), dest_id)
.expect("compute_dest_id should not fail");
fabric
.is_dest_id(&crypto, &random, dest_id.access())
.expect("is_dest_id should accept hash produced by compute_dest_id");
}
}