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 `cert` 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 `cert` 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 `cert` 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 contained in `input` using any supported scheme.
75///
76/// `input` contains a message, purported signature as a [`DigitallySignedStruct`][], and
77/// subject public key.
78///
79/// Since TLS 1.2 doesn't provide enough information to map the `input.signature.scheme` into a single
80/// [`SignatureVerificationAlgorithm`], this function will map to several candidates and try each in
81/// succession until one succeeds or we exhaust all candidates.
82///
83/// See [`WebPkiSupportedAlgorithms::mapping()`] for more information.
84///
85/// [`DigitallySignedStruct`]: crate::DigitallySignedStruct
86pub fn verify_tls12_signature(
87    input: &SignatureVerificationInput<'_>,
88    supported_schemes: &WebPkiSupportedAlgorithms,
89) -> Result<HandshakeSignatureValid, Error> {
90    if input
91        .signature
92        .scheme
93        .algorithm()
94        .is_none()
95    {
96        return Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into());
97    }
98
99    let possible_algs = supported_schemes.convert_scheme(input.signature.scheme)?;
100    let cert = match input.signer {
101        SignerPublicKey::X509(cert_der) => {
102            webpki::EndEntityCert::try_from(*cert_der).map_err(pki_error)?
103        }
104        SignerPublicKey::RawPublicKey(_) => {
105            return Err(ApiMisuse::InvalidSignerForProtocolVersion.into());
106        }
107    };
108
109    let mut error = None;
110    for alg in possible_algs {
111        match cert.verify_signature(*alg, input.message, input.signature.signature()) {
112            Err(err @ webpki::Error::UnsupportedSignatureAlgorithmForPublicKey(_)) => {
113                error = Some(err);
114                continue;
115            }
116            Err(e) => return Err(pki_error(e)),
117            Ok(()) => return Ok(HandshakeSignatureValid::assertion()),
118        }
119    }
120
121    Err(match error {
122        Some(e) => pki_error(e),
123        None => Error::ApiMisuse(ApiMisuse::NoSignatureVerificationAlgorithms),
124    })
125}
126
127/// Verify a message signature contained in `input` using the first TLS1.3 supported scheme.
128///
129/// `input` contains a message, purported signature as a [`DigitallySignedStruct`][], and
130/// subject public key.
131///
132/// Unlike [`verify_tls12_signature()`], this function only tries the first matching scheme. See
133/// [`WebPkiSupportedAlgorithms::mapping()`] for more information.
134///
135/// [`DigitallySignedStruct`]: crate::DigitallySignedStruct
136pub fn verify_tls13_signature(
137    input: &SignatureVerificationInput<'_>,
138    supported_schemes: &WebPkiSupportedAlgorithms,
139) -> Result<HandshakeSignatureValid, Error> {
140    if !input
141        .signature
142        .scheme
143        .supported_in_tls13()
144    {
145        return Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into());
146    }
147
148    let &alg = supported_schemes
149        .convert_scheme(input.signature.scheme)?
150        .first()
151        .ok_or(Error::ApiMisuse(
152            ApiMisuse::NoSignatureVerificationAlgorithms,
153        ))?;
154
155    match input.signer {
156        SignerPublicKey::X509(cert_der) => {
157            webpki::EndEntityCert::try_from(*cert_der).and_then(|cert| {
158                cert.verify_signature(alg, input.message, input.signature.signature())
159            })
160        }
161        SignerPublicKey::RawPublicKey(spki) => webpki::RawPublicKeyEntity::try_from(*spki)
162            .and_then(|rpk| rpk.verify_signature(alg, input.message, input.signature.signature())),
163    }
164    .map_err(pki_error)
165    .map(|_| HandshakeSignatureValid::assertion())
166}
167
168/// Verify that the end-entity certificate `cert` is a valid server cert
169/// and chains to at least one of the trust anchors in the `roots` [RootCertStore].
170///
171/// `intermediates` contains all certificates other than `cert` that
172/// were sent as part of the server's `Certificate` message. It is in the
173/// same order that the server sent them and may be empty.
174///
175/// `revocation` controls how revocation checking is performed, if at all.
176///
177/// This function exists to be used by [`verify_identity_signed_by_trust_anchor()`],
178/// and differs only in providing a `Option<webpki::RevocationOptions>` argument. We
179/// can't include this argument in `verify_identity_signed_by_trust_anchor()` because
180/// it will leak the webpki types into Rustls' public API.
181pub(crate) fn verify_identity_signed_by_trust_anchor_impl(
182    cert: &ParsedCertificate<'_>,
183    roots: &RootCertStore,
184    intermediates: &[CertificateDer<'_>],
185    revocation: Option<webpki::RevocationOptions<'_>>,
186    now: UnixTime,
187    supported_algs: &[&dyn SignatureVerificationAlgorithm],
188) -> Result<(), Error> {
189    let result = cert.0.verify_for_usage(
190        supported_algs,
191        &roots.roots,
192        intermediates,
193        now,
194        &ExtendedKeyUsage::server_auth(),
195        revocation,
196        None,
197    );
198    match result {
199        Ok(_) => Ok(()),
200        Err(e) => Err(pki_error(e)),
201    }
202}
203
204#[cfg(test)]
205mod tests {
206    use alloc::vec;
207    use std::format;
208
209    use super::*;
210    use crate::crypto::{SignatureScheme, TEST_PROVIDER};
211    use crate::verify::DigitallySignedStruct;
212
213    #[test]
214    fn tls13_empty_signature_mapping_panics() {
215        let supported = WebPkiSupportedAlgorithms {
216            all: TEST_PROVIDER
217                .signature_verification_algorithms
218                .all,
219            mapping: &[(SignatureScheme::ED25519, &[])],
220        };
221
222        let cert = CertificateDer::from(vec![0u8]); // never parsed; panic happens first
223        let dss = DigitallySignedStruct::new(SignatureScheme::ED25519, vec![]);
224        let signer = SignerPublicKey::X509(&cert);
225        let input = SignatureVerificationInput {
226            message: b"hello",
227            signer: &signer,
228            signature: &dss,
229        };
230
231        assert_eq!(
232            verify_tls13_signature(&input, &supported).unwrap_err(),
233            Error::ApiMisuse(ApiMisuse::NoSignatureVerificationAlgorithms)
234        );
235    }
236
237    #[test]
238    fn certificate_debug() {
239        assert_eq!(
240            "CertificateDer(0x6162)",
241            format!("{:?}", CertificateDer::from(b"ab".to_vec()))
242        );
243    }
244
245    #[test]
246    fn webpki_supported_algorithms_is_debug() {
247        assert_eq!(
248            "WebPkiSupportedAlgorithms { all: [ .. ], mapping: [] }",
249            format!(
250                "{:?}",
251                WebPkiSupportedAlgorithms {
252                    all: &[],
253                    mapping: &[]
254                }
255            )
256        );
257    }
258}