use crate::log_feature;
use crate::logging::features::LogFeature;
use crate::{
constants::SINGLE_PUBLIC_KEY_ID,
db_operations::DbOperations,
security::{
Ed25519PublicKey, KeyUtils, PublicKeyInfo, SecurityError, SecurityResult, SignedMessage,
VerificationResult,
},
};
use base64::{engine::general_purpose, Engine as _};
use serde_json::Value;
use std::sync::{Arc, RwLock};
pub struct MessageSigner {
keypair: crate::security::Ed25519KeyPair,
}
impl MessageSigner {
pub fn new(keypair: crate::security::Ed25519KeyPair) -> Self {
Self { keypair }
}
pub fn sign_message(&self, payload: Value) -> SecurityResult<SignedMessage> {
let payload_bytes = self.serialize_payload(&payload)?;
let timestamp = chrono::Utc::now().timestamp();
let mut message_to_sign = payload_bytes.clone();
message_to_sign.extend_from_slice(×tamp.to_be_bytes());
message_to_sign.extend_from_slice(SINGLE_PUBLIC_KEY_ID.as_bytes());
let signature = self.keypair.sign(&message_to_sign);
let signature_base64 = KeyUtils::signature_to_base64(&signature);
let payload_base64 = general_purpose::STANDARD.encode(&payload_bytes);
Ok(SignedMessage::new(
payload_base64,
SINGLE_PUBLIC_KEY_ID.to_string(),
signature_base64,
timestamp,
))
}
fn serialize_payload(&self, payload: &Value) -> SecurityResult<Vec<u8>> {
serde_json::to_vec(payload).map_err(|e| SecurityError::SerializationError(e.to_string()))
}
}
pub struct MessageVerifier {
public_key: Arc<RwLock<Option<PublicKeyInfo>>>,
db_ops: Option<Arc<DbOperations>>,
max_timestamp_drift: i64,
}
impl MessageVerifier {
pub fn new(max_timestamp_drift: i64) -> Self {
Self {
public_key: Arc::new(RwLock::new(None)),
db_ops: None,
max_timestamp_drift,
}
}
pub async fn new_with_persistence(
max_timestamp_drift: i64,
db_ops: Arc<DbOperations>,
) -> SecurityResult<Self> {
let verifier = Self {
public_key: Arc::new(RwLock::new(None)),
db_ops: Some(db_ops),
max_timestamp_drift,
};
verifier.load_persisted_key_async().await?;
Ok(verifier)
}
async fn load_persisted_key_async(&self) -> SecurityResult<()> {
if let Some(db_ops) = &self.db_ops {
match db_ops.get_system_public_key().await {
Ok(Some(persisted_key)) => {
let mut key_lock = self.public_key.write().map_err(|_| {
SecurityError::KeyNotFound("Failed to acquire write lock".to_string())
})?;
*key_lock = Some(persisted_key);
log_feature!(
LogFeature::Permissions,
info,
"Loaded system public key from database"
);
}
Ok(None) => {
log_feature!(
LogFeature::Permissions,
info,
"No system public key found in database."
);
}
Err(e) => {
log_feature!(
LogFeature::Permissions,
warn,
"Failed to load persisted public key: {}",
e
);
}
}
}
Ok(())
}
async fn persist_public_key(&self, key_info: &PublicKeyInfo) -> SecurityResult<()> {
if let Some(db_ops) = &self.db_ops {
match db_ops.store_system_public_key(key_info).await {
Ok(()) => {
log_feature!(
LogFeature::Permissions,
debug,
"Persisted system public key"
);
Ok(())
}
Err(e) => {
log_feature!(
LogFeature::Permissions,
error,
"Failed to persist system public key: {}",
e
);
Ok(())
}
}
} else {
Ok(())
}
}
pub async fn register_system_public_key(&self, key_info: PublicKeyInfo) -> SecurityResult<()> {
let mut key_to_store = key_info;
key_to_store.id = SINGLE_PUBLIC_KEY_ID.to_string();
{
let mut key = self.public_key.write().map_err(|_| {
SecurityError::KeyNotFound("Failed to acquire write lock".to_string())
})?;
*key = Some(key_to_store.clone());
}
self.persist_public_key(&key_to_store).await?;
log_feature!(
LogFeature::Permissions,
info,
"Registered system public key"
);
Ok(())
}
pub async fn remove_system_public_key(&self) -> SecurityResult<()> {
{
let mut key = self.public_key.write().map_err(|_| {
SecurityError::KeyNotFound("Failed to acquire write lock".to_string())
})?;
*key = None;
}
if let Some(db_ops) = &self.db_ops {
match db_ops.delete_system_public_key().await {
Ok(_) => log_feature!(
LogFeature::Permissions,
debug,
"Removed system public key from database"
),
Err(e) => log_feature!(
LogFeature::Permissions,
error,
"Failed to remove system public key from database: {}",
e
),
}
}
Ok(())
}
pub fn get_system_public_key(&self) -> SecurityResult<Option<PublicKeyInfo>> {
Ok(self
.public_key
.read()
.map_err(|_| SecurityError::KeyNotFound("Failed to acquire read lock".to_string()))?
.clone())
}
pub fn list_public_keys(&self) -> SecurityResult<Vec<PublicKeyInfo>> {
let key = self
.public_key
.read()
.map_err(|_| SecurityError::KeyNotFound("Failed to acquire read lock".to_string()))?;
if let Some(k) = &*key {
Ok(vec![k.clone()])
} else {
Ok(vec![])
}
}
pub fn verify_message(
&self,
signed_message: &SignedMessage,
) -> SecurityResult<VerificationResult> {
let key_info = match self.get_system_public_key()? {
Some(info) => info,
None => {
return Ok(VerificationResult::failure(
"System public key not found".to_string(),
))
}
};
if !key_info.is_valid() {
return Ok(VerificationResult::failure(
"Public key is not valid (expired or inactive)".to_string(),
));
}
let timestamp_valid = self.is_timestamp_valid(signed_message.timestamp);
let public_key = match Ed25519PublicKey::from_base64(&key_info.public_key) {
Ok(key) => key,
Err(e) => {
return Ok(VerificationResult::failure(format!(
"Invalid public key format: {}",
e
)))
}
};
let signature = match KeyUtils::signature_from_base64(&signed_message.signature) {
Ok(sig) => sig,
Err(e) => {
return Ok(VerificationResult::failure(format!(
"Invalid signature format: {}",
e
)))
}
};
let message_to_verify = match self.create_message_to_verify(signed_message) {
Ok(msg) => msg,
Err(e) => {
return Ok(VerificationResult::failure(format!(
"Failed to recreate message: {}",
e
)))
}
};
let is_valid = public_key.verify(&message_to_verify, &signature);
if is_valid && timestamp_valid {
Ok(VerificationResult::success(key_info, timestamp_valid))
} else {
Ok(VerificationResult::failure(
"Signature verification failed".to_string(),
))
}
}
fn is_timestamp_valid(&self, timestamp: i64) -> bool {
let current_time = chrono::Utc::now().timestamp();
(current_time - timestamp).abs() <= self.max_timestamp_drift
}
fn create_message_to_verify(&self, signed_message: &SignedMessage) -> SecurityResult<Vec<u8>> {
let mut message = general_purpose::STANDARD
.decode(&signed_message.payload)
.map_err(|e| SecurityError::DeserializationError(e.to_string()))?;
message.extend_from_slice(&signed_message.timestamp.to_be_bytes());
message.extend_from_slice(signed_message.public_key_id.as_bytes());
Ok(message)
}
pub fn verify_message_with_permissions(
&self,
signed_message: &SignedMessage,
required_permissions: &[String],
) -> SecurityResult<VerificationResult> {
let verification_result = self.verify_message(signed_message)?;
if !verification_result.is_valid {
return Ok(verification_result);
}
if let Some(key_info) = &verification_result.public_key_info {
for perm in required_permissions {
if !key_info.permissions.contains(perm) {
return Ok(VerificationResult::failure(format!(
"Missing required permission: {}",
perm
)));
}
}
}
Ok(verification_result)
}
}
pub struct SigningUtils;
impl SigningUtils {
pub fn create_signer_from_secret(secret_key_base64: &str) -> SecurityResult<MessageSigner> {
let secret_key_bytes = general_purpose::STANDARD
.decode(secret_key_base64)
.map_err(|e| SecurityError::InvalidKeyFormat(e.to_string()))?;
let keypair = crate::security::Ed25519KeyPair::from_secret_key(&secret_key_bytes)?;
Ok(MessageSigner::new(keypair))
}
pub fn get_message_owner(verification_result: &VerificationResult) -> Option<String> {
verification_result
.public_key_info
.as_ref()
.map(|info| info.owner_id.clone())
}
pub fn is_verification_successful(result: &VerificationResult) -> bool {
result.is_valid
}
}
#[cfg(test)]
mod persistence_tests {
use super::*;
use crate::security::Ed25519KeyPair;
use crate::testing_utils::TestDatabaseFactory;
#[tokio::test]
async fn test_message_verifier_persistence() {
let (db_ops, _) = TestDatabaseFactory::create_test_environment()
.await
.unwrap();
let verifier = MessageVerifier::new_with_persistence(60, db_ops.clone())
.await
.unwrap();
let keypair = Ed25519KeyPair::generate().unwrap();
let public_key_base64 = keypair.public_key_base64();
let key_info = PublicKeyInfo::new(
"test_key".to_string(),
public_key_base64,
"test_owner".to_string(),
vec!["read".to_string()],
);
verifier
.register_system_public_key(key_info.clone())
.await
.unwrap();
let verifier2 = MessageVerifier::new_with_persistence(60, db_ops)
.await
.unwrap();
let retrieved = verifier2.get_system_public_key().unwrap();
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap().id, SINGLE_PUBLIC_KEY_ID.to_string());
}
#[tokio::test]
async fn test_remove_public_key_persistence() {
let (db_ops, _) = TestDatabaseFactory::create_test_environment()
.await
.unwrap();
let verifier = MessageVerifier::new_with_persistence(60, db_ops.clone())
.await
.unwrap();
let keypair = Ed25519KeyPair::generate().unwrap();
let public_key_base64 = keypair.public_key_base64();
let key_info = PublicKeyInfo::new(
"test_key".to_string(),
public_key_base64,
"test_owner".to_string(),
vec!["read".to_string()],
);
verifier.register_system_public_key(key_info).await.unwrap();
assert!(verifier.get_system_public_key().unwrap().is_some());
verifier.remove_system_public_key().await.unwrap();
assert!(verifier.get_system_public_key().unwrap().is_none());
let verifier2 = MessageVerifier::new_with_persistence(60, db_ops)
.await
.unwrap();
let retrieved = verifier2.get_system_public_key().unwrap();
assert!(retrieved.is_none());
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::security::Ed25519KeyPair;
use serde_json::json;
#[tokio::test]
async fn test_message_signing_and_verification() {
let signer_keypair = Ed25519KeyPair::generate().unwrap();
let signer = MessageSigner::new(signer_keypair);
let verifier = MessageVerifier::new(60);
let public_key_base64 = signer.keypair.public_key_base64();
let key_info = PublicKeyInfo::new(
SINGLE_PUBLIC_KEY_ID.to_string(),
public_key_base64,
"test_owner".to_string(),
vec!["read".to_string()],
);
verifier.register_system_public_key(key_info).await.unwrap();
let payload = json!({"data": "hello world"});
let signed_message = signer.sign_message(payload).unwrap();
let result = verifier.verify_message(&signed_message).unwrap();
assert!(result.is_valid);
assert!(result.timestamp_valid);
assert_eq!(
result.public_key_info.unwrap().id,
SINGLE_PUBLIC_KEY_ID.to_string()
);
}
#[tokio::test]
async fn test_permission_verification() {
let signer_keypair = Ed25519KeyPair::generate().unwrap();
let signer = MessageSigner::new(signer_keypair);
let verifier = MessageVerifier::new(60);
let public_key_base64 = signer.keypair.public_key_base64();
let key_info = PublicKeyInfo::new(
SINGLE_PUBLIC_KEY_ID.to_string(),
public_key_base64,
"test_owner".to_string(),
vec!["read".to_string()],
);
verifier.register_system_public_key(key_info).await.unwrap();
let payload = json!({"action": "read_data"});
let signed_message = signer.sign_message(payload).unwrap();
let result1 = verifier
.verify_message_with_permissions(&signed_message, &["read".to_string()])
.unwrap();
assert!(result1.is_valid);
let result2 = verifier
.verify_message_with_permissions(&signed_message, &["write".to_string()])
.unwrap();
assert!(!result2.is_valid);
assert!(result2
.error
.unwrap()
.contains("Missing required permission"));
}
#[tokio::test]
async fn test_timestamp_validation() {
let signer_keypair = Ed25519KeyPair::generate().unwrap();
let signer = MessageSigner::new(signer_keypair);
let verifier = MessageVerifier::new(5);
let public_key_base64 = signer.keypair.public_key_base64();
let key_info = PublicKeyInfo::new(
"test_key".to_string(),
public_key_base64,
"test_owner".to_string(),
vec![],
);
verifier.register_system_public_key(key_info).await.unwrap();
let valid_payload = json!({"msg": "valid"});
let valid_message = signer.sign_message(valid_payload).unwrap();
let valid_result = verifier.verify_message(&valid_message).unwrap();
assert!(valid_result.is_valid);
assert!(valid_result.timestamp_valid);
let expired_payload = json!({"msg": "expired"});
let mut expired_message = signer.sign_message(expired_payload).unwrap();
expired_message.timestamp -= 10; let expired_result = verifier.verify_message(&expired_message).unwrap();
assert!(!expired_result.timestamp_valid);
}
}