portable_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    #[cfg(unstable_api_not_supported)] // [FIPS REMOVED FROM THIS FORK]
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
127/// Wrapper around internal representation of a parsed certificate.
128///
129/// This is used in order to avoid parsing twice when specifying custom verification
130pub struct ParsedCertificate<'a>(pub(crate) webpki::EndEntityCert<'a>);
131
132impl ParsedCertificate<'_> {
133    /// Get the parsed certificate's SubjectPublicKeyInfo (SPKI)
134    pub fn subject_public_key_info(&self) -> SubjectPublicKeyInfoDer<'static> {
135        self.0.subject_public_key_info()
136    }
137}
138
139impl<'a> TryFrom<&'a CertificateDer<'a>> for ParsedCertificate<'a> {
140    type Error = Error;
141    fn try_from(value: &'a CertificateDer<'a>) -> Result<Self, Self::Error> {
142        webpki::EndEntityCert::try_from(value)
143            .map_err(pki_error)
144            .map(ParsedCertificate)
145    }
146}
147
148/// Verify a message signature using the `cert` public key and any supported scheme.
149///
150/// This function verifies the `dss` signature over `message` using the subject public key from
151/// `cert`. Since TLS 1.2 doesn't provide enough information to map the `dss.scheme` into a single
152/// [`SignatureVerificationAlgorithm`], this function will map to several candidates and try each in
153/// succession until one succeeds or we exhaust all candidates.
154///
155/// See [WebPkiSupportedAlgorithms::mapping] for more information.
156pub fn verify_tls12_signature(
157    message: &[u8],
158    cert: &CertificateDer<'_>,
159    dss: &DigitallySignedStruct,
160    supported_schemes: &WebPkiSupportedAlgorithms,
161) -> Result<HandshakeSignatureValid, Error> {
162    let possible_algs = supported_schemes.convert_scheme(dss.scheme)?;
163    let cert = webpki::EndEntityCert::try_from(cert).map_err(pki_error)?;
164
165    for alg in possible_algs {
166        match cert.verify_signature(*alg, message, dss.signature()) {
167            Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey) => continue,
168            Err(e) => return Err(pki_error(e)),
169            Ok(()) => return Ok(HandshakeSignatureValid::assertion()),
170        }
171    }
172
173    Err(pki_error(
174        webpki::Error::UnsupportedSignatureAlgorithmForPublicKey,
175    ))
176}
177
178/// Verify a message signature using the `cert` public key and the first TLS 1.3 compatible
179/// supported scheme.
180///
181/// This function verifies the `dss` signature over `message` using the subject public key from
182/// `cert`. Unlike [verify_tls12_signature], this function only tries the first matching scheme. See
183/// [WebPkiSupportedAlgorithms::mapping] for more information.
184pub fn verify_tls13_signature(
185    msg: &[u8],
186    cert: &CertificateDer<'_>,
187    dss: &DigitallySignedStruct,
188    supported_schemes: &WebPkiSupportedAlgorithms,
189) -> Result<HandshakeSignatureValid, Error> {
190    if !dss.scheme.supported_in_tls13() {
191        return Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into());
192    }
193
194    let alg = supported_schemes.convert_scheme(dss.scheme)?[0];
195
196    let cert = webpki::EndEntityCert::try_from(cert).map_err(pki_error)?;
197
198    cert.verify_signature(alg, msg, dss.signature())
199        .map_err(pki_error)
200        .map(|_| HandshakeSignatureValid::assertion())
201}
202
203/// Verify a message signature using a raw public key and the first TLS 1.3 compatible
204/// supported scheme.
205pub fn verify_tls13_signature_with_raw_key(
206    msg: &[u8],
207    spki: &SubjectPublicKeyInfoDer<'_>,
208    dss: &DigitallySignedStruct,
209    supported_schemes: &WebPkiSupportedAlgorithms,
210) -> Result<HandshakeSignatureValid, Error> {
211    if !dss.scheme.supported_in_tls13() {
212        return Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into());
213    }
214
215    let raw_key = webpki::RawPublicKeyEntity::try_from(spki).map_err(pki_error)?;
216    let alg = supported_schemes.convert_scheme(dss.scheme)?[0];
217
218    raw_key
219        .verify_signature(alg, msg, dss.signature())
220        .map_err(pki_error)
221        .map(|_| HandshakeSignatureValid::assertion())
222}
223
224/// Verify that the end-entity certificate `end_entity` is a valid server cert
225/// and chains to at least one of the trust anchors in the `roots` [RootCertStore].
226///
227/// `intermediates` contains all certificates other than `end_entity` that
228/// were sent as part of the server's `Certificate` message. It is in the
229/// same order that the server sent them and may be empty.
230///
231/// `revocation` controls how revocation checking is performed, if at all.
232///
233/// This function exists to be used by [`verify_server_cert_signed_by_trust_anchor`],
234/// and differs only in providing a `Option<webpki::RevocationOptions>` argument. We
235/// can't include this argument in `verify_server_cert_signed_by_trust_anchor` because
236/// it will leak the webpki types into Rustls' public API.
237pub(crate) fn verify_server_cert_signed_by_trust_anchor_impl(
238    cert: &ParsedCertificate<'_>,
239    roots: &RootCertStore,
240    intermediates: &[CertificateDer<'_>],
241    revocation: Option<webpki::RevocationOptions<'_>>,
242    now: UnixTime,
243    supported_algs: &[&dyn SignatureVerificationAlgorithm],
244) -> Result<(), Error> {
245    let result = cert.0.verify_for_usage(
246        supported_algs,
247        &roots.roots,
248        intermediates,
249        now,
250        webpki::KeyUsage::server_auth(),
251        revocation,
252        None,
253    );
254    match result {
255        Ok(_) => Ok(()),
256        Err(e) => Err(pki_error(e)),
257    }
258}
259
260#[cfg(test)]
261mod tests {
262    use std::format;
263
264    use super::*;
265
266    #[test]
267    fn certificate_debug() {
268        assert_eq!(
269            "CertificateDer(0x6162)",
270            format!("{:?}", CertificateDer::from(b"ab".to_vec()))
271        );
272    }
273
274    #[cfg(feature = "ring")]
275    #[test]
276    fn webpki_supported_algorithms_is_debug() {
277        assert_eq!(
278            "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] }",
279            format!("{:?}", crate::crypto::ring::default_provider().signature_verification_algorithms)
280        );
281    }
282}