Skip to main content

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}