Skip to content
lib.rs 19.6 KiB
Newer Older
Stefan Schindler's avatar
Stefan Schindler committed
#![forbid(unsafe_code)]

use errors::{KeyManagementError, MultiKeyManagerResult};
pub mod file;
use file::{open_rwlocked, File};
pub mod slots;
use slots::{KeyId, Slot};

//use core::pin::Pin;
use argon2::{
    password_hash::{rand_core::OsRng, PasswordHasher, SaltString},
    Argon2, ParamsBuilder,
};
use core::{hint::black_box, ops::BitXor};
use ring::{
    aead::{BoundKey, Nonce, NonceSequence, OpeningKey, SealingKey, UnboundKey},
    error::Unspecified,
};
use std::{
    io::{BufReader, Seek, Write},
    path::Path,
    thread::panicking,
};
Stefan Schindler's avatar
Stefan Schindler committed
pub trait LiveKeyMaterial {
    /// This must keep the nonce as is
    fn get_opening_master_key(&mut self) -> OpeningKey<OpeningNonce>;
    /// This must update the nonce
    fn get_sealing_master_key(&mut self) -> SealingKey<BorrowedNonce>;
}

/// A set of decrypted keys in memory
#[derive(Debug)]
pub struct LiveKeys {
    master_key: LiveKey,
    master_nonce: LiveNonce,
    slots: Vec<Slot>,
    file_backend: File,
}

impl LiveKeys {
    /// The user input must be a strong random value or a long readable sequence (see paper key)
    ///
    /// Generates and stores a new master key
    pub fn generate_new<P: AsRef<Path>>(
        user_input: &str,
        output_path: P,
    ) -> MultiKeyManagerResult<Self> {
        let file_backend = open_rwlocked(output_path)?;
        let master_key = LiveKey::random()?;
        let master_nonce = LiveNonce::start();
        //let salt = random_salt();
        //let password = user_input.as_bytes();
        //let encoded_key_stream = argon2_key_stream(password, &salt)?;
        //let key_bytes = encoded_key_stream.as_bytes();
        //let slot = Slot::new(&master_key, &master_nonce, &salt, &key_bytes)?;
        //let slots = vec![slot];
        let mut live_keys = LiveKeys {
            slots: vec![],
        };
        // maybe make this store configurable in the future for key rollover?
        //live_keys.store_to_disk()?;
        live_keys.add_key(user_input)?;
        Ok(live_keys)
    pub fn read_from_disk<P: AsRef<Path>>(
        input_key: &str,
        input_path: P,
    ) -> MultiKeyManagerResult<Self> {
        //println!("LiveKeys::read_from_disk({:?})", input_path.as_ref());
        let input = open_rwlocked(&input_path)?;
        // if the creation during the previous run failed an empty file is left behind, this fixes that
        if input.empty()? {
            drop(input);
            return LiveKeys::generate_new(input_key, input_path);
        }
        //let live_keys:LiveKeys = rmp_serde::from_read(&*input)?;
        let mut buf_reader = BufReader::new(&*input);
        let slots: Vec<Slot> = rmp_serde::from_read(&mut buf_reader)?;

        let password = input_key.as_bytes();

        for slot in &slots {
            let encoded_key_stream = argon2_key_stream(password, &slot.salt_string()?)?;
            let key_bytes = encoded_key_stream.as_bytes();

            if slot.check_value_matches(&key_bytes) {
                let (master_key, master_nonce) = slot.decrypt_master_values(&key_bytes);
                return Ok(LiveKeys {
                    file_backend: input,
                    slots,
                    master_key,
                    master_nonce,
                });
            }
        }
        Err(errors::KeyManagementError::NoMatchingKeyFound)
    /// Flush the keys to disk
    pub fn store_to_disk(&mut self) -> MultiKeyManagerResult<()> {
        //assert!(self.master_nonce.has_changed == false);
        println!("LiveKeys::store_to_disk: {:?}", self.slots);
        let mut output = &*self.file_backend;

        // maybe don't save the field names in the future
        let buf = rmp_serde::to_vec_named(&self.slots)?;
        assert!(buf.len() > 0, "unable to serialise slots");
        // truncate the file if it existed before
        output.rewind()?;
        output.set_len(buf.len() as u64)?;
        output.write_all(&buf)?;
        output.flush()?;

        Ok(())
    }

    /// returns the number of keys currently stored if successfull
    pub fn add_key(&mut self, user_input: &str) -> MultiKeyManagerResult<usize> {
        let salt = random_salt();

        let password = user_input.as_bytes();

        let encoded_key_stream = argon2_key_stream(password, &salt)?;
        let key_bytes = encoded_key_stream.as_bytes();

        let slot = Slot::new(&self.master_key, &self.master_nonce, &salt, &key_bytes)?;
        self.slots.push(slot);

        self.store_to_disk()?;
        Ok(self.slots.len())
    }

    pub fn list_keys(&self) -> Vec<KeyId> {
        self.slots.iter().map(Slot::id).collect()
    pub fn del_key(&mut self, old: &KeyId) -> MultiKeyManagerResult<usize> {
        //let old = old.as_ref();
        let mut contained = false;
        self.slots.retain(|s| {
            if s.id() != *old {
                // keep
                true
            } else {
                contained = true;
                // drop
                false
            }
        });
        self.store_to_disk()?;
        if contained {
            Ok(self.slots.len())
        } else {
            Err(KeyManagementError::NoMatchingKeyFound)
        }
    /// an updating nonce borrow
    pub fn get_mutating_nonce<'a>(&'a mut self) -> BorrowedNonce<'a> {
        BorrowedNonce {
            counter: self.master_nonce.counter,
            live_keys: self,
        }
    }
    pub fn get_opening_nonce(&mut self) -> OpeningNonce {
        OpeningNonce {
            counter: self.master_nonce.counter,
        }
    }

    fn update_nonces(&mut self, new: u128) -> MultiKeyManagerResult<()> {
        println!("LiveKeys::update_nonces");
        let ref old = self.master_nonce;
        for slot in &mut self.slots {
            slot.update_xored_nonce(old, new);
        }
        self.store_to_disk()?;
        self.master_nonce.counter = new;
        Ok(())
    }

    pub fn unsafe_insecure_borrow_i_know_what_i_am_doing(&self) -> &[u8; 32] {
        &self.master_key.secret_key
    }
Stefan Schindler's avatar
Stefan Schindler committed
}
impl LiveKeyMaterial for LiveKeys {
    /// This will update the nonce
Stefan Schindler's avatar
Stefan Schindler committed
    fn get_sealing_master_key(&mut self) -> SealingKey<BorrowedNonce> {
        let algorithm = &ring::aead::CHACHA20_POLY1305;
        let key_material = self.master_key.secret_key;
        let nonce = self.get_mutating_nonce();

        let unbound_key =
            UnboundKey::new(algorithm, &key_material).expect("unable to construct UnboundKey ");
        SealingKey::new(unbound_key, nonce)
    }
    /// This will keep the nonce as is
Stefan Schindler's avatar
Stefan Schindler committed
    fn get_opening_master_key(&mut self) -> OpeningKey<OpeningNonce> {
        let algorithm = &ring::aead::CHACHA20_POLY1305;
        let key_material = self.master_key.secret_key;
        let nonce = self.get_opening_nonce();

        println!(" >>> DBG: {:?}", nonce);

        let unbound_key =
            UnboundKey::new(algorithm, &key_material).expect("unable to construct UnboundKey");
        OpeningKey::new(unbound_key, nonce)
    }
Stefan Schindler's avatar
Stefan Schindler committed

#[cfg(feature = "with_nonce")]
pub trait WithNonce {
    fn get_sealing_master_key_with_nonce(
        &mut self,
        nonce: MutableNonce,
    ) -> SealingKey<MutableNonce>;
    fn get_opening_master_key_with_nonce(
        &mut self,
        nonce: OpeningNonce,
    ) -> OpeningKey<OpeningNonce>;
}
#[cfg(feature = "with_nonce")]
impl WithNonce for LiveKeys {
    fn get_sealing_master_key_with_nonce(
        &mut self,
        nonce: MutableNonce,
    ) -> SealingKey<MutableNonce> {
        let algorithm = &ring::aead::CHACHA20_POLY1305;
        let key_material = self.master_key.secret_key;

        let unbound_key =
            UnboundKey::new(algorithm, &key_material).expect("unable to construct UnboundKey ");
        SealingKey::new(unbound_key, nonce)
    }
    fn get_opening_master_key_with_nonce(
        &mut self,
        nonce: OpeningNonce,
    ) -> OpeningKey<OpeningNonce> {
        let algorithm = &ring::aead::CHACHA20_POLY1305;
        let key_material = self.master_key.secret_key;

        println!(" >>> DBG: {:?}", nonce);

        let unbound_key =
            UnboundKey::new(algorithm, &key_material).expect("unable to construct UnboundKey");
        OpeningKey::new(unbound_key, nonce)
    }
}

impl std::fmt::Debug for LiveKey {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_tuple("LiveKey").field(&"<REDACTED>").finish()
    }
}
//pub trait FnBox{ fn call_box(self: Box<Self>);}
//impl<F :FnOnce(u128)> FnBox for FnBox { fn call_box(self: Box<F>) { (*self)(self.counter) } }
Stefan Schindler's avatar
Stefan Schindler committed
pub struct MutableNonce {
    //drop_syncer: Option<Box<dyn FnOnce(u128)>>,
    original: Rc<RefCell<u128>>,
Stefan Schindler's avatar
Stefan Schindler committed
    counter: u128,
}
Stefan Schindler's avatar
Stefan Schindler committed
impl MutableNonce {
    pub fn new(original: Rc<RefCell<u128>>) -> Self {
        let counter = original.borrow().clone();

        //(original.clone(),
Stefan Schindler's avatar
Stefan Schindler committed
        Self {
            //drop_syncer: Some(Box::new(drop_syncer)),
            original,
Stefan Schindler's avatar
Stefan Schindler committed
            counter,
        }
Stefan Schindler's avatar
Stefan Schindler committed
impl NonceSequence for MutableNonce {
    fn advance(&mut self) -> Result<Nonce, Unspecified> {
        println!("MutableNonce::advance from {}", self.counter);
        //let nonce = self.counter.to_le_bytes();
        match self.counter.checked_add(1) {
            Some(c) => {
                self.counter = c;
                let nonce = c.to_le_bytes();
                let n = Nonce::try_assume_unique_for_key(&nonce[..12]);
                println!(
                    "NonceSequence::advance -> try_assume_unique_for_key = {}",
                    n.is_ok()
                );
                n
            }
            None => Err(Unspecified),
        }
    }
}
impl Drop for MutableNonce {
    fn drop(&mut self) {
        *self.original.borrow_mut() = self.counter;
pub struct BorrowedNonce<'a> {
    live_keys: &'a mut LiveKeys,
    counter: u128,
}
impl<'a> NonceSequence for BorrowedNonce<'a> {
    fn advance(&mut self) -> Result<Nonce, Unspecified> {
        println!("NonceSequence::advance from {}", self.counter);
        //let nonce = self.counter.to_le_bytes();
        match self.counter.checked_add(1) {
            Some(c) => {
                self.counter = c;
                let nonce = c.to_le_bytes();
                let n = Nonce::try_assume_unique_for_key(&nonce[..12]);
                println!(
                    "NonceSequence::advance -> try_assume_unique_for_key = {}",
                    n.is_ok()
                );
                n
            }
            None => Err(Unspecified),
        }
    }
}
impl<'a> Drop for BorrowedNonce<'a> {
    fn drop(&mut self) {
        if self.counter != self.live_keys.master_nonce.counter {
            if let Err(e) = self.live_keys.update_nonces(self.counter) {
                let error = format!("BorrowedNonce::drop failed: {:?}", e);
                if panicking() {
                    eprintln!("{error}");
                } else {
                    panic!("{error}");
                }
            };
#[derive(Debug)]
pub struct OpeningNonce {
    counter: u128,
}
Stefan Schindler's avatar
Stefan Schindler committed
impl OpeningNonce {
    pub fn new(counter: u128) -> Self {
        Self { counter }
    }
}
impl NonceSequence for OpeningNonce {
    fn advance(&mut self) -> Result<Nonce, Unspecified> {
        let nonce = self.counter.to_le_bytes();
        match self.counter.checked_add(1) {
            Some(c) => {
                self.counter = c;
                //let nonce = c.to_le_bytes();
                Nonce::try_assume_unique_for_key(&nonce[..12])
            }
            None => Err(Unspecified),
        }
    }
}

/// A key in memory
pub struct LiveKey {
    /// 256 bit secret that overwrites itself on drop to minimise secret leaking.
Stefan Schindler's avatar
Stefan Schindler committed
    /// Could be improved by pinning the data with Pin<>
    secret_key: [u8; 32],
}
use ring::rand::SecureRandom;
impl LiveKey {
    pub fn random() -> MultiKeyManagerResult<Self> {
        let rng = ring::rand::SystemRandom::new();

        let mut secret_key = LiveKey {
            secret_key: [0u8; 32],
        };
        rng.fill(&mut secret_key.secret_key)?;

        Ok(secret_key)
    }

    /// b must be 32 Bytes long
    pub fn from_xor(a: &[u8; 32], b: &[u8; 32]) -> Self {
        let secret_key = &LiveKey { secret_key: *a } ^ b;
        LiveKey { secret_key }
    }
}
impl BitXor<&[u8]> for &LiveKey {
    type Output = [u8; 32];

    fn bitxor(self, rhs: &[u8]) -> Self::Output {
        assert_eq!(32, rhs.len());
        let mut buf = [0u8; 32];
        for (e, (l, r)) in buf.iter_mut().zip(self.secret_key.iter().zip(rhs.iter())) {
            *e = l ^ r;
        }
        buf
    }
}
impl Drop for LiveKey {
    fn drop(&mut self) {
        let rng = ring::rand::SystemRandom::new();

        // ignore errors
        let _ = rng.fill(&mut self.secret_key);
        // prevent the optimiser from not doing the work, best-effort
        let _ = black_box(self.secret_key);
    }
}

pub struct LiveNonce {
    counter: u128,
}
impl LiveNonce {
    /// A fresh starting NONCE, maybe at 0, 1 or some other lowish value
    pub fn start() -> Self {
        Self { counter: 1 }
    }

    pub fn from_xor(a: &[u8; 16], b: &[u8; 16]) -> Self {
        LiveNonce {
            counter: u128::from_le_bytes(*a) ^ u128::from_le_bytes(*b),
        }
    }
}
impl BitXor<&[u8]> for &LiveNonce {
    type Output = [u8; 16];

    fn bitxor(self, rhs: &[u8]) -> Self::Output {
        assert_eq!(16, rhs.len());
        let mut buf = [0u8; 16];
        for (e, (l, r)) in buf
            .iter_mut()
            .zip(self.counter.to_le_bytes().iter().zip(rhs.iter()))
        {
            *e = l ^ r;
        }
        buf
    }
}
impl std::fmt::Debug for LiveNonce {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_tuple("LiveNonce").field(&"<REDACTED>").finish()
    }
}
/*
impl PartialEq for LiveNonce {
    fn eq(&self, rhs: &Self) -> bool {
        self.counter == rhs.counter
    }
}
impl Drop for LiveNonce {
    fn drop(&mut self) {
        if self.has_changed {
            if panicking() {
                eprintln!("LiveKey was not saved");
            } else {
                panic!("LiveKey was not saved");
            }
        }
    }
}
*/

/// Generates a random salt that is stored as b64 encoded ascii in memory
fn random_salt() -> SaltString {
    // generate: `dd if=/dev/urandom bs=18 count=1 | base64`
    // like `Salt::new("cMXgoD2tB70HkX87dSfyVQne")?;`
    let s = SaltString::generate(&mut OsRng);
    //println!("salt: {:#?}, bytes_len: {:?}", s, s.as_bytes());
    s
}

fn argon2_key_stream(
    password: &[u8],
    salt: &SaltString,
) -> MultiKeyManagerResult<
    //Vec<u8>
    argon2::password_hash::Output,
> {
    // Argon2 with default params (Argon2id v19)
    let mut params = ParamsBuilder::new();
    params.output_len((256 + 128 + 128) / 8)?;
    let argon2 = Argon2::from(params.params()?);

    let key_stream = argon2.hash_password(password, salt)?;
    let output = key_stream.hash.expect("unable to produce key stream");
    assert!(
        64 <= output.len(),
        "key_bytes must be at least 64 Bytes long, was {}",
        output.len()
    );
    Ok(output)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::path::PathBuf;

    /// ext must not start with a `/`
    fn path<P: AsRef<Path>>(ext: P) -> PathBuf {
        let mut p = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        // we are in a cargo workspace
        p.pop();
        p.push("target");
        p.push("mkm_tests");
        std::fs::create_dir_all(&p).expect("unable to create testing directory");
        p.push(ext);
        p
    }

    #[test]
    fn generate_random_keys() {
        let result = LiveKeys::generate_new(
            "0198237401982734081230748120384701283740",
            path("generate_random_keys.out"),
        );
        assert!(result.is_ok());
    }
    #[test]
    fn generate_random_secrets() {
        let a = LiveKey::random();
        let b = LiveKey::random();
        assert!(a.is_ok());
        assert!(b.is_ok());
        assert_ne!(a.unwrap().secret_key, b.unwrap().secret_key);
    }

    #[test]
    fn store_random() {
        let mut keys = LiveKeys::generate_new(
            "0198237401982734081230748120384701283740",
            path("store_random.out"),
        )
        .expect("unable to generate random key");
        assert_eq!(Ok(()), keys.store_to_disk(), "unable to store on disk");
    }

    #[test]
    fn update_has_changed_flag() {
        let mut live_keys = LiveKeys::generate_new(
            "0198237401982734081230748120384701283740",
            path("update_has_changed_flag.out"),
        )
        .unwrap();
        let old_xored_nonce = live_keys.slots[0].__tests_encrypted_master_nonce();

        {
            let mut nonce = live_keys.get_mutating_nonce();
            let a = nonce.counter;
            let _ = nonce.advance().unwrap();
            // .get_and_increment();
            // let _ = nonce.advance().unwrap();
            // .get_and_increment();
            let b = nonce.counter;
            assert_ne!(a, b);
            println!("tests::update_has_changed_flag end scope");
        }

        let new_xored_nonce = live_keys.slots[0].__tests_encrypted_master_nonce();
        assert_ne!(old_xored_nonce, new_xored_nonce);
        assert_eq!(2, live_keys.master_nonce.counter);

    #[test]
    fn store_and_load_with_disk() {
        let paper_key = "0198237401982734081230748120384701283740";
        let path = path("store_and_load_with_disk.out");
        let mut live_keys = LiveKeys::generate_new(paper_key, &path).unwrap();
        live_keys.store_to_disk().expect("unable to store");

        let secret_key = live_keys.master_key.secret_key.clone();
        let master_nonce = live_keys.master_nonce.counter.clone();

        drop(live_keys);

        let live_keys =
            LiveKeys::read_from_disk(paper_key, path).expect("unable to read_from_disk");

        assert_eq!(secret_key, live_keys.master_key.secret_key);
        assert_eq!(master_nonce, live_keys.master_nonce.counter);
    }

    #[test]
    fn add_multiple_keys() {
        let paper_key_a = "0198237401982734081230748120384701283740";
        let paper_key_b = "AAAAAAAAAAAAAAAA081230748120384701283740";
        let paper_key_c = "BBBBBBBBBBBBBBBB081230748120384701283740";
        let path = path("add_multiple_keys.out");
        let mut live_keys = LiveKeys::generate_new(paper_key_a, &path).unwrap();

        assert_eq!(Ok(2), live_keys.add_key(paper_key_b));
        drop(live_keys);

        let mut live_keys = LiveKeys::read_from_disk(paper_key_b, &path).unwrap();
        live_keys.add_key(paper_key_c).unwrap();
    }

    #[test]
    fn list_and_del_key() {
        let paper_key_a = "AAAAAAAAAAAAAAAA081230748120384701283740";
        let paper_key_b = "BBBBBBBBBBBBBBBB081230748120384701283740";
        let path = path("list_and_del_key.out");
        let mut live_keys = LiveKeys::generate_new(paper_key_a, &path).unwrap();
        live_keys.add_key(paper_key_b).unwrap();

        let list = live_keys.list_keys();
        let first: &KeyId = &list[0];
        println!("deleteing {first:?}");
        assert_eq!(Ok(1), live_keys.del_key(first));

        // removing something that is not there fails
        assert_eq!(
            Err(KeyManagementError::NoMatchingKeyFound),
            live_keys.del_key(first)
        );
        drop(live_keys);

        assert_eq!(
            KeyManagementError::NoMatchingKeyFound,
            LiveKeys::read_from_disk(paper_key_a, &path).unwrap_err()
        );
        let _live_keys = LiveKeys::read_from_disk(paper_key_b, &path).unwrap();
        let original = Rc::new(RefCell::new(2));
        let mut nonce = MutableNonce::new(Rc::clone(&original));