1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
19#[repr(u8)]
20pub enum IdentityType {
21 Ecdsa = 0,
23}
24
25impl IdentityType {
26 pub fn from_u8(value: u8) -> Option<Self> {
28 match value {
29 0 => Some(IdentityType::Ecdsa),
30 _ => None,
31 }
32 }
33
34 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#[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
67pub 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
80pub 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
92pub 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
105pub 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
116pub 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
136pub 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
151pub 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
161pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
173 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
201pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
206 vec![
207 (0x0000, "ActiveNetworkIdentities"),
208 (0x0001, "Clients"),
209 (0x0002, "ClientTableSize"),
210 ]
211}
212
213pub 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
302pub 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
316pub 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
328pub 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
340pub 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
348pub 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
354pub 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
360pub 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
366pub 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
372pub 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
378pub 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
384pub 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