rustls/webpki/
verify.rs

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