x509_parser/
cri_attributes.rs

1use crate::{
2    error::{X509Error, X509Result},
3    extensions::X509Extension,
4};
5
6use asn1_rs::{Error, FromDer, Header, Oid, Sequence, Tag};
7use nom::combinator::{all_consuming, complete};
8use nom::multi::many0;
9use nom::Err;
10use oid_registry::*;
11use std::collections::HashMap;
12
13/// Attributes for Certification Request
14#[derive(Clone, Debug, PartialEq)]
15pub struct X509CriAttribute<'a> {
16    pub oid: Oid<'a>,
17    pub value: &'a [u8],
18    pub(crate) parsed_attribute: ParsedCriAttribute<'a>,
19}
20
21impl<'a> FromDer<'a, X509Error> for X509CriAttribute<'a> {
22    fn from_der(i: &'a [u8]) -> X509Result<'a, X509CriAttribute<'a>> {
23        Sequence::from_ber_and_then(i, |i| {
24            let (i, oid) = Oid::from_der(i)?;
25            let value_start = i;
26            let (i, hdr) = Header::from_der(i)?;
27            if hdr.tag() != Tag::Set {
28                return Err(Err::Error(Error::BerTypeError));
29            };
30
31            let (i, parsed_attribute) =
32                parser::parse_attribute(i, &oid).map_err(|_| Err::Error(Error::BerValueError))?;
33            let attribute = X509CriAttribute {
34                oid,
35                value: &value_start[..value_start.len() - i.len()],
36                parsed_attribute,
37            };
38            Ok((i, attribute))
39        })
40        .map_err(|_| X509Error::InvalidAttributes.into())
41    }
42}
43
44impl<'a> X509CriAttribute<'a> {
45    /// Return the attribute type or `UnsupportedAttribute` if the attribute is unknown.
46    #[inline]
47    pub fn parsed_attribute(&self) -> &ParsedCriAttribute<'a> {
48        &self.parsed_attribute
49    }
50}
51
52/// Section 3.1 of rfc 5272
53#[derive(Clone, Debug, PartialEq)]
54pub struct ExtensionRequest<'a> {
55    pub extensions: Vec<X509Extension<'a>>,
56}
57
58impl<'a> FromDer<'a, X509Error> for ExtensionRequest<'a> {
59    fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
60        parser::parse_extension_request(i).map_err(Err::convert)
61    }
62}
63
64#[derive(Clone, Debug, Eq, PartialEq)]
65pub struct ChallengePassword(pub String);
66
67/// Attributes for Certification Request
68#[derive(Clone, Debug, PartialEq)]
69pub enum ParsedCriAttribute<'a> {
70    ChallengePassword(ChallengePassword),
71    ExtensionRequest(ExtensionRequest<'a>),
72    UnsupportedAttribute,
73}
74
75pub(crate) mod parser {
76    use crate::cri_attributes::*;
77    use der_parser::der::{
78        parse_der_bmpstring, parse_der_printablestring, parse_der_t61string,
79        parse_der_universalstring, parse_der_utf8string,
80    };
81    use lazy_static::lazy_static;
82    use nom::branch::alt;
83    use nom::combinator::map;
84
85    type AttrParser = fn(&[u8]) -> X509Result<ParsedCriAttribute>;
86
87    lazy_static! {
88        static ref ATTRIBUTE_PARSERS: HashMap<Oid<'static>, AttrParser> = {
89            macro_rules! add {
90                ($m:ident, $oid:ident, $p:ident) => {
91                    $m.insert($oid, $p as AttrParser);
92                };
93            }
94
95            let mut m = HashMap::new();
96            add!(m, OID_PKCS9_EXTENSION_REQUEST, parse_extension_request_attr);
97            add!(
98                m,
99                OID_PKCS9_CHALLENGE_PASSWORD,
100                parse_challenge_password_attr
101            );
102            m
103        };
104    }
105
106    // look into the parser map if the extension is known, and parse it
107    // otherwise, leave it as UnsupportedExtension
108    pub(crate) fn parse_attribute<'a>(
109        i: &'a [u8],
110        oid: &Oid,
111    ) -> X509Result<'a, ParsedCriAttribute<'a>> {
112        if let Some(parser) = ATTRIBUTE_PARSERS.get(oid) {
113            parser(i)
114        } else {
115            Ok((i, ParsedCriAttribute::UnsupportedAttribute))
116        }
117    }
118
119    pub(super) fn parse_extension_request(i: &[u8]) -> X509Result<ExtensionRequest> {
120        crate::extensions::parse_extension_sequence(i)
121            .map(|(i, extensions)| (i, ExtensionRequest { extensions }))
122    }
123
124    fn parse_extension_request_attr(i: &[u8]) -> X509Result<ParsedCriAttribute> {
125        map(
126            parse_extension_request,
127            ParsedCriAttribute::ExtensionRequest,
128        )(i)
129    }
130
131    // RFC 2985, 5.4.1 Challenge password
132    //    challengePassword ATTRIBUTE ::= {
133    //            WITH SYNTAX DirectoryString {pkcs-9-ub-challengePassword}
134    //            EQUALITY MATCHING RULE caseExactMatch
135    //            SINGLE VALUE TRUE
136    //            ID pkcs-9-at-challengePassword
137    //    }
138    // RFC 5280, 4.1.2.4.  Issuer
139    //    DirectoryString ::= CHOICE {
140    //          teletexString           TeletexString (SIZE (1..MAX)),
141    //          printableString         PrintableString (SIZE (1..MAX)),
142    //          universalString         UniversalString (SIZE (1..MAX)),
143    //          utf8String              UTF8String (SIZE (1..MAX)),
144    //          bmpString               BMPString (SIZE (1..MAX))
145    //    }
146    pub(super) fn parse_challenge_password(i: &[u8]) -> X509Result<ChallengePassword> {
147        let (rem, obj) = match alt((
148            parse_der_utf8string,
149            parse_der_printablestring,
150            parse_der_universalstring,
151            parse_der_bmpstring,
152            parse_der_t61string, // == teletexString
153        ))(i)
154        {
155            Ok((rem, obj)) => (rem, obj),
156            Err(_) => return Err(Err::Error(X509Error::InvalidAttributes)),
157        };
158        match obj.content.as_str() {
159            Ok(s) => Ok((rem, ChallengePassword(s.to_string()))),
160            Err(_) => Err(Err::Error(X509Error::InvalidAttributes)),
161        }
162    }
163
164    fn parse_challenge_password_attr(i: &[u8]) -> X509Result<ParsedCriAttribute> {
165        map(
166            parse_challenge_password,
167            ParsedCriAttribute::ChallengePassword,
168        )(i)
169    }
170}
171
172pub(crate) fn parse_cri_attributes(i: &[u8]) -> X509Result<Vec<X509CriAttribute>> {
173    let (i, hdr) = Header::from_der(i).map_err(|_| Err::Error(X509Error::InvalidAttributes))?;
174    if hdr.is_contextspecific() && hdr.tag().0 == 0 {
175        all_consuming(many0(complete(X509CriAttribute::from_der)))(i)
176    } else {
177        Err(Err::Error(X509Error::InvalidAttributes))
178    }
179}