clia_rustls_mod/webpki/
verify.rs

1use alloc::vec::Vec;
2use core::fmt;
3
4use pki_types::{CertificateDer, ServerName, SignatureVerificationAlgorithm, UnixTime};
5
6use super::anchors::RootCertStore;
7use super::pki_error;
8use crate::enums::SignatureScheme;
9use crate::error::{Error, PeerMisbehaved};
10use crate::verify::{DigitallySignedStruct, HandshakeSignatureValid};
11
12/// Verify that the end-entity certificate `end_entity` is a valid server cert
13/// and chains to at least one of the trust anchors in the `roots` [RootCertStore].
14///
15/// This function is primarily useful when building a custom certificate verifier. It
16/// performs **no revocation checking**. Implementors must handle this themselves,
17/// along with checking that the server certificate is valid for the subject name
18/// being used (see [`verify_server_name`]).
19///
20/// `intermediates` contains all certificates other than `end_entity` that
21/// were sent as part of the server's `Certificate` message. It is in the
22/// same order that the server sent them and may be empty.
23#[allow(dead_code)]
24pub fn verify_server_cert_signed_by_trust_anchor(
25    cert: &ParsedCertificate,
26    roots: &RootCertStore,
27    intermediates: &[CertificateDer<'_>],
28    now: UnixTime,
29    supported_algs: &[&dyn SignatureVerificationAlgorithm],
30) -> Result<(), Error> {
31    verify_server_cert_signed_by_trust_anchor_impl(
32        cert,
33        roots,
34        intermediates,
35        None, // No revocation checking supported with this API.
36        now,
37        supported_algs,
38    )
39}
40
41/// Verify that the `end_entity` has an alternative name matching the `server_name`
42/// note: this only verifies the name and should be used in conjuction with more verification
43/// like [verify_server_cert_signed_by_trust_anchor]
44pub fn verify_server_name(
45    cert: &ParsedCertificate,
46    server_name: &ServerName<'_>,
47) -> Result<(), Error> {
48    cert.0
49        .verify_is_valid_for_subject_name(server_name)
50        .map_err(pki_error)
51}
52
53/// Describes which `webpki` signature verification algorithms are supported and
54/// how they map to TLS [`SignatureScheme`]s.
55#[derive(Clone, Copy)]
56#[allow(unreachable_pub)]
57pub struct WebPkiSupportedAlgorithms {
58    /// A list of all supported signature verification algorithms.
59    ///
60    /// Used for verifying certificate chains.
61    ///
62    /// The order of this list is not significant.
63    pub all: &'static [&'static dyn SignatureVerificationAlgorithm],
64
65    /// A mapping from TLS `SignatureScheme`s to matching webpki signature verification algorithms.
66    ///
67    /// This is one (`SignatureScheme`) to many ([`SignatureVerificationAlgorithm`]) because
68    /// (depending on the protocol version) there is not necessary a 1-to-1 mapping.
69    ///
70    /// For TLS1.2, all `SignatureVerificationAlgorithm`s are tried in sequence.
71    ///
72    /// For TLS1.3, only the first is tried.
73    ///
74    /// The supported schemes in this mapping is communicated to the peer and the order is significant.
75    /// The first mapping is our highest preference.
76    pub mapping: &'static [(
77        SignatureScheme,
78        &'static [&'static dyn SignatureVerificationAlgorithm],
79    )],
80}
81
82impl WebPkiSupportedAlgorithms {
83    /// Return all the `scheme` items in `mapping`, maintaining order.
84    pub fn supported_schemes(&self) -> Vec<SignatureScheme> {
85        self.mapping
86            .iter()
87            .map(|item| item.0)
88            .collect()
89    }
90
91    /// Return the first item in `mapping` that matches `scheme`.
92    fn convert_scheme(
93        &self,
94        scheme: SignatureScheme,
95    ) -> Result<&[&'static dyn SignatureVerificationAlgorithm], Error> {
96        self.mapping
97            .iter()
98            .filter_map(|item| if item.0 == scheme { Some(item.1) } else { None })
99            .next()
100            .ok_or_else(|| PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into())
101    }
102
103    /// Return `true` if all cryptography is FIPS-approved.
104    pub fn fips(&self) -> bool {
105        self.all.iter().all(|alg| alg.fips())
106            && self
107                .mapping
108                .iter()
109                .all(|item| item.1.iter().all(|alg| alg.fips()))
110    }
111}
112
113impl fmt::Debug for WebPkiSupportedAlgorithms {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        write!(f, "WebPkiSupportedAlgorithms {{ all: [ .. ], mapping: ")?;
116        f.debug_list()
117            .entries(self.mapping.iter().map(|item| item.0))
118            .finish()?;
119        write!(f, " }}")
120    }
121}
122
123/// Wrapper around internal representation of a parsed certificate.
124///
125/// This is used in order to avoid parsing twice when specifying custom verification
126pub struct ParsedCertificate<'a>(pub(crate) webpki::EndEntityCert<'a>);
127
128impl<'a> TryFrom<&'a CertificateDer<'a>> for ParsedCertificate<'a> {
129    type Error = Error;
130    fn try_from(value: &'a CertificateDer<'a>) -> Result<Self, Self::Error> {
131        webpki::EndEntityCert::try_from(value)
132            .map_err(pki_error)
133            .map(ParsedCertificate)
134    }
135}
136
137/// Verify a message signature using the `cert` public key and any supported scheme.
138///
139/// This function verifies the `dss` signature over `message` using the subject public key from
140/// `cert`. Since TLS 1.2 doesn't provide enough information to map the `dss.scheme` into a single
141/// [`SignatureVerificationAlgorithm`], this function will map to several candidates and try each in
142/// succession until one succeeds or we exhaust all candidates.
143///
144/// See [WebPkiSupportedAlgorithms::mapping] for more information.
145pub fn verify_tls12_signature(
146    message: &[u8],
147    cert: &CertificateDer<'_>,
148    dss: &DigitallySignedStruct,
149    supported_schemes: &WebPkiSupportedAlgorithms,
150) -> Result<HandshakeSignatureValid, Error> {
151    let possible_algs = supported_schemes.convert_scheme(dss.scheme)?;
152    let cert = webpki::EndEntityCert::try_from(cert).map_err(pki_error)?;
153
154    for alg in possible_algs {
155        match cert.verify_signature(*alg, message, dss.signature()) {
156            Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey) => continue,
157            Err(e) => return Err(pki_error(e)),
158            Ok(()) => return Ok(HandshakeSignatureValid::assertion()),
159        }
160    }
161
162    Err(pki_error(
163        webpki::Error::UnsupportedSignatureAlgorithmForPublicKey,
164    ))
165}
166
167/// Verify a message signature using the `cert` public key and the first TLS 1.3 compatible
168/// supported scheme.
169///
170/// This function verifies the `dss` signature over `message` using the subject public key from
171/// `cert`. Unlike [verify_tls12_signature], this function only tries the first matching scheme. See
172/// [WebPkiSupportedAlgorithms::mapping] for more information.
173pub fn verify_tls13_signature(
174    msg: &[u8],
175    cert: &CertificateDer<'_>,
176    dss: &DigitallySignedStruct,
177    supported_schemes: &WebPkiSupportedAlgorithms,
178) -> Result<HandshakeSignatureValid, Error> {
179    if !dss.scheme.supported_in_tls13() {
180        return Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into());
181    }
182
183    let alg = supported_schemes.convert_scheme(dss.scheme)?[0];
184
185    let cert = webpki::EndEntityCert::try_from(cert).map_err(pki_error)?;
186
187    cert.verify_signature(alg, msg, dss.signature())
188        .map_err(pki_error)
189        .map(|_| HandshakeSignatureValid::assertion())
190}
191
192/// Verify that the end-entity certificate `end_entity` is a valid server cert
193/// and chains to at least one of the trust anchors in the `roots` [RootCertStore].
194///
195/// `intermediates` contains all certificates other than `end_entity` that
196/// were sent as part of the server's `Certificate` message. It is in the
197/// same order that the server sent them and may be empty.
198///
199/// `revocation` controls how revocation checking is performed, if at all.
200///
201/// This function exists to be used by [`verify_server_cert_signed_by_trust_anchor`],
202/// and differs only in providing a `Option<webpki::RevocationOptions>` argument. We
203/// can't include this argument in `verify_server_cert_signed_by_trust_anchor` because
204/// it will leak the webpki types into Rustls' public API.
205pub(crate) fn verify_server_cert_signed_by_trust_anchor_impl(
206    cert: &ParsedCertificate,
207    roots: &RootCertStore,
208    intermediates: &[CertificateDer<'_>],
209    revocation: Option<webpki::RevocationOptions>,
210    now: UnixTime,
211    supported_algs: &[&dyn SignatureVerificationAlgorithm],
212) -> Result<(), Error> {
213    let result = cert.0.verify_for_usage(
214        supported_algs,
215        &roots.roots,
216        intermediates,
217        now,
218        webpki::KeyUsage::server_auth(),
219        revocation,
220        None,
221    );
222    match result {
223        Ok(_) => Ok(()),
224        Err(e) => Err(pki_error(e)),
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use std::format;
231
232    use super::*;
233
234    #[test]
235    fn certificate_debug() {
236        assert_eq!(
237            "CertificateDer(0x6162)",
238            format!("{:?}", CertificateDer::from(b"ab".to_vec()))
239        );
240    }
241
242    #[cfg(feature = "ring")]
243    #[test]
244    fn webpki_supported_algorithms_is_debug() {
245        assert_eq!(
246            "WebPkiSupportedAlgorithms { all: [ .. ], mapping: [ECDSA_NISTP384_SHA384, ECDSA_NISTP256_SHA256, ED25519, RSA_PSS_SHA512, RSA_PSS_SHA384, RSA_PSS_SHA256, RSA_PKCS1_SHA512, RSA_PKCS1_SHA384, RSA_PKCS1_SHA256] }",
247            format!("{:?}", crate::crypto::ring::default_provider().signature_verification_algorithms)
248        );
249    }
250}