Coverage Report

Created: 2026-04-14 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/x509-parser-0.18.1/src/certification_request.rs
Line
Count
Source
1
use crate::cri_attributes::*;
2
use crate::error::{X509Error, X509Result};
3
use crate::extensions::*;
4
use crate::x509::{
5
    parse_signature_value, AlgorithmIdentifier, SubjectPublicKeyInfo, X509Name, X509Version,
6
};
7
8
#[cfg(any(feature = "verify", feature = "verify-aws"))]
9
use crate::verify::verify_signature;
10
use asn1_rs::{BitString, FromDer};
11
use der_parser::der::*;
12
use der_parser::*;
13
use nom::Offset;
14
use std::collections::HashMap;
15
16
/// Certification Signing Request (CSR)
17
#[derive(Debug, PartialEq)]
18
pub struct X509CertificationRequest<'a> {
19
    pub certification_request_info: X509CertificationRequestInfo<'a>,
20
    pub signature_algorithm: AlgorithmIdentifier<'a>,
21
    pub signature_value: BitString<'a>,
22
23
    /// Complete raw ASN.1 DER content (request info, signature algorithm and signature).
24
    pub(crate) raw: &'a [u8],
25
}
26
27
impl<'a> X509CertificationRequest<'a> {
28
    /// Return the raw ASN.1 DER content of the complete signed certification request that was parsed.
29
    ///
30
    /// This includes the certification request info, the signature algorithm, and the signature.
31
    ///
32
    /// We avoid the `AsRef` trait in this instance to ensure the full lifetime of the `X509CertificationRequest` is used.
33
0
    pub fn as_raw(&self) -> &'a [u8] {
34
0
        self.raw
35
0
    }
36
37
0
    pub fn requested_extensions(&self) -> Option<impl Iterator<Item = &ParsedExtension<'_>>> {
38
0
        self.certification_request_info
39
0
            .iter_attributes()
40
0
            .find_map(|attr| {
41
0
                if let ParsedCriAttribute::ExtensionRequest(requested) = &attr.parsed_attribute {
42
0
                    Some(requested.extensions.iter().map(|ext| &ext.parsed_extension))
43
                } else {
44
0
                    None
45
                }
46
0
            })
47
0
    }
48
49
    /// Verify the cryptographic signature of this certification request
50
    ///
51
    /// Uses the public key contained in the CSR, which must be the one of the entity
52
    /// requesting the certification for this verification to succeed.
53
    #[cfg(any(feature = "verify", feature = "verify-aws"))]
54
    #[cfg_attr(docsrs, doc(cfg(any(feature = "verify", feature = "verify-aws"))))]
55
    pub fn verify_signature(&self) -> Result<(), X509Error> {
56
        let spki = &self.certification_request_info.subject_pki;
57
        verify_signature(
58
            spki,
59
            &self.signature_algorithm,
60
            &self.signature_value,
61
            self.certification_request_info.raw,
62
        )
63
    }
64
}
65
66
impl<'a> AsRef<[u8]> for X509CertificationRequest<'a> {
67
0
    fn as_ref(&self) -> &[u8] {
68
0
        self.as_raw()
69
0
    }
70
}
71
72
/// <pre>
73
/// CertificationRequest ::= SEQUENCE {
74
///     certificationRequestInfo CertificationRequestInfo,
75
///     signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }},
76
///     signature          BIT STRING
77
/// }
78
/// </pre>
79
impl<'a> FromDer<'a, X509Error> for X509CertificationRequest<'a> {
80
0
    fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
81
0
        let start_i = i;
82
0
        let (rem, mut req) = parse_der_sequence_defined_g(|i, _| {
83
0
            let (i, certification_request_info) = X509CertificationRequestInfo::from_der(i)?;
84
0
            let (i, signature_algorithm) = AlgorithmIdentifier::from_der(i)?;
85
0
            let (i, signature_value) = parse_signature_value(i)?;
86
0
            let cert = X509CertificationRequest {
87
0
                certification_request_info,
88
0
                signature_algorithm,
89
0
                signature_value,
90
0
                raw: &[],
91
0
            };
92
0
            Ok((i, cert))
93
0
        })(i)?;
94
0
        let len = start_i.offset(rem);
95
0
        req.raw = &start_i[..len];
96
0
        Ok((rem, req))
97
0
    }
98
}
99
100
/// Certification Request Info structure
101
///
102
/// Certification request information is defined by the following ASN.1 structure:
103
///
104
/// <pre>
105
/// CertificationRequestInfo ::= SEQUENCE {
106
///      version       INTEGER { v1(0) } (v1,...),
107
///      subject       Name,
108
///      subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
109
///      attributes    [0] Attributes{{ CRIAttributes }}
110
/// }
111
/// </pre>
112
///
113
/// version is the version number; subject is the distinguished name of the certificate
114
/// subject; subject_pki contains information about the public key being certified, and
115
/// attributes is a collection of attributes providing additional information about the
116
/// subject of the certificate.
117
#[derive(Debug, PartialEq)]
118
pub struct X509CertificationRequestInfo<'a> {
119
    pub version: X509Version,
120
    pub subject: X509Name<'a>,
121
    pub subject_pki: SubjectPublicKeyInfo<'a>,
122
    attributes: Vec<X509CriAttribute<'a>>,
123
    pub raw: &'a [u8],
124
}
125
126
impl X509CertificationRequestInfo<'_> {
127
    /// Get the CRL entry extensions.
128
    #[inline]
129
0
    pub fn attributes(&self) -> &[X509CriAttribute<'_>] {
130
0
        &self.attributes
131
0
    }
132
133
    /// Returns an iterator over the CRL entry extensions
134
    #[inline]
135
0
    pub fn iter_attributes(&self) -> impl Iterator<Item = &X509CriAttribute<'_>> {
136
0
        self.attributes.iter()
137
0
    }
138
139
    /// Searches for a CRL entry extension with the given `Oid`.
140
    ///
141
    /// Note: if there are several extensions with the same `Oid`, the first one is returned.
142
0
    pub fn find_attribute(&self, oid: &Oid) -> Option<&X509CriAttribute<'_>> {
143
0
        self.attributes.iter().find(|&ext| ext.oid == *oid)
144
0
    }
145
146
    /// Builds and returns a map of CRL entry extensions.
147
    ///
148
    /// If an extension is present twice, this will fail and return `DuplicateExtensions`.
149
0
    pub fn attributes_map(&self) -> Result<HashMap<Oid<'_>, &X509CriAttribute<'_>>, X509Error> {
150
0
        self.attributes
151
0
            .iter()
152
0
            .try_fold(HashMap::new(), |mut m, ext| {
153
0
                if m.contains_key(&ext.oid) {
154
0
                    return Err(X509Error::DuplicateAttributes);
155
0
                }
156
0
                m.insert(ext.oid.clone(), ext);
157
0
                Ok(m)
158
0
            })
159
0
    }
160
}
161
162
/// <pre>
163
/// CertificationRequestInfo ::= SEQUENCE {
164
///      version       INTEGER { v1(0) } (v1,...),
165
///      subject       Name,
166
///      subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
167
///      attributes    [0] Attributes{{ CRIAttributes }}
168
/// }
169
/// </pre>
170
impl<'a> FromDer<'a, X509Error> for X509CertificationRequestInfo<'a> {
171
0
    fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
172
0
        let start_i = i;
173
0
        parse_der_sequence_defined_g(move |i, _| {
174
0
            let (i, version) = X509Version::from_der(i)?;
175
0
            let (i, subject) = X509Name::from_der(i)?;
176
0
            let (i, subject_pki) = SubjectPublicKeyInfo::from_der(i)?;
177
0
            let (i, attributes) = parse_cri_attributes(i)?;
178
0
            let len = start_i.offset(i);
179
0
            let tbs = X509CertificationRequestInfo {
180
0
                version,
181
0
                subject,
182
0
                subject_pki,
183
0
                attributes,
184
0
                raw: &start_i[..len],
185
0
            };
186
0
            Ok((i, tbs))
187
0
        })(i)
188
0
    }
189
}