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