//! 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 { /// Algorithm OID, i.e. the `algorithm` field in the `AlgorithmIdentifier` /// ASN.1 schema. pub oid: ObjectIdentifier, /// Algorithm `parameters`. pub parameters: Option, } impl<'a, Params> DecodeValue<'a> for AlgorithmIdentifier where Params: Choice<'a>, { fn decode_value>(reader: &mut R, header: Header) -> der::Result { reader.read_nested(header.length, |reader| { Ok(Self { oid: reader.decode()?, parameters: reader.decode()?, }) }) } } impl EncodeValue for AlgorithmIdentifier where Params: Encode, { fn value_len(&self) -> der::Result { 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 where Params: Choice<'a> + Encode {} impl<'a, Params> TryFrom<&'a [u8]> for AlgorithmIdentifier where Params: Choice<'a> + Encode, { type Error = Error; fn try_from(bytes: &'a [u8]) -> Result { Ok(Self::from_der(bytes)?) } } impl ValueOrd for AlgorithmIdentifier where Params: DerOrd, { fn value_cmp(&self, other: &Self) -> der::Result { 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>; /// `AlgorithmIdentifier` with `ObjectIdentifier` parameters. pub type AlgorithmIdentifierWithOid = AlgorithmIdentifier; /// `AlgorithmIdentifier` reference which has `Any` parameters. #[cfg(feature = "alloc")] pub type AlgorithmIdentifierOwned = AlgorithmIdentifier; impl AlgorithmIdentifier { /// Assert the `algorithm` OID is an expected value. pub fn assert_algorithm_oid(&self, expected_oid: ObjectIdentifier) -> Result { 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 { 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> { 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 { 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)> { Ok(( self.oid, match self.parameters { None => None, Some(p) => match p { AnyRef::NULL => None, _ => Some(p.decode_as::()?), }, }, )) } } #[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(), } } } }