aranya-crypto 0.15.0

The Aranya Cryptography Engine
Documentation
//! Wrapped cryptographic key storage.

use core::fmt::{self, Display};

use crate::{
    engine::{Engine, UnwrappedKey, WrappedKey},
    id::BaseId,
};

pub mod fs_keystore;
pub mod memstore;

/// Stores wrapped secret key material.
pub trait KeyStore {
    /// The error returned by the trait methods.
    type Error: Error;

    /// A vacant entry.
    type Vacant<'a, T: WrappedKey>: Vacant<T, Error = Self::Error>;

    /// An occupied entry.
    type Occupied<'a, T: WrappedKey>: Occupied<T, Error = Self::Error>;

    /// Accesses a particular entry.
    fn entry<T: WrappedKey>(&mut self, id: BaseId) -> Result<Entry<'_, Self, T>, Self::Error>;

    /// Retrieves a stored `WrappedKey`.
    fn get<T: WrappedKey>(&self, id: BaseId) -> Result<Option<T>, Self::Error>;

    /// Stores a `WrappedKey`.
    ///
    /// It is an error if the key already exists.
    fn try_insert<T: WrappedKey>(&mut self, id: BaseId, key: T) -> Result<(), Self::Error> {
        match self.entry(id)? {
            Entry::Vacant(v) => v.insert(key),
            Entry::Occupied(_) => Err(<Self as KeyStore>::Error::new(
                ErrorKind::AlreadyExists,
                DuplicateKey,
            )),
        }
    }

    /// Retrieves and removes a stored `WrappedKey`.
    fn remove<T: WrappedKey>(&mut self, id: BaseId) -> Result<Option<T>, Self::Error> {
        match self.entry(id)? {
            Entry::Vacant(_) => Ok(None),
            Entry::Occupied(v) => Ok(Some(v.remove()?)),
        }
    }
}

/// A view into a [`KeyStore`] entry.
pub enum Entry<'a, S, T>
where
    S: KeyStore + ?Sized,
    T: WrappedKey,
{
    /// A vacant entry.
    Vacant(S::Vacant<'a, T>),
    /// An occupied entry.
    Occupied(S::Occupied<'a, T>),
}

/// A vacant entry.
pub trait Vacant<T: WrappedKey> {
    /// The error returned by [`insert`][Self::insert].
    type Error: Error;

    /// Inserts the entry.
    fn insert(self, key: T) -> Result<(), Self::Error>;
}

/// An occupied entry.
pub trait Occupied<T: WrappedKey> {
    /// The error returned by [`get`][Self::get] and
    /// [`remove`][Self::remove].
    type Error: Error;

    /// Retrieves the entry.
    fn get(&self) -> Result<T, Self::Error>;
    /// Retrieves and removes the entry.
    fn remove(self) -> Result<T, Self::Error>;
}

#[derive(Debug, thiserror::Error)]
#[error("key already exists")]
struct DuplicateKey;

/// An error returned by [`KeyStore`].
pub trait Error: core::error::Error + Send + Sync + 'static + Sized {
    /// Creates a new [`Error`].
    fn new<E>(kind: ErrorKind, err: E) -> Self
    where
        E: core::error::Error + Send + Sync + 'static;

    /// Shorthand for [`new`][Self::new] with
    /// [`ErrorKind::Other`].
    fn other<E>(err: E) -> Self
    where
        E: core::error::Error + Send + Sync + 'static,
    {
        Self::new(ErrorKind::Other, err)
    }

    /// Identifies the type of error.
    fn kind(&self) -> ErrorKind;
}

/// Categories of errors.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum ErrorKind {
    /// The entry already exists.
    AlreadyExists,
    /// Any other error.
    Other,
}

impl Display for ErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(match self {
            Self::AlreadyExists => "key entry already exists",
            Self::Other => "some keystore error occurred",
        })
    }
}

/// An extension trait.
pub trait KeyStoreExt: KeyStore {
    /// Wraps and inserts the key.
    fn insert_key<E, K>(&mut self, eng: &E, key: K) -> Result<K::Id, Self::Error>
    where
        E: Engine,
        K: UnwrappedKey<E::CS>;
    /// Retrieves and unwraps the key.
    fn get_key<E, K>(&self, eng: &E, id: K::Id) -> Result<Option<K>, Self::Error>
    where
        E: Engine,
        K: UnwrappedKey<E::CS>;

    /// Removes and unwraps the key.
    fn remove_key<E, K>(&mut self, eng: &E, id: K::Id) -> Result<Option<K>, Self::Error>
    where
        E: Engine,
        K: UnwrappedKey<E::CS>;
}

impl<T: KeyStore> KeyStoreExt for T {
    /// Wraps and inserts the key.
    fn insert_key<E, K>(&mut self, eng: &E, key: K) -> Result<K::Id, Self::Error>
    where
        E: Engine,
        K: UnwrappedKey<E::CS>,
    {
        let unwrapped_id = key.id().map_err(<<Self as KeyStore>::Error>::other)?;
        let wrapped_key = eng.wrap(key).map_err(<<Self as KeyStore>::Error>::other)?;
        self.try_insert(*unwrapped_id.as_ref(), wrapped_key)?;
        Ok(unwrapped_id)
    }

    /// Retrieves and unwraps the key.
    fn get_key<E, K>(&self, eng: &E, id: K::Id) -> Result<Option<K>, Self::Error>
    where
        E: Engine,
        K: UnwrappedKey<E::CS>,
    {
        if let Some(wrapped) = self.get(*id.as_ref())? {
            let sk = eng
                .unwrap(&wrapped)
                .map_err(<<Self as KeyStore>::Error>::other)?;
            Ok(Some(sk))
        } else {
            Ok(None)
        }
    }

    /// Removes and unwraps the key.
    fn remove_key<E, K>(&mut self, eng: &E, id: K::Id) -> Result<Option<K>, Self::Error>
    where
        E: Engine,
        K: UnwrappedKey<E::CS>,
    {
        if let Some(wrapped) = self.remove(*id.as_ref())? {
            let sk = eng
                .unwrap(&wrapped)
                .map_err(<<Self as KeyStore>::Error>::other)?;
            Ok(Some(sk))
        } else {
            Ok(None)
        }
    }
}

#[cfg(all(test, feature = "memstore"))]
mod tests {
    use crate::{
        Rng,
        aranya::EncryptionKey,
        default::{DefaultCipherSuite, DefaultEngine},
        keystore::{KeyStoreExt as _, memstore::MemStore},
    };

    type CS = DefaultCipherSuite;
    type Key = EncryptionKey<CS>;

    /// Exercises both the `Some` (present) and `None` (absent)
    /// arms of [`KeyStoreExt::get_key`] and
    /// [`KeyStoreExt::remove_key`].
    #[test]
    fn test_get_remove_key_present_and_absent() {
        let (eng, _) = DefaultEngine::<Rng, CS>::from_entropy(Rng);
        let mut store = MemStore::new();

        let id = store
            .insert_key(&eng, Key::new(&eng))
            .expect("should be able to insert key");

        // Present: `get_key` takes the `Some` arm.
        let got = store
            .get_key::<_, Key>(&eng, id)
            .expect("`get_key` should not fail");
        assert!(got.is_some(), "stored key should be found");

        // Present: `remove_key` takes the `Some` arm.
        let removed = store
            .remove_key::<_, Key>(&eng, id)
            .expect("`remove_key` should not fail");
        assert!(removed.is_some(), "stored key should be removed");

        // Absent: `get_key` takes the `None` arm.
        let got = store
            .get_key::<_, Key>(&eng, id)
            .expect("`get_key` should not fail");
        assert!(got.is_none(), "removed key should be gone");

        // Absent: `remove_key` takes the `None` arm.
        let removed = store
            .remove_key::<_, Key>(&eng, id)
            .expect("`remove_key` should not fail");
        assert!(removed.is_none(), "removed key should stay gone");
    }
}