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