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}