ssh_key/
error.rs

1//! Error types
2
3use crate::Algorithm;
4use core::fmt;
5
6#[cfg(feature = "alloc")]
7use crate::certificate;
8
9#[cfg(feature = "ppk")]
10use crate::ppk::PpkParseError;
11
12#[cfg(doc)]
13use crate::HashAlg;
14
15/// Result type with `ssh-key`'s [`Error`] as the error type.
16pub type Result<T> = core::result::Result<T, Error>;
17
18/// Error type.
19#[derive(Clone, Debug, Eq, PartialEq)]
20#[non_exhaustive]
21pub enum Error {
22    /// Unknown algorithm.
23    ///
24    /// This is returned when an algorithm is completely unknown to this crate.
25    AlgorithmUnknown,
26
27    /// Unsupported algorithm.
28    ///
29    /// This is typically returned when an algorithm is recognized, but the
30    /// relevant crate features to support it haven't been enabled.
31    ///
32    /// It may also be returned in the event an algorithm is inappropriate for
33    /// a given usage pattern or context.
34    AlgorithmUnsupported {
35        /// Algorithm identifier.
36        algorithm: Algorithm,
37    },
38
39    /// Certificate field is invalid or already set.
40    #[cfg(feature = "alloc")]
41    CertificateFieldInvalid(certificate::Field),
42
43    /// Certificate validation failed.
44    CertificateValidation,
45
46    /// Cryptographic errors.
47    Crypto,
48
49    /// Cannot perform operation on decrypted private key.
50    Decrypted,
51
52    /// ECDSA key encoding errors.
53    #[cfg(feature = "ecdsa")]
54    Ecdsa(sec1::Error),
55
56    /// Encoding errors.
57    Encoding(encoding::Error),
58
59    /// Cannot perform operation on encrypted private key.
60    Encrypted,
61
62    /// Other format encoding errors.
63    FormatEncoding,
64
65    /// Provided hash is the wrong size for a given [`HashAlg`].
66    HashSize,
67
68    /// Input/output errors.
69    #[cfg(feature = "std")]
70    Io(std::io::ErrorKind),
71
72    /// Namespace invalid.
73    Namespace,
74
75    /// Public key is incorrect.
76    PublicKey,
77
78    /// PuTTY format parsing errors.
79    #[cfg(feature = "ppk")]
80    Ppk(PpkParseError),
81
82    /// Random number generator errors.
83    #[cfg(feature = "rand_core")]
84    RngFailure,
85
86    /// Invalid timestamp (e.g. in a certificate)
87    Time,
88
89    /// Unexpected trailing data at end of message.
90    TrailingData {
91        /// Number of bytes of remaining data at end of message.
92        remaining: usize,
93    },
94
95    /// Unsupported version.
96    Version {
97        /// Version number.
98        number: u32,
99    },
100}
101
102impl fmt::Display for Error {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        match self {
105            Error::AlgorithmUnknown => write!(f, "unknown algorithm"),
106            Error::AlgorithmUnsupported { algorithm } => {
107                write!(f, "unsupported algorithm: {algorithm}")
108            }
109            #[cfg(feature = "alloc")]
110            Error::CertificateFieldInvalid(field) => {
111                write!(f, "certificate field invalid: {field}")
112            }
113            Error::CertificateValidation => write!(f, "certificate validation failed"),
114            Error::Crypto => write!(f, "cryptographic error"),
115            Error::Decrypted => write!(f, "private key is already decrypted"),
116            #[cfg(feature = "ecdsa")]
117            Error::Ecdsa(err) => write!(f, "ECDSA encoding error: {err}"),
118            Error::Encoding(err) => write!(f, "{err}"),
119            Error::Encrypted => write!(f, "private key is encrypted"),
120            Error::FormatEncoding => write!(f, "format encoding error"),
121            Error::HashSize => write!(f, "hash is the wrong size for the given algorithm"),
122            #[cfg(feature = "std")]
123            Error::Io(err) => write!(f, "I/O error: {}", std::io::Error::from(*err)),
124            Error::Namespace => write!(f, "namespace invalid"),
125            #[cfg(feature = "ppk")]
126            Error::Ppk(err) => write!(f, "PPK parsing error: {err}"),
127            Error::PublicKey => write!(f, "public key is incorrect"),
128            #[cfg(feature = "rand_core")]
129            Error::RngFailure => write!(f, "random number generator failure"),
130            Error::Time => write!(f, "invalid time"),
131            Error::TrailingData { remaining } => write!(
132                f,
133                "unexpected trailing data at end of message ({remaining} bytes)",
134            ),
135            Error::Version { number: version } => write!(f, "version unsupported: {version}"),
136        }
137    }
138}
139
140impl core::error::Error for Error {
141    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
142        match self {
143            #[cfg(feature = "ecdsa")]
144            Self::Ecdsa(err) => Some(err),
145            Self::Encoding(err) => Some(err),
146            _ => None,
147        }
148    }
149}
150
151impl From<cipher::Error> for Error {
152    fn from(_: cipher::Error) -> Error {
153        Error::Crypto
154    }
155}
156
157impl From<core::array::TryFromSliceError> for Error {
158    fn from(_: core::array::TryFromSliceError) -> Error {
159        Error::Encoding(encoding::Error::Length)
160    }
161}
162
163impl From<core::str::Utf8Error> for Error {
164    fn from(err: core::str::Utf8Error) -> Error {
165        Error::Encoding(err.into())
166    }
167}
168
169impl From<encoding::Error> for Error {
170    fn from(err: encoding::Error) -> Error {
171        Error::Encoding(err)
172    }
173}
174
175impl From<encoding::LabelError> for Error {
176    fn from(err: encoding::LabelError) -> Error {
177        Error::Encoding(err.into())
178    }
179}
180
181impl From<encoding::base64::Error> for Error {
182    fn from(err: encoding::base64::Error) -> Error {
183        Error::Encoding(err.into())
184    }
185}
186
187impl From<encoding::pem::Error> for Error {
188    fn from(err: encoding::pem::Error) -> Error {
189        Error::Encoding(err.into())
190    }
191}
192
193#[cfg(not(feature = "alloc"))]
194impl From<signature::Error> for Error {
195    fn from(_: signature::Error) -> Error {
196        Error::Crypto
197    }
198}
199
200#[cfg(feature = "alloc")]
201impl From<signature::Error> for Error {
202    fn from(err: signature::Error) -> Error {
203        use core::error::Error as _;
204
205        err.source()
206            .and_then(|source| source.downcast_ref().cloned())
207            .unwrap_or(Error::Crypto)
208    }
209}
210
211#[cfg(not(feature = "alloc"))]
212impl From<Error> for signature::Error {
213    fn from(_: Error) -> signature::Error {
214        signature::Error::new()
215    }
216}
217
218#[cfg(feature = "alloc")]
219impl From<Error> for signature::Error {
220    fn from(err: Error) -> signature::Error {
221        signature::Error::from_source(err)
222    }
223}
224
225#[cfg(feature = "alloc")]
226impl From<alloc::string::FromUtf8Error> for Error {
227    fn from(err: alloc::string::FromUtf8Error) -> Error {
228        Error::Encoding(err.into())
229    }
230}
231
232#[cfg(feature = "ecdsa")]
233impl From<sec1::Error> for Error {
234    fn from(err: sec1::Error) -> Error {
235        Error::Ecdsa(err)
236    }
237}
238
239#[cfg(any(feature = "dsa", feature = "rsa"))]
240impl From<encoding::bigint::DecodeError> for Error {
241    fn from(err: encoding::bigint::DecodeError) -> Error {
242        encoding::Error::from(err).into()
243    }
244}
245
246#[cfg(feature = "rsa")]
247impl From<rsa::errors::Error> for Error {
248    fn from(_: rsa::errors::Error) -> Error {
249        Error::Crypto
250    }
251}
252
253#[cfg(feature = "std")]
254impl From<std::io::Error> for Error {
255    fn from(err: std::io::Error) -> Error {
256        Error::Io(err.kind())
257    }
258}
259
260#[cfg(feature = "std")]
261impl From<std::time::SystemTimeError> for Error {
262    fn from(_: std::time::SystemTimeError) -> Error {
263        Error::Time
264    }
265}