Skip to main content

matter_cert/
error.rs

1//! Error type for `matter-cert`.
2
3use thiserror::Error;
4
5use crate::time::MatterTime;
6
7/// All errors `matter-cert` can produce.
8#[derive(Debug, Error)]
9#[non_exhaustive]
10pub enum Error {
11    /// TLV decoding or encoding failed inside `matter-codec`.
12    #[error("TLV codec error: {0}")]
13    Codec(#[from] matter_codec::Error),
14
15    /// A required certificate field was missing.
16    #[error("missing required certificate field (context tag {0})")]
17    MissingField(u8),
18
19    /// A certificate field appeared more than once.
20    #[error("duplicate certificate field (context tag {0})")]
21    DuplicateField(u8),
22
23    /// A certificate field had an unexpected element type.
24    #[error("invalid TLV element type for certificate field (context tag {0})")]
25    WrongFieldType(u8),
26
27    /// A certificate field's value was outside the spec-defined range.
28    #[error("certificate field value out of range (context tag {tag})")]
29    FieldValueOutOfRange {
30        /// Context tag of the offending field.
31        tag: u8,
32    },
33
34    /// The certificate serial number had a length outside the spec-allowed
35    /// range of 1..=20 bytes.
36    ///
37    /// The Matter operational-certificate profile (§6.5) inherits the X.509
38    /// `CertificateSerialNumber` constraint (RFC 5280 §4.1.2.2): a serial is
39    /// at most 20 octets, and a zero-length serial is not a valid INTEGER.
40    /// We reject both bounds at parse time so a malformed serial cannot
41    /// propagate into the X.509 TBS encoder or signature verification.
42    #[error("certificate serial number length {len} is outside the spec range 1..=20")]
43    InvalidSerialLength {
44        /// The offending serial-number length, in bytes.
45        len: usize,
46    },
47
48    /// Signature algorithm identifier was not `ecdsa-with-sha256` (1).
49    #[error("certificate signature algorithm {0} is not supported")]
50    UnsupportedSignatureAlgorithm(u8),
51
52    /// Public-key algorithm identifier was not `ec-public-key` (1).
53    #[error("certificate public-key algorithm {0} is not supported")]
54    UnsupportedPublicKeyAlgorithm(u8),
55
56    /// EC curve identifier was not `prime256v1` (1).
57    #[error("certificate EC curve {0} is not supported")]
58    UnsupportedEcCurve(u8),
59
60    /// Public-key bytes had wrong length.
61    #[error("public-key bytes have wrong length: expected 65, got {0}")]
62    WrongPublicKeyLength(usize),
63
64    /// Public-key bytes did not start with the uncompressed-point marker (0x04).
65    #[error("public-key bytes do not have the uncompressed-point prefix (0x04)")]
66    BadPublicKeyPrefix,
67
68    /// A required field on `MatterCertificate::builder()` was not set before
69    /// `build_unsigned()` was called.
70    #[error("builder field `{0}` was not set")]
71    MissingBuilderField(&'static str),
72
73    /// Signature bytes had wrong length.
74    #[error("signature bytes have wrong length: expected 64, got {0}")]
75    WrongSignatureLength(usize),
76
77    /// A distinguished-name attribute used a context tag not defined by the spec.
78    #[error("invalid distinguished-name attribute (tag {0})")]
79    InvalidDnAttribute(u8),
80
81    /// A distinguished-name attribute's value had the wrong TLV element type.
82    #[error("invalid TLV type for DN attribute (tag {0})")]
83    InvalidDnAttributeType(u8),
84
85    /// A key identifier had the wrong length (must be 20 bytes).
86    #[error("key identifier has wrong length: expected 20, got {0}")]
87    WrongKeyIdentifierLength(usize),
88
89    /// A Matter DN attribute had no defined X.509 OID mapping.
90    ///
91    /// Occurs when a [`crate::DnAttribute::Other`] is encountered during
92    /// X.509 conversion. We cannot invent an X.509 OID, and matter.js
93    /// wouldn't have signed against one we made up.
94    #[error("Matter DN attribute (tag {0}) has no defined X.509 OID mapping")]
95    DnAttributeHasNoX509Oid(u8),
96
97    /// A DN attribute belongs only to X.509 attestation certificates and
98    /// has no Matter operational-TLV cert encoding.
99    ///
100    /// Produced if [`crate::DnAttribute::VendorId`] or
101    /// [`crate::DnAttribute::ProductId`] is routed through the Matter TLV
102    /// writer. VID/PID identifiers live in DAC/PAI/PAA X.509 attestation
103    /// cert DNs (Matter §6.5.6.1), not in operational NOC/ICAC/RCAC TLV
104    /// certs, so there is no spec-defined TLV context tag for them.
105    #[error("DN attribute (tag {0}) is X.509-attestation-only and has no Matter TLV encoding")]
106    DnAttributeNotTlvEncodable(&'static str),
107
108    /// A DN attribute's value cannot be encoded in its X.509 ASN.1
109    /// string type.
110    ///
111    /// E.g., a `CountryName` containing non-printable bytes cannot
112    /// be encoded as `PrintableString`.
113    #[error("DN attribute value cannot be encoded as X.509 {asn1_type}: {reason}")]
114    InvalidDnAttributeForX509 {
115        /// The ASN.1 string type that the encoding attempt targeted.
116        asn1_type: &'static str,
117        /// Why the value did not fit.
118        reason: &'static str,
119    },
120
121    /// Signature verification failed.
122    ///
123    /// Reserved for M2.2; not produced by phase 1.
124    #[error("signature verification failed")]
125    SignatureVerificationFailed,
126
127    /// Test-support X.509 cert signing failed.
128    ///
129    /// Produced only by `test_support::build_x509_der` (behind the
130    /// `test-support` feature) when the supplied issuer PKCS#8 key is
131    /// malformed or `ring` rejects the signing request. Never produced by
132    /// production code paths.
133    #[error("test-support X.509 signing failed: {0}")]
134    TestX509SigningFailed(&'static str),
135
136    /// Production ECDSA-P256-SHA256 signing via `ring` failed.
137    ///
138    /// Produced by [`crate::operational::sign_with_ring`] when the supplied
139    /// issuer PKCS#8 key is malformed, or `ring` rejects the signing
140    /// request.
141    #[error("certificate signing failed: {0}")]
142    SigningFailed(&'static str),
143
144    /// A certificate's `not_before` is in the future.
145    #[error("certificate is not yet valid (cert_index={cert_index}, not_before={not_before:?}, at={at:?})")]
146    NotYetValid {
147        /// Index of the offending cert in the chain (0 = leaf).
148        cert_index: u8,
149        /// The certificate's `not_before` timestamp.
150        not_before: MatterTime,
151        /// The time at which validation was attempted.
152        at: MatterTime,
153    },
154
155    /// A certificate's `not_after` is in the past.
156    #[error(
157        "certificate has expired (cert_index={cert_index}, not_after={not_after:?}, at={at:?})"
158    )]
159    Expired {
160        /// Index of the offending cert in the chain (0 = leaf).
161        cert_index: u8,
162        /// The certificate's `not_after` timestamp.
163        not_after: MatterTime,
164        /// The time at which validation was attempted.
165        at: MatterTime,
166    },
167
168    /// A certificate chain did not terminate at a trusted root.
169    #[error("certificate chain does not reach a trusted root")]
170    UntrustedRoot,
171
172    /// A cert's `issuer` did not match the next cert's `subject`.
173    #[error("issuer DN does not match next cert's subject DN (cert_index={cert_index})")]
174    IssuerSubjectMismatch {
175        /// Index of the cert whose `issuer` did not match (0 = leaf).
176        cert_index: u8,
177    },
178
179    /// A non-leaf certificate did not have `basic_constraints.is_ca = true`.
180    #[error("non-leaf certificate is not a CA (cert_index={cert_index})")]
181    NotACa {
182        /// Index of the non-CA intermediate (always > 0).
183        cert_index: u8,
184    },
185
186    /// Chain length exceeded a cert's `path_len_constraint`.
187    #[error("chain length exceeds path-length constraint (cert_index={cert_index})")]
188    PathLengthExceeded {
189        /// Index of the cert whose path-length constraint was violated.
190        cert_index: u8,
191    },
192
193    /// A non-leaf (CA) certificate lacked the `keyCertSign` `KeyUsage` bit.
194    ///
195    /// RFC 5280 §4.2.1.3 and Matter §6.5.5 require any certificate that
196    /// signs other certificates to carry the `keyCertSign` `KeyUsage` bit
197    /// (and a `KeyUsage` extension at all). A cert asserting `is_ca = true`
198    /// but lacking `KeyUsage::KEY_CERT_SIGN` (or with no `KeyUsage` extension)
199    /// is not a valid signing CA and is rejected here.
200    #[error("CA certificate lacks the keyCertSign KeyUsage bit (cert_index={cert_index})")]
201    MissingKeyCertSign {
202        /// Index of the offending CA cert in the chain (always > 0).
203        cert_index: u8,
204    },
205
206    /// The end-entity leaf certificate asserted `basic_constraints.is_ca = true`.
207    ///
208    /// RFC 5280 forbids an end-entity (leaf) certificate from asserting the
209    /// CA bit. A leaf at chain index 0 with an explicit `is_ca = true` is a
210    /// profile violation and is rejected. An absent `basic_constraints`
211    /// extension on the leaf is permitted (it is not a violation).
212    #[error("end-entity leaf certificate asserts is_ca=true (cert_index=0)")]
213    LeafIsCa,
214}
215
216/// `Result<T, Error>` for convenience.
217pub type Result<T> = core::result::Result<T, Error>;