matc/device/
types.rs

1use anyhow::{Context, Result};
2
3use crate::{fabric, sigma, spake2p, tlv};
4
5#[derive(Clone)]
6pub struct DeviceConfig {
7    pub pin: u32,
8    pub discriminator: u16,
9    pub listen_address: String,
10    pub vendor_id: u16,
11    pub product_id: u16,
12    pub dac_cert_path: String,
13    pub pai_cert_path: String,
14    pub dac_key_path: String,
15    pub hostname: String,
16    /// If Some, load/save commissioned state from this directory.
17    pub state_dir: Option<String>,
18    pub vendor_name: String,
19    pub product_name: String,
20    pub hardware_version: u16,
21    pub software_version: u32,
22    pub serial_number: String,
23    pub unique_id: String,
24}
25
26/// Serde helper: serialize `Vec<u8>` as a lowercase hex string.
27mod hex_bytes {
28    use serde::{Deserialize, Deserializer, Serializer};
29
30    pub fn serialize<S: Serializer>(bytes: &[u8], s: S) -> Result<S::Ok, S::Error> {
31        s.serialize_str(&hex::encode(bytes))
32    }
33
34    pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
35        let s = String::deserialize(d)?;
36        hex::decode(&s).map_err(serde::de::Error::custom)
37    }
38}
39
40/// Serde helper: serialize `Option<Vec<u8>>` as an optional lowercase hex string.
41mod hex_bytes_opt {
42    use serde::{Deserialize, Deserializer, Serializer};
43
44    pub fn serialize<S: Serializer>(bytes: &Option<Vec<u8>>, s: S) -> Result<S::Ok, S::Error> {
45        match bytes {
46            Some(b) => s.serialize_some(&hex::encode(b)),
47            None => s.serialize_none(),
48        }
49    }
50
51    pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Vec<u8>>, D::Error> {
52        let opt: Option<String> = Option::deserialize(d)?;
53        opt.map(|s| hex::decode(&s).map_err(serde::de::Error::custom))
54            .transpose()
55    }
56}
57
58/// Per-fabric state in the persisted JSON.
59#[derive(serde::Serialize, serde::Deserialize)]
60pub struct PersistedFabricState {
61    pub fabric_index: u8,
62    #[serde(with = "hex_bytes")]
63    pub trusted_root_cert: Vec<u8>,
64    #[serde(with = "hex_bytes")]
65    pub noc: Vec<u8>,
66    #[serde(with = "hex_bytes_opt")]
67    pub icac: Option<Vec<u8>>,
68    #[serde(with = "hex_bytes")]
69    pub ipk: Vec<u8>,
70    pub controller_id: u64,
71    pub vendor_id: u16,
72    #[serde(with = "hex_bytes")]
73    pub device_matter_cert: Vec<u8>,
74    pub label: String,
75}
76
77/// Full commissioned state serialized to disk.
78#[derive(serde::Serialize, serde::Deserialize)]
79pub struct PersistedDeviceState {
80    pub operational_key_hex: String,
81    pub next_fabric_index: u8,
82    pub fabrics: Vec<PersistedFabricState>,
83    pub attribute_overrides: Vec<AttributeOverride>,
84}
85
86/// A single attribute value snapshot, encoded as hex TLV.
87#[derive(serde::Serialize, serde::Deserialize)]
88pub struct AttributeOverride {
89    pub endpoint: u16,
90    pub cluster: u32,
91    pub attribute: u32,
92    pub tlv_hex: String,
93}
94
95pub(crate) struct PaseState {
96    pub(crate) engine: spake2p::Engine,
97    pub(crate) verifier: spake2p::Verifier,
98    #[allow(dead_code)]
99    pub(crate) exchange_id: u16,
100    pub(crate) pbkdf_req_payload: Vec<u8>,
101    pub(crate) pbkdf_resp_payload: Vec<u8>,
102    pub(crate) responder_session_id: u16,
103    pub(crate) initiator_session_id: u16,
104}
105
106pub(crate) struct CaseState {
107    pub(crate) sigma2_ctx: sigma::Sigma2ResponseCtx,
108    #[allow(dead_code)]
109    pub(crate) exchange_id: u16,
110    /// Which fabric is being established in this CASE exchange.
111    pub(crate) fabric_index: u8,
112}
113
114/// Tracks which attributes a subscriber is watching.
115#[derive(Clone)]
116pub(crate) enum SubscribedPaths {
117    /// Wildcard subscribe (no AttributeRequests in the subscribe message).
118    All,
119    /// Resolved concrete (endpoint, cluster, attribute) keys.
120    Specific(Vec<(u16, u32, u32)>),
121}
122
123pub(crate) struct SubscribeState {
124    pub(crate) exchange_id: u16,
125    pub(crate) subscription_id: u32,
126    pub(crate) paths: SubscribedPaths,
127    pub(crate) max_interval_secs: u16,
128}
129
130pub(crate) struct ActiveSubscription {
131    pub(crate) subscription_id: u32,
132    pub(crate) session_id: u16,
133    pub(crate) peer_addr: std::net::SocketAddr,
134    pub(crate) max_interval_secs: u16,
135    pub(crate) paths: SubscribedPaths,
136}
137
138pub(crate) struct PendingChunkState {
139    pub(crate) exchange_id: u16,
140    /// Reports not yet sent; drained from the front as chunks are dispatched.
141    pub(crate) remaining: Vec<crate::device_messages::AttrReport>,
142    pub(crate) subscription_id: Option<u32>,
143}
144
145pub(crate) struct FabricInfo {
146    /// 1-based fabric index assigned at AddNOC time.
147    pub(crate) fabric_index: u8,
148    pub(crate) ipk: Vec<u8>,
149    pub(crate) fabric: Option<fabric::Fabric>,
150    pub(crate) device_matter_cert: Vec<u8>,
151    pub(crate) controller_id: u64,
152    pub(crate) vendor_id: u16,
153    /// Root certificate (TLV-encoded Matter cert) — moved from Device.
154    pub(crate) trusted_root_cert: Vec<u8>,
155    /// Node Operational Certificate (TLV-encoded) — moved from Device.
156    pub(crate) noc: Vec<u8>,
157    /// Intermediate CA certificate, if provided.
158    pub(crate) icac: Option<Vec<u8>>,
159    pub(crate) label: String,
160}
161
162impl FabricInfo {
163    /// Extract the CA public key (uncompressed SEC1) from the trusted root cert TLV.
164    pub(crate) fn ca_public_key(&self) -> Result<Vec<u8>> {
165        let decoded = tlv::decode_tlv(&self.trusted_root_cert)?;
166        let pubkey = decoded
167            .get_octet_string(&[9])
168            .context("CA cert: public key (tag 9) missing")?;
169        Ok(pubkey.to_vec())
170    }
171
172    fn noc_field(&self, tag_path: &[u8], field_name: &str) -> Result<u64> {
173        let decoded = tlv::decode_tlv(&self.noc)?;
174        decoded
175            .get_int(tag_path)
176            .with_context(|| format!("NOC: {} missing from subject", field_name))
177    }
178
179    pub(crate) fn fabric_id(&self) -> Result<u64> {
180        self.noc_field(&[6u8, 21], "fabric_id")
181    }
182
183    pub(crate) fn device_node_id(&self) -> Result<u64> {
184        self.noc_field(&[6u8, 17], "node_id")
185    }
186
187    pub(crate) fn ca_id(&self) -> Result<u64> {
188        let decoded = tlv::decode_tlv(&self.trusted_root_cert)?;
189        decoded
190            .get_int(&[6, 20])
191            .context("CA cert: ca_id missing from subject")
192    }
193}