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}