Skip to main content

sequoia_keystore/
error.rs

1use std::sync::Arc;
2
3use sequoia_openpgp as openpgp;
4use openpgp::Result;
5use openpgp::packet;
6use openpgp::parse::Parse;
7
8use crate::capnp_relay;
9use crate::capnp_relay::CapnProtoRelay;
10
11use crate::keystore;
12use crate::InaccessibleDecryptionKey;
13use crate::Key;
14use crate::server;
15
16#[derive(thiserror::Error, Debug)]
17/// Errors returned from the keystore.
18// There errors are also defined in keystore_protocol.capnp.  If you
19// add a variant here, then you'll probably need to add it there as
20// well.
21pub enum Error {
22    // keystore_protocol.capnp errors:
23
24    /// A generic error occurred.
25    ///
26    /// The other error variants aren't appropriate for representing
27    /// this error.  The text describes what went wrong.
28    #[error("Error: {}",
29            .0.as_deref().unwrap_or("an unspecified error occurred"))]
30    GenericError(Option<String>),
31
32    /// An unspecified protocol error occurred.
33    #[error("Unspecified protocol error: something went wrong")]
34    ProtocolError,
35
36    /// An end of file condition was reached.
37    ///
38    /// This is also used for iterations to indicate that there are no
39    /// more items.
40    #[error("EOF")]
41    EOF,
42
43    /// An operation couldn't be completed, because a required key is
44    /// inaccessible (unavailable, or locked).
45    ///
46    /// This is returned by `Keystore::decrypt`.  Repeat the operation
47    /// after ensuring that one of the keys is available by prompting
48    /// the user to connect it, or unlocking the key.
49    #[error("Can't decrypt a PKESK, the candidate keys are inaccessible")]
50    InaccessibleDecryptionKey(Vec<InaccessibleDecryptionKey>),
51
52    /// The key cannot be used for decryption.
53    #[error("Key {0} cannot be used for decryption")]
54    NotDecryptionCapable(String),
55
56    /// The key cannot be used for signing.
57    #[error("Key {0} cannot be used for signing")]
58    NotSigningCapable(String),
59
60    /// An internal server error occurred.
61    #[error("Internal server error")]
62    InternalError(String),
63
64    /// The backend doesn't support importing keys using this interface.
65    ///
66    /// Some backends require backend-specific information in order to
67    /// import a key.  For instance, when importing a key to a
68    /// smartcard, the user needs to specify the smartcard, and the
69    /// slot to use on the smartcard.  Rather than try and model these
70    /// parameters using this generic interface, backends should just
71    /// have their own tools.  The text should be a hint that is
72    /// displayed to the user describing how to find the tool.
73    #[error("Can't import keys into the backend: {}",
74            .0.as_deref().unwrap_or("use another tool to import keys \
75                                     into this backend"))]
76    ExternalImportRequired(Option<String>),
77
78    /// The secret key material cannot be exported.
79    ///
80    /// This error is returned by [`Key::export`] if the secret key
81    /// material cannot be exported.  Many backends do not allow
82    /// exporting secret key material.  Some may only allow exporting
83    /// secret key material in admin mode.  The text is a more
84    /// detailed description of why this is not possible, or what the
85    /// user could do to export the secret key material.
86    #[error("Can't export secret key material key: {}",
87            .0.as_deref().unwrap_or("use another tool to import keys \
88                                     into this backend"))]
89    SecretKeyMaterialSealed(Option<String>),
90
91    /// The key doesn't support inline passwords.
92    ///
93    /// Password entry is taken care of by the device managing the
94    /// key.  For instance, the password may be obtained using an
95    /// external PIN pad.
96    #[error("Can't unlock using an inline password{}{}",
97            .0.as_ref().map(|_| ": ").unwrap_or(""),
98            .0.as_deref().map(|msg| msg).unwrap_or(""))]
99    NoInlinePassword(Option<String>),
100
101    /// The key doesn't support getting passwords.
102    ///
103    /// The password must be provided inline.  The password cannot be
104    /// obtained using something like an external PIN pad.
105    #[error("External password entry is not supported{}{}",
106            .0.as_ref().map(|_| ": ").unwrap_or(""),
107            .0.as_deref().map(|msg| msg).unwrap_or(""))]
108    NoExternalPassword(Option<String>),
109
110    // Other errors:
111
112    /// A `capnp::Error` occurred.
113    #[error("Internal RPC error")]
114    RpcError(#[from] capnp::Error),
115
116    // !!! If you add an error here, make sure you update the
117    // following function.
118}
119
120/// Errors returned from the server, which first need to be converted
121/// to the public form, `Error`.
122#[derive(thiserror::Error, Debug)]
123pub(crate) enum ServerError {
124    /// An operation couldn't be completed, because a required key is
125    /// inaccessible (unavailable, or locked).
126    ///
127    /// This is returned by `Keystore::decrypt`.  Repeat the operation
128    /// after ensuring that one of the keys is available by prompting
129    /// the user to connect it, or unlocking the key.
130    #[error("Can't decrypt a PKESK, the candidate keys are inaccessible")]
131    InaccessibleDecryptionKey(Vec<server::InaccessibleDecryptionKey>),
132}
133
134impl Error {
135    /// Converts an error stored in a capnp buffer to our local error
136    /// type.
137    pub(crate) fn from_capnp(relay: Arc<CapnProtoRelay>,
138                             captable: &mut capnp_relay::CapTable,
139                             err: keystore::error::Reader<'_>)
140        -> anyhow::Error
141    {
142        let mut try_from = || {
143            match err.which() {
144                Ok(keystore::error::GenericError(msg)) => {
145                    let msg = msg?;
146                    if msg.is_empty() {
147                        Ok(Error::GenericError(None).into())
148                    } else if let Ok(msg) = msg.to_string() {
149                        Ok(Error::GenericError(Some(msg)).into())
150                    } else {
151                        // Yowzers: we couldn't extract the error
152                        // message.
153                        Ok(Error::GenericError(
154                            Some("Error parsing error message".into())).into())
155                    }
156                },
157                Ok(keystore::error::Protocol(())) => Ok(Error::ProtocolError.into()),
158                Ok(keystore::error::Eof(())) => Ok(Error::EOF.into()),
159                Ok(keystore::error::NotDecryptionCapable(s)) => {
160                    let s = s?.to_string()?;
161                    Ok(Error::NotDecryptionCapable(s).into())
162                }
163                Ok(keystore::error::NotSigningCapable(s)) => {
164                    let s = s?.to_string()?;
165                    Ok(Error::NotSigningCapable(s).into())
166                }
167                Ok(keystore::error::InaccessibleDecryptionKey(keys)) => {
168                    let keys = keys?.into_iter()
169                        .map(|inaccessible_key| {
170                            let key = inaccessible_key.get_key_descriptor()?;
171
172                            let cap = key.get_handle()?;
173
174                            let pk = key.get_public_key()?;
175                            let pk = packet::Key::<packet::key::UnspecifiedParts,
176                                                   packet::key::UnspecifiedRole>
177                                ::from_bytes(pk)?;
178                            let pk = pk.parts_into_public();
179
180                            let pkesk = inaccessible_key.get_pkesk()?;
181                            let pkesk = packet::PKESK::from_bytes(pkesk)?;
182
183                            Ok(InaccessibleDecryptionKey {
184                                key: Key {
185                                    relay: Arc::clone(&relay),
186                                    cap: captable.insert(cap.client),
187                                    key: pk,
188                                },
189                                pkesk: pkesk,
190                            })
191                        })
192                        .collect::<Result<Vec<_>>>()?;
193                    Ok(Error::InaccessibleDecryptionKey(keys).into())
194                }
195                Ok(keystore::error::InternalError(s)) => {
196                    let s = s?.to_string()?;
197                    Ok(Error::InternalError(s).into())
198                }
199                Ok(keystore::error::ExternalImportRequired(msg)) => {
200                    let msg = msg?;
201                    if msg.is_empty() {
202                        Ok(Error::ExternalImportRequired(None).into())
203                    } else if let Ok(msg) = msg.to_string() {
204                        Ok(Error::ExternalImportRequired(Some(msg)).into())
205                    } else {
206                        // Yowzers: we couldn't extract the error
207                        // message.
208                        Ok(Error::ExternalImportRequired(None).into())
209                    }
210                }
211                Ok(keystore::error::SecretKeyMaterialSealed(msg)) => {
212                    let msg = msg?;
213                    if msg.is_empty() {
214                        Ok(Error::SecretKeyMaterialSealed(None).into())
215                    } else if let Ok(msg) = msg.to_string() {
216                        Ok(Error::SecretKeyMaterialSealed(Some(msg)).into())
217                    } else {
218                        // Yowzers: we couldn't extract the error
219                        // message.
220                        Ok(Error::SecretKeyMaterialSealed(None).into())
221                    }
222                }
223                Ok(keystore::error::NoInlinePassword(msg)) => {
224                    let msg = msg?;
225                    if msg.is_empty() {
226                        Ok(Error::NoInlinePassword(None).into())
227                    } else if let Ok(msg) = msg.to_string() {
228                        Ok(Error::NoInlinePassword(Some(msg)).into())
229                    } else {
230                        // Yowzers: we couldn't extract the error
231                        // message.
232                        Ok(Error::NoInlinePassword(None).into())
233                    }
234                }
235                Ok(keystore::error::NoExternalPassword(msg)) => {
236                    let msg = msg?;
237                    if msg.is_empty() {
238                        Ok(Error::NoExternalPassword(None).into())
239                    } else if let Ok(msg) = msg.to_string() {
240                        Ok(Error::NoExternalPassword(Some(msg)).into())
241                    } else {
242                        // Yowzers: we couldn't extract the error
243                        // message.
244                        Ok(Error::NoExternalPassword(None).into())
245                    }
246                }
247                Err(err) => {
248                    // The error is incorrectly formatted.  Turn that into
249                    // a protocol error.
250                    log::debug!("Protocol violation while parsing error: {}",
251                                err);
252                    Ok(Error::ProtocolError.into())
253                }
254            }
255        };
256
257        match try_from() {
258            Ok(err) => err,
259            Err(err) => err,
260        }
261    }
262}
263
264impl keystore::error::Builder<'_> {
265    /// Sets the error on the wire from an `anyhow::Error`.
266    ///
267    /// This converts an error to the wire format.  This only handles
268    /// the local error type, [`keystore::error::Error]`; everything
269    /// else is mapped to [`Error::UnspecifiedError`].
270    ///
271    /// Note: client code never needs this.
272    pub(crate) fn from_anyhow(&mut self, err: &anyhow::Error) {
273        match err.downcast_ref::<ServerError>() {
274            Some(ServerError::InaccessibleDecryptionKey(keys)) => {
275                let mut keys_wire = self
276                    .reborrow()
277                    .init_inaccessible_decryption_key(keys.len() as u32);
278                for (i, key) in keys.into_iter().enumerate() {
279                    key.serialize(keys_wire.reborrow().get(i as u32));
280                }
281
282                return;
283            }
284            None => (),
285        }
286
287        match err.downcast_ref::<Error>() {
288            Some(Error::GenericError(msg)) =>
289                self.set_generic_error(msg.as_deref().unwrap_or("")),
290            Some(Error::ProtocolError) =>
291                self.set_protocol(()),
292            Some(Error::EOF) =>
293                self.set_eof(()),
294            Some(Error::NotDecryptionCapable(fpr)) => {
295                let mut builder = self.reborrow()
296                    .init_not_decryption_capable(fpr.len() as u32);
297                builder.push_str(&fpr);
298            }
299            Some(Error::NotSigningCapable(fpr)) => {
300                let mut builder = self.reborrow()
301                    .init_not_signing_capable(fpr.len() as u32);
302                builder.push_str(&fpr);
303            }
304            Some(Error::InaccessibleDecryptionKey(_keys)) => {
305                // We never have to serialize an Error::Inaccessible:
306                // that's a client variant.  On the server, we use
307                // ServerError::Inaccessible.
308                log::debug!("Invalid attempt to serialize Error::InaccessibleDecryptionKey");
309                self.set_internal_error("InaccessibleDecryptionKey shouldn't \
310                                         be returned in this context");
311            }
312            Some(Error::InternalError(err)) => {
313                let mut builder = self.reborrow()
314                    .init_internal_error(err.len() as u32);
315                builder.push_str(&err);
316            }
317            Some(Error::ExternalImportRequired(msg)) => {
318                self.set_external_import_required(msg.as_deref().unwrap_or(""));
319            }
320            Some(Error::SecretKeyMaterialSealed(msg)) => {
321                self.set_secret_key_material_sealed(msg.as_deref().unwrap_or(""));
322            }
323            Some(Error::NoInlinePassword(msg)) => {
324                self.set_no_inline_password(msg.as_deref().unwrap_or(""));
325            }
326            Some(Error::NoExternalPassword(msg)) => {
327                self.set_no_external_password(msg.as_deref().unwrap_or(""));
328            }
329            Some(Error::RpcError(_err)) =>
330                self.set_protocol(()),
331            None => {
332                // This is the best we can do.
333                self.set_generic_error(err.to_string());
334            }
335        }
336    }
337}
338
339impl From<capnp::NotInSchema> for Error {
340    fn from(_: capnp::NotInSchema) -> Self {
341        Error::ProtocolError
342    }
343}