matc/clusters/codec/
tls_certificate_management.rs

1//! Matter TLV encoders and decoders for TLS Certificate Management Cluster
2//! Cluster ID: 0x0801
3//!
4//! This file is automatically generated from TLSCertificateManagement.xml
5
6use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11// Import serialization helpers for octet strings
12use crate::clusters::helpers::{serialize_opt_bytes_as_hex, serialize_opt_vec_bytes_as_hex};
13
14// Struct definitions
15
16#[derive(Debug, serde::Serialize)]
17pub struct TLSCert {
18    pub caid: Option<u8>,
19    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
20    pub certificate: Option<Vec<u8>>,
21}
22
23#[derive(Debug, serde::Serialize)]
24pub struct TLSClientCertificateDetail {
25    pub ccdid: Option<u8>,
26    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
27    pub client_certificate: Option<Vec<u8>>,
28    #[serde(serialize_with = "serialize_opt_vec_bytes_as_hex")]
29    pub intermediate_certificates: Option<Vec<Vec<u8>>>,
30}
31
32// Command encoders
33
34/// Encode ProvisionRootCertificate command (0x00)
35pub fn encode_provision_root_certificate(certificate: Vec<u8>, caid: Option<u8>) -> anyhow::Result<Vec<u8>> {
36    let tlv = tlv::TlvItemEnc {
37        tag: 0,
38        value: tlv::TlvItemValueEnc::StructInvisible(vec![
39        (0, tlv::TlvItemValueEnc::OctetString(certificate)).into(),
40        (1, tlv::TlvItemValueEnc::UInt8(caid.unwrap_or(0))).into(),
41        ]),
42    };
43    Ok(tlv.encode()?)
44}
45
46/// Encode FindRootCertificate command (0x02)
47pub fn encode_find_root_certificate(caid: Option<u8>) -> anyhow::Result<Vec<u8>> {
48    let tlv = tlv::TlvItemEnc {
49        tag: 0,
50        value: tlv::TlvItemValueEnc::StructInvisible(vec![
51        (0, tlv::TlvItemValueEnc::UInt8(caid.unwrap_or(0))).into(),
52        ]),
53    };
54    Ok(tlv.encode()?)
55}
56
57/// Encode LookupRootCertificate command (0x04)
58pub fn encode_lookup_root_certificate(fingerprint: Vec<u8>) -> anyhow::Result<Vec<u8>> {
59    let tlv = tlv::TlvItemEnc {
60        tag: 0,
61        value: tlv::TlvItemValueEnc::StructInvisible(vec![
62        (0, tlv::TlvItemValueEnc::OctetString(fingerprint)).into(),
63        ]),
64    };
65    Ok(tlv.encode()?)
66}
67
68/// Encode RemoveRootCertificate command (0x06)
69pub fn encode_remove_root_certificate(caid: u8) -> anyhow::Result<Vec<u8>> {
70    let tlv = tlv::TlvItemEnc {
71        tag: 0,
72        value: tlv::TlvItemValueEnc::StructInvisible(vec![
73        (0, tlv::TlvItemValueEnc::UInt8(caid)).into(),
74        ]),
75    };
76    Ok(tlv.encode()?)
77}
78
79/// Encode ClientCSR command (0x07)
80pub fn encode_client_csr(nonce: Vec<u8>, ccdid: Option<u8>) -> anyhow::Result<Vec<u8>> {
81    let tlv = tlv::TlvItemEnc {
82        tag: 0,
83        value: tlv::TlvItemValueEnc::StructInvisible(vec![
84        (0, tlv::TlvItemValueEnc::OctetString(nonce)).into(),
85        (1, tlv::TlvItemValueEnc::UInt8(ccdid.unwrap_or(0))).into(),
86        ]),
87    };
88    Ok(tlv.encode()?)
89}
90
91/// Encode ProvisionClientCertificate command (0x09)
92pub fn encode_provision_client_certificate(ccdid: u8, client_certificate: Vec<u8>, intermediate_certificates: Vec<Vec<u8>>) -> anyhow::Result<Vec<u8>> {
93    let tlv = tlv::TlvItemEnc {
94        tag: 0,
95        value: tlv::TlvItemValueEnc::StructInvisible(vec![
96        (0, tlv::TlvItemValueEnc::UInt8(ccdid)).into(),
97        (1, tlv::TlvItemValueEnc::OctetString(client_certificate)).into(),
98        (2, tlv::TlvItemValueEnc::StructAnon(intermediate_certificates.into_iter().map(|v| (0, tlv::TlvItemValueEnc::OctetString(v)).into()).collect())).into(),
99        ]),
100    };
101    Ok(tlv.encode()?)
102}
103
104/// Encode FindClientCertificate command (0x0A)
105pub fn encode_find_client_certificate(ccdid: Option<u8>) -> anyhow::Result<Vec<u8>> {
106    let tlv = tlv::TlvItemEnc {
107        tag: 0,
108        value: tlv::TlvItemValueEnc::StructInvisible(vec![
109        (0, tlv::TlvItemValueEnc::UInt8(ccdid.unwrap_or(0))).into(),
110        ]),
111    };
112    Ok(tlv.encode()?)
113}
114
115/// Encode LookupClientCertificate command (0x0C)
116pub fn encode_lookup_client_certificate(fingerprint: Vec<u8>) -> anyhow::Result<Vec<u8>> {
117    let tlv = tlv::TlvItemEnc {
118        tag: 0,
119        value: tlv::TlvItemValueEnc::StructInvisible(vec![
120        (0, tlv::TlvItemValueEnc::OctetString(fingerprint)).into(),
121        ]),
122    };
123    Ok(tlv.encode()?)
124}
125
126/// Encode RemoveClientCertificate command (0x0E)
127pub fn encode_remove_client_certificate(ccdid: u8) -> anyhow::Result<Vec<u8>> {
128    let tlv = tlv::TlvItemEnc {
129        tag: 0,
130        value: tlv::TlvItemValueEnc::StructInvisible(vec![
131        (0, tlv::TlvItemValueEnc::UInt8(ccdid)).into(),
132        ]),
133    };
134    Ok(tlv.encode()?)
135}
136
137// Attribute decoders
138
139/// Decode MaxRootCertificates attribute (0x0000)
140pub fn decode_max_root_certificates(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
141    if let tlv::TlvItemValue::Int(v) = inp {
142        Ok(*v as u8)
143    } else {
144        Err(anyhow::anyhow!("Expected UInt8"))
145    }
146}
147
148/// Decode ProvisionedRootCertificates attribute (0x0001)
149pub fn decode_provisioned_root_certificates(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TLSCert>> {
150    let mut res = Vec::new();
151    if let tlv::TlvItemValue::List(v) = inp {
152        for item in v {
153            res.push(TLSCert {
154                caid: item.get_int(&[0]).map(|v| v as u8),
155                certificate: item.get_octet_string_owned(&[1]),
156            });
157        }
158    }
159    Ok(res)
160}
161
162/// Decode MaxClientCertificates attribute (0x0002)
163pub fn decode_max_client_certificates(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
164    if let tlv::TlvItemValue::Int(v) = inp {
165        Ok(*v as u8)
166    } else {
167        Err(anyhow::anyhow!("Expected UInt8"))
168    }
169}
170
171/// Decode ProvisionedClientCertificates attribute (0x0003)
172pub fn decode_provisioned_client_certificates(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TLSClientCertificateDetail>> {
173    let mut res = Vec::new();
174    if let tlv::TlvItemValue::List(v) = inp {
175        for item in v {
176            res.push(TLSClientCertificateDetail {
177                ccdid: item.get_int(&[0]).map(|v| v as u8),
178                client_certificate: item.get_octet_string_owned(&[1]),
179                intermediate_certificates: {
180                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
181                        let items: Vec<Vec<u8>> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::OctetString(v) = &e.value { Some(v.clone()) } else { None } }).collect();
182                        Some(items)
183                    } else {
184                        None
185                    }
186                },
187            });
188        }
189    }
190    Ok(res)
191}
192
193
194// JSON dispatcher function
195
196/// Decode attribute value and return as JSON string
197///
198/// # Parameters
199/// * `cluster_id` - The cluster identifier
200/// * `attribute_id` - The attribute identifier
201/// * `tlv_value` - The TLV value to decode
202///
203/// # Returns
204/// JSON string representation of the decoded value or error
205pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
206    // Verify this is the correct cluster
207    if cluster_id != 0x0801 {
208        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0801, got {}\"}}", cluster_id);
209    }
210
211    match attribute_id {
212        0x0000 => {
213            match decode_max_root_certificates(tlv_value) {
214                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
215                Err(e) => format!("{{\"error\": \"{}\"}}", e),
216            }
217        }
218        0x0001 => {
219            match decode_provisioned_root_certificates(tlv_value) {
220                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
221                Err(e) => format!("{{\"error\": \"{}\"}}", e),
222            }
223        }
224        0x0002 => {
225            match decode_max_client_certificates(tlv_value) {
226                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
227                Err(e) => format!("{{\"error\": \"{}\"}}", e),
228            }
229        }
230        0x0003 => {
231            match decode_provisioned_client_certificates(tlv_value) {
232                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
233                Err(e) => format!("{{\"error\": \"{}\"}}", e),
234            }
235        }
236        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
237    }
238}
239
240/// Get list of all attributes supported by this cluster
241///
242/// # Returns
243/// Vector of tuples containing (attribute_id, attribute_name)
244pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
245    vec![
246        (0x0000, "MaxRootCertificates"),
247        (0x0001, "ProvisionedRootCertificates"),
248        (0x0002, "MaxClientCertificates"),
249        (0x0003, "ProvisionedClientCertificates"),
250    ]
251}
252
253#[derive(Debug, serde::Serialize)]
254pub struct ProvisionRootCertificateResponse {
255    pub caid: Option<u8>,
256}
257
258#[derive(Debug, serde::Serialize)]
259pub struct FindRootCertificateResponse {
260    pub certificate_details: Option<Vec<TLSCert>>,
261}
262
263#[derive(Debug, serde::Serialize)]
264pub struct LookupRootCertificateResponse {
265    pub caid: Option<u8>,
266}
267
268#[derive(Debug, serde::Serialize)]
269pub struct ClientCSRResponse {
270    pub ccdid: Option<u8>,
271    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
272    pub csr: Option<Vec<u8>>,
273    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
274    pub nonce_signature: Option<Vec<u8>>,
275}
276
277#[derive(Debug, serde::Serialize)]
278pub struct FindClientCertificateResponse {
279    pub certificate_details: Option<Vec<TLSClientCertificateDetail>>,
280}
281
282#[derive(Debug, serde::Serialize)]
283pub struct LookupClientCertificateResponse {
284    pub ccdid: Option<u8>,
285}
286
287// Command response decoders
288
289/// Decode ProvisionRootCertificateResponse command response (01)
290pub fn decode_provision_root_certificate_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ProvisionRootCertificateResponse> {
291    if let tlv::TlvItemValue::List(_fields) = inp {
292        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
293        Ok(ProvisionRootCertificateResponse {
294                caid: item.get_int(&[0]).map(|v| v as u8),
295        })
296    } else {
297        Err(anyhow::anyhow!("Expected struct fields"))
298    }
299}
300
301/// Decode FindRootCertificateResponse command response (03)
302pub fn decode_find_root_certificate_response(inp: &tlv::TlvItemValue) -> anyhow::Result<FindRootCertificateResponse> {
303    if let tlv::TlvItemValue::List(_fields) = inp {
304        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
305        Ok(FindRootCertificateResponse {
306                certificate_details: {
307                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
308                        let mut items = Vec::new();
309                        for list_item in l {
310                            items.push(TLSCert {
311                caid: list_item.get_int(&[0]).map(|v| v as u8),
312                certificate: list_item.get_octet_string_owned(&[1]),
313                            });
314                        }
315                        Some(items)
316                    } else {
317                        None
318                    }
319                },
320        })
321    } else {
322        Err(anyhow::anyhow!("Expected struct fields"))
323    }
324}
325
326/// Decode LookupRootCertificateResponse command response (05)
327pub fn decode_lookup_root_certificate_response(inp: &tlv::TlvItemValue) -> anyhow::Result<LookupRootCertificateResponse> {
328    if let tlv::TlvItemValue::List(_fields) = inp {
329        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
330        Ok(LookupRootCertificateResponse {
331                caid: item.get_int(&[0]).map(|v| v as u8),
332        })
333    } else {
334        Err(anyhow::anyhow!("Expected struct fields"))
335    }
336}
337
338/// Decode ClientCSRResponse command response (08)
339pub fn decode_client_csr_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ClientCSRResponse> {
340    if let tlv::TlvItemValue::List(_fields) = inp {
341        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
342        Ok(ClientCSRResponse {
343                ccdid: item.get_int(&[0]).map(|v| v as u8),
344                csr: item.get_octet_string_owned(&[1]),
345                nonce_signature: item.get_octet_string_owned(&[2]),
346        })
347    } else {
348        Err(anyhow::anyhow!("Expected struct fields"))
349    }
350}
351
352/// Decode FindClientCertificateResponse command response (0B)
353pub fn decode_find_client_certificate_response(inp: &tlv::TlvItemValue) -> anyhow::Result<FindClientCertificateResponse> {
354    if let tlv::TlvItemValue::List(_fields) = inp {
355        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
356        Ok(FindClientCertificateResponse {
357                certificate_details: {
358                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
359                        let mut items = Vec::new();
360                        for list_item in l {
361                            items.push(TLSClientCertificateDetail {
362                ccdid: list_item.get_int(&[0]).map(|v| v as u8),
363                client_certificate: list_item.get_octet_string_owned(&[1]),
364                intermediate_certificates: {
365                    if let Some(tlv::TlvItemValue::List(l)) = list_item.get(&[2]) {
366                        let items: Vec<Vec<u8>> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::OctetString(v) = &e.value { Some(v.clone()) } else { None } }).collect();
367                        Some(items)
368                    } else {
369                        None
370                    }
371                },
372                            });
373                        }
374                        Some(items)
375                    } else {
376                        None
377                    }
378                },
379        })
380    } else {
381        Err(anyhow::anyhow!("Expected struct fields"))
382    }
383}
384
385/// Decode LookupClientCertificateResponse command response (0D)
386pub fn decode_lookup_client_certificate_response(inp: &tlv::TlvItemValue) -> anyhow::Result<LookupClientCertificateResponse> {
387    if let tlv::TlvItemValue::List(_fields) = inp {
388        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
389        Ok(LookupClientCertificateResponse {
390                ccdid: item.get_int(&[0]).map(|v| v as u8),
391        })
392    } else {
393        Err(anyhow::anyhow!("Expected struct fields"))
394    }
395}
396