use std::env;
use std::fs::File;
use std::io::{ErrorKind, Read};
use std::path::{Path, PathBuf};
use crate::error::KeyLoadError;
use crate::PrivateKey;
pub fn current_user_search_path() -> Vec<PathBuf> {
match env::var("CYLINDER_PATH") {
Ok(value) => value
.split(':')
.map(|s| Path::new(s).to_path_buf())
.collect(),
Err(env::VarError::NotUnicode(_)) => {
let mut dir = match dirs::home_dir() {
Some(dir) => dir,
None => Path::new(".").to_path_buf(),
};
dir.push(".cylinder");
dir.push("keys");
warn!(
"Value for CYLINDER_PATH is not unicode, unable to parse path, using default {:?}",
dir
);
vec![dir]
}
Err(env::VarError::NotPresent) => {
let mut dir = match dirs::home_dir() {
Some(dir) => dir,
None => Path::new(".").to_path_buf(),
};
dir.push(".cylinder");
dir.push("keys");
vec![dir]
}
}
}
pub fn current_user_key_name() -> String {
match env::var("CYLINDER_KEY_NAME") {
Ok(value) => value,
Err(env::VarError::NotUnicode(_)) => {
let key_name = whoami::username();
warn!(
"Value for CYLINDER_KEY_NAME is not unicode, using default {:?}",
key_name
);
key_name
}
Err(env::VarError::NotPresent) => whoami::username(),
}
}
pub fn load_key(name: &str, search_path: &[PathBuf]) -> Result<Option<PrivateKey>, KeyLoadError> {
match search_path.iter().find_map(|path| {
let mut key_path = path.clone();
key_path.push(name);
key_path.set_extension("priv");
if key_path.exists() && key_path.is_file() {
match File::open(key_path) {
Ok(f) => Some(Ok(f)),
Err(e) => match e.kind() {
ErrorKind::PermissionDenied => None,
_ => Some(Err(e)),
},
}
} else {
None
}
}) {
Some(Ok(file)) => match load_key_from_file(file) {
Ok(key) => Ok(Some(key)),
Err(e) => Err(e),
},
Some(Err(err)) => Err(KeyLoadError::with_source(
Box::new(err),
"Unable to retrieve key",
)),
None => Ok(None),
}
}
pub fn load_key_from_path(path: &Path) -> Result<PrivateKey, KeyLoadError> {
match File::open(&path) {
Ok(f) => match load_key_from_file(f) {
Ok(key) => Ok(key),
Err(e) => Err(KeyLoadError::with_source(
Box::new(e),
&format!("Unable to load key from path: {:?}", path),
)),
},
Err(err) => Err(KeyLoadError::with_source(
Box::new(err),
&format!("Unable to open key file: {:?}", path),
)),
}
}
fn load_key_from_file(file: File) -> Result<PrivateKey, KeyLoadError> {
let mut key_file = file;
let mut buf = String::new();
key_file
.read_to_string(&mut buf)
.map_err(|err| KeyLoadError::with_source(Box::new(err), "Unable to read key file"))?;
match buf.lines().next() {
Some(key) => PrivateKey::new_from_hex(key.trim()).map_err(|err| {
KeyLoadError::with_source(Box::new(err), "unable to create private key from hex")
}),
None => Err(KeyLoadError::new(&format!(
"Empty key file: {:?}",
key_file
))),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::PrivateKey;
use serial_test::serial;
use std::fs::File;
use std::io::Write;
use tempdir::TempDir;
#[test]
fn load_key_success() {
let temp_dir = TempDir::new("test_key_dir").expect("Failed to create temp dir");
let key_name = "test_key.priv";
let key_path = temp_dir.path().join(key_name);
let mut temp_file =
File::create(&key_path).expect("Unable to create temp private key file");
let private_key = PrivateKey::new(vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1,
]);
writeln!(temp_file, "{}", private_key.as_hex())
.expect("Unable to write private key to file");
let retrieved_private_key = load_key("test_key", &[temp_dir.path().to_path_buf()])
.expect("Unable retrieve key from file");
assert_eq!(
retrieved_private_key.unwrap().into_bytes(),
private_key.into_bytes(),
);
}
#[test]
#[serial(env_var)]
fn load_key_env_var_success() {
let temp_dir = TempDir::new("test_key_dir").expect("Failed to create temp dir");
let key_name = "test_key.priv";
let key_path = temp_dir.path().join(key_name);
let path_value = temp_dir.path().to_str().expect("failed to make path value");
let env_key_name = "CYLINDER_KEY_NAME";
let env_key_path = "CYLINDER_PATH";
env::set_var(env_key_name, "test_key");
env::set_var(env_key_path, path_value);
assert_eq!(env::var("CYLINDER_KEY_NAME"), Ok("test_key".to_string()));
assert_eq!(env::var("CYLINDER_PATH"), Ok(path_value.to_string()));
let mut temp_file =
File::create(&key_path).expect("Unable to create temp private key file");
let private_key = PrivateKey::new(vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1,
]);
writeln!(temp_file, "{}", private_key.as_hex())
.expect("Unable to write private key to file");
let retrieved_private_key = load_key(¤t_user_key_name(), ¤t_user_search_path())
.expect("Unable retrieve key from file");
assert_eq!(
retrieved_private_key.unwrap().into_bytes(),
private_key.into_bytes()
);
env::remove_var("CYLINDER_KEY_NAME");
env::remove_var("CYLINDER_PATH");
assert!(env::var("CYLINDER_KEY_NAME").is_err());
assert!(env::var("CYLINDER_PATH").is_err());
}
#[test]
#[serial(env_var)]
fn load_key_env_var_multiple_paths_success() {
let temp_dir = TempDir::new("test_key_dir").expect("Failed to create temp dir");
let key_name = "test_key.priv";
let key_path = temp_dir.path().join(key_name);
let path_value = temp_dir.path().to_str().expect("failed to make path value");
let paths = format!("test_key/keys/:{}", path_value);
let env_key_name = "CYLINDER_KEY_NAME";
let env_key_path = "CYLINDER_PATH";
env::set_var(env_key_name, "test_key");
env::set_var(env_key_path, paths.clone());
assert_eq!(env::var("CYLINDER_KEY_NAME"), Ok("test_key".to_string()));
assert_eq!(env::var("CYLINDER_PATH"), Ok(paths.to_string()));
let mut temp_file =
File::create(&key_path).expect("Unable to create temp private key file");
let private_key = PrivateKey::new(vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1,
]);
writeln!(temp_file, "{}", private_key.as_hex())
.expect("Unable to write private key to file");
let retrieved_private_key = load_key(¤t_user_key_name(), ¤t_user_search_path())
.expect("Unable retrieve key from file");
assert_eq!(
retrieved_private_key.unwrap().into_bytes(),
private_key.into_bytes()
);
env::remove_var("CYLINDER_KEY_NAME");
env::remove_var("CYLINDER_PATH");
assert!(env::var("CYLINDER_KEY_NAME").is_err());
assert!(env::var("CYLINDER_PATH").is_err());
}
#[test]
#[serial(env_var)]
fn load_key_env_var_bad_paths_none() {
let temp_dir = TempDir::new("test_key_dir").expect("Failed to create temp dir");
let key_name = "test_key.priv";
let key_path = temp_dir.path().join(key_name);
let paths = "test_key/keys/:bad_path/keys";
let env_key_name = "CYLINDER_KEY_NAME";
let env_key_path = "CYLINDER_PATH";
env::set_var(env_key_name, "test_key");
env::set_var(env_key_path, paths.clone());
assert_eq!(env::var("CYLINDER_KEY_NAME"), Ok("test_key".to_string()));
assert_eq!(env::var("CYLINDER_PATH"), Ok(paths.to_string()));
let mut temp_file =
File::create(&key_path).expect("Unable to create temp private key file");
let private_key = PrivateKey::new(vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1,
]);
writeln!(temp_file, "{}", private_key.as_hex())
.expect("Unable to write private key to file");
let retrieved_private_key = load_key(¤t_user_key_name(), ¤t_user_search_path())
.expect("Unable retrieve key from file");
assert!(retrieved_private_key.is_none());
env::remove_var("CYLINDER_KEY_NAME");
env::remove_var("CYLINDER_PATH");
assert!(env::var("CYLINDER_KEY_NAME").is_err());
assert!(env::var("CYLINDER_PATH").is_err());
}
#[test]
#[serial(env_var)]
fn load_key_default_path_success() {
let original_home = std::env::var("HOME").expect("failed to get original home dir");
let temp_home = TempDir::new("test_key_dir").expect("Failed to create temp dir");
std::env::set_var("HOME", temp_home.path());
let mut temp_path = temp_home.path().to_path_buf();
temp_path.push(".cylinder");
temp_path.push("keys");
std::fs::create_dir_all(temp_path.clone()).expect("Unable to create key directory");
let key_file = format!("{}.priv", whoami::username());
env::remove_var("CYLINDER_KEY_NAME");
env::remove_var("CYLINDER_PATH");
assert!(env::var("CYLINDER_KEY_NAME").is_err());
assert!(env::var("CYLINDER_PATH").is_err());
env::set_current_dir(&temp_path).unwrap();
let mut temp_file = File::create(key_file).expect("Unable to create temp private key file");
let private_key = PrivateKey::new(vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1,
]);
writeln!(temp_file, "{}", private_key.as_hex())
.expect("Unable to write private key to file");
let retrieved_private_key = load_key(¤t_user_key_name(), ¤t_user_search_path())
.expect("Unable retrieve key from file");
std::env::set_var("HOME", original_home);
assert_eq!(
retrieved_private_key.unwrap().into_bytes(),
private_key.into_bytes()
);
}
#[test]
fn load_key_from_path_success() {
let temp_dir = TempDir::new("test_key_dir").expect("Failed to create temp dir");
let key_name = "test_key.priv";
let key_path = temp_dir.path().join(key_name);
let mut temp_file =
File::create(&key_path).expect("Unable to create temp private key file");
let private_key = PrivateKey::new(vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1,
]);
writeln!(temp_file, "{}", private_key.as_hex())
.expect("Unable to write private key to file");
let retrieved_private_key =
load_key_from_path(&key_path).expect("Unable retrieve key from file");
assert_eq!(retrieved_private_key.into_bytes(), private_key.into_bytes());
}
#[test]
fn load_key_from_path_fail_empty_file() {
let temp_dir = TempDir::new("test_key_dir").expect("Failed to create temp dir");
let key_name = "test_key.priv";
let key_path = temp_dir.path().join(key_name);
File::create(&key_path).expect("Unable to create temp private key file");
assert!(load_key_from_path(&key_path).is_err());
}
#[test]
fn load_key_from_path_fail_bad_path() {
let temp_dir = TempDir::new("test_key_dir").expect("Failed to create temp dir");
let key_name = "test_key.priv";
let key_path = temp_dir.path().join(key_name);
let bad_path = Path::new("bad_path/keys/bad_file.priv");
let mut temp_file =
File::create(&key_path).expect("Unable to create temp private key file");
let private_key = PrivateKey::new(vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1,
]);
writeln!(temp_file, "{}", private_key.as_hex())
.expect("Unable to write private key to file");
assert!(load_key_from_path(&bad_path).is_err());
}
}