s2n_quic_core/crypto/tls/
error.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::event::metrics::aggregate;
5use core::fmt;
6use s2n_codec::DecoderError;
7
8/// Error type for TLS-related errors
9#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
10#[cfg_attr(feature = "thiserror", derive(thiserror::Error))]
11pub struct Error {
12    pub reason: &'static str,
13    pub code: u8,
14}
15
16impl Error {
17    /// Creates a new `tls::Error`
18    pub const fn new(code: u8) -> Self {
19        Self { code, reason: "" }
20    }
21
22    /// Sets the reason for `tls::Error`
23    #[must_use]
24    pub const fn with_reason(mut self, reason: &'static str) -> Self {
25        self.reason = reason;
26        self
27    }
28}
29
30impl fmt::Display for Error {
31    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32        if !self.reason.is_empty() {
33            self.reason.fmt(f)
34        } else if let Some(description) = self.description() {
35            description.fmt(f)
36        } else {
37            write!(f, "tls::Error({})", self.code)
38        }
39    }
40}
41
42impl fmt::Debug for Error {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        let mut d = f.debug_struct("tls::Error");
45
46        d.field("code", &self.code);
47
48        if let Some(description) = self.description() {
49            d.field("description", &description);
50        }
51
52        if !self.reason.is_empty() {
53            d.field("reason", &self.reason);
54        }
55
56        d.finish()
57    }
58}
59
60impl From<DecoderError> for Error {
61    fn from(_: DecoderError) -> Self {
62        Self::DECODE_ERROR
63    }
64}
65
66macro_rules! alert_descriptions {
67    ($($name:ident = $value:expr),* $(,)?) => {
68        impl Error {
69            pub fn description(&self) -> Option<&'static str> {
70                match self.code {
71                    $(
72                        $value => Some(stringify!($name)),
73                    )*
74                    _ => None,
75                }
76            }
77
78            $(
79                pub const $name: Self = Self::new($value);
80            )*
81        }
82
83        impl aggregate::AsVariant for Error {
84            const VARIANTS: &'static [aggregate::info::Variant] = &{
85                use aggregate::info::{Variant, Str};
86
87                const fn count(_v: u64) -> usize {
88                    1
89                }
90
91                const LEN: usize = 1 $(+ count($value))*;
92                let mut array = [Variant { name: Str::new("\0"), id: 0 }; LEN];
93
94                let mut id = 0;
95
96                $(
97                    array[id] = Variant {
98                        name: Str::new(concat!("TLS_", stringify!($name), "\0")),
99                        id,
100                    };
101                    id += 1;
102                )*
103
104                array[id] = aggregate::info::Variant {
105                    name: aggregate::info::Str::new("TLS_UNKNOWN_ERROR\0"),
106                    id,
107                };
108
109                array
110            };
111
112            #[inline]
113            fn variant_idx(&self) -> usize {
114                let mut idx = 0;
115
116                $(
117                    if self.code == $value {
118                        return idx;
119                    }
120                    idx += 1;
121                )*
122
123                idx
124            }
125        }
126
127        #[test]
128        fn description_test() {
129            $(
130                assert_eq!(&Error::$name.to_string(), stringify!($name));
131            )*
132        }
133
134        #[test]
135        #[cfg_attr(miri, ignore)]
136        fn variants_test() {
137            use aggregate::AsVariant;
138            insta::assert_debug_snapshot!(Error::VARIANTS);
139
140            let mut seen = std::collections::HashSet::new();
141            for variant in Error::VARIANTS {
142                assert!(seen.insert(variant.id));
143            }
144        }
145    };
146}
147
148//= https://www.rfc-editor.org/rfc/rfc8446#appendix-B.2
149//# enum { warning(1), fatal(2), (255) } AlertLevel;
150//#
151//# enum {
152//#     close_notify(0),
153//#     unexpected_message(10),
154//#     bad_record_mac(20),
155//#     decryption_failed_RESERVED(21),
156//#     record_overflow(22),
157//#     decompression_failure_RESERVED(30),
158//#     handshake_failure(40),
159//#     no_certificate_RESERVED(41),
160//#     bad_certificate(42),
161//#     unsupported_certificate(43),
162//#     certificate_revoked(44),
163//#     certificate_expired(45),
164//#     certificate_unknown(46),
165//#     illegal_parameter(47),
166//#     unknown_ca(48),
167//#     access_denied(49),
168//#     decode_error(50),
169//#     decrypt_error(51),
170//#     export_restriction_RESERVED(60),
171//#     protocol_version(70),
172//#     insufficient_security(71),
173//#     internal_error(80),
174//#     inappropriate_fallback(86),
175//#     user_canceled(90),
176//#     no_renegotiation_RESERVED(100),
177//#     missing_extension(109),
178//#     unsupported_extension(110),
179//#     certificate_unobtainable_RESERVED(111),
180//#     unrecognized_name(112),
181//#     bad_certificate_status_response(113),
182//#     bad_certificate_hash_value_RESERVED(114),
183//#     unknown_psk_identity(115),
184//#     certificate_required(116),
185//#     no_application_protocol(120),
186//#     (255)
187//# } AlertDescription;
188//#
189//# struct {
190//#     AlertLevel level;
191//#     AlertDescription description;
192//# } Alert;
193
194alert_descriptions!(
195    CLOSE_NOTIFY = 0,
196    UNEXPECTED_MESSAGE = 10,
197    BAD_RECORD_MAC = 20,
198    DECRYPTION_FAILED_RESERVED = 21,
199    RECORD_OVERFLOW = 22,
200    DECOMPRESSION_FAILURE_RESERVED = 30,
201    HANDSHAKE_FAILURE = 40,
202    NO_CERTIFICATE_RESERVED = 41,
203    BAD_CERTIFICATE = 42,
204    UNSUPPORTED_CERTIFICATE = 43,
205    CERTIFICATE_REVOKED = 44,
206    CERTIFICATE_EXPIRED = 45,
207    CERTIFICATE_UNKNOWN = 46,
208    ILLEGAL_PARAMETER = 47,
209    UNKNOWN_CA = 48,
210    ACCESS_DENIED = 49,
211    DECODE_ERROR = 50,
212    DECRYPT_ERROR = 51,
213    EXPORT_RESTRICTION_RESERVED = 60,
214    PROTOCOL_VERSION = 70,
215    INSUFFICIENT_SECURITY = 71,
216    INTERNAL_ERROR = 80,
217    INAPPROPRIATE_FALLBACK = 86,
218    USER_CANCELED = 90,
219    NO_RENEGOTIATION_RESERVED = 100,
220    MISSING_EXTENSION = 109,
221    UNSUPPORTED_EXTENSION = 110,
222    CERTIFICATE_UNOBTAINABLE_RESERVED = 111,
223    UNRECOGNIZED_NAME = 112,
224    BAD_CERTIFICATE_STATUS_RESPONSE = 113,
225    BAD_CERTIFICATE_HASH_VALUE_RESERVED = 114,
226    UNKNOWN_PSK_IDENTITY = 115,
227    CERTIFICATE_REQUIRED = 116,
228    NO_APPLICATION_PROTOCOL = 120,
229);