Skip to main content

matc/clusters/codec/
network_identity_management.rs

1//! Matter TLV encoders and decoders for Network Identity Management Cluster
2//! Cluster ID: 0x0450
3//!
4//! This file is automatically generated from NetworkIdentityManagement.xml
5
6#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13// Import serialization helpers for octet strings
14use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
15
16// Enum definitions
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
19#[repr(u8)]
20pub enum IdentityType {
21    /// ECDSA on the elliptic curve secp256r1
22    Ecdsa = 0,
23}
24
25impl IdentityType {
26    /// Convert from u8 value
27    pub fn from_u8(value: u8) -> Option<Self> {
28        match value {
29            0 => Some(IdentityType::Ecdsa),
30            _ => None,
31        }
32    }
33
34    /// Convert to u8 value
35    pub fn to_u8(self) -> u8 {
36        self as u8
37    }
38}
39
40impl From<IdentityType> for u8 {
41    fn from(val: IdentityType) -> Self {
42        val as u8
43    }
44}
45
46// Struct definitions
47
48#[derive(Debug, serde::Serialize)]
49pub struct ActiveNetworkIdentity {
50    pub index: Option<u16>,
51    pub type_: Option<IdentityType>,
52    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
53    pub identifier: Option<Vec<u8>>,
54    pub created_timestamp: Option<u64>,
55    pub current: Option<bool>,
56    pub remaining_clients: Option<u16>,
57}
58
59#[derive(Debug, serde::Serialize)]
60pub struct Client {
61    pub client_index: Option<u16>,
62    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
63    pub client_identifier: Option<Vec<u8>>,
64    pub network_identity_index: Option<u16>,
65}
66
67// Command encoders
68
69/// Encode AddClient command (0x00)
70pub fn encode_add_client(client_identity: Vec<u8>) -> anyhow::Result<Vec<u8>> {
71    let tlv = tlv::TlvItemEnc {
72        tag: 0,
73        value: tlv::TlvItemValueEnc::StructInvisible(vec![
74        (0, tlv::TlvItemValueEnc::OctetString(client_identity)).into(),
75        ]),
76    };
77    Ok(tlv.encode()?)
78}
79
80/// Encode RemoveClient command (0x02)
81pub fn encode_remove_client(client_index: Option<u16>, client_identifier: Option<Vec<u8>>) -> anyhow::Result<Vec<u8>> {
82    let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
83    if let Some(x) = client_index { tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
84    if let Some(x) = client_identifier { tlv_fields.push((1, tlv::TlvItemValueEnc::OctetString(x)).into()); }
85    let tlv = tlv::TlvItemEnc {
86        tag: 0,
87        value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
88    };
89    Ok(tlv.encode()?)
90}
91
92/// Encode QueryIdentity command (0x03)
93pub fn encode_query_identity(network_identity_index: Option<u16>, network_identity_type: Option<IdentityType>, identifier: Option<Vec<u8>>) -> anyhow::Result<Vec<u8>> {
94    let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
95    if let Some(x) = network_identity_index { tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
96    if let Some(x) = network_identity_type { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
97    if let Some(x) = identifier { tlv_fields.push((2, tlv::TlvItemValueEnc::OctetString(x)).into()); }
98    let tlv = tlv::TlvItemEnc {
99        tag: 0,
100        value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
101    };
102    Ok(tlv.encode()?)
103}
104
105/// Encode ImportAdminSecret command (0x40)
106pub fn encode_import_admin_secret(network_administrator_shared_secret: Vec<u8>) -> anyhow::Result<Vec<u8>> {
107    let tlv = tlv::TlvItemEnc {
108        tag: 0,
109        value: tlv::TlvItemValueEnc::StructInvisible(vec![
110        (0, tlv::TlvItemValueEnc::OctetString(network_administrator_shared_secret)).into(),
111        ]),
112    };
113    Ok(tlv.encode()?)
114}
115
116// Attribute decoders
117
118/// Decode ActiveNetworkIdentities attribute (0x0000)
119pub fn decode_active_network_identities(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<ActiveNetworkIdentity>> {
120    let mut res = Vec::new();
121    if let tlv::TlvItemValue::List(v) = inp {
122        for item in v {
123            res.push(ActiveNetworkIdentity {
124                index: item.get_int(&[0]).map(|v| v as u16),
125                type_: item.get_int(&[1]).and_then(|v| IdentityType::from_u8(v as u8)),
126                identifier: item.get_octet_string_owned(&[2]),
127                created_timestamp: item.get_int(&[3]),
128                current: item.get_bool(&[4]),
129                remaining_clients: item.get_int(&[5]).map(|v| v as u16),
130            });
131        }
132    }
133    Ok(res)
134}
135
136/// Decode Clients attribute (0x0001)
137pub fn decode_clients(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Client>> {
138    let mut res = Vec::new();
139    if let tlv::TlvItemValue::List(v) = inp {
140        for item in v {
141            res.push(Client {
142                client_index: item.get_int(&[0]).map(|v| v as u16),
143                client_identifier: item.get_octet_string_owned(&[1]),
144                network_identity_index: item.get_int(&[2]).map(|v| v as u16),
145            });
146        }
147    }
148    Ok(res)
149}
150
151/// Decode ClientTableSize attribute (0x0002)
152pub fn decode_client_table_size(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
153    if let tlv::TlvItemValue::Int(v) = inp {
154        Ok(*v as u16)
155    } else {
156        Err(anyhow::anyhow!("Expected UInt16"))
157    }
158}
159
160
161// JSON dispatcher function
162
163/// Decode attribute value and return as JSON string
164///
165/// # Parameters
166/// * `cluster_id` - The cluster identifier
167/// * `attribute_id` - The attribute identifier
168/// * `tlv_value` - The TLV value to decode
169///
170/// # Returns
171/// JSON string representation of the decoded value or error
172pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
173    // Verify this is the correct cluster
174    if cluster_id != 0x0450 {
175        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0450, got {}\"}}", cluster_id);
176    }
177
178    match attribute_id {
179        0x0000 => {
180            match decode_active_network_identities(tlv_value) {
181                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
182                Err(e) => format!("{{\"error\": \"{}\"}}", e),
183            }
184        }
185        0x0001 => {
186            match decode_clients(tlv_value) {
187                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
188                Err(e) => format!("{{\"error\": \"{}\"}}", e),
189            }
190        }
191        0x0002 => {
192            match decode_client_table_size(tlv_value) {
193                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
194                Err(e) => format!("{{\"error\": \"{}\"}}", e),
195            }
196        }
197        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
198    }
199}
200
201/// Get list of all attributes supported by this cluster
202///
203/// # Returns
204/// Vector of tuples containing (attribute_id, attribute_name)
205pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
206    vec![
207        (0x0000, "ActiveNetworkIdentities"),
208        (0x0001, "Clients"),
209        (0x0002, "ClientTableSize"),
210    ]
211}
212
213// Command listing
214
215pub fn get_command_list() -> Vec<(u32, &'static str)> {
216    vec![
217        (0x00, "AddClient"),
218        (0x02, "RemoveClient"),
219        (0x03, "QueryIdentity"),
220        (0x40, "ImportAdminSecret"),
221        (0x41, "ExportAdminSecret"),
222    ]
223}
224
225pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
226    match cmd_id {
227        0x00 => Some("AddClient"),
228        0x02 => Some("RemoveClient"),
229        0x03 => Some("QueryIdentity"),
230        0x40 => Some("ImportAdminSecret"),
231        0x41 => Some("ExportAdminSecret"),
232        _ => None,
233    }
234}
235
236pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
237    match cmd_id {
238        0x00 => Some(vec![
239            crate::clusters::codec::CommandField { tag: 0, name: "client_identity", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
240        ]),
241        0x02 => Some(vec![
242            crate::clusters::codec::CommandField { tag: 0, name: "client_index", kind: crate::clusters::codec::FieldKind::U16, optional: true, nullable: false },
243            crate::clusters::codec::CommandField { tag: 1, name: "client_identifier", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: false },
244        ]),
245        0x03 => Some(vec![
246            crate::clusters::codec::CommandField { tag: 0, name: "network_identity_index", kind: crate::clusters::codec::FieldKind::U16, optional: true, nullable: false },
247            crate::clusters::codec::CommandField { tag: 1, name: "network_identity_type", kind: crate::clusters::codec::FieldKind::Enum { name: "IdentityType", variants: &[(0, "Ecdsa")] }, optional: true, nullable: false },
248            crate::clusters::codec::CommandField { tag: 2, name: "identifier", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: false },
249        ]),
250        0x40 => Some(vec![
251            crate::clusters::codec::CommandField { tag: 0, name: "network_administrator_shared_secret", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
252        ]),
253        0x41 => Some(vec![]),
254        _ => None,
255    }
256}
257
258pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
259    match cmd_id {
260        0x00 => {
261        let client_identity = crate::clusters::codec::json_util::get_octstr(args, "client_identity")?;
262        encode_add_client(client_identity)
263        }
264        0x02 => {
265        let client_index = crate::clusters::codec::json_util::get_opt_u16(args, "client_index")?;
266        let client_identifier = crate::clusters::codec::json_util::get_opt_octstr(args, "client_identifier")?;
267        encode_remove_client(client_index, client_identifier)
268        }
269        0x03 => {
270        let network_identity_index = crate::clusters::codec::json_util::get_opt_u16(args, "network_identity_index")?;
271        let network_identity_type = crate::clusters::codec::json_util::get_opt_u64(args, "network_identity_type")?
272            .and_then(|n| IdentityType::from_u8(n as u8));
273        let identifier = crate::clusters::codec::json_util::get_opt_octstr(args, "identifier")?;
274        encode_query_identity(network_identity_index, network_identity_type, identifier)
275        }
276        0x40 => {
277        let network_administrator_shared_secret = crate::clusters::codec::json_util::get_octstr(args, "network_administrator_shared_secret")?;
278        encode_import_admin_secret(network_administrator_shared_secret)
279        }
280        0x41 => Ok(vec![]),
281        _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
282    }
283}
284
285#[derive(Debug, serde::Serialize)]
286pub struct AddClientResponse {
287    pub client_index: Option<u16>,
288}
289
290#[derive(Debug, serde::Serialize)]
291pub struct QueryIdentityResponse {
292    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
293    pub identity: Option<Vec<u8>>,
294}
295
296#[derive(Debug, serde::Serialize)]
297pub struct ExportAdminSecretResponse {
298    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
299    pub network_administrator_shared_secret: Option<Vec<u8>>,
300}
301
302// Command response decoders
303
304/// Decode AddClientResponse command response (01)
305pub fn decode_add_client_response(inp: &tlv::TlvItemValue) -> anyhow::Result<AddClientResponse> {
306    if let tlv::TlvItemValue::List(_fields) = inp {
307        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
308        Ok(AddClientResponse {
309                client_index: item.get_int(&[0]).map(|v| v as u16),
310        })
311    } else {
312        Err(anyhow::anyhow!("Expected struct fields"))
313    }
314}
315
316/// Decode QueryIdentityResponse command response (04)
317pub fn decode_query_identity_response(inp: &tlv::TlvItemValue) -> anyhow::Result<QueryIdentityResponse> {
318    if let tlv::TlvItemValue::List(_fields) = inp {
319        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
320        Ok(QueryIdentityResponse {
321                identity: item.get_octet_string_owned(&[0]),
322        })
323    } else {
324        Err(anyhow::anyhow!("Expected struct fields"))
325    }
326}
327
328/// Decode ExportAdminSecretResponse command response (42)
329pub fn decode_export_admin_secret_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ExportAdminSecretResponse> {
330    if let tlv::TlvItemValue::List(_fields) = inp {
331        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
332        Ok(ExportAdminSecretResponse {
333                network_administrator_shared_secret: item.get_octet_string_owned(&[0]),
334        })
335    } else {
336        Err(anyhow::anyhow!("Expected struct fields"))
337    }
338}
339
340// Typed facade (invokes + reads)
341
342/// Invoke `AddClient` command on cluster `Network Identity Management`.
343pub async fn add_client(conn: &crate::controller::Connection, endpoint: u16, client_identity: Vec<u8>) -> anyhow::Result<AddClientResponse> {
344    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_IDENTITY_MANAGEMENT, crate::clusters::defs::CLUSTER_NETWORK_IDENTITY_MANAGEMENT_CMD_ID_ADDCLIENT, &encode_add_client(client_identity)?).await?;
345    decode_add_client_response(&tlv)
346}
347
348/// Invoke `RemoveClient` command on cluster `Network Identity Management`.
349pub async fn remove_client(conn: &crate::controller::Connection, endpoint: u16, client_index: Option<u16>, client_identifier: Option<Vec<u8>>) -> anyhow::Result<()> {
350    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_IDENTITY_MANAGEMENT, crate::clusters::defs::CLUSTER_NETWORK_IDENTITY_MANAGEMENT_CMD_ID_REMOVECLIENT, &encode_remove_client(client_index, client_identifier)?).await?;
351    Ok(())
352}
353
354/// Invoke `QueryIdentity` command on cluster `Network Identity Management`.
355pub async fn query_identity(conn: &crate::controller::Connection, endpoint: u16, network_identity_index: Option<u16>, network_identity_type: Option<IdentityType>, identifier: Option<Vec<u8>>) -> anyhow::Result<QueryIdentityResponse> {
356    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_IDENTITY_MANAGEMENT, crate::clusters::defs::CLUSTER_NETWORK_IDENTITY_MANAGEMENT_CMD_ID_QUERYIDENTITY, &encode_query_identity(network_identity_index, network_identity_type, identifier)?).await?;
357    decode_query_identity_response(&tlv)
358}
359
360/// Invoke `ImportAdminSecret` command on cluster `Network Identity Management`.
361pub async fn import_admin_secret(conn: &crate::controller::Connection, endpoint: u16, network_administrator_shared_secret: Vec<u8>) -> anyhow::Result<()> {
362    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_IDENTITY_MANAGEMENT, crate::clusters::defs::CLUSTER_NETWORK_IDENTITY_MANAGEMENT_CMD_ID_IMPORTADMINSECRET, &encode_import_admin_secret(network_administrator_shared_secret)?).await?;
363    Ok(())
364}
365
366/// Invoke `ExportAdminSecret` command on cluster `Network Identity Management`.
367pub async fn export_admin_secret(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ExportAdminSecretResponse> {
368    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_IDENTITY_MANAGEMENT, crate::clusters::defs::CLUSTER_NETWORK_IDENTITY_MANAGEMENT_CMD_ID_EXPORTADMINSECRET, &[]).await?;
369    decode_export_admin_secret_response(&tlv)
370}
371
372/// Read `ActiveNetworkIdentities` attribute from cluster `Network Identity Management`.
373pub async fn read_active_network_identities(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<ActiveNetworkIdentity>> {
374    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_IDENTITY_MANAGEMENT, crate::clusters::defs::CLUSTER_NETWORK_IDENTITY_MANAGEMENT_ATTR_ID_ACTIVENETWORKIDENTITIES).await?;
375    decode_active_network_identities(&tlv)
376}
377
378/// Read `Clients` attribute from cluster `Network Identity Management`.
379pub async fn read_clients(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<Client>> {
380    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_IDENTITY_MANAGEMENT, crate::clusters::defs::CLUSTER_NETWORK_IDENTITY_MANAGEMENT_ATTR_ID_CLIENTS).await?;
381    decode_clients(&tlv)
382}
383
384/// Read `ClientTableSize` attribute from cluster `Network Identity Management`.
385pub async fn read_client_table_size(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
386    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_IDENTITY_MANAGEMENT, crate::clusters::defs::CLUSTER_NETWORK_IDENTITY_MANAGEMENT_ATTR_ID_CLIENTTABLESIZE).await?;
387    decode_client_table_size(&tlv)
388}
389