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