matc/
cert_x509.rs

1//! Handling of x509 certificate compatible with matter
2
3use byteorder::WriteBytesExt;
4use std::time::{Duration, SystemTime};
5
6use crate::util::asn1;
7use crate::util::cryptoutil;
8use anyhow::{Context, Result};
9
10fn add_ext(encoder: &mut asn1::Encoder, oid: &str, critical: bool, value: &[u8]) -> Result<()> {
11    encoder.start_seq(0x30)?;
12    encoder.write_oid(oid)?;
13    if critical {
14        encoder.write_bool(critical)?;
15    }
16    encoder.write_octet_string(value)?;
17    encoder.end_seq();
18    Ok(())
19}
20
21fn encode_nodeid(id: u64) -> String {
22    format!("{:0>16X}", id)
23}
24
25fn systemtime_to_x509_time(st: std::time::SystemTime) -> Result<String> {
26    let der_datetime = x509_cert::der::asn1::UtcTime::from_system_time(st)?;
27    let mut v = Vec::new();
28    x509_cert::der::EncodeValue::encode_value(&der_datetime, &mut v)?;
29    Ok(std::str::from_utf8(&v)?.to_owned())
30}
31
32const OID_MATTER_DN_NODE: &str = "1.3.6.1.4.1.37244.1.1";
33const OID_MATTER_DN_CA: &str = "1.3.6.1.4.1.37244.1.4";
34const OID_MATTER_DN_FABRIC: &str = "1.3.6.1.4.1.37244.1.5";
35
36const OID_SIG_ECDSA_WITH_SHA256: &str = "1.2.840.10045.4.3.2";
37
38pub(crate) const OID_CE_SUBJECT_KEY_IDENTIFIER: &str = "2.5.29.14";
39pub(crate) const OID_CE_KEY_USAGE: &str = "2.5.29.15";
40pub(crate) const OID_CE_BASIC_CONSTRAINTS: &str = "2.5.29.19";
41pub(crate) const OID_CE_EXT_KEU_USAGE: &str = "2.5.29.37";
42pub(crate) const OID_CE_AUTHORITY_KEY_IDENTIFIER: &str = "2.5.29.35";
43
44fn add_rdn(encoder: &mut asn1::Encoder, oid: &str, id: u64) -> Result<()> {
45    encoder.start_seq(0x31)?; //rdn
46    encoder.start_seq(0x30)?; //atv
47    encoder.write_oid(oid)?;
48    encoder.write_string(&encode_nodeid(id))?;
49    encoder.end_seq();
50    encoder.end_seq();
51    Ok(())
52}
53
54/// Create matter compatible certificate in x509 format.
55pub fn encode_x509(
56    node_public_key: &[u8],
57    node_id: u64,
58    fabric_id: u64,
59    ca_id: u64,
60    ca_private: &p256::SecretKey,
61    ca: bool,
62) -> Result<Vec<u8>> {
63    let mut encoder = asn1::Encoder::new();
64    encoder.start_seq(0x30)?;
65    encoder.start_seq(0x30)?;
66
67    encoder.start_seq(0xa0)?;
68    encoder.write_int(2)?; // version
69    encoder.end_seq();
70
71    encoder.write_int(10001)?; // serial
72
73    encoder.start_seq(0x30)?; //signature algorithm
74    encoder.write_oid(OID_SIG_ECDSA_WITH_SHA256)?;
75    encoder.end_seq();
76
77    encoder.start_seq(0x30)?; //issuer
78    add_rdn(&mut encoder, OID_MATTER_DN_CA, ca_id)?;
79    encoder.end_seq();
80
81    encoder.start_seq(0x30)?; //validity
82
83    let now = SystemTime::now();
84    encoder.write_string_with_tag(0x17, &systemtime_to_x509_time(now)?)?;
85    let not_after = now
86        .checked_add(Duration::from_secs(60 * 60 * 24 * 100))
87        .context("time continuity error")?;
88    encoder.write_string_with_tag(0x17, &systemtime_to_x509_time(not_after)?)?;
89    encoder.end_seq();
90
91    if ca {
92        encoder.start_seq(0x30)?; //subject
93        add_rdn(&mut encoder, OID_MATTER_DN_CA, node_id)?;
94        encoder.end_seq();
95    } else {
96        encoder.start_seq(0x30)?; //subject
97        add_rdn(&mut encoder, OID_MATTER_DN_NODE, node_id)?;
98        add_rdn(&mut encoder, OID_MATTER_DN_FABRIC, fabric_id)?;
99        encoder.end_seq();
100    }
101
102    encoder.start_seq(0x30)?; //subject key info
103    encoder.start_seq(0x30)?; //algorithm
104    encoder.write_oid("1.2.840.10045.2.1")?;
105    encoder.write_oid("1.2.840.10045.3.1.7")?;
106    encoder.end_seq();
107
108    let mut pk2 = Vec::new();
109    pk2.write_u8(0)?;
110
111    pk2.extend_from_slice(node_public_key);
112    encoder.write_octet_string_with_tag(0x3, &pk2)?;
113    encoder.end_seq();
114
115    let subjectkeyidasn = {
116        let mut encoder = asn1::Encoder::new();
117        encoder.write_octet_string(&cryptoutil::sha1_enc(node_public_key))?;
118        encoder.encode()
119    };
120
121    let authoritykey_sha1_asn = {
122        let mut encoder = asn1::Encoder::new();
123        encoder.start_seq(0x30)?;
124        let pubkey = ca_private.public_key().to_sec1_bytes();
125        encoder.write_octet_string_with_tag(0x80, &cryptoutil::sha1_enc(&pubkey))?;
126        encoder.encode()
127    };
128
129    encoder.start_seq(0xa3)?;
130    encoder.start_seq(0x30)?;
131    // basic constraints
132    if ca {
133        add_ext(
134            &mut encoder,
135            OID_CE_BASIC_CONSTRAINTS,
136            true,
137            &[0x30, 0x03, 0x01, 0x01, 0xFF],
138        )?
139    } else {
140        add_ext(&mut encoder, OID_CE_BASIC_CONSTRAINTS, true, &[0x30, 0x00])?
141    }
142    // key usage
143    if ca {
144        add_ext(
145            &mut encoder,
146            OID_CE_KEY_USAGE,
147            true,
148            &[0x03, 0x02, 0x01, 0x06],
149        )?;
150    } else {
151        add_ext(
152            &mut encoder,
153            OID_CE_KEY_USAGE,
154            true,
155            &[0x03, 0x02, 0x07, 0x80],
156        )?;
157    }
158    //ext key usage
159    if !ca {
160        let mut ext_ku_encoder = asn1::Encoder::new();
161        ext_ku_encoder.start_seq(0x30)?;
162        ext_ku_encoder.write_oid("1.3.6.1.5.5.7.3.2")?; // client-auth
163        ext_ku_encoder.write_oid("1.3.6.1.5.5.7.3.1")?; // server-auth
164        let ext_ku_bytes = ext_ku_encoder.encode();
165        add_ext(&mut encoder, OID_CE_EXT_KEU_USAGE, true, &ext_ku_bytes)?;
166    }
167    //subject key id
168    add_ext(
169        &mut encoder,
170        OID_CE_SUBJECT_KEY_IDENTIFIER,
171        false,
172        &subjectkeyidasn,
173    )?;
174
175    //authority key id
176    add_ext(
177        &mut encoder,
178        OID_CE_AUTHORITY_KEY_IDENTIFIER,
179        false,
180        &authoritykey_sha1_asn,
181    )?;
182
183    encoder.end_seq();
184    encoder.end_seq();
185    encoder.end_seq();
186
187    let to_sign = encoder.clone();
188    let to_sign_bytes = &to_sign.encode()[4..];
189    let key = ecdsa::SigningKey::from(ca_private);
190    let signed = key.sign_recoverable(to_sign_bytes)?.0;
191
192    encoder.start_seq(0x30)?; //alg
193    encoder.write_oid(OID_SIG_ECDSA_WITH_SHA256)?;
194    encoder.end_seq();
195    let mut signed_b = vec![0];
196    signed_b.extend_from_slice(signed.to_der().as_bytes());
197
198    encoder.write_octet_string_with_tag(0x3, &signed_b)?;
199
200    let res = encoder.encode();
201
202    Ok(res)
203}