Skip to main content

rustls/webpki/
verify.rs

1use pki_types::{
2    CertificateDer, ServerName, SignatureVerificationAlgorithm, SubjectPublicKeyInfoDer, UnixTime,
3};
4use webpki::{ExtendedKeyUsage, PathBuilder};
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 = PathBuilder::new(
190        intermediates,
191        revocation,
192        &ExtendedKeyUsage::SERVER_AUTH,
193        supported_algs,
194        &roots.roots,
195    )
196    .build(&cert.0, now);
197    match result {
198        Ok(_) => Ok(()),
199        Err(e) => Err(pki_error(e)),
200    }
201}
202
203#[cfg(test)]
204mod tests {
205    use alloc::vec;
206    use std::format;
207
208    use super::*;
209    use crate::crypto::{SignatureScheme, TEST_PROVIDER};
210    use crate::verify::DigitallySignedStruct;
211
212    #[test]
213    fn tls13_empty_signature_mapping_panics() {
214        let supported = WebPkiSupportedAlgorithms {
215            all: TEST_PROVIDER
216                .signature_verification_algorithms
217                .all,
218            mapping: &[(SignatureScheme::ED25519, &[])],
219        };
220
221        let cert = CertificateDer::from(vec![0u8]); // never parsed; panic happens first
222        let dss = DigitallySignedStruct::new(SignatureScheme::ED25519, vec![]);
223        let signer = SignerPublicKey::X509(&cert);
224        let input = SignatureVerificationInput {
225            message: b"hello",
226            signer: &signer,
227            signature: &dss,
228        };
229
230        assert_eq!(
231            verify_tls13_signature(&input, &supported).unwrap_err(),
232            Error::ApiMisuse(ApiMisuse::NoSignatureVerificationAlgorithms)
233        );
234    }
235
236    #[test]
237    fn certificate_debug() {
238        assert_eq!(
239            "CertificateDer(0x6162)",
240            format!("{:?}", CertificateDer::from(b"ab".to_vec()))
241        );
242    }
243
244    #[test]
245    fn webpki_supported_algorithms_is_debug() {
246        assert_eq!(
247            "WebPkiSupportedAlgorithms { all: [ .. ], mapping: [] }",
248            format!(
249                "{:?}",
250                WebPkiSupportedAlgorithms {
251                    all: &[],
252                    mapping: &[]
253                }
254            )
255        );
256    }
257}