gitdb 0.1.1

encrypted database built on top of git
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::path::PathBuf;

use toml;

use git_db;
use crypto;
use encoding;

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Meta {
    pub version: String,
    pub plaintext: Plaintext,
    pub pbkdf2: PBKDF2,
    pub aead: AEAD,
    pub paranoid: Paranoid
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Plaintext {
    pub min_bits: u32
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PBKDF2 {
    pub algo: String,
    pub iters: u32,
    pub salt: String
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AEAD {
    pub algo: String,
    pub nonce: String,
    pub keylen: u32
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Paranoid {
    pub simple_multiple_encryption: String,
    pub cascading_encryption: String
}

impl Meta {
    pub fn default_meta() -> Result<Meta, String> {
        Ok(Meta {
            version: String::from("0.0.1"),
            plaintext: Plaintext {
                min_bits: 1024
            },
            pbkdf2: PBKDF2 {
                algo: String::from("Sha256"),
                iters: 1_000_000,
                salt: encoding::encode(&crypto::generate_rand_bits(96)?)
            },
            aead: AEAD {
                algo: String::from("ChaCha20-Poly1305"),
                nonce: encoding::encode(&crypto::generate_rand_bits(96)?),
                keylen: 256
            },
            paranoid: Paranoid {
                simple_multiple_encryption: String::from("TBD"),
                cascading_encryption: String::from("TBD")
            }
        })
    }
    
    pub fn generate_secure_meta(db: &git_db::DB) -> Result<Meta, String> {
        let default_meta = Meta::default_meta()?;
        
        let salt = crypto::generate_rand_bits(96)?;
        let nonce = db.generate_nonce()?;
        
        let meta = Meta {
            pbkdf2: PBKDF2 {
                salt: encoding::encode(&salt),
                ..default_meta.pbkdf2.clone()
            },
            aead: AEAD {
                nonce: encoding::encode(&nonce),
                ..default_meta.aead.clone()
            },
            ..default_meta.clone()
        };
        Ok(meta)
    }

    pub fn from_toml(path: &PathBuf) -> Result<Meta, String> {
        File::open(path)
            .map_err(|e| format!("Failed to open {:?}: {:?}", path, e))
            .and_then(|mut f| {
                let mut contents = Vec::new();
                f.read_to_end(&mut contents)
                    .map_err(|e| format!("Failed to read {:?}: {:?}", path, e))
                    .map(|_| contents)
            })
            .and_then(|contents| {
                toml::from_slice(&contents).map_err(|e| format!("{:?}", e))
            })
    }

    pub fn to_toml_bytes(&self) -> Result<Vec<u8>, String> {
        toml::to_vec(&self)
            .map_err(|e| format!("Failed to serialize meta {:?}", e))
    }

    pub fn write_toml(&self, path: &PathBuf) -> Result<(), String> {
        self.to_toml_bytes()
            .and_then(|serialized_meta| {
                File::create(&path)
                    .map_err(|e| format!("Failed to create {:?}: {:?}", path, e))
                    .and_then(|mut f| {
                        f.write_all(&serialized_meta)
                            .map_err(|e| format!("Failed write to meta file {:?}: {:?}", path, e))
                    })
            })
    }
}