rs_matter/cert/x509.rs
1/*
2 *
3 * Copyright (c) 2026 Project CHIP Authors
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18//! X.509 DER certificate parsing utilities for extracting Matter-specific data.
19
20use crate::crypto::PKC_CANON_PUBLIC_KEY_LEN;
21use crate::error::{Error, ErrorCode};
22
23use der::asn1::{AnyRef, BitStringRef, GeneralizedTime, ObjectIdentifier, OctetStringRef, UtcTime};
24use der::{Choice, Sequence, Tag};
25
26pub mod cert;
27pub mod csr;
28
29/// OID 1.2.840.10045.4.3.2 — ECDSA with SHA256
30const OID_ECDSA_WITH_SHA256: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.2");
31/// OID 1.2.840.10045.2.1 — ecPublicKey (Elliptic Curve Public Key)
32const OID_EC_PUBLIC_KEY: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.2.1");
33/// OID 1.2.840.10045.3.1.7 — prime256v1 (secp256r1 / P-256)
34const OID_PRIME256V1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
35
36/// OID 2.5.29.14 — Subject Key Identifier
37const OID_SUBJECT_KEY_ID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.5.29.14");
38/// OID 2.5.29.35 — Authority Key Identifier
39const OID_AUTHORITY_KEY_ID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.5.29.35");
40/// OID 2.5.29.19 — Basic Constraints
41const OID_BASIC_CONSTRAINTS: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.5.29.19");
42/// OID 2.5.29.15 — Key Usage
43const OID_KEY_USAGE: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.5.29.15");
44
45/// Matter uses the P-256 uncompressed public key length
46/// (0x04 || X || Y = 65 bytes)
47const P256_PUBLIC_KEY_LEN: usize = PKC_CANON_PUBLIC_KEY_LEN;
48
49/// AlgorithmIdentifier ::= SEQUENCE {
50/// algorithm OBJECT IDENTIFIER,
51/// parameters ANY DEFINED BY algorithm OPTIONAL
52/// }
53///
54/// https://www.rfc-editor.org/rfc/rfc5280#appendix-A.1
55#[derive(Sequence)]
56pub struct AlgorithmIdentifier<'a> {
57 pub algorithm: ObjectIdentifier,
58 pub parameters: Option<AnyRef<'a>>,
59}
60
61/// SubjectPublicKeyInfo ::= SEQUENCE {
62/// algorithm AlgorithmIdentifier,
63/// subjectPublicKey BIT STRING
64/// }
65/// https://www.rfc-editor.org/rfc/rfc5280#appendix-A.1
66struct SubjectPublicKeyInfo<'a> {
67 algorithm: AlgorithmIdentifier<'a>,
68 subject_public_key: BitStringRef<'a>,
69}
70
71impl<'a> der::FixedTag for SubjectPublicKeyInfo<'a> {
72 const TAG: Tag = Tag::Sequence;
73}
74
75/// AttributeTypeAndValue ::= SEQUENCE {
76/// type OBJECT IDENTIFIER,
77/// value ANY
78/// }
79///
80/// https://www.rfc-editor.org/rfc/rfc5280#section-4.1.2.4
81#[derive(Sequence)]
82struct AttributeTypeAndValue<'a> {
83 oid: ObjectIdentifier,
84 value: AnyRef<'a>,
85}
86
87/// BasicConstraints ::= SEQUENCE {
88/// cA BOOLEAN DEFAULT FALSE,
89/// pathLenConstraint INTEGER (0..MAX) OPTIONAL
90/// }
91///
92/// https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.9
93#[derive(Sequence)]
94struct BasicConstraints {
95 #[asn1(default = "default_false")]
96 ca: bool,
97 path_len_constraint: Option<u8>,
98}
99
100fn default_false() -> bool {
101 false
102}
103
104/// X.509 KeyUsage bit flags in DER BIT STRING format.
105///
106/// In X.509 DER encoding, bit 0 is the MSB (leftmost bit) in the bit string.
107/// When represented as a u16, bit 0 corresponds to 0x8000.
108///
109/// KeyUsage ::= BIT STRING {
110/// digitalSignature (0),
111/// nonRepudiation (1),
112/// keyEncipherment (2),
113/// dataEncipherment (3),
114/// keyAgreement (4),
115/// keyCertSign (5),
116/// cRLSign (6),
117/// encipherOnly (7),
118/// decipherOnly (8)
119/// }
120/// https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.3
121pub mod key_usage_der {
122 pub const DIGITAL_SIGNATURE: u16 = 0x8000;
123 pub const NON_REPUDIATION: u16 = 0x4000;
124 pub const KEY_ENCIPHERMENT: u16 = 0x2000;
125 pub const DATA_ENCIPHERMENT: u16 = 0x1000;
126 pub const KEY_AGREEMENT: u16 = 0x0800;
127 pub const KEY_CERT_SIGN: u16 = 0x0400;
128 pub const CRL_SIGN: u16 = 0x0200;
129 pub const ENCIPHER_ONLY: u16 = 0x0100;
130 pub const DECIPHER_ONLY: u16 = 0x0080;
131}
132
133/// Matter TLV KeyUsage bit flags.
134///
135/// In Matter TLV encoding, the KeyUsage is stored as a plain u16 value
136/// where bit 0 is the LSB (standard bit numbering).
137///
138/// Bit positions follow the same naming as X.509 but use standard u16 bit positions.
139pub mod key_usage_tlv {
140 pub const DIGITAL_SIGNATURE: u16 = 0x0001;
141 pub const NON_REPUDIATION: u16 = 0x0002;
142 pub const KEY_ENCIPHERMENT: u16 = 0x0004;
143 pub const DATA_ENCIPHERMENT: u16 = 0x0008;
144 pub const KEY_AGREEMENT: u16 = 0x0010;
145 pub const KEY_CERT_SIGN: u16 = 0x0020;
146 pub const CRL_SIGN: u16 = 0x0040;
147 pub const ENCIPHER_ONLY: u16 = 0x0080;
148 pub const DECIPHER_ONLY: u16 = 0x0100;
149}
150
151struct KeyUsage {
152 bits: u16,
153}
154
155impl KeyUsage {
156 fn digital_signature(&self) -> bool {
157 self.bits & key_usage_der::DIGITAL_SIGNATURE != 0
158 }
159
160 fn key_cert_sign(&self) -> bool {
161 self.bits & key_usage_der::KEY_CERT_SIGN != 0
162 }
163
164 fn crl_sign(&self) -> bool {
165 self.bits & key_usage_der::CRL_SIGN != 0
166 }
167
168 /// Check that only the specified bits are set (exact match)
169 fn has_only_bits(&self, mask: u16) -> bool {
170 self.bits == mask
171 }
172}
173
174impl<'a> From<BitStringRef<'a>> for KeyUsage {
175 fn from(bs: BitStringRef<'a>) -> Self {
176 let bytes = bs.raw_bytes();
177 let unused = bs.unused_bits();
178
179 // Reject malformed bitstrings longer than 2 bytes since KeyUsage has a 9 bit max
180 if bytes.len() > 2 {
181 // Return empty KeyUsage for invalid input
182 return Self { bits: 0 };
183 }
184
185 // Load bytes big-endian
186 let mut buf = [0u8; 2];
187 let len = bytes.len();
188 buf[..len].copy_from_slice(&bytes[..len]);
189
190 let mut bits = u16::from_be_bytes(buf);
191
192 // Mask off unused padding bits in the last byte
193 if unused > 0 && len > 0 {
194 // For len=1: unused bits are in byte[0] (upper 8 bits of u16)
195 // For len=2: unused bits are in byte[1] (lower 8 bits of u16)
196 let shift = if len == 1 { 8 } else { 0 };
197 let mask = !((1u16 << unused) - 1) << shift;
198 bits &= mask;
199 }
200
201 Self { bits }
202 }
203}
204
205/// AuthorityKeyIdentifier ::= SEQUENCE {
206/// keyIdentifier [0] KeyIdentifier OPTIONAL,
207/// authorityCertIssuer [1] GeneralNames OPTIONAL,
208/// authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL
209/// }
210///
211/// KeyIdentifier ::= OCTET STRING
212///
213/// We only need the keyIdentifier field. The `[0]` is IMPLICIT.
214#[derive(Sequence)]
215struct AuthorityKeyIdentifier<'a> {
216 #[asn1(context_specific = "0", tag_mode = "IMPLICIT")]
217 key_identifier: OctetStringRef<'a>,
218}
219
220/// Time ::= CHOICE { utcTime UTCTime, generalTime GeneralizedTime }
221#[derive(Choice)]
222enum Time {
223 #[asn1(type = "UTCTime")]
224 Utc(UtcTime),
225 #[asn1(type = "GeneralizedTime")]
226 General(GeneralizedTime),
227}
228
229/// Validity ::= SEQUENCE {
230/// notBefore Time,
231/// notAfter Time
232/// }
233#[derive(Sequence)]
234struct Validity {
235 not_before: Time,
236 not_after: Time,
237}
238
239/// Parse a hex character (0-9, A-F, a-f) into its numeric value.
240fn hex_digit(b: u8) -> Result<u8, Error> {
241 match b {
242 b'0'..=b'9' => Ok(b - b'0'),
243 b'A'..=b'F' => Ok(b - b'A' + 10),
244 b'a'..=b'f' => Ok(b - b'a' + 10),
245 _ => Err(ErrorCode::InvalidData.into()),
246 }
247}
248
249/// Parse a hex string into a u16.
250///
251/// Used for Vendor ID and Product ID which are stored as UTF8String
252/// hex values in the Subject DN.
253fn parse_hex_u16(s: &[u8]) -> Result<u16, Error> {
254 if s.len() != 4 {
255 return Err(ErrorCode::InvalidData.into());
256 }
257
258 let mut val: u16 = 0;
259 // build hex number
260 for &b in s {
261 val = val << 4 | hex_digit(b)? as u16;
262 }
263 Ok(val)
264}
265
266/// Convert a `Time` value (UTCTime or GeneralizedTime) to Unix epoch seconds (u64).
267///
268/// For GeneralizedTime with year 9999 (DateTime::INFINITY), returns `u64::MAX`
269/// to indicate no expiry.
270fn time_to_unix_secs(time: &Time) -> Result<u64, Error> {
271 let dt = match time {
272 Time::Utc(utc) => utc.to_date_time(),
273 Time::General(gt) => gt.to_date_time(),
274 };
275
276 // Check for the "no expiry" sentinel: 9999-12-31T23:59:59Z
277 if dt == der::DateTime::INFINITY {
278 return Ok(u64::MAX);
279 }
280
281 Ok(dt.unix_duration().as_secs())
282}