#[cfg(test)]
mod unit_tests;
use crate::{
config::ConfigValidation,
core::{KeyPair, KeyStorage},
crypto::translator::KeyPairInstance,
errors::Result,
persistence::{deserialize_keypair, serialize_keypair},
KeyIdentifier, NewKeyId,
};
use base64::{decode, encode};
use hashicorp_vault::client::{TokenData, VaultClient};
use secrecy::{ExposeSecret, Secret};
use url::Url;
const KEYPAIR_PREFIX: &str = "kp-";
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct HashicorpVaultKeyManagerConfig {
pub vault_path: String,
pub vault_token: String,
pub vault_addr: Url,
}
impl ConfigValidation for HashicorpVaultKeyManagerConfig {
fn validate(&self) -> Result<()> {
VaultClient::new(self.vault_addr.as_str(), &self.vault_token)?;
Ok(())
}
}
pub(crate) struct HashicorpVaultKeyManager {
pub(crate) client: VaultClient<TokenData>,
pub(crate) vault_path: String,
}
impl HashicorpVaultKeyManager {
pub(crate) fn new(config: HashicorpVaultKeyManagerConfig) -> Self {
let mut path = config.vault_path;
if path.starts_with('/') {
path.remove(0);
}
if !path.ends_with('/') {
path.push('/');
}
HashicorpVaultKeyManager {
client: VaultClient::new(config.vault_addr.as_str(), &config.vault_token).unwrap(),
vault_path: path,
}
}
fn get_key_by_string(&self, key: &str) -> Result<KeyPairInstance> {
let encoded = Secret::new(self.client.get_secret(self.vault_path.clone() + key)?);
let serialized: String = String::from_utf8(decode(encoded.expose_secret())?)?;
deserialize_keypair(Secret::new(serialized))
}
fn get_keys_at_vault_path(&self) -> Result<Vec<String>> {
let entries = self.client.list_secrets(&self.vault_path);
Ok(
if let Err(hashicorp_vault::Error::VaultResponse(err, response)) = entries {
if response.status() == reqwest::StatusCode::NOT_FOUND {
Ok(vec![])
} else {
Err(hashicorp_vault::Error::VaultResponse(err, response))
}
} else {
entries
}?,
)
}
}
impl KeyStorage for HashicorpVaultKeyManager {
fn store_has_key(&self, id: &KeyIdentifier) -> Result<bool> {
let entries = &self.get_keys_at_vault_path()?;
Ok(entries.contains(&format!("{}{}", KEYPAIR_PREFIX, id.value)))
}
fn get_key(&self, id: &KeyIdentifier) -> Result<KeyPairInstance> {
let key = format!("{}{}", KEYPAIR_PREFIX, id.value);
self.get_key_by_string(&key)
}
fn get_keys(&self) -> Result<Vec<KeyIdentifier>> {
let entries = &self.get_keys_at_vault_path()?;
let keypair_keys = entries.iter().filter(|key| key.starts_with(KEYPAIR_PREFIX));
keypair_keys
.map(|key| Ok(self.get_key_by_string(key)?.identifier()))
.collect()
}
fn save_key(&mut self, key_pair: &KeyPairInstance) -> Result<NewKeyId> {
let serialized = serialize_keypair(key_pair)?;
let id = key_pair.identifier();
let encoded = Secret::new(encode(serialized.expose_secret()));
let path = format!("{}{}{}", &self.vault_path, KEYPAIR_PREFIX, id.value);
self.client.set_secret(path, encoded.expose_secret())?;
Ok(NewKeyId {
id,
pubkey: key_pair.public(),
})
}
}