recipher 0.2.2

CipherStash key generation library for ZeroKMS
Documentation
/// KeySet Generation
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;
use serde::{Deserialize, Serialize};
use zeroize::{Zeroize, ZeroizeOnDrop};

use crate::{
    errors::RecipherError,
    key::{GenRandom, Key},
    permutation::Permutation,
};

#[derive(Clone, Default, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct EncryptionKeySet {
    pub(crate) p1: Permutation,
    pub(crate) p2: Permutation,
    pub(crate) p3: Permutation,
}

opaque_debug::implement!(EncryptionKeySet);

impl EncryptionKeySet {
    pub fn generate() -> Result<Self, RecipherError> {
        let mut rng = ChaCha20Rng::from_entropy();

        let k1: Key = GenRandom::gen_random(&mut rng)?;
        let k2: Key = GenRandom::gen_random(&mut rng)?;
        let k3: Key = GenRandom::gen_random(&mut rng)?;

        let p1 = Permutation::generate(&k1, 16);
        let p2 = Permutation::generate(&k2, 16);
        let p3 = Permutation::generate(&k3, 33); // TODO: Length check!?

        Ok(EncryptionKeySet { p1, p2, p3 })
    }

    /// Derive an "authority" `EncryptionKeySet` that will allow a client coresponding
    /// to `authority` in the given `domain` such that the client can use the current domain.
    ///
    /// ```no_compile
    /// D1=C1(A1)
    /// D2=C1(A2)
    /// C1=D1(A1*)
    /// D2=D1(A1*)A2
    /// D1*D2=A1*A2
    /// => A2=A1D1*D2
    /// ```
    pub fn grant(
        &self,
        domain: &EncryptionKeySet,
        authority: &EncryptionKeySet,
    ) -> EncryptionKeySet {
        let p1 = authority.p1.permute(&domain.p1.invert()).permute(&self.p1);
        let p3 = authority.p3.permute(&domain.p3.invert()).permute(&self.p3);

        Self {
            p1,
            p2: authority.p2.clone(),
            p3,
        }
    }

    pub fn to_bytes(&self) -> Result<Vec<u8>, RecipherError> {
        serde_cbor::to_vec(self).map_err(RecipherError::Serialization)
    }

    pub fn from_bytes(bytes: &[u8]) -> Result<Self, RecipherError> {
        Ok(serde_cbor::from_slice(bytes)?)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::keyset::ProxyKeySet;
    use rand::{thread_rng, Fill};

    #[test]
    fn composition() {
        let d1 = EncryptionKeySet::generate().unwrap();
        let a1 = EncryptionKeySet::generate().unwrap();
        let client = ProxyKeySet::generate(&a1, &d1);

        let mut input = [0u8; 16];
        input.try_fill(&mut thread_rng()).unwrap();
        let mut check = input;

        // a1 and client should compose to d1 for p1
        a1.p1.permute_slice_mut(&mut input);
        client.p1.permute_slice_mut(&mut input);
        d1.p1.permute_slice_mut(&mut check);
        assert_eq!(input, check);

        // And for p3
        let mut input = [0u8; 33];
        input.try_fill(&mut thread_rng()).unwrap();
        let mut check = input;

        a1.p3.permute_slice_mut(&mut input);
        client.p3.permute_slice_mut(&mut input);
        d1.p3.permute_slice_mut(&mut check);

        assert_eq!(input, check);
    }

    #[test]
    fn delegate_test() {
        let d1 = EncryptionKeySet::generate().unwrap();
        let a1 = EncryptionKeySet::generate().unwrap();
        let client = ProxyKeySet::generate(&a1, &d1);
        let d2 = EncryptionKeySet::generate().unwrap();

        let a2 = d2.grant(&d1, &a1);

        let mut input_p1 = [0u8; 16];
        let mut input_p3 = [0u8; 33];
        input_p1.try_fill(&mut thread_rng()).unwrap();
        input_p3.try_fill(&mut thread_rng()).unwrap();
        let mut check_p1 = input_p1;
        let mut check_p3 = input_p3;

        // a2 and client should compose to d2
        a2.p1.permute_slice_mut(&mut input_p1);
        client.p1.permute_slice_mut(&mut input_p1);
        a2.p3.permute_slice_mut(&mut input_p3);
        client.p3.permute_slice_mut(&mut input_p3);

        d2.p1.permute_slice_mut(&mut check_p1);
        d2.p3.permute_slice_mut(&mut check_p3);

        assert_eq!(input_p1, check_p1);
        assert_eq!(input_p3, check_p3);

        // p2 should be the same
        assert_eq!(a2.p2.permutation, a1.p2.permutation);
    }
}