Skip to main content

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    /// Signature errors.
87    Signature,
88
89    /// Invalid timestamp (e.g. in a certificate)
90    Time,
91
92    /// Unexpected trailing data at end of message.
93    TrailingData {
94        /// Number of bytes of remaining data at end of message.
95        remaining: usize,
96    },
97
98    /// Unsupported version.
99    Version {
100        /// Version number.
101        number: u32,
102    },
103}
104
105impl fmt::Display for Error {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        match self {
108            Error::AlgorithmUnknown => write!(f, "unknown algorithm"),
109            Error::AlgorithmUnsupported { algorithm } => {
110                write!(f, "unsupported algorithm: {algorithm}")
111            }
112            #[cfg(feature = "alloc")]
113            Error::CertificateFieldInvalid(field) => {
114                write!(f, "certificate field invalid: {field}")
115            }
116            Error::CertificateValidation => write!(f, "certificate validation failed"),
117            Error::Crypto => write!(f, "cryptographic error"),
118            Error::Decrypted => write!(f, "private key is already decrypted"),
119            #[cfg(feature = "ecdsa")]
120            Error::Ecdsa(err) => write!(f, "ECDSA encoding error: {err}"),
121            Error::Encoding(err) => write!(f, "{err}"),
122            Error::Encrypted => write!(f, "private key is encrypted"),
123            Error::FormatEncoding => write!(f, "format encoding error"),
124            Error::HashSize => write!(f, "hash is the wrong size for the given algorithm"),
125            #[cfg(feature = "std")]
126            Error::Io(err) => write!(f, "I/O error: {}", std::io::Error::from(*err)),
127            Error::Namespace => write!(f, "namespace invalid"),
128            #[cfg(feature = "ppk")]
129            Error::Ppk(err) => write!(f, "PPK parsing error: {err}"),
130            Error::PublicKey => write!(f, "public key is incorrect"),
131            #[cfg(feature = "rand_core")]
132            Error::RngFailure => write!(f, "random number generator failure"),
133            Error::Signature => write!(f, "signature error"),
134            Error::Time => write!(f, "invalid time"),
135            Error::TrailingData { remaining } => write!(
136                f,
137                "unexpected trailing data at end of message ({remaining} bytes)",
138            ),
139            Error::Version { number: version } => write!(f, "version unsupported: {version}"),
140        }
141    }
142}
143
144impl core::error::Error for Error {
145    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
146        match self {
147            #[cfg(feature = "ecdsa")]
148            Self::Ecdsa(err) => Some(err),
149            Self::Encoding(err) => Some(err),
150            _ => None,
151        }
152    }
153}
154
155impl From<cipher::Error> for Error {
156    fn from(_: cipher::Error) -> Error {
157        Error::Crypto
158    }
159}
160
161impl From<core::array::TryFromSliceError> for Error {
162    fn from(_: core::array::TryFromSliceError) -> Error {
163        Error::Encoding(encoding::Error::Length)
164    }
165}
166
167impl From<core::str::Utf8Error> for Error {
168    fn from(err: core::str::Utf8Error) -> Error {
169        Error::Encoding(err.into())
170    }
171}
172
173impl From<encoding::Error> for Error {
174    fn from(err: encoding::Error) -> Error {
175        Error::Encoding(err)
176    }
177}
178
179impl From<encoding::LabelError> for Error {
180    fn from(err: encoding::LabelError) -> Error {
181        Error::Encoding(err.into())
182    }
183}
184
185impl From<encoding::base64::Error> for Error {
186    fn from(err: encoding::base64::Error) -> Error {
187        Error::Encoding(err.into())
188    }
189}
190
191impl From<encoding::pem::Error> for Error {
192    fn from(err: encoding::pem::Error) -> Error {
193        Error::Encoding(err.into())
194    }
195}
196
197#[cfg(not(feature = "alloc"))]
198impl From<signature::Error> for Error {
199    fn from(_: signature::Error) -> Error {
200        Error::Signature
201    }
202}
203
204#[cfg(feature = "alloc")]
205impl From<signature::Error> for Error {
206    fn from(err: signature::Error) -> Error {
207        use core::error::Error as _;
208
209        err.source()
210            .and_then(|source| source.downcast_ref().cloned())
211            .unwrap_or(Error::Signature)
212    }
213}
214
215#[cfg(not(feature = "alloc"))]
216impl From<Error> for signature::Error {
217    fn from(_: Error) -> signature::Error {
218        signature::Error::new()
219    }
220}
221
222#[cfg(feature = "alloc")]
223impl From<Error> for signature::Error {
224    fn from(err: Error) -> signature::Error {
225        signature::Error::from_source(err)
226    }
227}
228
229#[cfg(feature = "alloc")]
230impl From<alloc::string::FromUtf8Error> for Error {
231    fn from(err: alloc::string::FromUtf8Error) -> Error {
232        Error::Encoding(err.into())
233    }
234}
235
236#[cfg(feature = "ecdsa")]
237impl From<sec1::Error> for Error {
238    fn from(err: sec1::Error) -> Error {
239        Error::Ecdsa(err)
240    }
241}
242
243#[cfg(any(feature = "dsa", feature = "rsa"))]
244impl From<encoding::bigint::DecodeError> for Error {
245    fn from(err: encoding::bigint::DecodeError) -> Error {
246        encoding::Error::from(err).into()
247    }
248}
249
250#[cfg(feature = "rsa")]
251impl From<rsa::errors::Error> for Error {
252    fn from(_: rsa::errors::Error) -> Error {
253        Error::Crypto
254    }
255}
256
257#[cfg(feature = "std")]
258impl From<std::io::Error> for Error {
259    fn from(err: std::io::Error) -> Error {
260        Error::Io(err.kind())
261    }
262}
263
264#[cfg(feature = "std")]
265impl From<std::time::SystemTimeError> for Error {
266    fn from(_: std::time::SystemTimeError) -> Error {
267        Error::Time
268    }
269}