Skip to main content

matc/clusters/codec/
operational_credential_cluster.rs

1//! Matter TLV encoders and decoders for Operational Credentials Cluster
2//! Cluster IDs: 0x003E
3//!
4//! This file is automatically generated from OperationalCredentialCluster.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 CertificateChainType {
21    /// Request the DER-encoded DAC certificate
22    Daccertificate = 1,
23    /// Request the DER-encoded PAI certificate
24    Paicertificate = 2,
25}
26
27impl CertificateChainType {
28    /// Convert from u8 value
29    pub fn from_u8(value: u8) -> Option<Self> {
30        match value {
31            1 => Some(CertificateChainType::Daccertificate),
32            2 => Some(CertificateChainType::Paicertificate),
33            _ => None,
34        }
35    }
36
37    /// Convert to u8 value
38    pub fn to_u8(self) -> u8 {
39        self as u8
40    }
41}
42
43impl From<CertificateChainType> for u8 {
44    fn from(val: CertificateChainType) -> Self {
45        val as u8
46    }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
50#[repr(u8)]
51pub enum NodeOperationalCertStatus {
52    /// OK, no error
53    Ok = 0,
54    /// Public Key in the NOC does not match the public key in the NOCSR
55    Invalidpublickey = 1,
56    /// The Node Operational ID in the NOC is not formatted correctly.
57    Invalidnodeopid = 2,
58    /// Any other validation error in NOC chain
59    Invalidnoc = 3,
60    /// No record of prior CSR for which this NOC could match
61    Missingcsr = 4,
62    /// NOCs table full, cannot add another one
63    Tablefull = 5,
64    /// Invalid CaseAdminSubject field for an AddNOC command.
65    Invalidadminsubject = 6,
66    /// Trying to AddNOC instead of UpdateNOC against an existing Fabric.
67    Fabricconflict = 9,
68    /// Label already exists on another Fabric.
69    Labelconflict = 10,
70    /// FabricIndex argument is invalid.
71    Invalidfabricindex = 11,
72}
73
74impl NodeOperationalCertStatus {
75    /// Convert from u8 value
76    pub fn from_u8(value: u8) -> Option<Self> {
77        match value {
78            0 => Some(NodeOperationalCertStatus::Ok),
79            1 => Some(NodeOperationalCertStatus::Invalidpublickey),
80            2 => Some(NodeOperationalCertStatus::Invalidnodeopid),
81            3 => Some(NodeOperationalCertStatus::Invalidnoc),
82            4 => Some(NodeOperationalCertStatus::Missingcsr),
83            5 => Some(NodeOperationalCertStatus::Tablefull),
84            6 => Some(NodeOperationalCertStatus::Invalidadminsubject),
85            9 => Some(NodeOperationalCertStatus::Fabricconflict),
86            10 => Some(NodeOperationalCertStatus::Labelconflict),
87            11 => Some(NodeOperationalCertStatus::Invalidfabricindex),
88            _ => None,
89        }
90    }
91
92    /// Convert to u8 value
93    pub fn to_u8(self) -> u8 {
94        self as u8
95    }
96}
97
98impl From<NodeOperationalCertStatus> for u8 {
99    fn from(val: NodeOperationalCertStatus) -> Self {
100        val as u8
101    }
102}
103
104// Struct definitions
105
106#[derive(Debug, serde::Serialize)]
107pub struct FabricDescriptor {
108    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
109    pub root_public_key: Option<Vec<u8>>,
110    pub vendor_id: Option<u16>,
111    pub fabric_id: Option<u8>,
112    pub node_id: Option<u64>,
113    pub label: Option<String>,
114    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
115    pub vid_verification_statement: Option<Vec<u8>>,
116}
117
118#[derive(Debug, serde::Serialize)]
119pub struct NOC {
120    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
121    pub noc: Option<Vec<u8>>,
122    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
123    pub icac: Option<Vec<u8>>,
124    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
125    pub vvsc: Option<Vec<u8>>,
126}
127
128// Command encoders
129
130/// Encode AttestationRequest command (0x00)
131pub fn encode_attestation_request(attestation_nonce: Vec<u8>) -> anyhow::Result<Vec<u8>> {
132    let tlv = tlv::TlvItemEnc {
133        tag: 0,
134        value: tlv::TlvItemValueEnc::StructInvisible(vec![
135        (0, tlv::TlvItemValueEnc::OctetString(attestation_nonce)).into(),
136        ]),
137    };
138    Ok(tlv.encode()?)
139}
140
141/// Encode CertificateChainRequest command (0x02)
142pub fn encode_certificate_chain_request(certificate_type: CertificateChainType) -> anyhow::Result<Vec<u8>> {
143    let tlv = tlv::TlvItemEnc {
144        tag: 0,
145        value: tlv::TlvItemValueEnc::StructInvisible(vec![
146        (0, tlv::TlvItemValueEnc::UInt8(certificate_type.to_u8())).into(),
147        ]),
148    };
149    Ok(tlv.encode()?)
150}
151
152/// Encode CSRRequest command (0x04)
153pub fn encode_csr_request(csr_nonce: Vec<u8>, is_for_update_noc: Option<bool>) -> anyhow::Result<Vec<u8>> {
154    let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
155    tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(csr_nonce)).into());
156    if let Some(x) = is_for_update_noc { tlv_fields.push((1, tlv::TlvItemValueEnc::Bool(x)).into()); }
157    let tlv = tlv::TlvItemEnc {
158        tag: 0,
159        value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
160    };
161    Ok(tlv.encode()?)
162}
163
164/// Encode AddNOC command (0x06)
165pub fn encode_add_noc(noc_value: Vec<u8>, icac_value: Option<Vec<u8>>, ipk_value: Vec<u8>, case_admin_subject: u64, admin_vendor_id: u16) -> anyhow::Result<Vec<u8>> {
166    let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
167    tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(noc_value)).into());
168    if let Some(x) = icac_value { tlv_fields.push((1, tlv::TlvItemValueEnc::OctetString(x)).into()); }
169    tlv_fields.push((2, tlv::TlvItemValueEnc::OctetString(ipk_value)).into());
170    tlv_fields.push((3, tlv::TlvItemValueEnc::UInt64(case_admin_subject)).into());
171    tlv_fields.push((4, tlv::TlvItemValueEnc::UInt16(admin_vendor_id)).into());
172    let tlv = tlv::TlvItemEnc {
173        tag: 0,
174        value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
175    };
176    Ok(tlv.encode()?)
177}
178
179/// Encode UpdateNOC command (0x07)
180pub fn encode_update_noc(noc_value: Vec<u8>, icac_value: Option<Vec<u8>>) -> anyhow::Result<Vec<u8>> {
181    let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
182    tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(noc_value)).into());
183    if let Some(x) = icac_value { tlv_fields.push((1, tlv::TlvItemValueEnc::OctetString(x)).into()); }
184    let tlv = tlv::TlvItemEnc {
185        tag: 0,
186        value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
187    };
188    Ok(tlv.encode()?)
189}
190
191/// Encode UpdateFabricLabel command (0x09)
192pub fn encode_update_fabric_label(label: String) -> anyhow::Result<Vec<u8>> {
193    let tlv = tlv::TlvItemEnc {
194        tag: 0,
195        value: tlv::TlvItemValueEnc::StructInvisible(vec![
196        (0, tlv::TlvItemValueEnc::String(label)).into(),
197        ]),
198    };
199    Ok(tlv.encode()?)
200}
201
202/// Encode RemoveFabric command (0x0A)
203pub fn encode_remove_fabric(fabric_index: u8) -> anyhow::Result<Vec<u8>> {
204    let tlv = tlv::TlvItemEnc {
205        tag: 0,
206        value: tlv::TlvItemValueEnc::StructInvisible(vec![
207        (0, tlv::TlvItemValueEnc::UInt8(fabric_index)).into(),
208        ]),
209    };
210    Ok(tlv.encode()?)
211}
212
213/// Encode AddTrustedRootCertificate command (0x0B)
214pub fn encode_add_trusted_root_certificate(root_ca_certificate: Vec<u8>) -> anyhow::Result<Vec<u8>> {
215    let tlv = tlv::TlvItemEnc {
216        tag: 0,
217        value: tlv::TlvItemValueEnc::StructInvisible(vec![
218        (0, tlv::TlvItemValueEnc::OctetString(root_ca_certificate)).into(),
219        ]),
220    };
221    Ok(tlv.encode()?)
222}
223
224/// Encode SetVIDVerificationStatement command (0x0C)
225pub fn encode_set_vid_verification_statement(vendor_id: Option<u16>, vid_verification_statement: Option<Vec<u8>>, vvsc: Option<Vec<u8>>) -> anyhow::Result<Vec<u8>> {
226    let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
227    if let Some(x) = vendor_id { tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
228    if let Some(x) = vid_verification_statement { tlv_fields.push((1, tlv::TlvItemValueEnc::OctetString(x)).into()); }
229    if let Some(x) = vvsc { tlv_fields.push((2, tlv::TlvItemValueEnc::OctetString(x)).into()); }
230    let tlv = tlv::TlvItemEnc {
231        tag: 0,
232        value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
233    };
234    Ok(tlv.encode()?)
235}
236
237/// Encode SignVIDVerificationRequest command (0x0D)
238pub fn encode_sign_vid_verification_request(fabric_index: u8, client_challenge: Vec<u8>) -> anyhow::Result<Vec<u8>> {
239    let tlv = tlv::TlvItemEnc {
240        tag: 0,
241        value: tlv::TlvItemValueEnc::StructInvisible(vec![
242        (0, tlv::TlvItemValueEnc::UInt8(fabric_index)).into(),
243        (1, tlv::TlvItemValueEnc::OctetString(client_challenge)).into(),
244        ]),
245    };
246    Ok(tlv.encode()?)
247}
248
249// Attribute decoders
250
251/// Decode NOCs attribute (0x0000)
252pub fn decode_no_cs(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<NOC>> {
253    let mut res = Vec::new();
254    if let tlv::TlvItemValue::List(v) = inp {
255        for item in v {
256            res.push(NOC {
257                noc: item.get_octet_string_owned(&[1]),
258                icac: item.get_octet_string_owned(&[2]),
259                vvsc: item.get_octet_string_owned(&[3]),
260            });
261        }
262    }
263    Ok(res)
264}
265
266/// Decode Fabrics attribute (0x0001)
267pub fn decode_fabrics(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<FabricDescriptor>> {
268    let mut res = Vec::new();
269    if let tlv::TlvItemValue::List(v) = inp {
270        for item in v {
271            res.push(FabricDescriptor {
272                root_public_key: item.get_octet_string_owned(&[1]),
273                vendor_id: item.get_int(&[2]).map(|v| v as u16),
274                fabric_id: item.get_int(&[3]).map(|v| v as u8),
275                node_id: item.get_int(&[4]),
276                label: item.get_string_owned(&[5]),
277                vid_verification_statement: item.get_octet_string_owned(&[6]),
278            });
279        }
280    }
281    Ok(res)
282}
283
284/// Decode SupportedFabrics attribute (0x0002)
285pub fn decode_supported_fabrics(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
286    if let tlv::TlvItemValue::Int(v) = inp {
287        Ok(*v as u8)
288    } else {
289        Err(anyhow::anyhow!("Expected UInt8"))
290    }
291}
292
293/// Decode CommissionedFabrics attribute (0x0003)
294pub fn decode_commissioned_fabrics(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
295    if let tlv::TlvItemValue::Int(v) = inp {
296        Ok(*v as u8)
297    } else {
298        Err(anyhow::anyhow!("Expected UInt8"))
299    }
300}
301
302/// Decode TrustedRootCertificates attribute (0x0004)
303pub fn decode_trusted_root_certificates(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Vec<u8>>> {
304    let mut res = Vec::new();
305    if let tlv::TlvItemValue::List(v) = inp {
306        for item in v {
307            if let tlv::TlvItemValue::OctetString(o) = &item.value {
308                res.push(o.clone());
309            }
310        }
311    }
312    Ok(res)
313}
314
315/// Decode CurrentFabricIndex attribute (0x0005)
316pub fn decode_current_fabric_index(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
317    if let tlv::TlvItemValue::Int(v) = inp {
318        Ok(*v as u8)
319    } else {
320        Err(anyhow::anyhow!("Expected UInt8"))
321    }
322}
323
324
325// JSON dispatcher function
326
327/// Decode attribute value and return as JSON string
328///
329/// # Parameters
330/// * `cluster_id` - The cluster identifier
331/// * `attribute_id` - The attribute identifier
332/// * `tlv_value` - The TLV value to decode
333///
334/// # Returns
335/// JSON string representation of the decoded value or error
336pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
337    // Verify this is the correct cluster
338    if ![0x003E].contains(&cluster_id) {
339        return format!("{{\"error\": \"Invalid cluster ID. Expected [0x003E], got {}\"}}", cluster_id);
340    }
341
342    match attribute_id {
343        0x0000 => {
344            match decode_no_cs(tlv_value) {
345                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
346                Err(e) => format!("{{\"error\": \"{}\"}}", e),
347            }
348        }
349        0x0001 => {
350            match decode_fabrics(tlv_value) {
351                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
352                Err(e) => format!("{{\"error\": \"{}\"}}", e),
353            }
354        }
355        0x0002 => {
356            match decode_supported_fabrics(tlv_value) {
357                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
358                Err(e) => format!("{{\"error\": \"{}\"}}", e),
359            }
360        }
361        0x0003 => {
362            match decode_commissioned_fabrics(tlv_value) {
363                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
364                Err(e) => format!("{{\"error\": \"{}\"}}", e),
365            }
366        }
367        0x0004 => {
368            match decode_trusted_root_certificates(tlv_value) {
369                Ok(value) => {
370                    // Serialize Vec<Vec<u8>> as array of hex strings
371                    let hex_array: Vec<String> = value.iter()
372                        .map(|bytes| bytes.iter()
373                            .map(|byte| format!("{:02x}", byte))
374                            .collect::<String>())
375                        .collect();
376                    serde_json::to_string(&hex_array).unwrap_or_else(|_| "null".to_string())
377                },
378                Err(e) => format!("{{\"error\": \"{}\"}}", e),
379            }
380        }
381        0x0005 => {
382            match decode_current_fabric_index(tlv_value) {
383                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
384                Err(e) => format!("{{\"error\": \"{}\"}}", e),
385            }
386        }
387        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
388    }
389}
390
391/// Get list of all attributes supported by this cluster
392///
393/// # Returns
394/// Vector of tuples containing (attribute_id, attribute_name)
395pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
396    vec![
397        (0x0000, "NOCs"),
398        (0x0001, "Fabrics"),
399        (0x0002, "SupportedFabrics"),
400        (0x0003, "CommissionedFabrics"),
401        (0x0004, "TrustedRootCertificates"),
402        (0x0005, "CurrentFabricIndex"),
403    ]
404}
405
406// Command listing
407
408pub fn get_command_list() -> Vec<(u32, &'static str)> {
409    vec![
410        (0x00, "AttestationRequest"),
411        (0x02, "CertificateChainRequest"),
412        (0x04, "CSRRequest"),
413        (0x06, "AddNOC"),
414        (0x07, "UpdateNOC"),
415        (0x09, "UpdateFabricLabel"),
416        (0x0A, "RemoveFabric"),
417        (0x0B, "AddTrustedRootCertificate"),
418        (0x0C, "SetVIDVerificationStatement"),
419        (0x0D, "SignVIDVerificationRequest"),
420    ]
421}
422
423pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
424    match cmd_id {
425        0x00 => Some("AttestationRequest"),
426        0x02 => Some("CertificateChainRequest"),
427        0x04 => Some("CSRRequest"),
428        0x06 => Some("AddNOC"),
429        0x07 => Some("UpdateNOC"),
430        0x09 => Some("UpdateFabricLabel"),
431        0x0A => Some("RemoveFabric"),
432        0x0B => Some("AddTrustedRootCertificate"),
433        0x0C => Some("SetVIDVerificationStatement"),
434        0x0D => Some("SignVIDVerificationRequest"),
435        _ => None,
436    }
437}
438
439pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
440    match cmd_id {
441        0x00 => Some(vec![
442            crate::clusters::codec::CommandField { tag: 0, name: "attestation_nonce", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
443        ]),
444        0x02 => Some(vec![
445            crate::clusters::codec::CommandField { tag: 0, name: "certificate_type", kind: crate::clusters::codec::FieldKind::Enum { name: "CertificateChainType", variants: &[(1, "Daccertificate"), (2, "Paicertificate")] }, optional: false, nullable: false },
446        ]),
447        0x04 => Some(vec![
448            crate::clusters::codec::CommandField { tag: 0, name: "csr_nonce", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
449            crate::clusters::codec::CommandField { tag: 1, name: "is_for_update_noc", kind: crate::clusters::codec::FieldKind::Bool, optional: true, nullable: false },
450        ]),
451        0x06 => Some(vec![
452            crate::clusters::codec::CommandField { tag: 0, name: "noc_value", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
453            crate::clusters::codec::CommandField { tag: 1, name: "icac_value", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: false },
454            crate::clusters::codec::CommandField { tag: 2, name: "ipk_value", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
455            crate::clusters::codec::CommandField { tag: 3, name: "case_admin_subject", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
456            crate::clusters::codec::CommandField { tag: 4, name: "admin_vendor_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
457        ]),
458        0x07 => Some(vec![
459            crate::clusters::codec::CommandField { tag: 0, name: "noc_value", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
460            crate::clusters::codec::CommandField { tag: 1, name: "icac_value", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: false },
461        ]),
462        0x09 => Some(vec![
463            crate::clusters::codec::CommandField { tag: 0, name: "label", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: false },
464        ]),
465        0x0A => Some(vec![
466            crate::clusters::codec::CommandField { tag: 0, name: "fabric_index", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
467        ]),
468        0x0B => Some(vec![
469            crate::clusters::codec::CommandField { tag: 0, name: "root_ca_certificate", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
470        ]),
471        0x0C => Some(vec![
472            crate::clusters::codec::CommandField { tag: 0, name: "vendor_id", kind: crate::clusters::codec::FieldKind::U16, optional: true, nullable: false },
473            crate::clusters::codec::CommandField { tag: 1, name: "vid_verification_statement", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: false },
474            crate::clusters::codec::CommandField { tag: 2, name: "vvsc", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: false },
475        ]),
476        0x0D => Some(vec![
477            crate::clusters::codec::CommandField { tag: 0, name: "fabric_index", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
478            crate::clusters::codec::CommandField { tag: 1, name: "client_challenge", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
479        ]),
480        _ => None,
481    }
482}
483
484pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
485    match cmd_id {
486        0x00 => {
487        let attestation_nonce = crate::clusters::codec::json_util::get_octstr(args, "attestation_nonce")?;
488        encode_attestation_request(attestation_nonce)
489        }
490        0x02 => {
491        let certificate_type = {
492            let n = crate::clusters::codec::json_util::get_u64(args, "certificate_type")?;
493            CertificateChainType::from_u8(n as u8).ok_or_else(|| anyhow::anyhow!("invalid CertificateChainType: {}", n))?
494        };
495        encode_certificate_chain_request(certificate_type)
496        }
497        0x04 => {
498        let csr_nonce = crate::clusters::codec::json_util::get_octstr(args, "csr_nonce")?;
499        let is_for_update_noc = crate::clusters::codec::json_util::get_opt_bool(args, "is_for_update_noc")?;
500        encode_csr_request(csr_nonce, is_for_update_noc)
501        }
502        0x06 => {
503        let noc_value = crate::clusters::codec::json_util::get_octstr(args, "noc_value")?;
504        let icac_value = crate::clusters::codec::json_util::get_opt_octstr(args, "icac_value")?;
505        let ipk_value = crate::clusters::codec::json_util::get_octstr(args, "ipk_value")?;
506        let case_admin_subject = crate::clusters::codec::json_util::get_u64(args, "case_admin_subject")?;
507        let admin_vendor_id = crate::clusters::codec::json_util::get_u16(args, "admin_vendor_id")?;
508        encode_add_noc(noc_value, icac_value, ipk_value, case_admin_subject, admin_vendor_id)
509        }
510        0x07 => {
511        let noc_value = crate::clusters::codec::json_util::get_octstr(args, "noc_value")?;
512        let icac_value = crate::clusters::codec::json_util::get_opt_octstr(args, "icac_value")?;
513        encode_update_noc(noc_value, icac_value)
514        }
515        0x09 => {
516        let label = crate::clusters::codec::json_util::get_string(args, "label")?;
517        encode_update_fabric_label(label)
518        }
519        0x0A => {
520        let fabric_index = crate::clusters::codec::json_util::get_u8(args, "fabric_index")?;
521        encode_remove_fabric(fabric_index)
522        }
523        0x0B => {
524        let root_ca_certificate = crate::clusters::codec::json_util::get_octstr(args, "root_ca_certificate")?;
525        encode_add_trusted_root_certificate(root_ca_certificate)
526        }
527        0x0C => {
528        let vendor_id = crate::clusters::codec::json_util::get_opt_u16(args, "vendor_id")?;
529        let vid_verification_statement = crate::clusters::codec::json_util::get_opt_octstr(args, "vid_verification_statement")?;
530        let vvsc = crate::clusters::codec::json_util::get_opt_octstr(args, "vvsc")?;
531        encode_set_vid_verification_statement(vendor_id, vid_verification_statement, vvsc)
532        }
533        0x0D => {
534        let fabric_index = crate::clusters::codec::json_util::get_u8(args, "fabric_index")?;
535        let client_challenge = crate::clusters::codec::json_util::get_octstr(args, "client_challenge")?;
536        encode_sign_vid_verification_request(fabric_index, client_challenge)
537        }
538        _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
539    }
540}
541
542#[derive(Debug, serde::Serialize)]
543pub struct AttestationResponse {
544    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
545    pub attestation_elements: Option<Vec<u8>>,
546    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
547    pub attestation_signature: Option<Vec<u8>>,
548}
549
550#[derive(Debug, serde::Serialize)]
551pub struct CertificateChainResponse {
552    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
553    pub certificate: Option<Vec<u8>>,
554}
555
556#[derive(Debug, serde::Serialize)]
557pub struct CSRResponse {
558    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
559    pub nocsr_elements: Option<Vec<u8>>,
560    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
561    pub attestation_signature: Option<Vec<u8>>,
562}
563
564#[derive(Debug, serde::Serialize)]
565pub struct NOCResponse {
566    pub status_code: Option<NodeOperationalCertStatus>,
567    pub fabric_index: Option<u8>,
568    pub debug_text: Option<String>,
569}
570
571#[derive(Debug, serde::Serialize)]
572pub struct SignVIDVerificationResponse {
573    pub fabric_index: Option<u8>,
574    pub fabric_binding_version: Option<u8>,
575    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
576    pub signature: Option<Vec<u8>>,
577}
578
579// Command response decoders
580
581/// Decode AttestationResponse command response (01)
582pub fn decode_attestation_response(inp: &tlv::TlvItemValue) -> anyhow::Result<AttestationResponse> {
583    if let tlv::TlvItemValue::List(_fields) = inp {
584        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
585        Ok(AttestationResponse {
586                attestation_elements: item.get_octet_string_owned(&[0]),
587                attestation_signature: item.get_octet_string_owned(&[1]),
588        })
589    } else {
590        Err(anyhow::anyhow!("Expected struct fields"))
591    }
592}
593
594/// Decode CertificateChainResponse command response (03)
595pub fn decode_certificate_chain_response(inp: &tlv::TlvItemValue) -> anyhow::Result<CertificateChainResponse> {
596    if let tlv::TlvItemValue::List(_fields) = inp {
597        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
598        Ok(CertificateChainResponse {
599                certificate: item.get_octet_string_owned(&[0]),
600        })
601    } else {
602        Err(anyhow::anyhow!("Expected struct fields"))
603    }
604}
605
606/// Decode CSRResponse command response (05)
607pub fn decode_csr_response(inp: &tlv::TlvItemValue) -> anyhow::Result<CSRResponse> {
608    if let tlv::TlvItemValue::List(_fields) = inp {
609        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
610        Ok(CSRResponse {
611                nocsr_elements: item.get_octet_string_owned(&[0]),
612                attestation_signature: item.get_octet_string_owned(&[1]),
613        })
614    } else {
615        Err(anyhow::anyhow!("Expected struct fields"))
616    }
617}
618
619/// Decode NOCResponse command response (08)
620pub fn decode_noc_response(inp: &tlv::TlvItemValue) -> anyhow::Result<NOCResponse> {
621    if let tlv::TlvItemValue::List(_fields) = inp {
622        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
623        Ok(NOCResponse {
624                status_code: item.get_int(&[0]).and_then(|v| NodeOperationalCertStatus::from_u8(v as u8)),
625                fabric_index: item.get_int(&[1]).map(|v| v as u8),
626                debug_text: item.get_string_owned(&[2]),
627        })
628    } else {
629        Err(anyhow::anyhow!("Expected struct fields"))
630    }
631}
632
633/// Decode SignVIDVerificationResponse command response (0E)
634pub fn decode_sign_vid_verification_response(inp: &tlv::TlvItemValue) -> anyhow::Result<SignVIDVerificationResponse> {
635    if let tlv::TlvItemValue::List(_fields) = inp {
636        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
637        Ok(SignVIDVerificationResponse {
638                fabric_index: item.get_int(&[0]).map(|v| v as u8),
639                fabric_binding_version: item.get_int(&[1]).map(|v| v as u8),
640                signature: item.get_octet_string_owned(&[2]),
641        })
642    } else {
643        Err(anyhow::anyhow!("Expected struct fields"))
644    }
645}
646
647// Typed facade (invokes + reads)
648
649/// Invoke `AttestationRequest` command on cluster `Operational Credentials`.
650pub async fn attestation_request(conn: &crate::controller::Connection, endpoint: u16, attestation_nonce: Vec<u8>) -> anyhow::Result<AttestationResponse> {
651    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_ATTESTATIONREQUEST, &encode_attestation_request(attestation_nonce)?).await?;
652    decode_attestation_response(&tlv)
653}
654
655/// Invoke `CertificateChainRequest` command on cluster `Operational Credentials`.
656pub async fn certificate_chain_request(conn: &crate::controller::Connection, endpoint: u16, certificate_type: CertificateChainType) -> anyhow::Result<CertificateChainResponse> {
657    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_CERTIFICATECHAINREQUEST, &encode_certificate_chain_request(certificate_type)?).await?;
658    decode_certificate_chain_response(&tlv)
659}
660
661/// Invoke `CSRRequest` command on cluster `Operational Credentials`.
662pub async fn csr_request(conn: &crate::controller::Connection, endpoint: u16, csr_nonce: Vec<u8>, is_for_update_noc: Option<bool>) -> anyhow::Result<CSRResponse> {
663    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_CSRREQUEST, &encode_csr_request(csr_nonce, is_for_update_noc)?).await?;
664    decode_csr_response(&tlv)
665}
666
667/// Invoke `AddNOC` command on cluster `Operational Credentials`.
668pub async fn add_noc(conn: &crate::controller::Connection, endpoint: u16, noc_value: Vec<u8>, icac_value: Option<Vec<u8>>, ipk_value: Vec<u8>, case_admin_subject: u64, admin_vendor_id: u16) -> anyhow::Result<NOCResponse> {
669    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_ADDNOC, &encode_add_noc(noc_value, icac_value, ipk_value, case_admin_subject, admin_vendor_id)?).await?;
670    decode_noc_response(&tlv)
671}
672
673/// Invoke `UpdateNOC` command on cluster `Operational Credentials`.
674pub async fn update_noc(conn: &crate::controller::Connection, endpoint: u16, noc_value: Vec<u8>, icac_value: Option<Vec<u8>>) -> anyhow::Result<NOCResponse> {
675    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_UPDATENOC, &encode_update_noc(noc_value, icac_value)?).await?;
676    decode_noc_response(&tlv)
677}
678
679/// Invoke `UpdateFabricLabel` command on cluster `Operational Credentials`.
680pub async fn update_fabric_label(conn: &crate::controller::Connection, endpoint: u16, label: String) -> anyhow::Result<NOCResponse> {
681    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_UPDATEFABRICLABEL, &encode_update_fabric_label(label)?).await?;
682    decode_noc_response(&tlv)
683}
684
685/// Invoke `RemoveFabric` command on cluster `Operational Credentials`.
686pub async fn remove_fabric(conn: &crate::controller::Connection, endpoint: u16, fabric_index: u8) -> anyhow::Result<NOCResponse> {
687    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_REMOVEFABRIC, &encode_remove_fabric(fabric_index)?).await?;
688    decode_noc_response(&tlv)
689}
690
691/// Invoke `AddTrustedRootCertificate` command on cluster `Operational Credentials`.
692pub async fn add_trusted_root_certificate(conn: &crate::controller::Connection, endpoint: u16, root_ca_certificate: Vec<u8>) -> anyhow::Result<()> {
693    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_ADDTRUSTEDROOTCERTIFICATE, &encode_add_trusted_root_certificate(root_ca_certificate)?).await?;
694    Ok(())
695}
696
697/// Invoke `SetVIDVerificationStatement` command on cluster `Operational Credentials`.
698pub async fn set_vid_verification_statement(conn: &crate::controller::Connection, endpoint: u16, vendor_id: Option<u16>, vid_verification_statement: Option<Vec<u8>>, vvsc: Option<Vec<u8>>) -> anyhow::Result<()> {
699    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_SETVIDVERIFICATIONSTATEMENT, &encode_set_vid_verification_statement(vendor_id, vid_verification_statement, vvsc)?).await?;
700    Ok(())
701}
702
703/// Invoke `SignVIDVerificationRequest` command on cluster `Operational Credentials`.
704pub async fn sign_vid_verification_request(conn: &crate::controller::Connection, endpoint: u16, fabric_index: u8, client_challenge: Vec<u8>) -> anyhow::Result<SignVIDVerificationResponse> {
705    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_SIGNVIDVERIFICATIONREQUEST, &encode_sign_vid_verification_request(fabric_index, client_challenge)?).await?;
706    decode_sign_vid_verification_response(&tlv)
707}
708
709/// Read `NOCs` attribute from cluster `Operational Credentials`.
710pub async fn read_no_cs(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<NOC>> {
711    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_ATTR_ID_NOCS).await?;
712    decode_no_cs(&tlv)
713}
714
715/// Read `Fabrics` attribute from cluster `Operational Credentials`.
716pub async fn read_fabrics(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<FabricDescriptor>> {
717    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_ATTR_ID_FABRICS).await?;
718    decode_fabrics(&tlv)
719}
720
721/// Read `SupportedFabrics` attribute from cluster `Operational Credentials`.
722pub async fn read_supported_fabrics(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
723    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_ATTR_ID_SUPPORTEDFABRICS).await?;
724    decode_supported_fabrics(&tlv)
725}
726
727/// Read `CommissionedFabrics` attribute from cluster `Operational Credentials`.
728pub async fn read_commissioned_fabrics(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
729    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_ATTR_ID_COMMISSIONEDFABRICS).await?;
730    decode_commissioned_fabrics(&tlv)
731}
732
733/// Read `TrustedRootCertificates` attribute from cluster `Operational Credentials`.
734pub async fn read_trusted_root_certificates(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<Vec<u8>>> {
735    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_ATTR_ID_TRUSTEDROOTCERTIFICATES).await?;
736    decode_trusted_root_certificates(&tlv)
737}
738
739/// Read `CurrentFabricIndex` attribute from cluster `Operational Credentials`.
740pub async fn read_current_fabric_index(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
741    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_CREDENTIALS, crate::clusters::defs::CLUSTER_OPERATIONAL_CREDENTIALS_ATTR_ID_CURRENTFABRICINDEX).await?;
742    decode_current_fabric_index(&tlv)
743}
744