matc/
cert_matter.rs

1//! Handling of certificates in Matter format
2
3use anyhow::{Context, Result};
4use p256::NistP256;
5use x509_cert::{
6    certificate::CertificateInner,
7    der::{Decode, DecodePem},
8};
9
10use crate::{
11    tlv::{self, TlvBuffer},
12    util::cryptoutil,
13};
14
15fn decode_dn_value(dn: &x509_cert::der::Any) -> Result<u64> {
16    let valstr = dn.decode_as::<String>()?;
17    Ok(u64::from_str_radix(&valstr, 16)?)
18}
19
20fn dn_to_matter(dn: &x509_cert::name::RdnSequence, tlv: &mut TlvBuffer) -> Result<()> {
21    for extra in &dn.0 {
22        for e2 in extra.0.as_slice() {
23            if e2.oid == const_oid::ObjectIdentifier::new_unwrap("1.3.6.1.4.1.37244.1.1") {
24                tlv.write_uint64(17, decode_dn_value(&e2.value)?)?;
25            }
26            if e2.oid == const_oid::ObjectIdentifier::new_unwrap("1.3.6.1.4.1.37244.1.4") {
27                tlv.write_uint64(20, decode_dn_value(&e2.value)?)?;
28            }
29            if e2.oid == const_oid::ObjectIdentifier::new_unwrap("1.3.6.1.4.1.37244.1.5") {
30                tlv.write_uint64(21, decode_dn_value(&e2.value)?)?;
31            }
32        }
33    }
34    Ok(())
35}
36
37fn extract_extension(cert: &x509_cert::TbsCertificate, oid: &str) -> Result<Vec<u8>> {
38    let extensions = cert
39        .extensions
40        .as_ref()
41        .context("can't get cert extensions")?;
42    for extension in extensions {
43        if extension.extn_id == const_oid::ObjectIdentifier::new_unwrap(oid) {
44            let v = extension.extn_value.as_bytes().to_vec();
45            return Ok(v);
46        }
47    }
48    Err(anyhow::anyhow!(format!("can't find extension {:?}", oid)))
49}
50
51pub fn get_subject_node_id_from_x509(fname: &str) -> Result<u64> {
52    let cert_file = std::fs::read_to_string(fname)?;
53    let cert = x509_cert::Certificate::from_pem(cert_file)?;
54    for extra in cert.tbs_certificate.subject.0 {
55        for e2 in extra.0.as_slice() {
56            if e2.oid == const_oid::ObjectIdentifier::new_unwrap("1.3.6.1.4.1.37244.1.1") {
57                return decode_dn_value(&e2.value);
58            }
59        }
60    }
61    Err(anyhow::anyhow!("matter subject/node not found in x509"))
62}
63
64/// Convert certificate in PEM file to matter format
65/// PEM file must contain x509 certificate compatible with matter
66pub fn convert_x509_to_matter(fname: &str, ca_pubkey: &[u8]) -> Result<Vec<u8>> {
67    let x509_raw = cryptoutil::read_data_from_pem(fname)?;
68    convert_x509_bytes_to_matter(&x509_raw, ca_pubkey)
69}
70
71/// Convert certificate from X509/DER array of bytes to matter format
72/// x509 certificate must be compatible with matter
73pub fn convert_x509_bytes_to_matter(bytes: &[u8], ca_pubkey: &[u8]) -> Result<Vec<u8>> {
74    let x509 = x509_cert::Certificate::from_der(bytes)?;
75    convert_x509_to_matter_int(&x509, ca_pubkey)
76}
77
78fn convert_x509_to_matter_int(cert: &CertificateInner, ca_pubkey: &[u8]) -> Result<Vec<u8>> {
79    let mut enc = tlv::TlvBuffer::new();
80    enc.write_anon_struct()?;
81    enc.write_octetstring(1, cert.tbs_certificate.serial_number.as_bytes())?;
82    enc.write_uint8(2, 1)?; //signature algorithm
83
84    enc.write_list(3)?; // issuer
85    dn_to_matter(&cert.tbs_certificate.issuer, &mut enc)?;
86    enc.write_struct_end()?;
87
88    let not_before = cert.tbs_certificate.validity.not_before;
89    enc.write_uint32(
90        4,
91        (not_before.to_unix_duration().as_secs() - 946684800) as u32,
92    )?;
93    let not_after = cert.tbs_certificate.validity.not_after;
94    enc.write_uint32(
95        5,
96        (not_after.to_unix_duration().as_secs() - 946684800) as u32,
97    )?;
98
99    enc.write_list(6)?; // subject
100    dn_to_matter(&cert.tbs_certificate.subject, &mut enc)?;
101    enc.write_struct_end()?;
102
103    enc.write_uint8(7, 1)?;
104    enc.write_uint8(8, 1)?;
105
106    let subject_public_key = cert
107        .tbs_certificate
108        .subject_public_key_info
109        .subject_public_key
110        .as_bytes()
111        .context("can't extract subject public key")?;
112
113    enc.write_octetstring(9, subject_public_key)?;
114
115    enc.write_list(10)?;
116    enc.write_struct(1)?;
117    let is_ca = {
118        let basicc = extract_extension(
119            &cert.tbs_certificate,
120            crate::cert_x509::OID_CE_BASIC_CONSTRAINTS,
121        );
122        if let Ok(v) = basicc {
123            v[v.len() - 1] == 0xff
124        } else {
125            false
126        }
127    };
128    enc.write_bool(1, is_ca)?;
129    enc.write_struct_end()?;
130    let kus = extract_extension(&cert.tbs_certificate, crate::cert_x509::OID_CE_KEY_USAGE)?;
131    let kus = x509_cert::ext::pkix::KeyUsage::from_der(&kus)?;
132
133    enc.write_uint8(2, kus.0.bits() as u8)?;
134    ///////// ext key usage
135    let extu = extract_extension(
136        &cert.tbs_certificate,
137        crate::cert_x509::OID_CE_EXT_KEU_USAGE,
138    );
139    if extu.is_ok() {
140        enc.write_array(0x3)?;
141        let extu = x509_cert::ext::pkix::ExtendedKeyUsage::from_der(&extu?)?;
142        for u in extu.0 {
143            match u.to_string().as_str() {
144                "1.3.6.1.5.5.7.3.1" => {
145                    enc.write_uint8_notag(1)?;
146                } // server-auth
147                "1.3.6.1.5.5.7.3.2" => {
148                    enc.write_uint8_notag(2)?;
149                } // client-auth
150                _ => {
151                    return Err(anyhow::anyhow!(
152                        "unsupported oid in extendedKeyUsage {:?}",
153                        u.to_string()
154                    ))
155                }
156            };
157        }
158        enc.write_struct_end()?;
159    }
160
161    // do-sha1
162    let cakey_sha1 = cryptoutil::sha1_enc(ca_pubkey);
163
164    enc.write_octetstring(
165        4,
166        &extract_extension(
167            &cert.tbs_certificate,
168            crate::cert_x509::OID_CE_SUBJECT_KEY_IDENTIFIER,
169        )?[2..],
170    )?;
171    enc.write_octetstring(5, &cakey_sha1)?;
172    enc.write_struct_end()?;
173
174    let sig = cert
175        .signature
176        .as_bytes()
177        .context("can't get signature from x509")?;
178
179    let sig = ecdsa::Signature::<NistP256>::from_der(sig)?;
180
181    enc.write_octetstring(11, sig.to_bytes().as_slice())?;
182
183    enc.write_struct_end()?;
184    Ok(enc.data)
185}