1use byteorder::WriteBytesExt;
4use std::time::{Duration, SystemTime};
5
6use crate::tlv;
7use crate::util::asn1;
8use crate::util::cryptoutil;
9use anyhow::{Context, Result};
10
11fn add_ext(encoder: &mut asn1::Encoder, oid: &str, critical: bool, value: &[u8]) -> Result<()> {
12 encoder.start_seq(0x30)?;
13 encoder.write_oid(oid)?;
14 if critical {
15 encoder.write_bool(critical)?;
16 }
17 encoder.write_octet_string(value)?;
18 encoder.end_seq();
19 Ok(())
20}
21
22fn encode_nodeid(id: u64) -> String {
23 format!("{:0>16X}", id)
24}
25
26fn systemtime_to_x509_time(st: std::time::SystemTime) -> Result<String> {
27 let der_datetime = x509_cert::der::asn1::UtcTime::from_system_time(st)?;
28 let mut v = Vec::new();
29 x509_cert::der::EncodeValue::encode_value(&der_datetime, &mut v)?;
30 Ok(std::str::from_utf8(&v)?.to_owned())
31}
32
33const OID_MATTER_DN_NODE: &str = "1.3.6.1.4.1.37244.1.1";
34const OID_MATTER_DN_CA: &str = "1.3.6.1.4.1.37244.1.4";
35const OID_MATTER_DN_FABRIC: &str = "1.3.6.1.4.1.37244.1.5";
36
37const OID_SIG_ECDSA_WITH_SHA256: &str = "1.2.840.10045.4.3.2";
38
39pub(crate) const OID_CE_SUBJECT_KEY_IDENTIFIER: &str = "2.5.29.14";
40pub(crate) const OID_CE_KEY_USAGE: &str = "2.5.29.15";
41pub(crate) const OID_CE_BASIC_CONSTRAINTS: &str = "2.5.29.19";
42pub(crate) const OID_CE_EXT_KEU_USAGE: &str = "2.5.29.37";
43pub(crate) const OID_CE_AUTHORITY_KEY_IDENTIFIER: &str = "2.5.29.35";
44
45fn add_rdn(encoder: &mut asn1::Encoder, oid: &str, id: u64) -> Result<()> {
46 encoder.start_seq(0x31)?; encoder.start_seq(0x30)?; encoder.write_oid(oid)?;
49 encoder.write_string(&encode_nodeid(id))?;
50 encoder.end_seq();
51 encoder.end_seq();
52 Ok(())
53}
54
55fn epoch2000_to_x509_time(secs: u32) -> Result<String> {
56 let st = SystemTime::UNIX_EPOCH
57 .checked_add(Duration::from_secs(946684800 + secs as u64))
58 .context("certificate time out of range")?;
59 systemtime_to_x509_time(st)
60}
61
62pub(crate) fn matter_cert_to_x509_tbs(matter_cert: &[u8]) -> Result<Vec<u8>> {
69 let cert = tlv::decode_tlv(matter_cert)?;
70 let serial = cert
71 .get_octet_string(&[1])
72 .context("matter cert: serial missing")?;
73 let issuer_ca_id = cert
74 .get_int(&[3, 20])
75 .context("matter cert: issuer ca id missing")?;
76 let not_before = cert.get_int(&[4]).context("matter cert: not_before missing")? as u32;
77 let not_after = cert.get_int(&[5]).context("matter cert: not_after missing")? as u32;
78 let public_key = cert
79 .get_octet_string(&[9])
80 .context("matter cert: public key missing")?;
81 let is_ca = cert.get_bool(&[10, 1, 1]).unwrap_or(false);
82 let subject_key_id = cert
83 .get_octet_string(&[10, 4])
84 .context("matter cert: subject key id missing")?;
85 let authority_key_id = cert
86 .get_octet_string(&[10, 5])
87 .context("matter cert: authority key id missing")?;
88
89 let mut encoder = asn1::Encoder::new();
90 encoder.start_seq(0x30)?;
91
92 encoder.start_seq(0xa0)?;
93 encoder.write_int(2)?; encoder.end_seq();
95
96 encoder.write_octet_string_with_tag(0x2, serial)?; encoder.start_seq(0x30)?; encoder.write_oid(OID_SIG_ECDSA_WITH_SHA256)?;
100 encoder.end_seq();
101
102 encoder.start_seq(0x30)?; add_rdn(&mut encoder, OID_MATTER_DN_CA, issuer_ca_id)?;
104 encoder.end_seq();
105
106 encoder.start_seq(0x30)?; encoder.write_string_with_tag(0x17, &epoch2000_to_x509_time(not_before)?)?;
108 encoder.write_string_with_tag(0x17, &epoch2000_to_x509_time(not_after)?)?;
109 encoder.end_seq();
110
111 encoder.start_seq(0x30)?; if is_ca {
113 let subject_ca_id = cert
114 .get_int(&[6, 20])
115 .context("matter cert: subject ca id missing")?;
116 add_rdn(&mut encoder, OID_MATTER_DN_CA, subject_ca_id)?;
117 } else {
118 let node_id = cert
119 .get_int(&[6, 17])
120 .context("matter cert: subject node id missing")?;
121 let fabric_id = cert
122 .get_int(&[6, 21])
123 .context("matter cert: subject fabric id missing")?;
124 add_rdn(&mut encoder, OID_MATTER_DN_NODE, node_id)?;
125 add_rdn(&mut encoder, OID_MATTER_DN_FABRIC, fabric_id)?;
126 }
127 encoder.end_seq();
128
129 encoder.start_seq(0x30)?; encoder.start_seq(0x30)?; encoder.write_oid("1.2.840.10045.2.1")?;
132 encoder.write_oid("1.2.840.10045.3.1.7")?;
133 encoder.end_seq();
134 let mut pk2 = vec![0u8];
135 pk2.extend_from_slice(public_key);
136 encoder.write_octet_string_with_tag(0x3, &pk2)?;
137 encoder.end_seq();
138
139 let subjectkeyidasn = {
140 let mut encoder = asn1::Encoder::new();
141 encoder.write_octet_string(subject_key_id)?;
142 encoder.encode()
143 };
144
145 let authoritykey_sha1_asn = {
146 let mut encoder = asn1::Encoder::new();
147 encoder.start_seq(0x30)?;
148 encoder.write_octet_string_with_tag(0x80, authority_key_id)?;
149 encoder.encode()
150 };
151
152 encoder.start_seq(0xa3)?;
153 encoder.start_seq(0x30)?;
154 if is_ca {
155 add_ext(
156 &mut encoder,
157 OID_CE_BASIC_CONSTRAINTS,
158 true,
159 &[0x30, 0x03, 0x01, 0x01, 0xFF],
160 )?;
161 add_ext(
162 &mut encoder,
163 OID_CE_KEY_USAGE,
164 true,
165 &[0x03, 0x02, 0x01, 0x06],
166 )?;
167 } else {
168 add_ext(&mut encoder, OID_CE_BASIC_CONSTRAINTS, true, &[0x30, 0x00])?;
169 add_ext(
170 &mut encoder,
171 OID_CE_KEY_USAGE,
172 true,
173 &[0x03, 0x02, 0x07, 0x80],
174 )?;
175 let mut ext_ku_encoder = asn1::Encoder::new();
176 ext_ku_encoder.start_seq(0x30)?;
177 ext_ku_encoder.write_oid("1.3.6.1.5.5.7.3.2")?; ext_ku_encoder.write_oid("1.3.6.1.5.5.7.3.1")?; let ext_ku_bytes = ext_ku_encoder.encode();
180 add_ext(&mut encoder, OID_CE_EXT_KEU_USAGE, true, &ext_ku_bytes)?;
181 }
182 add_ext(
183 &mut encoder,
184 OID_CE_SUBJECT_KEY_IDENTIFIER,
185 false,
186 &subjectkeyidasn,
187 )?;
188 add_ext(
189 &mut encoder,
190 OID_CE_AUTHORITY_KEY_IDENTIFIER,
191 false,
192 &authoritykey_sha1_asn,
193 )?;
194 encoder.end_seq();
195 encoder.end_seq();
196 encoder.end_seq();
197
198 Ok(encoder.encode())
199}
200
201pub fn encode_x509(
203 node_public_key: &[u8],
204 node_id: u64,
205 fabric_id: u64,
206 ca_id: u64,
207 ca_private: &p256::SecretKey,
208 ca: bool,
209) -> Result<Vec<u8>> {
210 let mut encoder = asn1::Encoder::new();
211 encoder.start_seq(0x30)?;
212 encoder.start_seq(0x30)?;
213
214 encoder.start_seq(0xa0)?;
215 encoder.write_int(2)?; encoder.end_seq();
217
218 encoder.write_int(10001)?; encoder.start_seq(0x30)?; encoder.write_oid(OID_SIG_ECDSA_WITH_SHA256)?;
222 encoder.end_seq();
223
224 encoder.start_seq(0x30)?; add_rdn(&mut encoder, OID_MATTER_DN_CA, ca_id)?;
226 encoder.end_seq();
227
228 encoder.start_seq(0x30)?; let now = SystemTime::now();
231 encoder.write_string_with_tag(0x17, &systemtime_to_x509_time(now)?)?;
232 let not_after = now
233 .checked_add(Duration::from_secs(60 * 60 * 24 * 100))
234 .context("time continuity error")?;
235 encoder.write_string_with_tag(0x17, &systemtime_to_x509_time(not_after)?)?;
236 encoder.end_seq();
237
238 if ca {
239 encoder.start_seq(0x30)?; add_rdn(&mut encoder, OID_MATTER_DN_CA, node_id)?;
241 encoder.end_seq();
242 } else {
243 encoder.start_seq(0x30)?; add_rdn(&mut encoder, OID_MATTER_DN_NODE, node_id)?;
245 add_rdn(&mut encoder, OID_MATTER_DN_FABRIC, fabric_id)?;
246 encoder.end_seq();
247 }
248
249 encoder.start_seq(0x30)?; encoder.start_seq(0x30)?; encoder.write_oid("1.2.840.10045.2.1")?;
252 encoder.write_oid("1.2.840.10045.3.1.7")?;
253 encoder.end_seq();
254
255 let mut pk2 = Vec::new();
256 pk2.write_u8(0)?;
257
258 pk2.extend_from_slice(node_public_key);
259 encoder.write_octet_string_with_tag(0x3, &pk2)?;
260 encoder.end_seq();
261
262 let subjectkeyidasn = {
263 let mut encoder = asn1::Encoder::new();
264 encoder.write_octet_string(&cryptoutil::sha1_enc(node_public_key))?;
265 encoder.encode()
266 };
267
268 let authoritykey_sha1_asn = {
269 let mut encoder = asn1::Encoder::new();
270 encoder.start_seq(0x30)?;
271 let pubkey = ca_private.public_key().to_sec1_bytes();
272 encoder.write_octet_string_with_tag(0x80, &cryptoutil::sha1_enc(&pubkey))?;
273 encoder.encode()
274 };
275
276 encoder.start_seq(0xa3)?;
277 encoder.start_seq(0x30)?;
278 if ca {
280 add_ext(
281 &mut encoder,
282 OID_CE_BASIC_CONSTRAINTS,
283 true,
284 &[0x30, 0x03, 0x01, 0x01, 0xFF],
285 )?
286 } else {
287 add_ext(&mut encoder, OID_CE_BASIC_CONSTRAINTS, true, &[0x30, 0x00])?
288 }
289 if ca {
291 add_ext(
292 &mut encoder,
293 OID_CE_KEY_USAGE,
294 true,
295 &[0x03, 0x02, 0x01, 0x06],
296 )?;
297 } else {
298 add_ext(
299 &mut encoder,
300 OID_CE_KEY_USAGE,
301 true,
302 &[0x03, 0x02, 0x07, 0x80],
303 )?;
304 }
305 if !ca {
307 let mut ext_ku_encoder = asn1::Encoder::new();
308 ext_ku_encoder.start_seq(0x30)?;
309 ext_ku_encoder.write_oid("1.3.6.1.5.5.7.3.2")?; ext_ku_encoder.write_oid("1.3.6.1.5.5.7.3.1")?; let ext_ku_bytes = ext_ku_encoder.encode();
312 add_ext(&mut encoder, OID_CE_EXT_KEU_USAGE, true, &ext_ku_bytes)?;
313 }
314 add_ext(
316 &mut encoder,
317 OID_CE_SUBJECT_KEY_IDENTIFIER,
318 false,
319 &subjectkeyidasn,
320 )?;
321
322 add_ext(
324 &mut encoder,
325 OID_CE_AUTHORITY_KEY_IDENTIFIER,
326 false,
327 &authoritykey_sha1_asn,
328 )?;
329
330 encoder.end_seq();
331 encoder.end_seq();
332 encoder.end_seq();
333
334 let to_sign = encoder.clone();
335 let to_sign_bytes = &to_sign.encode()[4..];
336 let key = ecdsa::SigningKey::from(ca_private);
337 let signed = key.sign_recoverable(to_sign_bytes)?.0;
338
339 encoder.start_seq(0x30)?; encoder.write_oid(OID_SIG_ECDSA_WITH_SHA256)?;
341 encoder.end_seq();
342 let mut signed_b = vec![0];
343 signed_b.extend_from_slice(signed.to_der().as_bytes());
344
345 encoder.write_octet_string_with_tag(0x3, &signed_b)?;
346
347 let res = encoder.encode();
348
349 Ok(res)
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 fn der_element(der: &[u8]) -> &[u8] {
357 match der[1] {
358 l if l < 0x80 => &der[..2 + l as usize],
359 0x81 => &der[..3 + der[2] as usize],
360 0x82 => &der[..4 + ((der[2] as usize) << 8) + der[3] as usize],
361 _ => panic!("unsupported DER length"),
362 }
363 }
364
365 #[test]
366 fn test_matter_cert_to_x509_tbs_roundtrip() -> Result<()> {
367 let ca_secret = p256::SecretKey::random(&mut rand::thread_rng());
368 let ca_public = ca_secret.public_key().to_sec1_bytes();
369 for is_ca in [false, true] {
370 let node_secret = p256::SecretKey::random(&mut rand::thread_rng());
371 let node_public = node_secret.public_key().to_sec1_bytes();
372 let x509 = encode_x509(&node_public, 1111, 1234, 5678, &ca_secret, is_ca)?;
373 let matter = crate::cert_matter::convert_x509_bytes_to_matter(&x509, &ca_public)?;
374 let tbs = matter_cert_to_x509_tbs(&matter)?;
375
376 let header = match x509[1] {
377 l if l < 0x80 => 2,
378 0x81 => 3,
379 0x82 => 4,
380 _ => panic!("unsupported DER length"),
381 };
382 let expected_tbs = der_element(&x509[header..]);
383 assert_eq!(tbs, expected_tbs, "reconstructed TBS must match original (is_ca={})", is_ca);
384
385 let cert_tlv = tlv::decode_tlv(&matter)?;
386 let sig = cert_tlv.get_octet_string(&[11]).unwrap();
387 let verifying_key =
388 ecdsa::VerifyingKey::from(p256::PublicKey::from_sec1_bytes(&ca_public)?);
389 let sig = ecdsa::Signature::<p256::NistP256>::from_slice(sig)?;
390 ecdsa::signature::Verifier::verify(&verifying_key, &tbs, &sig)?;
391 }
392 Ok(())
393 }
394}