use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use crate::error::KeyLoadError;
use crate::PrivateKey;
pub fn load_user_key(
key_name: Option<&str>,
default_path: &str,
) -> Result<PrivateKey, KeyLoadError> {
let name: String = match key_name {
Some(name) => String::from(name),
None => {
if let Ok(user) = env::var("USER") {
user
} else {
whoami::username()
}
}
};
let key_path = match env::var(&default_path) {
Ok(path) => path,
Err(_) => default_path.to_string(),
};
if name.contains('/') {
Ok(load_key_file(None, &name)?)
} else {
let name = match env::var(&name) {
Ok(val) => val,
Err(_) => name,
};
if key_path.contains(':') {
let paths = key_path.split(':');
for path in paths {
match load_key_file(Some(name.clone()), &path) {
Ok(key) => return Ok(key),
Err(_) => continue,
}
}
Err(KeyLoadError(format!(
"Failed to find key file in {}",
&key_path
)))
} else {
Ok(load_key_file(Some(name), &key_path)?)
}
}
}
fn load_key_file(key_name: Option<String>, key_path: &str) -> Result<PrivateKey, KeyLoadError> {
let mut path = PathBuf::from(key_path);
if let Some(key_name) = key_name {
path.push(key_name);
}
if path.exists() {
read_private_key(path)
} else {
path.set_extension("priv");
if path.exists() {
read_private_key(path)
} else {
Err(KeyLoadError(format!(
"Failed to load key: could not be found {}",
path.as_path().display()
)))
}
}
}
fn read_private_key(path: PathBuf) -> Result<PrivateKey, KeyLoadError> {
let mut file = File::open(&path).map_err(|err| {
KeyLoadError(format!(
"Unable to open key file '{}': {}",
path.display(),
err,
))
})?;
let mut buf = String::new();
file.read_to_string(&mut buf).map_err(|err| {
KeyLoadError(format!(
"Unable to read key file '{}': {}",
path.display(),
err,
))
})?;
let key = match buf.lines().next() {
Some(k) => k.trim().to_string(),
None => {
return Err(KeyLoadError(format!("Empty key file: {}", path.display())));
}
};
Ok(PrivateKey::new_from_hex(&key)
.map_err(|err| KeyLoadError(format!("unable to create private key from hex: {}", err)))?)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::PrivateKey;
use std::fs::File;
use std::io::Write;
use tempdir::TempDir;
#[test]
fn retrieve_key_success() {
let temp_dir = TempDir::new("test_key_dir").expect("Failed to create temp dir");
let key_name = "test_key.priv";
let default_path = temp_dir
.path()
.to_str()
.expect("Failed to get default path");
let key_path = temp_dir
.path()
.join(key_name)
.to_str()
.expect("Failed to get path")
.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_user_key(Some(key_name), default_path).expect("Unable retrieve key from file");
assert_eq!(retrieved_private_key.into_bytes(), private_key.into_bytes(),);
}
#[test]
fn retrieve_key_success_no_keyname() {
let temp_dir = TempDir::new("test_key_dir").expect("Failed to create temp dir");
let key = "USER";
env::set_var(key, "test_user");
assert_eq!(env::var("USER"), Ok("test_user".to_string()));
let default_path = temp_dir
.path()
.to_str()
.expect("Failed to get default path");
let key_path = temp_dir
.path()
.join("test_user.priv")
.to_str()
.expect("Failed to get path")
.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_user_key(None, default_path).expect("Unable retrieve key from file");
assert_eq!(retrieved_private_key.into_bytes(), private_key.into_bytes(),);
}
#[test]
fn retrieve_key_success_environment_var() {
let temp_dir = TempDir::new("test_key_dir").expect("Failed to create temp dir");
let key_name = "test_key.priv";
let default_path = temp_dir
.path()
.to_str()
.expect("Failed to get default path");
let key_path = temp_dir
.path()
.join(key_name)
.to_str()
.expect("Failed to get path")
.to_string();
let key = "TEST_KEY_PATH";
env::set_var(key, default_path);
assert_eq!(env::var("TEST_KEY_PATH"), Ok(default_path.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_user_key(Some(key_name), "TEST_KEY_PATH").expect("Unable retrieve key from file");
assert_eq!(retrieved_private_key.into_bytes(), private_key.into_bytes(),);
}
#[test]
fn retrieve_key_success_environment_var2() {
let temp_dir = TempDir::new("test_key_dir").expect("Failed to create temp dir");
let key_name = "test_key.priv";
let default_path = temp_dir
.path()
.to_str()
.expect("Failed to get default path");
let key_path = temp_dir
.path()
.join(key_name)
.to_str()
.expect("Failed to get path")
.to_string();
let paths = format!("test_key/keys:{}", default_path);
let key = "TEST_PATH_MULTIPLE";
env::set_var(key, paths.clone());
assert_eq!(
env::var("TEST_PATH_MULTIPLE"),
Ok(paths.clone().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_user_key(Some(key_name), "TEST_PATH_MULTIPLE")
.expect("Unable retrieve key from file");
assert_eq!(retrieved_private_key.into_bytes(), private_key.into_bytes(),);
}
#[test]
fn retrieve_key_fail_empty_file() {
let temp_dir = TempDir::new("test_key_dir").expect("Failed to create temp dir");
let key_name = "test_key.priv";
let default_path = temp_dir
.path()
.to_str()
.expect("Failed to get default path");
let key_path = temp_dir
.path()
.join(key_name)
.to_str()
.expect("Failed to get path")
.to_string();
File::create(&key_path).expect("Unable to create temp private key file");
assert!(load_user_key(Some(key_name), default_path).is_err());
}
}