use crate::enums::{AlertDescription, ContentType, HandshakeType};
use crate::msgs::handshake::KeyExchangeAlgorithm;
use crate::rand;
use std::error::Error as StdError;
use std::fmt;
use std::sync::Arc;
use std::time::SystemTimeError;
#[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,
},
InvalidMessage(InvalidMessage),
NoCertificatesPresented,
UnsupportedNameType,
DecryptError,
EncryptError,
PeerIncompatible(PeerIncompatible),
PeerMisbehaved(PeerMisbehaved),
AlertReceived(AlertDescription),
InvalidCertificate(CertificateError),
InvalidSct(sct::Error),
InvalidCertRevocationList(CertRevocationListError),
General(String),
FailedToGetCurrentTime,
FailedToGetRandomBytes,
HandshakeNotComplete,
PeerSentOversizedRecord,
NoApplicationProtocol,
BadMaxFragmentSize,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum InvalidMessage {
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),
}
impl From<InvalidMessage> for Error {
#[inline]
fn from(e: InvalidMessage) -> Self {
Self::InvalidMessage(e)
}
}
#[non_exhaustive]
#[allow(missing_docs)]
#[derive(Debug, PartialEq, Clone)]
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,
InvalidSctList,
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)]
pub enum PeerIncompatible {
EcPointsExtensionRequired,
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)]
pub enum CertificateError {
BadEncoding,
Expired,
NotValidYet,
Revoked,
UnhandledCriticalExtension,
UnknownIssuer,
BadSignature,
NotValidForName,
InvalidPurpose,
ApplicationVerificationFailure,
Other(Arc<dyn StdError + Send + Sync>),
}
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,
}
}
}
impl From<CertificateError> for AlertDescription {
fn from(e: CertificateError) -> Self {
use CertificateError::*;
match e {
BadEncoding | UnhandledCriticalExtension | NotValidForName => Self::BadCertificate,
Expired | NotValidYet => Self::CertificateExpired,
Revoked => Self::CertificateRevoked,
UnknownIssuer => Self::UnknownCA,
BadSignature => Self::DecryptError,
InvalidPurpose => Self::UnsupportedCertificate,
ApplicationVerificationFailure => Self::AccessDenied,
Other(_) => Self::CertificateUnknown,
}
}
}
impl From<CertificateError> for Error {
#[inline]
fn from(e: CertificateError) -> Self {
Self::InvalidCertificate(e)
}
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum CertRevocationListError {
BadSignature,
InvalidCrlNumber,
InvalidRevokedCertSerialNumber,
IssuerInvalidForCrl,
Other(Arc<dyn StdError + Send + Sync>),
ParseError,
UnsupportedCrlVersion,
UnsupportedCriticalExtension,
UnsupportedDeltaCrl,
UnsupportedIndirectCrl,
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<webpki::Error> for CertRevocationListError {
fn from(e: webpki::Error) -> Self {
use webpki::Error::*;
match e {
InvalidCrlSignatureForPublicKey
| UnsupportedCrlSignatureAlgorithm
| UnsupportedCrlSignatureAlgorithmForPublicKey => Self::BadSignature,
InvalidCrlNumber => Self::InvalidCrlNumber,
InvalidSerialNumber => Self::InvalidRevokedCertSerialNumber,
IssuerNotCrlSigner => Self::IssuerInvalidForCrl,
MalformedExtensions | BadDer | BadDerTime => Self::ParseError,
UnsupportedCriticalExtension => Self::UnsupportedCriticalExtension,
UnsupportedCrlVersion => Self::UnsupportedCrlVersion,
UnsupportedDeltaCrl => Self::UnsupportedDeltaCrl,
UnsupportedIndirectCrl => Self::UnsupportedIndirectCrl,
UnsupportedRevocationReason => Self::UnsupportedRevocationReason,
_ => Self::Other(Arc::new(e)),
}
}
}
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::InvalidSct(ref err) => write!(f, "invalid certificate timestamp: {:?}", err),
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),
}
}
}
impl From<SystemTimeError> for Error {
#[inline]
fn from(_: SystemTimeError) -> Self {
Self::FailedToGetCurrentTime
}
}
impl StdError for Error {}
impl From<rand::GetRandomFailed> for Error {
fn from(_: rand::GetRandomFailed) -> Self {
Self::FailedToGetRandomBytes
}
}
#[cfg(test)]
mod tests {
use super::{Error, InvalidMessage};
use crate::error::CertRevocationListError;
#[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(std::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(std::sync::Arc::from(Box::from("")));
assert_ne!(other, other);
assert_ne!(BadSignature, InvalidCrlNumber);
}
#[test]
fn crl_error_from_webpki() {
use super::CertRevocationListError::*;
let testcases = &[
(webpki::Error::InvalidCrlSignatureForPublicKey, BadSignature),
(
webpki::Error::UnsupportedCrlSignatureAlgorithm,
BadSignature,
),
(
webpki::Error::UnsupportedCrlSignatureAlgorithmForPublicKey,
BadSignature,
),
(webpki::Error::InvalidCrlNumber, InvalidCrlNumber),
(
webpki::Error::InvalidSerialNumber,
InvalidRevokedCertSerialNumber,
),
(webpki::Error::IssuerNotCrlSigner, IssuerInvalidForCrl),
(webpki::Error::MalformedExtensions, ParseError),
(webpki::Error::BadDer, ParseError),
(webpki::Error::BadDerTime, ParseError),
(
webpki::Error::UnsupportedCriticalExtension,
UnsupportedCriticalExtension,
),
(webpki::Error::UnsupportedCrlVersion, UnsupportedCrlVersion),
(webpki::Error::UnsupportedDeltaCrl, UnsupportedDeltaCrl),
(
webpki::Error::UnsupportedIndirectCrl,
UnsupportedIndirectCrl,
),
(
webpki::Error::UnsupportedRevocationReason,
UnsupportedRevocationReason,
),
];
for t in testcases {
assert_eq!(
<webpki::Error as Into<CertRevocationListError>>::into(t.0),
t.1
);
}
assert!(matches!(
<webpki::Error as Into<CertRevocationListError>>::into(
webpki::Error::NameConstraintViolation
),
Other(_)
));
}
#[test]
fn smoke() {
use crate::enums::{AlertDescription, ContentType, HandshakeType};
use sct;
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::InvalidSct(sct::Error::MalformedSct),
Error::General("undocumented error".to_string()),
Error::FailedToGetCurrentTime,
Error::FailedToGetRandomBytes,
Error::HandshakeNotComplete,
Error::PeerSentOversizedRecord,
Error::NoApplicationProtocol,
Error::BadMaxFragmentSize,
Error::InvalidCertRevocationList(CertRevocationListError::BadSignature),
];
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);
}
#[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);
}
}