use crate::Message;
#[cfg(feature = "signing")]
use crate::auth::{MessageSigner, SignatureError};
#[cfg(feature = "encryption")]
use crate::crypto::{EncryptionError, MessageEncryptor};
use std::fmt;
#[derive(Debug)]
pub enum ExtensionError {
#[cfg(feature = "signing")]
Signature(SignatureError),
#[cfg(feature = "encryption")]
Encryption(EncryptionError),
Validation(String),
Serialization(String),
}
impl fmt::Display for ExtensionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
#[cfg(feature = "signing")]
ExtensionError::Signature(e) => write!(f, "Signature error: {}", e),
#[cfg(feature = "encryption")]
ExtensionError::Encryption(e) => write!(f, "Encryption error: {}", e),
ExtensionError::Validation(msg) => write!(f, "Validation error: {}", msg),
ExtensionError::Serialization(msg) => write!(f, "Serialization error: {}", msg),
}
}
}
impl From<crate::ValidationError> for ExtensionError {
fn from(err: crate::ValidationError) -> Self {
ExtensionError::Validation(err.to_string())
}
}
impl std::error::Error for ExtensionError {}
#[cfg(feature = "signing")]
impl From<SignatureError> for ExtensionError {
fn from(e: SignatureError) -> Self {
ExtensionError::Signature(e)
}
}
#[cfg(feature = "encryption")]
impl From<EncryptionError> for ExtensionError {
fn from(e: EncryptionError) -> Self {
ExtensionError::Encryption(e)
}
}
pub trait MessageExt {
fn validate_basic(&self) -> Result<(), ExtensionError>;
fn is_expired(&self) -> bool;
fn is_scheduled(&self) -> bool;
fn get_age_seconds(&self) -> Option<i64>;
#[cfg(feature = "signing")]
fn sign_body(&self, signer: &MessageSigner) -> Result<Vec<u8>, crate::auth::SignatureError>;
#[cfg(feature = "signing")]
fn verify_body(&self, signer: &MessageSigner, signature: &[u8]) -> Result<(), ExtensionError>;
#[cfg(feature = "encryption")]
fn encrypt_body(&mut self, encryptor: &MessageEncryptor) -> Result<Vec<u8>, ExtensionError>;
#[cfg(feature = "encryption")]
fn decrypt_body(
&self,
encryptor: &MessageEncryptor,
nonce: &[u8],
) -> Result<Vec<u8>, ExtensionError>;
}
impl MessageExt for Message {
fn validate_basic(&self) -> Result<(), ExtensionError> {
self.validate().map_err(ExtensionError::from)
}
fn is_expired(&self) -> bool {
if let Some(expires) = self.headers.expires {
chrono::Utc::now() > expires
} else {
false
}
}
fn is_scheduled(&self) -> bool {
if let Some(eta) = self.headers.eta {
chrono::Utc::now() < eta
} else {
false
}
}
fn get_age_seconds(&self) -> Option<i64> {
None
}
#[cfg(feature = "signing")]
fn sign_body(&self, signer: &MessageSigner) -> Result<Vec<u8>, crate::auth::SignatureError> {
signer.sign(&self.body)
}
#[cfg(feature = "signing")]
fn verify_body(&self, signer: &MessageSigner, signature: &[u8]) -> Result<(), ExtensionError> {
signer.verify(&self.body, signature)?;
Ok(())
}
#[cfg(feature = "encryption")]
fn encrypt_body(&mut self, encryptor: &MessageEncryptor) -> Result<Vec<u8>, ExtensionError> {
let (ciphertext, nonce) = encryptor.encrypt(&self.body)?;
self.body = ciphertext;
Ok(nonce)
}
#[cfg(feature = "encryption")]
fn decrypt_body(
&self,
encryptor: &MessageEncryptor,
nonce: &[u8],
) -> Result<Vec<u8>, ExtensionError> {
let plaintext = encryptor.decrypt(&self.body, nonce)?;
Ok(plaintext)
}
}
#[cfg(feature = "signing")]
#[derive(Debug, Clone)]
pub struct SignedMessage {
pub message: Message,
pub signature: Vec<u8>,
}
#[cfg(feature = "signing")]
impl SignedMessage {
pub fn new(
message: Message,
signer: &MessageSigner,
) -> Result<Self, crate::auth::SignatureError> {
let signature = message.sign_body(signer)?;
Ok(Self { message, signature })
}
pub fn verify(&self, signer: &MessageSigner) -> Result<(), ExtensionError> {
self.message.verify_body(signer, &self.signature)
}
pub fn signature_hex(&self) -> String {
hex::encode(&self.signature)
}
}
#[cfg(feature = "encryption")]
#[derive(Debug, Clone)]
pub struct EncryptedMessage {
pub message: Message,
pub nonce: Vec<u8>,
}
#[cfg(feature = "encryption")]
impl EncryptedMessage {
pub fn new(mut message: Message, encryptor: &MessageEncryptor) -> Result<Self, ExtensionError> {
let nonce = message.encrypt_body(encryptor)?;
Ok(Self { message, nonce })
}
pub fn decrypt(&self, encryptor: &MessageEncryptor) -> Result<Vec<u8>, ExtensionError> {
self.message.decrypt_body(encryptor, &self.nonce)
}
pub fn nonce_hex(&self) -> String {
hex::encode(&self.nonce)
}
}
#[cfg(all(feature = "signing", feature = "encryption"))]
pub type SecureBuildResult = Result<(Message, Option<Vec<u8>>, Option<Vec<u8>>), ExtensionError>;
pub struct SecureMessageBuilder {
message: Message,
#[cfg(feature = "signing")]
signer: Option<MessageSigner>,
#[cfg(feature = "encryption")]
encryptor: Option<MessageEncryptor>,
}
impl SecureMessageBuilder {
pub fn new(task: String, id: uuid::Uuid, body: Vec<u8>) -> Self {
Self {
message: Message::new(task, id, body),
#[cfg(feature = "signing")]
signer: None,
#[cfg(feature = "encryption")]
encryptor: None,
}
}
#[cfg(feature = "signing")]
pub fn with_signer(mut self, key: &[u8]) -> Self {
self.signer = Some(MessageSigner::new(key));
self
}
#[cfg(feature = "encryption")]
pub fn with_encryptor(mut self, key: &[u8]) -> Result<Self, ExtensionError> {
self.encryptor = Some(MessageEncryptor::new(key)?);
Ok(self)
}
pub fn with_priority(mut self, priority: u8) -> Self {
self.message = self.message.with_priority(priority);
self
}
#[cfg(feature = "signing")]
#[cfg(not(feature = "encryption"))]
pub fn build(self) -> Result<(Message, Option<Vec<u8>>), ExtensionError> {
let signature = self.signer.as_ref().map(|s| self.message.sign_body(s));
Ok((self.message, signature))
}
#[cfg(feature = "encryption")]
#[cfg(not(feature = "signing"))]
pub fn build(mut self) -> Result<(Message, Option<Vec<u8>>), ExtensionError> {
let nonce = if let Some(enc) = self.encryptor.as_ref() {
Some(self.message.encrypt_body(enc)?)
} else {
None
};
Ok((self.message, nonce))
}
#[cfg(all(feature = "signing", feature = "encryption"))]
pub fn build_secure(mut self) -> SecureBuildResult {
let signature = self
.signer
.as_ref()
.map(|s| self.message.sign_body(s))
.transpose()?;
let nonce = if let Some(enc) = self.encryptor.as_ref() {
Some(self.message.encrypt_body(enc)?)
} else {
None
};
Ok((self.message, signature, nonce))
}
#[cfg(not(any(feature = "signing", feature = "encryption")))]
pub fn build(self) -> Message {
self.message
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::TaskArgs;
use uuid::Uuid;
#[test]
fn test_message_validate_basic() {
let task_id = Uuid::new_v4();
let body = serde_json::to_vec(&TaskArgs::new()).unwrap();
let msg = Message::new("tasks.add".to_string(), task_id, body);
assert!(msg.validate_basic().is_ok());
}
#[test]
fn test_message_is_expired() {
let task_id = Uuid::new_v4();
let body = vec![1, 2, 3];
let mut msg = Message::new("tasks.test".to_string(), task_id, body);
assert!(!msg.is_expired());
msg.headers.expires = Some(chrono::Utc::now() - chrono::Duration::hours(1));
assert!(msg.is_expired());
msg.headers.expires = Some(chrono::Utc::now() + chrono::Duration::hours(1));
assert!(!msg.is_expired());
}
#[test]
fn test_message_is_scheduled() {
let task_id = Uuid::new_v4();
let body = vec![1, 2, 3];
let mut msg = Message::new("tasks.test".to_string(), task_id, body);
assert!(!msg.is_scheduled());
msg.headers.eta = Some(chrono::Utc::now() + chrono::Duration::hours(1));
assert!(msg.is_scheduled());
msg.headers.eta = Some(chrono::Utc::now() - chrono::Duration::hours(1));
assert!(!msg.is_scheduled());
}
#[cfg(feature = "signing")]
#[test]
fn test_sign_and_verify_message() {
let task_id = Uuid::new_v4();
let body = serde_json::to_vec(&TaskArgs::new()).unwrap();
let msg = Message::new("tasks.add".to_string(), task_id, body);
let signer = MessageSigner::new(b"secret-key");
let signature = msg.sign_body(&signer).expect("signing failed in test");
assert!(msg.verify_body(&signer, &signature).is_ok());
}
#[cfg(feature = "signing")]
#[test]
fn test_signed_message_wrapper() {
let task_id = Uuid::new_v4();
let body = serde_json::to_vec(&TaskArgs::new()).unwrap();
let msg = Message::new("tasks.add".to_string(), task_id, body);
let signer = MessageSigner::new(b"secret-key");
let signed = SignedMessage::new(msg, &signer).expect("signing should not fail");
assert!(signed.verify(&signer).is_ok());
assert!(!signed.signature_hex().is_empty());
}
#[cfg(feature = "encryption")]
#[test]
fn test_encrypt_and_decrypt_message() {
let task_id = Uuid::new_v4();
let body = b"secret data".to_vec();
let mut msg = Message::new("tasks.add".to_string(), task_id, body.clone());
let encryptor = MessageEncryptor::new(b"32-byte-secret-key-for-aes-256!!").unwrap();
let nonce = msg.encrypt_body(&encryptor).unwrap();
assert_ne!(msg.body, body);
let decrypted = msg.decrypt_body(&encryptor, &nonce).unwrap();
assert_eq!(decrypted, body);
}
#[cfg(feature = "encryption")]
#[test]
fn test_encrypted_message_wrapper() {
let task_id = Uuid::new_v4();
let body = b"secret data".to_vec();
let msg = Message::new("tasks.add".to_string(), task_id, body.clone());
let encryptor = MessageEncryptor::new(b"32-byte-secret-key-for-aes-256!!").unwrap();
let encrypted = EncryptedMessage::new(msg, &encryptor).unwrap();
let decrypted = encrypted.decrypt(&encryptor).unwrap();
assert_eq!(decrypted, body);
assert!(!encrypted.nonce_hex().is_empty());
}
#[cfg(feature = "signing")]
#[test]
fn test_secure_message_builder_with_signing() {
let task_id = Uuid::new_v4();
let body = serde_json::to_vec(&TaskArgs::new()).unwrap();
let builder = SecureMessageBuilder::new("tasks.add".to_string(), task_id, body)
.with_signer(b"secret-key")
.with_priority(5);
#[cfg(not(feature = "encryption"))]
{
let (msg, signature) = builder.build().unwrap();
assert_eq!(msg.properties.priority, Some(5));
assert!(signature.is_some());
}
#[cfg(feature = "encryption")]
{
let _ = builder; }
}
#[test]
fn test_extension_error_display() {
let err = ExtensionError::Validation("test error".to_string());
assert_eq!(err.to_string(), "Validation error: test error");
let err = ExtensionError::Serialization("parse failed".to_string());
assert_eq!(err.to_string(), "Serialization error: parse failed");
}
}