Skip to content

Commit

Permalink
Merge pull request #2 from OpenXbox/develop
Browse files Browse the repository at this point in the history
Sync master with develop
  • Loading branch information
tuxuser authored Mar 13, 2021
2 parents 4b68adf + d687a89 commit 2f4e0d4
Show file tree
Hide file tree
Showing 35 changed files with 1,714 additions and 7 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v2
with:
submodules: recursive

- name: Install dependencies
run: sudo apt install libpcap-dev

- name: Install toolchain
uses: actions-rs/toolchain@v1
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
*/target
Cargo.lock
Cargo.lock
.idea
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "webrtc-srtp"]
path = webrtc-srtp
url = [email protected]:OpenXbox/srtp.git
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[workspace]
members = ["xal", "smartglass", "gamestreaming", "client"]
members = ["xal", "smartglass", "gamestreaming", "client", "pcap_parser", "teredo"]
71 changes: 69 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,73 @@ cargo test

## Usage

```text
cargo run
### PCAP Parser

To simply decrypt communication and print to terminal:

```sh
$ cargo run --bin pcap_parser -- --srtp-key <SRTP KEY BASE64> <PATH TO PCAP>
Using SRTP key: Some("<SRTP KEY>")
PCAP Decrypt path: None
STUN Packet: Binding request l=76 attrs=4 id=hvRAX7bbtFhz4GZP
STUN Packet: Binding request l=76 attrs=4 id=Q/ZCmawz/1kmzU2P
STUN Packet: Binding request l=76 attrs=4 id=nv5JP7xN12fgbyXu
STUN Packet: Binding request l=76 attrs=4 id=hvRAX7bbtFhz4GZP
STUN Packet: Binding request l=76 attrs=4 id=OuJFwaMs+G7QrfBu
STUN Packet: Binding success response l=48 attrs=3 id=OuJFwaMs+G7QrfBu
STUN Packet: Binding request l=80 attrs=5 id=cyFG476hFBbaeza+
STUN Packet: Binding success response l=48 attrs=3 id=cyFG476hFBbaeza+
ConnectionProbingPacket::Syn(DataLen=1434)
ConnectionProbingPacket::Syn(DataLen=1434)
ConnectionProbingPacket::Syn(DataLen=1418)
ConnectionProbingPacket::Syn(DataLen=1402)
ConnectionProbingPacket::Syn(DataLen=1386)
ConnectionProbingPacket::Syn(DataLen=1370)
ConnectionProbingPacket::Syn(DataLen=1354)
STUN Packet: Binding request l=76 attrs=4 id=nv5JP7xN12fgbyXu
STUN Packet: Binding request l=76 attrs=4 id=hvRAX7bbtFhz4GZP
STUN Packet: Binding success response l=48 attrs=3 id=hvRAX7bbtFhz4GZP
STUN Packet: Binding request l=76 attrs=4 id=BXVF8Y8k7Bex1R5Y
STUN Packet: Binding success response l=48 attrs=3 id=BXVF8Y8k7Bex1R5Y
ConnectionProbingPacket::Syn(DataLen=1334)
ConnectionProbingPacket::Syn(DataLen=1318)
ConnectionProbingPacket::Syn(DataLen=1302)
ConnectionProbingPacket::Syn(DataLen=1286)
ConnectionProbingPacket::Syn(DataLen=1270)
ConnectionProbingPacket::Syn(DataLen=1254)
ConnectionProbingPacket::Syn(DataLen=1434)
ConnectionProbingPacket::Ack(AcceptedSize=1434, Appendix=0)
ConnectionProbingPacket::Ack(AcceptedSize=1334, Appendix=0)
RTP: UDPKeepAlive Seq: 2, ts: 0, ssrc: 0
|00000000 09000000 64000000 00000000| ........d....... 00000000
|401f0000 00000000 0a000000 58020000| @...........X... 00000010
|88130000 00000000 0000| .......... 00000020
...
RTP: MuxDCTControl Seq: 5, ts: 0, ssrc: 1024
|14c10af4 01640064 00020000 002e004d| .....d.d.......M 00000000
|6963726f 736f6674 3a3a4261 7369783a| icrosoft::Basix: 00000010
|3a446374 3a3a4368 616e6e65 6c3a3a43| :Dct::Channel::C 00000020
|6c617373 3a3a436f 6e74726f 6c000000| lass::Control... 00000030
|00020000 00020000 00|
```

To decrypt into new PCAP file

```sh
$ cargo run --bin pcap_parser -- --srtp-key <SRTP KEY BASE64> --decrypt-pcap <TARGET PLAINTEXT PCAP> <PATH TO PCAP>
Using SRTP key: Some("<SRTP KEY>")
PCAP Decrypt path: Some("plaintext.pcap")
STUN Packet: Binding request l=76 attrs=4 id=hvRAX7bbtFhz4GZP
STUN Packet: Binding request l=76 attrs=4 id=Q/ZCmawz/1kmzU2P
STUN Packet: Binding request l=76 attrs=4 id=nv5JP7xN12fgbyXu
STUN Packet: Binding request l=76 attrs=4 id=hvRAX7bbtFhz4GZP
STUN Packet: Binding request l=76 attrs=4 id=OuJFwaMs+G7QrfBu
STUN Packet: Binding success response l=48 attrs=3 id=OuJFwaMs+G7QrfBu
STUN Packet: Binding request l=80 attrs=5 id=cyFG476hFBbaeza+
STUN Packet: Binding success response l=48 attrs=3 id=cyFG476hFBbaeza+
STUN Packet: Binding request l=76 attrs=4 id=nv5JP7xN12fgbyXu
STUN Packet: Binding request l=76 attrs=4 id=hvRAX7bbtFhz4GZP
STUN Packet: Binding success response l=48 attrs=3 id=hvRAX7bbtFhz4GZP
STUN Packet: Binding request l=76 attrs=4 id=BXVF8Y8k7Bex1R5Y
STUN Packet: Binding success response l=48 attrs=3 id=BXVF8Y8k7Bex1R5Y
```
13 changes: 11 additions & 2 deletions gamestreaming/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,18 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
base64 = "0.13.0"
reqwest = { version = "0.10", features = ["json"] }
serde = { version = "1.0.117", features = ["derive"] }
serde_json = "1.0.59"
correlation_vector = { git = "https://github.com/tuxuser/CorrelationVector-Rust.git", branch = "master" }
hexdump = "0.1.1"
uuid = { version = "0.8.1", features = ["v4"] }
xal = { path = "../xal" }
pnet = "0.27.2"
webrtc-rs-stun = "0.1.13"
webrtc-rs-rtp = "0.1.0"
webrtc-srtp = { path = "../webrtc-srtp" }
xal = { path = "../xal" }
teredo = { path = "../teredo" }
correlation_vector = { git = "https://github.com/OpenXbox/CorrelationVector-Rust.git", branch = "master" }
byteorder = "1.4.3"
bitflags = "1.2.1"
113 changes: 113 additions & 0 deletions gamestreaming/src/crypto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use std::convert::TryInto;

/// Implementation of MS-SRTP
/// Source: https://docs.microsoft.com/en-us/openspecs/office_protocols/ms-srtp/bf622cc1-9fb5-4fa2-b18d-239a84dcca65
///
/// SRTP requires that each endpoint in an SRTP session maintain cryptographic contexts. For more information, see
/// [RFC3711] section 3.2.3. This protocol maintains cryptographic contexts differently from SRTP [RFC3711].
///
/// This protocol maintains two cryptographic contexts per SRTP session:
///
/// - One for all media streams on the send direction.
/// - One for all media streams on the receive direction.
///
/// This protocol supports multiple media streams sharing the same SRTP session. Each media stream MUST be uniquely
/// identified by one Synchronization Source (SSRC). This protocol maintains per SSRC transform independent
/// parameters in cryptographic contexts, as specified in section 3.1.3.2.
///
/// When sending or receiving an SRTP packet, this protocol first uses the SRTP session and direction to identify
/// the cryptographic context, then uses the SSRC in the packet to decide the per SSRC transform independent
/// parameters in the cryptographic context.
use crate::webrtc::srtp::{protection_profile, context};
use crate::webrtc::rtp::header::Header;

type Error = Box<dyn std::error::Error>;
type Result<T> = std::result::Result<T, Error>;

pub struct MsSrtpCryptoContext {
crypto_ctx_in: context::Context,
crypto_ctx_out: context::Context,
}

impl MsSrtpCryptoContext {
pub fn new(master_key: [u8; 16], master_salt: [u8; 14]) -> Result<Self> {
Ok(Self {
crypto_ctx_in: context::Context::new(
&master_key,
&master_salt,
protection_profile::ProtectionProfile::AEADAES128GCM_MS_SRTP,
None,
None,
)?,
crypto_ctx_out: context::Context::new(
&master_key,
&master_salt,
protection_profile::ProtectionProfile::AEADAES128GCM_MS_SRTP,
None,
None,
)?,
})
}

pub fn from_base64(master_bytes: &str) -> Result<Self> {
let master_bytes = base64::decode(master_bytes)?;
Self::new(
master_bytes[..16].try_into()?,
master_bytes[16..].try_into()?
)
}

pub fn decrypt_rtp_with_header(
&mut self,
encrypted: &[u8],
header: &Header
) -> Result<Vec<u8>> {
Ok(self.crypto_ctx_out.decrypt_rtp_with_header(encrypted, header)?)
}

pub fn decrypt_rtp(&mut self, encrypted: &[u8]) -> Result<Vec<u8>> {
Ok(self.crypto_ctx_in.decrypt_rtp(encrypted)?)
}

pub fn encrypt_rtp_with_header(
&mut self,
plaintext: &[u8],
header: &Header
) -> Result<Vec<u8>> {
Ok(self.crypto_ctx_out.encrypt_rtp_with_header(plaintext, header)?)
}

pub fn encrypt_rtp(&mut self, plaintext: &[u8]) -> Result<Vec<u8>> {
Ok(self.crypto_ctx_out.encrypt_rtp(plaintext)?)
}

pub fn decrypt_rtp_as_host(&mut self, encrypted: &[u8]) -> Result<Vec<u8>> {
Ok(self.crypto_ctx_out.decrypt_rtp(encrypted)?)
}

pub fn encrypt_rtp_as_host(&mut self, encrypted: &[u8]) -> Result<Vec<u8>> {
Ok(self.crypto_ctx_in.decrypt_rtp(encrypted)?)
}
}

#[cfg(test)]
mod test {
use super::MsSrtpCryptoContext;

pub const SRTP_KEY: &str = "RdHzuLLVGuO1aHILIEVJ1UzR7RWVioepmpy+9SRf";

#[test]
fn test_decrypt() {
let data = include_bytes!("../testdata/rtp_connection_probing.bin");
let mut context = MsSrtpCryptoContext::from_base64(SRTP_KEY)
.expect("Failed to initialize crypto context");

assert_eq!(data.len(), 1364);

let decrypted = context.decrypt_rtp(data)
.expect("Failed to decrypt packet");

assert_eq!(decrypted.len(), 1348);
}
}
9 changes: 9 additions & 0 deletions gamestreaming/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
pub extern crate pnet;
pub extern crate teredo;
pub extern crate byteorder;
#[macro_use]
pub extern crate bitflags;

pub mod crypto;
pub mod models;
pub mod packets;
pub mod webrtc;

#[cfg(test)]
mod tests {
Expand Down
71 changes: 71 additions & 0 deletions gamestreaming/src/packets/audio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#[derive(Debug, Clone, PartialEq)]
pub enum AudioPacketType {
ServerHandshake = 1,
ClientHandshake = 2,
Control = 3,
Data = 4,
}

#[derive(Debug, Clone, PartialEq)]
pub enum AudioCodec {
Opus = 0,
PCM = 1,
AAC = 2
}

#[derive(Debug, Clone, PartialEq)]
pub enum AudioControlFlags {
StopStream = 0x08,
StartStream = 0x10,
Reinitialize = 0x40,
}

#[derive(Debug, Clone, PartialEq)]
pub struct PCMAudioFormat {
pub bits: u32,
pub is_float: u32,
}

#[derive(Debug, Clone, PartialEq)]
pub struct AudioFormat {
pub channels: u32,
pub frequency: u32,
pub codec: u32,
pub pcm_format: Option<PCMAudioFormat>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct AudioServerHandshake {
pub protocol_version: u32,
pub reference_timestamp: u64,
pub format_count: u32,
pub formats: Box<[AudioFormat]>
}

#[derive(Debug, Clone, PartialEq)]
pub struct AudioClientHandshake {
pub initial_frame_id: u32,
pub requested_format: AudioFormat
}

#[derive(Debug, Clone, PartialEq)]
pub struct AudioControl {
pub flags: u32,
}

#[derive(Debug, Clone, PartialEq)]
pub struct AudioData {
pub flags: u32,
pub frame_id: u32,
pub timestamp: u64,
pub data_size: u32,
pub data: Vec<u8>
}

#[derive(Debug, Clone, PartialEq)]
pub enum AudioPacket {
ServerHandshake(AudioServerHandshake),
ClientHandshake(AudioClientHandshake),
Control(AudioControl),
Data(AudioData)
}
37 changes: 37 additions & 0 deletions gamestreaming/src/packets/input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#[derive(Debug, Clone, PartialEq)]
pub enum InputPacketType {
ServerHandshakeV3 = 1,
ClientHandshakeV3 = 2,
FrameAck = 3,
FrameV3 = 4,

ServerHandshakeV4 = 5,
ClientHandshakeV4 = 6,
FrameV4 = 7,
}

#[derive(Debug, Clone, PartialEq)]
pub struct InputServerHandshake {
pub min_protocol_version: u32,
pub max_protocol_version: u32,
pub desktop_width: u32,
pub desktop_height: u32,
pub maximum_touches: u32,
pub initial_frame_id: u32,
}

#[derive(Debug, Clone, PartialEq)]
pub struct InputClientHandshake {
pub min_protocol_version: u32,
pub max_protocol_version: u32,
pub maximum_touches: u32,
pub reference_timestamp: u64,
}

#[derive(Debug, Clone, PartialEq)]
pub enum InputPacket {
ServerHandshake(InputServerHandshake),
ClientHandshake(InputClientHandshake),
FrameAck,
Frame
}
16 changes: 16 additions & 0 deletions gamestreaming/src/packets/message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#[derive(Debug, Clone, PartialEq)]
pub enum MessagePacketType {
Handshake = 1,
Data = 2,
CancelRequest = 3,
}

#[derive(Debug, Clone, PartialEq)]
pub struct MessageData {
pub unknown1: u32,
pub unknown2: u32,
pub unknown3: u32,
pub unknown4: u32,
pub unknown5: u32,
pub unknown6: u32,
}
Loading

0 comments on commit 2f4e0d4

Please sign in to comment.