tap_agent/
agent.rs

1//! TAP Agent implementation.
2//!
3//! This module provides the core Agent functionality for the TAP Protocol:
4//! - The `Agent` trait defining core capabilities
5//! - The `DefaultAgent` implementation of the trait
6//! - Functions for sending and receiving TAP messages with DIDComm
7
8use crate::config::AgentConfig;
9use crate::crypto::MessagePacker;
10use crate::error::{Error, Result};
11use crate::message::SecurityMode;
12use async_trait::async_trait;
13use serde::de::DeserializeOwned;
14use serde_json::Value;
15use std::fmt::Debug;
16use std::sync::Arc;
17use tap_msg::message::tap_message_trait::TapMessageBody;
18
19/// A trait for agents that can send and receive TAP messages
20///
21/// This trait defines the core capabilities of a TAP Agent, including
22/// managing identity, sending messages, and receiving messages.
23#[async_trait]
24pub trait Agent: Debug + Sync + Send {
25    /// Get the agent's DID
26    ///
27    /// Returns the Decentralized Identifier (DID) that identifies this agent
28    fn get_agent_did(&self) -> &str;
29
30    /// Send a TAP message to a recipient
31    ///
32    /// This method handles:
33    /// 1. Serializing the message
34    /// 2. Determining appropriate security mode
35    /// 3. Packing the message with DIDComm
36    ///
37    /// # Parameters
38    /// * `message` - The message to send, implementing TapMessageBody
39    /// * `to` - The DID of the recipient
40    ///
41    /// # Returns
42    /// The packed message as a string, ready for transmission
43    async fn send_message<T: TapMessageBody + serde::Serialize + Send + Sync>(
44        &self,
45        message: &T,
46        to: &str,
47    ) -> Result<String>;
48
49    /// Receive and unpack a TAP message
50    ///
51    /// This method handles:
52    /// 1. Unpacking the DIDComm message
53    /// 2. Validating the message type
54    /// 3. Deserializing to the requested type
55    ///
56    /// # Parameters
57    /// * `packed_message` - The packed message as received
58    ///
59    /// # Returns
60    /// The unpacked message deserialized to type T
61    async fn receive_message<T: TapMessageBody + DeserializeOwned + Send>(
62        &self,
63        packed_message: &str,
64    ) -> Result<T>;
65}
66
67/// Default implementation of the Agent trait
68///
69/// This implementation provides the standard TAP Agent functionality
70/// using DIDComm for secure message exchange.
71#[derive(Debug)]
72pub struct DefaultAgent {
73    /// Configuration for the agent
74    config: AgentConfig,
75    /// Message packer for handling DIDComm message packing/unpacking
76    message_packer: Arc<dyn MessagePacker>,
77}
78
79impl DefaultAgent {
80    /// Create a new DefaultAgent with the given configuration and message packer
81    ///
82    /// # Parameters
83    /// * `config` - The agent configuration
84    /// * `message_packer` - The message packer for DIDComm operations
85    ///
86    /// # Returns
87    /// A new DefaultAgent instance
88    pub fn new(config: AgentConfig, message_packer: Arc<dyn MessagePacker>) -> Self {
89        Self {
90            config,
91            message_packer,
92        }
93    }
94
95    /// Determine the appropriate security mode for a message type
96    ///
97    /// This method implements TAP protocol rules for which security modes
98    /// should be used with different message types:
99    /// - Presentation messages use authenticated encryption (AuthCrypt)
100    /// - All other messages use digital signatures (Signed)
101    ///
102    /// # Parameters
103    /// * `message_type` - The type of the message
104    ///
105    /// # Returns
106    /// The appropriate SecurityMode for the message type
107    fn determine_security_mode<T: TapMessageBody>(&self) -> SecurityMode {
108        let message_type = T::message_type();
109        if message_type == crate::message::PRESENTATION_MESSAGE_TYPE {
110            SecurityMode::AuthCrypt
111        } else {
112            SecurityMode::Signed
113        }
114    }
115}
116
117#[async_trait]
118impl Agent for DefaultAgent {
119    fn get_agent_did(&self) -> &str {
120        &self.config.agent_did
121    }
122
123    async fn send_message<T: TapMessageBody + serde::Serialize + Send + Sync>(
124        &self,
125        message: &T,
126        to: &str,
127    ) -> Result<String> {
128        // Add type to message if needed
129        let mut message_obj = serde_json::to_value(message)
130            .map_err(|e| Error::Serialization(format!("Failed to serialize message: {}", e)))?;
131
132        // Ensure message has a type field
133        if message_obj.get("type").is_none() {
134            if let serde_json::Value::Object(ref mut obj) = message_obj {
135                obj.insert(
136                    "type".to_string(),
137                    serde_json::Value::String(T::message_type().to_string()),
138                );
139            }
140        }
141
142        // Validate the message
143        message.validate().map_err(|e| {
144            Error::Validation(format!(
145                "Message validation failed for type {}: {}",
146                T::message_type(),
147                e
148            ))
149        })?;
150
151        // Determine the appropriate security mode
152        let security_mode = self.determine_security_mode::<T>();
153
154        // Use message packer to pack the message
155        let packed = self
156            .message_packer
157            .pack_message(&message_obj, to, Some(self.get_agent_did()), security_mode)
158            .await?;
159
160        Ok(packed)
161    }
162
163    async fn receive_message<T: TapMessageBody + DeserializeOwned + Send>(
164        &self,
165        packed_message: &str,
166    ) -> Result<T> {
167        // Unpack the message
168        let message_value: Value = self
169            .message_packer
170            .unpack_message_value(packed_message)
171            .await?;
172
173        // Get the message type from the unpacked message
174        let message_type = message_value
175            .get("type")
176            .and_then(|t| t.as_str())
177            .ok_or_else(|| Error::Validation("Message missing 'type' field".to_string()))?;
178
179        // Validate the message type
180        if message_type != T::message_type() {
181            return Err(Error::Validation(format!(
182                "Expected message type {} but got {}",
183                T::message_type(),
184                message_type
185            )));
186        }
187
188        // Check if we need to convert to a DIDComm message first
189        if let Some(id) = message_value.get("id") {
190            // This appears to be a proper DIDComm message already
191            // Create a DIDComm message with the required fields
192            let didcomm_message = didcomm::Message {
193                id: id.as_str().unwrap_or("").to_string(),
194                typ: "application/didcomm-plain+json".to_string(),
195                type_: message_type.to_string(),
196                // Use the entire message as the body, not just the "body" field
197                body: message_value.clone(),
198                from: message_value
199                    .get("from")
200                    .and_then(|v| v.as_str())
201                    .map(|s| s.to_string()),
202                to: message_value
203                    .get("to")
204                    .and_then(|v| v.as_array())
205                    .map(|arr| {
206                        arr.iter()
207                            .filter_map(|v| v.as_str().map(|s| s.to_string()))
208                            .collect()
209                    }),
210                thid: None,
211                pthid: None,
212                extra_headers: Default::default(),
213                created_time: None,
214                expires_time: None,
215                from_prior: None,
216                attachments: None,
217            };
218
219            // Convert to the requested type using the TapMessageBody trait
220            let message = T::from_didcomm(&didcomm_message)
221                .map_err(|e| Error::Validation(format!("Failed to convert message: {}", e)))?;
222
223            // Validate the message
224            message.validate().map_err(|e| {
225                Error::Validation(format!(
226                    "Message validation failed for type {}: {}",
227                    T::message_type(),
228                    e
229                ))
230            })?;
231
232            Ok(message)
233        } else {
234            // This might be just the message body directly, try to deserialize
235            let message = serde_json::from_value::<T>(message_value).map_err(|e| {
236                Error::Serialization(format!("Failed to deserialize message: {}", e))
237            })?;
238
239            // Validate the message
240            message.validate().map_err(|e| {
241                Error::Validation(format!(
242                    "Message validation failed for type {}: {}",
243                    T::message_type(),
244                    e
245                ))
246            })?;
247
248            Ok(message)
249        }
250    }
251}