matc/clusters/codec/
operational_credential_cluster.rs

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