zvault/src/util/encryption.rs

234 lines
7.3 KiB
Rust
Raw Normal View History

2017-03-10 11:43:32 +00:00
use std::collections::HashMap;
2017-03-18 16:22:11 +00:00
use std::path::{Path, PathBuf};
use std::io;
use std::fs::{self, File};
2017-04-12 06:30:42 +00:00
use std::sync::{Once, ONCE_INIT};
2017-03-18 16:22:11 +00:00
use serde_yaml;
2017-04-27 11:35:48 +00:00
use serde_bytes::ByteBuf;
2017-03-18 16:22:11 +00:00
2017-05-08 13:11:07 +00:00
use libsodium_sys;
2017-04-12 06:30:42 +00:00
use sodiumoxide;
2017-03-18 16:22:11 +00:00
use sodiumoxide::crypto::sealedbox;
2017-04-12 06:30:42 +00:00
use sodiumoxide::crypto::box_;
2017-05-08 13:11:07 +00:00
use sodiumoxide::crypto::pwhash;
2017-04-12 06:30:42 +00:00
pub use sodiumoxide::crypto::box_::{SecretKey, PublicKey};
2017-03-18 16:22:11 +00:00
use ::util::*;
2017-03-10 11:43:32 +00:00
2017-04-12 06:30:42 +00:00
static INIT: Once = ONCE_INIT;
fn sodium_init() {
INIT.call_once(|| {
if !sodiumoxide::init() {
panic!("Failed to initialize sodiumoxide");
}
});
}
2017-03-16 08:42:30 +00:00
quick_error!{
#[derive(Debug)]
pub enum EncryptionError {
2017-03-18 16:22:11 +00:00
InvalidKey {
description("Invalid key")
}
MissingKey(key: PublicKey) {
description("Missing key")
display("Missing key: {}", to_hex(&key[..]))
}
2017-03-16 08:42:30 +00:00
Operation(reason: &'static str) {
description("Operation failed")
display("Operation failed: {}", reason)
}
2017-03-18 16:22:11 +00:00
Io(err: io::Error) {
from()
cause(err)
description("IO error")
display("IO error: {}", err)
}
Yaml(err: serde_yaml::Error) {
from()
cause(err)
description("Yaml format error")
display("Yaml format error: {}", err)
}
2017-03-16 08:42:30 +00:00
}
}
2017-03-18 16:22:11 +00:00
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[allow(unknown_lints,non_camel_case_types)]
2017-03-10 11:43:32 +00:00
pub enum EncryptionMethod {
2017-03-18 16:22:11 +00:00
Sodium,
2017-03-10 11:43:32 +00:00
}
serde_impl!(EncryptionMethod(u64) {
2017-03-18 16:22:11 +00:00
Sodium => 0
});
impl EncryptionMethod {
pub fn from_string(val: &str) -> Result<Self, &'static str> {
match val {
"sodium" => Ok(EncryptionMethod::Sodium),
_ => Err("Unsupported encryption method")
}
}
pub fn to_string(&self) -> String {
match *self {
EncryptionMethod::Sodium => "sodium".to_string()
}
}
}
pub type Encryption = (EncryptionMethod, ByteBuf);
struct KeyfileYaml {
public: String,
secret: String
}
impl Default for KeyfileYaml {
fn default() -> Self {
KeyfileYaml {
public: "".to_string(),
secret: "".to_string()
}
}
}
serde_impl!(KeyfileYaml(String) {
public: String => "public",
secret: String => "secret"
2017-03-10 11:43:32 +00:00
});
2017-03-18 16:22:11 +00:00
impl KeyfileYaml {
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, EncryptionError> {
let f = try!(File::open(path));
Ok(try!(serde_yaml::from_reader(f)))
}
2017-03-10 11:43:32 +00:00
2017-03-18 16:22:11 +00:00
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), EncryptionError> {
let mut f = try!(File::create(path));
Ok(try!(serde_yaml::to_writer(&mut f, &self)))
}
}
2017-03-10 11:43:32 +00:00
pub struct Crypto {
2017-03-18 16:22:11 +00:00
path: PathBuf,
keys: HashMap<PublicKey, SecretKey>
2017-03-10 11:43:32 +00:00
}
impl Crypto {
#[inline]
2017-03-20 17:11:03 +00:00
pub fn dummy() -> Self {
2017-04-12 06:30:42 +00:00
sodium_init();
2017-03-18 16:22:11 +00:00
Crypto { path: PathBuf::new(), keys: HashMap::new() }
2017-03-10 11:43:32 +00:00
}
2017-03-18 16:22:11 +00:00
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, EncryptionError> {
2017-04-12 06:30:42 +00:00
sodium_init();
2017-03-18 16:22:11 +00:00
let path = path.as_ref().to_owned();
let mut keys: HashMap<PublicKey, SecretKey> = HashMap::default();
for entry in try!(fs::read_dir(&path)) {
let entry = try!(entry);
let keyfile = try!(KeyfileYaml::load(entry.path()));
let public = try!(parse_hex(&keyfile.public).map_err(|_| EncryptionError::InvalidKey));
let public = try!(PublicKey::from_slice(&public).ok_or(EncryptionError::InvalidKey));
let secret = try!(parse_hex(&keyfile.secret).map_err(|_| EncryptionError::InvalidKey));
let secret = try!(SecretKey::from_slice(&secret).ok_or(EncryptionError::InvalidKey));
keys.insert(public, secret);
}
Ok(Crypto { path: path, keys: keys })
2017-03-10 11:43:32 +00:00
}
#[inline]
2017-03-18 16:22:11 +00:00
pub fn add_secret_key(&mut self, public: PublicKey, secret: SecretKey) {
self.keys.insert(public, secret);
2017-03-10 11:43:32 +00:00
}
2017-03-22 16:28:45 +00:00
#[inline]
pub fn register_keyfile<P: AsRef<Path>>(&mut self, path: P) -> Result<(), EncryptionError> {
let (public, secret) = try!(Self::load_keypair_from_file(path));
self.register_secret_key(public, secret)
}
pub fn load_keypair_from_file<P: AsRef<Path>>(path: P) -> Result<(PublicKey, SecretKey), EncryptionError> {
let keyfile = try!(KeyfileYaml::load(path));
let public = try!(parse_hex(&keyfile.public).map_err(|_| EncryptionError::InvalidKey));
let public = try!(PublicKey::from_slice(&public).ok_or(EncryptionError::InvalidKey));
let secret = try!(parse_hex(&keyfile.secret).map_err(|_| EncryptionError::InvalidKey));
let secret = try!(SecretKey::from_slice(&secret).ok_or(EncryptionError::InvalidKey));
Ok((public, secret))
}
#[inline]
pub fn save_keypair_to_file<P: AsRef<Path>>(public: &PublicKey, secret: &SecretKey, path: P) -> Result<(), EncryptionError> {
KeyfileYaml { public: to_hex(&public[..]), secret: to_hex(&secret[..]) }.save(path)
}
2017-03-10 11:43:32 +00:00
#[inline]
2017-03-18 16:22:11 +00:00
pub fn register_secret_key(&mut self, public: PublicKey, secret: SecretKey) -> Result<(), EncryptionError> {
let path = self.path.join(to_hex(&public[..]) + ".yaml");
2017-03-22 16:28:45 +00:00
try!(Self::save_keypair_to_file(&public, &secret, path));
2017-03-18 16:22:11 +00:00
self.keys.insert(public, secret);
Ok(())
2017-03-10 11:43:32 +00:00
}
#[inline]
2017-03-18 16:22:11 +00:00
pub fn contains_secret_key(&mut self, public: &PublicKey) -> bool {
self.keys.contains_key(public)
}
fn get_secret_key(&self, public: &PublicKey) -> Result<&SecretKey, EncryptionError> {
self.keys.get(public).ok_or_else(|| EncryptionError::MissingKey(*public))
2017-03-10 11:43:32 +00:00
}
#[inline]
2017-03-18 16:22:11 +00:00
pub fn encrypt(&self, enc: &Encryption, data: &[u8]) -> Result<Vec<u8>, EncryptionError> {
let &(ref method, ref public) = enc;
let public = try!(PublicKey::from_slice(public).ok_or(EncryptionError::InvalidKey));
match *method {
EncryptionMethod::Sodium => {
Ok(sealedbox::seal(data, &public))
}
}
}
#[inline]
pub fn decrypt(&self, enc: &Encryption, data: &[u8]) -> Result<Vec<u8>, EncryptionError> {
let &(ref method, ref public) = enc;
let public = try!(PublicKey::from_slice(public).ok_or(EncryptionError::InvalidKey));
let secret = try!(self.get_secret_key(&public));
match *method {
EncryptionMethod::Sodium => {
sealedbox::open(data, &public, secret).map_err(|_| EncryptionError::Operation("Decryption failed"))
}
}
2017-03-10 11:43:32 +00:00
}
2017-04-12 06:30:42 +00:00
#[inline]
pub fn gen_keypair() -> (PublicKey, SecretKey) {
sodium_init();
box_::gen_keypair()
}
2017-05-08 13:11:07 +00:00
pub fn keypair_from_password(password: &str) -> (PublicKey, SecretKey) {
let salt = pwhash::Salt::from_slice(b"the_great_zvault_password_salt_1").unwrap();
let mut key = [0u8; pwhash::HASHEDPASSWORDBYTES];
let key = pwhash::derive_key(&mut key, password.as_bytes(), &salt, pwhash::OPSLIMIT_INTERACTIVE, pwhash::MEMLIMIT_INTERACTIVE).unwrap();
let mut seed = [0u8; 32];
let offset = key.len()-seed.len();
for (i, b) in seed.iter_mut().enumerate() {
*b = key[i+offset];
}
let mut pk = [0u8; 32];
let mut sk = [0u8; 32];
if unsafe { libsodium_sys::crypto_box_seed_keypair(&mut pk, &mut sk, &seed) } != 0 {
panic!("Libsodium failed");
}
(PublicKey::from_slice(&pk).unwrap(), SecretKey::from_slice(&sk).unwrap())
}
2017-03-10 11:43:32 +00:00
}