diff --git a/CHANGELOG.md b/CHANGELOG.md index 855d67d..20c0b58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This project follows [semantic versioning](http://semver.org). ### UNRELEASED +* [added] Derive key pairs from passwords * [modified] Added root repository to exclude list * [modified] Initializing data in index before use diff --git a/Cargo.lock b/Cargo.lock index 7a7971d..f88b52c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,7 @@ dependencies = [ "index 0.1.0", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", + "libsodium-sys 0.0.14 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "murmurhash3 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "pbr 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 3142e21..4c06aed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ log = "0.3" byteorder = "1.0" ansi_term = "0.9" sodiumoxide = "0.0.14" +libsodium-sys = "0.0.14" filetime = "0.1" regex = "0.2" fuse = "0.3" diff --git a/docs/man/zvault-addkey.1.md b/docs/man/zvault-addkey.1.md index 109ceaa..d8196bc 100644 --- a/docs/man/zvault-addkey.1.md +++ b/docs/man/zvault-addkey.1.md @@ -32,6 +32,11 @@ key will be set as default encryption key. Set the key pair as default + * `-p`, `--password `: + + Derive the key pair from the given password instead of randomly creating it. + + * `-h`, `--help`: Prints help information diff --git a/docs/man/zvault-genkey.1.md b/docs/man/zvault-genkey.1.md index fab9fd3..21dffdf 100644 --- a/docs/man/zvault-genkey.1.md +++ b/docs/man/zvault-genkey.1.md @@ -14,6 +14,10 @@ writes it to the given file `FILE`. ## OPTIONS + * `-p`, `--password `: + + Derive the key pair from the given password instead of randomly creating it. + * `-h`, `--help`: Prints help information diff --git a/src/cli/args.rs b/src/cli/args.rs index c587546..63a301c 100644 --- a/src/cli/args.rs +++ b/src/cli/args.rs @@ -116,11 +116,13 @@ pub enum Arguments { hash: Option }, GenKey { - file: Option + file: Option, + password: Option }, AddKey { repo_path: String, file: Option, + password: Option, set_default: bool }, AlgoTest { @@ -399,11 +401,14 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> { .arg(Arg::from_usage(" 'Path of the repository'") .validator(|val| validate_repo_path(val, true, Some(false), Some(false))))) .subcommand(SubCommand::with_name("genkey").about("Generate a new key pair") + .arg(Arg::from_usage("-p --password [PASSWORD] 'Derive the key pair from the given password'")) .arg(Arg::from_usage("[FILE] 'Destination file for the keypair'"))) .subcommand(SubCommand::with_name("addkey").about("Add a key pair to the repository") .arg(Arg::from_usage("-g --generate 'Generate a new key pair'") - .conflicts_with("FILE")) + .conflicts_with_all(&["FILE", "PASSWORD"])) .arg(Arg::from_usage("[set_default] --default -d 'Set the key pair as default'")) + .arg(Arg::from_usage("-p --password [PASSWORD] 'Derive the key pair from the given password'") + .conflicts_with("FILE")) .arg(Arg::from_usage(" 'Path of the repository'") .validator(|val| validate_repo_path(val, true, Some(false), Some(false)))) .arg(Arg::from_usage("[FILE] 'File containing the keypair'") @@ -592,7 +597,8 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> { }, ("genkey", Some(args)) => { Arguments::GenKey { - file: args.value_of("FILE").map(|v| v.to_string()) + file: args.value_of("FILE").map(|v| v.to_string()), + password: args.value_of("password").map(|v| v.to_string()) } }, ("addkey", Some(args)) => { @@ -600,6 +606,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> { Arguments::AddKey { repo_path: repository.to_string(), set_default: args.is_present("set_default"), + password: args.value_of("password").map(|v| v.to_string()), file: args.value_of("FILE").map(|v| v.to_string()) } }, diff --git a/src/cli/mod.rs b/src/cli/mod.rs index f8d0b55..9961e12 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -625,8 +625,11 @@ pub fn run() -> Result<(), ErrorCode> { print_config(&repo.config); } }, - Arguments::GenKey{file} => { - let (public, secret) = Crypto::gen_keypair(); + Arguments::GenKey{file, password} => { + let (public, secret) = match password { + None => Crypto::gen_keypair(), + Some(ref password) => Crypto::keypair_from_password(password) + }; info!("Created the following key pair"); println!("public: {}", to_hex(&public[..])); println!("secret: {}", to_hex(&secret[..])); @@ -634,13 +637,16 @@ pub fn run() -> Result<(), ErrorCode> { checked!(Crypto::save_keypair_to_file(&public, &secret, file), "save key pair", ErrorCode::SaveKey); } }, - Arguments::AddKey{repo_path, set_default, file} => { + Arguments::AddKey{repo_path, set_default, password, file} => { let mut repo = try!(open_repository(&repo_path)); let (public, secret) = if let Some(file) = file { checked!(Crypto::load_keypair_from_file(file), "load key pair", ErrorCode::LoadKey) } else { info!("Created the following key pair"); - let (public, secret) = Crypto::gen_keypair(); + let (public, secret) = match password { + None => Crypto::gen_keypair(), + Some(ref password) => Crypto::keypair_from_password(password) + }; println!("public: {}", to_hex(&public[..])); println!("secret: {}", to_hex(&secret[..])); (public, secret) diff --git a/src/main.rs b/src/main.rs index b98c543..bbef9fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ extern crate chrono; #[macro_use] extern crate log; extern crate byteorder; extern crate sodiumoxide; +extern crate libsodium_sys; extern crate ansi_term; extern crate filetime; extern crate regex; diff --git a/src/util/encryption.rs b/src/util/encryption.rs index 46010fc..31499e1 100644 --- a/src/util/encryption.rs +++ b/src/util/encryption.rs @@ -7,9 +7,11 @@ use std::sync::{Once, ONCE_INIT}; use serde_yaml; use serde_bytes::ByteBuf; +use libsodium_sys; use sodiumoxide; use sodiumoxide::crypto::sealedbox; use sodiumoxide::crypto::box_; +use sodiumoxide::crypto::pwhash; pub use sodiumoxide::crypto::box_::{SecretKey, PublicKey}; use ::util::*; @@ -211,4 +213,21 @@ impl Crypto { sodium_init(); box_::gen_keypair() } + + 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()) + } }