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