aws_lc_rs/
error.rs

1// Copyright 2015-2021 Brian Smith.
2// SPDX-License-Identifier: ISC
3// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4// SPDX-License-Identifier: Apache-2.0 OR ISC
5
6//! Error reporting.
7
8extern crate std;
9
10use core::num::TryFromIntError;
11// The Error trait is not in core: https://github.com/rust-lang/rust/issues/103765
12use std::error::Error;
13
14/// An error with absolutely no details.
15///
16/// *aws-lc-rs* uses this unit type as the error type in most of its results
17/// because (a) usually the specific reasons for a failure are obvious or are
18/// not useful to know, and/or (b) providing more details about a failure might
19/// provide a dangerous side channel, and/or (c) it greatly simplifies the
20/// error handling logic.
21///
22/// `Result<T, aws_lc_rs::error::Unspecified>` is mostly equivalent to
23/// `Result<T, ()>`. However, `aws_lc_rs::error::Unspecified` implements
24/// [`std::error::Error`] and users can implement
25/// `From<error::Unspecified>` to map this to their own error types, as
26/// described in [“Error Handling” in the Rust Book](https://doc.rust-lang.org/book/ch09-00-error-handling.html):
27///
28/// ```
29/// use aws_lc_rs::rand::{self, SecureRandom};
30///
31/// enum Error {
32///     CryptoError,
33///
34///     IOError(std::io::Error),
35///     // [...]
36/// }
37///
38/// impl From<aws_lc_rs::error::Unspecified> for Error {
39///     fn from(_: aws_lc_rs::error::Unspecified) -> Self {
40///         Error::CryptoError
41///     }
42/// }
43///
44/// fn eight_random_bytes() -> Result<[u8; 8], Error> {
45///     let rng = rand::SystemRandom::new();
46///     let mut bytes = [0; 8];
47///
48///     // The `From<aws_lc_rs::error::Unspecified>` implementation above makes this
49///     // equivalent to
50///     // `rng.fill(&mut bytes).map_err(|_| Error::CryptoError)?`.
51///     rng.fill(&mut bytes)?;
52///
53///     Ok(bytes)
54/// }
55///
56/// assert!(eight_random_bytes().is_ok());
57/// ```
58///
59/// Experience with using and implementing other crypto libraries like has
60/// shown that sophisticated error reporting facilities often cause significant
61/// bugs themselves, both within the crypto library and within users of the
62/// crypto library. This approach attempts to minimize complexity in the hopes
63/// of avoiding such problems. In some cases, this approach may be too extreme,
64/// and it may be important for an operation to provide some details about the
65/// cause of a failure. Users of *aws-lc-rs* are encouraged to report such cases so
66/// that they can be addressed individually.
67///
68/// [`std::error::Error`]: https://doc.rust-lang.org/std/error/trait.Error.html
69/// [“Error Handling” in the Rust Book]:
70///     https://doc.rust-lang.org/book/first-edition/error-handling.html#the-from-trait
71#[derive(Clone, Copy, Debug, PartialEq, Eq)]
72pub struct Unspecified;
73
74// This is required for the implementation of `std::error::Error`.
75impl core::fmt::Display for Unspecified {
76    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
77        f.write_str("Unspecified")
78    }
79}
80
81impl From<core::array::TryFromSliceError> for Unspecified {
82    fn from(_: core::array::TryFromSliceError) -> Self {
83        Self
84    }
85}
86
87/// An error parsing or validating a key.
88///
89/// The `Display` implementation and `<KeyRejected as Error>::description()`
90/// will return a string that will help you better understand why a key was
91/// rejected change which errors are reported in which situations while
92/// minimizing the likelihood that any applications will be broken.
93///
94/// Here is an incomplete list of reasons a key may be unsupported:
95///
96/// * Invalid or Inconsistent Components: A component of the key has an invalid
97///   value, or the mathematical relationship between two (or more) components
98///   required for a valid key does not hold.
99///
100/// * The encoding of the key is invalid. Perhaps the key isn't in the correct
101///   format; e.g. it may be Base64 ("PEM") encoded, in which case   the Base64
102///   encoding needs to be undone first.
103///
104/// * The encoding includes a versioning mechanism and that mechanism indicates
105///   that the key is encoded in a version of the encoding that isn't supported.
106///   This might happen for multi-prime RSA keys (keys with more than two
107///   private   prime factors), which aren't supported, for example.
108///
109/// * Too small or too Large: One of the primary components of the key is too
110///   small or two large. Too-small keys are rejected for security reasons. Some
111///   unnecessarily large keys are rejected for performance reasons.
112///
113///  * Wrong algorithm: The key is not valid for the algorithm in which it was
114///    being used.
115///
116///  * Unexpected errors: Report this as a bug.
117#[derive(Copy, Clone, Debug, PartialEq)]
118pub struct KeyRejected(&'static str);
119
120impl KeyRejected {
121    /// The value returned from `<Self as std::error::Error>::description()`
122    #[must_use]
123    pub fn description_(&self) -> &'static str {
124        self.0
125    }
126
127    pub(crate) fn inconsistent_components() -> Self {
128        KeyRejected("InconsistentComponents")
129    }
130
131    #[inline]
132    pub(crate) fn invalid_encoding() -> Self {
133        KeyRejected("InvalidEncoding")
134    }
135
136    pub(crate) fn too_small() -> Self {
137        KeyRejected("TooSmall")
138    }
139
140    pub(crate) fn too_large() -> Self {
141        KeyRejected("TooLarge")
142    }
143
144    pub(crate) fn wrong_algorithm() -> Self {
145        KeyRejected("WrongAlgorithm")
146    }
147
148    pub(crate) fn unexpected_error() -> Self {
149        KeyRejected("UnexpectedError")
150    }
151
152    pub(crate) fn unspecified() -> Self {
153        KeyRejected("Unspecified")
154    }
155}
156
157impl Error for KeyRejected {
158    fn description(&self) -> &str {
159        self.description_()
160    }
161
162    fn cause(&self) -> Option<&dyn Error> {
163        None
164    }
165}
166
167impl Error for Unspecified {
168    #[allow(clippy::unnecessary_literal_bound)]
169    fn description(&self) -> &str {
170        "Unspecified"
171    }
172
173    #[inline]
174    fn cause(&self) -> Option<&dyn Error> {
175        None
176    }
177}
178
179impl core::fmt::Display for KeyRejected {
180    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
181        f.write_str(self.description_())
182    }
183}
184
185impl From<KeyRejected> for Unspecified {
186    fn from(_: KeyRejected) -> Self {
187        Unspecified
188    }
189}
190
191impl From<()> for Unspecified {
192    fn from((): ()) -> Self {
193        Unspecified
194    }
195}
196
197impl From<Unspecified> for () {
198    fn from(_: Unspecified) -> Self {}
199}
200
201impl From<()> for KeyRejected {
202    fn from((): ()) -> Self {
203        KeyRejected::unexpected_error()
204    }
205}
206
207#[cfg(any(feature = "ring-sig-verify", feature = "ring-io"))]
208impl From<untrusted::EndOfInput> for Unspecified {
209    fn from(_: untrusted::EndOfInput) -> Self {
210        Unspecified
211    }
212}
213
214impl From<TryFromIntError> for Unspecified {
215    fn from(_: TryFromIntError) -> Self {
216        Unspecified
217    }
218}
219
220impl From<TryFromIntError> for KeyRejected {
221    fn from(_: TryFromIntError) -> Self {
222        KeyRejected::unexpected_error()
223    }
224}
225
226impl From<Unspecified> for KeyRejected {
227    fn from(_: Unspecified) -> Self {
228        Self::unspecified()
229    }
230}
231
232#[allow(deprecated, unused_imports)]
233#[cfg(test)]
234mod tests {
235    use crate::error::KeyRejected;
236    use crate::test;
237    use std::error::Error;
238
239    #[test]
240    fn display_unspecified() {
241        let output = format!("{}", super::Unspecified);
242        assert_eq!("Unspecified", output);
243    }
244
245    #[test]
246    fn unexpected_error() {
247        let key_rejected = super::KeyRejected::from(());
248        assert_eq!("UnexpectedError", key_rejected.description());
249
250        let unspecified = super::Unspecified::from(key_rejected);
251        assert_eq!("Unspecified", unspecified.description());
252
253        #[allow(clippy::redundant_locals)]
254        let unspecified = unspecified;
255        assert_eq!("Unspecified", unspecified.description());
256    }
257
258    #[test]
259    fn std_error() {
260        let key_rejected = KeyRejected::wrong_algorithm();
261        assert!(key_rejected.cause().is_none());
262        assert_eq!("WrongAlgorithm", key_rejected.description());
263
264        let unspecified = super::Unspecified;
265        assert!(unspecified.cause().is_none());
266        assert_eq!("Unspecified", unspecified.description());
267
268        test::compile_time_assert_std_error_error::<KeyRejected>();
269    }
270}