Skip to main content

structured_pkix/
identifier.rs

1//! The identifiers that say which key signed a certificate and which key it
2//! carries.
3//!
4//! These are hints for finding a candidate issuer, not proof of one. A path is
5//! built by verifying signatures and matching names; an identifier that
6//! matches only says which certificate to try first, and a certificate whose
7//! identifier matches nothing may still be the right one.
8
9use crate::error::{Error, ErrorCode, Located, Result};
10use crate::extension::{self, Extension};
11use crate::general_name::{self, GeneralName};
12use structured_asn1::memory::{Memory, OwnedVec};
13use structured_asn1::{Element, Integer, Tag, universal};
14
15/// An identifier for the key this certificate carries.
16#[derive(Debug, Clone, Copy)]
17pub struct SubjectKeyIdentifier<'a> {
18    bytes: &'a [u8],
19}
20
21impl<'a> SubjectKeyIdentifier<'a> {
22    /// The identifier, as the issuer computed it. How it was computed is the
23    /// issuer's choice and is not stated in the certificate, so it is compared
24    /// rather than recomputed.
25    #[must_use]
26    pub fn as_bytes(self) -> &'a [u8] {
27        self.bytes
28    }
29
30    /// Reads the value of a subject key identifier extension.
31    pub fn read(extension: &Extension<'a>, execution: &Memory) -> Result<Self> {
32        const FIELD: &str = "extensions.subjectKeyIdentifier";
33        let fail = |code| Error::new(code, FIELD, extension.span().start);
34
35        let mut reader = extension.reader(execution).field(FIELD)?;
36        let element = reader
37            .next_element()
38            .field(FIELD)?
39            .ok_or_else(|| fail(ErrorCode::MissingField))?;
40        if element.tag() != Tag::primitive(universal::OCTET_STRING) {
41            return Err(fail(ErrorCode::Encoding(
42                structured_asn1::ErrorCode::UnexpectedTag,
43            )));
44        }
45        if !reader.is_empty() {
46            return Err(fail(ErrorCode::TrailingData));
47        }
48
49        Ok(Self {
50            bytes: element.contents(),
51        })
52    }
53}
54
55/// An identifier for the key that signed this certificate.
56#[derive(Debug)]
57pub struct AuthorityKeyIdentifier<'a> {
58    key_id: Option<&'a [u8]>,
59    issuer: OwnedVec<GeneralName<'a>>,
60    serial: Option<Integer<'a>>,
61}
62
63impl<'a> AuthorityKeyIdentifier<'a> {
64    /// The identifier of the signing key, where the issuer wrote one.
65    #[must_use]
66    pub fn key_id(&self) -> Option<&'a [u8]> {
67        self.key_id
68    }
69
70    /// The names of the certificate that carries the signing key, where the
71    /// issuer wrote them.
72    #[must_use]
73    pub fn issuer(&self) -> &[GeneralName<'a>] {
74        &self.issuer
75    }
76
77    /// The serial number of that certificate, which is written with the names
78    /// or not at all.
79    #[must_use]
80    pub fn serial(&self) -> Option<Integer<'a>> {
81        self.serial
82    }
83
84    /// Reads the value of an authority key identifier extension.
85    pub fn read(extension: &Extension<'a>, execution: &Memory) -> Result<Self> {
86        const FIELD: &str = "extensions.authorityKeyIdentifier";
87        let fail = |code| Error::new(code, FIELD, extension.span().start);
88
89        let mut inside = extension::sequence(extension, FIELD, execution)?;
90        let mut identifier = Self {
91            key_id: None,
92            issuer: inside.memory().vector(),
93            serial: None,
94        };
95
96        // The three fields are written in the order of their numbers, once
97        // each: a tag at or below the last one read is not this schema.
98        let mut last_number = None;
99        while let Some(field) = inside.next_element().field(FIELD)? {
100            let tag = extension::context_of(field.tag());
101            match tag {
102                Some((0, false)) if last_number.is_none() => {
103                    identifier.key_id = Some(field.contents());
104                }
105                Some((1, true)) if last_number < Some(1) => {
106                    identifier.issuer = general_name::read_all(&inside, &field, FIELD)?;
107                }
108                Some((2, false)) if last_number < Some(2) => {
109                    identifier.serial = Some(inside.integer(&field).field(FIELD)?);
110                }
111                _ => return Err(at(FIELD, ErrorCode::UnexpectedField, &field)),
112            }
113            last_number = tag.map(|(number, _)| number);
114        }
115
116        // The names and the serial number identify one certificate together.
117        // Either alone names a certificate the issuer did not mean: a serial
118        // number is unique within one issuer and says nothing without it.
119        if identifier.issuer.is_empty() != identifier.serial.is_none() {
120            return Err(fail(ErrorCode::MalformedValue));
121        }
122
123        // The issuer half of the pair stands for the issuer field of the
124        // identified certificate, which is a distinguished name: without at
125        // least one directory name beside the serial, the pair names no
126        // certificate issuer while claiming to.
127        if !identifier.issuer.is_empty()
128            && !identifier
129                .issuer
130                .iter()
131                .any(|name| matches!(name, general_name::GeneralName::Directory(_)))
132        {
133            return Err(fail(ErrorCode::MalformedValue));
134        }
135
136        // Empty, the structure names no issuing key and no certificate while
137        // wearing the extension that exists to name one: at least the key
138        // identifier or the complete issuer-and-serial pair is required.
139        if identifier.key_id.is_none() && identifier.serial.is_none() {
140            return Err(fail(ErrorCode::MalformedValue));
141        }
142
143        Ok(identifier)
144    }
145}
146
147fn at(field: &'static str, code: ErrorCode, element: &Element<'_>) -> Error {
148    Error::new(code, field, element.span().start)
149}
150
151#[cfg(test)]
152mod tests;