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