#[cfg(test)]
mod unit_tests;
use crate::core::KeyStorage;
use crate::{
config::ConfigValidation,
core::KeyPair,
crypto::translator::KeyPairInstance,
errors::{IoError, KeyManagementError, Result},
persistence::serialize_keypair,
persistence::{deserialize_keypair, KEYPAIR_FILE_PREFIX},
KeyIdentifier, NewKeyId,
};
use secrecy::{ExposeSecret, Secret};
use snafu::ResultExt;
use std::{
fs,
path::{Path, PathBuf},
};
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FileKeyManagerConfig {
pub keypair_directory_path: String,
}
impl ConfigValidation for FileKeyManagerConfig {
fn validate(&self) -> Result<()> {
let path = PathBuf::from(&self.keypair_directory_path);
if !path.is_dir() || !path.exists() {
return Err(KeyManagementError::InvalidDirectoryPath {
path: self.keypair_directory_path.clone(),
});
}
fs::read_dir(&self.keypair_directory_path).context(IoError {
message: format!(
"Could not read directory {:?}",
&self.keypair_directory_path
),
})?;
Ok(())
}
}
pub(crate) struct FileKeyManager {
pub keypair_directory_path: PathBuf,
}
impl FileKeyManager {
pub(crate) fn new(config: FileKeyManagerConfig) -> Self {
FileKeyManager {
keypair_directory_path: PathBuf::from(&config.keypair_directory_path),
}
}
}
fn starts_with_keypair_prefix(path: &Path) -> Option<bool> {
let filename = path.file_name()?.to_str()?;
Some(filename.starts_with(KEYPAIR_FILE_PREFIX))
}
impl KeyStorage for FileKeyManager {
fn store_has_key(&self, id: &KeyIdentifier) -> Result<bool> {
let filepath = self
.keypair_directory_path
.join(format!("{}{}", KEYPAIR_FILE_PREFIX, id.value));
Ok(filepath.exists())
}
fn get_key(&self, id: &KeyIdentifier) -> Result<KeyPairInstance> {
let filepath = self
.keypair_directory_path
.join(format!("{}{}", KEYPAIR_FILE_PREFIX, id.value));
if !filepath.exists() {
return Err(KeyManagementError::AddressNotFound {
address: id.value.clone(),
});
}
let serialized = Secret::new(fs::read_to_string(&filepath).context(IoError {
message: format!("Error reading from filepath {:?}", &filepath),
})?);
deserialize_keypair(serialized)
}
fn get_keys(&self) -> Result<Vec<KeyIdentifier>> {
let entries = fs::read_dir(&self.keypair_directory_path).context(IoError {
message: format!(
"Could not read directory {:?}",
&self.keypair_directory_path
),
})?;
let filepaths = entries
.flat_map(|res| res.map(|e| e.path()))
.filter(|path| !path.is_dir())
.filter(|path| starts_with_keypair_prefix(path).unwrap_or(false));
filepaths
.map(|path| {
let data = fs::read_to_string(&path).context(IoError {
message: format!("Error reading from filepath {:?}", path),
})?;
let keypair = deserialize_keypair(Secret::new(data))?;
Ok(keypair.identifier())
})
.collect()
}
fn save_key(&mut self, key_pair: &KeyPairInstance) -> Result<NewKeyId> {
let serialized = serialize_keypair(key_pair)?;
let id = key_pair.identifier();
let filepath = &self
.keypair_directory_path
.join(format!("{}{}", KEYPAIR_FILE_PREFIX, id.value));
fs::write(filepath, serialized.expose_secret()).context(IoError {
message: format!("Error writing to filepath {:?}", &filepath),
})?;
Ok(NewKeyId {
id,
pubkey: key_pair.public(),
})
}
}