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 /// A certificate's `not_before` is in the future.
137 #[error("certificate is not yet valid (cert_index={cert_index}, not_before={not_before:?}, at={at:?})")]
138 NotYetValid {
139 /// Index of the offending cert in the chain (0 = leaf).
140 cert_index: u8,
141 /// The certificate's `not_before` timestamp.
142 not_before: MatterTime,
143 /// The time at which validation was attempted.
144 at: MatterTime,
145 },
146
147 /// A certificate's `not_after` is in the past.
148 #[error(
149 "certificate has expired (cert_index={cert_index}, not_after={not_after:?}, at={at:?})"
150 )]
151 Expired {
152 /// Index of the offending cert in the chain (0 = leaf).
153 cert_index: u8,
154 /// The certificate's `not_after` timestamp.
155 not_after: MatterTime,
156 /// The time at which validation was attempted.
157 at: MatterTime,
158 },
159
160 /// A certificate chain did not terminate at a trusted root.
161 #[error("certificate chain does not reach a trusted root")]
162 UntrustedRoot,
163
164 /// A cert's `issuer` did not match the next cert's `subject`.
165 #[error("issuer DN does not match next cert's subject DN (cert_index={cert_index})")]
166 IssuerSubjectMismatch {
167 /// Index of the cert whose `issuer` did not match (0 = leaf).
168 cert_index: u8,
169 },
170
171 /// A non-leaf certificate did not have `basic_constraints.is_ca = true`.
172 #[error("non-leaf certificate is not a CA (cert_index={cert_index})")]
173 NotACa {
174 /// Index of the non-CA intermediate (always > 0).
175 cert_index: u8,
176 },
177
178 /// Chain length exceeded a cert's `path_len_constraint`.
179 #[error("chain length exceeds path-length constraint (cert_index={cert_index})")]
180 PathLengthExceeded {
181 /// Index of the cert whose path-length constraint was violated.
182 cert_index: u8,
183 },
184
185 /// A non-leaf (CA) certificate lacked the `keyCertSign` `KeyUsage` bit.
186 ///
187 /// RFC 5280 §4.2.1.3 and Matter §6.5.5 require any certificate that
188 /// signs other certificates to carry the `keyCertSign` `KeyUsage` bit
189 /// (and a `KeyUsage` extension at all). A cert asserting `is_ca = true`
190 /// but lacking `KeyUsage::KEY_CERT_SIGN` (or with no `KeyUsage` extension)
191 /// is not a valid signing CA and is rejected here.
192 #[error("CA certificate lacks the keyCertSign KeyUsage bit (cert_index={cert_index})")]
193 MissingKeyCertSign {
194 /// Index of the offending CA cert in the chain (always > 0).
195 cert_index: u8,
196 },
197
198 /// The end-entity leaf certificate asserted `basic_constraints.is_ca = true`.
199 ///
200 /// RFC 5280 forbids an end-entity (leaf) certificate from asserting the
201 /// CA bit. A leaf at chain index 0 with an explicit `is_ca = true` is a
202 /// profile violation and is rejected. An absent `basic_constraints`
203 /// extension on the leaf is permitted (it is not a violation).
204 #[error("end-entity leaf certificate asserts is_ca=true (cert_index=0)")]
205 LeafIsCa,
206}
207
208/// `Result<T, Error>` for convenience.
209pub type Result<T> = core::result::Result<T, Error>;