Skip to main content

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}