structured_pkix/key/rsa.rs
1//! The RSA public key a certificate carries, read in place.
2//!
3//! RFC 8017 A.1.1 defines `RSAPublicKey ::= SEQUENCE { modulus INTEGER,
4//! publicExponent INTEGER }`; RFC 4055 §1.2 carries the same structure for
5//! `rsaEncryption` and `id-RSASSA-PSS` keys. [`read`] returns the original
6//! magnitudes and where they were written. Key size, exponent policy and every
7//! arithmetic check belong to the provider that verifies with the key.
8//! <https://www.rfc-editor.org/rfc/rfc8017#appendix-A.1.1>
9
10use super::{PublicKeyInfo, same_oid};
11use crate::error::{Error, ErrorCode, Located, Result};
12use structured_asn1::memory::Work;
13use structured_asn1::{Element, ScopedReader, Tag, universal};
14
15const FIELD: &str = "subjectPublicKey.rsa";
16
17/// A positive integer as its big-endian magnitude, without the sign octet.
18#[derive(Debug, Clone, Copy)]
19pub struct Magnitude<'a> {
20 bytes: &'a [u8],
21 offset: u64,
22}
23
24impl<'a> Magnitude<'a> {
25 /// Big-endian magnitude; the first octet is never zero.
26 #[must_use]
27 pub fn as_bytes(self) -> &'a [u8] {
28 self.bytes
29 }
30
31 /// Where the first magnitude octet sits in the whole document.
32 #[must_use]
33 pub fn offset(self) -> u64 {
34 self.offset
35 }
36
37 /// The exact number of significant bits. A 256-octet modulus whose first
38 /// octet is `0x01` has 2041 bits, not 2048.
39 #[must_use]
40 pub fn bits(self) -> u64 {
41 // Nonempty with a nonzero first octet by construction.
42 (self.bytes.len() as u64 - 1) * 8 + u64::from(8 - self.bytes[0].leading_zeros())
43 }
44}
45
46/// The two components of an RSA public key, as the certificate wrote them.
47#[derive(Debug, Clone, Copy)]
48pub struct PublicKey<'a> {
49 element: Element<'a>,
50 modulus: Magnitude<'a>,
51 exponent: Magnitude<'a>,
52}
53
54impl<'a> PublicKey<'a> {
55 /// The whole original `RSAPublicKey` SEQUENCE inside the key bits.
56 #[must_use]
57 pub fn element(self) -> Element<'a> {
58 self.element
59 }
60
61 /// The modulus `n`.
62 #[must_use]
63 pub fn modulus(self) -> Magnitude<'a> {
64 self.modulus
65 }
66
67 /// The public exponent `e`.
68 #[must_use]
69 pub fn exponent(self) -> Magnitude<'a> {
70 self.exponent
71 }
72}
73
74/// Reads the RSA key carried by `key` under the current `execution`.
75///
76/// The key's algorithm must be `rsaEncryption` or `id-RSASSA-PSS`. The key
77/// bits are decoded with their original parser session, offsets, encoding and
78/// depth; `execution` pays for every step and the source ceilings still apply.
79/// Key bytes and components remain borrowed; nested-reader metadata uses
80/// the existing accounted scratch path.
81///
82/// # Errors
83///
84/// `UnexpectedField` for another algorithm, `MalformedValue` for padded key
85/// bits or a non-positive component, `TrailingData` for anything after the
86/// structure or its two fields, and the located encoding or resource error
87/// otherwise. A resource refusal happens before the work it would fund.
88///
89/// # Examples
90///
91/// ```
92/// use std::sync::Arc;
93/// use structured_asn1::{Parser, memory::{Memory, MemoryAccount}};
94/// use structured_pkix::{PublicKeyInfo, key::rsa};
95/// let memory = Memory::new(Arc::new(MemoryAccount::new(4096, 4096)));
96/// let parser = Parser::der(memory.clone());
97/// // rsaEncryption, NULL; RSAPublicKey { 0x00c3 (195), 65537 }
98/// let spki = b"\x30\x1d\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\
99/// \x03\x0c\x00\x30\x09\x02\x02\x00\xc3\x02\x03\x01\x00\x01";
100/// let mut reader = parser.read(spki, 0).unwrap();
101/// let element = reader.next_element().unwrap().unwrap();
102/// let key = PublicKeyInfo::read_element(&reader, &element, "subjectPublicKeyInfo")?;
103/// let rsa = rsa::read(key, &memory)?;
104/// assert_eq!(rsa.modulus().as_bytes(), [0xc3]);
105/// assert_eq!(rsa.modulus().bits(), 8);
106/// assert_eq!(rsa.modulus().offset(), 25);
107/// assert_eq!(rsa.exponent().as_bytes(), [1, 0, 1]);
108/// # Ok::<(), structured_pkix::Error>(())
109/// ```
110pub fn read<'a, W: Work>(key: PublicKeyInfo<'a>, execution: &W) -> Result<PublicKey<'a>> {
111 let identifier = key.algorithm();
112 let at = identifier.offset();
113 let algorithm = identifier.algorithm();
114 // Deferred OID decisions share the source ceiling with the nested key
115 // decoder; a separately funded operation must not reset that ceiling.
116 let query = identifier.query_in(execution);
117 if !same_oid(algorithm, crate::oid::RSA_ENCRYPTION, &query, FIELD, at)?
118 && !same_oid(algorithm, crate::oid::RSASSA_PSS, &query, FIELD, at)?
119 {
120 return Err(Error::new(ErrorCode::UnexpectedField, FIELD, at));
121 }
122 let octets = key.whole_octets(FIELD)?;
123 let (reader, element) = identifier
124 .nested(octets, key.key_offset())
125 .open(execution, FIELD)?;
126 if element.tag() != Tag::constructed(universal::SEQUENCE) {
127 return Err(Error::new(
128 ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag),
129 FIELD,
130 element.span().start,
131 ));
132 }
133 let mut fields = reader.enter(&element).field(FIELD)?;
134 let modulus = magnitude(&mut fields)?;
135 let exponent = magnitude(&mut fields)?;
136 if !fields.is_empty() {
137 return Err(Error::new(ErrorCode::TrailingData, FIELD, fields.offset()));
138 }
139 Ok(PublicKey {
140 element,
141 modulus,
142 exponent,
143 })
144}
145
146fn magnitude<'a>(fields: &mut ScopedReader<'a, '_>) -> Result<Magnitude<'a>> {
147 let element = fields
148 .expect(Tag::primitive(universal::INTEGER))
149 .field(FIELD)?;
150 let integer = fields.integer(&element).field(FIELD)?;
151 // X.690 §8.3.2 leaves at most one leading zero octet, the sign of a
152 // positive value whose top bit is set. Zero and negative values are not
153 // RSA components (RFC 8017 §3.1).
154 let start = element.contents_span().start;
155 let (bytes, offset) = match integer.as_bytes() {
156 [0x00, rest @ ..] => (rest, start + 1),
157 all => (all, start),
158 };
159 if integer.is_negative() || bytes.is_empty() {
160 return Err(Error::new(
161 ErrorCode::MalformedValue,
162 FIELD,
163 element.span().start,
164 ));
165 }
166 Ok(Magnitude { bytes, offset })
167}