Coverage Report

Created: 2025-11-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/x509-parser-0.16.0/src/cri_attributes.rs
Line
Count
Source
1
use crate::{
2
    error::{X509Error, X509Result},
3
    extensions::X509Extension,
4
};
5
6
use asn1_rs::{Error, FromDer, Header, Oid, Sequence, Tag};
7
use nom::combinator::{all_consuming, complete};
8
use nom::multi::many0;
9
use nom::Err;
10
use oid_registry::*;
11
use std::collections::HashMap;
12
13
/// Attributes for Certification Request
14
#[derive(Clone, Debug, PartialEq)]
15
pub struct X509CriAttribute<'a> {
16
    pub oid: Oid<'a>,
17
    pub value: &'a [u8],
18
    pub(crate) parsed_attribute: ParsedCriAttribute<'a>,
19
}
20
21
impl<'a> FromDer<'a, X509Error> for X509CriAttribute<'a> {
22
0
    fn from_der(i: &'a [u8]) -> X509Result<X509CriAttribute> {
23
0
        Sequence::from_ber_and_then(i, |i| {
24
0
            let (i, oid) = Oid::from_der(i)?;
25
0
            let value_start = i;
26
0
            let (i, hdr) = Header::from_der(i)?;
27
0
            if hdr.tag() != Tag::Set {
28
0
                return Err(Err::Error(Error::BerTypeError));
29
0
            };
30
31
0
            let (i, parsed_attribute) = crate::cri_attributes::parser::parse_attribute(i, &oid)
32
0
                .map_err(|_| Err::Error(Error::BerValueError))?;
33
0
            let attribute = X509CriAttribute {
34
0
                oid,
35
0
                value: &value_start[..value_start.len() - i.len()],
36
0
                parsed_attribute,
37
0
            };
38
0
            Ok((i, attribute))
39
0
        })
40
0
        .map_err(|_| X509Error::InvalidAttributes.into())
41
0
    }
42
}
43
44
impl<'a> X509CriAttribute<'a> {
45
    /// Return the attribute type or `UnsupportedAttribute` if the attribute is unknown.
46
    #[inline]
47
0
    pub fn parsed_attribute(&self) -> &ParsedCriAttribute<'a> {
48
0
        &self.parsed_attribute
49
0
    }
50
}
51
52
/// Section 3.1 of rfc 5272
53
#[derive(Clone, Debug, PartialEq)]
54
pub struct ExtensionRequest<'a> {
55
    pub extensions: Vec<X509Extension<'a>>,
56
}
57
58
impl<'a> FromDer<'a, X509Error> for ExtensionRequest<'a> {
59
0
    fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
60
0
        parser::parse_extension_request(i).map_err(Err::convert)
61
0
    }
62
}
63
64
#[derive(Clone, Debug, Eq, PartialEq)]
65
pub struct ChallengePassword(pub String);
66
67
/// Attributes for Certification Request
68
#[derive(Clone, Debug, PartialEq)]
69
pub enum ParsedCriAttribute<'a> {
70
    ChallengePassword(ChallengePassword),
71
    ExtensionRequest(ExtensionRequest<'a>),
72
    UnsupportedAttribute,
73
}
74
75
pub(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
0
    pub(crate) fn parse_attribute<'a>(
109
0
        i: &'a [u8],
110
0
        oid: &Oid,
111
0
    ) -> X509Result<'a, ParsedCriAttribute<'a>> {
112
0
        if let Some(parser) = ATTRIBUTE_PARSERS.get(oid) {
113
0
            parser(i)
114
        } else {
115
0
            Ok((i, ParsedCriAttribute::UnsupportedAttribute))
116
        }
117
0
    }
118
119
0
    pub(super) fn parse_extension_request(i: &[u8]) -> X509Result<ExtensionRequest> {
120
0
        crate::extensions::parse_extension_sequence(i)
121
0
            .map(|(i, extensions)| (i, ExtensionRequest { extensions }))
122
0
    }
123
124
0
    fn parse_extension_request_attr(i: &[u8]) -> X509Result<ParsedCriAttribute> {
125
0
        map(
126
0
            parse_extension_request,
127
0
            ParsedCriAttribute::ExtensionRequest,
128
0
        )(i)
129
0
    }
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
0
    pub(super) fn parse_challenge_password(i: &[u8]) -> X509Result<ChallengePassword> {
147
0
        let (rem, obj) = match alt((
148
0
            parse_der_utf8string,
149
0
            parse_der_printablestring,
150
0
            parse_der_universalstring,
151
0
            parse_der_bmpstring,
152
0
            parse_der_t61string, // == teletexString
153
0
        ))(i)
154
        {
155
0
            Ok((rem, obj)) => (rem, obj),
156
0
            Err(_) => return Err(Err::Error(X509Error::InvalidAttributes)),
157
        };
158
0
        match obj.content.as_str() {
159
0
            Ok(s) => Ok((rem, ChallengePassword(s.to_string()))),
160
0
            Err(_) => Err(Err::Error(X509Error::InvalidAttributes)),
161
        }
162
0
    }
163
164
0
    fn parse_challenge_password_attr(i: &[u8]) -> X509Result<ParsedCriAttribute> {
165
0
        map(
166
0
            parse_challenge_password,
167
0
            ParsedCriAttribute::ChallengePassword,
168
0
        )(i)
169
0
    }
170
}
171
172
0
pub(crate) fn parse_cri_attributes(i: &[u8]) -> X509Result<Vec<X509CriAttribute>> {
173
0
    let (i, hdr) = Header::from_der(i).map_err(|_| Err::Error(X509Error::InvalidAttributes))?;
174
0
    if hdr.is_contextspecific() && hdr.tag().0 == 0 {
175
0
        all_consuming(many0(complete(X509CriAttribute::from_der)))(i)
176
    } else {
177
0
        Err(Err::Error(X509Error::InvalidAttributes))
178
    }
179
0
}