aboutsummaryrefslogtreecommitdiff
path: root/src/security/crypto.rs
//! Comprehensive encryption submodule which handles serialising and de-serialising

use miscreant::aead::{Aes256Siv, Algorithm};
use security::{keys::{Key, KEY_LENGTH},
               utils::{encoding, random}};
use serde::{de::DeserializeOwned, Serialize};
use serde_json;

use std::io::prelude::*;
use std::{error::Error, fs::File};

/// The main encryption context
pub struct CryptoEngine {
    ctx: Aes256Siv,
    key: Key,
    iv: Vec<u8>,
}

/// Represents some packed data – includes nonce and blob
#[derive(Serialize, Deserialize)]
struct PackedData {
    nonce: Vec<u8>,
    iv: Vec<u8>,
    data: Vec<u8>,
}

impl CryptoEngine {
    /// Create a new CryptoEngine from a key
    ///
    /// This generates a new IV which is then used for all
    /// cryptographic transactions in a vault context
    pub fn generate(key: Key) -> CryptoEngine {
        return CryptoEngine {
            ctx: Aes256Siv::new(&key.to_slice()),
            key: key,
            iv: random::bytes(KEY_LENGTH),
        };
    }

    /// Load an existing encryption context into scope
    ///
    /// Takes an encrypted stream of a key file that wraps a key object
    /// in a packed encryption object.
    pub fn load(path: &str, pw: &str, salt: &str) -> Result<CryptoEngine, Box<Error>> {
        let mut file = File::open(path)?;
        let mut file_content = String::new();
        file.read_to_string(&mut file_content)?;

        let decoded = String::from_utf8(encoding::base64_decode(&file_content))?;
        let packed: PackedData = serde_json::from_str(&decoded)?;

        /* Decrypt key */
        let decrypted_key: Key = CryptoEngine {
            ctx: Aes256Siv::new(&packed.data.as_slice()),
            key: Key::from_password(pw, salt),
            iv: packed.iv.clone(),
        }.decrypt(String::from_utf8(packed.data)?)?;

        return Ok(CryptoEngine {
            ctx: Aes256Siv::new(&decrypted_key.to_slice()),
            key: decrypted_key,
            iv: packed.iv,
        });
    }

    /// Save the current key, encrypted to disk
    pub fn save(&mut self, path: &str, pw: &str, salt: &str) -> Result<(), Box<Error>> {
        /* Encrypt key */
        let mut tmp = CryptoEngine {
            ctx: Aes256Siv::new(&self.key.data.as_slice()),
            key: Key::from_password(pw, salt),
            iv: self.iv.clone(),
        };

        let encrypted_key = tmp.encrypt(&self.key)?;

        let mut file = File::create(path)?;
        file.write_all(encrypted_key.as_bytes())?;
        return Ok(());
    }

    /// Encrypt a piece of data, returns a packed and encoded string
    pub fn encrypt<T: Serialize>(&mut self, data: &T) -> Result<String, Box<Error>> {
        let serial = serde_json::to_string(&data)?;
        let nonce = random::bytes(64);
        let iv = &self.iv.as_slice();
        let data = &serial.as_bytes();

        let encrypted = self.ctx.seal(nonce.as_slice(), iv, data);
        let packed = PackedData {
            iv: self.iv.clone(),
            data: encrypted,
            nonce: nonce,
        };

        let enc_packed = serde_json::to_string(&packed)?;
        return Ok(encoding::base64_encode(&enc_packed.into_bytes()));
    }

    /// Decrypt a ciphertext string into a type object
    pub fn decrypt<T: DeserializeOwned>(&mut self, cipher: String) -> Result<T, Box<Error>> {
        let dec_packed = String::from_utf8(encoding::base64_decode(&cipher))?;
        let p: PackedData = serde_json::from_str(&dec_packed)?;

        let iv = &self.iv.as_slice();
        let decrypted = self.ctx.open(p.nonce.as_slice(), iv, p.data.as_slice())?;
        let decr_str = String::from_utf8(decrypted)?;

        let t: T = serde_json::from_str(&decr_str)?;
        return Ok(t);
    }
}