spki/
algorithm.rs

1//! X.509 `AlgorithmIdentifier`
2
3use crate::{Error, Result};
4use core::cmp::Ordering;
5use der::{
6    asn1::{AnyRef, Choice, ObjectIdentifier},
7    Decode, DecodeValue, DerOrd, Encode, EncodeValue, Header, Length, Reader, Sequence, ValueOrd,
8    Writer,
9};
10
11#[cfg(feature = "alloc")]
12use der::asn1::Any;
13
14/// X.509 `AlgorithmIdentifier` as defined in [RFC 5280 Section 4.1.1.2].
15///
16/// ```text
17/// AlgorithmIdentifier  ::=  SEQUENCE  {
18///      algorithm               OBJECT IDENTIFIER,
19///      parameters              ANY DEFINED BY algorithm OPTIONAL  }
20/// ```
21///
22/// [RFC 5280 Section 4.1.1.2]: https://tools.ietf.org/html/rfc5280#section-4.1.1.2
23#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
24#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
25pub struct AlgorithmIdentifier<Params> {
26    /// Algorithm OID, i.e. the `algorithm` field in the `AlgorithmIdentifier`
27    /// ASN.1 schema.
28    pub oid: ObjectIdentifier,
29
30    /// Algorithm `parameters`.
31    pub parameters: Option<Params>,
32}
33
34impl<'a, Params> DecodeValue<'a> for AlgorithmIdentifier<Params>
35where
36    Params: Choice<'a>,
37{
38    fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> {
39        reader.read_nested(header.length, |reader| {
40            Ok(Self {
41                oid: reader.decode()?,
42                parameters: reader.decode()?,
43            })
44        })
45    }
46}
47
48impl<Params> EncodeValue for AlgorithmIdentifier<Params>
49where
50    Params: Encode,
51{
52    fn value_len(&self) -> der::Result<Length> {
53        self.oid.encoded_len()? + self.parameters.encoded_len()?
54    }
55
56    fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
57        self.oid.encode(writer)?;
58        self.parameters.encode(writer)?;
59        Ok(())
60    }
61}
62
63impl<'a, Params> Sequence<'a> for AlgorithmIdentifier<Params> where Params: Choice<'a> + Encode {}
64
65impl<'a, Params> TryFrom<&'a [u8]> for AlgorithmIdentifier<Params>
66where
67    Params: Choice<'a> + Encode,
68{
69    type Error = Error;
70
71    fn try_from(bytes: &'a [u8]) -> Result<Self> {
72        Ok(Self::from_der(bytes)?)
73    }
74}
75
76impl<Params> ValueOrd for AlgorithmIdentifier<Params>
77where
78    Params: DerOrd,
79{
80    fn value_cmp(&self, other: &Self) -> der::Result<Ordering> {
81        match self.oid.der_cmp(&other.oid)? {
82            Ordering::Equal => self.parameters.der_cmp(&other.parameters),
83            other => Ok(other),
84        }
85    }
86}
87
88/// `AlgorithmIdentifier` reference which has `AnyRef` parameters.
89pub type AlgorithmIdentifierRef<'a> = AlgorithmIdentifier<AnyRef<'a>>;
90
91/// `AlgorithmIdentifier` with `ObjectIdentifier` parameters.
92pub type AlgorithmIdentifierWithOid = AlgorithmIdentifier<ObjectIdentifier>;
93
94/// `AlgorithmIdentifier` reference which has `Any` parameters.
95#[cfg(feature = "alloc")]
96pub type AlgorithmIdentifierOwned = AlgorithmIdentifier<Any>;
97
98impl<Params> AlgorithmIdentifier<Params> {
99    /// Assert the `algorithm` OID is an expected value.
100    pub fn assert_algorithm_oid(&self, expected_oid: ObjectIdentifier) -> Result<ObjectIdentifier> {
101        if self.oid == expected_oid {
102            Ok(expected_oid)
103        } else {
104            Err(Error::OidUnknown { oid: expected_oid })
105        }
106    }
107}
108
109impl<'a> AlgorithmIdentifierRef<'a> {
110    /// Assert `parameters` is an OID and has the expected value.
111    pub fn assert_parameters_oid(
112        &self,
113        expected_oid: ObjectIdentifier,
114    ) -> Result<ObjectIdentifier> {
115        let actual_oid = self.parameters_oid()?;
116
117        if actual_oid == expected_oid {
118            Ok(actual_oid)
119        } else {
120            Err(Error::OidUnknown { oid: expected_oid })
121        }
122    }
123
124    /// Assert the values of the `algorithm` and `parameters` OIDs.
125    pub fn assert_oids(
126        &self,
127        algorithm: ObjectIdentifier,
128        parameters: ObjectIdentifier,
129    ) -> Result<()> {
130        self.assert_algorithm_oid(algorithm)?;
131        self.assert_parameters_oid(parameters)?;
132        Ok(())
133    }
134
135    /// Get the `parameters` field as an [`AnyRef`].
136    ///
137    /// Returns an error if `parameters` are `None`.
138    pub fn parameters_any(&self) -> Result<AnyRef<'a>> {
139        self.parameters.ok_or(Error::AlgorithmParametersMissing)
140    }
141
142    /// Get the `parameters` field as an [`ObjectIdentifier`].
143    ///
144    /// Returns an error if it is absent or not an OID.
145    pub fn parameters_oid(&self) -> Result<ObjectIdentifier> {
146        Ok(ObjectIdentifier::try_from(self.parameters_any()?)?)
147    }
148
149    /// Convert to a pair of [`ObjectIdentifier`]s.
150    ///
151    /// This method is helpful for decomposing in match statements. Note in
152    /// particular that `NULL` parameters are treated the same as missing
153    /// parameters.
154    ///
155    /// Returns an error if parameters are present but not an OID.
156    pub fn oids(&self) -> der::Result<(ObjectIdentifier, Option<ObjectIdentifier>)> {
157        Ok((
158            self.oid,
159            match self.parameters {
160                None => None,
161                Some(p) => match p {
162                    AnyRef::NULL => None,
163                    _ => Some(p.decode_as::<ObjectIdentifier>()?),
164                },
165            },
166        ))
167    }
168}
169
170#[cfg(feature = "alloc")]
171mod allocating {
172    use super::*;
173    use der::referenced::*;
174
175    impl<'a> RefToOwned<'a> for AlgorithmIdentifierRef<'a> {
176        type Owned = AlgorithmIdentifierOwned;
177        fn ref_to_owned(&self) -> Self::Owned {
178            AlgorithmIdentifier {
179                oid: self.oid,
180                parameters: self.parameters.ref_to_owned(),
181            }
182        }
183    }
184
185    impl OwnedToRef for AlgorithmIdentifierOwned {
186        type Borrowed<'a> = AlgorithmIdentifierRef<'a>;
187        fn owned_to_ref(&self) -> Self::Borrowed<'_> {
188            AlgorithmIdentifier {
189                oid: self.oid,
190                parameters: self.parameters.owned_to_ref(),
191            }
192        }
193    }
194}