use super::error::ConfigError;
pub trait KeySource: Send + Sync {
async fn get_key(&self) -> Result<[u8; 32], ConfigError>;
}
pub struct OsKeyringKeySource {
service: String,
account: String,
}
impl OsKeyringKeySource {
pub fn new(service: impl Into<String>, account: impl Into<String>) -> Self {
Self {
service: service.into(),
account: account.into(),
}
}
}
impl KeySource for OsKeyringKeySource {
async fn get_key(&self) -> Result<[u8; 32], ConfigError> {
let entry = keyring::Entry::new(&self.service, &self.account)
.map_err(|e| ConfigError::KeyUnavailable(format!("Failed to access keyring: {}", e)))?;
match entry.get_password() {
Ok(password) => {
let decoded = base64::Engine::decode(
&base64::engine::general_purpose::STANDARD,
&password,
)
.map_err(|e| ConfigError::InvalidKey(format!("Invalid key encoding: {}", e)))?;
if decoded.len() != 32 {
return Err(ConfigError::InvalidKey("Key must be 32 bytes".to_string()));
}
let mut key = [0u8; 32];
key.copy_from_slice(&decoded);
Ok(key)
}
Err(keyring::Error::NoEntry) => {
let key = super::crypto::XChaCha20Poly1305Cipher::generate_key();
let encoded =
base64::Engine::encode(&base64::engine::general_purpose::STANDARD, key);
if let Err(e) = entry.set_password(&encoded) {
if let Ok(password) = entry.get_password() {
let decoded = base64::Engine::decode(
&base64::engine::general_purpose::STANDARD,
&password,
)
.map_err(|e| {
ConfigError::InvalidKey(format!("Invalid key encoding: {}", e))
})?;
if decoded.len() != 32 {
return Err(ConfigError::InvalidKey(
"Key must be 32 bytes".to_string(),
));
}
let mut peer_key = [0u8; 32];
peer_key.copy_from_slice(&decoded);
return Ok(peer_key);
}
return Err(ConfigError::KeyUnavailable(format!(
"Failed to store key: {}",
e
)));
}
Ok(key)
}
Err(e) => Err(ConfigError::KeyUnavailable(format!("Keyring error: {}", e))),
}
}
}
pub struct EnvVarKeySource {
var_name: String,
}
impl EnvVarKeySource {
pub fn new(var_name: impl Into<String>) -> Self {
Self {
var_name: var_name.into(),
}
}
}
impl KeySource for EnvVarKeySource {
async fn get_key(&self) -> Result<[u8; 32], ConfigError> {
let value = std::env::var(&self.var_name).map_err(|_| {
ConfigError::KeyUnavailable(format!("Environment variable {} not set", self.var_name))
})?;
let decoded = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, &value)
.map_err(|e| ConfigError::InvalidKey(format!("Invalid base64: {}", e)))?;
if decoded.len() != 32 {
return Err(ConfigError::InvalidKey("Key must be 32 bytes".to_string()));
}
let mut key = [0u8; 32];
key.copy_from_slice(&decoded);
Ok(key)
}
}
pub struct StaticKeySource {
key: [u8; 32],
}
impl StaticKeySource {
pub fn new(key: [u8; 32]) -> Self {
Self { key }
}
}
impl KeySource for StaticKeySource {
async fn get_key(&self) -> Result<[u8; 32], ConfigError> {
Ok(self.key)
}
}