use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
use core::ops::Deref;
use core::{fmt, mem};
use std::time::SystemTimeError;
use pki_types::{AlgorithmIdentifier, EchConfigListBytes, ServerName, UnixTime};
#[cfg(feature = "webpki")]
use webpki::ExtendedKeyUsage;
use crate::common_state::maybe_send_fatal_alert;
use crate::conn::SendPath;
use crate::crypto::kx::KeyExchangeAlgorithm;
use crate::crypto::{CipherSuite, GetRandomFailed, InconsistentKeys};
use crate::enums::{ContentType, HandshakeType};
use crate::msgs::{Codec, EchConfigPayload};
#[cfg(test)]
mod tests;
#[non_exhaustive]
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
InappropriateMessage {
expect_types: Vec<ContentType>,
got_type: ContentType,
},
InappropriateHandshakeMessage {
expect_types: Vec<HandshakeType>,
got_type: HandshakeType,
},
InvalidEncryptedClientHello(EncryptedClientHelloError),
InvalidMessage(InvalidMessage),
UnsupportedNameType,
DecryptError,
EncryptError,
PeerIncompatible(PeerIncompatible),
PeerMisbehaved(PeerMisbehaved),
AlertReceived(AlertDescription),
InvalidCertificate(CertificateError),
InvalidCertRevocationList(CertRevocationListError),
General(String),
FailedToGetCurrentTime,
FailedToGetRandomBytes,
HandshakeNotComplete,
PeerSentOversizedRecord,
NoApplicationProtocol,
NoSuitableCertificate,
BadMaxFragmentSize,
InconsistentKeys(InconsistentKeys),
RejectedEch(RejectedEch),
Unreachable(&'static str),
ApiMisuse(ApiMisuse),
Other(OtherError),
}
impl TryFrom<&Error> for AlertDescription {
type Error = ();
fn try_from(error: &Error) -> Result<Self, Self::Error> {
Ok(match error {
Error::DecryptError => Self::BadRecordMac,
Error::InappropriateMessage { .. } | Error::InappropriateHandshakeMessage { .. } => {
Self::UnexpectedMessage
}
Error::InvalidCertificate(e) => Self::from(e),
Error::InvalidMessage(e) => Self::from(*e),
Error::NoApplicationProtocol => Self::NoApplicationProtocol,
Error::PeerMisbehaved(e) => Self::from(*e),
Error::PeerIncompatible(e) => Self::from(*e),
Error::PeerSentOversizedRecord => Self::RecordOverflow,
Error::RejectedEch(_) => Self::EncryptedClientHelloRequired,
_ => return Err(()),
})
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InappropriateMessage {
expect_types,
got_type,
} => write!(
f,
"received unexpected message: got {:?} when expecting {}",
got_type,
join::<ContentType>(expect_types)
),
Self::InappropriateHandshakeMessage {
expect_types,
got_type,
} => write!(
f,
"received unexpected handshake message: got {:?} when expecting {}",
got_type,
join::<HandshakeType>(expect_types)
),
Self::InvalidMessage(typ) => {
write!(f, "received corrupt message of type {typ:?}")
}
Self::PeerIncompatible(why) => write!(f, "peer is incompatible: {why:?}"),
Self::PeerMisbehaved(why) => write!(f, "peer misbehaved: {why:?}"),
Self::AlertReceived(alert) => write!(f, "received fatal alert: the peer {alert}"),
Self::InvalidCertificate(err) => {
write!(f, "invalid peer certificate: {err}")
}
Self::InvalidCertRevocationList(err) => {
write!(f, "invalid certificate revocation list: {err:?}")
}
Self::UnsupportedNameType => write!(f, "presented server name type wasn't supported"),
Self::DecryptError => write!(f, "cannot decrypt peer's message"),
Self::InvalidEncryptedClientHello(err) => {
write!(f, "encrypted client hello failure: {err:?}")
}
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::NoSuitableCertificate => write!(f, "no suitable certificate found"),
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::InconsistentKeys(why) => {
write!(f, "keys may not be consistent: {why:?}")
}
Self::RejectedEch(why) => {
write!(
f,
"server rejected encrypted client hello (ECH) {} retry configs",
if why.can_retry() { "with" } else { "without" }
)
}
Self::General(err) => write!(f, "unexpected error: {err}"),
Self::Unreachable(err) => write!(
f,
"unreachable condition: {err} (please file a bug in rustls)"
),
Self::ApiMisuse(why) => write!(f, "API misuse: {why:?}"),
Self::Other(err) => write!(f, "other error: {err}"),
}
}
}
impl From<CertificateError> for Error {
#[inline]
fn from(e: CertificateError) -> Self {
Self::InvalidCertificate(e)
}
}
impl From<InvalidMessage> for Error {
#[inline]
fn from(e: InvalidMessage) -> Self {
Self::InvalidMessage(e)
}
}
impl From<PeerMisbehaved> for Error {
#[inline]
fn from(e: PeerMisbehaved) -> Self {
Self::PeerMisbehaved(e)
}
}
impl From<PeerIncompatible> for Error {
#[inline]
fn from(e: PeerIncompatible) -> Self {
Self::PeerIncompatible(e)
}
}
impl From<CertRevocationListError> for Error {
#[inline]
fn from(e: CertRevocationListError) -> Self {
Self::InvalidCertRevocationList(e)
}
}
impl From<EncryptedClientHelloError> for Error {
#[inline]
fn from(e: EncryptedClientHelloError) -> Self {
Self::InvalidEncryptedClientHello(e)
}
}
impl From<RejectedEch> for Error {
fn from(rejected_error: RejectedEch) -> Self {
Self::RejectedEch(rejected_error)
}
}
impl From<ApiMisuse> for Error {
fn from(e: ApiMisuse) -> Self {
Self::ApiMisuse(e)
}
}
impl From<OtherError> for Error {
fn from(value: OtherError) -> Self {
Self::Other(value)
}
}
impl From<InconsistentKeys> for Error {
#[inline]
fn from(e: InconsistentKeys) -> Self {
Self::InconsistentKeys(e)
}
}
impl From<SystemTimeError> for Error {
#[inline]
fn from(_: SystemTimeError) -> Self {
Self::FailedToGetCurrentTime
}
}
impl From<GetRandomFailed> for Error {
fn from(_: GetRandomFailed) -> Self {
Self::FailedToGetRandomBytes
}
}
impl core::error::Error for Error {}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum CertificateError {
BadEncoding,
Expired,
ExpiredContext {
time: UnixTime,
not_after: UnixTime,
},
NotValidYet,
NotValidYetContext {
time: UnixTime,
not_before: UnixTime,
},
Revoked,
UnhandledCriticalExtension,
UnknownIssuer,
UnknownRevocationStatus,
ExpiredRevocationList,
ExpiredRevocationListContext {
time: UnixTime,
next_update: UnixTime,
},
BadSignature,
UnsupportedSignatureAlgorithm {
signature_algorithm_id: Vec<u8>,
supported_algorithms: Vec<AlgorithmIdentifier>,
},
UnsupportedSignatureAlgorithmForPublicKey {
signature_algorithm_id: Vec<u8>,
public_key_algorithm_id: Vec<u8>,
},
NotValidForName,
NotValidForNameContext {
expected: ServerName<'static>,
presented: Vec<String>,
},
InvalidPurpose,
InvalidPurposeContext {
required: ExtendedKeyPurpose,
presented: Vec<ExtendedKeyPurpose>,
},
InvalidOcspResponse,
ApplicationVerificationFailure,
Other(OtherError),
}
impl PartialEq<Self> for CertificateError {
fn eq(&self, other: &Self) -> bool {
use CertificateError::*;
match (self, other) {
(BadEncoding, BadEncoding) => true,
(Expired, Expired) => true,
(
ExpiredContext {
time: left_time,
not_after: left_not_after,
},
ExpiredContext {
time: right_time,
not_after: right_not_after,
},
) => (left_time, left_not_after) == (right_time, right_not_after),
(NotValidYet, NotValidYet) => true,
(
NotValidYetContext {
time: left_time,
not_before: left_not_before,
},
NotValidYetContext {
time: right_time,
not_before: right_not_before,
},
) => (left_time, left_not_before) == (right_time, right_not_before),
(Revoked, Revoked) => true,
(UnhandledCriticalExtension, UnhandledCriticalExtension) => true,
(UnknownIssuer, UnknownIssuer) => true,
(BadSignature, BadSignature) => true,
(
UnsupportedSignatureAlgorithm {
signature_algorithm_id: left_signature_algorithm_id,
supported_algorithms: left_supported_algorithms,
},
UnsupportedSignatureAlgorithm {
signature_algorithm_id: right_signature_algorithm_id,
supported_algorithms: right_supported_algorithms,
},
) => {
(left_signature_algorithm_id, left_supported_algorithms)
== (right_signature_algorithm_id, right_supported_algorithms)
}
(
UnsupportedSignatureAlgorithmForPublicKey {
signature_algorithm_id: left_signature_algorithm_id,
public_key_algorithm_id: left_public_key_algorithm_id,
},
UnsupportedSignatureAlgorithmForPublicKey {
signature_algorithm_id: right_signature_algorithm_id,
public_key_algorithm_id: right_public_key_algorithm_id,
},
) => {
(left_signature_algorithm_id, left_public_key_algorithm_id)
== (right_signature_algorithm_id, right_public_key_algorithm_id)
}
(NotValidForName, NotValidForName) => true,
(
NotValidForNameContext {
expected: left_expected,
presented: left_presented,
},
NotValidForNameContext {
expected: right_expected,
presented: right_presented,
},
) => (left_expected, left_presented) == (right_expected, right_presented),
(InvalidPurpose, InvalidPurpose) => true,
(
InvalidPurposeContext {
required: left_required,
presented: left_presented,
},
InvalidPurposeContext {
required: right_required,
presented: right_presented,
},
) => (left_required, left_presented) == (right_required, right_presented),
(InvalidOcspResponse, InvalidOcspResponse) => true,
(ApplicationVerificationFailure, ApplicationVerificationFailure) => true,
(UnknownRevocationStatus, UnknownRevocationStatus) => true,
(ExpiredRevocationList, ExpiredRevocationList) => true,
(
ExpiredRevocationListContext {
time: left_time,
next_update: left_next_update,
},
ExpiredRevocationListContext {
time: right_time,
next_update: right_next_update,
},
) => (left_time, left_next_update) == (right_time, right_next_update),
_ => false,
}
}
}
impl From<&CertificateError> for AlertDescription {
fn from(e: &CertificateError) -> Self {
use CertificateError::*;
match e {
BadEncoding
| UnhandledCriticalExtension
| NotValidForName
| NotValidForNameContext { .. } => Self::BadCertificate,
Expired | ExpiredContext { .. } | NotValidYet | NotValidYetContext { .. } => {
Self::CertificateExpired
}
Revoked => Self::CertificateRevoked,
UnknownIssuer
| UnknownRevocationStatus
| ExpiredRevocationList
| ExpiredRevocationListContext { .. } => Self::UnknownCa,
InvalidOcspResponse => Self::BadCertificateStatusResponse,
BadSignature
| UnsupportedSignatureAlgorithm { .. }
| UnsupportedSignatureAlgorithmForPublicKey { .. } => Self::DecryptError,
InvalidPurpose | InvalidPurposeContext { .. } => Self::UnsupportedCertificate,
ApplicationVerificationFailure => Self::AccessDenied,
Other(..) => Self::CertificateUnknown,
}
}
}
impl fmt::Display for CertificateError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NotValidForNameContext {
expected,
presented,
} => {
write!(
f,
"certificate not valid for name {:?}; certificate ",
expected.to_str()
)?;
match presented.as_slice() {
&[] => write!(
f,
"is not valid for any names (according to its subjectAltName extension)"
),
[one] => write!(f, "is only valid for {one}"),
many => {
write!(f, "is only valid for ")?;
let n = many.len();
let all_but_last = &many[..n - 1];
let last = &many[n - 1];
for (i, name) in all_but_last.iter().enumerate() {
write!(f, "{name}")?;
if i < n - 2 {
write!(f, ", ")?;
}
}
write!(f, " or {last}")
}
}
}
Self::ExpiredContext { time, not_after } => write!(
f,
"certificate expired: verification time {} (UNIX), \
but certificate is not valid after {} \
({} seconds ago)",
time.as_secs(),
not_after.as_secs(),
time.as_secs()
.saturating_sub(not_after.as_secs())
),
Self::NotValidYetContext { time, not_before } => write!(
f,
"certificate not valid yet: verification time {} (UNIX), \
but certificate is not valid before {} \
({} seconds in future)",
time.as_secs(),
not_before.as_secs(),
not_before
.as_secs()
.saturating_sub(time.as_secs())
),
Self::ExpiredRevocationListContext { time, next_update } => write!(
f,
"certificate revocation list expired: \
verification time {} (UNIX), \
but CRL is not valid after {} \
({} seconds ago)",
time.as_secs(),
next_update.as_secs(),
time.as_secs()
.saturating_sub(next_update.as_secs())
),
Self::InvalidPurposeContext {
required,
presented,
} => {
write!(
f,
"certificate does not allow extended key usage for {required}, allows "
)?;
for (i, eku) in presented.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{eku}")?;
}
Ok(())
}
Self::Other(other) => write!(f, "{other}"),
other => write!(f, "{other:?}"),
}
}
}
enum_builder! {
pub struct AlertDescription(pub u8);
enum AlertDescriptionName {
CloseNotify => 0x00,
UnexpectedMessage => 0x0a,
BadRecordMac => 0x14,
DecryptionFailed => 0x15,
RecordOverflow => 0x16,
DecompressionFailure => 0x1e,
HandshakeFailure => 0x28,
NoCertificate => 0x29,
BadCertificate => 0x2a,
UnsupportedCertificate => 0x2b,
CertificateRevoked => 0x2c,
CertificateExpired => 0x2d,
CertificateUnknown => 0x2e,
IllegalParameter => 0x2f,
UnknownCa => 0x30,
AccessDenied => 0x31,
DecodeError => 0x32,
DecryptError => 0x33,
ExportRestriction => 0x3c,
ProtocolVersion => 0x46,
InsufficientSecurity => 0x47,
InternalError => 0x50,
InappropriateFallback => 0x56,
UserCanceled => 0x5a,
NoRenegotiation => 0x64,
MissingExtension => 0x6d,
UnsupportedExtension => 0x6e,
CertificateUnobtainable => 0x6f,
UnrecognizedName => 0x70,
BadCertificateStatusResponse => 0x71,
BadCertificateHashValue => 0x72,
UnknownPskIdentity => 0x73,
CertificateRequired => 0x74,
NoApplicationProtocol => 0x78,
EncryptedClientHelloRequired => 0x79,
}
}
impl fmt::Display for AlertDescription {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Ok(known) = AlertDescriptionName::try_from(*self) else {
return write!(f, "sent an unknown alert (0x{:02x?})", self.0);
};
match known {
AlertDescriptionName::CloseNotify => write!(f, "cleanly closed the connection"),
AlertDescriptionName::UnexpectedMessage => write!(f, "received an unexpected message"),
AlertDescriptionName::BadRecordMac => write!(f, "failed to verify a message"),
AlertDescriptionName::RecordOverflow => write!(f, "rejected an over-length message"),
AlertDescriptionName::IllegalParameter => write!(
f,
"rejected a message because a field was incorrect or inconsistent"
),
AlertDescriptionName::DecodeError => write!(f, "failed to decode a message"),
AlertDescriptionName::DecryptError => {
write!(f, "failed to perform a handshake cryptographic operation")
}
AlertDescriptionName::InappropriateFallback => {
write!(f, "detected an attempted version downgrade")
}
AlertDescriptionName::MissingExtension => {
write!(f, "required a specific extension that was not provided")
}
AlertDescriptionName::UnsupportedExtension => {
write!(f, "rejected an unsolicited extension")
}
AlertDescriptionName::DecryptionFailed => write!(f, "failed to decrypt a message"),
AlertDescriptionName::DecompressionFailure => {
write!(f, "failed to decompress a message")
}
AlertDescriptionName::NoCertificate => write!(f, "found no certificate"),
AlertDescriptionName::ExportRestriction => {
write!(f, "refused due to export restrictions")
}
AlertDescriptionName::NoRenegotiation => {
write!(f, "rejected an attempt at renegotiation")
}
AlertDescriptionName::CertificateUnobtainable => {
write!(f, "failed to retrieve its certificate")
}
AlertDescriptionName::BadCertificateHashValue => {
write!(f, "rejected the `certificate_hash` extension")
}
AlertDescriptionName::HandshakeFailure => write!(
f,
"failed to negotiate an acceptable set of security parameters"
),
AlertDescriptionName::ProtocolVersion => {
write!(f, "did not support a suitable TLS version")
}
AlertDescriptionName::InsufficientSecurity => {
write!(f, "required a higher security level than was offered")
}
AlertDescriptionName::BadCertificate => {
write!(
f,
"rejected the certificate as corrupt or incorrectly signed"
)
}
AlertDescriptionName::UnsupportedCertificate => {
write!(f, "did not support the certificate")
}
AlertDescriptionName::CertificateRevoked => {
write!(f, "found the certificate to be revoked")
}
AlertDescriptionName::CertificateExpired => {
write!(f, "found the certificate to be expired")
}
AlertDescriptionName::CertificateUnknown => {
write!(f, "rejected the certificate for an unspecified reason")
}
AlertDescriptionName::UnknownCa => {
write!(f, "found the certificate was not issued by a trusted CA")
}
AlertDescriptionName::BadCertificateStatusResponse => {
write!(f, "rejected the certificate status response")
}
AlertDescriptionName::AccessDenied => write!(f, "denied access"),
AlertDescriptionName::CertificateRequired => {
write!(f, "required a client certificate")
}
AlertDescriptionName::InternalError => write!(f, "encountered an internal error"),
AlertDescriptionName::UserCanceled => write!(f, "canceled the handshake"),
AlertDescriptionName::UnrecognizedName => {
write!(f, "did not recognize a name in the `server_name` extension")
}
AlertDescriptionName::UnknownPskIdentity => {
write!(f, "did not recognize any offered PSK identity")
}
AlertDescriptionName::NoApplicationProtocol => write!(
f,
"did not support any of the offered application protocols"
),
AlertDescriptionName::EncryptedClientHelloRequired => {
write!(f, "required use of encrypted client hello")
}
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum InvalidMessage {
CertificatePayloadTooLarge,
HandshakePayloadTooLarge,
InvalidCcs,
InvalidContentType,
InvalidCertificateStatusType,
InvalidCertRequest,
InvalidDhParams,
InvalidEmptyPayload,
InvalidKeyUpdate,
InvalidServerName,
MessageTooLarge,
MessageTooShort,
MissingData(&'static str),
MissingKeyExchange,
NoSignatureSchemes,
TrailingData(&'static str),
UnexpectedMessage(&'static str),
UnknownProtocolVersion,
UnsupportedCompression,
UnsupportedCurveType,
UnsupportedKeyExchangeAlgorithm(KeyExchangeAlgorithm),
EmptyTicketValue,
IllegalEmptyList(&'static str),
DuplicateExtension(u16),
PreSharedKeyIsNotFinalExtension,
UnknownHelloRetryRequestExtension,
UnknownCertificateExtension,
}
impl From<InvalidMessage> for AlertDescription {
fn from(e: InvalidMessage) -> Self {
match e {
InvalidMessage::PreSharedKeyIsNotFinalExtension => Self::IllegalParameter,
InvalidMessage::DuplicateExtension(_) => Self::IllegalParameter,
InvalidMessage::UnknownHelloRetryRequestExtension => Self::UnsupportedExtension,
InvalidMessage::CertificatePayloadTooLarge => Self::BadCertificate,
_ => Self::DecodeError,
}
}
}
#[expect(missing_docs)]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PeerMisbehaved {
AttemptedDowngradeToTls12WhenTls13IsSupported,
BadCertChainExtensions,
DisallowedEncryptedExtension,
DuplicateClientHelloExtensions,
DuplicateEncryptedExtensions,
DuplicateHelloRetryRequestExtensions,
DuplicateNewSessionTicketExtensions,
DuplicateServerHelloExtensions,
DuplicateServerNameTypes,
EarlyDataAttemptedInSecondClientHello,
EarlyDataExtensionWithoutResumption,
EarlyDataOfferedWithVariedCipherSuite,
EmptyFragment,
HandshakeHashVariedAfterRetry,
IllegalAlertLevel(u8, AlertDescription),
IllegalHelloRetryRequestWithEmptyCookie,
IllegalHelloRetryRequestWithNoChanges,
IllegalHelloRetryRequestWithOfferedGroup,
IllegalHelloRetryRequestWithUnofferedCipherSuite,
IllegalHelloRetryRequestWithUnofferedNamedGroup,
IllegalHelloRetryRequestWithUnsupportedVersion,
IllegalHelloRetryRequestWithWrongSessionId,
IllegalHelloRetryRequestWithInvalidEch,
IllegalMiddleboxChangeCipherSpec,
IllegalTlsInnerPlaintext,
IllegalWarningAlert(AlertDescription),
IncorrectBinder,
IncorrectFinished,
InvalidCertCompression,
InvalidMaxEarlyDataSize,
InvalidKeyShare,
KeyEpochWithPendingFragment,
KeyUpdateReceivedInQuicConnection,
MessageInterleavedWithHandshakeMessage,
MissingBinderInPskExtension,
MissingKeyShare,
MissingPskModesExtension,
MissingQuicTransportParameters,
NoCertificatesPresented,
OfferedDuplicateCertificateCompressions,
OfferedDuplicateKeyShares,
OfferedEarlyDataWithOldProtocolVersion,
OfferedEmptyApplicationProtocol,
OfferedIncorrectCompressions,
PskExtensionMustBeLast,
PskExtensionWithMismatchedIdsAndBinders,
RefusedToFollowHelloRetryRequest,
RejectedEarlyDataInterleavedWithHandshakeMessage,
ResumptionAttemptedWithVariedEms,
ResumptionOfferedWithVariedCipherSuite,
ResumptionOfferedWithVariedEms,
ResumptionOfferedWithIncompatibleCipherSuite,
SelectedDifferentCipherSuiteAfterRetry,
SelectedInvalidPsk,
SelectedTls12UsingTls13VersionExtension,
SelectedUnofferedApplicationProtocol,
SelectedUnofferedCertCompression,
SelectedUnofferedCipherSuite,
SelectedUnofferedCompression,
SelectedUnofferedKxGroup,
SelectedUnofferedPsk,
ServerEchoedCompatibilitySessionId,
ServerHelloMustOfferUncompressedEcPoints,
ServerNameDifferedOnRetry,
ServerNameMustContainOneHostName,
SignedKxWithWrongAlgorithm,
SignedHandshakeWithUnadvertisedSigScheme,
TooManyEmptyFragments,
TooManyConsecutiveHandshakeMessagesAfterHandshake,
TooManyRenegotiationRequests,
TooManyWarningAlertsReceived,
TooMuchEarlyDataReceived,
UnexpectedCleartextExtension,
UnsolicitedCertExtension,
UnsolicitedEncryptedExtension,
UnsolicitedSctList,
UnsolicitedServerHelloExtension,
WrongGroupForKeyShare,
UnsolicitedEchExtension,
}
impl From<PeerMisbehaved> for AlertDescription {
fn from(e: PeerMisbehaved) -> Self {
match e {
PeerMisbehaved::DisallowedEncryptedExtension
| PeerMisbehaved::IllegalHelloRetryRequestWithInvalidEch
| PeerMisbehaved::UnexpectedCleartextExtension
| PeerMisbehaved::UnsolicitedEchExtension
| PeerMisbehaved::UnsolicitedEncryptedExtension
| PeerMisbehaved::UnsolicitedServerHelloExtension => Self::UnsupportedExtension,
PeerMisbehaved::IllegalMiddleboxChangeCipherSpec
| PeerMisbehaved::KeyEpochWithPendingFragment
| PeerMisbehaved::KeyUpdateReceivedInQuicConnection => Self::UnexpectedMessage,
PeerMisbehaved::IllegalWarningAlert(_) => Self::DecodeError,
PeerMisbehaved::IncorrectBinder | PeerMisbehaved::IncorrectFinished => {
Self::DecryptError
}
PeerMisbehaved::InvalidCertCompression
| PeerMisbehaved::SelectedUnofferedCertCompression => Self::BadCertificate,
PeerMisbehaved::MissingKeyShare
| PeerMisbehaved::MissingPskModesExtension
| PeerMisbehaved::MissingQuicTransportParameters => Self::MissingExtension,
PeerMisbehaved::NoCertificatesPresented => Self::CertificateRequired,
_ => Self::IllegalParameter,
}
}
}
#[expect(missing_docs)]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PeerIncompatible {
EcPointsExtensionRequired,
ExtendedMasterSecretExtensionRequired,
IncorrectCertificateTypeExtension,
KeyShareExtensionRequired,
MultipleRawKeys,
NamedGroupsExtensionRequired,
NoCertificateRequestSignatureSchemesInCommon,
NoCipherSuitesInCommon,
NoEcPointFormatsInCommon,
NoKxGroupsInCommon,
NoSignatureSchemesInCommon,
NoServerNameProvided,
NullCompressionRequired,
ServerDoesNotSupportTls12Or13,
ServerSentHelloRetryRequestWithUnknownExtension,
ServerTlsVersionIsDisabledByOurConfig,
SignatureAlgorithmsExtensionRequired,
SupportedVersionsExtensionRequired,
Tls12NotOffered,
Tls12NotOfferedOrEnabled,
Tls13RequiredForQuic,
UncompressedEcPointsRequired,
UnknownCertificateType(u8),
UnsolicitedCertificateTypeExtension,
}
impl From<PeerIncompatible> for AlertDescription {
fn from(e: PeerIncompatible) -> Self {
match e {
PeerIncompatible::NullCompressionRequired => Self::IllegalParameter,
PeerIncompatible::ServerTlsVersionIsDisabledByOurConfig
| PeerIncompatible::SupportedVersionsExtensionRequired
| PeerIncompatible::Tls12NotOffered
| PeerIncompatible::Tls12NotOfferedOrEnabled
| PeerIncompatible::Tls13RequiredForQuic => Self::ProtocolVersion,
PeerIncompatible::UnknownCertificateType(_) => Self::UnsupportedCertificate,
_ => Self::HandshakeFailure,
}
}
}
#[non_exhaustive]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ExtendedKeyPurpose {
ClientAuth,
ServerAuth,
Other(Vec<usize>),
}
impl ExtendedKeyPurpose {
#[cfg(feature = "webpki")]
pub(crate) fn for_values(values: impl Iterator<Item = usize>) -> Self {
let values = values.collect::<Vec<_>>();
match &*values {
ExtendedKeyUsage::CLIENT_AUTH_REPR => Self::ClientAuth,
ExtendedKeyUsage::SERVER_AUTH_REPR => Self::ServerAuth,
_ => Self::Other(values),
}
}
}
impl fmt::Display for ExtendedKeyPurpose {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ClientAuth => write!(f, "client authentication"),
Self::ServerAuth => write!(f, "server authentication"),
Self::Other(values) => {
for (i, value) in values.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{value}")?;
}
Ok(())
}
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum CertRevocationListError {
BadSignature,
UnsupportedSignatureAlgorithm {
signature_algorithm_id: Vec<u8>,
supported_algorithms: Vec<AlgorithmIdentifier>,
},
UnsupportedSignatureAlgorithmForPublicKey {
signature_algorithm_id: Vec<u8>,
public_key_algorithm_id: Vec<u8>,
},
InvalidCrlNumber,
InvalidRevokedCertSerialNumber,
IssuerInvalidForCrl,
Other(OtherError),
ParseError,
UnsupportedCrlVersion,
UnsupportedCriticalExtension,
UnsupportedDeltaCrl,
UnsupportedIndirectCrl,
UnsupportedRevocationReason,
}
impl PartialEq<Self> for CertRevocationListError {
fn eq(&self, other: &Self) -> bool {
use CertRevocationListError::*;
match (self, other) {
(BadSignature, BadSignature) => true,
(
UnsupportedSignatureAlgorithm {
signature_algorithm_id: left_signature_algorithm_id,
supported_algorithms: left_supported_algorithms,
},
UnsupportedSignatureAlgorithm {
signature_algorithm_id: right_signature_algorithm_id,
supported_algorithms: right_supported_algorithms,
},
) => {
(left_signature_algorithm_id, left_supported_algorithms)
== (right_signature_algorithm_id, right_supported_algorithms)
}
(
UnsupportedSignatureAlgorithmForPublicKey {
signature_algorithm_id: left_signature_algorithm_id,
public_key_algorithm_id: left_public_key_algorithm_id,
},
UnsupportedSignatureAlgorithmForPublicKey {
signature_algorithm_id: right_signature_algorithm_id,
public_key_algorithm_id: right_public_key_algorithm_id,
},
) => {
(left_signature_algorithm_id, left_public_key_algorithm_id)
== (right_signature_algorithm_id, right_public_key_algorithm_id)
}
(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,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum EncryptedClientHelloError {
InvalidConfigList,
NoCompatibleConfig,
SniRequired,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
pub struct RejectedEch {
pub(crate) retry_configs: Option<Vec<EchConfigPayload>>,
}
impl RejectedEch {
pub fn can_retry(&self) -> bool {
self.retry_configs.is_some()
}
pub fn retry_configs(&self) -> Option<EchConfigListBytes<'static>> {
let Some(retry_configs) = &self.retry_configs else {
return None;
};
let mut tls_encoded_list = Vec::new();
retry_configs.encode(&mut tls_encoded_list);
Some(EchConfigListBytes::from(tls_encoded_list))
}
}
fn join<T: fmt::Debug>(items: &[T]) -> String {
items
.iter()
.map(|x| format!("{x:?}"))
.collect::<Vec<String>>()
.join(" or ")
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
pub enum ApiMisuse {
ResumingFromUnknownCipherSuite(CipherSuite),
ExporterAlreadyUsed,
ExporterContextTooLong,
ExporterOutputTooLong,
ExporterOutputZeroLength,
InvalidQuicHeaderProtectionSampleLength,
InvalidQuicHeaderProtectionPacketNumberLength,
InvalidSignerForProtocolVersion,
QuicRequiresTls13Support,
NoQuicCompatibleCipherSuites,
EmptyCertificateChain,
QuicRestrictsMaxEarlyDataSize,
NoCipherSuitesConfigured,
NoKeyExchangeGroupsConfigured,
NoSignatureVerificationAlgorithms,
EchRequiresTls13Support,
EchForbidsTls12Support,
ReceivedPlaintextBufferFull,
SecretExtractionRequiresPriorOptIn,
SecretExtractionWithPendingSendableData,
UnverifiableCertificateType,
NoSupportedCertificateTypes,
NonceArraySizeMismatch {
expected: usize,
actual: usize,
},
IvLengthExceedsMaximum {
actual: usize,
maximum: usize,
},
ResumptionDataProvidedTooLate,
KeyUpdateNotAvailableForTls12,
SplitDuringHandshake,
SplitWithPendingBuffers,
EncryptBufferTooSmall {
required: usize,
provided: usize,
},
}
impl fmt::Display for ApiMisuse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}
impl core::error::Error for ApiMisuse {}
mod other_error {
use core::error::Error as StdError;
use core::fmt;
use crate::sync::Arc;
#[derive(Debug, Clone)]
pub struct OtherError(Arc<dyn StdError + Send + Sync>);
impl OtherError {
pub fn new(err: impl StdError + Send + Sync + 'static) -> Self {
Self(Arc::new(err))
}
}
impl PartialEq<Self> for OtherError {
fn eq(&self, _other: &Self) -> bool {
false
}
}
impl fmt::Display for OtherError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl StdError for OtherError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
Some(self.0.as_ref())
}
}
}
pub use other_error::OtherError;
pub struct ErrorWithAlert {
pub error: Error,
pub(crate) data: Vec<u8>,
}
impl ErrorWithAlert {
pub(crate) fn new(error: Error, send_path: &mut SendPath) -> Self {
maybe_send_fatal_alert(send_path, &error);
Self {
error,
data: send_path.sendable_tls.take_one_vec(),
}
}
pub fn take_tls_data(&mut self) -> Option<Vec<u8>> {
match self.data.is_empty() {
true => None,
false => Some(mem::take(&mut self.data)),
}
}
}
impl Deref for ErrorWithAlert {
type Target = Error;
fn deref(&self) -> &Self::Target {
&self.error
}
}
impl From<Error> for ErrorWithAlert {
fn from(error: Error) -> Self {
Self {
error,
data: Vec::new(),
}
}
}
impl fmt::Debug for ErrorWithAlert {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ErrorWithAlert")
.field("error", &self.error)
.field("data", &self.data.len())
.finish_non_exhaustive()
}
}