rustls/webpki/
verify.rs

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