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