diff options
Diffstat (limited to 'src/spki.rs')
-rw-r--r-- | src/spki.rs | 162 |
1 files changed, 119 insertions, 43 deletions
diff --git a/src/spki.rs b/src/spki.rs index 9058e49..b7e4c92 100644 --- a/src/spki.rs +++ b/src/spki.rs @@ -3,24 +3,30 @@ use crate::{AlgorithmIdentifier, Error, Result}; use core::cmp::Ordering; use der::{ - asn1::BitStringRef, Decode, DecodeValue, DerOrd, Encode, Header, Reader, Sequence, ValueOrd, + asn1::{AnyRef, BitStringRef}, + Choice, Decode, DecodeValue, DerOrd, Encode, EncodeValue, FixedTag, Header, Length, Reader, + Sequence, ValueOrd, Writer, }; #[cfg(feature = "alloc")] -use der::Document; +use der::{ + asn1::{Any, BitString}, + Document, +}; #[cfg(feature = "fingerprint")] use crate::{fingerprint, FingerprintBytes}; -#[cfg(all(feature = "alloc", feature = "fingerprint"))] -use { - alloc::string::String, - base64ct::{Base64, Encoding}, -}; - #[cfg(feature = "pem")] use der::pem::PemLabel; +/// [`SubjectPublicKeyInfo`] with [`AnyRef`] algorithm parameters, and [`BitStringRef`] params. +pub type SubjectPublicKeyInfoRef<'a> = SubjectPublicKeyInfo<AnyRef<'a>, BitStringRef<'a>>; + +/// [`SubjectPublicKeyInfo`] with [`Any`] algorithm parameters, and [`BitString`] params. +#[cfg(feature = "alloc")] +pub type SubjectPublicKeyInfoOwned = SubjectPublicKeyInfo<Any, BitString>; + /// X.509 `SubjectPublicKeyInfo` (SPKI) as defined in [RFC 5280 § 4.1.2.7]. /// /// ASN.1 structure containing an [`AlgorithmIdentifier`] and public key @@ -33,25 +39,32 @@ use der::pem::PemLabel; /// ``` /// /// [RFC 5280 § 4.1.2.7]: https://tools.ietf.org/html/rfc5280#section-4.1.2.7 -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct SubjectPublicKeyInfo<'a> { +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct SubjectPublicKeyInfo<Params, Key> { /// X.509 [`AlgorithmIdentifier`] for the public key type - pub algorithm: AlgorithmIdentifier<'a>, + pub algorithm: AlgorithmIdentifier<Params>, /// Public key data - pub subject_public_key: &'a [u8], + pub subject_public_key: Key, } -impl<'a> SubjectPublicKeyInfo<'a> { +impl<'a, Params, Key> SubjectPublicKeyInfo<Params, Key> +where + Params: Choice<'a> + Encode, + // TODO: replace FixedTag with FixedTag<TAG = { Tag::BitString }> once + // https://github.com/rust-lang/rust/issues/92827 is fixed + Key: Decode<'a> + Encode + FixedTag, +{ /// Calculate the SHA-256 fingerprint of this [`SubjectPublicKeyInfo`] and /// encode it as a Base64 string. /// /// See [RFC7469 § 2.1.1] for more information. /// /// [RFC7469 § 2.1.1]: https://datatracker.ietf.org/doc/html/rfc7469#section-2.1.1 - #[cfg(all(feature = "fingerprint", feature = "alloc"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "fingerprint", feature = "alloc"))))] - pub fn fingerprint_base64(&self) -> Result<String> { + #[cfg(all(feature = "fingerprint", feature = "alloc", feature = "base64"))] + pub fn fingerprint_base64(&self) -> Result<alloc::string::String> { + use base64ct::{Base64, Encoding}; Ok(Base64::encode_string(&self.fingerprint_bytes()?)) } @@ -62,42 +75,56 @@ impl<'a> SubjectPublicKeyInfo<'a> { /// /// [RFC7469 § 2.1.1]: https://datatracker.ietf.org/doc/html/rfc7469#section-2.1.1 #[cfg(feature = "fingerprint")] - #[cfg_attr(docsrs, doc(cfg(feature = "fingerprint")))] pub fn fingerprint_bytes(&self) -> Result<FingerprintBytes> { let mut builder = fingerprint::Builder::new(); self.encode(&mut builder)?; Ok(builder.finish()) } - - /// Get a [`BitString`] representing the `subject_public_key` - fn bitstring(&self) -> der::Result<BitStringRef<'a>> { - BitStringRef::from_bytes(self.subject_public_key) - } } -impl<'a> DecodeValue<'a> for SubjectPublicKeyInfo<'a> { +impl<'a: 'k, 'k, Params, Key: 'k> DecodeValue<'a> for SubjectPublicKeyInfo<Params, Key> +where + Params: Choice<'a> + Encode, + Key: Decode<'a>, +{ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> { reader.read_nested(header.length, |reader| { Ok(Self { algorithm: reader.decode()?, - subject_public_key: BitStringRef::decode(reader)? - .as_bytes() - .ok_or_else(|| der::Tag::BitString.value_error())?, + subject_public_key: Key::decode(reader)?, }) }) } } -impl<'a> Sequence<'a> for SubjectPublicKeyInfo<'a> { - fn fields<F, T>(&self, f: F) -> der::Result<T> - where - F: FnOnce(&[&dyn Encode]) -> der::Result<T>, - { - f(&[&self.algorithm, &self.bitstring()?]) +impl<'a, Params, Key> EncodeValue for SubjectPublicKeyInfo<Params, Key> +where + Params: Choice<'a> + Encode, + Key: Encode, +{ + fn value_len(&self) -> der::Result<Length> { + self.algorithm.encoded_len()? + self.subject_public_key.encoded_len()? } + + fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> { + self.algorithm.encode(writer)?; + self.subject_public_key.encode(writer)?; + Ok(()) + } +} + +impl<'a, Params, Key> Sequence<'a> for SubjectPublicKeyInfo<Params, Key> +where + Params: Choice<'a> + Encode, + Key: Decode<'a> + Encode + FixedTag, +{ } -impl<'a> TryFrom<&'a [u8]> for SubjectPublicKeyInfo<'a> { +impl<'a, Params, Key> TryFrom<&'a [u8]> for SubjectPublicKeyInfo<Params, Key> +where + Params: Choice<'a> + Encode, + Key: Decode<'a> + Encode + FixedTag, +{ type Error = Error; fn try_from(bytes: &'a [u8]) -> Result<Self> { @@ -105,37 +132,86 @@ impl<'a> TryFrom<&'a [u8]> for SubjectPublicKeyInfo<'a> { } } -impl ValueOrd for SubjectPublicKeyInfo<'_> { +impl<'a, Params, Key> ValueOrd for SubjectPublicKeyInfo<Params, Key> +where + Params: Choice<'a> + DerOrd + Encode, + Key: ValueOrd, +{ fn value_cmp(&self, other: &Self) -> der::Result<Ordering> { match self.algorithm.der_cmp(&other.algorithm)? { - Ordering::Equal => self.bitstring()?.der_cmp(&other.bitstring()?), + Ordering::Equal => self.subject_public_key.value_cmp(&other.subject_public_key), other => Ok(other), } } } #[cfg(feature = "alloc")] -#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] -impl TryFrom<SubjectPublicKeyInfo<'_>> for Document { +impl<'a: 'k, 'k, Params, Key: 'k> TryFrom<SubjectPublicKeyInfo<Params, Key>> for Document +where + Params: Choice<'a> + Encode, + Key: Decode<'a> + Encode + FixedTag, + BitStringRef<'a>: From<&'k Key>, +{ type Error = Error; - fn try_from(spki: SubjectPublicKeyInfo<'_>) -> Result<Document> { + fn try_from(spki: SubjectPublicKeyInfo<Params, Key>) -> Result<Document> { Self::try_from(&spki) } } #[cfg(feature = "alloc")] -#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] -impl TryFrom<&SubjectPublicKeyInfo<'_>> for Document { +impl<'a: 'k, 'k, Params, Key: 'k> TryFrom<&SubjectPublicKeyInfo<Params, Key>> for Document +where + Params: Choice<'a> + Encode, + Key: Decode<'a> + Encode + FixedTag, + BitStringRef<'a>: From<&'k Key>, +{ type Error = Error; - fn try_from(spki: &SubjectPublicKeyInfo<'_>) -> Result<Document> { + fn try_from(spki: &SubjectPublicKeyInfo<Params, Key>) -> Result<Document> { Ok(Self::encode_msg(spki)?) } } #[cfg(feature = "pem")] -#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] -impl PemLabel for SubjectPublicKeyInfo<'_> { +impl<Params, Key> PemLabel for SubjectPublicKeyInfo<Params, Key> { const PEM_LABEL: &'static str = "PUBLIC KEY"; } + +#[cfg(feature = "alloc")] +mod allocating { + use super::*; + use crate::EncodePublicKey; + use der::referenced::*; + + impl<'a> RefToOwned<'a> for SubjectPublicKeyInfoRef<'a> { + type Owned = SubjectPublicKeyInfoOwned; + fn ref_to_owned(&self) -> Self::Owned { + SubjectPublicKeyInfo { + algorithm: self.algorithm.ref_to_owned(), + subject_public_key: self.subject_public_key.ref_to_owned(), + } + } + } + + impl OwnedToRef for SubjectPublicKeyInfoOwned { + type Borrowed<'a> = SubjectPublicKeyInfoRef<'a>; + fn owned_to_ref(&self) -> Self::Borrowed<'_> { + SubjectPublicKeyInfo { + algorithm: self.algorithm.owned_to_ref(), + subject_public_key: self.subject_public_key.owned_to_ref(), + } + } + } + + impl SubjectPublicKeyInfoOwned { + /// Create a [`SubjectPublicKeyInfoOwned`] from any object that implements + /// [`EncodePublicKey`]. + pub fn from_key<T>(source: T) -> Result<Self> + where + T: EncodePublicKey, + { + Ok(source.to_public_key_der()?.decode_msg::<Self>()?) + } + } +} |