Skip to main content

structured_pkix/
key.rs

1//! The public key a certificate carries.
2//!
3//! The key stays as it was encoded: re-encoding it here would change what a
4//! key identifier is computed over. [`rsa`], [`ec`], [`dsa`] and [`rfc8410`]
5//! read its structure in place; whether a key is usable remains the algorithm
6//! registry's decision.
7
8use crate::algorithm::{self, AlgorithmIdentifier};
9use crate::error::{Error, ErrorCode, Located, Result};
10use structured_asn1::memory::Work;
11use structured_asn1::{BitString, Element, Oid, Reader, Tag, universal};
12
13pub mod dsa;
14pub mod ec;
15pub mod ml_dsa;
16pub mod rfc8410;
17pub mod rsa;
18
19/// A public key and the algorithm it belongs to.
20#[derive(Debug, Clone, Copy)]
21pub struct PublicKeyInfo<'a> {
22    algorithm: AlgorithmIdentifier<'a>,
23    key: BitString<'a>,
24    der: &'a [u8],
25    // Document offset of the first key octet, after the unused-bits octet.
26    key_offset: u64,
27}
28
29impl<'a> PublicKeyInfo<'a> {
30    /// Which algorithm this key belongs to.
31    #[must_use]
32    pub fn algorithm(self) -> AlgorithmIdentifier<'a> {
33        self.algorithm
34    }
35
36    /// The key itself.
37    #[must_use]
38    pub fn key(self) -> BitString<'a> {
39        self.key
40    }
41
42    /// The key bytes, where the key is a whole number of octets, which it is
43    /// for every algorithm a certificate carries.
44    #[must_use]
45    pub fn key_octets(self) -> Option<&'a [u8]> {
46        self.key.octets()
47    }
48
49    /// Where the first key octet sits in the whole document.
50    #[must_use]
51    pub fn key_offset(self) -> u64 {
52        self.key_offset
53    }
54
55    /// Whole key octets, or a located refusal of a padded bit string.
56    pub(crate) fn whole_octets(self, field: &'static str) -> Result<&'a [u8]> {
57        self.key_octets()
58            .ok_or_else(|| Error::new(ErrorCode::MalformedValue, field, self.key_offset))
59    }
60
61    /// The whole structure as it was encoded.
62    ///
63    /// What a fingerprint over the key information is taken of. The key
64    /// identifier the profile describes is a digest of [`Self::key_octets`]
65    /// instead, and the two are different values over different bytes.
66    #[must_use]
67    pub fn as_bytes(self) -> &'a [u8] {
68        self.der
69    }
70    /// Reads SPKI from an element in the caller's reader, retaining its budget.
71    ///
72    /// The structure follows RFC 5280 section 4.1.2.7:
73    /// <https://www.rfc-editor.org/rfc/rfc5280.html#section-4.1.2.7>.
74    /// Algorithm-specific restrictions belong to the cryptographic consumer.
75    /// No key bytes are copied or re-encoded, and no new parser budget is created.
76    ///
77    /// # Errors
78    ///
79    /// Reports malformed structure, trailing fields or an exhausted parent budget.
80    pub fn read_element(
81        parent: &Reader<'a>,
82        element: &Element<'a>,
83        field: &'static str,
84    ) -> Result<PublicKeyInfo<'a>> {
85        let fail = |code| Error::new(code, field, element.span().start);
86
87        if element.tag() != Tag::constructed(universal::SEQUENCE) {
88            return Err(fail(ErrorCode::Encoding(
89                structured_asn1::ErrorCode::UnexpectedTag,
90            )));
91        }
92
93        let mut inside = parent.enter(element).field(field)?;
94        let algorithm_element = inside
95            .next_element()
96            .field(field)?
97            .ok_or_else(|| fail(ErrorCode::MissingField))?;
98        let algorithm = algorithm::read(&inside, &algorithm_element, field)?;
99        let key_element = inside
100            .expect(Tag::primitive(universal::BIT_STRING))
101            .field(field)?;
102        let key = inside.bit_string(&key_element).field(field)?;
103
104        if !inside.is_empty() {
105            return Err(fail(ErrorCode::TrailingData));
106        }
107
108        Ok(PublicKeyInfo {
109            algorithm,
110            key,
111            der: element.as_bytes(),
112            // A primitive BIT STRING: one unused-bits octet, then the bits.
113            key_offset: key_element.contents_span().start + 1,
114        })
115    }
116}
117
118/// Paid identity of two OIDs: the length check, then the octets compared.
119pub(crate) fn same_oid<W: Work>(
120    left: Oid<'_>,
121    right: Oid<'_>,
122    execution: &W,
123    field: &'static str,
124    at: u64,
125) -> Result<bool> {
126    let charge = |amount| {
127        execution
128            .charge_work(amount)
129            .map_err(|reason| Error::resource(reason, field, at))
130    };
131    charge(1)?;
132    if left.as_bytes().len() != right.as_bytes().len() {
133        return Ok(false);
134    }
135    charge(left.as_bytes().len() as u64)?;
136    Ok(left == right)
137}
138
139#[cfg(test)]
140mod tests;