Merge pull request #9 from project-chip/feature/multi-admin
Multi-Admin Support
This commit is contained in:
commit
d2926a4177
28 changed files with 677 additions and 214 deletions
|
@ -19,17 +19,15 @@ mod dev_att;
|
|||
use matter::core::{self, CommissioningData};
|
||||
use matter::data_model::cluster_basic_information::BasicInfoConfig;
|
||||
use matter::data_model::device_types::device_type_add_on_off_light;
|
||||
use rand::prelude::*;
|
||||
use matter::secure_channel::spake2p::VerifierData;
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
let mut comm_data = CommissioningData {
|
||||
let comm_data = CommissioningData {
|
||||
// TODO: Hard-coded for now
|
||||
passwd: 123456,
|
||||
verifier: VerifierData::new_with_pw(123456),
|
||||
discriminator: 250,
|
||||
..Default::default()
|
||||
};
|
||||
rand::thread_rng().fill_bytes(&mut comm_data.salt);
|
||||
|
||||
// vid/pid should match those in the DAC
|
||||
let dev_info = BasicInfoConfig {
|
||||
|
@ -37,10 +35,11 @@ fn main() {
|
|||
pid: 0x8002,
|
||||
hw_ver: 2,
|
||||
sw_ver: 1,
|
||||
serial_no: "aabbccdd".to_string(),
|
||||
};
|
||||
let dev_att = Box::new(dev_att::HardCodedDevAtt::new());
|
||||
|
||||
let mut matter = core::Matter::new(&dev_info, dev_att, &comm_data).unwrap();
|
||||
let mut matter = core::Matter::new(dev_info, dev_att, comm_data).unwrap();
|
||||
let dm = matter.get_data_model();
|
||||
{
|
||||
let mut node = dm.node.write().unwrap();
|
||||
|
|
|
@ -25,19 +25,15 @@ use crate::{
|
|||
fabric::FabricMgr,
|
||||
interaction_model::InteractionModel,
|
||||
mdns::Mdns,
|
||||
secure_channel::core::SecureChannel,
|
||||
secure_channel::{core::SecureChannel, pake::PaseMgr, spake2p::VerifierData},
|
||||
transport,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Default)]
|
||||
/// Device Commissioning Data
|
||||
pub struct CommissioningData {
|
||||
/// The commissioning salt
|
||||
pub salt: [u8; 16],
|
||||
/// The password for commissioning the device
|
||||
// TODO: We should replace this with verifier instead of password
|
||||
pub passwd: u32,
|
||||
/// The data like password or verifier that is required to authenticate
|
||||
pub verifier: VerifierData,
|
||||
/// The 12-bit discriminator used to differentiate between multiple devices
|
||||
pub discriminator: u16,
|
||||
}
|
||||
|
@ -57,17 +53,19 @@ impl Matter {
|
|||
/// requires a set of device attestation certificates and keys. It is the responsibility of
|
||||
/// this object to return the device attestation details when queried upon.
|
||||
pub fn new(
|
||||
dev_det: &BasicInfoConfig,
|
||||
dev_det: BasicInfoConfig,
|
||||
dev_att: Box<dyn DevAttDataFetcher>,
|
||||
dev_comm: &CommissioningData,
|
||||
dev_comm: CommissioningData,
|
||||
) -> Result<Box<Matter>, Error> {
|
||||
let mdns = Mdns::get()?;
|
||||
mdns.set_values(dev_det.vid, dev_det.pid, dev_comm.discriminator);
|
||||
mdns.set_values(dev_det.vid, dev_det.pid);
|
||||
|
||||
let fabric_mgr = Arc::new(FabricMgr::new()?);
|
||||
let acl_mgr = Arc::new(AclMgr::new()?);
|
||||
let mut pase = PaseMgr::new();
|
||||
let open_comm_window = fabric_mgr.is_empty();
|
||||
let data_model = DataModel::new(dev_det, dev_att, fabric_mgr.clone(), acl_mgr)?;
|
||||
let data_model =
|
||||
DataModel::new(dev_det, dev_att, fabric_mgr.clone(), acl_mgr, pase.clone())?;
|
||||
let mut matter = Box::new(Matter {
|
||||
transport_mgr: transport::mgr::Mgr::new()?,
|
||||
data_model,
|
||||
|
@ -76,11 +74,12 @@ impl Matter {
|
|||
let interaction_model =
|
||||
Box::new(InteractionModel::new(Box::new(matter.data_model.clone())));
|
||||
matter.transport_mgr.register_protocol(interaction_model)?;
|
||||
let mut secure_channel = Box::new(SecureChannel::new(matter.fabric_mgr.clone()));
|
||||
|
||||
if open_comm_window {
|
||||
secure_channel.open_comm_window(&dev_comm.salt, dev_comm.passwd)?;
|
||||
pase.enable_pase_session(dev_comm.verifier, dev_comm.discriminator)?;
|
||||
}
|
||||
|
||||
let secure_channel = Box::new(SecureChannel::new(pase, matter.fabric_mgr.clone()));
|
||||
matter.transport_mgr.register_protocol(secure_channel)?;
|
||||
Ok(matter)
|
||||
}
|
||||
|
|
|
@ -17,13 +17,18 @@
|
|||
|
||||
use super::objects::*;
|
||||
use crate::error::*;
|
||||
use num_derive::FromPrimitive;
|
||||
|
||||
pub const ID: u32 = 0x0028;
|
||||
|
||||
#[derive(FromPrimitive)]
|
||||
enum Attributes {
|
||||
DMRevision = 0,
|
||||
VendorId = 2,
|
||||
ProductId = 4,
|
||||
HwVer = 7,
|
||||
SwVer = 9,
|
||||
SerialNo = 0x0f,
|
||||
}
|
||||
|
||||
pub struct BasicInfoConfig {
|
||||
|
@ -31,6 +36,16 @@ pub struct BasicInfoConfig {
|
|||
pub pid: u16,
|
||||
pub hw_ver: u16,
|
||||
pub sw_ver: u32,
|
||||
pub serial_no: String,
|
||||
}
|
||||
|
||||
fn attr_dm_rev_new() -> Result<Attribute, Error> {
|
||||
Attribute::new(
|
||||
Attributes::DMRevision as u16,
|
||||
AttrValue::Uint8(1),
|
||||
Access::RV,
|
||||
Quality::FIXED,
|
||||
)
|
||||
}
|
||||
|
||||
fn attr_vid_new(vid: u16) -> Result<Attribute, Error> {
|
||||
|
@ -69,19 +84,31 @@ fn attr_sw_ver_new(sw_ver: u32) -> Result<Attribute, Error> {
|
|||
)
|
||||
}
|
||||
|
||||
fn attr_serial_no_new(label: String) -> Result<Attribute, Error> {
|
||||
Attribute::new(
|
||||
Attributes::SerialNo as u16,
|
||||
AttrValue::Utf8(label),
|
||||
Access::RV,
|
||||
Quality::FIXED,
|
||||
)
|
||||
}
|
||||
pub struct BasicInfoCluster {
|
||||
base: Cluster,
|
||||
}
|
||||
|
||||
impl BasicInfoCluster {
|
||||
pub fn new(cfg: &BasicInfoConfig) -> Result<Box<Self>, Error> {
|
||||
pub fn new(cfg: BasicInfoConfig) -> Result<Box<Self>, Error> {
|
||||
let mut cluster = Box::new(BasicInfoCluster {
|
||||
base: Cluster::new(ID)?,
|
||||
});
|
||||
cluster.base.add_attribute(attr_dm_rev_new()?)?;
|
||||
cluster.base.add_attribute(attr_vid_new(cfg.vid)?)?;
|
||||
cluster.base.add_attribute(attr_pid_new(cfg.pid)?)?;
|
||||
cluster.base.add_attribute(attr_hw_ver_new(cfg.hw_ver)?)?;
|
||||
cluster.base.add_attribute(attr_sw_ver_new(cfg.sw_ver)?)?;
|
||||
cluster
|
||||
.base
|
||||
.add_attribute(attr_serial_no_new(cfg.serial_no)?)?;
|
||||
Ok(cluster)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ use crate::{
|
|||
},
|
||||
InteractionConsumer, Transaction,
|
||||
},
|
||||
secure_channel::pake::PaseMgr,
|
||||
tlv::{TLVArray, TLVWriter, TagType, ToTLV},
|
||||
transport::session::{Session, SessionMode},
|
||||
};
|
||||
|
@ -50,10 +51,11 @@ pub struct DataModel {
|
|||
|
||||
impl DataModel {
|
||||
pub fn new(
|
||||
dev_details: &BasicInfoConfig,
|
||||
dev_details: BasicInfoConfig,
|
||||
dev_att: Box<dyn DevAttDataFetcher>,
|
||||
fabric_mgr: Arc<FabricMgr>,
|
||||
acl_mgr: Arc<AclMgr>,
|
||||
pase_mgr: PaseMgr,
|
||||
) -> Result<Self, Error> {
|
||||
let dm = DataModel {
|
||||
node: Arc::new(RwLock::new(Node::new()?)),
|
||||
|
@ -62,7 +64,14 @@ impl DataModel {
|
|||
{
|
||||
let mut node = dm.node.write()?;
|
||||
node.set_changes_cb(Box::new(dm.clone()));
|
||||
device_type_add_root_node(&mut node, dev_details, dev_att, fabric_mgr, acl_mgr)?;
|
||||
device_type_add_root_node(
|
||||
&mut node,
|
||||
dev_details,
|
||||
dev_att,
|
||||
fabric_mgr,
|
||||
acl_mgr,
|
||||
pase_mgr,
|
||||
)?;
|
||||
}
|
||||
Ok(dm)
|
||||
}
|
||||
|
@ -75,7 +84,7 @@ impl DataModel {
|
|||
) -> Result<AttrValue, IMStatusCode> {
|
||||
let node = self.node.read().unwrap();
|
||||
let cluster = node.get_cluster(endpoint, cluster)?;
|
||||
cluster.base().read_attribute_raw(attr).map(|a| *a)
|
||||
cluster.base().read_attribute_raw(attr).map(|a| a.clone())
|
||||
}
|
||||
|
||||
// Encode a write attribute from a path that may or may not be wildcard
|
||||
|
|
|
@ -19,6 +19,7 @@ use super::cluster_basic_information::BasicInfoCluster;
|
|||
use super::cluster_basic_information::BasicInfoConfig;
|
||||
use super::cluster_on_off::OnOffCluster;
|
||||
use super::objects::*;
|
||||
use super::sdm::admin_commissioning::AdminCommCluster;
|
||||
use super::sdm::dev_att::DevAttDataFetcher;
|
||||
use super::sdm::general_commissioning::GenCommCluster;
|
||||
use super::sdm::noc::NocCluster;
|
||||
|
@ -27,20 +28,27 @@ use super::system_model::access_control::AccessControlCluster;
|
|||
use crate::acl::AclMgr;
|
||||
use crate::error::*;
|
||||
use crate::fabric::FabricMgr;
|
||||
use crate::secure_channel::pake::PaseMgr;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLockWriteGuard;
|
||||
|
||||
pub const DEV_TYPE_ROOT_NODE: DeviceType = DeviceType {
|
||||
dtype: 0x0016,
|
||||
drev: 1,
|
||||
};
|
||||
|
||||
type WriteNode<'a> = RwLockWriteGuard<'a, Box<Node>>;
|
||||
|
||||
pub fn device_type_add_root_node(
|
||||
node: &mut WriteNode,
|
||||
dev_info: &BasicInfoConfig,
|
||||
dev_info: BasicInfoConfig,
|
||||
dev_att: Box<dyn DevAttDataFetcher>,
|
||||
fabric_mgr: Arc<FabricMgr>,
|
||||
acl_mgr: Arc<AclMgr>,
|
||||
pase_mgr: PaseMgr,
|
||||
) -> Result<u32, Error> {
|
||||
// Add the root endpoint
|
||||
let endpoint = node.add_endpoint()?;
|
||||
let endpoint = node.add_endpoint(DEV_TYPE_ROOT_NODE)?;
|
||||
if endpoint != 0 {
|
||||
// Somehow endpoint 0 was already added, this shouldn't be the case
|
||||
return Err(Error::Invalid);
|
||||
|
@ -51,6 +59,7 @@ pub fn device_type_add_root_node(
|
|||
let failsafe = general_commissioning.failsafe();
|
||||
node.add_cluster(0, general_commissioning)?;
|
||||
node.add_cluster(0, NwCommCluster::new()?)?;
|
||||
node.add_cluster(0, AdminCommCluster::new(pase_mgr)?)?;
|
||||
node.add_cluster(
|
||||
0,
|
||||
NocCluster::new(dev_att, fabric_mgr, acl_mgr.clone(), failsafe)?,
|
||||
|
@ -59,8 +68,13 @@ pub fn device_type_add_root_node(
|
|||
Ok(endpoint)
|
||||
}
|
||||
|
||||
const DEV_TYPE_ON_OFF_LIGHT: DeviceType = DeviceType {
|
||||
dtype: 0x0100,
|
||||
drev: 2,
|
||||
};
|
||||
|
||||
pub fn device_type_add_on_off_light(node: &mut WriteNode) -> Result<u32, Error> {
|
||||
let endpoint = node.add_endpoint()?;
|
||||
let endpoint = node.add_endpoint(DEV_TYPE_ON_OFF_LIGHT)?;
|
||||
node.add_cluster(endpoint, OnOffCluster::new()?)?;
|
||||
Ok(endpoint)
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ bitflags! {
|
|||
* - instead of arrays, can use linked-lists to conserve space and avoid the internal fragmentation
|
||||
*/
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub enum AttrValue {
|
||||
Int64(i64),
|
||||
Uint8(u8),
|
||||
|
@ -96,6 +96,7 @@ pub enum AttrValue {
|
|||
Uint32(u32),
|
||||
Uint64(u64),
|
||||
Bool(bool),
|
||||
Utf8(String),
|
||||
Custom,
|
||||
}
|
||||
|
||||
|
@ -108,6 +109,7 @@ impl Debug for AttrValue {
|
|||
AttrValue::Uint32(v) => write!(f, "{:?}", *v),
|
||||
AttrValue::Uint64(v) => write!(f, "{:?}", *v),
|
||||
AttrValue::Bool(v) => write!(f, "{:?}", *v),
|
||||
AttrValue::Utf8(v) => write!(f, "{:?}", *v),
|
||||
AttrValue::Custom => write!(f, "custom-attribute"),
|
||||
}?;
|
||||
Ok(())
|
||||
|
@ -123,6 +125,7 @@ impl ToTLV for AttrValue {
|
|||
AttrValue::Uint16(v) => tw.u16(tag_type, *v),
|
||||
AttrValue::Uint32(v) => tw.u32(tag_type, *v),
|
||||
AttrValue::Uint64(v) => tw.u64(tag_type, *v),
|
||||
AttrValue::Utf8(v) => tw.utf8(tag_type, v.as_bytes()),
|
||||
_ => {
|
||||
error!("Attribute type not yet supported");
|
||||
Err(Error::AttributeNotFound)
|
||||
|
|
|
@ -87,7 +87,6 @@ pub trait ClusterType {
|
|||
pub struct Cluster {
|
||||
pub(super) id: u32,
|
||||
attributes: Vec<Attribute>,
|
||||
feature_map: Option<u32>,
|
||||
data_ver: u32,
|
||||
}
|
||||
|
||||
|
@ -96,7 +95,6 @@ impl Cluster {
|
|||
let mut c = Cluster {
|
||||
id,
|
||||
attributes: Vec::with_capacity(ATTRS_PER_CLUSTER),
|
||||
feature_map: None,
|
||||
data_ver: rand::thread_rng().gen_range(0..0xFFFFFFFF),
|
||||
};
|
||||
c.add_default_attributes()?;
|
||||
|
@ -112,22 +110,20 @@ impl Cluster {
|
|||
}
|
||||
|
||||
pub fn set_feature_map(&mut self, map: u32) -> Result<(), Error> {
|
||||
if self.feature_map.is_none() {
|
||||
self.add_attribute(Attribute::new(
|
||||
GlobalElements::FeatureMap as u16,
|
||||
AttrValue::Uint32(map),
|
||||
Access::RV,
|
||||
Quality::NONE,
|
||||
)?)?;
|
||||
} else {
|
||||
self.write_attribute_raw(GlobalElements::FeatureMap as u16, AttrValue::Uint32(map))
|
||||
.map_err(|_| Error::Invalid)?;
|
||||
}
|
||||
self.feature_map = Some(map);
|
||||
self.write_attribute_raw(GlobalElements::FeatureMap as u16, AttrValue::Uint32(map))
|
||||
.map_err(|_| Error::Invalid)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_default_attributes(&mut self) -> Result<(), Error> {
|
||||
// Default feature map is 0
|
||||
self.add_attribute(Attribute::new(
|
||||
GlobalElements::FeatureMap as u16,
|
||||
AttrValue::Uint32(0),
|
||||
Access::RV,
|
||||
Quality::NONE,
|
||||
)?)?;
|
||||
|
||||
self.add_attribute(Attribute::new(
|
||||
GlobalElements::AttributeList as u16,
|
||||
AttrValue::Custom,
|
||||
|
@ -233,8 +229,7 @@ impl Cluster {
|
|||
return;
|
||||
}
|
||||
GlobalElements::FeatureMap => {
|
||||
let val = if let Some(m) = self.feature_map { m } else { 0 };
|
||||
encoder.encode(EncodeValue::Value(&val));
|
||||
encoder.encode(EncodeValue::Value(&attr.value));
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
|
@ -284,7 +279,7 @@ impl Cluster {
|
|||
) -> Result<(), IMStatusCode> {
|
||||
let a = self.get_attribute_mut(attr_id)?;
|
||||
if a.value != AttrValue::Custom {
|
||||
let mut value = a.value;
|
||||
let mut value = a.value.clone();
|
||||
value
|
||||
.update_from_tlv(data)
|
||||
.map_err(|_| IMStatusCode::Failure)?;
|
||||
|
|
|
@ -115,3 +115,9 @@ pub trait Encoder {
|
|||
/// Encode a status report
|
||||
fn encode_status(&mut self, status: IMStatusCode, cluster_status: u16);
|
||||
}
|
||||
|
||||
#[derive(ToTLV, Copy, Clone)]
|
||||
pub struct DeviceType {
|
||||
pub dtype: u16,
|
||||
pub drev: u16,
|
||||
}
|
||||
|
|
|
@ -19,17 +19,21 @@ use crate::{data_model::objects::ClusterType, error::*, interaction_model::core:
|
|||
|
||||
use std::fmt;
|
||||
|
||||
pub const CLUSTERS_PER_ENDPT: usize = 7;
|
||||
use super::DeviceType;
|
||||
|
||||
pub const CLUSTERS_PER_ENDPT: usize = 9;
|
||||
|
||||
pub struct Endpoint {
|
||||
dev_type: DeviceType,
|
||||
clusters: Vec<Box<dyn ClusterType>>,
|
||||
}
|
||||
|
||||
pub type BoxedClusters = [Box<dyn ClusterType>];
|
||||
|
||||
impl Endpoint {
|
||||
pub fn new() -> Result<Box<Endpoint>, Error> {
|
||||
pub fn new(dev_type: DeviceType) -> Result<Box<Endpoint>, Error> {
|
||||
Ok(Box::new(Endpoint {
|
||||
dev_type,
|
||||
clusters: Vec::with_capacity(CLUSTERS_PER_ENDPT),
|
||||
}))
|
||||
}
|
||||
|
@ -43,6 +47,10 @@ impl Endpoint {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_dev_type(&self) -> &DeviceType {
|
||||
&self.dev_type
|
||||
}
|
||||
|
||||
fn get_cluster_index(&self, cluster_id: u32) -> Option<usize> {
|
||||
self.clusters.iter().position(|c| c.base().id == cluster_id)
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ use crate::{
|
|||
};
|
||||
use std::fmt;
|
||||
|
||||
use super::DeviceType;
|
||||
|
||||
pub trait ChangeConsumer {
|
||||
fn endpoint_added(&self, id: u16, endpoint: &mut Endpoint) -> Result<(), Error>;
|
||||
}
|
||||
|
@ -59,13 +61,13 @@ impl Node {
|
|||
self.changes_cb = Some(consumer);
|
||||
}
|
||||
|
||||
pub fn add_endpoint(&mut self) -> Result<u32, Error> {
|
||||
pub fn add_endpoint(&mut self, dev_type: DeviceType) -> Result<u32, Error> {
|
||||
let index = self
|
||||
.endpoints
|
||||
.iter()
|
||||
.position(|x| x.is_none())
|
||||
.ok_or(Error::NoSpace)?;
|
||||
let mut endpoint = Endpoint::new()?;
|
||||
let mut endpoint = Endpoint::new(dev_type)?;
|
||||
if let Some(cb) = &self.changes_cb {
|
||||
cb.endpoint_added(index as u16, &mut endpoint)?;
|
||||
}
|
||||
|
|
160
matter/src/data_model/sdm/admin_commissioning.rs
Normal file
160
matter/src/data_model/sdm/admin_commissioning.rs
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
*
|
||||
* Copyright (c) 2023 Project CHIP Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::cmd_enter;
|
||||
use crate::data_model::objects::*;
|
||||
use crate::interaction_model::core::IMStatusCode;
|
||||
use crate::secure_channel::pake::PaseMgr;
|
||||
use crate::secure_channel::spake2p::VerifierData;
|
||||
use crate::tlv::{FromTLV, Nullable, OctetStr, TLVElement};
|
||||
use crate::{error::*, interaction_model::command::CommandReq};
|
||||
use log::{error, info};
|
||||
use num_derive::FromPrimitive;
|
||||
|
||||
pub const ID: u32 = 0x003C;
|
||||
|
||||
#[derive(FromPrimitive, Debug, Copy, Clone, PartialEq)]
|
||||
pub enum WindowStatus {
|
||||
WindowNotOpen = 0,
|
||||
EnhancedWindowOpen = 1,
|
||||
BasicWindowOpen = 2,
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive)]
|
||||
pub enum Attributes {
|
||||
WindowStatus = 0,
|
||||
AdminFabricIndex = 1,
|
||||
AdminVendorId = 2,
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive)]
|
||||
pub enum Commands {
|
||||
OpenCommWindow = 0x00,
|
||||
OpenBasicCommWindow = 0x01,
|
||||
RevokeComm = 0x02,
|
||||
}
|
||||
|
||||
fn attr_window_status_new() -> Result<Attribute, Error> {
|
||||
Attribute::new(
|
||||
Attributes::WindowStatus as u16,
|
||||
AttrValue::Custom,
|
||||
Access::RV,
|
||||
Quality::NONE,
|
||||
)
|
||||
}
|
||||
|
||||
fn attr_admin_fabid_new() -> Result<Attribute, Error> {
|
||||
Attribute::new(
|
||||
Attributes::AdminFabricIndex as u16,
|
||||
AttrValue::Custom,
|
||||
Access::RV,
|
||||
Quality::NULLABLE,
|
||||
)
|
||||
}
|
||||
|
||||
fn attr_admin_vid_new() -> Result<Attribute, Error> {
|
||||
Attribute::new(
|
||||
Attributes::AdminVendorId as u16,
|
||||
AttrValue::Custom,
|
||||
Access::RV,
|
||||
Quality::NULLABLE,
|
||||
)
|
||||
}
|
||||
|
||||
pub struct AdminCommCluster {
|
||||
pase_mgr: PaseMgr,
|
||||
base: Cluster,
|
||||
}
|
||||
|
||||
impl ClusterType for AdminCommCluster {
|
||||
fn base(&self) -> &Cluster {
|
||||
&self.base
|
||||
}
|
||||
fn base_mut(&mut self) -> &mut Cluster {
|
||||
&mut self.base
|
||||
}
|
||||
|
||||
fn read_custom_attribute(&self, encoder: &mut dyn Encoder, attr: &AttrDetails) {
|
||||
match num::FromPrimitive::from_u16(attr.attr_id) {
|
||||
Some(Attributes::WindowStatus) => {
|
||||
let status = 1_u8;
|
||||
encoder.encode(EncodeValue::Value(&status))
|
||||
}
|
||||
Some(Attributes::AdminVendorId) => {
|
||||
let vid = Nullable::NotNull(1_u8);
|
||||
|
||||
encoder.encode(EncodeValue::Value(&vid))
|
||||
}
|
||||
Some(Attributes::AdminFabricIndex) => {
|
||||
let vid = Nullable::NotNull(1_u8);
|
||||
encoder.encode(EncodeValue::Value(&vid))
|
||||
}
|
||||
_ => {
|
||||
error!("Unsupported Attribute: this shouldn't happen");
|
||||
}
|
||||
}
|
||||
}
|
||||
fn handle_command(&mut self, cmd_req: &mut CommandReq) -> Result<(), IMStatusCode> {
|
||||
let cmd = cmd_req
|
||||
.cmd
|
||||
.path
|
||||
.leaf
|
||||
.map(num::FromPrimitive::from_u32)
|
||||
.ok_or(IMStatusCode::UnsupportedCommand)?
|
||||
.ok_or(IMStatusCode::UnsupportedCommand)?;
|
||||
match cmd {
|
||||
Commands::OpenCommWindow => self.handle_command_opencomm_win(cmd_req),
|
||||
_ => Err(IMStatusCode::UnsupportedCommand),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AdminCommCluster {
|
||||
pub fn new(pase_mgr: PaseMgr) -> Result<Box<Self>, Error> {
|
||||
let mut c = Box::new(AdminCommCluster {
|
||||
pase_mgr,
|
||||
base: Cluster::new(ID)?,
|
||||
});
|
||||
c.base.add_attribute(attr_window_status_new()?)?;
|
||||
c.base.add_attribute(attr_admin_fabid_new()?)?;
|
||||
c.base.add_attribute(attr_admin_vid_new()?)?;
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
fn handle_command_opencomm_win(
|
||||
&mut self,
|
||||
cmd_req: &mut CommandReq,
|
||||
) -> Result<(), IMStatusCode> {
|
||||
cmd_enter!("Open Commissioning Window");
|
||||
let req =
|
||||
OpenCommWindowReq::from_tlv(&cmd_req.data).map_err(|_| IMStatusCode::InvalidCommand)?;
|
||||
let verifier = VerifierData::new(req.verifier.0, req.iterations, req.salt.0);
|
||||
self.pase_mgr
|
||||
.enable_pase_session(verifier, req.discriminator)?;
|
||||
Err(IMStatusCode::Sucess)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromTLV)]
|
||||
#[tlvargs(lifetime = "'a")]
|
||||
pub struct OpenCommWindowReq<'a> {
|
||||
_timeout: u16,
|
||||
verifier: OctetStr<'a>,
|
||||
discriminator: u16,
|
||||
iterations: u32,
|
||||
salt: OctetStr<'a>,
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
pub mod admin_commissioning;
|
||||
pub mod dev_att;
|
||||
pub mod failsafe;
|
||||
pub mod general_commissioning;
|
||||
|
|
|
@ -27,7 +27,7 @@ use crate::fabric::{Fabric, FabricMgr};
|
|||
use crate::interaction_model::command::CommandReq;
|
||||
use crate::interaction_model::core::IMStatusCode;
|
||||
use crate::interaction_model::messages::ib;
|
||||
use crate::tlv::{FromTLV, OctetStr, TLVElement, TLVWriter, TagType, ToTLV};
|
||||
use crate::tlv::{FromTLV, OctetStr, TLVElement, TLVWriter, TagType, ToTLV, UtfStr};
|
||||
use crate::transport::session::SessionMode;
|
||||
use crate::utils::writebuf::WriteBuf;
|
||||
use crate::{cmd_enter, error::*};
|
||||
|
@ -75,6 +75,8 @@ pub enum Commands {
|
|||
CSRResp = 0x05,
|
||||
AddNOC = 0x06,
|
||||
NOCResp = 0x08,
|
||||
UpdateFabricLabel = 0x09,
|
||||
RemoveFabric = 0x0a,
|
||||
AddTrustedRootCert = 0x0b,
|
||||
}
|
||||
|
||||
|
@ -183,19 +185,55 @@ impl NocCluster {
|
|||
if self.failsafe.record_add_noc(fab_idx).is_err() {
|
||||
error!("Failed to record NoC in the FailSafe, what to do?");
|
||||
}
|
||||
NocCluster::create_nocresponse(cmd_req.resp, NocStatus::Ok, fab_idx, "".to_owned());
|
||||
cmd_req.trans.complete();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_nocresponse(
|
||||
tw: &mut TLVWriter,
|
||||
status_code: NocStatus,
|
||||
fab_idx: u8,
|
||||
debug_txt: String,
|
||||
) {
|
||||
let cmd_data = NocResp {
|
||||
status_code: NocStatus::Ok as u8,
|
||||
status_code: status_code as u8,
|
||||
fab_idx,
|
||||
debug_txt: "".to_owned(),
|
||||
debug_txt,
|
||||
};
|
||||
let resp = ib::InvResp::cmd_new(
|
||||
let invoke_resp = ib::InvResp::cmd_new(
|
||||
0,
|
||||
ID,
|
||||
Commands::NOCResp as u16,
|
||||
EncodeValue::Value(&cmd_data),
|
||||
);
|
||||
let _ = resp.to_tlv(cmd_req.resp, TagType::Anonymous);
|
||||
let _ = invoke_resp.to_tlv(tw, TagType::Anonymous);
|
||||
}
|
||||
|
||||
fn handle_command_updatefablabel(
|
||||
&mut self,
|
||||
cmd_req: &mut CommandReq,
|
||||
) -> Result<(), IMStatusCode> {
|
||||
cmd_enter!("Update Fabric Label");
|
||||
let req = UpdateFabricLabelReq::from_tlv(&cmd_req.data)
|
||||
.map_err(|_| IMStatusCode::InvalidDataType)?;
|
||||
let label = req
|
||||
.label
|
||||
.to_string()
|
||||
.map_err(|_| IMStatusCode::InvalidDataType)?;
|
||||
|
||||
let (result, fab_idx) =
|
||||
if let SessionMode::Case(fab_idx) = cmd_req.trans.session.get_session_mode() {
|
||||
if self.fabric_mgr.set_label(fab_idx, label).is_err() {
|
||||
(NocStatus::LabelConflict, fab_idx)
|
||||
} else {
|
||||
(NocStatus::Ok, fab_idx)
|
||||
}
|
||||
} else {
|
||||
// Update Fabric Label not allowed
|
||||
(NocStatus::InvalidFabricIndex, 0)
|
||||
};
|
||||
NocCluster::create_nocresponse(cmd_req.resp, result, fab_idx, "".to_string());
|
||||
cmd_req.trans.complete();
|
||||
Ok(())
|
||||
}
|
||||
|
@ -203,18 +241,8 @@ impl NocCluster {
|
|||
fn handle_command_addnoc(&mut self, cmd_req: &mut CommandReq) -> Result<(), IMStatusCode> {
|
||||
cmd_enter!("AddNOC");
|
||||
if let Err(e) = self._handle_command_addnoc(cmd_req) {
|
||||
let cmd_data = NocResp {
|
||||
status_code: e as u8,
|
||||
fab_idx: 0,
|
||||
debug_txt: "".to_owned(),
|
||||
};
|
||||
let invoke_resp = ib::InvResp::cmd_new(
|
||||
0,
|
||||
ID,
|
||||
Commands::NOCResp as u16,
|
||||
EncodeValue::Value(&cmd_data),
|
||||
);
|
||||
let _ = invoke_resp.to_tlv(cmd_req.resp, TagType::Anonymous);
|
||||
//TODO: Fab-idx 0?
|
||||
NocCluster::create_nocresponse(cmd_req.resp, e, 0, "".to_owned());
|
||||
cmd_req.trans.complete();
|
||||
}
|
||||
Ok(())
|
||||
|
@ -401,6 +429,7 @@ impl ClusterType for NocCluster {
|
|||
Commands::AddTrustedRootCert => self.handle_command_addtrustedrootcert(cmd_req),
|
||||
Commands::AttReq => self.handle_command_attrequest(cmd_req),
|
||||
Commands::CertChainReq => self.handle_command_certchainrequest(cmd_req),
|
||||
Commands::UpdateFabricLabel => self.handle_command_updatefablabel(cmd_req),
|
||||
_ => Err(IMStatusCode::UnsupportedCommand),
|
||||
}
|
||||
}
|
||||
|
@ -515,6 +544,12 @@ struct CommonReq<'a> {
|
|||
str: OctetStr<'a>,
|
||||
}
|
||||
|
||||
#[derive(FromTLV)]
|
||||
#[tlvargs(lifetime = "'a")]
|
||||
struct UpdateFabricLabelReq<'a> {
|
||||
label: UtfStr<'a>,
|
||||
}
|
||||
|
||||
#[derive(FromTLV)]
|
||||
struct CertChainReq {
|
||||
cert_type: u8,
|
||||
|
|
|
@ -21,7 +21,7 @@ use crate::data_model::core::DataModel;
|
|||
use crate::data_model::objects::*;
|
||||
use crate::error::*;
|
||||
use crate::interaction_model::messages::GenericPath;
|
||||
use crate::tlv::{TLVWriter, TagType};
|
||||
use crate::tlv::{TLVWriter, TagType, ToTLV};
|
||||
use log::error;
|
||||
|
||||
pub const ID: u32 = 0x001D;
|
||||
|
@ -48,11 +48,28 @@ impl DescriptorCluster {
|
|||
data_model,
|
||||
base: Cluster::new(ID)?,
|
||||
});
|
||||
c.base.add_attribute(attr_devtypelist_new()?)?;
|
||||
c.base.add_attribute(attr_serverlist_new()?)?;
|
||||
c.base.add_attribute(attr_partslist_new()?)?;
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
fn encode_devtype_list(&self, tag: TagType, tw: &mut TLVWriter) {
|
||||
let path = GenericPath {
|
||||
endpoint: Some(self.endpoint_id),
|
||||
cluster: None,
|
||||
leaf: None,
|
||||
};
|
||||
let _ = tw.start_array(tag);
|
||||
let dm = self.data_model.node.read().unwrap();
|
||||
let _ = dm.for_each_endpoint(&path, |_, e| {
|
||||
let dev_type = e.get_dev_type();
|
||||
let _ = dev_type.to_tlv(tw, TagType::Anonymous);
|
||||
Ok(())
|
||||
});
|
||||
let _ = tw.end_container();
|
||||
}
|
||||
|
||||
fn encode_server_list(&self, tag: TagType, tw: &mut TLVWriter) {
|
||||
let path = GenericPath {
|
||||
endpoint: Some(self.endpoint_id),
|
||||
|
@ -69,8 +86,24 @@ impl DescriptorCluster {
|
|||
}
|
||||
|
||||
fn encode_parts_list(&self, tag: TagType, tw: &mut TLVWriter) {
|
||||
// TODO: Support Partslist
|
||||
let path = GenericPath {
|
||||
endpoint: None,
|
||||
cluster: None,
|
||||
leaf: None,
|
||||
};
|
||||
let _ = tw.start_array(tag);
|
||||
if self.endpoint_id == 0 {
|
||||
// TODO: If endpoint is another than 0, need to figure out what to do
|
||||
let dm = self.data_model.node.read().unwrap();
|
||||
let _ = dm.for_each_endpoint(&path, |current_path, _| {
|
||||
if let Some(endpoint_id) = current_path.endpoint {
|
||||
if endpoint_id != 0 {
|
||||
let _ = tw.u16(TagType::Anonymous, endpoint_id);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
let _ = tw.end_container();
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +118,9 @@ impl ClusterType for DescriptorCluster {
|
|||
|
||||
fn read_custom_attribute(&self, encoder: &mut dyn Encoder, attr: &AttrDetails) {
|
||||
match num::FromPrimitive::from_u16(attr.attr_id) {
|
||||
Some(Attributes::DeviceTypeList) => encoder.encode(EncodeValue::Closure(&|tag, tw| {
|
||||
self.encode_devtype_list(tag, tw)
|
||||
})),
|
||||
Some(Attributes::ServerList) => encoder.encode(EncodeValue::Closure(&|tag, tw| {
|
||||
self.encode_server_list(tag, tw)
|
||||
})),
|
||||
|
@ -98,6 +134,14 @@ impl ClusterType for DescriptorCluster {
|
|||
}
|
||||
}
|
||||
|
||||
fn attr_devtypelist_new() -> Result<Attribute, Error> {
|
||||
Attribute::new(
|
||||
Attributes::DeviceTypeList as u16,
|
||||
AttrValue::Custom,
|
||||
Access::RV,
|
||||
Quality::NONE,
|
||||
)
|
||||
}
|
||||
fn attr_serverlist_new() -> Result<Attribute, Error> {
|
||||
Attribute::new(
|
||||
Attributes::ServerList as u16,
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
use std::sync::{Arc, Mutex, MutexGuard, RwLock};
|
||||
|
||||
use byteorder::{BigEndian, ByteOrder, LittleEndian};
|
||||
use log::info;
|
||||
use log::{error, info};
|
||||
use owning_ref::RwLockReadGuardRef;
|
||||
|
||||
use crate::{
|
||||
|
@ -45,6 +45,7 @@ const ST_RCA: &str = "rca";
|
|||
const ST_ICA: &str = "ica";
|
||||
const ST_NOC: &str = "noc";
|
||||
const ST_IPK: &str = "ipk";
|
||||
const ST_LBL: &str = "label";
|
||||
const ST_PBKEY: &str = "pubkey";
|
||||
const ST_PRKEY: &str = "privkey";
|
||||
|
||||
|
@ -58,6 +59,7 @@ pub struct Fabric {
|
|||
pub icac: Option<Cert>,
|
||||
pub noc: Cert,
|
||||
pub ipk: KeySet,
|
||||
label: String,
|
||||
compressed_id: [u8; COMPRESSED_FABRIC_ID_LEN],
|
||||
mdns_service: Option<SysMdnsService>,
|
||||
}
|
||||
|
@ -97,6 +99,7 @@ impl Fabric {
|
|||
noc,
|
||||
ipk: KeySet::default(),
|
||||
compressed_id: [0; COMPRESSED_FABRIC_ID_LEN],
|
||||
label: "".into(),
|
||||
mdns_service: None,
|
||||
};
|
||||
Fabric::get_compressed_id(f.root_ca.get_pubkey(), fabric_id, &mut f.compressed_id)?;
|
||||
|
@ -129,6 +132,7 @@ impl Fabric {
|
|||
icac: Some(Cert::default()),
|
||||
noc: Cert::default(),
|
||||
ipk: KeySet::default(),
|
||||
label: "".into(),
|
||||
compressed_id: [0; COMPRESSED_FABRIC_ID_LEN],
|
||||
mdns_service: None,
|
||||
})
|
||||
|
@ -186,7 +190,7 @@ impl Fabric {
|
|||
vendor_id: self.vendor_id,
|
||||
fabric_id: self.fabric_id,
|
||||
node_id: self.node_id,
|
||||
label: UtfStr::new(b""),
|
||||
label: UtfStr(self.label.as_bytes()),
|
||||
fab_idx: Some(fab_idx),
|
||||
}
|
||||
}
|
||||
|
@ -206,6 +210,7 @@ impl Fabric {
|
|||
let len = self.noc.as_tlv(&mut key)?;
|
||||
psm.set_kv_slice(fb_key!(index, ST_NOC), &key[..len])?;
|
||||
psm.set_kv_slice(fb_key!(index, ST_IPK), self.ipk.epoch_key())?;
|
||||
psm.set_kv_slice(fb_key!(index, ST_LBL), self.label.as_bytes())?;
|
||||
|
||||
let mut key = [0_u8; crypto::EC_POINT_LEN_BYTES];
|
||||
let len = self.key_pair.get_public_key(&mut key)?;
|
||||
|
@ -217,7 +222,7 @@ impl Fabric {
|
|||
let key = &key[..len];
|
||||
psm.set_kv_slice(fb_key!(index, ST_PRKEY), key)?;
|
||||
|
||||
psm.set_kv_u64(ST_VID, self.vendor_id.into())?;
|
||||
psm.set_kv_u64(fb_key!(index, ST_VID), self.vendor_id.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -241,6 +246,13 @@ impl Fabric {
|
|||
let mut ipk = Vec::new();
|
||||
psm.get_kv_slice(fb_key!(index, ST_IPK), &mut ipk)?;
|
||||
|
||||
let mut label = Vec::new();
|
||||
psm.get_kv_slice(fb_key!(index, ST_LBL), &mut label)?;
|
||||
let label = String::from_utf8(label).map_err(|_| {
|
||||
error!("Couldn't read label");
|
||||
Error::Invalid
|
||||
})?;
|
||||
|
||||
let mut pub_key = Vec::new();
|
||||
psm.get_kv_slice(fb_key!(index, ST_PBKEY), &mut pub_key)?;
|
||||
let mut priv_key = Vec::new();
|
||||
|
@ -248,16 +260,20 @@ impl Fabric {
|
|||
let keypair = KeyPair::new_from_components(pub_key.as_slice(), priv_key.as_slice())?;
|
||||
|
||||
let mut vendor_id = 0;
|
||||
psm.get_kv_u64(ST_VID, &mut vendor_id)?;
|
||||
psm.get_kv_u64(fb_key!(index, ST_VID), &mut vendor_id)?;
|
||||
|
||||
Fabric::new(
|
||||
let f = Fabric::new(
|
||||
keypair,
|
||||
root_ca,
|
||||
icac,
|
||||
noc,
|
||||
ipk.as_slice(),
|
||||
vendor_id as u16,
|
||||
)
|
||||
);
|
||||
f.map(|mut f| {
|
||||
f.label = label;
|
||||
f
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -361,4 +377,28 @@ impl FabricMgr {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_label(&self, index: u8, label: String) -> Result<(), Error> {
|
||||
let index = index as usize;
|
||||
let mut mgr = self.inner.write()?;
|
||||
if label != "" {
|
||||
for i in 1..MAX_SUPPORTED_FABRICS {
|
||||
if let Some(fabric) = &mgr.fabrics[i] {
|
||||
if fabric.label == label {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(fabric) = &mut mgr.fabrics[index] {
|
||||
let old = fabric.label.clone();
|
||||
fabric.label = label;
|
||||
let psm = self.psm.lock().unwrap();
|
||||
if fabric.store(index, &psm).is_err() {
|
||||
fabric.label = old;
|
||||
return Err(Error::StdIoError);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
//! use matter::{Matter, CommissioningData};
|
||||
//! use matter::data_model::device_types::device_type_add_on_off_light;
|
||||
//! use matter::data_model::cluster_basic_information::BasicInfoConfig;
|
||||
//! use rand::prelude::*;
|
||||
//! use matter::secure_channel::spake2p::VerifierData;
|
||||
//!
|
||||
//! # use matter::data_model::sdm::dev_att::{DataType, DevAttDataFetcher};
|
||||
//! # use matter::error::Error;
|
||||
|
@ -38,12 +38,11 @@
|
|||
//! # let dev_att = Box::new(DevAtt{});
|
||||
//!
|
||||
//! /// The commissioning data for this device
|
||||
//! let mut comm_data = CommissioningData {
|
||||
//! passwd: 123456,
|
||||
//! let comm_data = CommissioningData {
|
||||
//! verifier: VerifierData::new_with_pw(123456),
|
||||
//! discriminator: 250,
|
||||
//! ..Default::default()
|
||||
//!
|
||||
//! };
|
||||
//! rand::thread_rng().fill_bytes(&mut comm_data.salt);
|
||||
//!
|
||||
//! /// The basic information about this device
|
||||
//! let dev_info = BasicInfoConfig {
|
||||
|
@ -51,11 +50,12 @@
|
|||
//! pid: 0xFFF1,
|
||||
//! hw_ver: 2,
|
||||
//! sw_ver: 1,
|
||||
//! serial_no: "aabbcc".to_string(),
|
||||
//! };
|
||||
//!
|
||||
//! /// Get the Matter Object
|
||||
//! /// The dev_att is an object that implements the DevAttDataFetcher trait.
|
||||
//! let mut matter = Matter::new(&dev_info, dev_att, &comm_data).unwrap();
|
||||
//! let mut matter = Matter::new(dev_info, dev_att, comm_data).unwrap();
|
||||
//! let dm = matter.get_data_model();
|
||||
//! {
|
||||
//! let mut node = dm.node.write().unwrap();
|
||||
|
|
|
@ -30,15 +30,13 @@ pub struct MdnsInner {
|
|||
vid: u16,
|
||||
/// Product ID
|
||||
pid: u16,
|
||||
/// Discriminator
|
||||
discriminator: u16,
|
||||
}
|
||||
|
||||
pub struct Mdns {
|
||||
inner: Mutex<MdnsInner>,
|
||||
}
|
||||
|
||||
const SHORT_DISCRIMINATOR_MASK: u16 = 0x700;
|
||||
const SHORT_DISCRIMINATOR_MASK: u16 = 0xf00;
|
||||
const SHORT_DISCRIMINATOR_SHIFT: u16 = 8;
|
||||
|
||||
static mut G_MDNS: Option<Arc<Mdns>> = None;
|
||||
|
@ -46,8 +44,10 @@ static INIT: Once = Once::new();
|
|||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ServiceMode {
|
||||
/// The commissioned state
|
||||
Commissioned,
|
||||
Commissionable,
|
||||
/// The commissionable state with the discriminator that should be used
|
||||
Commissionable(u16),
|
||||
}
|
||||
|
||||
impl Mdns {
|
||||
|
@ -72,11 +72,10 @@ impl Mdns {
|
|||
/// Set mDNS service specific values
|
||||
/// Values like vid, pid, discriminator etc
|
||||
// TODO: More things like device-type etc can be added here
|
||||
pub fn set_values(&self, vid: u16, pid: u16, discriminator: u16) {
|
||||
pub fn set_values(&self, vid: u16, pid: u16) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner.vid = vid;
|
||||
inner.pid = pid;
|
||||
inner.discriminator = discriminator;
|
||||
}
|
||||
|
||||
/// Publish a mDNS service
|
||||
|
@ -87,13 +86,11 @@ impl Mdns {
|
|||
ServiceMode::Commissioned => {
|
||||
sys_publish_service(name, "_matter._tcp", MATTER_PORT, &[])
|
||||
}
|
||||
ServiceMode::Commissionable => {
|
||||
let inner = self.inner.lock().unwrap();
|
||||
let short =
|
||||
(inner.discriminator & SHORT_DISCRIMINATOR_MASK) >> SHORT_DISCRIMINATOR_SHIFT;
|
||||
let serv_type = format!("_matterc._udp,_S{},_L{}", short, inner.discriminator);
|
||||
ServiceMode::Commissionable(discriminator) => {
|
||||
let short = (discriminator & SHORT_DISCRIMINATOR_MASK) >> SHORT_DISCRIMINATOR_SHIFT;
|
||||
let serv_type = format!("_matterc._udp,_S{},_L{}", short, discriminator);
|
||||
|
||||
let str_discriminator = format!("{}", inner.discriminator);
|
||||
let str_discriminator = format!("{}", discriminator);
|
||||
let txt_kvs = [["D", &str_discriminator], ["CM", "1"]];
|
||||
sys_publish_service(name, &serv_type, MATTER_PORT, &txt_kvs)
|
||||
}
|
||||
|
|
|
@ -26,12 +26,12 @@ use crate::{
|
|||
crypto::{self, CryptoKeyPair, KeyPair, Sha256},
|
||||
error::Error,
|
||||
fabric::{Fabric, FabricMgr, FabricMgrInner},
|
||||
secure_channel::common,
|
||||
secure_channel::common::SCStatusCodes,
|
||||
secure_channel::common::{self, OpCode},
|
||||
tlv::{get_root_node_struct, FromTLV, OctetStr, TLVElement, TLVWriter, TagType},
|
||||
transport::{
|
||||
network::Address,
|
||||
proto_demux::ProtoCtx,
|
||||
proto_demux::{ProtoCtx, ResponseRequired},
|
||||
queue::{Msg, WorkQ},
|
||||
session::{CloneData, SessionMode},
|
||||
},
|
||||
|
@ -78,7 +78,7 @@ impl Case {
|
|||
Self { fabric_mgr }
|
||||
}
|
||||
|
||||
pub fn handle_casesigma3(&mut self, ctx: &mut ProtoCtx) -> Result<(), Error> {
|
||||
pub fn casesigma3_handler(&mut self, ctx: &mut ProtoCtx) -> Result<ResponseRequired, Error> {
|
||||
let mut case_session = ctx
|
||||
.exch_ctx
|
||||
.exch
|
||||
|
@ -97,7 +97,7 @@ impl Case {
|
|||
None,
|
||||
)?;
|
||||
ctx.exch_ctx.exch.close();
|
||||
return Ok(());
|
||||
return Ok(ResponseRequired::Yes);
|
||||
}
|
||||
// Safe to unwrap here
|
||||
let fabric = fabric.as_ref().as_ref().unwrap();
|
||||
|
@ -132,7 +132,7 @@ impl Case {
|
|||
None,
|
||||
)?;
|
||||
ctx.exch_ctx.exch.close();
|
||||
return Ok(());
|
||||
return Ok(ResponseRequired::Yes);
|
||||
}
|
||||
|
||||
if Case::validate_sigma3_sign(
|
||||
|
@ -151,7 +151,7 @@ impl Case {
|
|||
None,
|
||||
)?;
|
||||
ctx.exch_ctx.exch.close();
|
||||
return Ok(());
|
||||
return Ok(ResponseRequired::Yes);
|
||||
}
|
||||
|
||||
// Only now do we add this message to the TT Hash
|
||||
|
@ -174,10 +174,12 @@ impl Case {
|
|||
ctx.exch_ctx.exch.clear_data_boxed();
|
||||
ctx.exch_ctx.exch.close();
|
||||
|
||||
Ok(())
|
||||
Ok(ResponseRequired::Yes)
|
||||
}
|
||||
|
||||
pub fn handle_casesigma1(&mut self, ctx: &mut ProtoCtx) -> Result<(), Error> {
|
||||
pub fn casesigma1_handler(&mut self, ctx: &mut ProtoCtx) -> Result<ResponseRequired, Error> {
|
||||
ctx.tx.set_proto_opcode(OpCode::CASESigma2 as u8);
|
||||
|
||||
let rx_buf = ctx.rx.as_borrow_slice();
|
||||
let root = get_root_node_struct(rx_buf)?;
|
||||
let r = Sigma1Req::from_tlv(&root)?;
|
||||
|
@ -193,7 +195,7 @@ impl Case {
|
|||
None,
|
||||
)?;
|
||||
ctx.exch_ctx.exch.close();
|
||||
return Ok(());
|
||||
return Ok(ResponseRequired::Yes);
|
||||
}
|
||||
|
||||
let local_sessid = ctx.exch_ctx.sess.reserve_new_sess_id();
|
||||
|
@ -239,7 +241,7 @@ impl Case {
|
|||
None,
|
||||
)?;
|
||||
ctx.exch_ctx.exch.close();
|
||||
return Ok(());
|
||||
return Ok(ResponseRequired::Yes);
|
||||
}
|
||||
|
||||
let sign_len = Case::get_sigma2_sign(
|
||||
|
@ -270,7 +272,7 @@ impl Case {
|
|||
tw.end_container()?;
|
||||
case_session.tt_hash.update(ctx.tx.as_borrow_slice())?;
|
||||
ctx.exch_ctx.exch.set_data_boxed(case_session);
|
||||
Ok(())
|
||||
Ok(ResponseRequired::Yes)
|
||||
}
|
||||
|
||||
fn get_session_clone_data(
|
||||
|
|
|
@ -20,100 +20,30 @@ use std::sync::Arc;
|
|||
use crate::{
|
||||
error::*,
|
||||
fabric::FabricMgr,
|
||||
mdns::{self, Mdns},
|
||||
secure_channel::{common::*, pake::PAKE},
|
||||
sys::SysMdnsService,
|
||||
secure_channel::common::*,
|
||||
tlv,
|
||||
transport::proto_demux::{self, ProtoCtx, ResponseRequired},
|
||||
};
|
||||
use log::{error, info};
|
||||
use num;
|
||||
use rand::prelude::*;
|
||||
|
||||
use super::case::Case;
|
||||
use super::{case::Case, pake::PaseMgr};
|
||||
|
||||
/* Handle messages related to the Secure Channel
|
||||
*/
|
||||
|
||||
pub struct SecureChannel {
|
||||
case: Case,
|
||||
pake: Option<(PAKE, SysMdnsService)>,
|
||||
pase: PaseMgr,
|
||||
}
|
||||
|
||||
impl SecureChannel {
|
||||
pub fn new(fabric_mgr: Arc<FabricMgr>) -> SecureChannel {
|
||||
pub fn new(pase: PaseMgr, fabric_mgr: Arc<FabricMgr>) -> SecureChannel {
|
||||
SecureChannel {
|
||||
pake: None,
|
||||
pase,
|
||||
case: Case::new(fabric_mgr),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_comm_window(&mut self, salt: &[u8; 16], passwd: u32) -> Result<(), Error> {
|
||||
let name: u64 = rand::thread_rng().gen_range(0..0xFFFFFFFFFFFFFFFF);
|
||||
let name = format!("{:016X}", name);
|
||||
let mdns = Mdns::get()?.publish_service(&name, mdns::ServiceMode::Commissionable)?;
|
||||
self.pake = Some((PAKE::new(salt, passwd), mdns));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn close_comm_window(&mut self) {
|
||||
self.pake = None;
|
||||
}
|
||||
|
||||
fn mrpstandaloneack_handler(&mut self, _ctx: &mut ProtoCtx) -> Result<ResponseRequired, Error> {
|
||||
info!("In MRP StandAlone ACK Handler");
|
||||
Ok(ResponseRequired::No)
|
||||
}
|
||||
|
||||
fn pbkdfparamreq_handler(&mut self, ctx: &mut ProtoCtx) -> Result<ResponseRequired, Error> {
|
||||
info!("In PBKDF Param Request Handler");
|
||||
ctx.tx.set_proto_opcode(OpCode::PBKDFParamResponse as u8);
|
||||
if let Some((pake, _)) = &mut self.pake {
|
||||
pake.handle_pbkdfparamrequest(ctx)?;
|
||||
} else {
|
||||
error!("PASE Not enabled");
|
||||
create_sc_status_report(&mut ctx.tx, SCStatusCodes::InvalidParameter, None)?;
|
||||
}
|
||||
Ok(ResponseRequired::Yes)
|
||||
}
|
||||
|
||||
fn pasepake1_handler(&mut self, ctx: &mut ProtoCtx) -> Result<ResponseRequired, Error> {
|
||||
info!("In PASE Pake1 Handler");
|
||||
ctx.tx.set_proto_opcode(OpCode::PASEPake2 as u8);
|
||||
if let Some((pake, _)) = &mut self.pake {
|
||||
pake.handle_pasepake1(ctx)?;
|
||||
} else {
|
||||
error!("PASE Not enabled");
|
||||
create_sc_status_report(&mut ctx.tx, SCStatusCodes::InvalidParameter, None)?;
|
||||
}
|
||||
Ok(ResponseRequired::Yes)
|
||||
}
|
||||
|
||||
fn pasepake3_handler(&mut self, ctx: &mut ProtoCtx) -> Result<ResponseRequired, Error> {
|
||||
info!("In PASE Pake3 Handler");
|
||||
if let Some((pake, _)) = &mut self.pake {
|
||||
pake.handle_pasepake3(ctx)?;
|
||||
// TODO: Currently we assume that PAKE is not successful and reset the PAKE object
|
||||
self.pake = None;
|
||||
} else {
|
||||
error!("PASE Not enabled");
|
||||
create_sc_status_report(&mut ctx.tx, SCStatusCodes::InvalidParameter, None)?;
|
||||
}
|
||||
Ok(ResponseRequired::Yes)
|
||||
}
|
||||
|
||||
fn casesigma1_handler(&mut self, ctx: &mut ProtoCtx) -> Result<ResponseRequired, Error> {
|
||||
info!("In CASE Sigma1 Handler");
|
||||
ctx.tx.set_proto_opcode(OpCode::CASESigma2 as u8);
|
||||
self.case.handle_casesigma1(ctx)?;
|
||||
Ok(ResponseRequired::Yes)
|
||||
}
|
||||
|
||||
fn casesigma3_handler(&mut self, ctx: &mut ProtoCtx) -> Result<ResponseRequired, Error> {
|
||||
info!("In CASE Sigma3 Handler");
|
||||
self.case.handle_casesigma3(ctx)?;
|
||||
Ok(ResponseRequired::Yes)
|
||||
}
|
||||
}
|
||||
|
||||
impl proto_demux::HandleProto for SecureChannel {
|
||||
|
@ -121,15 +51,16 @@ impl proto_demux::HandleProto for SecureChannel {
|
|||
let proto_opcode: OpCode =
|
||||
num::FromPrimitive::from_u8(ctx.rx.get_proto_opcode()).ok_or(Error::Invalid)?;
|
||||
ctx.tx.set_proto_id(PROTO_ID_SECURE_CHANNEL as u16);
|
||||
info!("Received Data");
|
||||
info!("Received Opcode: {:?}", proto_opcode);
|
||||
info!("Received Data:");
|
||||
tlv::print_tlv_list(ctx.rx.as_borrow_slice());
|
||||
let result = match proto_opcode {
|
||||
OpCode::MRPStandAloneAck => self.mrpstandaloneack_handler(ctx),
|
||||
OpCode::PBKDFParamRequest => self.pbkdfparamreq_handler(ctx),
|
||||
OpCode::PASEPake1 => self.pasepake1_handler(ctx),
|
||||
OpCode::PASEPake3 => self.pasepake3_handler(ctx),
|
||||
OpCode::CASESigma1 => self.casesigma1_handler(ctx),
|
||||
OpCode::CASESigma3 => self.casesigma3_handler(ctx),
|
||||
OpCode::MRPStandAloneAck => Ok(ResponseRequired::No),
|
||||
OpCode::PBKDFParamRequest => self.pase.pbkdfparamreq_handler(ctx),
|
||||
OpCode::PASEPake1 => self.pase.pasepake1_handler(ctx),
|
||||
OpCode::PASEPake3 => self.pase.pasepake3_handler(ctx),
|
||||
OpCode::CASESigma1 => self.case.casesigma1_handler(ctx),
|
||||
OpCode::CASESigma3 => self.case.casesigma3_handler(ctx),
|
||||
_ => {
|
||||
error!("OpCode Not Handled: {:?}", proto_opcode);
|
||||
Err(Error::InvalidOpcode)
|
||||
|
|
|
@ -38,7 +38,9 @@ pub trait CryptoSpake2 {
|
|||
fn set_w1(&mut self, w1: &[u8]) -> Result<(), Error>;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn set_L(&mut self, w1s: &[u8]) -> Result<(), Error>;
|
||||
fn set_L(&mut self, l: &[u8]) -> Result<(), Error>;
|
||||
#[allow(non_snake_case)]
|
||||
fn set_L_from_w1s(&mut self, w1s: &[u8]) -> Result<(), Error>;
|
||||
#[allow(non_snake_case)]
|
||||
fn get_pB(&mut self, pB: &mut [u8]) -> Result<(), Error>;
|
||||
#[allow(non_snake_case)]
|
||||
|
|
|
@ -114,9 +114,14 @@ impl CryptoSpake2 for CryptoMbedTLS {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn set_L(&mut self, l: &[u8]) -> Result<(), Error> {
|
||||
self.L = EcPoint::from_binary(&mut self.group, l)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(dead_code)]
|
||||
fn set_L(&mut self, w1s: &[u8]) -> Result<(), Error> {
|
||||
fn set_L_from_w1s(&mut self, w1s: &[u8]) -> Result<(), Error> {
|
||||
// From the Matter spec,
|
||||
// L = w1 * P
|
||||
// where P is the generator of the underlying elliptic curve
|
||||
|
|
|
@ -117,9 +117,14 @@ impl CryptoSpake2 for CryptoOpenSSL {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn set_L(&mut self, l: &[u8]) -> Result<(), Error> {
|
||||
self.L = EcPoint::from_bytes(&self.group, l, &mut self.bn_ctx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(dead_code)]
|
||||
fn set_L(&mut self, w1s: &[u8]) -> Result<(), Error> {
|
||||
fn set_L_from_w1s(&mut self, w1s: &[u8]) -> Result<(), Error> {
|
||||
// From the Matter spec,
|
||||
// L = w1 * P
|
||||
// where P is the generator of the underlying elliptic curve
|
||||
|
|
|
@ -15,21 +15,26 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use std::time::{Duration, SystemTime};
|
||||
use std::{
|
||||
sync::{Arc, Mutex},
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use super::{
|
||||
common::{create_sc_status_report, SCStatusCodes},
|
||||
spake2p::Spake2P,
|
||||
spake2p::{Spake2P, VerifierData},
|
||||
};
|
||||
use crate::{
|
||||
crypto,
|
||||
error::Error,
|
||||
sys::SPAKE2_ITERATION_COUNT,
|
||||
mdns::{self, Mdns},
|
||||
secure_channel::common::OpCode,
|
||||
sys::SysMdnsService,
|
||||
tlv::{self, get_root_node_struct, FromTLV, OctetStr, TLVElement, TLVWriter, TagType, ToTLV},
|
||||
transport::{
|
||||
exchange::ExchangeCtx,
|
||||
network::Address,
|
||||
proto_demux::ProtoCtx,
|
||||
proto_demux::{ProtoCtx, ResponseRequired},
|
||||
queue::{Msg, WorkQ},
|
||||
session::{CloneData, SessionMode},
|
||||
},
|
||||
|
@ -37,6 +42,79 @@ use crate::{
|
|||
use log::{error, info};
|
||||
use rand::prelude::*;
|
||||
|
||||
enum PaseMgrState {
|
||||
Enabled(PAKE, SysMdnsService),
|
||||
Disabled,
|
||||
}
|
||||
|
||||
pub struct PaseMgrInternal {
|
||||
state: PaseMgrState,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
// Could this lock be avoided?
|
||||
pub struct PaseMgr(Arc<Mutex<PaseMgrInternal>>);
|
||||
|
||||
impl PaseMgr {
|
||||
pub fn new() -> Self {
|
||||
Self(Arc::new(Mutex::new(PaseMgrInternal {
|
||||
state: PaseMgrState::Disabled,
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn enable_pase_session(
|
||||
&mut self,
|
||||
verifier: VerifierData,
|
||||
discriminator: u16,
|
||||
) -> Result<(), Error> {
|
||||
let mut s = self.0.lock().unwrap();
|
||||
let name: u64 = rand::thread_rng().gen_range(0..0xFFFFFFFFFFFFFFFF);
|
||||
let name = format!("{:016X}", name);
|
||||
let mdns = Mdns::get()?
|
||||
.publish_service(&name, mdns::ServiceMode::Commissionable(discriminator))?;
|
||||
s.state = PaseMgrState::Enabled(PAKE::new(verifier), mdns);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn disable_pase_session(&mut self) {
|
||||
let mut s = self.0.lock().unwrap();
|
||||
s.state = PaseMgrState::Disabled;
|
||||
}
|
||||
|
||||
/// If the PASE Session is enabled, execute the closure,
|
||||
/// if not enabled, generate SC Status Report
|
||||
fn if_enabled<F>(&mut self, ctx: &mut ProtoCtx, f: F) -> Result<(), Error>
|
||||
where
|
||||
F: FnOnce(&mut PAKE, &mut ProtoCtx) -> Result<(), Error>,
|
||||
{
|
||||
let mut s = self.0.lock().unwrap();
|
||||
if let PaseMgrState::Enabled(pake, _) = &mut s.state {
|
||||
f(pake, ctx)
|
||||
} else {
|
||||
error!("PASE Not enabled");
|
||||
create_sc_status_report(&mut ctx.tx, SCStatusCodes::InvalidParameter, None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pbkdfparamreq_handler(&mut self, ctx: &mut ProtoCtx) -> Result<ResponseRequired, Error> {
|
||||
ctx.tx.set_proto_opcode(OpCode::PBKDFParamResponse as u8);
|
||||
self.if_enabled(ctx, |pake, ctx| pake.handle_pbkdfparamrequest(ctx))?;
|
||||
Ok(ResponseRequired::Yes)
|
||||
}
|
||||
|
||||
pub fn pasepake1_handler(&mut self, ctx: &mut ProtoCtx) -> Result<ResponseRequired, Error> {
|
||||
ctx.tx.set_proto_opcode(OpCode::PASEPake2 as u8);
|
||||
self.if_enabled(ctx, |pake, ctx| pake.handle_pasepake1(ctx))?;
|
||||
Ok(ResponseRequired::Yes)
|
||||
}
|
||||
|
||||
pub fn pasepake3_handler(&mut self, ctx: &mut ProtoCtx) -> Result<ResponseRequired, Error> {
|
||||
self.if_enabled(ctx, |pake, ctx| pake.handle_pasepake3(ctx))?;
|
||||
self.disable_pase_session();
|
||||
Ok(ResponseRequired::Yes)
|
||||
}
|
||||
}
|
||||
|
||||
// This file basically deals with the handlers for the PASE secure channel protocol
|
||||
// TLV extraction and encoding is done in this file.
|
||||
// We create a Spake2p object and set it up in the exchange-data. This object then
|
||||
|
@ -111,20 +189,17 @@ impl Default for PakeState {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PAKE {
|
||||
salt: [u8; 16],
|
||||
passwd: u32,
|
||||
pub verifier: VerifierData,
|
||||
state: PakeState,
|
||||
}
|
||||
|
||||
impl PAKE {
|
||||
pub fn new(salt: &[u8; 16], passwd: u32) -> Self {
|
||||
pub fn new(verifier: VerifierData) -> Self {
|
||||
// TODO: Can any PBKDF2 calculation be pre-computed here
|
||||
PAKE {
|
||||
passwd,
|
||||
salt: *salt,
|
||||
..Default::default()
|
||||
verifier,
|
||||
state: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,8 +251,7 @@ impl PAKE {
|
|||
let pA = extract_pasepake_1_or_3_params(ctx.rx.as_borrow_slice())?;
|
||||
let mut pB: [u8; 65] = [0; 65];
|
||||
let mut cB: [u8; 32] = [0; 32];
|
||||
sd.spake2p
|
||||
.start_verifier(self.passwd, SPAKE2_ITERATION_COUNT, &self.salt)?;
|
||||
sd.spake2p.start_verifier(&self.verifier)?;
|
||||
sd.spake2p.handle_pA(pA, &mut pB, &mut cB)?;
|
||||
|
||||
let mut tw = TLVWriter::new(ctx.tx.get_writebuf()?);
|
||||
|
@ -231,8 +305,8 @@ impl PAKE {
|
|||
};
|
||||
if !a.has_params {
|
||||
let params_resp = PBKDFParamRespParams {
|
||||
count: SPAKE2_ITERATION_COUNT,
|
||||
salt: OctetStr(&self.salt),
|
||||
count: self.verifier.count,
|
||||
salt: OctetStr(&self.verifier.salt),
|
||||
};
|
||||
resp.params = Some(params_resp);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::crypto::{self, HmacSha256};
|
||||
use crate::{
|
||||
crypto::{self, HmacSha256},
|
||||
sys,
|
||||
};
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use log::error;
|
||||
use rand::prelude::*;
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
use crate::{
|
||||
|
@ -74,6 +79,10 @@ const SPAKE2P_KEY_CONFIRM_INFO: [u8; 16] = *b"ConfirmationKeys";
|
|||
const SPAKE2P_CONTEXT_PREFIX: [u8; 26] = *b"CHIP PAKE V1 Commissioning";
|
||||
const CRYPTO_GROUP_SIZE_BYTES: usize = 32;
|
||||
const CRYPTO_W_SIZE_BYTES: usize = CRYPTO_GROUP_SIZE_BYTES + 8;
|
||||
const CRYPTO_PUBLIC_KEY_SIZE_BYTES: usize = (2 * CRYPTO_GROUP_SIZE_BYTES) + 1;
|
||||
|
||||
const MAX_SALT_SIZE_BYTES: usize = 32;
|
||||
const VERIFIER_SIZE_BYTES: usize = CRYPTO_GROUP_SIZE_BYTES + CRYPTO_PUBLIC_KEY_SIZE_BYTES;
|
||||
|
||||
#[cfg(feature = "crypto_openssl")]
|
||||
fn crypto_spake2_new() -> Result<Box<dyn CryptoSpake2>, Error> {
|
||||
|
@ -96,6 +105,50 @@ impl Default for Spake2P {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct VerifierData {
|
||||
pub data: VerifierOption,
|
||||
// For the VerifierOption::Verifier, the following fields only serve
|
||||
// information purposes
|
||||
pub salt: [u8; MAX_SALT_SIZE_BYTES],
|
||||
pub count: u32,
|
||||
}
|
||||
|
||||
pub enum VerifierOption {
|
||||
/// With Password
|
||||
Password(u32),
|
||||
/// With Verifier
|
||||
Verifier([u8; VERIFIER_SIZE_BYTES]),
|
||||
}
|
||||
|
||||
impl VerifierData {
|
||||
pub fn new_with_pw(pw: u32) -> Self {
|
||||
let mut s = Self {
|
||||
salt: [0; MAX_SALT_SIZE_BYTES],
|
||||
count: sys::SPAKE2_ITERATION_COUNT,
|
||||
data: VerifierOption::Password(pw),
|
||||
};
|
||||
rand::thread_rng().fill_bytes(&mut s.salt);
|
||||
s
|
||||
}
|
||||
|
||||
pub fn new(verifier: &[u8], count: u32, salt: &[u8]) -> Self {
|
||||
let mut v = [0_u8; VERIFIER_SIZE_BYTES];
|
||||
let mut s = [0_u8; MAX_SALT_SIZE_BYTES];
|
||||
|
||||
let slice = &mut v[..verifier.len()];
|
||||
slice.copy_from_slice(verifier);
|
||||
|
||||
let slice = &mut s[..salt.len()];
|
||||
slice.copy_from_slice(salt);
|
||||
|
||||
Self {
|
||||
data: VerifierOption::Verifier(v),
|
||||
count,
|
||||
salt: s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Spake2P {
|
||||
pub fn new() -> Self {
|
||||
Spake2P {
|
||||
|
@ -132,17 +185,31 @@ impl Spake2P {
|
|||
let _ = pbkdf2_hmac(&pw_str, iter as usize, salt, w0w1s);
|
||||
}
|
||||
|
||||
pub fn start_verifier(&mut self, pw: u32, iter: u32, salt: &[u8]) -> Result<(), Error> {
|
||||
let mut w0w1s: [u8; (2 * CRYPTO_W_SIZE_BYTES)] = [0; (2 * CRYPTO_W_SIZE_BYTES)];
|
||||
Spake2P::get_w0w1s(pw, iter, salt, &mut w0w1s);
|
||||
pub fn start_verifier(&mut self, verifier: &VerifierData) -> Result<(), Error> {
|
||||
self.crypto_spake2 = Some(crypto_spake2_new()?);
|
||||
match verifier.data {
|
||||
VerifierOption::Password(pw) => {
|
||||
// Derive w0 and L from the password
|
||||
let mut w0w1s: [u8; (2 * CRYPTO_W_SIZE_BYTES)] = [0; (2 * CRYPTO_W_SIZE_BYTES)];
|
||||
Spake2P::get_w0w1s(pw, verifier.count, &verifier.salt, &mut w0w1s);
|
||||
|
||||
let w0s_len = w0w1s.len() / 2;
|
||||
if let Some(crypto_spake2) = &mut self.crypto_spake2 {
|
||||
crypto_spake2.set_w0_from_w0s(&w0w1s[0..w0s_len])?;
|
||||
crypto_spake2.set_L(&w0w1s[w0s_len..])?;
|
||||
let w0s_len = w0w1s.len() / 2;
|
||||
if let Some(crypto_spake2) = &mut self.crypto_spake2 {
|
||||
crypto_spake2.set_w0_from_w0s(&w0w1s[0..w0s_len])?;
|
||||
crypto_spake2.set_L_from_w1s(&w0w1s[w0s_len..])?;
|
||||
}
|
||||
}
|
||||
VerifierOption::Verifier(v) => {
|
||||
// Extract w0 and L from the verifier
|
||||
if v.len() != CRYPTO_GROUP_SIZE_BYTES + CRYPTO_PUBLIC_KEY_SIZE_BYTES {
|
||||
error!("Verifier of invalid length");
|
||||
}
|
||||
if let Some(crypto_spake2) = &mut self.crypto_spake2 {
|
||||
crypto_spake2.set_w0(&v[0..CRYPTO_GROUP_SIZE_BYTES])?;
|
||||
crypto_spake2.set_L(&v[CRYPTO_GROUP_SIZE_BYTES..])?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.mode = Spake2Mode::Verifier(Spake2VerifierState::Init);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -164,6 +231,7 @@ impl Spake2P {
|
|||
Spake2P::get_Ke_and_cAcB(&TT, pA, pB, &mut self.Ke, &mut self.cA, cB)?;
|
||||
}
|
||||
}
|
||||
|
||||
// We are finished with using the crypto_spake2 now
|
||||
self.crypto_spake2 = None;
|
||||
self.mode = Spake2Mode::Verifier(Spake2VerifierState::PendingConfirmation);
|
||||
|
|
|
@ -119,6 +119,10 @@ impl<'a> UtfStr<'a> {
|
|||
pub fn new(str: &'a [u8]) -> Self {
|
||||
Self(str)
|
||||
}
|
||||
|
||||
pub fn to_string(self) -> Result<String, Error> {
|
||||
String::from_utf8(self.0.to_vec()).map_err(|_| Error::Invalid)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToTLV for UtfStr<'a> {
|
||||
|
@ -127,6 +131,12 @@ impl<'a> ToTLV for UtfStr<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> FromTLV<'a> for UtfStr<'a> {
|
||||
fn from_tlv(t: &TLVElement<'a>) -> Result<UtfStr<'a>, Error> {
|
||||
t.slice().map(UtfStr)
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements OctetString from the spec
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct OctetStr<'a>(pub &'a [u8]);
|
||||
|
|
|
@ -29,6 +29,7 @@ use matter::{
|
|||
error::Error,
|
||||
fabric::FabricMgr,
|
||||
interaction_model::{core::OpCode, InteractionModel},
|
||||
secure_channel::pake::PaseMgr,
|
||||
tlv::{TLVWriter, TagType, ToTLV},
|
||||
transport::packet::Packet,
|
||||
transport::proto_demux::HandleProto,
|
||||
|
@ -93,16 +94,25 @@ impl ImEngine {
|
|||
pid: 11,
|
||||
hw_ver: 12,
|
||||
sw_ver: 13,
|
||||
serial_no: "aabbccdd".to_string(),
|
||||
};
|
||||
let dev_att = Box::new(DummyDevAtt {});
|
||||
let fabric_mgr = Arc::new(FabricMgr::new().unwrap());
|
||||
let acl_mgr = Arc::new(AclMgr::new_with(false).unwrap());
|
||||
let pase_mgr = PaseMgr::new();
|
||||
acl_mgr.erase_all();
|
||||
let mut default_acl = AclEntry::new(1, Privilege::ADMIN, AuthMode::Case);
|
||||
// Only allow the standard peer node id of the IM Engine
|
||||
default_acl.add_subject(IM_ENGINE_PEER_ID).unwrap();
|
||||
acl_mgr.add(default_acl).unwrap();
|
||||
let dm = DataModel::new(&dev_det, dev_att, fabric_mgr, acl_mgr.clone()).unwrap();
|
||||
let dm = DataModel::new(
|
||||
dev_det,
|
||||
dev_att,
|
||||
fabric_mgr.clone(),
|
||||
acl_mgr.clone(),
|
||||
pase_mgr,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let mut d = dm.node.write().unwrap();
|
||||
|
|
|
@ -204,10 +204,10 @@ fn read_cluster_id_write_attr(im: &ImEngine, endpoint: u16) -> AttrValue {
|
|||
let node = im.dm.node.read().unwrap();
|
||||
let echo = node.get_cluster(endpoint, echo_cluster::ID).unwrap();
|
||||
|
||||
*echo
|
||||
.base()
|
||||
echo.base()
|
||||
.read_attribute_raw(echo_cluster::Attributes::AttWrite as u16)
|
||||
.unwrap()
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn read_cluster_id_data_ver(im: &ImEngine, endpoint: u16) -> u32 {
|
||||
|
|
|
@ -238,6 +238,7 @@ fn test_read_wc_endpoint_wc_attribute() {
|
|||
let attr_list_tlvs = get_tlvs(
|
||||
&mut buf,
|
||||
&[
|
||||
GlobalElements::FeatureMap as u16,
|
||||
GlobalElements::AttributeList as u16,
|
||||
echo_cluster::Attributes::Att1 as u16,
|
||||
echo_cluster::Attributes::Att2 as u16,
|
||||
|
@ -247,6 +248,14 @@ fn test_read_wc_endpoint_wc_attribute() {
|
|||
);
|
||||
|
||||
let expected = &[
|
||||
attr_data!(
|
||||
GenericPath::new(
|
||||
Some(0),
|
||||
Some(echo_cluster::ID),
|
||||
Some(GlobalElements::FeatureMap as u32),
|
||||
),
|
||||
ElementType::U8(0)
|
||||
),
|
||||
attr_data!(
|
||||
GenericPath::new(
|
||||
Some(0),
|
||||
|
@ -279,6 +288,14 @@ fn test_read_wc_endpoint_wc_attribute() {
|
|||
),
|
||||
ElementType::U32(echo_cluster::ATTR_CUSTOM_VALUE)
|
||||
),
|
||||
attr_data!(
|
||||
GenericPath::new(
|
||||
Some(1),
|
||||
Some(echo_cluster::ID),
|
||||
Some(GlobalElements::FeatureMap as u32),
|
||||
),
|
||||
ElementType::U8(0)
|
||||
),
|
||||
attr_data!(
|
||||
GenericPath::new(
|
||||
Some(1),
|
||||
|
|
Loading…
Add table
Reference in a new issue