1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/// Alert level.
///
/// # References
///
/// * [RFC 8446 Section 6](https://datatracker.ietf.org/doc/html/rfc8446#section-6)
///
/// ```text
/// enum { warning(1), fatal(2), (255) } AlertLevel;
/// ```
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AlertLevel {
    /// Warning.
    Warning = 1,
    /// Fatal.
    ///
    /// Also used for unknown [`AlertLevel`] values.
    Fatal = 2,
}

impl From<AlertLevel> for u8 {
    #[inline]
    fn from(alert_level: AlertLevel) -> Self {
        alert_level as u8
    }
}

impl TryFrom<u8> for AlertLevel {
    type Error = u8;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            x if x == (Self::Warning as u8) => Ok(Self::Warning),
            x if x == (Self::Fatal as u8) => Ok(Self::Fatal),
            _ => Err(value),
        }
    }
}

/// Alert description.
///
/// # References
///
/// * [RFC 8446 Section 6](https://datatracker.ietf.org/doc/html/rfc8446#section-6)
/// * [RFC 8446 Section 6.1](https://datatracker.ietf.org/doc/html/rfc8446#section-6.1)
/// * [RFC 8446 Section 6.2](https://datatracker.ietf.org/doc/html/rfc8446#section-6.2)
///
/// ```text
/// enum {
///     close_notify(0),
///     unexpected_message(10),
///     bad_record_mac(20),
///     record_overflow(22),
///     handshake_failure(40),
///     bad_certificate(42),
///     unsupported_certificate(43),
///     certificate_revoked(44),
///     certificate_expired(45),
///     certificate_unknown(46),
///     illegal_parameter(47),
///     unknown_ca(48),
///     access_denied(49),
///     decode_error(50),
///     decrypt_error(51),
///     protocol_version(70),
///     insufficient_security(71),
///     internal_error(80),
///     inappropriate_fallback(86),
///     user_canceled(90),
///     missing_extension(109),
///     unsupported_extension(110),
///     unrecognized_name(112),
///     bad_certificate_status_response(113),
///     unknown_psk_identity(115),
///     certificate_required(116),
///     no_application_protocol(120),
///     (255)
/// } AlertDescription;
/// ```
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AlertDescription {
    /// This alert notifies the recipient that the sender will
    /// not send any more messages on this connection.  Any data received
    /// after a closure alert has been received MUST be ignored.
    CloseNotify = 0,
    /// An inappropriate message (e.g., the wrong
    /// handshake message, premature Application Data, etc.) was received.
    /// This alert should never be observed in communication between
    /// proper implementations.
    UnexpectedMessage = 10,
    /// This alert is returned if a record is received which
    /// cannot be deprotected.  Because AEAD algorithms combine decryption
    /// and verification, and also to avoid side-channel attacks, this
    /// alert is used for all deprotection failures.  This alert should
    /// never be observed in communication between proper implementations,
    /// except when messages were corrupted in the network.
    BadRecordMac = 20,
    /// A TLSCiphertext record was received that had a
    /// length more than `2^14 + 256` bytes, or a record decrypted to a
    /// TLSPlaintext record with more than `2^14` bytes (or some other
    /// negotiated limit).  This alert should never be observed in
    /// communication between proper implementations, except when messages
    /// were corrupted in the network.
    RecordOverflow = 22,
    /// Receipt of a `handshake_failure` alert message
    /// indicates that the sender was unable to negotiate an acceptable
    /// set of security parameters given the options available.
    HandshakeFailure = 40,
    /// A certificate was corrupt, contained signatures
    /// that did not verify correctly, etc.
    BadCertificate = 42,
    /// A certificate was of an unsupported type.
    UnsupportedCertificate = 43,
    /// A certificate was revoked by its signer.
    CertificateRevoked = 44,
    /// A certificate has expired or is not currently valid.
    CertificateExpired = 45,
    /// Some other (unspecified) issue arose in
    /// processing the certificate, rendering it unacceptable.
    CertificateUnknown = 46,
    /// A field in the handshake was incorrect or
    /// inconsistent with other fields.  This alert is used for errors
    /// which conform to the formal protocol syntax but are otherwise
    /// incorrect.
    IllegalParameter = 47,
    /// A valid certificate chain or partial chain was received,
    /// but the certificate was not accepted because the CA certificate
    /// could not be located or could not be matched with a known trust
    /// anchor.
    UnknownCa = 48,
    /// A valid certificate or PSK was received, but when
    /// access control was applied, the sender decided not to proceed with
    /// negotiation.
    AccessDenied = 49,
    /// A message could not be decoded because some field was
    /// out of the specified range or the length of the message was
    /// incorrect.  This alert is used for errors where the message does
    /// not conform to the formal protocol syntax.  This alert should
    /// never be observed in communication between proper implementations,
    /// except when messages were corrupted in the network.
    DecodeError = 50,
    /// A handshake (not record layer) cryptographic
    /// operation failed, including being unable to correctly verify a
    /// signature or validate a Finished message or a PSK binder.
    DecryptError = 51,
    /// The protocol version the peer has attempted to
    /// negotiate is recognized but not supported.
    ProtocolVersion = 70,
    /// Returned instead of `handshake_failure` when
    /// a negotiation has failed specifically because the server requires
    /// parameters more secure than those supported by the client.
    InsufficientSecurity = 71,
    /// An internal error unrelated to the peer or the
    /// correctness of the protocol (such as a memory allocation failure)
    /// makes it impossible to continue.
    InternalError = 80,
    /// Sent by a server in response to an invalid
    /// connection retry attempt from a client (see [RFC 7507]).
    ///
    /// [RFC 7507]: https://datatracker.ietf.org/doc/html/rfc7507
    InappropriateFallback = 86,
    /// This alert notifies the recipient that the sender is
    /// canceling the handshake for some reason unrelated to a protocol
    /// failure.  If a user cancels an operation after the handshake is
    /// complete, just closing the connection by sending a `close_notify`
    /// is more appropriate.  This alert SHOULD be followed by a
    /// `close_notify`.  This alert generally has [`AlertLevel::Warning`].
    UserCanceled = 90,
    /// Sent by endpoints that receive a handshake
    /// message not containing an extension that is mandatory to send for
    /// the offered TLS version or other negotiated parameters.
    MissingExtension = 109,
    /// Sent by endpoints receiving any handshake
    /// message containing an extension known to be prohibited for
    /// inclusion in the given handshake message, or including any
    /// extensions in a `ServerHello` or `Certificate` not first offered in
    /// the corresponding `ClientHello` or `CertificateRequest`.
    UnsupportedExtension = 110,
    /// Sent by servers when no server exists identified
    /// by the name provided by the client via the `server_name` extension
    /// (see [RFC 6066]).
    ///
    /// [RFC 6066]: https://datatracker.ietf.org/doc/html/rfc6066
    UnrecognizedName = 112,
    /// Sent by clients when an invalid or
    /// unacceptable OCSP response is provided by the server via the
    /// `status_request` extension (see [RFC 6066]).
    ///
    /// [RFC 6066]: https://datatracker.ietf.org/doc/html/rfc6066
    BadCertificateStatusResponse = 113,
    /// Sent by servers when PSK key establishment is
    /// desired but no acceptable PSK identity is provided by the client.
    /// Sending this alert is OPTIONAL; servers MAY instead choose to send
    /// a `decrypt_error` alert to merely indicate an invalid PSK
    /// identity.
    UnknownPskIdentity = 115,
    /// Sent by servers when a client certificate is
    /// desired but none was provided by the client.
    CertificateRequired = 116,
    /// Sent by servers when a client
    /// `application_layer_protocol_negotiation` extension advertises only
    /// protocols that the server does not support (see [RFC 7301]).
    ///
    /// [RFC 7301]: https://datatracker.ietf.org/doc/html/rfc7301
    NoApplicationProtocol = 120,
}

impl From<AlertDescription> for u8 {
    #[inline]
    fn from(alert_description: AlertDescription) -> Self {
        alert_description as u8
    }
}

impl TryFrom<u8> for AlertDescription {
    type Error = u8;
    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            x if x == (Self::CloseNotify as u8) => Ok(Self::CloseNotify),
            x if x == (Self::UnexpectedMessage as u8) => Ok(Self::UnexpectedMessage),
            x if x == (Self::BadRecordMac as u8) => Ok(Self::BadRecordMac),
            x if x == (Self::RecordOverflow as u8) => Ok(Self::RecordOverflow),
            x if x == (Self::HandshakeFailure as u8) => Ok(Self::HandshakeFailure),
            x if x == (Self::BadCertificate as u8) => Ok(Self::BadCertificate),
            x if x == (Self::UnsupportedCertificate as u8) => Ok(Self::UnsupportedCertificate),
            x if x == (Self::CertificateRevoked as u8) => Ok(Self::CertificateRevoked),
            x if x == (Self::CertificateExpired as u8) => Ok(Self::CertificateExpired),
            x if x == (Self::CertificateUnknown as u8) => Ok(Self::CertificateUnknown),
            x if x == (Self::IllegalParameter as u8) => Ok(Self::IllegalParameter),
            x if x == (Self::UnknownCa as u8) => Ok(Self::UnknownCa),
            x if x == (Self::AccessDenied as u8) => Ok(Self::AccessDenied),
            x if x == (Self::DecodeError as u8) => Ok(Self::DecodeError),
            x if x == (Self::DecryptError as u8) => Ok(Self::DecryptError),
            x if x == (Self::ProtocolVersion as u8) => Ok(Self::ProtocolVersion),
            x if x == (Self::InsufficientSecurity as u8) => Ok(Self::InsufficientSecurity),
            x if x == (Self::InternalError as u8) => Ok(Self::InternalError),
            x if x == (Self::InappropriateFallback as u8) => Ok(Self::InappropriateFallback),
            x if x == (Self::UserCanceled as u8) => Ok(Self::UserCanceled),
            x if x == (Self::MissingExtension as u8) => Ok(Self::MissingExtension),
            x if x == (Self::UnsupportedExtension as u8) => Ok(Self::UnsupportedExtension),
            x if x == (Self::UnrecognizedName as u8) => Ok(Self::UnrecognizedName),
            x if x == (Self::BadCertificateStatusResponse as u8) => {
                Ok(Self::BadCertificateStatusResponse)
            }
            x if x == (Self::UnknownPskIdentity as u8) => Ok(Self::UnknownPskIdentity),
            x if x == (Self::CertificateRequired as u8) => Ok(Self::CertificateRequired),
            x if x == (Self::NoApplicationProtocol as u8) => Ok(Self::NoApplicationProtocol),
            _ => Err(value),
        }
    }
}

impl AlertDescription {
    pub(crate) fn map_w5500<E>(e: w5500_hl::Error<E>) -> Self {
        match e {
            w5500_hl::Error::UnexpectedEof => AlertDescription::DecodeError,
            w5500_hl::Error::OutOfMemory => AlertDescription::InternalError,
            w5500_hl::Error::Other(_) => AlertDescription::InternalError,
            // technically unreachable, but this can occur if there is
            // a bit flip on the SPI bus
            w5500_hl::Error::WouldBlock => {
                error!("W5500 unexpectedly blocked");
                AlertDescription::InternalError
            }
        }
    }
}

/// TLS Alert.
///
/// See [`AlertLevel`] and [`AlertDescription`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Alert {
    /// Alert level.
    pub level: AlertLevel,
    /// Alert description.
    pub description: AlertDescription,
}

impl Alert {
    pub(crate) fn new_fatal(description: AlertDescription) -> Self {
        Self {
            level: AlertLevel::Warning,
            description,
        }
    }

    pub(crate) fn new_warning(description: AlertDescription) -> Self {
        Self {
            level: AlertLevel::Warning,
            description,
        }
    }
}