sequoia-keystore 0.3.0

Sequoia's private key store server.
Documentation
use std::sync::Arc;
use std::sync::Mutex;

use sequoia_openpgp as openpgp;
use openpgp::Result;
use openpgp::packet;
use openpgp::parse::Parse;

use crate::capnp_relay;
use crate::capnp_relay::CapnProtoRelay;

use crate::keystore;
use crate::InaccessibleDecryptionKey;
use crate::Key;
use crate::server;

#[derive(thiserror::Error, Debug)]
/// Errors returned from the keystore.
// There errors are also defined in keystore_protocol.capnp.  If you
// add a variant here, then you'll probably need to add it there as
// well.
pub enum Error {
    // keystore_protocol.capnp errors:

    /// A requested key was not found.
    #[error("Unspecified error")]
    UnspecifiedError,

    #[error("Unspecified protocol error")]
    ProtocolError,

    /// An end of file condition was reached.
    ///
    /// This is also used for iterations to indicate that there are no
    /// more items.
    #[error("EOF")]
    EOF,

    /// An operation couldn't be completed, because a required key is
    /// inaccessible (unavailable, or locked).
    ///
    /// This is returned by `Keystore::decrypt`.  Repeat the operation
    /// after ensuring that one of the keys is available by prompting
    /// the user to connect it, or unlocking the key.
    #[error("Can't decrypt a PKESK, the candidate keys are inaccessible")]
    InaccessibleDecryptionKey(Vec<InaccessibleDecryptionKey>),

    /// The key cannot be used for decryption.
    #[error("Key {0} cannot be used for decryption")]
    NotDecryptionCapable(String),

    /// The key cannot be used for signing.
    #[error("Key {0} cannot be used for signing")]
    NotSigningCapable(String),

    /// An internal server error occurred.
    #[error("Internal server error")]
    InternalError(String),

    // Other errors:

    /// A `capnp::Error` occurred.
    #[error("Internal RPC error")]
    RpcError(#[from] capnp::Error),

    // !!! If you add an error here, make sure you update the
    // following function.
}

/// Errors returned from the server, which first need to be converted
/// to the public form, `Error`.
#[derive(thiserror::Error, Debug)]
pub(crate) enum ServerError {
    /// An operation couldn't be completed, because a required key is
    /// inaccessible (unavailable, or locked).
    ///
    /// This is returned by `Keystore::decrypt`.  Repeat the operation
    /// after ensuring that one of the keys is available by prompting
    /// the user to connect it, or unlocking the key.
    #[error("Can't decrypt a PKESK, the candidate keys are inaccessible")]
    InaccessibleDecryptionKey(Vec<server::InaccessibleDecryptionKey>),
}

impl Error {
    /// Converts an error stored in a capnp buffer to our local error
    /// type.
    pub(crate) fn from_capnp(relay: Arc<Mutex<CapnProtoRelay>>,
                             captable: &mut capnp_relay::CapTable,
                             err: keystore::error::Reader<'_>)
        -> anyhow::Error
    {
        let mut try_from = || {
            match err.which() {
                Ok(keystore::error::Unspecified(())) => Ok(Error::UnspecifiedError.into()),
                Ok(keystore::error::Protocol(())) => Ok(Error::ProtocolError.into()),
                Ok(keystore::error::Eof(())) => Ok(Error::EOF.into()),
                Ok(keystore::error::NotDecryptionCapable(s)) => {
                    let s = s?.to_string()?;
                    Ok(Error::NotDecryptionCapable(s).into())
                }
                Ok(keystore::error::NotSigningCapable(s)) => {
                    let s = s?.to_string()?;
                    Ok(Error::NotSigningCapable(s).into())
                }
                Ok(keystore::error::InaccessibleDecryptionKey(keys)) => {
                    let keys = keys?.into_iter()
                        .map(|inaccessible_key| {
                            let key = inaccessible_key.get_key_descriptor()?;

                            let cap = key.get_handle()?;

                            let pk = key.get_public_key()?;
                            let pk = packet::Key::<packet::key::UnspecifiedParts,
                                                   packet::key::UnspecifiedRole>
                                ::from_bytes(pk)?;
                            let pk = pk.parts_into_public();

                            let pkesk = inaccessible_key.get_pkesk()?;
                            let pkesk = packet::PKESK::from_bytes(pkesk)?;

                            Ok(InaccessibleDecryptionKey {
                                key: Key {
                                    relay: Arc::clone(&relay),
                                    cap: captable.insert(cap.client),
                                    key: pk,
                                },
                                pkesk: pkesk,
                            })
                        })
                        .collect::<Result<Vec<_>>>()?;
                    Ok(Error::InaccessibleDecryptionKey(keys).into())
                }
                Ok(keystore::error::InternalError(s)) => {
                    let s = s?.to_string()?;
                    Ok(Error::InternalError(s).into())
                }
                Err(err) => {
                    // The error is incorrectly formatted.  Turn that into
                    // a protocol error.
                    log::debug!("Protocol violation while parsing error: {}",
                                err);
                    Ok(Error::ProtocolError.into())
                }
            }
        };

        match try_from() {
            Ok(err) => err,
            Err(err) => err,
        }
    }
}

impl keystore::error::Builder<'_> {
    /// Sets the error on the wire from an `anyhow::Error`.
    ///
    /// This converts an error to the wire format.  This only handles
    /// the local error type, [`keystore::error::Error]`; everything
    /// else is mapped to [`Error::UnspecifiedError`].
    ///
    /// Note: client code never needs this.
    pub(crate) fn from_anyhow(&mut self, err: &anyhow::Error) {
        match err.downcast_ref::<ServerError>() {
            Some(ServerError::InaccessibleDecryptionKey(keys)) => {
                let mut keys_wire = self
                    .reborrow()
                    .init_inaccessible_decryption_key(keys.len() as u32);
                for (i, key) in keys.into_iter().enumerate() {
                    key.serialize(keys_wire.reborrow().get(i as u32));
                }

                return;
            }
            None => (),
        }

        match err.downcast_ref::<Error>() {
            Some(Error::UnspecifiedError) =>
                self.set_unspecified(()),
            Some(Error::ProtocolError) =>
                self.set_protocol(()),
            Some(Error::EOF) =>
                self.set_eof(()),
            Some(Error::NotDecryptionCapable(fpr)) => {
                let mut builder = self.reborrow()
                    .init_not_decryption_capable(fpr.len() as u32);
                builder.push_str(&fpr);
            }
            Some(Error::NotSigningCapable(fpr)) => {
                let mut builder = self.reborrow()
                    .init_not_signing_capable(fpr.len() as u32);
                builder.push_str(&fpr);
            }
            Some(Error::InaccessibleDecryptionKey(_keys)) => {
                // We never have to serialize an Error::Inaccessible:
                // that's a client variant.  On the server, we use
                // ServerError::Inaccessible.
                log::debug!("Invalid attempt to serialize Error::InaccessibleDecryptionKey");
                self.set_unspecified(());
            }
            Some(Error::InternalError(err)) => {
                let mut builder = self.reborrow()
                    .init_internal_error(err.len() as u32);
                builder.push_str(&err);
            }
            Some(Error::RpcError(_err)) =>
                self.set_protocol(()),
            None => {
                // This is the best we can do.
                self.set_unspecified(());
            }
        }
    }
}

impl From<capnp::NotInSchema> for Error {
    fn from(_: capnp::NotInSchema) -> Self {
        Error::ProtocolError
    }
}