spki/
spki.rs

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