ssh_key/
error.rs

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