Skip to main content

spki/
spki.rs

1//! X.509 `SubjectPublicKeyInfo`
2
3use crate::{AlgorithmIdentifier, Error, Result};
4use core::cmp::Ordering;
5use der::{
6    Choice, Decode, DecodeValue, DerOrd, Encode, EncodeValue, FixedTag, Header, Length, Reader,
7    Sequence, ValueOrd, Writer,
8    asn1::{AnyRef, BitStringRef},
9};
10
11#[cfg(feature = "alloc")]
12use der::{
13    Document,
14    asn1::{Any, BitString},
15};
16
17#[cfg(feature = "fingerprint")]
18use {
19    crate::{DigestWriter, FingerprintBytes},
20    digest::Digest,
21    sha2::Sha256,
22};
23
24#[cfg(feature = "pem")]
25use der::pem::PemLabel;
26
27/// [`SubjectPublicKeyInfo`] with [`AnyRef`] algorithm parameters, and [`BitStringRef`] params.
28///
29/// This is the borrowing-pendant to [`SubjectPublicKeyInfoOwned`].
30pub type SubjectPublicKeyInfoRef<'a> = SubjectPublicKeyInfo<AnyRef<'a>, BitStringRef<'a>>;
31
32/// [`SubjectPublicKeyInfo`] with [`Any`] algorithm parameters, and [`BitString`] params.
33///
34/// This is the owning-pendant to [`SubjectPublicKeyInfoRef`].
35#[cfg(feature = "alloc")]
36pub type SubjectPublicKeyInfoOwned = SubjectPublicKeyInfo<Any, BitString>;
37
38/// X.509 `SubjectPublicKeyInfo` (SPKI) as defined in [RFC 5280 § 4.1.2.7].
39///
40/// ASN.1 structure containing an [`AlgorithmIdentifier`] and public key
41/// data in an algorithm specific format.
42///
43/// ```text
44///    SubjectPublicKeyInfo  ::=  SEQUENCE  {
45///         algorithm            AlgorithmIdentifier,
46///         subjectPublicKey     BIT STRING  }
47/// ```
48///
49/// [RFC 5280 § 4.1.2.7]: https://tools.ietf.org/html/rfc5280#section-4.1.2.7
50#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
51#[derive(Clone, Debug, Eq, PartialEq)]
52pub struct SubjectPublicKeyInfo<Params, Key> {
53    /// X.509 [`AlgorithmIdentifier`] for the public key type
54    pub algorithm: AlgorithmIdentifier<Params>,
55
56    /// Public key data
57    pub subject_public_key: Key,
58}
59
60impl<'a, Params, Key> SubjectPublicKeyInfo<Params, Key>
61where
62    Params: Choice<'a, Error = der::Error> + Encode,
63    // TODO: replace FixedTag with FixedTag<TAG = { Tag::BitString }> once
64    // https://github.com/rust-lang/rust/issues/92827 is fixed
65    Key: Decode<'a, Error = der::Error> + Encode + FixedTag,
66{
67    /// Calculate the SHA-256 fingerprint of this [`SubjectPublicKeyInfo`] and
68    /// encode it as a Base64 string.
69    ///
70    /// See [RFC7469 § 2.1.1] for more information.
71    ///
72    /// # Errors
73    /// Propagates errors that occur during encoding.
74    ///
75    /// [RFC7469 § 2.1.1]: https://datatracker.ietf.org/doc/html/rfc7469#section-2.1.1
76    #[cfg(all(feature = "fingerprint", feature = "alloc", feature = "base64"))]
77    pub fn fingerprint_base64(&self) -> Result<alloc::string::String> {
78        use base64ct::{Base64, Encoding};
79        Ok(Base64::encode_string(&self.fingerprint_bytes()?))
80    }
81
82    /// Calculate the SHA-256 fingerprint of this [`SubjectPublicKeyInfo`] as
83    /// a raw byte array.
84    ///
85    /// See [RFC7469 § 2.1.1] for more information.
86    ///
87    /// # Errors
88    /// Propagates errors that occur during encoding.
89    ///
90    /// [RFC7469 § 2.1.1]: https://datatracker.ietf.org/doc/html/rfc7469#section-2.1.1
91    #[cfg(feature = "fingerprint")]
92    pub fn fingerprint_bytes(&self) -> Result<FingerprintBytes> {
93        let mut hash = Sha256::new();
94        self.encode(&mut DigestWriter(&mut hash))?;
95        Ok(hash.finalize().into())
96    }
97}
98
99impl<'a, Params, Key> DecodeValue<'a> for SubjectPublicKeyInfo<Params, Key>
100where
101    Params: Choice<'a, Error = der::Error> + Encode,
102    Key: Decode<'a, Error = der::Error>,
103{
104    type Error = der::Error;
105
106    fn decode_value<R: Reader<'a>>(reader: &mut R, _header: Header) -> der::Result<Self> {
107        Ok(Self {
108            algorithm: reader.decode()?,
109            subject_public_key: Key::decode(reader)?,
110        })
111    }
112}
113
114impl<'a, Params, Key> EncodeValue for SubjectPublicKeyInfo<Params, Key>
115where
116    Params: Choice<'a, Error = der::Error> + Encode,
117    Key: Encode,
118{
119    fn value_len(&self) -> der::Result<Length> {
120        self.algorithm.encoded_len()? + self.subject_public_key.encoded_len()?
121    }
122
123    fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
124        self.algorithm.encode(writer)?;
125        self.subject_public_key.encode(writer)?;
126        Ok(())
127    }
128}
129
130impl<'a, Params, Key> Sequence<'a> for SubjectPublicKeyInfo<Params, Key>
131where
132    Params: Choice<'a, Error = der::Error> + Encode,
133    Key: Decode<'a, Error = der::Error> + Encode + FixedTag,
134{
135}
136
137impl<'a, Params, Key> TryFrom<&'a [u8]> for SubjectPublicKeyInfo<Params, Key>
138where
139    Params: Choice<'a, Error = der::Error> + Encode,
140    Key: Decode<'a, Error = der::Error> + Encode + FixedTag,
141{
142    type Error = Error;
143
144    fn try_from(bytes: &'a [u8]) -> Result<Self> {
145        Ok(Self::from_der(bytes)?)
146    }
147}
148
149impl<'a, Params, Key> ValueOrd for SubjectPublicKeyInfo<Params, Key>
150where
151    Params: Choice<'a, Error = der::Error> + DerOrd + Encode,
152    Key: ValueOrd,
153{
154    fn value_cmp(&self, other: &Self) -> der::Result<Ordering> {
155        match self.algorithm.der_cmp(&other.algorithm)? {
156            Ordering::Equal => self.subject_public_key.value_cmp(&other.subject_public_key),
157            other => Ok(other),
158        }
159    }
160}
161
162#[cfg(feature = "alloc")]
163impl<'a: 'k, 'k, Params, Key: 'k> TryFrom<SubjectPublicKeyInfo<Params, Key>> for Document
164where
165    Params: Choice<'a, Error = der::Error> + Encode,
166    Key: Decode<'a, Error = der::Error> + Encode + FixedTag,
167    BitStringRef<'a>: From<&'k Key>,
168{
169    type Error = Error;
170
171    fn try_from(spki: SubjectPublicKeyInfo<Params, Key>) -> Result<Document> {
172        Self::try_from(&spki)
173    }
174}
175
176#[cfg(feature = "alloc")]
177impl<'a: 'k, 'k, Params, Key: 'k> TryFrom<&SubjectPublicKeyInfo<Params, Key>> for Document
178where
179    Params: Choice<'a, Error = der::Error> + Encode,
180    Key: Decode<'a, Error = der::Error> + Encode + FixedTag,
181    BitStringRef<'a>: From<&'k Key>,
182{
183    type Error = Error;
184
185    fn try_from(spki: &SubjectPublicKeyInfo<Params, Key>) -> Result<Document> {
186        Ok(Self::encode_msg(spki)?)
187    }
188}
189
190#[cfg(feature = "pem")]
191impl<Params, Key> PemLabel for SubjectPublicKeyInfo<Params, Key> {
192    const PEM_LABEL: &'static str = "PUBLIC KEY";
193}
194
195#[cfg(feature = "alloc")]
196mod allocating {
197    use super::*;
198    use crate::EncodePublicKey;
199    use der::referenced::*;
200
201    impl<'a> RefToOwned<'a> for SubjectPublicKeyInfoRef<'a> {
202        type Owned = SubjectPublicKeyInfoOwned;
203        fn ref_to_owned(&self) -> Self::Owned {
204            SubjectPublicKeyInfo {
205                algorithm: self.algorithm.ref_to_owned(),
206                subject_public_key: self.subject_public_key.ref_to_owned(),
207            }
208        }
209    }
210
211    impl OwnedToRef for SubjectPublicKeyInfoOwned {
212        type Borrowed<'a> = SubjectPublicKeyInfoRef<'a>;
213        fn owned_to_ref(&self) -> Self::Borrowed<'_> {
214            SubjectPublicKeyInfo {
215                algorithm: self.algorithm.owned_to_ref(),
216                subject_public_key: self.subject_public_key.owned_to_ref(),
217            }
218        }
219    }
220
221    impl SubjectPublicKeyInfoOwned {
222        /// Create a [`SubjectPublicKeyInfoOwned`] from any object that implements
223        /// [`EncodePublicKey`].
224        ///
225        /// # Errors
226        /// Propagates encoding and decoding errors.
227        pub fn from_key<T: EncodePublicKey>(source: &T) -> Result<Self> {
228            Ok(source.to_public_key_der()?.decode_msg::<Self>()?)
229        }
230    }
231}