1use bitflags::bitflags;
11use matter_codec::{Element, Tag, TlvReader, TlvWriter, Value};
12
13use crate::error::{Error, Result};
14use crate::tlv_tags as tags;
15
16#[derive(Debug, Clone, PartialEq, Eq, Default)]
24#[non_exhaustive]
25pub struct Extensions {
26 pub basic_constraints: Option<BasicConstraints>,
28 pub key_usage: Option<KeyUsage>,
30 pub extended_key_usage: Option<Vec<u32>>,
32 pub subject_key_identifier: Option<KeyIdentifier>,
34 pub authority_key_identifier: Option<KeyIdentifier>,
36}
37
38#[derive(Debug, Clone, Default)]
44pub struct ExtensionsBuilder(Extensions);
45
46impl ExtensionsBuilder {
47 #[must_use]
49 pub fn basic_constraints(mut self, v: Option<BasicConstraints>) -> Self {
50 self.0.basic_constraints = v;
51 self
52 }
53
54 #[must_use]
56 pub fn key_usage(mut self, v: Option<KeyUsage>) -> Self {
57 self.0.key_usage = v;
58 self
59 }
60
61 #[must_use]
63 pub fn extended_key_usage(mut self, v: Option<Vec<u32>>) -> Self {
64 self.0.extended_key_usage = v;
65 self
66 }
67
68 #[must_use]
70 pub fn subject_key_identifier(mut self, v: Option<KeyIdentifier>) -> Self {
71 self.0.subject_key_identifier = v;
72 self
73 }
74
75 #[must_use]
77 pub fn authority_key_identifier(mut self, v: Option<KeyIdentifier>) -> Self {
78 self.0.authority_key_identifier = v;
79 self
80 }
81
82 #[must_use]
84 pub fn build(self) -> Extensions {
85 self.0
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91#[non_exhaustive]
92pub struct BasicConstraints {
93 pub is_ca: bool,
95 pub path_len_constraint: Option<u8>,
98}
99
100impl BasicConstraints {
101 #[must_use]
108 pub const fn new(is_ca: bool, path_len_constraint: Option<u8>) -> Self {
109 Self {
110 is_ca,
111 path_len_constraint,
112 }
113 }
114}
115
116bitflags! {
117 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
120 pub struct KeyUsage: u16 {
121 const DIGITAL_SIGNATURE = 0x0001;
123 const CONTENT_COMMITMENT = 0x0002;
126 const KEY_ENCIPHERMENT = 0x0004;
128 const DATA_ENCIPHERMENT = 0x0008;
130 const KEY_AGREEMENT = 0x0010;
132 const KEY_CERT_SIGN = 0x0020;
135 const CRL_SIGN = 0x0040;
138 const ENCIPHER_ONLY = 0x0080;
141 const DECIPHER_ONLY = 0x0100;
144 }
145}
146
147#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
150pub struct KeyIdentifier(pub [u8; 20]);
151
152impl KeyIdentifier {
153 pub fn from_slice(slice: &[u8]) -> Result<Self> {
160 let bytes: [u8; 20] = slice
161 .try_into()
162 .map_err(|_| Error::WrongKeyIdentifierLength(slice.len()))?;
163 Ok(Self(bytes))
164 }
165}
166
167impl Extensions {
168 #[must_use]
174 pub fn builder() -> ExtensionsBuilder {
175 ExtensionsBuilder(Self::default())
176 }
177
178 #[allow(dead_code)]
186 pub(crate) fn read(reader: &mut TlvReader<'_>) -> Result<Self> {
187 match reader.next()? {
188 Some(Element::ContainerStart {
189 kind: matter_codec::ContainerKind::List,
190 ..
191 }) => {}
192 _ => return Err(Error::WrongFieldType(tags::CERT_EXTENSIONS)),
193 }
194 Self::read_from_open_list(reader)
195 }
196
197 pub(crate) fn read_from_open_list(reader: &mut TlvReader<'_>) -> Result<Self> {
206 let mut out = Self::default();
207 loop {
208 match reader.next()? {
209 None => return Err(matter_codec::Error::UnclosedContainer.into()),
210 Some(Element::ContainerEnd) => break,
211 Some(Element::ContainerStart {
212 tag: Tag::Context(t),
213 kind: matter_codec::ContainerKind::Structure,
214 }) if t == tags::EXT_BASIC_CONSTRAINTS => {
215 if out.basic_constraints.is_some() {
216 return Err(Error::DuplicateField(t));
217 }
218 out.basic_constraints = Some(BasicConstraints::read_body(reader)?);
219 }
220 Some(Element::Scalar {
221 tag: Tag::Context(t),
222 value: Value::Uint(bits),
223 }) if t == tags::EXT_KEY_USAGE => {
224 if out.key_usage.is_some() {
225 return Err(Error::DuplicateField(t));
226 }
227 let bits16 =
228 u16::try_from(bits).map_err(|_| Error::FieldValueOutOfRange { tag: t })?;
229 out.key_usage = Some(KeyUsage::from_bits_truncate(bits16));
230 }
231 Some(Element::ContainerStart {
232 tag: Tag::Context(t),
233 kind: matter_codec::ContainerKind::Array,
234 }) if t == tags::EXT_EXTENDED_KEY_USAGE => {
235 if out.extended_key_usage.is_some() {
236 return Err(Error::DuplicateField(t));
237 }
238 out.extended_key_usage = Some(read_uint_array(reader)?);
239 }
240 Some(Element::Scalar {
241 tag: Tag::Context(t),
242 value: Value::Bytes(b),
243 }) if t == tags::EXT_SUBJECT_KEY_IDENTIFIER => {
244 if out.subject_key_identifier.is_some() {
245 return Err(Error::DuplicateField(t));
246 }
247 out.subject_key_identifier = Some(KeyIdentifier::from_slice(&b)?);
248 }
249 Some(Element::Scalar {
250 tag: Tag::Context(t),
251 value: Value::Bytes(b),
252 }) if t == tags::EXT_AUTHORITY_KEY_IDENTIFIER => {
253 if out.authority_key_identifier.is_some() {
254 return Err(Error::DuplicateField(t));
255 }
256 out.authority_key_identifier = Some(KeyIdentifier::from_slice(&b)?);
257 }
258 Some(elem) => {
259 let tag_num = element_tag_number(&elem);
260 return Err(Error::WrongFieldType(tag_num));
261 }
262 }
263 }
264 Ok(out)
265 }
266
267 pub(crate) fn write(&self, writer: &mut TlvWriter<'_>, outer_tag: Tag) -> Result<()> {
273 writer.start_list(outer_tag)?;
274 if let Some(bc) = self.basic_constraints {
275 bc.write_body(writer, Tag::Context(tags::EXT_BASIC_CONSTRAINTS))?;
276 }
277 if let Some(ku) = self.key_usage {
278 writer.put_uint(Tag::Context(tags::EXT_KEY_USAGE), u64::from(ku.bits()))?;
279 }
280 if let Some(eku) = &self.extended_key_usage {
281 writer.start_array(Tag::Context(tags::EXT_EXTENDED_KEY_USAGE))?;
282 for oid in eku {
283 writer.put_uint(Tag::Anonymous, u64::from(*oid))?;
284 }
285 writer.end_container()?;
286 }
287 if let Some(ski) = &self.subject_key_identifier {
288 writer.put_bytes(Tag::Context(tags::EXT_SUBJECT_KEY_IDENTIFIER), &ski.0)?;
289 }
290 if let Some(aki) = &self.authority_key_identifier {
291 writer.put_bytes(Tag::Context(tags::EXT_AUTHORITY_KEY_IDENTIFIER), &aki.0)?;
292 }
293 writer.end_container()?;
294 Ok(())
295 }
296}
297
298impl BasicConstraints {
299 fn read_body(reader: &mut TlvReader<'_>) -> Result<Self> {
300 let mut is_ca = false;
301 let mut path_len = None;
302 loop {
303 match reader.next()? {
304 None => return Err(matter_codec::Error::UnclosedContainer.into()),
305 Some(Element::ContainerEnd) => break,
306 Some(Element::Scalar {
307 tag: Tag::Context(t),
308 value: Value::Bool(b),
309 }) if t == tags::BC_IS_CA => {
310 is_ca = b;
311 }
312 Some(Element::Scalar {
313 tag: Tag::Context(t),
314 value: Value::Uint(v),
315 }) if t == tags::BC_PATH_LEN_CONSTRAINT => {
316 let v8 = u8::try_from(v).map_err(|_| Error::FieldValueOutOfRange { tag: t })?;
317 path_len = Some(v8);
318 }
319 Some(_) => {
320 return Err(Error::WrongFieldType(tags::EXT_BASIC_CONSTRAINTS));
321 }
322 }
323 }
324 Ok(Self {
325 is_ca,
326 path_len_constraint: path_len,
327 })
328 }
329
330 fn write_body(self, writer: &mut TlvWriter<'_>, outer_tag: Tag) -> Result<()> {
332 writer.start_structure(outer_tag)?;
333 writer.put_bool(Tag::Context(tags::BC_IS_CA), self.is_ca)?;
334 if let Some(plc) = self.path_len_constraint {
335 writer.put_uint(Tag::Context(tags::BC_PATH_LEN_CONSTRAINT), u64::from(plc))?;
336 }
337 writer.end_container()?;
338 Ok(())
339 }
340}
341
342fn read_uint_array(reader: &mut TlvReader<'_>) -> Result<Vec<u32>> {
344 let mut out = Vec::new();
345 loop {
346 match reader.next()? {
347 None => return Err(matter_codec::Error::UnclosedContainer.into()),
348 Some(Element::ContainerEnd) => break,
349 Some(Element::Scalar {
350 value: Value::Uint(v),
351 ..
352 }) => {
353 let v32 = u32::try_from(v).map_err(|_| Error::FieldValueOutOfRange {
354 tag: tags::EXT_EXTENDED_KEY_USAGE,
355 })?;
356 out.push(v32);
357 }
358 Some(_) => return Err(Error::WrongFieldType(tags::EXT_EXTENDED_KEY_USAGE)),
359 }
360 }
361 Ok(out)
362}
363
364fn element_tag_number(elem: &Element) -> u8 {
366 match elem {
367 Element::Scalar { tag, .. } | Element::ContainerStart { tag, .. } => match tag {
368 Tag::Context(n) => *n,
369 _ => 0,
370 },
371 _ => 0,
373 }
374}
375
376#[cfg(test)]
377#[allow(clippy::unwrap_used)] mod tests {
379 use super::*;
380
381 fn round_trip(ext: &Extensions) {
382 let mut buf = Vec::new();
383 {
384 let mut w = TlvWriter::new(&mut buf);
385 ext.write(&mut w, Tag::Anonymous).unwrap();
386 }
387 let mut r = TlvReader::new(&buf);
388 let parsed = Extensions::read(&mut r).unwrap();
389 assert_eq!(parsed, *ext);
390 }
391
392 #[test]
393 fn round_trip_empty() {
394 round_trip(&Extensions::default());
395 }
396
397 #[test]
398 fn round_trip_basic_constraints_only() {
399 round_trip(&Extensions {
400 basic_constraints: Some(BasicConstraints {
401 is_ca: true,
402 path_len_constraint: Some(3),
403 }),
404 ..Default::default()
405 });
406 }
407
408 #[test]
409 fn round_trip_key_usage_only() {
410 round_trip(&Extensions {
411 key_usage: Some(KeyUsage::DIGITAL_SIGNATURE | KeyUsage::KEY_CERT_SIGN),
412 ..Default::default()
413 });
414 }
415
416 #[test]
417 fn round_trip_extended_key_usage_only() {
418 round_trip(&Extensions {
419 extended_key_usage: Some(vec![1, 2, 3]),
420 ..Default::default()
421 });
422 }
423
424 #[test]
425 fn round_trip_key_identifiers() {
426 round_trip(&Extensions {
427 subject_key_identifier: Some(KeyIdentifier([0xAB; 20])),
428 authority_key_identifier: Some(KeyIdentifier([0xCD; 20])),
429 ..Default::default()
430 });
431 }
432
433 #[test]
434 fn round_trip_all_extensions() {
435 round_trip(&Extensions {
436 basic_constraints: Some(BasicConstraints {
437 is_ca: true,
438 path_len_constraint: None,
439 }),
440 key_usage: Some(KeyUsage::DIGITAL_SIGNATURE),
441 extended_key_usage: Some(vec![42]),
442 subject_key_identifier: Some(KeyIdentifier([0x11; 20])),
443 authority_key_identifier: Some(KeyIdentifier([0x22; 20])),
444 });
445 }
446}