rustls/rustls/src/error.rs

738 lines
26 KiB
Rust

use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
#[cfg(feature = "std")]
use std::time::SystemTimeError;
use crate::enums::{AlertDescription, ContentType, HandshakeType};
use crate::msgs::handshake::KeyExchangeAlgorithm;
use crate::rand;
/// rustls reports protocol errors using this type.
#[non_exhaustive]
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
/// We received a TLS message that isn't valid right now.
/// `expect_types` lists the message types we can expect right now.
/// `got_type` is the type we found. This error is typically
/// caused by a buggy TLS stack (the peer or this one), a broken
/// network, or an attack.
InappropriateMessage {
/// Which types we expected
expect_types: Vec<ContentType>,
/// What type we received
got_type: ContentType,
},
/// We received a TLS handshake message that isn't valid right now.
/// `expect_types` lists the handshake message types we can expect
/// right now. `got_type` is the type we found.
InappropriateHandshakeMessage {
/// Which handshake type we expected
expect_types: Vec<HandshakeType>,
/// What handshake type we received
got_type: HandshakeType,
},
/// The peer sent us a TLS message with invalid contents.
InvalidMessage(InvalidMessage),
/// The peer didn't give us any certificates.
NoCertificatesPresented,
/// The certificate verifier doesn't support the given type of name.
UnsupportedNameType,
/// We couldn't decrypt a message. This is invariably fatal.
DecryptError,
/// We couldn't encrypt a message because it was larger than the allowed message size.
/// This should never happen if the application is using valid record sizes.
EncryptError,
/// The peer doesn't support a protocol version/feature we require.
/// The parameter gives a hint as to what version/feature it is.
PeerIncompatible(PeerIncompatible),
/// The peer deviated from the standard TLS protocol.
/// The parameter gives a hint where.
PeerMisbehaved(PeerMisbehaved),
/// We received a fatal alert. This means the peer is unhappy.
AlertReceived(AlertDescription),
/// We saw an invalid certificate.
///
/// The contained error is from the certificate validation trait
/// implementation.
InvalidCertificate(CertificateError),
/// A provided certificate revocation list (CRL) was invalid.
InvalidCertRevocationList(CertRevocationListError),
/// A catch-all error for unlikely errors.
General(String),
/// We failed to figure out what time it currently is.
FailedToGetCurrentTime,
/// We failed to acquire random bytes from the system.
FailedToGetRandomBytes,
/// This function doesn't work until the TLS handshake
/// is complete.
HandshakeNotComplete,
/// The peer sent an oversized record/fragment.
PeerSentOversizedRecord,
/// An incoming connection did not support any known application protocol.
NoApplicationProtocol,
/// The `max_fragment_size` value supplied in configuration was too small,
/// or too large.
BadMaxFragmentSize,
/// Any other error.
///
/// This variant should only be used when the error is not better described by a more
/// specific variant. For example, if a custom crypto provider returns a
/// provider specific error.
///
/// Enums holding this variant will never compare equal to each other.
Other(OtherError),
}
/// A corrupt TLS message payload that resulted in an error.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum InvalidMessage {
/// An advertised message was larger then expected.
HandshakePayloadTooLarge,
/// The peer sent us a syntactically incorrect ChangeCipherSpec payload.
InvalidCcs,
/// An unknown content type was encountered during message decoding.
InvalidContentType,
/// A peer sent an invalid certificate status type
InvalidCertificateStatusType,
/// Context was incorrectly attached to a certificate request during a handshake.
InvalidCertRequest,
/// A peer's DH params could not be decoded
InvalidDhParams,
/// A message was zero-length when its record kind forbids it.
InvalidEmptyPayload,
/// A peer sent an unexpected key update request.
InvalidKeyUpdate,
/// A peer's server name could not be decoded
InvalidServerName,
/// A TLS message payload was larger then allowed by the specification.
MessageTooLarge,
/// Message is shorter than the expected length
MessageTooShort,
/// Missing data for the named handshake payload value
MissingData(&'static str),
/// A peer did not advertise its supported key exchange groups.
MissingKeyExchange,
/// A peer sent an empty list of signature schemes
NoSignatureSchemes,
/// Trailing data found for the named handshake payload value
TrailingData(&'static str),
/// A peer sent an unexpected message type.
UnexpectedMessage(&'static str),
/// An unknown TLS protocol was encountered during message decoding.
UnknownProtocolVersion,
/// A peer sent a non-null compression method.
UnsupportedCompression,
/// A peer sent an unknown elliptic curve type.
UnsupportedCurveType,
/// A peer sent an unsupported key exchange algorithm.
UnsupportedKeyExchangeAlgorithm(KeyExchangeAlgorithm),
/// A peer sent a message where a given extension type was repeated
DuplicateExtension,
/// A peer sent a ClientHello with a "pre_shared_key" extension before another extension
PreSharedKeyIsNotFinalExtension,
}
impl From<InvalidMessage> for Error {
#[inline]
fn from(e: InvalidMessage) -> Self {
Self::InvalidMessage(e)
}
}
impl From<InvalidMessage> for AlertDescription {
fn from(e: InvalidMessage) -> Self {
match e {
InvalidMessage::PreSharedKeyIsNotFinalExtension => Self::IllegalParameter,
_ => Self::DecodeError,
}
}
}
#[non_exhaustive]
#[allow(missing_docs)]
#[derive(Debug, PartialEq, Clone)]
/// The set of cases where we failed to make a connection because we thought
/// the peer was misbehaving.
///
/// This is `non_exhaustive`: we might add or stop using items here in minor
/// versions. We also don't document what they mean. Generally a user of
/// rustls shouldn't vary its behaviour on these error codes, and there is
/// nothing it can do to improve matters.
///
/// Please file a bug against rustls if you see `Error::PeerMisbehaved` in
/// the wild.
pub enum PeerMisbehaved {
AttemptedDowngradeToTls12WhenTls13IsSupported,
BadCertChainExtensions,
DisallowedEncryptedExtension,
DuplicateClientHelloExtensions,
DuplicateEncryptedExtensions,
DuplicateHelloRetryRequestExtensions,
DuplicateNewSessionTicketExtensions,
DuplicateServerHelloExtensions,
DuplicateServerNameTypes,
EarlyDataAttemptedInSecondClientHello,
EarlyDataExtensionWithoutResumption,
EarlyDataOfferedWithVariedCipherSuite,
HandshakeHashVariedAfterRetry,
IllegalHelloRetryRequestWithEmptyCookie,
IllegalHelloRetryRequestWithNoChanges,
IllegalHelloRetryRequestWithOfferedGroup,
IllegalHelloRetryRequestWithUnofferedCipherSuite,
IllegalHelloRetryRequestWithUnofferedNamedGroup,
IllegalHelloRetryRequestWithUnsupportedVersion,
IllegalHelloRetryRequestWithWrongSessionId,
IllegalMiddleboxChangeCipherSpec,
IllegalTlsInnerPlaintext,
IncorrectBinder,
InvalidMaxEarlyDataSize,
InvalidKeyShare,
KeyEpochWithPendingFragment,
KeyUpdateReceivedInQuicConnection,
MessageInterleavedWithHandshakeMessage,
MissingBinderInPskExtension,
MissingKeyShare,
MissingPskModesExtension,
MissingQuicTransportParameters,
OfferedDuplicateKeyShares,
OfferedEarlyDataWithOldProtocolVersion,
OfferedEmptyApplicationProtocol,
OfferedIncorrectCompressions,
PskExtensionMustBeLast,
PskExtensionWithMismatchedIdsAndBinders,
RefusedToFollowHelloRetryRequest,
RejectedEarlyDataInterleavedWithHandshakeMessage,
ResumptionAttemptedWithVariedEms,
ResumptionOfferedWithVariedCipherSuite,
ResumptionOfferedWithVariedEms,
ResumptionOfferedWithIncompatibleCipherSuite,
SelectedDifferentCipherSuiteAfterRetry,
SelectedInvalidPsk,
SelectedTls12UsingTls13VersionExtension,
SelectedUnofferedApplicationProtocol,
SelectedUnofferedCipherSuite,
SelectedUnofferedCompression,
SelectedUnofferedKxGroup,
SelectedUnofferedPsk,
SelectedUnusableCipherSuiteForVersion,
ServerHelloMustOfferUncompressedEcPoints,
ServerNameDifferedOnRetry,
ServerNameMustContainOneHostName,
SignedKxWithWrongAlgorithm,
SignedHandshakeWithUnadvertisedSigScheme,
TooMuchEarlyDataReceived,
UnexpectedCleartextExtension,
UnsolicitedCertExtension,
UnsolicitedEncryptedExtension,
UnsolicitedSctList,
UnsolicitedServerHelloExtension,
WrongGroupForKeyShare,
}
impl From<PeerMisbehaved> for Error {
#[inline]
fn from(e: PeerMisbehaved) -> Self {
Self::PeerMisbehaved(e)
}
}
#[non_exhaustive]
#[allow(missing_docs)]
#[derive(Debug, PartialEq, Clone)]
/// The set of cases where we failed to make a connection because a peer
/// doesn't support a TLS version/feature we require.
///
/// This is `non_exhaustive`: we might add or stop using items here in minor
/// versions.
pub enum PeerIncompatible {
EcPointsExtensionRequired,
ExtendedMasterSecretExtensionRequired,
KeyShareExtensionRequired,
NamedGroupsExtensionRequired,
NoCertificateRequestSignatureSchemesInCommon,
NoCipherSuitesInCommon,
NoEcPointFormatsInCommon,
NoKxGroupsInCommon,
NoSignatureSchemesInCommon,
NullCompressionRequired,
ServerDoesNotSupportTls12Or13,
ServerSentHelloRetryRequestWithUnknownExtension,
ServerTlsVersionIsDisabledByOurConfig,
SignatureAlgorithmsExtensionRequired,
SupportedVersionsExtensionRequired,
Tls12NotOffered,
Tls12NotOfferedOrEnabled,
Tls13RequiredForQuic,
UncompressedEcPointsRequired,
}
impl From<PeerIncompatible> for Error {
#[inline]
fn from(e: PeerIncompatible) -> Self {
Self::PeerIncompatible(e)
}
}
#[non_exhaustive]
#[derive(Debug, Clone)]
/// The ways in which certificate validators can express errors.
///
/// Note that the rustls TLS protocol code interprets specifically these
/// error codes to send specific TLS alerts. Therefore, if a
/// custom certificate validator uses incorrect errors the library as
/// a whole will send alerts that do not match the standard (this is usually
/// a minor issue, but could be misleading).
pub enum CertificateError {
/// The certificate is not correctly encoded.
BadEncoding,
/// The current time is after the `notAfter` time in the certificate.
Expired,
/// The current time is before the `notBefore` time in the certificate.
NotValidYet,
/// The certificate has been revoked.
Revoked,
/// The certificate contains an extension marked critical, but it was
/// not processed by the certificate validator.
UnhandledCriticalExtension,
/// The certificate chain is not issued by a known root certificate.
UnknownIssuer,
/// The certificate's revocation status could not be determined.
UnknownRevocationStatus,
/// A certificate is not correctly signed by the key of its alleged
/// issuer.
BadSignature,
/// The subject names in an end-entity certificate do not include
/// the expected name.
NotValidForName,
/// The certificate is being used for a different purpose than allowed.
InvalidPurpose,
/// The certificate is valid, but the handshake is rejected for other
/// reasons.
ApplicationVerificationFailure,
/// Any other error.
///
/// This can be used by custom verifiers to expose the underlying error
/// (where they are not better described by the more specific errors
/// above).
///
/// It is also used by the default verifier in case its error is
/// not covered by the above common cases.
///
/// Enums holding this variant will never compare equal to each other.
Other(OtherError),
}
impl PartialEq<Self> for CertificateError {
fn eq(&self, other: &Self) -> bool {
use CertificateError::*;
#[allow(clippy::match_like_matches_macro)]
match (self, other) {
(BadEncoding, BadEncoding) => true,
(Expired, Expired) => true,
(NotValidYet, NotValidYet) => true,
(Revoked, Revoked) => true,
(UnhandledCriticalExtension, UnhandledCriticalExtension) => true,
(UnknownIssuer, UnknownIssuer) => true,
(BadSignature, BadSignature) => true,
(NotValidForName, NotValidForName) => true,
(InvalidPurpose, InvalidPurpose) => true,
(ApplicationVerificationFailure, ApplicationVerificationFailure) => true,
_ => false,
}
}
}
// The following mapping are heavily referenced in:
// * [OpenSSL Implementation](https://github.com/openssl/openssl/blob/45bb98bfa223efd3258f445ad443f878011450f0/ssl/statem/statem_lib.c#L1434)
// * [BoringSSL Implementation](https://github.com/google/boringssl/blob/583c60bd4bf76d61b2634a58bcda99a92de106cb/ssl/ssl_x509.cc#L1323)
impl From<CertificateError> for AlertDescription {
fn from(e: CertificateError) -> Self {
use CertificateError::*;
match e {
BadEncoding | UnhandledCriticalExtension | NotValidForName => Self::BadCertificate,
// RFC 5246/RFC 8446
// certificate_expired
// A certificate has expired or **is not currently valid**.
Expired | NotValidYet => Self::CertificateExpired,
Revoked => Self::CertificateRevoked,
// OpenSSL, BoringSSL and AWS-LC all generate an Unknown CA alert for
// the case where revocation status can not be determined, so we do the same here.
UnknownIssuer | UnknownRevocationStatus => Self::UnknownCA,
BadSignature => Self::DecryptError,
InvalidPurpose => Self::UnsupportedCertificate,
ApplicationVerificationFailure => Self::AccessDenied,
// RFC 5246/RFC 8446
// certificate_unknown
// Some other (unspecified) issue arose in processing the
// certificate, rendering it unacceptable.
Other(..) => Self::CertificateUnknown,
}
}
}
impl From<CertificateError> for Error {
#[inline]
fn from(e: CertificateError) -> Self {
Self::InvalidCertificate(e)
}
}
#[non_exhaustive]
#[derive(Debug, Clone)]
/// The ways in which a certificate revocation list (CRL) can be invalid.
pub enum CertRevocationListError {
/// The CRL had a bad, or unsupported signature from its issuer.
BadSignature,
/// The CRL contained an invalid CRL number.
InvalidCrlNumber,
/// The CRL contained a revoked certificate with an invalid serial number.
InvalidRevokedCertSerialNumber,
/// The CRL issuer does not specify the cRLSign key usage.
IssuerInvalidForCrl,
/// The CRL is invalid for some other reason.
///
/// Enums holding this variant will never compare equal to each other.
Other(OtherError),
/// The CRL is not correctly encoded.
ParseError,
/// The CRL is not a v2 X.509 CRL.
UnsupportedCrlVersion,
/// The CRL, or a revoked certificate in the CRL, contained an unsupported critical extension.
UnsupportedCriticalExtension,
/// The CRL is an unsupported delta CRL, containing only changes relative to another CRL.
UnsupportedDeltaCrl,
/// The CRL is an unsupported indirect CRL, containing revoked certificates issued by a CA
/// other than the issuer of the CRL.
UnsupportedIndirectCrl,
/// The CRL contained a revoked certificate with an unsupported revocation reason.
/// See RFC 5280 Section 5.3.1[^1] for a list of supported revocation reasons.
///
/// [^1]: <https://www.rfc-editor.org/rfc/rfc5280#section-5.3.1>
UnsupportedRevocationReason,
}
impl PartialEq<Self> for CertRevocationListError {
fn eq(&self, other: &Self) -> bool {
use CertRevocationListError::*;
#[allow(clippy::match_like_matches_macro)]
match (self, other) {
(BadSignature, BadSignature) => true,
(InvalidCrlNumber, InvalidCrlNumber) => true,
(InvalidRevokedCertSerialNumber, InvalidRevokedCertSerialNumber) => true,
(IssuerInvalidForCrl, IssuerInvalidForCrl) => true,
(ParseError, ParseError) => true,
(UnsupportedCrlVersion, UnsupportedCrlVersion) => true,
(UnsupportedCriticalExtension, UnsupportedCriticalExtension) => true,
(UnsupportedDeltaCrl, UnsupportedDeltaCrl) => true,
(UnsupportedIndirectCrl, UnsupportedIndirectCrl) => true,
(UnsupportedRevocationReason, UnsupportedRevocationReason) => true,
_ => false,
}
}
}
impl From<CertRevocationListError> for Error {
#[inline]
fn from(e: CertRevocationListError) -> Self {
Self::InvalidCertRevocationList(e)
}
}
fn join<T: fmt::Debug>(items: &[T]) -> String {
items
.iter()
.map(|x| format!("{:?}", x))
.collect::<Vec<String>>()
.join(" or ")
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::InappropriateMessage {
ref expect_types,
ref got_type,
} => write!(
f,
"received unexpected message: got {:?} when expecting {}",
got_type,
join::<ContentType>(expect_types)
),
Self::InappropriateHandshakeMessage {
ref expect_types,
ref got_type,
} => write!(
f,
"received unexpected handshake message: got {:?} when expecting {}",
got_type,
join::<HandshakeType>(expect_types)
),
Self::InvalidMessage(ref typ) => {
write!(f, "received corrupt message of type {:?}", typ)
}
Self::PeerIncompatible(ref why) => write!(f, "peer is incompatible: {:?}", why),
Self::PeerMisbehaved(ref why) => write!(f, "peer misbehaved: {:?}", why),
Self::AlertReceived(ref alert) => write!(f, "received fatal alert: {:?}", alert),
Self::InvalidCertificate(ref err) => {
write!(f, "invalid peer certificate: {:?}", err)
}
Self::InvalidCertRevocationList(ref err) => {
write!(f, "invalid certificate revocation list: {:?}", err)
}
Self::NoCertificatesPresented => write!(f, "peer sent no certificates"),
Self::UnsupportedNameType => write!(f, "presented server name type wasn't supported"),
Self::DecryptError => write!(f, "cannot decrypt peer's message"),
Self::EncryptError => write!(f, "cannot encrypt message"),
Self::PeerSentOversizedRecord => write!(f, "peer sent excess record size"),
Self::HandshakeNotComplete => write!(f, "handshake not complete"),
Self::NoApplicationProtocol => write!(f, "peer doesn't support any known protocol"),
Self::FailedToGetCurrentTime => write!(f, "failed to get current time"),
Self::FailedToGetRandomBytes => write!(f, "failed to get random bytes"),
Self::BadMaxFragmentSize => {
write!(f, "the supplied max_fragment_size was too small or large")
}
Self::General(ref err) => write!(f, "unexpected error: {}", err),
Self::Other(ref err) => write!(f, "other error: {}", err),
}
}
}
#[cfg(feature = "std")]
impl From<SystemTimeError> for Error {
#[inline]
fn from(_: SystemTimeError) -> Self {
Self::FailedToGetCurrentTime
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
impl From<rand::GetRandomFailed> for Error {
fn from(_: rand::GetRandomFailed) -> Self {
Self::FailedToGetRandomBytes
}
}
mod other_error {
#[cfg(feature = "std")]
use alloc::sync::Arc;
use core::fmt;
#[cfg(feature = "std")]
use std::error::Error as StdError;
use super::Error;
/// Any other error that cannot be expressed by a more specific [`Error`] variant.
///
/// For example, an `OtherError` could be produced by a custom crypto provider
/// exposing a provider specific error.
///
/// Enums holding this type will never compare equal to each other.
#[derive(Debug, Clone)]
pub struct OtherError(#[cfg(feature = "std")] pub Arc<dyn StdError + Send + Sync>);
impl PartialEq<Self> for OtherError {
fn eq(&self, _other: &Self) -> bool {
false
}
}
impl From<OtherError> for Error {
fn from(value: OtherError) -> Self {
Self::Other(value)
}
}
impl fmt::Display for OtherError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
#[cfg(feature = "std")]
{
write!(f, "{}", self.0)
}
#[cfg(not(feature = "std"))]
{
f.write_str("no further information available")
}
}
}
#[cfg(feature = "std")]
impl StdError for OtherError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
Some(self.0.as_ref())
}
}
}
pub use other_error::OtherError;
#[cfg(test)]
mod tests {
use std::prelude::v1::*;
use std::{println, vec};
use super::{Error, InvalidMessage};
use crate::error::{CertRevocationListError, OtherError};
#[test]
fn certificate_error_equality() {
use super::CertificateError::*;
assert_eq!(BadEncoding, BadEncoding);
assert_eq!(Expired, Expired);
assert_eq!(NotValidYet, NotValidYet);
assert_eq!(Revoked, Revoked);
assert_eq!(UnhandledCriticalExtension, UnhandledCriticalExtension);
assert_eq!(UnknownIssuer, UnknownIssuer);
assert_eq!(BadSignature, BadSignature);
assert_eq!(NotValidForName, NotValidForName);
assert_eq!(InvalidPurpose, InvalidPurpose);
assert_eq!(
ApplicationVerificationFailure,
ApplicationVerificationFailure
);
let other = Other(OtherError(
#[cfg(feature = "std")]
alloc::sync::Arc::from(Box::from("")),
));
assert_ne!(other, other);
assert_ne!(BadEncoding, Expired);
}
#[test]
fn crl_error_equality() {
use super::CertRevocationListError::*;
assert_eq!(BadSignature, BadSignature);
assert_eq!(InvalidCrlNumber, InvalidCrlNumber);
assert_eq!(
InvalidRevokedCertSerialNumber,
InvalidRevokedCertSerialNumber
);
assert_eq!(IssuerInvalidForCrl, IssuerInvalidForCrl);
assert_eq!(ParseError, ParseError);
assert_eq!(UnsupportedCriticalExtension, UnsupportedCriticalExtension);
assert_eq!(UnsupportedCrlVersion, UnsupportedCrlVersion);
assert_eq!(UnsupportedDeltaCrl, UnsupportedDeltaCrl);
assert_eq!(UnsupportedIndirectCrl, UnsupportedIndirectCrl);
assert_eq!(UnsupportedRevocationReason, UnsupportedRevocationReason);
let other = Other(OtherError(
#[cfg(feature = "std")]
alloc::sync::Arc::from(Box::from("")),
));
assert_ne!(other, other);
assert_ne!(BadSignature, InvalidCrlNumber);
}
#[test]
#[cfg(feature = "std")]
fn other_error_equality() {
let other_error = OtherError(alloc::sync::Arc::from(Box::from("")));
assert_ne!(other_error, other_error);
let other: Error = other_error.into();
assert_ne!(other, other);
}
#[test]
fn smoke() {
use crate::enums::{AlertDescription, ContentType, HandshakeType};
let all = vec![
Error::InappropriateMessage {
expect_types: vec![ContentType::Alert],
got_type: ContentType::Handshake,
},
Error::InappropriateHandshakeMessage {
expect_types: vec![HandshakeType::ClientHello, HandshakeType::Finished],
got_type: HandshakeType::ServerHello,
},
Error::InvalidMessage(InvalidMessage::InvalidCcs),
Error::NoCertificatesPresented,
Error::DecryptError,
super::PeerIncompatible::Tls12NotOffered.into(),
super::PeerMisbehaved::UnsolicitedCertExtension.into(),
Error::AlertReceived(AlertDescription::ExportRestriction),
super::CertificateError::Expired.into(),
Error::General("undocumented error".to_string()),
Error::FailedToGetCurrentTime,
Error::FailedToGetRandomBytes,
Error::HandshakeNotComplete,
Error::PeerSentOversizedRecord,
Error::NoApplicationProtocol,
Error::BadMaxFragmentSize,
Error::InvalidCertRevocationList(CertRevocationListError::BadSignature),
Error::Other(OtherError(
#[cfg(feature = "std")]
alloc::sync::Arc::from(Box::from("")),
)),
];
for err in all {
println!("{:?}:", err);
println!(" fmt '{}'", err);
}
}
#[test]
fn rand_error_mapping() {
use super::rand;
let err: Error = rand::GetRandomFailed.into();
assert_eq!(err, Error::FailedToGetRandomBytes);
}
#[cfg(feature = "std")]
#[test]
fn time_error_mapping() {
use std::time::SystemTime;
let time_error = SystemTime::UNIX_EPOCH
.duration_since(SystemTime::now())
.unwrap_err();
let err: Error = time_error.into();
assert_eq!(err, Error::FailedToGetCurrentTime);
}
}