/rust/registry/src/index.crates.io-6f17d22bba15001f/rustls-0.23.26/src/webpki/verify.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use alloc::vec::Vec; |
2 | | use core::fmt; |
3 | | |
4 | | use pki_types::{ |
5 | | CertificateDer, ServerName, SignatureVerificationAlgorithm, SubjectPublicKeyInfoDer, UnixTime, |
6 | | }; |
7 | | |
8 | | use super::anchors::RootCertStore; |
9 | | use super::pki_error; |
10 | | use crate::enums::SignatureScheme; |
11 | | use crate::error::{Error, PeerMisbehaved}; |
12 | | use 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)] |
26 | 0 | pub fn verify_server_cert_signed_by_trust_anchor( |
27 | 0 | cert: &ParsedCertificate<'_>, |
28 | 0 | roots: &RootCertStore, |
29 | 0 | intermediates: &[CertificateDer<'_>], |
30 | 0 | now: UnixTime, |
31 | 0 | supported_algs: &[&dyn SignatureVerificationAlgorithm], |
32 | 0 | ) -> Result<(), Error> { |
33 | 0 | verify_server_cert_signed_by_trust_anchor_impl( |
34 | 0 | cert, |
35 | 0 | roots, |
36 | 0 | intermediates, |
37 | 0 | None, // No revocation checking supported with this API. |
38 | 0 | now, |
39 | 0 | supported_algs, |
40 | 0 | ) |
41 | 0 | } |
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] |
47 | 0 | pub fn verify_server_name( |
48 | 0 | cert: &ParsedCertificate<'_>, |
49 | 0 | server_name: &ServerName<'_>, |
50 | 0 | ) -> Result<(), Error> { |
51 | 0 | cert.0 |
52 | 0 | .verify_is_valid_for_subject_name(server_name) |
53 | 0 | .map_err(pki_error) |
54 | 0 | } |
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)] |
60 | | pub 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 | | |
85 | | impl WebPkiSupportedAlgorithms { |
86 | | /// Return all the `scheme` items in `mapping`, maintaining order. |
87 | 0 | pub fn supported_schemes(&self) -> Vec<SignatureScheme> { |
88 | 0 | self.mapping |
89 | 0 | .iter() |
90 | 0 | .map(|item| item.0) |
91 | 0 | .collect() |
92 | 0 | } |
93 | | |
94 | | /// Return the first item in `mapping` that matches `scheme`. |
95 | 0 | fn convert_scheme( |
96 | 0 | &self, |
97 | 0 | scheme: SignatureScheme, |
98 | 0 | ) -> Result<&[&'static dyn SignatureVerificationAlgorithm], Error> { |
99 | 0 | self.mapping |
100 | 0 | .iter() |
101 | 0 | .filter_map(|item| if item.0 == scheme { Some(item.1) } else { None }) |
102 | 0 | .next() |
103 | 0 | .ok_or_else(|| PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into()) |
104 | 0 | } |
105 | | |
106 | | /// Return `true` if all cryptography is FIPS-approved. |
107 | 0 | pub fn fips(&self) -> bool { |
108 | 0 | self.all.iter().all(|alg| alg.fips()) |
109 | 0 | && self |
110 | 0 | .mapping |
111 | 0 | .iter() |
112 | 0 | .all(|item| item.1.iter().all(|alg| alg.fips())) |
113 | 0 | } |
114 | | } |
115 | | |
116 | | impl fmt::Debug for WebPkiSupportedAlgorithms { |
117 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
118 | 0 | write!(f, "WebPkiSupportedAlgorithms {{ all: [ .. ], mapping: ")?; |
119 | 0 | f.debug_list() |
120 | 0 | .entries(self.mapping.iter().map(|item| item.0)) |
121 | 0 | .finish()?; |
122 | 0 | write!(f, " }}") |
123 | 0 | } |
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 |
129 | | pub struct ParsedCertificate<'a>(pub(crate) webpki::EndEntityCert<'a>); |
130 | | |
131 | | impl ParsedCertificate<'_> { |
132 | | /// Get the parsed certificate's SubjectPublicKeyInfo (SPKI) |
133 | 0 | pub fn subject_public_key_info(&self) -> SubjectPublicKeyInfoDer<'static> { |
134 | 0 | self.0.subject_public_key_info() |
135 | 0 | } |
136 | | } |
137 | | |
138 | | impl<'a> TryFrom<&'a CertificateDer<'a>> for ParsedCertificate<'a> { |
139 | | type Error = Error; |
140 | 0 | fn try_from(value: &'a CertificateDer<'a>) -> Result<Self, Self::Error> { |
141 | 0 | webpki::EndEntityCert::try_from(value) |
142 | 0 | .map_err(pki_error) |
143 | 0 | .map(ParsedCertificate) |
144 | 0 | } |
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. |
155 | 0 | pub fn verify_tls12_signature( |
156 | 0 | message: &[u8], |
157 | 0 | cert: &CertificateDer<'_>, |
158 | 0 | dss: &DigitallySignedStruct, |
159 | 0 | supported_schemes: &WebPkiSupportedAlgorithms, |
160 | 0 | ) -> Result<HandshakeSignatureValid, Error> { |
161 | 0 | let possible_algs = supported_schemes.convert_scheme(dss.scheme)?; |
162 | 0 | let cert = webpki::EndEntityCert::try_from(cert).map_err(pki_error)?; |
163 | | |
164 | 0 | for alg in possible_algs { |
165 | 0 | match cert.verify_signature(*alg, message, dss.signature()) { |
166 | 0 | Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey) => continue, |
167 | 0 | Err(e) => return Err(pki_error(e)), |
168 | 0 | Ok(()) => return Ok(HandshakeSignatureValid::assertion()), |
169 | | } |
170 | | } |
171 | | |
172 | 0 | Err(pki_error( |
173 | 0 | webpki::Error::UnsupportedSignatureAlgorithmForPublicKey, |
174 | 0 | )) |
175 | 0 | } |
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. |
183 | 0 | pub fn verify_tls13_signature( |
184 | 0 | msg: &[u8], |
185 | 0 | cert: &CertificateDer<'_>, |
186 | 0 | dss: &DigitallySignedStruct, |
187 | 0 | supported_schemes: &WebPkiSupportedAlgorithms, |
188 | 0 | ) -> Result<HandshakeSignatureValid, Error> { |
189 | 0 | if !dss.scheme.supported_in_tls13() { |
190 | 0 | return Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into()); |
191 | 0 | } |
192 | | |
193 | 0 | let alg = supported_schemes.convert_scheme(dss.scheme)?[0]; |
194 | | |
195 | 0 | let cert = webpki::EndEntityCert::try_from(cert).map_err(pki_error)?; |
196 | | |
197 | 0 | cert.verify_signature(alg, msg, dss.signature()) |
198 | 0 | .map_err(pki_error) |
199 | 0 | .map(|_| HandshakeSignatureValid::assertion()) |
200 | 0 | } |
201 | | |
202 | | /// Verify a message signature using a raw public key and the first TLS 1.3 compatible |
203 | | /// supported scheme. |
204 | 0 | pub fn verify_tls13_signature_with_raw_key( |
205 | 0 | msg: &[u8], |
206 | 0 | spki: &SubjectPublicKeyInfoDer<'_>, |
207 | 0 | dss: &DigitallySignedStruct, |
208 | 0 | supported_schemes: &WebPkiSupportedAlgorithms, |
209 | 0 | ) -> Result<HandshakeSignatureValid, Error> { |
210 | 0 | if !dss.scheme.supported_in_tls13() { |
211 | 0 | return Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into()); |
212 | 0 | } |
213 | | |
214 | 0 | let raw_key = webpki::RawPublicKeyEntity::try_from(spki).map_err(pki_error)?; |
215 | 0 | let alg = supported_schemes.convert_scheme(dss.scheme)?[0]; |
216 | 0 |
|
217 | 0 | raw_key |
218 | 0 | .verify_signature(alg, msg, dss.signature()) |
219 | 0 | .map_err(pki_error) |
220 | 0 | .map(|_| HandshakeSignatureValid::assertion()) |
221 | 0 | } |
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. |
236 | 0 | pub(crate) fn verify_server_cert_signed_by_trust_anchor_impl( |
237 | 0 | cert: &ParsedCertificate<'_>, |
238 | 0 | roots: &RootCertStore, |
239 | 0 | intermediates: &[CertificateDer<'_>], |
240 | 0 | revocation: Option<webpki::RevocationOptions<'_>>, |
241 | 0 | now: UnixTime, |
242 | 0 | supported_algs: &[&dyn SignatureVerificationAlgorithm], |
243 | 0 | ) -> Result<(), Error> { |
244 | 0 | let result = cert.0.verify_for_usage( |
245 | 0 | supported_algs, |
246 | 0 | &roots.roots, |
247 | 0 | intermediates, |
248 | 0 | now, |
249 | 0 | webpki::KeyUsage::server_auth(), |
250 | 0 | revocation, |
251 | 0 | None, |
252 | 0 | ); |
253 | 0 | match result { |
254 | 0 | Ok(_) => Ok(()), |
255 | 0 | Err(e) => Err(pki_error(e)), |
256 | | } |
257 | 0 | } |
258 | | |
259 | | #[cfg(test)] |
260 | | mod 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!( |
279 | | "{:?}", |
280 | | crate::crypto::ring::default_provider().signature_verification_algorithms |
281 | | ) |
282 | | ); |
283 | | } |
284 | | } |