727 lines
21 KiB
Rust
727 lines
21 KiB
Rust
/*
|
|
*
|
|
* Copyright (c) 2020-2022 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 core::cell::RefCell;
|
|
use core::convert::TryInto;
|
|
|
|
use crate::acl::{AclEntry, AclMgr, AuthMode};
|
|
use crate::cert::{Cert, MAX_CERT_TLV_LEN};
|
|
use crate::crypto::{self, KeyPair};
|
|
use crate::data_model::objects::*;
|
|
use crate::data_model::sdm::dev_att;
|
|
use crate::fabric::{Fabric, FabricMgr, MAX_SUPPORTED_FABRICS};
|
|
use crate::interaction_model::core::Transaction;
|
|
use crate::mdns::Mdns;
|
|
use crate::tlv::{FromTLV, OctetStr, TLVElement, TLVWriter, TagType, ToTLV, UtfStr};
|
|
use crate::transport::session::SessionMode;
|
|
use crate::utils::epoch::Epoch;
|
|
use crate::utils::rand::Rand;
|
|
use crate::utils::writebuf::WriteBuf;
|
|
use crate::{attribute_enum, cmd_enter, command_enum, error::*};
|
|
use log::{error, info};
|
|
use strum::{EnumDiscriminants, FromRepr};
|
|
|
|
use super::dev_att::{DataType, DevAttDataFetcher};
|
|
use super::failsafe::FailSafe;
|
|
|
|
// Node Operational Credentials Cluster
|
|
|
|
#[derive(Clone, Copy)]
|
|
#[allow(dead_code)]
|
|
enum NocStatus {
|
|
Ok = 0,
|
|
InvalidPublicKey = 1,
|
|
InvalidNodeOpId = 2,
|
|
InvalidNOC = 3,
|
|
MissingCsr = 4,
|
|
TableFull = 5,
|
|
MissingAcl = 6,
|
|
MissingIpk = 7,
|
|
InsufficientPrivlege = 8,
|
|
FabricConflict = 9,
|
|
LabelConflict = 10,
|
|
InvalidFabricIndex = 11,
|
|
}
|
|
|
|
enum NocError {
|
|
Status(NocStatus),
|
|
Error(Error),
|
|
}
|
|
|
|
impl From<NocStatus> for NocError {
|
|
fn from(value: NocStatus) -> Self {
|
|
Self::Status(value)
|
|
}
|
|
}
|
|
|
|
impl From<Error> for NocError {
|
|
fn from(value: Error) -> Self {
|
|
Self::Error(value)
|
|
}
|
|
}
|
|
|
|
// Some placeholder value for now
|
|
const MAX_CERT_DECLARATION_LEN: usize = 600;
|
|
// Some placeholder value for now
|
|
const MAX_CSR_LEN: usize = 300;
|
|
// As defined in the Matter Spec
|
|
const RESP_MAX: usize = 900;
|
|
|
|
pub const ID: u32 = 0x003E;
|
|
|
|
#[derive(FromRepr)]
|
|
#[repr(u32)]
|
|
pub enum Commands {
|
|
AttReq = 0x00,
|
|
CertChainReq = 0x02,
|
|
CSRReq = 0x04,
|
|
AddNOC = 0x06,
|
|
UpdateFabricLabel = 0x09,
|
|
RemoveFabric = 0x0a,
|
|
AddTrustedRootCert = 0x0b,
|
|
}
|
|
|
|
command_enum!(Commands);
|
|
|
|
#[repr(u16)]
|
|
pub enum RespCommands {
|
|
AttReqResp = 0x01,
|
|
CertChainResp = 0x03,
|
|
CSRResp = 0x05,
|
|
NOCResp = 0x08,
|
|
}
|
|
|
|
#[derive(FromRepr, EnumDiscriminants)]
|
|
#[repr(u16)]
|
|
pub enum Attributes {
|
|
NOCs = 0,
|
|
Fabrics(()) = 1,
|
|
SupportedFabrics(AttrType<u8>) = 2,
|
|
CommissionedFabrics(AttrType<u8>) = 3,
|
|
TrustedRootCerts = 4,
|
|
CurrentFabricIndex(AttrType<u8>) = 5,
|
|
}
|
|
|
|
attribute_enum!(Attributes);
|
|
|
|
pub const CLUSTER: Cluster<'static> = Cluster {
|
|
id: ID as _,
|
|
feature_map: 0,
|
|
attributes: &[
|
|
FEATURE_MAP,
|
|
ATTRIBUTE_LIST,
|
|
Attribute::new(
|
|
AttributesDiscriminants::CurrentFabricIndex as u16,
|
|
Access::RV,
|
|
Quality::NONE,
|
|
),
|
|
Attribute::new(
|
|
AttributesDiscriminants::Fabrics as u16,
|
|
Access::RV.union(Access::FAB_SCOPED),
|
|
Quality::NONE,
|
|
),
|
|
Attribute::new(
|
|
AttributesDiscriminants::SupportedFabrics as u16,
|
|
Access::RV,
|
|
Quality::FIXED,
|
|
),
|
|
Attribute::new(
|
|
AttributesDiscriminants::CommissionedFabrics as u16,
|
|
Access::RV,
|
|
Quality::NONE,
|
|
),
|
|
],
|
|
commands: &[
|
|
Commands::AttReq as _,
|
|
Commands::CertChainReq as _,
|
|
Commands::CSRReq as _,
|
|
Commands::AddNOC as _,
|
|
Commands::UpdateFabricLabel as _,
|
|
Commands::RemoveFabric as _,
|
|
Commands::AddTrustedRootCert as _,
|
|
],
|
|
};
|
|
|
|
pub struct NocData {
|
|
pub key_pair: KeyPair,
|
|
pub root_ca: heapless::Vec<u8, { MAX_CERT_TLV_LEN }>,
|
|
}
|
|
|
|
impl NocData {
|
|
pub fn new(key_pair: KeyPair) -> Self {
|
|
Self {
|
|
key_pair,
|
|
root_ca: heapless::Vec::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(ToTLV)]
|
|
struct CertChainResp<'a> {
|
|
cert: OctetStr<'a>,
|
|
}
|
|
|
|
#[derive(ToTLV)]
|
|
struct NocResp<'a> {
|
|
status_code: u8,
|
|
fab_idx: u8,
|
|
debug_txt: UtfStr<'a>,
|
|
}
|
|
|
|
#[derive(FromTLV)]
|
|
#[tlvargs(lifetime = "'a")]
|
|
struct AddNocReq<'a> {
|
|
noc_value: OctetStr<'a>,
|
|
icac_value: OctetStr<'a>,
|
|
ipk_value: OctetStr<'a>,
|
|
case_admin_subject: u64,
|
|
vendor_id: u16,
|
|
}
|
|
|
|
#[derive(FromTLV)]
|
|
#[tlvargs(lifetime = "'a")]
|
|
struct CommonReq<'a> {
|
|
str: OctetStr<'a>,
|
|
}
|
|
|
|
#[derive(FromTLV)]
|
|
#[tlvargs(lifetime = "'a")]
|
|
struct UpdateFabricLabelReq<'a> {
|
|
label: UtfStr<'a>,
|
|
}
|
|
|
|
#[derive(FromTLV)]
|
|
struct CertChainReq {
|
|
cert_type: u8,
|
|
}
|
|
|
|
#[derive(FromTLV)]
|
|
struct RemoveFabricReq {
|
|
fab_idx: u8,
|
|
}
|
|
|
|
pub struct NocCluster<'a> {
|
|
data_ver: Dataver,
|
|
epoch: Epoch,
|
|
rand: Rand,
|
|
dev_att: &'a dyn DevAttDataFetcher,
|
|
fabric_mgr: &'a RefCell<FabricMgr>,
|
|
acl_mgr: &'a RefCell<AclMgr>,
|
|
failsafe: &'a RefCell<FailSafe>,
|
|
mdns: &'a dyn Mdns,
|
|
}
|
|
|
|
impl<'a> NocCluster<'a> {
|
|
pub fn new(
|
|
dev_att: &'a dyn DevAttDataFetcher,
|
|
fabric_mgr: &'a RefCell<FabricMgr>,
|
|
acl_mgr: &'a RefCell<AclMgr>,
|
|
failsafe: &'a RefCell<FailSafe>,
|
|
mdns: &'a dyn Mdns,
|
|
epoch: Epoch,
|
|
rand: Rand,
|
|
) -> Self {
|
|
Self {
|
|
data_ver: Dataver::new(rand),
|
|
epoch,
|
|
rand,
|
|
dev_att,
|
|
fabric_mgr,
|
|
acl_mgr,
|
|
failsafe,
|
|
mdns,
|
|
}
|
|
}
|
|
|
|
pub fn read(&self, attr: &AttrDetails, encoder: AttrDataEncoder) -> Result<(), Error> {
|
|
if let Some(mut writer) = encoder.with_dataver(self.data_ver.get())? {
|
|
if attr.is_system() {
|
|
CLUSTER.read(attr.attr_id, writer)
|
|
} else {
|
|
match attr.attr_id.try_into()? {
|
|
Attributes::SupportedFabrics(codec) => {
|
|
codec.encode(writer, MAX_SUPPORTED_FABRICS as _)
|
|
}
|
|
Attributes::CurrentFabricIndex(codec) => codec.encode(writer, attr.fab_idx),
|
|
Attributes::Fabrics(_) => {
|
|
writer.start_array(AttrDataWriter::TAG)?;
|
|
self.fabric_mgr.borrow().for_each(|entry, fab_idx| {
|
|
if !attr.fab_filter || attr.fab_idx == fab_idx {
|
|
let root_ca_cert = entry.get_root_ca()?;
|
|
|
|
entry
|
|
.get_fabric_desc(fab_idx, &root_ca_cert)?
|
|
.to_tlv(&mut writer, TagType::Anonymous)?;
|
|
}
|
|
|
|
Ok(())
|
|
})?;
|
|
writer.end_container()?;
|
|
|
|
writer.complete()
|
|
}
|
|
Attributes::CommissionedFabrics(codec) => {
|
|
codec.encode(writer, self.fabric_mgr.borrow().used_count() as _)
|
|
}
|
|
_ => {
|
|
error!("Attribute not supported: this shouldn't happen");
|
|
Err(ErrorCode::AttributeNotFound.into())
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub fn invoke(
|
|
&mut self,
|
|
transaction: &mut Transaction,
|
|
cmd: &CmdDetails,
|
|
data: &TLVElement,
|
|
encoder: CmdDataEncoder,
|
|
) -> Result<(), Error> {
|
|
match cmd.cmd_id.try_into()? {
|
|
Commands::AddNOC => self.handle_command_addnoc(transaction, data, encoder)?,
|
|
Commands::CSRReq => self.handle_command_csrrequest(transaction, data, encoder)?,
|
|
Commands::AddTrustedRootCert => {
|
|
self.handle_command_addtrustedrootcert(transaction, data)?
|
|
}
|
|
Commands::AttReq => self.handle_command_attrequest(transaction, data, encoder)?,
|
|
Commands::CertChainReq => {
|
|
self.handle_command_certchainrequest(transaction, data, encoder)?
|
|
}
|
|
Commands::UpdateFabricLabel => {
|
|
self.handle_command_updatefablabel(transaction, data, encoder)?;
|
|
}
|
|
Commands::RemoveFabric => self.handle_command_rmfabric(transaction, data, encoder)?,
|
|
}
|
|
|
|
self.data_ver.changed();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn add_acl(&self, fab_idx: u8, admin_subject: u64) -> Result<(), Error> {
|
|
let mut acl = AclEntry::new(fab_idx, Privilege::ADMIN, AuthMode::Case);
|
|
acl.add_subject(admin_subject)?;
|
|
self.acl_mgr.borrow_mut().add(acl)
|
|
}
|
|
|
|
fn _handle_command_addnoc(
|
|
&mut self,
|
|
transaction: &mut Transaction,
|
|
data: &TLVElement,
|
|
) -> Result<u8, NocError> {
|
|
let noc_data = transaction
|
|
.session_mut()
|
|
.take_noc_data()
|
|
.ok_or(NocStatus::MissingCsr)?;
|
|
|
|
if !self
|
|
.failsafe
|
|
.borrow_mut()
|
|
.allow_noc_change()
|
|
.map_err(|_| NocStatus::InsufficientPrivlege)?
|
|
{
|
|
error!("AddNOC not allowed by Fail Safe");
|
|
Err(NocStatus::InsufficientPrivlege)?;
|
|
}
|
|
|
|
// TODO
|
|
// // This command's processing may take longer, send a stand alone ACK to the peer to avoid any retranmissions
|
|
// let ack_send = secure_channel::common::send_mrp_standalone_ack(
|
|
// trans.exch,
|
|
// trans.session,
|
|
// );
|
|
// if ack_send.is_err() {
|
|
// error!("Error sending Standalone ACK, falling back to piggybacked ACK");
|
|
// }
|
|
|
|
let r = AddNocReq::from_tlv(data).map_err(|_| NocStatus::InvalidNOC)?;
|
|
|
|
let noc_cert = Cert::new(r.noc_value.0).map_err(|_| NocStatus::InvalidNOC)?;
|
|
info!("Received NOC as: {}", noc_cert);
|
|
|
|
let noc = heapless::Vec::from_slice(r.noc_value.0).map_err(|_| NocStatus::InvalidNOC)?;
|
|
|
|
let icac = if !r.icac_value.0.is_empty() {
|
|
let icac_cert = Cert::new(r.icac_value.0).map_err(|_| NocStatus::InvalidNOC)?;
|
|
info!("Received ICAC as: {}", icac_cert);
|
|
|
|
let icac =
|
|
heapless::Vec::from_slice(r.icac_value.0).map_err(|_| NocStatus::InvalidNOC)?;
|
|
Some(icac)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let fabric = Fabric::new(
|
|
noc_data.key_pair,
|
|
noc_data.root_ca,
|
|
icac,
|
|
noc,
|
|
r.ipk_value.0,
|
|
r.vendor_id,
|
|
"",
|
|
)
|
|
.map_err(|_| NocStatus::TableFull)?;
|
|
let fab_idx = self
|
|
.fabric_mgr
|
|
.borrow_mut()
|
|
.add(fabric, self.mdns)
|
|
.map_err(|_| NocStatus::TableFull)?;
|
|
|
|
self.add_acl(fab_idx, r.case_admin_subject)?;
|
|
|
|
self.failsafe.borrow_mut().record_add_noc(fab_idx)?;
|
|
|
|
Ok(fab_idx)
|
|
}
|
|
|
|
fn create_nocresponse(
|
|
encoder: CmdDataEncoder,
|
|
status_code: NocStatus,
|
|
fab_idx: u8,
|
|
debug_txt: &str,
|
|
) -> Result<(), Error> {
|
|
let cmd_data = NocResp {
|
|
status_code: status_code as u8,
|
|
fab_idx,
|
|
debug_txt: UtfStr::new(debug_txt.as_bytes()),
|
|
};
|
|
|
|
encoder
|
|
.with_command(RespCommands::NOCResp as _)?
|
|
.set(cmd_data)
|
|
}
|
|
|
|
fn handle_command_updatefablabel(
|
|
&mut self,
|
|
transaction: &mut Transaction,
|
|
data: &TLVElement,
|
|
encoder: CmdDataEncoder,
|
|
) -> Result<(), Error> {
|
|
cmd_enter!("Update Fabric Label");
|
|
let req = UpdateFabricLabelReq::from_tlv(data).map_err(Error::map_invalid_data_type)?;
|
|
let (result, fab_idx) =
|
|
if let SessionMode::Case(c) = transaction.session().get_session_mode() {
|
|
if self
|
|
.fabric_mgr
|
|
.borrow_mut()
|
|
.set_label(
|
|
c.fab_idx,
|
|
req.label.as_str().map_err(Error::map_invalid_data_type)?,
|
|
)
|
|
.is_err()
|
|
{
|
|
(NocStatus::LabelConflict, c.fab_idx)
|
|
} else {
|
|
(NocStatus::Ok, c.fab_idx)
|
|
}
|
|
} else {
|
|
// Update Fabric Label not allowed
|
|
(NocStatus::InvalidFabricIndex, 0)
|
|
};
|
|
|
|
Self::create_nocresponse(encoder, result, fab_idx, "")?;
|
|
|
|
transaction.complete();
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_command_rmfabric(
|
|
&mut self,
|
|
transaction: &mut Transaction,
|
|
data: &TLVElement,
|
|
encoder: CmdDataEncoder,
|
|
) -> Result<(), Error> {
|
|
cmd_enter!("Remove Fabric");
|
|
let req = RemoveFabricReq::from_tlv(data).map_err(Error::map_invalid_data_type)?;
|
|
if self
|
|
.fabric_mgr
|
|
.borrow_mut()
|
|
.remove(req.fab_idx, self.mdns)
|
|
.is_ok()
|
|
{
|
|
let _ = self.acl_mgr.borrow_mut().delete_for_fabric(req.fab_idx);
|
|
transaction.terminate();
|
|
Ok(())
|
|
} else {
|
|
Self::create_nocresponse(encoder, NocStatus::InvalidFabricIndex, req.fab_idx, "")
|
|
}
|
|
}
|
|
|
|
fn handle_command_addnoc(
|
|
&mut self,
|
|
transaction: &mut Transaction,
|
|
data: &TLVElement,
|
|
encoder: CmdDataEncoder,
|
|
) -> Result<(), Error> {
|
|
cmd_enter!("AddNOC");
|
|
|
|
let (status, fab_idx) = match self._handle_command_addnoc(transaction, data) {
|
|
Ok(fab_idx) => (NocStatus::Ok, fab_idx),
|
|
Err(NocError::Status(status)) => (status, 0),
|
|
Err(NocError::Error(error)) => Err(error)?,
|
|
};
|
|
|
|
Self::create_nocresponse(encoder, status, fab_idx, "")?;
|
|
transaction.complete();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_command_attrequest(
|
|
&mut self,
|
|
transaction: &mut Transaction,
|
|
data: &TLVElement,
|
|
encoder: CmdDataEncoder,
|
|
) -> Result<(), Error> {
|
|
cmd_enter!("AttestationRequest");
|
|
|
|
let req = CommonReq::from_tlv(data).map_err(Error::map_invalid_command)?;
|
|
info!("Received Attestation Nonce:{:?}", req.str);
|
|
|
|
let mut attest_challenge = [0u8; crypto::SYMM_KEY_LEN_BYTES];
|
|
attest_challenge.copy_from_slice(transaction.session().get_att_challenge());
|
|
|
|
let mut writer = encoder.with_command(RespCommands::AttReqResp as _)?;
|
|
|
|
let mut buf: [u8; RESP_MAX] = [0; RESP_MAX];
|
|
let mut attest_element = WriteBuf::new(&mut buf);
|
|
writer.start_struct(CmdDataWriter::TAG)?;
|
|
add_attestation_element(
|
|
self.epoch,
|
|
self.dev_att,
|
|
req.str.0,
|
|
&mut attest_element,
|
|
&mut writer,
|
|
)?;
|
|
add_attestation_signature(
|
|
self.dev_att,
|
|
&mut attest_element,
|
|
&attest_challenge,
|
|
&mut writer,
|
|
)?;
|
|
writer.end_container()?;
|
|
|
|
writer.complete()?;
|
|
|
|
transaction.complete();
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_command_certchainrequest(
|
|
&mut self,
|
|
transaction: &mut Transaction,
|
|
data: &TLVElement,
|
|
encoder: CmdDataEncoder,
|
|
) -> Result<(), Error> {
|
|
cmd_enter!("CertChainRequest");
|
|
|
|
info!("Received data: {}", data);
|
|
let cert_type = get_certchainrequest_params(data).map_err(Error::map_invalid_command)?;
|
|
|
|
let mut buf: [u8; RESP_MAX] = [0; RESP_MAX];
|
|
let len = self.dev_att.get_devatt_data(cert_type, &mut buf)?;
|
|
let buf = &buf[0..len];
|
|
|
|
let cmd_data = CertChainResp {
|
|
cert: OctetStr::new(buf),
|
|
};
|
|
|
|
encoder
|
|
.with_command(RespCommands::CertChainResp as _)?
|
|
.set(cmd_data)?;
|
|
|
|
transaction.complete();
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_command_csrrequest(
|
|
&mut self,
|
|
transaction: &mut Transaction,
|
|
data: &TLVElement,
|
|
encoder: CmdDataEncoder,
|
|
) -> Result<(), Error> {
|
|
cmd_enter!("CSRRequest");
|
|
|
|
let req = CommonReq::from_tlv(data).map_err(Error::map_invalid_command)?;
|
|
info!("Received CSR Nonce:{:?}", req.str);
|
|
|
|
if !self.failsafe.borrow().is_armed() {
|
|
Err(ErrorCode::UnsupportedAccess)?;
|
|
}
|
|
|
|
let noc_keypair = KeyPair::new(self.rand)?;
|
|
let mut attest_challenge = [0u8; crypto::SYMM_KEY_LEN_BYTES];
|
|
attest_challenge.copy_from_slice(transaction.session().get_att_challenge());
|
|
|
|
let mut writer = encoder.with_command(RespCommands::CSRResp as _)?;
|
|
|
|
let mut buf: [u8; RESP_MAX] = [0; RESP_MAX];
|
|
let mut nocsr_element = WriteBuf::new(&mut buf);
|
|
writer.start_struct(CmdDataWriter::TAG)?;
|
|
add_nocsrelement(&noc_keypair, req.str.0, &mut nocsr_element, &mut writer)?;
|
|
add_attestation_signature(
|
|
self.dev_att,
|
|
&mut nocsr_element,
|
|
&attest_challenge,
|
|
&mut writer,
|
|
)?;
|
|
writer.end_container()?;
|
|
|
|
writer.complete()?;
|
|
|
|
let noc_data = NocData::new(noc_keypair);
|
|
// Store this in the session data instead of cluster data, so it gets cleared
|
|
// if the session goes away for some reason
|
|
transaction.session_mut().set_noc_data(noc_data);
|
|
|
|
transaction.complete();
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_command_addtrustedrootcert(
|
|
&mut self,
|
|
transaction: &mut Transaction,
|
|
data: &TLVElement,
|
|
) -> Result<(), Error> {
|
|
cmd_enter!("AddTrustedRootCert");
|
|
if !self.failsafe.borrow().is_armed() {
|
|
Err(ErrorCode::UnsupportedAccess)?;
|
|
}
|
|
|
|
// This may happen on CASE or PASE. For PASE, the existence of NOC Data is necessary
|
|
match transaction.session().get_session_mode() {
|
|
SessionMode::Case(_) => error!("CASE: AddTrustedRootCert handling pending"), // For a CASE Session, we just return success for now,
|
|
SessionMode::Pase => {
|
|
let noc_data = transaction
|
|
.session_mut()
|
|
.get_noc_data()
|
|
.ok_or(ErrorCode::NoSession)?;
|
|
|
|
let req = CommonReq::from_tlv(data).map_err(Error::map_invalid_command)?;
|
|
info!("Received Trusted Cert:{:x?}", req.str);
|
|
|
|
noc_data.root_ca =
|
|
heapless::Vec::from_slice(req.str.0).map_err(|_| ErrorCode::BufferTooSmall)?;
|
|
// TODO
|
|
}
|
|
_ => (),
|
|
}
|
|
|
|
transaction.complete();
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<'a> Handler for NocCluster<'a> {
|
|
fn read(&self, attr: &AttrDetails, encoder: AttrDataEncoder) -> Result<(), Error> {
|
|
NocCluster::read(self, attr, encoder)
|
|
}
|
|
|
|
fn invoke(
|
|
&mut self,
|
|
transaction: &mut Transaction,
|
|
cmd: &CmdDetails,
|
|
data: &TLVElement,
|
|
encoder: CmdDataEncoder,
|
|
) -> Result<(), Error> {
|
|
NocCluster::invoke(self, transaction, cmd, data, encoder)
|
|
}
|
|
}
|
|
|
|
impl<'a> NonBlockingHandler for NocCluster<'a> {}
|
|
|
|
impl<'a> ChangeNotifier<()> for NocCluster<'a> {
|
|
fn consume_change(&mut self) -> Option<()> {
|
|
self.data_ver.consume_change(())
|
|
}
|
|
}
|
|
|
|
fn add_attestation_element(
|
|
epoch: Epoch,
|
|
dev_att: &dyn DevAttDataFetcher,
|
|
att_nonce: &[u8],
|
|
write_buf: &mut WriteBuf,
|
|
t: &mut TLVWriter,
|
|
) -> Result<(), Error> {
|
|
let mut cert_dec: [u8; MAX_CERT_DECLARATION_LEN] = [0; MAX_CERT_DECLARATION_LEN];
|
|
let len = dev_att.get_devatt_data(dev_att::DataType::CertDeclaration, &mut cert_dec)?;
|
|
let cert_dec = &cert_dec[0..len];
|
|
|
|
let epoch = epoch().as_secs() as u32;
|
|
let mut writer = TLVWriter::new(write_buf);
|
|
writer.start_struct(TagType::Anonymous)?;
|
|
writer.str16(TagType::Context(1), cert_dec)?;
|
|
writer.str8(TagType::Context(2), att_nonce)?;
|
|
writer.u32(TagType::Context(3), epoch)?;
|
|
writer.end_container()?;
|
|
|
|
t.str16(TagType::Context(0), write_buf.as_slice())
|
|
}
|
|
|
|
fn add_attestation_signature(
|
|
dev_att: &dyn DevAttDataFetcher,
|
|
attest_element: &mut WriteBuf,
|
|
attest_challenge: &[u8],
|
|
resp: &mut TLVWriter,
|
|
) -> Result<(), Error> {
|
|
let dac_key = {
|
|
let mut pubkey = [0_u8; crypto::EC_POINT_LEN_BYTES];
|
|
let mut privkey = [0_u8; crypto::BIGNUM_LEN_BYTES];
|
|
dev_att.get_devatt_data(dev_att::DataType::DACPubKey, &mut pubkey)?;
|
|
dev_att.get_devatt_data(dev_att::DataType::DACPrivKey, &mut privkey)?;
|
|
KeyPair::new_from_components(&pubkey, &privkey)
|
|
}?;
|
|
attest_element.copy_from_slice(attest_challenge)?;
|
|
let mut signature = [0u8; crypto::EC_SIGNATURE_LEN_BYTES];
|
|
dac_key.sign_msg(attest_element.as_slice(), &mut signature)?;
|
|
resp.str8(TagType::Context(1), &signature)
|
|
}
|
|
|
|
fn add_nocsrelement(
|
|
noc_keypair: &KeyPair,
|
|
csr_nonce: &[u8],
|
|
write_buf: &mut WriteBuf,
|
|
resp: &mut TLVWriter,
|
|
) -> Result<(), Error> {
|
|
let mut csr: [u8; MAX_CSR_LEN] = [0; MAX_CSR_LEN];
|
|
let csr = noc_keypair.get_csr(&mut csr)?;
|
|
let mut writer = TLVWriter::new(write_buf);
|
|
writer.start_struct(TagType::Anonymous)?;
|
|
writer.str8(TagType::Context(1), csr)?;
|
|
writer.str8(TagType::Context(2), csr_nonce)?;
|
|
writer.end_container()?;
|
|
|
|
resp.str8(TagType::Context(0), write_buf.as_slice())
|
|
}
|
|
|
|
fn get_certchainrequest_params(data: &TLVElement) -> Result<DataType, Error> {
|
|
let cert_type = CertChainReq::from_tlv(data)?.cert_type;
|
|
|
|
const CERT_TYPE_DAC: u8 = 1;
|
|
const CERT_TYPE_PAI: u8 = 2;
|
|
info!("Received Cert Type:{:?}", cert_type);
|
|
match cert_type {
|
|
CERT_TYPE_DAC => Ok(dev_att::DataType::DAC),
|
|
CERT_TYPE_PAI => Ok(dev_att::DataType::PAI),
|
|
_ => Err(ErrorCode::Invalid.into()),
|
|
}
|
|
}
|