1 //! Traits for encoding/decoding SPKI public keys.
2 
3 use crate::{AlgorithmIdentifier, Error, Result, SubjectPublicKeyInfoRef};
4 use der::{EncodeValue, Tagged};
5 
6 #[cfg(feature = "alloc")]
7 use {
8     crate::AlgorithmIdentifierOwned,
9     der::{asn1::BitString, Any, Document},
10 };
11 
12 #[cfg(feature = "pem")]
13 use {
14     alloc::string::String,
15     der::pem::{LineEnding, PemLabel},
16 };
17 
18 #[cfg(feature = "std")]
19 use std::path::Path;
20 
21 #[cfg(doc)]
22 use crate::SubjectPublicKeyInfo;
23 
24 /// Parse a public key object from an encoded SPKI document.
25 pub trait DecodePublicKey: Sized {
26     /// Deserialize object from ASN.1 DER-encoded [`SubjectPublicKeyInfo`]
27     /// (binary format).
from_public_key_der(bytes: &[u8]) -> Result<Self>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")]
from_public_key_pem(s: &str) -> Result<Self>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")]
read_public_key_der_file(path: impl AsRef<Path>) -> Result<Self>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"))]
read_public_key_pem_file(path: impl AsRef<Path>) -> Result<Self>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 
61 impl<T> DecodePublicKey for T
62 where
63     T: for<'a> TryFrom<SubjectPublicKeyInfoRef<'a>, Error = Error>,
64 {
from_public_key_der(bytes: &[u8]) -> Result<Self>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")]
72 pub trait EncodePublicKey {
73     /// Serialize a [`Document`] containing a SPKI-encoded public key.
to_public_key_der(&self) -> Result<Document>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")]
to_public_key_pem(&self, line_ending: LineEnding) -> Result<String>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")]
write_public_key_der_file(&self, path: impl AsRef<Path>) -> Result<()>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"))]
write_public_key_pem_file( &self, path: impl AsRef<Path>, line_ending: LineEnding, ) -> Result<()>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.
104 pub 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")]
116 pub trait DynAssociatedAlgorithmIdentifier {
117     /// `AlgorithmIdentifier` for this structure.
algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned>118     fn algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned>;
119 }
120 
121 #[cfg(feature = "alloc")]
122 impl<T> DynAssociatedAlgorithmIdentifier for T
123 where
124     T: AssociatedAlgorithmIdentifier,
125 {
algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned>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.
142 pub 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")]
155 pub trait DynSignatureAlgorithmIdentifier {
156     /// `AlgorithmIdentifier` for the corresponding singature system.
signature_algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned>157     fn signature_algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned>;
158 }
159 
160 #[cfg(feature = "alloc")]
161 impl<T> DynSignatureAlgorithmIdentifier for T
162 where
163     T: SignatureAlgorithmIdentifier,
164 {
signature_algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned>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")]
181 pub trait SignatureBitStringEncoding {
182     /// `BitString` encoding for this signature.
to_bitstring(&self) -> der::Result<BitString>183     fn to_bitstring(&self) -> der::Result<BitString>;
184 }
185