Skip to main content

spki/
traits.rs

1//! Traits for encoding/decoding SPKI public keys.
2
3use crate::{AlgorithmIdentifier, Error, Result, SubjectPublicKeyInfoRef};
4use der::{EncodeValue, Tagged};
5
6#[cfg(feature = "alloc")]
7use {
8    crate::AlgorithmIdentifierOwned,
9    der::{Any, Document, asn1::BitString},
10};
11
12#[cfg(feature = "pem")]
13use {
14    alloc::string::String,
15    der::pem::{LineEnding, PemLabel},
16};
17
18#[cfg(feature = "std")]
19use std::path::Path;
20
21#[cfg(doc)]
22use crate::SubjectPublicKeyInfo;
23
24/// Parse a public key object from an encoded SPKI document.
25pub trait DecodePublicKey: Sized {
26    /// Deserialize object from ASN.1 DER-encoded [`SubjectPublicKeyInfo`]
27    /// (binary format).
28    ///
29    /// # Errors
30    /// Returns decoding errors specific to the concrete type which impls this trait.
31    fn from_public_key_der(bytes: &[u8]) -> Result<Self>;
32
33    /// Deserialize PEM-encoded [`SubjectPublicKeyInfo`].
34    ///
35    /// Keys in this format begin with the following delimiter:
36    ///
37    /// ```text
38    /// -----BEGIN PUBLIC KEY-----
39    /// ```
40    ///
41    /// # Errors
42    /// Returns decoding errors specific to the concrete type which impls this trait.
43    #[cfg(feature = "pem")]
44    fn from_public_key_pem(s: &str) -> Result<Self> {
45        let (label, doc) = Document::from_pem(s)?;
46        SubjectPublicKeyInfoRef::validate_pem_label(label)?;
47        Self::from_public_key_der(doc.as_bytes())
48    }
49
50    /// Load public key object from an ASN.1 DER-encoded file on the local
51    /// filesystem (binary format).
52    ///
53    /// # Errors
54    /// Returns decoding errors specific to the concrete type which impls this trait.
55    #[cfg(feature = "std")]
56    fn read_public_key_der_file(path: impl AsRef<Path>) -> Result<Self> {
57        let doc = Document::read_der_file(path)?;
58        Self::from_public_key_der(doc.as_bytes())
59    }
60
61    /// Load public key object from a PEM-encoded file on the local filesystem.
62    ///
63    /// # Errors
64    /// Returns decoding errors specific to the concrete type which impls this trait.
65    #[cfg(all(feature = "pem", feature = "std"))]
66    fn read_public_key_pem_file(path: impl AsRef<Path>) -> Result<Self> {
67        let (label, doc) = Document::read_pem_file(path)?;
68        SubjectPublicKeyInfoRef::validate_pem_label(&label)?;
69        Self::from_public_key_der(doc.as_bytes())
70    }
71}
72
73impl<T> DecodePublicKey for T
74where
75    T: for<'a> TryFrom<SubjectPublicKeyInfoRef<'a>, Error = Error>,
76{
77    fn from_public_key_der(bytes: &[u8]) -> Result<Self> {
78        Self::try_from(SubjectPublicKeyInfoRef::try_from(bytes)?)
79    }
80}
81
82/// Serialize a public key object to a SPKI-encoded document.
83#[cfg(feature = "alloc")]
84pub trait EncodePublicKey {
85    /// Serialize a [`Document`] containing a SPKI-encoded public key.
86    ///
87    /// # Errors
88    /// Returns encoding errors specific to the concrete type which impls this trait.
89    fn to_public_key_der(&self) -> Result<Document>;
90
91    /// Serialize this public key as PEM-encoded SPKI with the given [`LineEnding`].
92    ///
93    /// # Errors
94    /// Returns encoding errors specific to the concrete type which impls this trait.
95    #[cfg(feature = "pem")]
96    fn to_public_key_pem(&self, line_ending: LineEnding) -> Result<String> {
97        let doc = self.to_public_key_der()?;
98        Ok(doc.to_pem(SubjectPublicKeyInfoRef::PEM_LABEL, line_ending)?)
99    }
100
101    /// Write ASN.1 DER-encoded public key to the given path.
102    ///
103    /// # Errors
104    /// Returns encoding errors specific to the concrete type which impls this trait.
105    #[cfg(feature = "std")]
106    fn write_public_key_der_file(&self, path: impl AsRef<Path>) -> Result<()> {
107        Ok(self.to_public_key_der()?.write_der_file(path)?)
108    }
109
110    /// Write ASN.1 PEM-encoded public key to the given path.
111    ///
112    /// # Errors
113    /// Returns encoding errors specific to the concrete type which impls this trait.
114    #[cfg(all(feature = "pem", feature = "std"))]
115    fn write_public_key_pem_file(
116        &self,
117        path: impl AsRef<Path>,
118        line_ending: LineEnding,
119    ) -> Result<()> {
120        let doc = self.to_public_key_der()?;
121        Ok(doc.write_pem_file(path, SubjectPublicKeyInfoRef::PEM_LABEL, line_ending)?)
122    }
123}
124
125/// Returns `AlgorithmIdentifier` associated with the structure.
126///
127/// This is useful for e.g. keys for digital signature algorithms.
128pub trait AssociatedAlgorithmIdentifier {
129    /// Algorithm parameters.
130    type Params: Tagged + EncodeValue;
131
132    /// `AlgorithmIdentifier` for this structure.
133    const ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params>;
134}
135
136/// Returns `AlgorithmIdentifier` associated with the structure.
137///
138/// This is useful for e.g. keys for digital signature algorithms.
139#[cfg(feature = "alloc")]
140pub trait DynAssociatedAlgorithmIdentifier {
141    /// `AlgorithmIdentifier` for this structure.
142    ///
143    /// # Errors
144    /// Returns errors specific to the concrete type which impls this trait.
145    fn algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned>;
146}
147
148#[cfg(feature = "alloc")]
149impl<T> DynAssociatedAlgorithmIdentifier for T
150where
151    T: AssociatedAlgorithmIdentifier,
152{
153    fn algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned> {
154        Ok(AlgorithmIdentifierOwned {
155            oid: T::ALGORITHM_IDENTIFIER.oid,
156            parameters: T::ALGORITHM_IDENTIFIER
157                .parameters
158                .as_ref()
159                .map(Any::encode_from)
160                .transpose()?,
161        })
162    }
163}
164
165/// Returns [`AlgorithmIdentifier`] associated with the signature system.
166///
167/// Unlike [`AssociatedAlgorithmIdentifier`] this is intended to be implemented for public and/or
168/// private keys.
169pub trait SignatureAlgorithmIdentifier {
170    /// Algorithm parameters.
171    type Params: Tagged + EncodeValue;
172
173    /// `AlgorithmIdentifier` for the corresponding signature system.
174    const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params>;
175}
176
177/// Returns [`AlgorithmIdentifierOwned`] associated with the signature system.
178///
179/// Unlike [`AssociatedAlgorithmIdentifier`] this is intended to be implemented for public and/or
180/// private keys.
181#[cfg(feature = "alloc")]
182pub trait DynSignatureAlgorithmIdentifier {
183    /// `AlgorithmIdentifier` for the corresponding signature system.
184    ///
185    /// # Errors
186    /// Returns errors specific to the concrete type which impls this trait.
187    fn signature_algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned>;
188}
189
190#[cfg(feature = "alloc")]
191impl<T> DynSignatureAlgorithmIdentifier for T
192where
193    T: SignatureAlgorithmIdentifier,
194{
195    fn signature_algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned> {
196        Ok(AlgorithmIdentifierOwned {
197            oid: T::SIGNATURE_ALGORITHM_IDENTIFIER.oid,
198            parameters: T::SIGNATURE_ALGORITHM_IDENTIFIER
199                .parameters
200                .as_ref()
201                .map(Any::encode_from)
202                .transpose()?,
203        })
204    }
205}
206
207/// Returns the [`BitString`] encoding of the signature.
208///
209/// X.509 and CSR structures require signatures to be `BitString` encoded.
210#[cfg(feature = "alloc")]
211pub trait SignatureBitStringEncoding {
212    /// `BitString` encoding for this signature.
213    ///
214    /// # Errors
215    /// Returns errors specific to the concrete type which impls this trait.
216    fn to_bitstring(&self) -> der::Result<BitString>;
217}