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