use std::net::{Ipv4Addr, Ipv6Addr};
use anyhow::{Context, Result};
use crate::{fabric, sigma, spake2p, tlv};
#[derive(Clone)]
pub struct DeviceConfig {
pub pin: u32,
pub discriminator: u16,
pub listen_address: String,
pub vendor_id: u16,
pub product_id: u16,
pub dac_cert_path: String,
pub pai_cert_path: String,
pub dac_key_path: String,
pub hostname: String,
pub state_dir: Option<String>,
pub vendor_name: String,
pub product_name: String,
pub hardware_version: u16,
pub software_version: u32,
pub serial_number: String,
pub unique_id: String,
pub advertise_addresses: Option<Vec<std::net::IpAddr>>,
}
impl DeviceConfig {
pub fn split_advertise_ips(&self) -> (Option<Vec<Ipv4Addr>>, Option<Vec<Ipv6Addr>>) {
match &self.advertise_addresses {
None => (None, None),
Some(addrs) => {
let v4: Vec<Ipv4Addr> = addrs.iter().filter_map(|a| match a {
std::net::IpAddr::V4(ip) => Some(*ip),
_ => None,
}).collect();
let v6: Vec<Ipv6Addr> = addrs.iter().filter_map(|a| match a {
std::net::IpAddr::V6(ip) => Some(*ip),
_ => None,
}).collect();
(Some(v4), Some(v6))
}
}
}
}
mod hex_bytes {
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S: Serializer>(bytes: &[u8], s: S) -> Result<S::Ok, S::Error> {
s.serialize_str(&hex::encode(bytes))
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
let s = String::deserialize(d)?;
hex::decode(&s).map_err(serde::de::Error::custom)
}
}
mod hex_bytes_opt {
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S: Serializer>(bytes: &Option<Vec<u8>>, s: S) -> Result<S::Ok, S::Error> {
match bytes {
Some(b) => s.serialize_some(&hex::encode(b)),
None => s.serialize_none(),
}
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Vec<u8>>, D::Error> {
let opt: Option<String> = Option::deserialize(d)?;
opt.map(|s| hex::decode(&s).map_err(serde::de::Error::custom))
.transpose()
}
}
#[derive(serde::Serialize, serde::Deserialize)]
pub struct PersistedFabricState {
pub fabric_index: u8,
#[serde(with = "hex_bytes")]
pub trusted_root_cert: Vec<u8>,
#[serde(with = "hex_bytes")]
pub noc: Vec<u8>,
#[serde(with = "hex_bytes_opt")]
pub icac: Option<Vec<u8>>,
#[serde(with = "hex_bytes")]
pub ipk: Vec<u8>,
pub controller_id: u64,
pub vendor_id: u16,
#[serde(with = "hex_bytes")]
pub device_matter_cert: Vec<u8>,
pub label: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
pub struct PersistedDeviceState {
pub operational_key_hex: String,
pub next_fabric_index: u8,
pub fabrics: Vec<PersistedFabricState>,
pub attribute_overrides: Vec<AttributeOverride>,
}
#[derive(serde::Serialize, serde::Deserialize)]
pub struct AttributeOverride {
pub endpoint: u16,
pub cluster: u32,
pub attribute: u32,
pub tlv_hex: String,
}
pub(crate) struct PaseState {
pub(crate) engine: spake2p::Engine,
pub(crate) verifier: spake2p::Verifier,
#[allow(dead_code)]
pub(crate) exchange_id: u16,
pub(crate) pbkdf_req_payload: Vec<u8>,
pub(crate) pbkdf_resp_payload: Vec<u8>,
pub(crate) responder_session_id: u16,
pub(crate) initiator_session_id: u16,
}
pub(crate) struct CaseState {
pub(crate) sigma2_ctx: sigma::Sigma2ResponseCtx,
#[allow(dead_code)]
pub(crate) exchange_id: u16,
pub(crate) fabric_index: u8,
}
#[derive(Clone)]
pub(crate) enum SubscribedPaths {
All,
Specific(Vec<(u16, u32, u32)>),
}
pub(crate) struct SubscribeState {
pub(crate) exchange_id: u16,
pub(crate) subscription_id: u32,
pub(crate) paths: SubscribedPaths,
pub(crate) max_interval_secs: u16,
}
pub(crate) struct ActiveSubscription {
pub(crate) subscription_id: u32,
pub(crate) session_id: u16,
pub(crate) peer_addr: std::net::SocketAddr,
pub(crate) max_interval_secs: u16,
pub(crate) paths: SubscribedPaths,
}
pub(crate) struct PendingChunkState {
pub(crate) exchange_id: u16,
pub(crate) remaining: Vec<crate::device_messages::AttrReport>,
pub(crate) subscription_id: Option<u32>,
}
pub(crate) struct FabricInfo {
pub(crate) fabric_index: u8,
pub(crate) ipk: Vec<u8>,
pub(crate) fabric: Option<fabric::Fabric>,
pub(crate) device_matter_cert: Vec<u8>,
pub(crate) controller_id: u64,
pub(crate) vendor_id: u16,
pub(crate) trusted_root_cert: Vec<u8>,
pub(crate) noc: Vec<u8>,
pub(crate) icac: Option<Vec<u8>>,
pub(crate) label: String,
}
impl FabricInfo {
pub(crate) fn ca_public_key(&self) -> Result<Vec<u8>> {
let decoded = tlv::decode_tlv(&self.trusted_root_cert)?;
let pubkey = decoded
.get_octet_string(&[9])
.context("CA cert: public key (tag 9) missing")?;
Ok(pubkey.to_vec())
}
fn noc_field(&self, tag_path: &[u8], field_name: &str) -> Result<u64> {
let decoded = tlv::decode_tlv(&self.noc)?;
decoded
.get_int(tag_path)
.with_context(|| format!("NOC: {} missing from subject", field_name))
}
pub(crate) fn fabric_id(&self) -> Result<u64> {
self.noc_field(&[6u8, 21], "fabric_id")
}
pub(crate) fn device_node_id(&self) -> Result<u64> {
self.noc_field(&[6u8, 17], "node_id")
}
pub(crate) fn ca_id(&self) -> Result<u64> {
let decoded = tlv::decode_tlv(&self.trusted_root_cert)?;
decoded
.get_int(&[6, 20])
.context("CA cert: ca_id missing from subject")
}
}