1 // Copyright 2018 Developers of the Rand project.
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8 use core::{fmt, num::NonZeroU32};
9 
10 /// A small and `no_std` compatible error type
11 ///
12 /// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and
13 /// if so, which error code the OS gave the application. If such an error is
14 /// encountered, please consult with your system documentation.
15 ///
16 /// Internally this type is a NonZeroU32, with certain values reserved for
17 /// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`].
18 ///
19 /// *If this crate's `"std"` Cargo feature is enabled*, then:
20 /// - [`getrandom::Error`][Error] implements
21 ///   [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html)
22 /// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements
23 ///   [`From<getrandom::Error>`](https://doc.rust-lang.org/std/convert/trait.From.html).
24 #[derive(Copy, Clone, Eq, PartialEq)]
25 pub struct Error(NonZeroU32);
26 
internal_error(n: u16) -> Error27 const fn internal_error(n: u16) -> Error {
28     // SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32.
29     let code = Error::INTERNAL_START + (n as u32);
30     Error(unsafe { NonZeroU32::new_unchecked(code) })
31 }
32 
33 impl Error {
34     /// This target/platform is not supported by `getrandom`.
35     pub const UNSUPPORTED: Error = internal_error(0);
36     /// The platform-specific `errno` returned a non-positive value.
37     pub const ERRNO_NOT_POSITIVE: Error = internal_error(1);
38     /// Call to iOS [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) failed.
39     pub const IOS_SEC_RANDOM: Error = internal_error(3);
40     /// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed.
41     pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4);
42     /// RDRAND instruction failed due to a hardware issue.
43     pub const FAILED_RDRAND: Error = internal_error(5);
44     /// RDRAND instruction unsupported on this target.
45     pub const NO_RDRAND: Error = internal_error(6);
46     /// The browser does not have support for `self.crypto`.
47     pub const WEB_CRYPTO: Error = internal_error(7);
48     /// The browser does not have support for `crypto.getRandomValues`.
49     pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8);
50     /// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized).
51     pub const VXWORKS_RAND_SECURE: Error = internal_error(11);
52     /// NodeJS does not have support for the `crypto` module.
53     pub const NODE_CRYPTO: Error = internal_error(12);
54     /// NodeJS does not have support for `crypto.randomFillSync`.
55     pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13);
56 
57     /// Codes below this point represent OS Errors (i.e. positive i32 values).
58     /// Codes at or above this point, but below [`Error::CUSTOM_START`] are
59     /// reserved for use by the `rand` and `getrandom` crates.
60     pub const INTERNAL_START: u32 = 1 << 31;
61 
62     /// Codes at or above this point can be used by users to define their own
63     /// custom errors.
64     pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30);
65 
66     /// Extract the raw OS error code (if this error came from the OS)
67     ///
68     /// This method is identical to [`std::io::Error::raw_os_error()`][1], except
69     /// that it works in `no_std` contexts. If this method returns `None`, the
70     /// error value can still be formatted via the `Display` implementation.
71     ///
72     /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error
73     #[inline]
raw_os_error(self) -> Option<i32>74     pub fn raw_os_error(self) -> Option<i32> {
75         if self.0.get() < Self::INTERNAL_START {
76             Some(self.0.get() as i32)
77         } else {
78             None
79         }
80     }
81 
82     /// Extract the bare error code.
83     ///
84     /// This code can either come from the underlying OS, or be a custom error.
85     /// Use [`Error::raw_os_error()`] to disambiguate.
86     #[inline]
code(self) -> NonZeroU3287     pub const fn code(self) -> NonZeroU32 {
88         self.0
89     }
90 }
91 
92 cfg_if! {
93     if #[cfg(unix)] {
94         fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> {
95             let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char;
96             if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 {
97                 return None;
98             }
99 
100             // Take up to trailing null byte
101             let n = buf.len();
102             let idx = buf.iter().position(|&b| b == 0).unwrap_or(n);
103             core::str::from_utf8(&buf[..idx]).ok()
104         }
105     } else if #[cfg(target_os = "wasi")] {
106         fn os_err(errno: i32, _buf: &mut [u8]) -> Option<wasi::Error> {
107             wasi::Error::from_raw_error(errno as _)
108         }
109     } else {
110         fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
111             None
112         }
113     }
114 }
115 
116 impl fmt::Debug for Error {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result117     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118         let mut dbg = f.debug_struct("Error");
119         if let Some(errno) = self.raw_os_error() {
120             dbg.field("os_error", &errno);
121             let mut buf = [0u8; 128];
122             if let Some(err) = os_err(errno, &mut buf) {
123                 dbg.field("description", &err);
124             }
125         } else if let Some(desc) = internal_desc(*self) {
126             dbg.field("internal_code", &self.0.get());
127             dbg.field("description", &desc);
128         } else {
129             dbg.field("unknown_code", &self.0.get());
130         }
131         dbg.finish()
132     }
133 }
134 
135 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result136     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137         if let Some(errno) = self.raw_os_error() {
138             let mut buf = [0u8; 128];
139             match os_err(errno, &mut buf) {
140                 Some(err) => err.fmt(f),
141                 None => write!(f, "OS Error: {}", errno),
142             }
143         } else if let Some(desc) = internal_desc(*self) {
144             f.write_str(desc)
145         } else {
146             write!(f, "Unknown Error: {}", self.0.get())
147         }
148     }
149 }
150 
151 impl From<NonZeroU32> for Error {
from(code: NonZeroU32) -> Self152     fn from(code: NonZeroU32) -> Self {
153         Self(code)
154     }
155 }
156 
internal_desc(error: Error) -> Option<&'static str>157 fn internal_desc(error: Error) -> Option<&'static str> {
158     match error {
159         Error::UNSUPPORTED => Some("getrandom: this target is not supported"),
160         Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"),
161         Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"),
162         Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"),
163         Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
164         Error::NO_RDRAND => Some("RDRAND: instruction not supported"),
165         Error::WEB_CRYPTO => Some("Web API self.crypto is unavailable"),
166         Error::WEB_GET_RANDOM_VALUES => Some("Web API crypto.getRandomValues is unavailable"),
167         Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"),
168         Error::NODE_CRYPTO => Some("Node.js crypto module is unavailable"),
169         Error::NODE_RANDOM_FILL_SYNC => Some("Node.js API crypto.randomFillSync is unavailable"),
170         _ => None,
171     }
172 }
173 
174 #[cfg(test)]
175 mod tests {
176     use super::Error;
177     use core::mem::size_of;
178 
179     #[test]
test_size()180     fn test_size() {
181         assert_eq!(size_of::<Error>(), 4);
182         assert_eq!(size_of::<Result<(), Error>>(), 4);
183     }
184 }
185