p2p/webrtc/
connection_auth.rs

1//! WebRTC connection authentication.
2//!
3//! This module provides cryptographic authentication for WebRTC connections
4//! using SDP hashes and public key encryption to prevent man-in-the-middle
5//! attacks. The authentication mechanism ensures that WebRTC connections are
6//! established only between legitimate peers with verified identities.
7//!
8//! ## Security Model
9//!
10//! The connection authentication process works by:
11//!
12//! 1. **SDP Hash Combination**: Combining the SDP hashes from both the WebRTC
13//!    offer and answer to create a unique authentication token
14//! 2. **Public Key Encryption**: Encrypting the authentication data using the
15//!    recipient's public key to ensure only they can decrypt it
16//! 3. **Mutual Verification**: Both parties verify each other's ability to
17//!    decrypt the authentication data, proving they possess the correct private
18//!    keys
19//!
20//! ## Authentication Flow
21//!
22//! ```text
23//! Peer A                                    Peer B
24//!   |                                         |
25//!   |  1. Create Offer (with SDP)            |
26//!   |------------------------------------>   |
27//!   |                                         |
28//!   |  2. Create Answer (with SDP)           |
29//!   |   <------------------------------------|
30//!   |                                         |
31//!   |  3. Generate ConnectionAuth from       |
32//!   |     both SDP hashes                    |
33//!   |                                         |
34//!   |  4. Encrypt with peer's public key     |
35//!   |------------------------------------>   |
36//!   |                                         |
37//!   |  5. Decrypt and verify                 |
38//!   |   <------------------------------------|
39//!   |                                         |
40//!   |  6. Connection authenticated ✓         |
41//! ```
42//!
43//! ## Security Properties
44//!
45//! - **Identity Verification**: Ensures both parties possess the private keys
46//!   corresponding to their advertised public keys
47//! - **Man-in-the-Middle Protection**: Prevents attackers from intercepting and
48//!   modifying the connection establishment process
49//! - **Replay Attack Prevention**: Uses unique SDP hashes for each connection
50//!   attempt, preventing replay attacks
51
52use rand::{CryptoRng, Rng};
53use serde::{Deserialize, Serialize};
54
55use crate::identity::{PublicKey, SecretKey};
56
57use super::{Answer, Offer};
58
59/// Connection authentication data derived from WebRTC signaling.
60///
61/// `ConnectionAuth` contains the authentication material generated from the
62/// SDP (Session Description Protocol) hashes of both the WebRTC offer and
63/// answer.
64/// This creates a unique, connection-specific authentication token that can be
65/// used to verify the authenticity of the WebRTC connection.
66///
67/// ## Construction
68///
69/// The authentication data is created by concatenating the SDP hashes from both
70/// the offer and answer messages:
71///
72/// ```text
73/// ConnectionAuth = SDP_Hash(Offer) || SDP_Hash(Answer)
74/// ```
75///
76/// This ensures that both parties contributed to the authentication material
77/// and that any tampering with either the offer or answer would be detected.
78///
79/// ## Security Properties
80///
81/// - **Uniqueness**: Each connection attempt generates unique SDP data,
82///   preventing replay attacks
83/// - **Integrity**: Any modification to the offer or answer changes the hashes,
84///   invalidating the authentication
85/// - **Binding**: Cryptographically binds the authentication to the specific
86///   WebRTC session parameters
87///
88/// ## Usage
89///
90/// ```rust
91/// use mina_p2p::webrtc::{ConnectionAuth, Offer, Answer};
92///
93/// let connection_auth = ConnectionAuth::new(&offer, &answer);
94/// let encrypted_auth = connection_auth.encrypt(&my_secret_key, &peer_public_key, rng)?;
95/// ```
96#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
97pub struct ConnectionAuth(Vec<u8>);
98
99/// Encrypted connection authentication data.
100///
101/// `ConnectionAuthEncrypted` represents the connection authentication data after
102/// it has been encrypted using public key cryptography. The encrypted data is
103/// stored in a fixed-size array of 92 bytes, which corresponds to the output
104/// size of the encryption algorithm used.
105///
106/// ## Encryption Process
107///
108/// The encryption uses the recipient's public key to ensure that only the
109/// intended recipient can decrypt and verify the authentication data. This
110/// prevents man-in-the-middle attackers from forging authentication tokens.
111///
112/// ## Fixed Size
113///
114/// The 92-byte fixed size is determined by the cryptographic parameters:
115/// - The encryption algorithm produces a deterministic output size
116/// - Fixed sizing enables efficient serialization and network transmission
117/// - Prevents information leakage through size analysis
118///
119/// ## Network Transmission
120///
121/// This type is designed for transmission over the network and includes
122/// serialization support for JSON and binary formats.
123///
124/// ## Example
125///
126/// ```rust
127/// // After receiving encrypted authentication data
128/// let decrypted_auth = encrypted_auth.decrypt(&my_secret_key, &peer_public_key)?;
129/// // Verify that the decrypted data matches expected values
130/// ```
131#[derive(Debug, Clone)]
132pub struct ConnectionAuthEncrypted(Box<[u8; 92]>);
133
134impl ConnectionAuth {
135    /// Creates new connection authentication data from WebRTC offer and answer.
136    ///
137    /// This method generates connection authentication data by concatenating the
138    /// SDP hashes from both the WebRTC offer and answer messages. The resulting
139    /// authentication token is unique to this specific connection attempt and
140    /// binds the authentication to the exact WebRTC session parameters.
141    ///
142    /// # Parameters
143    ///
144    /// * `offer` - The WebRTC offer containing SDP data and peer information
145    /// * `answer` - The WebRTC answer containing SDP data and peer information
146    ///
147    /// # Returns
148    ///
149    /// A new `ConnectionAuth` instance containing the concatenated SDP hashes.
150    ///
151    /// # Security Considerations
152    ///
153    /// The authentication data is derived from both the offer and answer, ensuring
154    /// that any tampering with either message will result in different authentication
155    /// data. This prevents attackers from modifying signaling messages without
156    /// detection.
157    ///
158    /// # Example
159    ///
160    /// ```rust
161    /// use mina_p2p::webrtc::ConnectionAuth;
162    ///
163    /// let auth = ConnectionAuth::new(&offer, &answer);
164    /// // Use auth for connection verification
165    /// ```
166    pub fn new(offer: &Offer, answer: &Answer) -> Self {
167        Self([offer.sdp_hash(), answer.sdp_hash()].concat())
168    }
169
170    /// Encrypts the connection authentication data using public key cryptography.
171    ///
172    /// This method encrypts the authentication data using the recipient's
173    /// public key, ensuring that only the intended recipient (who possesses the
174    /// corresponding private key) can decrypt and verify the authentication
175    /// token.
176    ///
177    /// # Parameters
178    ///
179    /// * `sec_key` - The sender's secret key used for encryption
180    /// * `other_pk` - The recipient's public key used for encryption
181    /// * `rng` - A cryptographically secure random number generator
182    ///
183    /// # Returns
184    ///
185    /// * `Some(ConnectionAuthEncrypted)` if encryption succeeds
186    /// * `None` if encryption fails (e.g., due to invalid keys or cryptographic
187    ///   errors)
188    ///
189    /// # Security Properties
190    ///
191    /// - **Confidentiality**: Only the holder of the corresponding private key can
192    ///   decrypt the authentication data
193    /// - **Authenticity**: The encryption process provides assurance about the
194    ///   sender's identity
195    ///
196    /// # Example
197    ///
198    /// ```rust
199    /// use rand::thread_rng;
200    ///
201    /// let mut rng = thread_rng();
202    /// let encrypted_auth = connection_auth.encrypt(&my_secret_key, &peer_public_key, &mut rng);
203    ///
204    /// if let Some(encrypted) = encrypted_auth {
205    ///     // Send encrypted authentication data to peer
206    /// }
207    /// ```
208    pub fn encrypt(
209        &self,
210        sec_key: &SecretKey,
211        other_pk: &PublicKey,
212        rng: impl Rng + CryptoRng,
213    ) -> Option<ConnectionAuthEncrypted> {
214        let bytes = sec_key.encrypt_raw(other_pk, rng, &self.0).ok()?;
215        bytes.try_into().ok()
216    }
217}
218
219impl ConnectionAuthEncrypted {
220    /// Decrypts the connection authentication data using public key cryptography.
221    ///
222    /// This method decrypts the authentication data using the recipient's
223    /// secret key and the sender's public key. Successful decryption proves
224    /// that the sender possesses the private key corresponding to their
225    /// advertised public key, providing authentication and preventing
226    /// man-in-the-middle attacks.
227    ///
228    /// # Parameters
229    ///
230    /// * `sec_key` - The recipient's secret key used for decryption
231    /// * `other_pk` - The sender's public key used for decryption
232    ///
233    /// # Returns
234    ///
235    /// * `Some(ConnectionAuth)` if decryption succeeds and authentication is valid
236    /// * `None` if decryption fails (e.g., due to invalid keys, corrupted data, or
237    ///   cryptographic errors)
238    ///
239    /// # Security Verification
240    ///
241    /// Successful decryption provides several security guarantees:
242    ///
243    /// - **Identity Verification**: The sender possesses the private key
244    ///   corresponding to their public key
245    /// - **Message Integrity**: The encrypted data has not been tampered with
246    /// - **Authenticity**: The authentication data came from the claimed sender
247    ///
248    /// # Usage in Authentication Flow
249    ///
250    /// This method is typically called during the final stage of WebRTC connection
251    /// establishment to verify the peer's identity before allowing the connection
252    /// to proceed.
253    ///
254    /// # Example
255    ///
256    /// ```rust
257    /// // After receiving encrypted authentication data from peer
258    /// if let Some(decrypted_auth) = encrypted_auth.decrypt(&my_secret_key, &peer_public_key) {
259    ///     // Authentication successful, proceed with connection
260    ///     println!("Peer authentication verified");
261    /// } else {
262    ///     // Authentication failed, reject connection
263    ///     println!("Peer authentication failed");
264    /// }
265    /// ```
266    pub fn decrypt(&self, sec_key: &SecretKey, other_pk: &PublicKey) -> Option<ConnectionAuth> {
267        sec_key
268            .decrypt_raw(other_pk, &*self.0)
269            .map(ConnectionAuth)
270            .ok()
271    }
272}
273
274impl Serialize for ConnectionAuthEncrypted {
275    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
276    where
277        S: serde::Serializer,
278    {
279        self.0.to_vec().serialize(serializer)
280    }
281}
282
283impl<'de> Deserialize<'de> for ConnectionAuthEncrypted {
284    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
285    where
286        D: serde::Deserializer<'de>,
287    {
288        Vec::deserialize(deserializer).and_then(|v| {
289            use serde::de::Error;
290            v.try_into().map_err(Error::custom)
291        })
292    }
293}
294
295impl TryFrom<Vec<u8>> for ConnectionAuthEncrypted {
296    type Error = &'static str;
297
298    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
299        value.as_slice().try_into()
300    }
301}
302
303impl TryFrom<&[u8]> for ConnectionAuthEncrypted {
304    type Error = &'static str;
305
306    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
307        value
308            .try_into()
309            .map(|v| Self(Box::new(v)))
310            .map_err(|_| "ConnectionAuthEncrypted not in expected size")
311    }
312}
313
314impl AsRef<[u8]> for ConnectionAuthEncrypted {
315    fn as_ref(&self) -> &[u8] {
316        &*self.0
317    }
318}