diff options
Diffstat (limited to 'vendor/spki/src')
-rw-r--r-- | vendor/spki/src/algorithm.rs | 194 | ||||
-rw-r--r-- | vendor/spki/src/error.rs | 68 | ||||
-rw-r--r-- | vendor/spki/src/fingerprint.rs | 42 | ||||
-rw-r--r-- | vendor/spki/src/lib.rs | 71 | ||||
-rw-r--r-- | vendor/spki/src/spki.rs | 205 | ||||
-rw-r--r-- | vendor/spki/src/traits.rs | 184 |
6 files changed, 764 insertions, 0 deletions
diff --git a/vendor/spki/src/algorithm.rs b/vendor/spki/src/algorithm.rs new file mode 100644 index 0000000..5f4b5e8 --- /dev/null +++ b/vendor/spki/src/algorithm.rs @@ -0,0 +1,194 @@ +//! X.509 `AlgorithmIdentifier` + +use crate::{Error, Result}; +use core::cmp::Ordering; +use der::{ + asn1::{AnyRef, Choice, ObjectIdentifier}, + Decode, DecodeValue, DerOrd, Encode, EncodeValue, Header, Length, Reader, Sequence, ValueOrd, + Writer, +}; + +#[cfg(feature = "alloc")] +use der::asn1::Any; + +/// X.509 `AlgorithmIdentifier` as defined in [RFC 5280 Section 4.1.1.2]. +/// +/// ```text +/// AlgorithmIdentifier ::= SEQUENCE { +/// algorithm OBJECT IDENTIFIER, +/// parameters ANY DEFINED BY algorithm OPTIONAL } +/// ``` +/// +/// [RFC 5280 Section 4.1.1.2]: https://tools.ietf.org/html/rfc5280#section-4.1.1.2 +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct AlgorithmIdentifier<Params> { + /// Algorithm OID, i.e. the `algorithm` field in the `AlgorithmIdentifier` + /// ASN.1 schema. + pub oid: ObjectIdentifier, + + /// Algorithm `parameters`. + pub parameters: Option<Params>, +} + +impl<'a, Params> DecodeValue<'a> for AlgorithmIdentifier<Params> +where + Params: Choice<'a>, +{ + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> { + reader.read_nested(header.length, |reader| { + Ok(Self { + oid: reader.decode()?, + parameters: reader.decode()?, + }) + }) + } +} + +impl<Params> EncodeValue for AlgorithmIdentifier<Params> +where + Params: Encode, +{ + fn value_len(&self) -> der::Result<Length> { + self.oid.encoded_len()? + self.parameters.encoded_len()? + } + + fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> { + self.oid.encode(writer)?; + self.parameters.encode(writer)?; + Ok(()) + } +} + +impl<'a, Params> Sequence<'a> for AlgorithmIdentifier<Params> where Params: Choice<'a> + Encode {} + +impl<'a, Params> TryFrom<&'a [u8]> for AlgorithmIdentifier<Params> +where + Params: Choice<'a> + Encode, +{ + type Error = Error; + + fn try_from(bytes: &'a [u8]) -> Result<Self> { + Ok(Self::from_der(bytes)?) + } +} + +impl<Params> ValueOrd for AlgorithmIdentifier<Params> +where + Params: DerOrd, +{ + fn value_cmp(&self, other: &Self) -> der::Result<Ordering> { + match self.oid.der_cmp(&other.oid)? { + Ordering::Equal => self.parameters.der_cmp(&other.parameters), + other => Ok(other), + } + } +} + +/// `AlgorithmIdentifier` reference which has `AnyRef` parameters. +pub type AlgorithmIdentifierRef<'a> = AlgorithmIdentifier<AnyRef<'a>>; + +/// `AlgorithmIdentifier` with `ObjectIdentifier` parameters. +pub type AlgorithmIdentifierWithOid = AlgorithmIdentifier<ObjectIdentifier>; + +/// `AlgorithmIdentifier` reference which has `Any` parameters. +#[cfg(feature = "alloc")] +pub type AlgorithmIdentifierOwned = AlgorithmIdentifier<Any>; + +impl<Params> AlgorithmIdentifier<Params> { + /// Assert the `algorithm` OID is an expected value. + pub fn assert_algorithm_oid(&self, expected_oid: ObjectIdentifier) -> Result<ObjectIdentifier> { + if self.oid == expected_oid { + Ok(expected_oid) + } else { + Err(Error::OidUnknown { oid: expected_oid }) + } + } +} + +impl<'a> AlgorithmIdentifierRef<'a> { + /// Assert `parameters` is an OID and has the expected value. + pub fn assert_parameters_oid( + &self, + expected_oid: ObjectIdentifier, + ) -> Result<ObjectIdentifier> { + let actual_oid = self.parameters_oid()?; + + if actual_oid == expected_oid { + Ok(actual_oid) + } else { + Err(Error::OidUnknown { oid: expected_oid }) + } + } + + /// Assert the values of the `algorithm` and `parameters` OIDs. + pub fn assert_oids( + &self, + algorithm: ObjectIdentifier, + parameters: ObjectIdentifier, + ) -> Result<()> { + self.assert_algorithm_oid(algorithm)?; + self.assert_parameters_oid(parameters)?; + Ok(()) + } + + /// Get the `parameters` field as an [`AnyRef`]. + /// + /// Returns an error if `parameters` are `None`. + pub fn parameters_any(&self) -> Result<AnyRef<'a>> { + self.parameters.ok_or(Error::AlgorithmParametersMissing) + } + + /// Get the `parameters` field as an [`ObjectIdentifier`]. + /// + /// Returns an error if it is absent or not an OID. + pub fn parameters_oid(&self) -> Result<ObjectIdentifier> { + Ok(ObjectIdentifier::try_from(self.parameters_any()?)?) + } + + /// Convert to a pair of [`ObjectIdentifier`]s. + /// + /// This method is helpful for decomposing in match statements. Note in + /// particular that `NULL` parameters are treated the same as missing + /// parameters. + /// + /// Returns an error if parameters are present but not an OID. + pub fn oids(&self) -> der::Result<(ObjectIdentifier, Option<ObjectIdentifier>)> { + Ok(( + self.oid, + match self.parameters { + None => None, + Some(p) => match p { + AnyRef::NULL => None, + _ => Some(p.decode_as::<ObjectIdentifier>()?), + }, + }, + )) + } +} + +#[cfg(feature = "alloc")] +mod allocating { + use super::*; + use der::referenced::*; + + impl<'a> RefToOwned<'a> for AlgorithmIdentifierRef<'a> { + type Owned = AlgorithmIdentifierOwned; + fn ref_to_owned(&self) -> Self::Owned { + AlgorithmIdentifier { + oid: self.oid, + parameters: self.parameters.ref_to_owned(), + } + } + } + + impl OwnedToRef for AlgorithmIdentifierOwned { + type Borrowed<'a> = AlgorithmIdentifierRef<'a>; + fn owned_to_ref(&self) -> Self::Borrowed<'_> { + AlgorithmIdentifier { + oid: self.oid, + parameters: self.parameters.owned_to_ref(), + } + } + } +} diff --git a/vendor/spki/src/error.rs b/vendor/spki/src/error.rs new file mode 100644 index 0000000..9d05990 --- /dev/null +++ b/vendor/spki/src/error.rs @@ -0,0 +1,68 @@ +//! Error types + +use core::fmt; +use der::asn1::ObjectIdentifier; + +/// Result type with `spki` crate's [`Error`] type. +pub type Result<T> = core::result::Result<T, Error>; + +#[cfg(feature = "pem")] +use der::pem; + +/// Error type +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub enum Error { + /// Algorithm parameters are missing. + AlgorithmParametersMissing, + + /// ASN.1 DER-related errors. + Asn1(der::Error), + + /// Malformed cryptographic key contained in a SPKI document. + /// + /// This is intended for relaying errors related to the raw data contained + /// in [`SubjectPublicKeyInfo::subject_public_key`][`crate::SubjectPublicKeyInfo::subject_public_key`]. + KeyMalformed, + + /// Unknown algorithm OID. + OidUnknown { + /// Unrecognized OID value found in e.g. a SPKI `AlgorithmIdentifier`. + oid: ObjectIdentifier, + }, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::AlgorithmParametersMissing => { + f.write_str("AlgorithmIdentifier parameters missing") + } + Error::Asn1(err) => write!(f, "ASN.1 error: {}", err), + Error::KeyMalformed => f.write_str("SPKI cryptographic key data malformed"), + Error::OidUnknown { oid } => { + write!(f, "unknown/unsupported algorithm OID: {}", oid) + } + } + } +} + +impl From<der::Error> for Error { + fn from(err: der::Error) -> Error { + if let der::ErrorKind::OidUnknown { oid } = err.kind() { + Error::OidUnknown { oid } + } else { + Error::Asn1(err) + } + } +} + +#[cfg(feature = "pem")] +impl From<pem::Error> for Error { + fn from(err: pem::Error) -> Error { + der::Error::from(err).into() + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} diff --git a/vendor/spki/src/fingerprint.rs b/vendor/spki/src/fingerprint.rs new file mode 100644 index 0000000..ba06e62 --- /dev/null +++ b/vendor/spki/src/fingerprint.rs @@ -0,0 +1,42 @@ +//! SPKI fingerprint support. + +use der::Writer; +use sha2::{Digest, Sha256}; + +/// Size of a SHA-256 SPKI fingerprint in bytes. +pub(crate) const SIZE: usize = 32; + +/// Raw bytes of a SPKI fingerprint i.e. SHA-256 digest of +/// `SubjectPublicKeyInfo`'s DER encoding. +/// +/// See [RFC7469 § 2.1.1] for more information. +/// +/// [RFC7469 § 2.1.1]: https://datatracker.ietf.org/doc/html/rfc7469#section-2.1.1 +pub type FingerprintBytes = [u8; SIZE]; + +/// Writer newtype which accepts DER being serialized on-the-fly and computes a +/// hash of the contents. +#[derive(Clone, Default)] +pub(crate) struct Builder { + /// In-progress digest being computed from streaming DER. + digest: Sha256, +} + +impl Builder { + /// Create a new fingerprint builder. + pub fn new() -> Self { + Self::default() + } + + /// Finish computing a fingerprint, returning the computed digest. + pub fn finish(self) -> FingerprintBytes { + self.digest.finalize().into() + } +} + +impl Writer for Builder { + fn write(&mut self, der_bytes: &[u8]) -> der::Result<()> { + self.digest.update(der_bytes); + Ok(()) + } +} diff --git a/vendor/spki/src/lib.rs b/vendor/spki/src/lib.rs new file mode 100644 index 0000000..6c0caa7 --- /dev/null +++ b/vendor/spki/src/lib.rs @@ -0,0 +1,71 @@ +#![no_std] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![doc = include_str!("../README.md")] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" +)] +#![forbid(unsafe_code)] +#![warn( + clippy::mod_module_files, + clippy::unwrap_used, + missing_docs, + rust_2018_idioms, + unused_lifetimes, + unused_qualifications +)] +//! # Usage +//! The following example demonstrates how to use an OID as the `parameters` +//! of an [`AlgorithmIdentifier`]. +//! +//! Borrow the [`ObjectIdentifier`] first then use [`der::AnyRef::from`] or `.into()`: +//! +//! ``` +//! use spki::{AlgorithmIdentifier, ObjectIdentifier}; +//! +//! let alg_oid = "1.2.840.10045.2.1".parse::<ObjectIdentifier>().unwrap(); +//! let params_oid = "1.2.840.10045.3.1.7".parse::<ObjectIdentifier>().unwrap(); +//! +//! let alg_id = AlgorithmIdentifier { +//! oid: alg_oid, +//! parameters: Some(params_oid) +//! }; +//! ``` + +#[cfg(feature = "alloc")] +#[allow(unused_extern_crates)] +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; + +mod algorithm; +mod error; +mod spki; +mod traits; + +#[cfg(feature = "fingerprint")] +mod fingerprint; + +pub use crate::{ + algorithm::{AlgorithmIdentifier, AlgorithmIdentifierRef, AlgorithmIdentifierWithOid}, + error::{Error, Result}, + spki::{SubjectPublicKeyInfo, SubjectPublicKeyInfoRef}, + traits::{AssociatedAlgorithmIdentifier, DecodePublicKey, SignatureAlgorithmIdentifier}, +}; +pub use der::{self, asn1::ObjectIdentifier}; + +#[cfg(feature = "alloc")] +pub use { + crate::{ + algorithm::AlgorithmIdentifierOwned, + spki::SubjectPublicKeyInfoOwned, + traits::{ + DynAssociatedAlgorithmIdentifier, DynSignatureAlgorithmIdentifier, EncodePublicKey, + SignatureBitStringEncoding, + }, + }, + der::Document, +}; + +#[cfg(feature = "fingerprint")] +pub use crate::fingerprint::FingerprintBytes; diff --git a/vendor/spki/src/spki.rs b/vendor/spki/src/spki.rs new file mode 100644 index 0000000..3d949f8 --- /dev/null +++ b/vendor/spki/src/spki.rs @@ -0,0 +1,205 @@ +//! X.509 `SubjectPublicKeyInfo` + +use crate::{AlgorithmIdentifier, Error, Result}; +use core::cmp::Ordering; +use der::{ + asn1::{AnyRef, BitStringRef}, + Choice, Decode, DecodeValue, DerOrd, Encode, EncodeValue, FixedTag, Header, Length, Reader, + Sequence, ValueOrd, Writer, +}; + +#[cfg(feature = "alloc")] +use der::{ + asn1::{Any, BitString}, + Document, +}; + +#[cfg(feature = "fingerprint")] +use crate::{fingerprint, FingerprintBytes}; + +#[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 +/// data in an algorithm specific format. +/// +/// ```text +/// SubjectPublicKeyInfo ::= SEQUENCE { +/// algorithm AlgorithmIdentifier, +/// subjectPublicKey BIT STRING } +/// ``` +/// +/// [RFC 5280 § 4.1.2.7]: https://tools.ietf.org/html/rfc5280#section-4.1.2.7 +#[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<Params>, + + /// Public key data + pub subject_public_key: Key, +} + +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", feature = "base64"))] + pub fn fingerprint_base64(&self) -> Result<alloc::string::String> { + use base64ct::{Base64, Encoding}; + Ok(Base64::encode_string(&self.fingerprint_bytes()?)) + } + + /// Calculate the SHA-256 fingerprint of this [`SubjectPublicKeyInfo`] as + /// a raw byte array. + /// + /// See [RFC7469 § 2.1.1] for more information. + /// + /// [RFC7469 § 2.1.1]: https://datatracker.ietf.org/doc/html/rfc7469#section-2.1.1 + #[cfg(feature = "fingerprint")] + pub fn fingerprint_bytes(&self) -> Result<FingerprintBytes> { + let mut builder = fingerprint::Builder::new(); + self.encode(&mut builder)?; + Ok(builder.finish()) + } +} + +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: Key::decode(reader)?, + }) + }) + } +} + +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, 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> { + Ok(Self::from_der(bytes)?) + } +} + +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.subject_public_key.value_cmp(&other.subject_public_key), + other => Ok(other), + } + } +} + +#[cfg(feature = "alloc")] +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<Params, Key>) -> Result<Document> { + Self::try_from(&spki) + } +} + +#[cfg(feature = "alloc")] +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<Params, Key>) -> Result<Document> { + Ok(Self::encode_msg(spki)?) + } +} + +#[cfg(feature = "pem")] +impl<Params, Key> PemLabel for SubjectPublicKeyInfo<Params, Key> { + const PEM_LABEL: &'static str = "PUBLIC KEY"; +} + +#[cfg(feature = "alloc")] +mod allocating { + use super::*; + 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(), + } + } + } +} diff --git a/vendor/spki/src/traits.rs b/vendor/spki/src/traits.rs new file mode 100644 index 0000000..764b02a --- /dev/null +++ b/vendor/spki/src/traits.rs @@ -0,0 +1,184 @@ +//! Traits for encoding/decoding SPKI public keys. + +use crate::{AlgorithmIdentifier, Error, Result, SubjectPublicKeyInfoRef}; +use der::{EncodeValue, Tagged}; + +#[cfg(feature = "alloc")] +use { + crate::AlgorithmIdentifierOwned, + der::{asn1::BitString, Any, Document}, +}; + +#[cfg(feature = "pem")] +use { + alloc::string::String, + der::pem::{LineEnding, PemLabel}, +}; + +#[cfg(feature = "std")] +use std::path::Path; + +#[cfg(doc)] +use crate::SubjectPublicKeyInfo; + +/// Parse a public key object from an encoded SPKI document. +pub trait DecodePublicKey: Sized { + /// Deserialize object from ASN.1 DER-encoded [`SubjectPublicKeyInfo`] + /// (binary format). + fn from_public_key_der(bytes: &[u8]) -> Result<Self>; + + /// Deserialize PEM-encoded [`SubjectPublicKeyInfo`]. + /// + /// Keys in this format begin with the following delimiter: + /// + /// ```text + /// -----BEGIN PUBLIC KEY----- + /// ``` + #[cfg(feature = "pem")] + fn from_public_key_pem(s: &str) -> Result<Self> { + let (label, doc) = Document::from_pem(s)?; + SubjectPublicKeyInfoRef::validate_pem_label(label)?; + Self::from_public_key_der(doc.as_bytes()) + } + + /// Load public key object from an ASN.1 DER-encoded file on the local + /// filesystem (binary format). + #[cfg(feature = "std")] + fn read_public_key_der_file(path: impl AsRef<Path>) -> Result<Self> { + let doc = Document::read_der_file(path)?; + Self::from_public_key_der(doc.as_bytes()) + } + + /// Load public key object from a PEM-encoded file on the local filesystem. + #[cfg(all(feature = "pem", feature = "std"))] + fn read_public_key_pem_file(path: impl AsRef<Path>) -> Result<Self> { + let (label, doc) = Document::read_pem_file(path)?; + SubjectPublicKeyInfoRef::validate_pem_label(&label)?; + Self::from_public_key_der(doc.as_bytes()) + } +} + +impl<T> DecodePublicKey for T +where + T: for<'a> TryFrom<SubjectPublicKeyInfoRef<'a>, Error = Error>, +{ + fn from_public_key_der(bytes: &[u8]) -> Result<Self> { + Self::try_from(SubjectPublicKeyInfoRef::try_from(bytes)?) + } +} + +/// Serialize a public key object to a SPKI-encoded document. +#[cfg(feature = "alloc")] +pub trait EncodePublicKey { + /// Serialize a [`Document`] containing a SPKI-encoded public key. + fn to_public_key_der(&self) -> Result<Document>; + + /// Serialize this public key as PEM-encoded SPKI with the given [`LineEnding`]. + #[cfg(feature = "pem")] + fn to_public_key_pem(&self, line_ending: LineEnding) -> Result<String> { + let doc = self.to_public_key_der()?; + Ok(doc.to_pem(SubjectPublicKeyInfoRef::PEM_LABEL, line_ending)?) + } + + /// Write ASN.1 DER-encoded public key to the given path + #[cfg(feature = "std")] + fn write_public_key_der_file(&self, path: impl AsRef<Path>) -> Result<()> { + Ok(self.to_public_key_der()?.write_der_file(path)?) + } + + /// Write ASN.1 DER-encoded public key to the given path + #[cfg(all(feature = "pem", feature = "std"))] + fn write_public_key_pem_file( + &self, + path: impl AsRef<Path>, + line_ending: LineEnding, + ) -> Result<()> { + let doc = self.to_public_key_der()?; + Ok(doc.write_pem_file(path, SubjectPublicKeyInfoRef::PEM_LABEL, line_ending)?) + } +} + +/// Returns `AlgorithmIdentifier` associated with the structure. +/// +/// This is useful for e.g. keys for digital signature algorithms. +pub trait AssociatedAlgorithmIdentifier { + /// Algorithm parameters. + type Params: Tagged + EncodeValue; + + /// `AlgorithmIdentifier` for this structure. + const ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params>; +} + +/// Returns `AlgorithmIdentifier` associated with the structure. +/// +/// This is useful for e.g. keys for digital signature algorithms. +#[cfg(feature = "alloc")] +pub trait DynAssociatedAlgorithmIdentifier { + /// `AlgorithmIdentifier` for this structure. + fn algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned>; +} + +#[cfg(feature = "alloc")] +impl<T> DynAssociatedAlgorithmIdentifier for T +where + T: AssociatedAlgorithmIdentifier, +{ + fn algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned> { + Ok(AlgorithmIdentifierOwned { + oid: T::ALGORITHM_IDENTIFIER.oid, + parameters: T::ALGORITHM_IDENTIFIER + .parameters + .as_ref() + .map(Any::encode_from) + .transpose()?, + }) + } +} + +/// Returns `AlgorithmIdentifier` associated with the signature system. +/// +/// Unlike AssociatedAlgorithmIdentifier this is intended to be implemented for public and/or +/// private keys. +pub trait SignatureAlgorithmIdentifier { + /// Algorithm parameters. + type Params: Tagged + EncodeValue; + + /// `AlgorithmIdentifier` for the corresponding singature system. + const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params>; +} + +/// Returns `AlgorithmIdentifier` associated with the signature system. +/// +/// Unlike AssociatedAlgorithmIdentifier this is intended to be implemented for public and/or +/// private keys. +#[cfg(feature = "alloc")] +pub trait DynSignatureAlgorithmIdentifier { + /// `AlgorithmIdentifier` for the corresponding singature system. + fn signature_algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned>; +} + +#[cfg(feature = "alloc")] +impl<T> DynSignatureAlgorithmIdentifier for T +where + T: SignatureAlgorithmIdentifier, +{ + fn signature_algorithm_identifier(&self) -> Result<AlgorithmIdentifierOwned> { + Ok(AlgorithmIdentifierOwned { + oid: T::SIGNATURE_ALGORITHM_IDENTIFIER.oid, + parameters: T::SIGNATURE_ALGORITHM_IDENTIFIER + .parameters + .as_ref() + .map(Any::encode_from) + .transpose()?, + }) + } +} + +/// Returns the `BitString` encoding of the signature. +/// +/// X.509 and CSR structures require signatures to be BitString encoded. +#[cfg(feature = "alloc")] +pub trait SignatureBitStringEncoding { + /// `BitString` encoding for this signature. + fn to_bitstring(&self) -> der::Result<BitString>; +} |