use async_trait::async_trait;
use std::fmt;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum KeyProviderError {
#[error("Key not found: 0x{}", hex::encode(.0))]
KeyNotFound([u8; 48]),
#[error("Signing failed: {0}")]
SigningFailed(String),
#[error("Initialization failed: {0}")]
InitializationFailed(String),
#[error("Backend error: {0}")]
BackendError(String),
#[error("Invalid key format: {0}")]
InvalidKeyFormat(String),
#[error("Operation timed out")]
Timeout,
}
#[async_trait]
pub trait KeyProvider: Send + Sync + fmt::Debug {
fn provider_name(&self) -> &str;
async fn sign(&self, pubkey: &[u8; 48], message: &[u8; 32]) -> Result<[u8; 96], KeyProviderError>;
async fn list_keys(&self) -> Result<Vec<[u8; 48]>, KeyProviderError>;
async fn has_key(&self, pubkey: &[u8; 48]) -> bool;
async fn key_count(&self) -> usize {
self.list_keys().await.map(|keys| keys.len()).unwrap_or(0)
}
async fn verify(
&self,
pubkey: &[u8; 48],
message: &[u8; 32],
signature: &[u8; 96],
) -> Result<bool, KeyProviderError> {
let _ = (pubkey, message, signature);
Ok(true)
}
}
#[derive(Debug, Clone)]
pub enum KeyProviderConfig {
Local {
keystore_dir: String,
password: String,
},
#[cfg(feature = "aws-kms")]
AwsKms {
region: String,
key_id: String,
endpoint: Option<String>,
},
#[cfg(feature = "vault")]
Vault {
address: String,
token: String,
path_prefix: String,
},
}
impl Default for KeyProviderConfig {
fn default() -> Self {
KeyProviderConfig::Local {
keystore_dir: "./keystores".to_string(),
password: String::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_provider_error_display() {
let err = KeyProviderError::KeyNotFound([1u8; 48]);
assert!(err.to_string().contains("Key not found"));
let err = KeyProviderError::SigningFailed("test error".to_string());
assert!(err.to_string().contains("Signing failed"));
}
#[test]
fn test_default_config() {
let config = KeyProviderConfig::default();
match config {
KeyProviderConfig::Local { keystore_dir, .. } => {
assert_eq!(keystore_dir, "./keystores");
}
#[allow(unreachable_patterns)]
_ => panic!("Expected Local config"),
}
}
}