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