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}