Skip to main content

tls_tester/
alert.rs

1/// Alert level.
2///
3/// # References
4///
5/// * [RFC 8446 Section 6](https://datatracker.ietf.org/doc/html/rfc8446#section-6)
6///
7/// ```text
8/// enum { warning(1), fatal(2), (255) } AlertLevel;
9/// ```
10#[repr(u8)]
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
12#[non_exhaustive]
13pub enum AlertLevel {
14    /// Warning.
15    Warning = 1,
16    /// Fatal.
17    ///
18    /// Also used for unknown [`AlertLevel`] values.
19    Fatal = 2,
20}
21
22impl From<AlertLevel> for u8 {
23    #[inline]
24    fn from(alert_level: AlertLevel) -> Self {
25        alert_level as u8
26    }
27}
28
29impl TryFrom<u8> for AlertLevel {
30    type Error = u8;
31
32    fn try_from(value: u8) -> Result<Self, Self::Error> {
33        match value {
34            x if x == (Self::Warning as u8) => Ok(Self::Warning),
35            x if x == (Self::Fatal as u8) => Ok(Self::Fatal),
36            _ => Err(value),
37        }
38    }
39}
40
41/// Alert description.
42///
43/// # References
44///
45/// * [RFC 8446 Section 6](https://datatracker.ietf.org/doc/html/rfc8446#section-6)
46/// * [RFC 8446 Section 6.1](https://datatracker.ietf.org/doc/html/rfc8446#section-6.1)
47/// * [RFC 8446 Section 6.2](https://datatracker.ietf.org/doc/html/rfc8446#section-6.2)
48///
49/// ```text
50/// enum {
51///     close_notify(0),
52///     unexpected_message(10),
53///     bad_record_mac(20),
54///     record_overflow(22),
55///     handshake_failure(40),
56///     bad_certificate(42),
57///     unsupported_certificate(43),
58///     certificate_revoked(44),
59///     certificate_expired(45),
60///     certificate_unknown(46),
61///     illegal_parameter(47),
62///     unknown_ca(48),
63///     access_denied(49),
64///     decode_error(50),
65///     decrypt_error(51),
66///     protocol_version(70),
67///     insufficient_security(71),
68///     internal_error(80),
69///     inappropriate_fallback(86),
70///     user_canceled(90),
71///     missing_extension(109),
72///     unsupported_extension(110),
73///     unrecognized_name(112),
74///     bad_certificate_status_response(113),
75///     unknown_psk_identity(115),
76///     certificate_required(116),
77///     no_application_protocol(120),
78///     (255)
79/// } AlertDescription;
80/// ```
81#[repr(u8)]
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83#[non_exhaustive]
84pub enum AlertDescription {
85    /// This alert notifies the recipient that the sender will
86    /// not send any more messages on this connection.  Any data received
87    /// after a closure alert has been received MUST be ignored.
88    CloseNotify = 0,
89    /// An inappropriate message (e.g., the wrong
90    /// handshake message, premature Application Data, etc.) was received.
91    /// This alert should never be observed in communication between
92    /// proper implementations.
93    UnexpectedMessage = 10,
94    /// This alert is returned if a record is received which
95    /// cannot be deprotected.  Because AEAD algorithms combine decryption
96    /// and verification, and also to avoid side-channel attacks, this
97    /// alert is used for all deprotection failures.  This alert should
98    /// never be observed in communication between proper implementations,
99    /// except when messages were corrupted in the network.
100    BadRecordMac = 20,
101    /// A TLSCiphertext record was received that had a
102    /// length more than `2^14 + 256` bytes, or a record decrypted to a
103    /// TLSPlaintext record with more than `2^14` bytes (or some other
104    /// negotiated limit).  This alert should never be observed in
105    /// communication between proper implementations, except when messages
106    /// were corrupted in the network.
107    RecordOverflow = 22,
108    /// Receipt of a `handshake_failure` alert message
109    /// indicates that the sender was unable to negotiate an acceptable
110    /// set of security parameters given the options available.
111    HandshakeFailure = 40,
112    /// A certificate was corrupt, contained signatures
113    /// that did not verify correctly, etc.
114    BadCertificate = 42,
115    /// A certificate was of an unsupported type.
116    UnsupportedCertificate = 43,
117    /// A certificate was revoked by its signer.
118    CertificateRevoked = 44,
119    /// A certificate has expired or is not currently valid.
120    CertificateExpired = 45,
121    /// Some other (unspecified) issue arose in
122    /// processing the certificate, rendering it unacceptable.
123    CertificateUnknown = 46,
124    /// A field in the handshake was incorrect or
125    /// inconsistent with other fields.  This alert is used for errors
126    /// which conform to the formal protocol syntax but are otherwise
127    /// incorrect.
128    IllegalParameter = 47,
129    /// A valid certificate chain or partial chain was received,
130    /// but the certificate was not accepted because the CA certificate
131    /// could not be located or could not be matched with a known trust
132    /// anchor.
133    UnknownCa = 48,
134    /// A valid certificate or PSK was received, but when
135    /// access control was applied, the sender decided not to proceed with
136    /// negotiation.
137    AccessDenied = 49,
138    /// A message could not be decoded because some field was
139    /// out of the specified range or the length of the message was
140    /// incorrect.  This alert is used for errors where the message does
141    /// not conform to the formal protocol syntax.  This alert should
142    /// never be observed in communication between proper implementations,
143    /// except when messages were corrupted in the network.
144    DecodeError = 50,
145    /// A handshake (not record layer) cryptographic
146    /// operation failed, including being unable to correctly verify a
147    /// signature or validate a Finished message or a PSK binder.
148    DecryptError = 51,
149    /// The protocol version the peer has attempted to
150    /// negotiate is recognized but not supported.
151    ProtocolVersion = 70,
152    /// Returned instead of `handshake_failure` when
153    /// a negotiation has failed specifically because the server requires
154    /// parameters more secure than those supported by the client.
155    InsufficientSecurity = 71,
156    /// An internal error unrelated to the peer or the
157    /// correctness of the protocol (such as a memory allocation failure)
158    /// makes it impossible to continue.
159    InternalError = 80,
160    /// Sent by a server in response to an invalid
161    /// connection retry attempt from a client (see [RFC 7507]).
162    ///
163    /// [RFC 7507]: https://datatracker.ietf.org/doc/html/rfc7507
164    InappropriateFallback = 86,
165    /// This alert notifies the recipient that the sender is
166    /// canceling the handshake for some reason unrelated to a protocol
167    /// failure.  If a user cancels an operation after the handshake is
168    /// complete, just closing the connection by sending a `close_notify`
169    /// is more appropriate.  This alert SHOULD be followed by a
170    /// `close_notify`.  This alert generally has [`AlertLevel::Warning`].
171    UserCanceled = 90,
172    /// Sent by endpoints that receive a handshake
173    /// message not containing an extension that is mandatory to send for
174    /// the offered TLS version or other negotiated parameters.
175    MissingExtension = 109,
176    /// Sent by endpoints receiving any handshake
177    /// message containing an extension known to be prohibited for
178    /// inclusion in the given handshake message, or including any
179    /// extensions in a `ServerHello` or `Certificate` not first offered in
180    /// the corresponding `ClientHello` or `CertificateRequest`.
181    UnsupportedExtension = 110,
182    /// Sent by servers when no server exists identified
183    /// by the name provided by the client via the `server_name` extension
184    /// (see [RFC 6066]).
185    ///
186    /// [RFC 6066]: https://datatracker.ietf.org/doc/html/rfc6066
187    UnrecognizedName = 112,
188    /// Sent by clients when an invalid or
189    /// unacceptable OCSP response is provided by the server via the
190    /// `status_request` extension (see [RFC 6066]).
191    ///
192    /// [RFC 6066]: https://datatracker.ietf.org/doc/html/rfc6066
193    BadCertificateStatusResponse = 113,
194    /// Sent by servers when PSK key establishment is
195    /// desired but no acceptable PSK identity is provided by the client.
196    /// Sending this alert is OPTIONAL; servers MAY instead choose to send
197    /// a `decrypt_error` alert to merely indicate an invalid PSK
198    /// identity.
199    UnknownPskIdentity = 115,
200    /// Sent by servers when a client certificate is
201    /// desired but none was provided by the client.
202    CertificateRequired = 116,
203    /// Sent by servers when a client
204    /// `application_layer_protocol_negotiation` extension advertises only
205    /// protocols that the server does not support (see [RFC 7301]).
206    ///
207    /// [RFC 7301]: https://datatracker.ietf.org/doc/html/rfc7301
208    NoApplicationProtocol = 120,
209}
210
211impl From<AlertDescription> for u8 {
212    #[inline]
213    fn from(alert_description: AlertDescription) -> Self {
214        alert_description as u8
215    }
216}
217
218impl TryFrom<u8> for AlertDescription {
219    type Error = u8;
220    fn try_from(value: u8) -> Result<Self, Self::Error> {
221        match value {
222            x if x == (Self::CloseNotify as u8) => Ok(Self::CloseNotify),
223            x if x == (Self::UnexpectedMessage as u8) => Ok(Self::UnexpectedMessage),
224            x if x == (Self::BadRecordMac as u8) => Ok(Self::BadRecordMac),
225            x if x == (Self::RecordOverflow as u8) => Ok(Self::RecordOverflow),
226            x if x == (Self::HandshakeFailure as u8) => Ok(Self::HandshakeFailure),
227            x if x == (Self::BadCertificate as u8) => Ok(Self::BadCertificate),
228            x if x == (Self::UnsupportedCertificate as u8) => Ok(Self::UnsupportedCertificate),
229            x if x == (Self::CertificateRevoked as u8) => Ok(Self::CertificateRevoked),
230            x if x == (Self::CertificateExpired as u8) => Ok(Self::CertificateExpired),
231            x if x == (Self::CertificateUnknown as u8) => Ok(Self::CertificateUnknown),
232            x if x == (Self::IllegalParameter as u8) => Ok(Self::IllegalParameter),
233            x if x == (Self::UnknownCa as u8) => Ok(Self::UnknownCa),
234            x if x == (Self::AccessDenied as u8) => Ok(Self::AccessDenied),
235            x if x == (Self::DecodeError as u8) => Ok(Self::DecodeError),
236            x if x == (Self::DecryptError as u8) => Ok(Self::DecryptError),
237            x if x == (Self::ProtocolVersion as u8) => Ok(Self::ProtocolVersion),
238            x if x == (Self::InsufficientSecurity as u8) => Ok(Self::InsufficientSecurity),
239            x if x == (Self::InternalError as u8) => Ok(Self::InternalError),
240            x if x == (Self::InappropriateFallback as u8) => Ok(Self::InappropriateFallback),
241            x if x == (Self::UserCanceled as u8) => Ok(Self::UserCanceled),
242            x if x == (Self::MissingExtension as u8) => Ok(Self::MissingExtension),
243            x if x == (Self::UnsupportedExtension as u8) => Ok(Self::UnsupportedExtension),
244            x if x == (Self::UnrecognizedName as u8) => Ok(Self::UnrecognizedName),
245            x if x == (Self::BadCertificateStatusResponse as u8) => {
246                Ok(Self::BadCertificateStatusResponse)
247            }
248            x if x == (Self::UnknownPskIdentity as u8) => Ok(Self::UnknownPskIdentity),
249            x if x == (Self::CertificateRequired as u8) => Ok(Self::CertificateRequired),
250            x if x == (Self::NoApplicationProtocol as u8) => Ok(Self::NoApplicationProtocol),
251            _ => Err(value),
252        }
253    }
254}
255
256/// TLS Alert.
257///
258/// See [`AlertLevel`] and [`AlertDescription`].
259#[derive(Debug, Clone, Copy, PartialEq, Eq)]
260pub struct Alert {
261    /// Alert level.
262    pub level: AlertLevel,
263    /// Alert description.
264    pub description: AlertDescription,
265}
266
267impl Alert {
268    pub fn from_be_bytes(bytes: [u8; 2]) -> Result<Self, AlertDescription> {
269        let level = match AlertLevel::try_from(bytes[0]) {
270            Ok(level) => level,
271            Err(val) => {
272                log::error!("Reserved AlertLevel 0x{val:02x}");
273                return Err(AlertDescription::DecodeError);
274            }
275        };
276        let description = match AlertDescription::try_from(bytes[1]) {
277            Ok(description) => description,
278            Err(val) => {
279                log::error!("Reserved AlertDescription 0x{val:02x}");
280                return Err(AlertDescription::DecodeError);
281            }
282        };
283
284        Ok(Self { level, description })
285    }
286
287    pub fn to_be_bytes(self) -> [u8; 2] {
288        [self.level.into(), self.description.into()]
289    }
290
291    pub(crate) fn new_fatal(description: AlertDescription) -> Self {
292        Self {
293            level: AlertLevel::Warning,
294            description,
295        }
296    }
297
298    pub(crate) fn new_warning(description: AlertDescription) -> Self {
299        Self {
300            level: AlertLevel::Warning,
301            description,
302        }
303    }
304}