Skip to main content

rustls/webpki/
verify.rs

1use pki_types::{
2    CertificateDer, ServerName, SignatureVerificationAlgorithm, SubjectPublicKeyInfoDer, UnixTime,
3};
4use webpki::ExtendedKeyUsage;
5
6use super::anchors::RootCertStore;
7use super::pki_error;
8use crate::crypto::WebPkiSupportedAlgorithms;
9use crate::error::{ApiMisuse, Error, PeerMisbehaved};
10use crate::verify::{HandshakeSignatureValid, SignatureVerificationInput, SignerPublicKey};
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**. Implementers 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.
23pub fn verify_identity_signed_by_trust_anchor(
24    cert: &ParsedCertificate<'_>,
25    roots: &RootCertStore,
26    intermediates: &[CertificateDer<'_>],
27    now: UnixTime,
28    supported_algs: &[&dyn SignatureVerificationAlgorithm],
29) -> Result<(), Error> {
30    verify_identity_signed_by_trust_anchor_impl(
31        cert,
32        roots,
33        intermediates,
34        None, // No revocation checking supported with this API.
35        now,
36        supported_algs,
37    )
38}
39
40/// Verify that the `end_entity` has an alternative name matching the `server_name`.
41///
42/// Note: this only verifies the name and should be used in conjunction with more verification
43/// like [verify_identity_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/// Wrapper around internal representation of a parsed certificate.
54///
55/// This is used in order to avoid parsing twice when specifying custom verification
56pub struct ParsedCertificate<'a>(pub(crate) webpki::EndEntityCert<'a>);
57
58impl ParsedCertificate<'_> {
59    /// Get the parsed certificate's SubjectPublicKeyInfo (SPKI)
60    pub fn subject_public_key_info(&self) -> SubjectPublicKeyInfoDer<'static> {
61        self.0.subject_public_key_info()
62    }
63}
64
65impl<'a> TryFrom<&'a CertificateDer<'a>> for ParsedCertificate<'a> {
66    type Error = Error;
67    fn try_from(value: &'a CertificateDer<'a>) -> Result<Self, Self::Error> {
68        webpki::EndEntityCert::try_from(value)
69            .map_err(pki_error)
70            .map(ParsedCertificate)
71    }
72}
73
74/// Verify a message signature using the `cert` public key and any supported scheme.
75///
76/// This function verifies the `dss` signature over `message` using the subject public key from
77/// `cert`. Since TLS 1.2 doesn't provide enough information to map the `dss.scheme` into a single
78/// [`SignatureVerificationAlgorithm`], this function will map to several candidates and try each in
79/// succession until one succeeds or we exhaust all candidates.
80///
81/// See [WebPkiSupportedAlgorithms::mapping] for more information.
82pub fn verify_tls12_signature(
83    input: &SignatureVerificationInput<'_>,
84    supported_schemes: &WebPkiSupportedAlgorithms,
85) -> Result<HandshakeSignatureValid, Error> {
86    let possible_algs = supported_schemes.convert_scheme(input.signature.scheme)?;
87    let cert = match input.signer {
88        SignerPublicKey::X509(cert_der) => {
89            webpki::EndEntityCert::try_from(*cert_der).map_err(pki_error)?
90        }
91        SignerPublicKey::RawPublicKey(_) => {
92            return Err(ApiMisuse::InvalidSignerForProtocolVersion.into());
93        }
94    };
95
96    let mut error = None;
97    for alg in possible_algs {
98        match cert.verify_signature(*alg, input.message, input.signature.signature()) {
99            Err(err @ webpki::Error::UnsupportedSignatureAlgorithmForPublicKey(_)) => {
100                error = Some(err);
101                continue;
102            }
103            Err(e) => return Err(pki_error(e)),
104            Ok(()) => return Ok(HandshakeSignatureValid::assertion()),
105        }
106    }
107
108    Err(match error {
109        Some(e) => pki_error(e),
110        None => Error::ApiMisuse(ApiMisuse::NoSignatureVerificationAlgorithms),
111    })
112}
113
114/// Verify a message signature using the `cert` public key and the first TLS 1.3 compatible
115/// supported scheme.
116///
117/// This function verifies the `dss` signature over `message` using the subject public key from
118/// `cert`. Unlike [verify_tls12_signature], this function only tries the first matching scheme. See
119/// [WebPkiSupportedAlgorithms::mapping] for more information.
120pub fn verify_tls13_signature(
121    input: &SignatureVerificationInput<'_>,
122    supported_schemes: &WebPkiSupportedAlgorithms,
123) -> Result<HandshakeSignatureValid, Error> {
124    if !input
125        .signature
126        .scheme
127        .supported_in_tls13()
128    {
129        return Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into());
130    }
131
132    let alg = supported_schemes.convert_scheme(input.signature.scheme)?[0];
133    match input.signer {
134        SignerPublicKey::X509(cert_der) => {
135            webpki::EndEntityCert::try_from(*cert_der).and_then(|cert| {
136                cert.verify_signature(alg, input.message, input.signature.signature())
137            })
138        }
139        SignerPublicKey::RawPublicKey(spki) => webpki::RawPublicKeyEntity::try_from(*spki)
140            .and_then(|rpk| rpk.verify_signature(alg, input.message, input.signature.signature())),
141    }
142    .map_err(pki_error)
143    .map(|_| HandshakeSignatureValid::assertion())
144}
145
146/// Verify that the end-entity certificate `end_entity` is a valid server cert
147/// and chains to at least one of the trust anchors in the `roots` [RootCertStore].
148///
149/// `intermediates` contains all certificates other than `end_entity` that
150/// were sent as part of the server's `Certificate` message. It is in the
151/// same order that the server sent them and may be empty.
152///
153/// `revocation` controls how revocation checking is performed, if at all.
154///
155/// This function exists to be used by [`verify_identity_signed_by_trust_anchor`],
156/// and differs only in providing a `Option<webpki::RevocationOptions>` argument. We
157/// can't include this argument in `verify_identity_signed_by_trust_anchor` because
158/// it will leak the webpki types into Rustls' public API.
159pub(crate) fn verify_identity_signed_by_trust_anchor_impl(
160    cert: &ParsedCertificate<'_>,
161    roots: &RootCertStore,
162    intermediates: &[CertificateDer<'_>],
163    revocation: Option<webpki::RevocationOptions<'_>>,
164    now: UnixTime,
165    supported_algs: &[&dyn SignatureVerificationAlgorithm],
166) -> Result<(), Error> {
167    let result = cert.0.verify_for_usage(
168        supported_algs,
169        &roots.roots,
170        intermediates,
171        now,
172        &ExtendedKeyUsage::server_auth(),
173        revocation,
174        None,
175    );
176    match result {
177        Ok(_) => Ok(()),
178        Err(e) => Err(pki_error(e)),
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use std::format;
185
186    use super::*;
187
188    #[test]
189    fn certificate_debug() {
190        assert_eq!(
191            "CertificateDer(0x6162)",
192            format!("{:?}", CertificateDer::from(b"ab".to_vec()))
193        );
194    }
195
196    #[test]
197    fn webpki_supported_algorithms_is_debug() {
198        assert_eq!(
199            "WebPkiSupportedAlgorithms { all: [ .. ], mapping: [] }",
200            format!(
201                "{:?}",
202                WebPkiSupportedAlgorithms {
203                    all: &[],
204                    mapping: &[]
205                }
206            )
207        );
208    }
209}