x509_parser/
signature_algorithm.rs

1use crate::error::X509Error;
2use crate::x509::AlgorithmIdentifier;
3use asn1_rs::{
4    oid, Any, CheckDerConstraints, Class, DerAutoDerive, Error, FromDer, Oid, OptTaggedExplicit,
5    OptTaggedParser, Tag,
6};
7use core::convert::TryFrom;
8use oid_registry::*;
9
10#[allow(non_camel_case_types)]
11#[derive(Debug, PartialEq)]
12pub enum SignatureAlgorithm<'a> {
13    RSA,
14    RSASSA_PSS(Box<RsaSsaPssParams<'a>>),
15    RSAAES_OAEP(Box<RsaAesOaepParams<'a>>),
16    DSA,
17    ECDSA,
18    ED25519,
19}
20
21impl<'a, 'b> TryFrom<&'b AlgorithmIdentifier<'a>> for SignatureAlgorithm<'a> {
22    type Error = X509Error;
23
24    fn try_from(value: &'b AlgorithmIdentifier<'a>) -> Result<Self, Self::Error> {
25        if value.algorithm.starts_with(&oid! {1.2.840.113549.1.1}) {
26            // children of PKCS1 are all RSA
27            // test if RSASSA-PSS
28            if value.algorithm == OID_PKCS1_RSASSAPSS {
29                let params = match value.parameters.as_ref() {
30                    Some(any) => any,
31                    None => return Err(X509Error::InvalidSignatureValue),
32                };
33                let params = RsaSsaPssParams::try_from(params)
34                    .map_err(|_| X509Error::InvalidSignatureValue)?;
35                Ok(SignatureAlgorithm::RSASSA_PSS(Box::new(params)))
36            } else {
37                // rfc3279#section-2.2.1: the parameters component of that type SHALL be
38                // the ASN.1 type NULL
39                // We could enforce presence of NULL, but that would make a strict parser
40                // so it would best go to a verifier.
41                Ok(SignatureAlgorithm::RSA)
42            }
43        } else if test_ecdsa_oid(&value.algorithm) {
44            // parameter should be NULL - see above
45            Ok(SignatureAlgorithm::ECDSA)
46        } else if value.algorithm.starts_with(&oid! {1.2.840.10040.4}) {
47            // parameter should be NULL - see above
48            Ok(SignatureAlgorithm::DSA)
49        } else if value.algorithm == OID_SIG_ED25519 {
50            Ok(SignatureAlgorithm::ED25519)
51        } else if value.algorithm == oid! {1.2.840.113549.1.1.7} {
52            let params = match value.parameters.as_ref() {
53                Some(any) => any,
54                None => return Err(X509Error::InvalidSignatureValue),
55            };
56            let params =
57                RsaAesOaepParams::try_from(params).map_err(|_| X509Error::InvalidSignatureValue)?;
58            Ok(SignatureAlgorithm::RSAAES_OAEP(Box::new(params)))
59        } else {
60            if cfg!(debug_assertions) {
61                // TODO: remove debug
62                eprintln!("bad Signature AlgorithmIdentifier: {}", value.algorithm);
63            }
64            Err(X509Error::InvalidSignatureValue)
65        }
66    }
67}
68
69#[inline]
70fn test_ecdsa_oid(oid: &Oid) -> bool {
71    // test if oid is a child from {ansi-x962 signatures}
72    oid.starts_with(&oid! {1.2.840.10045.4})
73}
74
75// RSASSA-PSS public keys [RFC4055](https://www.rfc-editor.org/rfc/rfc4055.html)
76
77// RSASSA-PSS-params  ::=  SEQUENCE  {
78//     hashAlgorithm      [0] HashAlgorithm DEFAULT
79//                               sha1Identifier,
80//     maskGenAlgorithm   [1] MaskGenAlgorithm DEFAULT
81//                               mgf1SHA1Identifier,
82//     saltLength         [2] INTEGER DEFAULT 20,
83//     trailerField       [3] INTEGER DEFAULT 1  }
84#[derive(Debug, PartialEq)]
85pub struct RsaSsaPssParams<'a> {
86    hash_alg: Option<AlgorithmIdentifier<'a>>,
87    mask_gen_algorithm: Option<AlgorithmIdentifier<'a>>,
88    salt_length: Option<u32>,
89    trailer_field: Option<u32>,
90}
91
92impl<'a> RsaSsaPssParams<'a> {
93    /// Get a reference to the rsa ssa pss params's hash algorithm.
94    pub fn hash_algorithm(&self) -> Option<&AlgorithmIdentifier> {
95        self.hash_alg.as_ref()
96    }
97
98    /// Return the hash algorithm OID, or SHA1 if absent (RFC4055)
99    pub fn hash_algorithm_oid(&self) -> &'a Oid {
100        const SHA1: &Oid = &OID_HASH_SHA1;
101        self.hash_alg
102            .as_ref()
103            .map(|alg| &alg.algorithm)
104            .unwrap_or(SHA1)
105    }
106
107    /// Get a reference to the rsa ssa pss params's mask generation algorithm.
108    pub fn mask_gen_algorithm_raw(&self) -> Option<&AlgorithmIdentifier> {
109        self.mask_gen_algorithm.as_ref()
110    }
111
112    /// Get the rsa ssa pss params's mask generation algorithm.
113    ///
114    /// If the algorithm encoding is invalid, raise an error `InvalidAlgorithmIdentifier`
115    pub fn mask_gen_algorithm(&self) -> Result<MaskGenAlgorithm, X509Error> {
116        match self.mask_gen_algorithm.as_ref() {
117            Some(alg) => {
118                let (_, hash) = alg
119                    .parameters()
120                    .and_then(|any| Oid::from_der(any.data).ok())
121                    .ok_or(X509Error::InvalidAlgorithmIdentifier)?;
122                Ok(MaskGenAlgorithm::new(alg.algorithm.clone(), hash))
123            }
124            _ => {
125                Ok(MaskGenAlgorithm::new(
126                    oid! {1.2.840.113549.1.1.8}, // id-mgf1
127                    OID_HASH_SHA1,
128                ))
129            }
130        }
131    }
132
133    /// Return the salt length
134    pub fn salt_length(&self) -> u32 {
135        self.salt_length.unwrap_or(20)
136    }
137
138    /// Return the trailer field (value must be `1` according to RFC4055)
139    pub fn trailer_field(&self) -> u32 {
140        self.trailer_field.unwrap_or(1)
141    }
142}
143
144impl<'a> TryFrom<Any<'a>> for RsaSsaPssParams<'a> {
145    type Error = X509Error;
146
147    fn try_from(value: Any<'a>) -> Result<Self, Self::Error> {
148        Self::try_from(&value)
149    }
150}
151
152impl<'a, 'b> TryFrom<&'b Any<'a>> for RsaSsaPssParams<'a> {
153    type Error = X509Error;
154
155    fn try_from(value: &'b Any<'a>) -> Result<Self, Self::Error> {
156        value.tag().assert_eq(Tag::Sequence)?;
157        let i = &value.data;
158        // let (i, hash_alg) = OptTaggedExplicit::<_, X509Error, 0>::from_der(i)?;
159        let (i, hash_alg) = OptTaggedParser::new(Class::ContextSpecific, Tag(0))
160            .parse_der(i, |_, inner| AlgorithmIdentifier::from_der(inner))?;
161        // let (i, mask_gen_algorithm) = OptTaggedExplicit::<_, Error, 1>::from_der(i)?;
162        let (i, mask_gen_algorithm) = OptTaggedParser::new(Class::ContextSpecific, Tag(1))
163            .parse_der(i, |_, inner| AlgorithmIdentifier::from_der(inner))?;
164        let (i, salt_length) = OptTaggedExplicit::<_, Error, 2>::from_der(i)?;
165        let (_, trailer_field) = OptTaggedExplicit::<_, Error, 3>::from_der(i)?;
166        let params = RsaSsaPssParams {
167            hash_alg,
168            mask_gen_algorithm,
169            salt_length: salt_length.map(|t| t.into_inner()),
170            trailer_field: trailer_field.map(|t| t.into_inner()),
171        };
172        Ok(params)
173    }
174}
175
176impl CheckDerConstraints for RsaSsaPssParams<'_> {
177    fn check_constraints(any: &Any) -> asn1_rs::Result<()> {
178        any.header.assert_constructed()?;
179        Ok(())
180    }
181}
182
183impl DerAutoDerive for RsaSsaPssParams<'_> {}
184
185#[derive(Debug, PartialEq, Eq)]
186pub struct MaskGenAlgorithm<'a, 'b> {
187    pub mgf: Oid<'a>,
188    pub hash: Oid<'b>,
189}
190
191impl<'a, 'b> MaskGenAlgorithm<'a, 'b> {
192    pub const fn new(mgf: Oid<'a>, hash: Oid<'b>) -> Self {
193        Self { mgf, hash }
194    }
195}
196
197// RSAAES-OAEP public keys [RFC8017](https://www.rfc-editor.org/rfc/rfc8017.html)
198
199// RSAES-OAEP-params  ::=  SEQUENCE  {
200//     hashFunc          [0] AlgorithmIdentifier DEFAULT
201//                              sha1Identifier,
202//     maskGenFunc       [1] AlgorithmIdentifier DEFAULT
203//                              mgf1SHA1Identifier,
204//     pSourceFunc       [2] AlgorithmIdentifier DEFAULT
205//                              pSpecifiedEmptyIdentifier  }
206//
207//  pSpecifiedEmptyIdentifier  AlgorithmIdentifier  ::=
208//                       { id-pSpecified, nullOctetString }
209//
210//  nullOctetString  OCTET STRING (SIZE (0))  ::=  { ''H }
211#[derive(Debug, PartialEq)]
212pub struct RsaAesOaepParams<'a> {
213    hash_alg: Option<AlgorithmIdentifier<'a>>,
214    mask_gen_alg: Option<AlgorithmIdentifier<'a>>,
215    p_source_alg: Option<AlgorithmIdentifier<'a>>,
216}
217
218impl<'a> RsaAesOaepParams<'a> {
219    pub const EMPTY: &'static AlgorithmIdentifier<'static> = &AlgorithmIdentifier::new(
220        oid! {1.2.840.113549.1.1.9}, // id-pSpecified
221        None,
222    );
223
224    /// Get a reference to the rsa aes oaep params's hash algorithm.
225    pub fn hash_algorithm(&self) -> Option<&AlgorithmIdentifier> {
226        self.hash_alg.as_ref()
227    }
228
229    /// Return the hash algorithm OID, or SHA1 if absent (RFC4055)
230    pub fn hash_algorithm_oid(&self) -> &'a Oid {
231        const SHA1: &Oid = &OID_HASH_SHA1;
232        self.hash_alg
233            .as_ref()
234            .map(|alg| &alg.algorithm)
235            .unwrap_or(SHA1)
236    }
237
238    /// Get a reference to the rsa ssa pss params's mask generation algorithm.
239    pub fn mask_gen_algorithm_raw(&self) -> Option<&AlgorithmIdentifier> {
240        self.mask_gen_alg.as_ref()
241    }
242
243    /// Get the rsa ssa pss params's mask generation algorithm.
244    ///
245    /// If the algorithm encoding is invalid, raise an error `InvalidAlgorithmIdentifier`
246    pub fn mask_gen_algorithm(&self) -> Result<MaskGenAlgorithm, X509Error> {
247        match self.mask_gen_alg.as_ref() {
248            Some(alg) => {
249                let (_, hash) = alg
250                    .parameters()
251                    .and_then(|any| Oid::from_der(any.data).ok())
252                    .ok_or(X509Error::InvalidAlgorithmIdentifier)?;
253                Ok(MaskGenAlgorithm::new(alg.algorithm.clone(), hash))
254            }
255            _ => {
256                Ok(MaskGenAlgorithm::new(
257                    oid! {1.2.840.113549.1.1.8}, // id-mgf1
258                    OID_HASH_SHA1,
259                ))
260            }
261        }
262    }
263
264    /// Return the pSourceFunc algorithm
265    pub fn p_source_alg(&'a self) -> &'a AlgorithmIdentifier {
266        self.p_source_alg.as_ref().unwrap_or(Self::EMPTY)
267    }
268}
269
270impl<'a> TryFrom<Any<'a>> for RsaAesOaepParams<'a> {
271    type Error = X509Error;
272
273    fn try_from(value: Any<'a>) -> Result<Self, Self::Error> {
274        Self::try_from(&value)
275    }
276}
277
278//     hashFunc          [0] AlgorithmIdentifier DEFAULT
279//                              sha1Identifier,
280//     maskGenFunc       [1] AlgorithmIdentifier DEFAULT
281//                              mgf1SHA1Identifier,
282//     pSourceFunc       [2] AlgorithmIdentifier DEFAULT
283//                              pSpecifiedEmptyIdentifier  }
284impl<'a, 'b> TryFrom<&'b Any<'a>> for RsaAesOaepParams<'a> {
285    type Error = X509Error;
286
287    fn try_from(value: &'b Any<'a>) -> Result<Self, Self::Error> {
288        value.tag().assert_eq(Tag::Sequence)?;
289        let i = &value.data;
290        // let (i, hash_alg) = OptTaggedExplicit::<_, X509Error, 0>::from_der(i)?;
291        let (i, hash_alg) = OptTaggedParser::new(Class::ContextSpecific, Tag(0))
292            .parse_der(i, |_, inner| AlgorithmIdentifier::from_der(inner))?;
293        // let (i, mask_gen_algorithm) = OptTaggedExplicit::<_, Error, 1>::from_der(i)?;
294        let (i, mask_gen_alg) = OptTaggedParser::new(Class::ContextSpecific, Tag(1))
295            .parse_der(i, |_, inner| AlgorithmIdentifier::from_der(inner))?;
296        let (_, p_source_alg) = OptTaggedParser::new(Class::ContextSpecific, Tag(2))
297            .parse_der(i, |_, inner| AlgorithmIdentifier::from_der(inner))?;
298        let params = RsaAesOaepParams {
299            hash_alg,
300            mask_gen_alg,
301            p_source_alg,
302        };
303        Ok(params)
304    }
305}
306
307impl CheckDerConstraints for RsaAesOaepParams<'_> {
308    fn check_constraints(any: &Any) -> asn1_rs::Result<()> {
309        any.header.assert_constructed()?;
310        Ok(())
311    }
312}
313
314impl DerAutoDerive for RsaAesOaepParams<'_> {}
315
316// ECC subject public key information [RFC5480](https://datatracker.ietf.org/doc/rfc5480/)
317
318// ECParameters ::= CHOICE {
319//     namedCurve         OBJECT IDENTIFIER
320//     -- implicitCurve   NULL
321//     -- specifiedCurve  SpecifiedECDomain
322//   }
323//     -- implicitCurve and specifiedCurve MUST NOT be used in PKIX.
324//     -- Details for SpecifiedECDomain can be found in [X9.62].
325//     -- Any future additions to this CHOICE should be coordinated
326//     -- with ANSI X9.