zoe_wire_protocol/connection_info.rs
1//! Connection information for authenticated connections
2//!
3//! This module provides the `ConnectionInfo` type that tracks both transport-level
4//! and application-level authentication for active connections in the Zoe protocol.
5
6use crate::VerifyingKey;
7use std::collections::HashSet;
8use std::net::SocketAddr;
9use std::time::SystemTime;
10
11/// Connection information with verified cryptographic keys
12///
13/// This structure tracks both transport-level and application-level authentication
14/// for active connections in the Zoe protocol. It provides the foundation for
15/// connection-scoped message authentication across multiple key algorithms.
16///
17/// ## Authentication Layers
18///
19/// 1. **Transport Layer**: TLS certificate provides connection-level identity
20/// 2. **Application Layer**: Verified keys provide message-level authentication
21///
22/// The separation allows for different keys to be used for different purposes
23/// while maintaining a clear security model across Ed25519 and ML-DSA algorithms.
24#[derive(Debug, Clone)]
25pub struct ConnectionInfo {
26 /// The public key from the client's TLS certificate
27 ///
28 /// This key identifies the client at the transport layer and is used
29 /// for QUIC connection authentication. It remains constant for the
30 /// lifetime of the connection. Supports Ed25519, ML-DSA-44, ML-DSA-65, ML-DSA-87.
31 pub transport_public_key: VerifyingKey,
32
33 /// Set of public keys verified during challenge handshake
34 ///
35 /// These keys were proven during the initial handshake and can be used
36 /// for message-level authentication. The set is populated during the
37 /// challenge phase and remains immutable for the connection lifetime.
38 ///
39 /// Supports all key algorithms: Ed25519, ML-DSA-44, ML-DSA-65, ML-DSA-87.
40 /// Use `has_verified_key()` for membership testing.
41 pub verified_keys: HashSet<VerifyingKey>,
42
43 /// The remote network address of the client
44 pub remote_address: SocketAddr,
45
46 /// Timestamp when the connection was established
47 pub connected_at: SystemTime,
48}
49
50impl ConnectionInfo {
51 /// Create a new ConnectionInfo with the given transport public key and remote address
52 ///
53 /// # Parameters
54 ///
55 /// * `transport_public_key` - The public key from the client's TLS certificate
56 /// * `remote_address` - The remote network address of the client
57 ///
58 /// # Returns
59 ///
60 /// A new `ConnectionInfo` with empty verified keys set and current timestamp
61 pub fn new(transport_public_key: VerifyingKey, remote_address: SocketAddr) -> Self {
62 Self {
63 transport_public_key,
64 verified_keys: HashSet::new(),
65 remote_address,
66 connected_at: SystemTime::now(),
67 }
68 }
69
70 /// Create a new ConnectionInfo with verified keys
71 ///
72 /// # Parameters
73 ///
74 /// * `transport_public_key` - The public key from the client's TLS certificate
75 /// * `verified_keys` - Set of keys verified during handshake
76 /// * `remote_address` - The remote network address of the client
77 ///
78 /// # Returns
79 ///
80 /// A new `ConnectionInfo` with the provided verified keys and current timestamp
81 pub fn with_verified_keys(
82 transport_public_key: VerifyingKey,
83 verified_keys: HashSet<VerifyingKey>,
84 remote_address: SocketAddr,
85 ) -> Self {
86 Self {
87 transport_public_key,
88 verified_keys,
89 remote_address,
90 connected_at: SystemTime::now(),
91 }
92 }
93
94 /// Add a verified key to this connection
95 ///
96 /// # Parameters
97 ///
98 /// * `public_key` - The public key to add
99 pub fn add_verified_key(&mut self, public_key: VerifyingKey) {
100 self.verified_keys.insert(public_key);
101 }
102
103 /// Check if a specific public key has been verified for this connection
104 ///
105 /// This is the primary method for checking message authentication permissions.
106 /// Services should call this before processing messages that require specific
107 /// key possession proofs.
108 ///
109 /// # Parameters
110 ///
111 /// * `public_key` - The public key to check
112 ///
113 /// # Returns
114 ///
115 /// `true` if the key was successfully verified during handshake, `false` otherwise
116 ///
117 /// # Example
118 ///
119 /// ```rust
120 /// use zoe_wire_protocol::ConnectionInfo;
121 /// use std::net::SocketAddr;
122 ///
123 /// # fn example(connection_info: &ConnectionInfo, required_key: &zoe_wire_protocol::VerifyingKey) -> Result<(), String> {
124 /// // In a message service handler
125 /// if !connection_info.has_verified_key(required_key) {
126 /// return Err(format!(
127 /// "Verification required for key: {}",
128 /// hex::encode(required_key.id())
129 /// ));
130 /// }
131 /// # Ok(())
132 /// # }
133 /// ```
134 pub fn has_verified_key(&self, public_key: &VerifyingKey) -> bool {
135 self.verified_keys.contains(public_key)
136 }
137
138 /// Get the number of verified keys for this connection
139 ///
140 /// Useful for logging and debugging connection capabilities.
141 ///
142 /// # Returns
143 ///
144 /// The count of keys that were successfully verified during handshake
145 pub fn verified_key_count(&self) -> usize {
146 self.verified_keys.len()
147 }
148
149 /// Get all verified public keys as hex strings (for logging/debugging)
150 ///
151 /// Returns a vector of hex-encoded key IDs for human-readable logging.
152 /// Uses the key's ID (which is a hash for ML-DSA keys or the key bytes for Ed25519).
153 ///
154 /// # Returns
155 ///
156 /// Vector of hex strings representing the key IDs of each verified key
157 ///
158 /// # Example
159 ///
160 /// ```rust
161 /// use zoe_wire_protocol::ConnectionInfo;
162 ///
163 /// # fn example(connection_info: &ConnectionInfo) {
164 /// let key_previews = connection_info.verified_keys_hex();
165 /// println!("Connection has verified keys: {:?}", key_previews);
166 /// // Output: ["a1b2c3d4e5f6g7h8...", "9a8b7c6d5e4f3g2h..."]
167 /// # }
168 /// ```
169 pub fn verified_keys_hex(&self) -> Vec<String> {
170 self.verified_keys
171 .iter()
172 .map(|key| hex::encode(key.id()))
173 .collect()
174 }
175
176 /// Get a reference to the verified keys set
177 ///
178 /// # Returns
179 ///
180 /// A reference to the HashSet containing all verified keys
181 pub fn verified_keys(&self) -> &HashSet<VerifyingKey> {
182 &self.verified_keys
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use crate::KeyPair;
190 use std::net::{IpAddr, Ipv4Addr};
191
192 #[test]
193 fn test_connection_info_creation() {
194 let keypair = KeyPair::generate_ml_dsa44(&mut rand::thread_rng());
195 let transport_key = keypair.public_key();
196 let remote_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)), 8080);
197
198 let conn_info = ConnectionInfo::new(transport_key.clone(), remote_addr);
199
200 assert_eq!(conn_info.transport_public_key, transport_key);
201 assert_eq!(conn_info.remote_address, remote_addr);
202 assert_eq!(conn_info.verified_key_count(), 0);
203 assert!(conn_info.verified_keys().is_empty());
204 }
205
206 #[test]
207 fn test_connection_info_with_verified_keys() {
208 let keypair1 = KeyPair::generate_ml_dsa44(&mut rand::thread_rng());
209 let keypair2 = KeyPair::generate_ed25519(&mut rand::thread_rng());
210 let transport_key = keypair1.public_key();
211 let remote_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)), 8080);
212
213 let mut verified_keys = HashSet::new();
214 verified_keys.insert(keypair1.public_key());
215 verified_keys.insert(keypair2.public_key());
216
217 let conn_info = ConnectionInfo::with_verified_keys(
218 transport_key.clone(),
219 verified_keys.clone(),
220 remote_addr,
221 );
222
223 assert_eq!(conn_info.transport_public_key, transport_key);
224 assert_eq!(conn_info.remote_address, remote_addr);
225 assert_eq!(conn_info.verified_key_count(), 2);
226 assert_eq!(conn_info.verified_keys(), &verified_keys);
227 }
228
229 #[test]
230 fn test_add_verified_key() {
231 let keypair1 = KeyPair::generate_ml_dsa44(&mut rand::thread_rng());
232 let keypair2 = KeyPair::generate_ed25519(&mut rand::thread_rng());
233 let transport_key = keypair1.public_key();
234 let remote_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)), 8080);
235
236 let mut conn_info = ConnectionInfo::new(transport_key, remote_addr);
237 assert_eq!(conn_info.verified_key_count(), 0);
238
239 let test_key = keypair2.public_key();
240 conn_info.add_verified_key(test_key.clone());
241 assert_eq!(conn_info.verified_key_count(), 1);
242 assert!(conn_info.has_verified_key(&test_key));
243 }
244
245 #[test]
246 fn test_has_verified_key() {
247 let keypair1 = KeyPair::generate_ml_dsa44(&mut rand::thread_rng());
248 let keypair2 = KeyPair::generate_ed25519(&mut rand::thread_rng());
249 let keypair3 = KeyPair::generate_ml_dsa65(&mut rand::thread_rng());
250 let transport_key = keypair1.public_key();
251 let remote_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)), 8080);
252
253 let mut verified_keys = HashSet::new();
254 let verified_key = keypair2.public_key();
255 verified_keys.insert(verified_key.clone());
256
257 let conn_info =
258 ConnectionInfo::with_verified_keys(transport_key, verified_keys, remote_addr);
259
260 assert!(conn_info.has_verified_key(&verified_key));
261 assert!(!conn_info.has_verified_key(&keypair3.public_key()));
262 }
263
264 #[test]
265 fn test_verified_keys_hex() {
266 let keypair1 = KeyPair::generate_ml_dsa44(&mut rand::thread_rng());
267 let keypair2 = KeyPair::generate_ed25519(&mut rand::thread_rng());
268 let transport_key = keypair1.public_key();
269 let remote_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)), 8080);
270
271 let mut verified_keys = HashSet::new();
272 let key1 = keypair1.public_key();
273 let key2 = keypair2.public_key();
274 verified_keys.insert(key1.clone());
275 verified_keys.insert(key2.clone());
276
277 let conn_info =
278 ConnectionInfo::with_verified_keys(transport_key, verified_keys, remote_addr);
279 let hex_keys = conn_info.verified_keys_hex();
280
281 assert_eq!(hex_keys.len(), 2);
282 assert!(hex_keys.contains(&hex::encode(key1.id())));
283 assert!(hex_keys.contains(&hex::encode(key2.id())));
284 }
285}