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 | pub 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 | revocation: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] |
47 | pub 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(op: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)] |
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 | 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 | |
116 | impl fmt::Debug for WebPkiSupportedAlgorithms { |
117 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
118 | write!(f, "WebPkiSupportedAlgorithms {{ all: [ .. ], mapping: " )?; |
119 | f&mut DebugList<'_, '_>.debug_list() |
120 | .entries(self.mapping.iter().map(|item: &(SignatureScheme, &[&dyn SignatureVerificationAlgorithm])| 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 |
129 | pub struct ParsedCertificate<'a>(pub(crate) webpki::EndEntityCert<'a>); |
130 | |
131 | impl 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 | |
138 | impl<'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(op: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. |
155 | pub 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: &[&dyn SignatureVerificationAlgorithm] = supported_schemes.convert_scheme(dss.scheme)?; |
162 | let cert: EndEntityCert<'_> = webpki::EndEntityCert::try_from(cert).map_err(op:pki_error)?; |
163 | |
164 | for alg: &&dyn SignatureVerificationAlgorithm in possible_algs { |
165 | match cert.verify_signature(*alg, msg:message, dss.signature()) { |
166 | Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey) => continue, |
167 | Err(e: Error) => 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. |
183 | pub 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: &'static dyn SignatureVerificationAlgorithm = supported_schemes.convert_scheme(dss.scheme)?[0]; |
194 | |
195 | let cert: EndEntityCert<'_> = webpki::EndEntityCert::try_from(cert).map_err(op: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. |
204 | pub 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: RawPublicKeyEntity<'_> = webpki::RawPublicKeyEntity::try_from(spki).map_err(op:pki_error)?; |
215 | let alg: &'static dyn SignatureVerificationAlgorithm = 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. |
236 | pub(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: Result, …> = cert.0.verify_for_usage( |
245 | supported_sig_algs:supported_algs, |
246 | &roots.roots, |
247 | intermediate_certs:intermediates, |
248 | time:now, |
249 | usage:webpki::KeyUsage::server_auth(), |
250 | revocation, |
251 | verify_path:None, |
252 | ); |
253 | match result { |
254 | Ok(_) => Ok(()), |
255 | Err(e: Error) => Err(pki_error(e)), |
256 | } |
257 | } |
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 | } |
285 | |