| 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 | |