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