structured_pkix/key/rfc8410.rs
1//! Borrowed Ed25519, Ed448, X25519 and X448 public-key structure.
2//!
3//! RFC 8410 §§3–4 requires absent parameters and raw whole-octet keys, not
4//! nested ASN.1 values. This reader checks identity and framing only. Curve
5//! decoding, signature modes, key agreement and certificate usage remain with
6//! the caller. RFC 9295 §3 updates keyUsage, not the public-key representation.
7//! <https://www.rfc-editor.org/rfc/rfc8410#section-4>
8
9use super::PublicKeyInfo;
10use crate::error::{Error, ErrorCode, Result};
11use structured_asn1::memory::Work;
12
13const FIELD: &str = "subjectPublicKey.rfc8410";
14const PARAMETERS: &str = "subjectPublicKeyInfo.algorithm.parameters";
15
16/// The exact public-key algorithm named by an RFC 8410 SPKI.
17///
18/// Equal key widths do not make X25519 and Ed25519 interchangeable. This is
19/// key identity, not authorization to use a signature or key-agreement mode.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum Algorithm {
22 /// X25519 key agreement (RFC 7748).
23 X25519,
24 /// X448 key agreement (RFC 7748).
25 X448,
26 /// Ed25519 signature public key (RFC 8032).
27 Ed25519,
28 /// Ed448 signature public key (RFC 8032).
29 Ed448,
30}
31
32impl Algorithm {
33 /// Encoded public-key width, not a mathematical validity check.
34 #[must_use]
35 pub const fn public_key_len(self) -> usize {
36 match self {
37 Self::X25519 | Self::Ed25519 => 32,
38 Self::X448 => 56,
39 Self::Ed448 => 57,
40 }
41 }
42}
43
44/// Original raw public-key bytes with an unambiguous algorithm and location.
45///
46/// Success does not prove that an Edwards point decodes or that a Montgomery
47/// key is contributory. The selected backend performs those checks.
48#[derive(Debug, Clone, Copy)]
49pub struct PublicKey<'a> {
50 algorithm: Algorithm,
51 bytes: &'a [u8],
52 offset: u64,
53}
54
55impl<'a> PublicKey<'a> {
56 /// Algorithm named by the original SPKI.
57 #[must_use]
58 pub fn algorithm(&self) -> Algorithm {
59 self.algorithm
60 }
61
62 /// Exact raw key octets, without BIT STRING tag, length or unused-bit octet.
63 #[must_use]
64 pub fn as_bytes(&self) -> &'a [u8] {
65 self.bytes
66 }
67
68 /// First key octet in the original source's coordinate system.
69 #[must_use]
70 pub fn offset(&self) -> u64 {
71 self.offset
72 }
73}
74
75/// Checks RFC 8410 key identity and framing without parsing or copying raw keys.
76///
77/// Both the original source and current operation fund deferred work. No
78/// parser, account, buffer or reference-counted owner is created or retained.
79/// Parameters must be absent, including NULL: inheritance cannot repair this
80/// algorithm-specific spelling. Key bytes remain uninterpreted, including
81/// noncanonical or low-order points whose acceptance belongs to the backend.
82///
83/// # Errors
84/// Reports `UnexpectedField` for another algorithm, `MalformedValue` for
85/// parameters, padded bits or wrong width, and original located resource
86/// failures. A resource refusal is never a key mismatch or malformed input.
87///
88/// # Examples
89/// ```
90/// use std::sync::Arc;
91/// use structured_asn1::{Parser, memory::{Memory, MemoryAccount}};
92/// use structured_pkix::{PublicKeyInfo, key::rfc8410};
93/// let memory = Memory::new(Arc::new(MemoryAccount::new(4096, 4096)));
94/// let parser = Parser::der(memory.clone());
95/// let mut bytes = vec![0x30, 0x2a, 0x30, 5, 6, 3, 0x2b, 0x65, 0x70, 3, 0x21, 0];
96/// bytes.extend([0; 32]); // framing example, not a trusted or usable key
97/// let mut reader = parser.read(&bytes, 400).unwrap();
98/// let element = reader.next_element().unwrap().unwrap();
99/// let spki = PublicKeyInfo::read_element(&reader, &element, "key")?;
100/// let key = rfc8410::read(spki, &memory)?;
101/// assert_eq!(key.algorithm(), rfc8410::Algorithm::Ed25519);
102/// assert_eq!(key.offset(), 412);
103/// # Ok::<(), structured_pkix::Error>(())
104/// ```
105pub fn read<'a, W: Work>(key: PublicKeyInfo<'a>, execution: &W) -> Result<PublicKey<'a>> {
106 let identifier = key.algorithm();
107 let work = identifier.query_in(execution);
108 let at = identifier.offset();
109 let charge = |amount| {
110 work.charge_work(amount)
111 .map_err(|reason| Error::resource(reason, FIELD, at))
112 };
113 charge(1)?;
114 let oid = identifier.algorithm();
115 let bytes = oid.as_bytes();
116 if bytes.len() != 3 {
117 return Err(Error::new(ErrorCode::UnexpectedField, FIELD, at));
118 }
119 charge(3)?;
120 // RFC 8410 §3: these are four distinct OIDs, all with absent parameters.
121 // https://www.rfc-editor.org/rfc/rfc8410#section-3
122 let algorithm = match bytes {
123 [0x2b, 0x65, 0x6e] => Algorithm::X25519,
124 [0x2b, 0x65, 0x6f] => Algorithm::X448,
125 [0x2b, 0x65, 0x70] => Algorithm::Ed25519,
126 [0x2b, 0x65, 0x71] => Algorithm::Ed448,
127 _ => return Err(Error::new(ErrorCode::UnexpectedField, FIELD, at)),
128 };
129 if let Some(parameters) = identifier.parameters_nested() {
130 return Err(Error::new(
131 ErrorCode::MalformedValue,
132 PARAMETERS,
133 parameters.offset(),
134 ));
135 }
136 let bytes = key.whole_octets(FIELD)?;
137 let offset = key.key_offset();
138 // RFC 7748 §5 and RFC 8032 §§5.1.2,5.2.2 define these raw widths.
139 // Do not interpret the octets as an INTEGER or nested OCTET STRING.
140 if bytes.len() != algorithm.public_key_len() {
141 return Err(Error::new(ErrorCode::MalformedValue, FIELD, offset));
142 }
143 Ok(PublicKey {
144 algorithm,
145 bytes,
146 offset,
147 })
148}