steamkit/networking/steam3/
envelope_encrypted_connection.rs1use std::net::{IpAddr, SocketAddr};
27use std::sync::atomic::{AtomicBool, Ordering};
28use std::sync::{Arc, Mutex, MutexGuard};
29use std::time::Duration;
30
31use async_trait::async_trait;
32use bytes::Bytes;
33use rand::RngCore;
34use rsa::pkcs8::DecodePublicKey;
35use rsa::{Oaep, RsaPublicKey};
36use sha1::Sha1;
37use tokio::sync::{broadcast, mpsc};
38
39use crate::base::generated::steam_language::{EMsg, EResult, EUniverse};
40use crate::error::SteamError;
41use crate::util::key_dictionary::KeyDictionary;
42
43use super::disconnected_event_args::DisconnectedEventArgs;
44use super::i_connection::{
45 Connection, ConnectionEvent, ConnectionEventSenders, ConnectionEvents, IConnection,
46};
47use super::net_filter_encryption_with_hmac::NetFilterEncryptionWithHMAC;
48use super::net_msg_event_args::NetMsgEventArgs;
49use super::protocol_type::ProtocolTypes;
50
51const MSG_HDR_LENGTH: usize = 20;
53
54const PROTO_MASK: u32 = 0x8000_0000;
56
57const PROTOCOL_VERSION: u32 = 1;
59
60const KEY_SIZE: u32 = 128;
62
63const SESSION_KEY_LENGTH: usize = 32;
65
66const MIN_CHALLENGE_LENGTH: usize = 16;
68
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71enum EncryptionState {
72 Disconnected,
73 Connected,
74 Challenged,
75 Encrypted,
76}
77
78pub use crate::util::debug_network_listener::IDebugNetworkListener as DebugNetworkListener;
80
81struct EnvelopeShared {
82 inner: Arc<dyn Connection>,
83 events: ConnectionEventSenders,
84 universe: EUniverse,
85 state: Mutex<EncryptionState>,
86 encryption: Mutex<Option<Arc<NetFilterEncryptionWithHMAC>>>,
87 debug_network_listener: Option<Arc<dyn DebugNetworkListener>>,
88 forwarders_started: AtomicBool,
89}
90
91pub struct EnvelopeEncryptedConnection {
94 shared: Arc<EnvelopeShared>,
95}
96
97impl std::fmt::Debug for EnvelopeEncryptedConnection {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 f.debug_struct("EnvelopeEncryptedConnection").finish_non_exhaustive()
100 }
101}
102
103impl EnvelopeEncryptedConnection {
104 pub fn new(inner: Arc<dyn Connection>, universe: EUniverse) -> Self {
106 Self::with_debug_network_listener(inner, universe, None)
107 }
108
109 pub fn with_debug_network_listener(
112 inner: Arc<dyn Connection>,
113 universe: EUniverse,
114 debug_network_listener: Option<Arc<dyn DebugNetworkListener>>,
115 ) -> Self {
116 let shared = Arc::new(EnvelopeShared {
117 inner,
118 events: ConnectionEventSenders::with_default_capacity(),
119 universe,
120 state: Mutex::new(EncryptionState::Disconnected),
121 encryption: Mutex::new(None),
122 debug_network_listener,
123 forwarders_started: AtomicBool::new(false),
124 });
125
126 Self { shared }
127 }
128
129 fn spawn_forwarders(&self) {
133 if self
134 .shared
135 .forwarders_started
136 .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
137 .is_err()
138 {
139 return;
140 }
141
142 spawn_forwarders(self.shared.clone(), self.shared.inner.clone());
143 }
144
145 pub fn subscribe_net_msg_received(&self) -> broadcast::Receiver<NetMsgEventArgs> {
147 self.shared.events.subscribe_net_msg_received()
148 }
149
150 pub fn subscribe_connected(&self) -> broadcast::Receiver<()> {
152 self.shared.events.subscribe_connected()
153 }
154
155 pub fn subscribe_disconnected(&self) -> broadcast::Receiver<DisconnectedEventArgs> {
157 self.shared.events.subscribe_disconnected()
158 }
159}
160
161#[async_trait]
162impl IConnection for EnvelopeEncryptedConnection {
163 fn current_end_point(&self) -> Option<SocketAddr> {
164 self.shared.inner.current_end_point()
165 }
166
167 fn protocol_types(&self) -> ProtocolTypes {
168 self.shared.inner.protocol_types()
169 }
170
171 async fn connect(&self, end_point: SocketAddr, timeout: Duration) -> Result<(), SteamError> {
172 self.spawn_forwarders();
173
174 self.shared.inner.connect(end_point, timeout).await
175 }
176
177 async fn disconnect(&self, user_initiated: bool) {
178 self.shared.inner.disconnect(user_initiated).await;
179 }
180
181 async fn send(&self, data: Bytes) -> Result<(), SteamError> {
182 let state = lock(&self.shared.state).clone();
183
184 if state != EncryptionState::Encrypted {
185 return self.shared.inner.send(data).await;
186 }
187
188 let encryption = lock(&self.shared.encryption).clone();
189
190 let Some(encryption) = encryption else {
191 tracing::debug!(
192 "EnvelopeEncryptedConnection: Attempting to send client data when not connected."
193 );
194 return Ok(());
195 };
196
197 let encrypted = encryption.process_outgoing(&data)?;
198 self.shared.inner.send(Bytes::from(encrypted)).await
199 }
200
201 fn local_ip(&self) -> Option<IpAddr> {
202 self.shared.inner.local_ip()
203 }
204}
205
206impl ConnectionEvents for EnvelopeEncryptedConnection {
207 fn events(&self) -> mpsc::UnboundedReceiver<ConnectionEvent> {
208 self.shared.events.subscribe_events()
209 }
210
211 fn net_msg_received(&self) -> broadcast::Receiver<NetMsgEventArgs> {
212 self.shared.events.subscribe_net_msg_received()
213 }
214
215 fn connected(&self) -> broadcast::Receiver<()> {
216 self.shared.events.subscribe_connected()
217 }
218
219 fn disconnected(&self) -> broadcast::Receiver<DisconnectedEventArgs> {
220 self.shared.events.subscribe_disconnected()
221 }
222}
223
224fn spawn_forwarders(shared: Arc<EnvelopeShared>, inner: Arc<dyn Connection>) {
233 let mut events = inner.events();
235 let weak = Arc::downgrade(&shared);
236 drop(shared);
237
238 tokio::spawn(async move {
239 while let Some(event) = events.recv().await {
240 let Some(shared) = weak.upgrade() else {
241 break;
242 };
243
244 match event {
245 ConnectionEvent::NetMsgReceived(args) => {
246 handle_net_msg_received(&shared, args).await;
247 }
248 ConnectionEvent::Connected => {
249 *lock(&shared.state) = EncryptionState::Connected;
250 }
251 ConnectionEvent::Disconnected(args) => {
252 *lock(&shared.state) = EncryptionState::Disconnected;
253 *lock(&shared.encryption) = None;
254 shared
255 .events
256 .emit_disconnected(DisconnectedEventArgs::new(args.user_initiated));
257 }
258 }
259 }
260 });
261}
262
263async fn handle_net_msg_received(shared: &Arc<EnvelopeShared>, args: NetMsgEventArgs) {
264 let state = lock(&shared.state).clone();
265
266 if state == EncryptionState::Encrypted {
267 let encryption = lock(&shared.encryption).clone();
268
269 let Some(encryption) = encryption else {
270 return;
271 };
272
273 match encryption.process_incoming(&args.data) {
274 Ok(plain_text) => {
275 shared.events.emit_net_msg_received(args.with_data(plain_text));
276 }
277 Err(err) => {
278 tracing::debug!(
279 "EnvelopeEncryptedConnection: Unable to decrypt incoming packet: {err}"
280 );
281 }
282 }
283
284 return;
285 }
286
287 let Some(msg_type) = parse_msg_type(&args.data) else {
288 tracing::debug!(
289 "EnvelopeEncryptedConnection: Failed to parse message during channel setup, shutting down connection"
290 );
291 shared.inner.disconnect(false).await;
292 return;
293 };
294
295 if let Some(listener) = &shared.debug_network_listener {
296 listener.on_incoming_network_message(msg_type, &args.data);
297 }
298
299 if !is_expected_emsg(state, msg_type) {
300 tracing::debug!("EnvelopeEncryptedConnection: Rejected EMsg during channel setup");
301 return;
302 }
303
304 match msg_type {
305 EMsg::ChannelEncryptRequest => handle_encrypt_request(shared, &args.data).await,
306 EMsg::ChannelEncryptResult => handle_encrypt_result(shared, &args.data).await,
307 _ => {}
308 }
309}
310
311async fn handle_encrypt_request(shared: &Arc<EnvelopeShared>, data: &[u8]) {
312 if data.len() < MSG_HDR_LENGTH + 8 {
313 tracing::debug!(
314 "EnvelopeEncryptedConnection: Encryption handshake has a payload that is less than 16 bytes"
315 );
316 shared.inner.disconnect(false).await;
317 return;
318 }
319
320 let protocol_version = read_u32(data, MSG_HDR_LENGTH);
321 let universe =
322 EUniverse::from_value(read_i32(data, MSG_HDR_LENGTH + 4)).unwrap_or(EUniverse::Invalid);
323
324 tracing::debug!(
325 "EnvelopeEncryptedConnection: Got encryption request. Universe: {universe:?} Protocol ver: {protocol_version}"
326 );
327
328 if protocol_version != PROTOCOL_VERSION {
330 tracing::debug!(
331 "EnvelopeEncryptedConnection: Encryption handshake protocol version mismatch!"
332 );
333 }
334
335 if universe != shared.universe {
336 tracing::debug!(
337 "EnvelopeEncryptedConnection: Expected universe {:?} but server reported universe {universe:?}",
338 shared.universe
339 );
340 }
341
342 let random_challenge = &data[MSG_HDR_LENGTH + 8..];
343 if random_challenge.len() < MIN_CHALLENGE_LENGTH {
344 tracing::debug!(
345 "EnvelopeEncryptedConnection: Encryption handshake has a payload that is less than 16 bytes"
346 );
347 shared.inner.disconnect(false).await;
348 return;
349 }
350
351 let Some(public_key) = public_key_for_universe(universe) else {
352 tracing::debug!(
353 "EnvelopeEncryptedConnection: HandleEncryptRequest got request for invalid universe!"
354 );
355 shared.inner.disconnect(false).await;
356 return;
357 };
358
359 let mut temp_session_key = [0u8; SESSION_KEY_LENGTH];
360 rand::thread_rng().fill_bytes(&mut temp_session_key);
361
362 let mut blob_to_encrypt = Vec::with_capacity(SESSION_KEY_LENGTH + random_challenge.len());
363 blob_to_encrypt.extend_from_slice(&temp_session_key);
364 blob_to_encrypt.extend_from_slice(random_challenge);
365
366 let encrypted_handshake_blob = match rsa_encrypt_oaep_sha1(public_key, &blob_to_encrypt) {
367 Ok(blob) => blob,
368 Err(err) => {
369 tracing::debug!("EnvelopeEncryptedConnection: {err}");
370 shared.inner.disconnect(false).await;
371 return;
372 }
373 };
374
375 let key_crc = crc32fast::hash(&encrypted_handshake_blob);
376
377 let encryption = match NetFilterEncryptionWithHMAC::new(&temp_session_key) {
378 Ok(encryption) => encryption,
379 Err(err) => {
380 tracing::debug!("EnvelopeEncryptedConnection: {err}");
381 shared.inner.disconnect(false).await;
382 return;
383 }
384 };
385
386 let serialized = build_channel_encrypt_response(&encrypted_handshake_blob, key_crc);
387
388 if let Some(listener) = &shared.debug_network_listener {
389 listener.on_outgoing_network_message(EMsg::ChannelEncryptResponse, &serialized);
390 }
391
392 *lock(&shared.encryption) = Some(Arc::new(encryption));
393 *lock(&shared.state) = EncryptionState::Challenged;
394
395 let _ = shared.inner.send(Bytes::from(serialized)).await;
396}
397
398async fn handle_encrypt_result(shared: &Arc<EnvelopeShared>, data: &[u8]) {
399 if data.len() < MSG_HDR_LENGTH + 4 {
400 shared.inner.disconnect(false).await;
401 return;
402 }
403
404 let result = EResult::from_value(read_i32(data, MSG_HDR_LENGTH)).unwrap_or(EResult::Invalid);
405 tracing::debug!("EnvelopeEncryptedConnection: Encryption result: {result:?}");
406
407 let has_encryption = lock(&shared.encryption).is_some();
408
409 if result == EResult::OK && has_encryption {
410 *lock(&shared.state) = EncryptionState::Encrypted;
411 shared.events.emit_connected();
412 } else {
413 tracing::debug!("EnvelopeEncryptedConnection: Encryption channel setup failed");
414 shared.inner.disconnect(false).await;
415 }
416}
417
418fn is_expected_emsg(state: EncryptionState, msg: EMsg) -> bool {
419 match state {
420 EncryptionState::Disconnected => false,
421 EncryptionState::Connected => msg == EMsg::ChannelEncryptRequest,
422 EncryptionState::Challenged => msg == EMsg::ChannelEncryptResult,
423 EncryptionState::Encrypted => true,
424 }
425}
426
427fn parse_msg_type(data: &[u8]) -> Option<EMsg> {
428 if data.len() < 4 {
429 return None;
430 }
431
432 let raw = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
433 EMsg::from_value((raw & !PROTO_MASK) as i32)
434}
435
436fn build_channel_encrypt_response(encrypted_handshake_blob: &[u8], key_crc: u32) -> Vec<u8> {
437 let mut serialized = Vec::with_capacity(MSG_HDR_LENGTH + 8 + encrypted_handshake_blob.len() + 8);
438 serialized.extend_from_slice(&(EMsg::ChannelEncryptResponse as i32).to_le_bytes());
439 serialized.extend_from_slice(&u64::MAX.to_le_bytes());
440 serialized.extend_from_slice(&u64::MAX.to_le_bytes());
441 serialized.extend_from_slice(&PROTOCOL_VERSION.to_le_bytes());
442 serialized.extend_from_slice(&KEY_SIZE.to_le_bytes());
443 serialized.extend_from_slice(encrypted_handshake_blob);
444 serialized.extend_from_slice(&key_crc.to_le_bytes());
445 serialized.extend_from_slice(&0u32.to_le_bytes());
446 serialized
447}
448
449fn rsa_encrypt_oaep_sha1(
450 public_key: &[u8],
451 blob_to_encrypt: &[u8],
452) -> Result<Vec<u8>, SteamError> {
453 let key = RsaPublicKey::from_public_key_der(public_key)
454 .map_err(|err| SteamError::Crypto(format!("invalid RSA public key: {err}")))?;
455
456 let padding = Oaep::new::<Sha1>();
457 let mut rng = rand::thread_rng();
458
459 key.encrypt(&mut rng, padding, blob_to_encrypt)
460 .map_err(|err| SteamError::Crypto(format!("RSA encryption failed: {err}")))
461}
462
463fn read_u32(data: &[u8], offset: usize) -> u32 {
464 u32::from_le_bytes([
465 data[offset],
466 data[offset + 1],
467 data[offset + 2],
468 data[offset + 3],
469 ])
470}
471
472fn read_i32(data: &[u8], offset: usize) -> i32 {
473 i32::from_le_bytes([
474 data[offset],
475 data[offset + 1],
476 data[offset + 2],
477 data[offset + 3],
478 ])
479}
480
481fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
482 match mutex.lock() {
483 Ok(guard) => guard,
484 Err(poisoned) => poisoned.into_inner(),
485 }
486}
487
488pub fn public_key_for_universe(universe: EUniverse) -> Option<&'static [u8]> {
490 KeyDictionary::get_public_key(universe)
491}