Skip to content

Commit 2f4e0d4

Browse files
authored
Merge pull request #2 from OpenXbox/develop
Sync master with develop
2 parents 4b68adf + d687a89 commit 2f4e0d4

35 files changed

Lines changed: 1714 additions & 7 deletions

.github/workflows/build.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ jobs:
88

99
steps:
1010
- name: Checkout
11-
uses: actions/checkout@v1
11+
uses: actions/checkout@v2
12+
with:
13+
submodules: recursive
14+
15+
- name: Install dependencies
16+
run: sudo apt install libpcap-dev
1217

1318
- name: Install toolchain
1419
uses: actions-rs/toolchain@v1

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
/target
22
*/target
3-
Cargo.lock
3+
Cargo.lock
4+
.idea

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "webrtc-srtp"]
2+
path = webrtc-srtp
3+
url = git@github.com:OpenXbox/srtp.git

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
[workspace]
2-
members = ["xal", "smartglass", "gamestreaming", "client"]
2+
members = ["xal", "smartglass", "gamestreaming", "client", "pcap_parser", "teredo"]

README.md

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,73 @@ cargo test
1111

1212
## Usage
1313

14-
```text
15-
cargo run
14+
### PCAP Parser
15+
16+
To simply decrypt communication and print to terminal:
17+
18+
```sh
19+
$ cargo run --bin pcap_parser -- --srtp-key <SRTP KEY BASE64> <PATH TO PCAP>
20+
Using SRTP key: Some("<SRTP KEY>")
21+
PCAP Decrypt path: None
22+
STUN Packet: Binding request l=76 attrs=4 id=hvRAX7bbtFhz4GZP
23+
STUN Packet: Binding request l=76 attrs=4 id=Q/ZCmawz/1kmzU2P
24+
STUN Packet: Binding request l=76 attrs=4 id=nv5JP7xN12fgbyXu
25+
STUN Packet: Binding request l=76 attrs=4 id=hvRAX7bbtFhz4GZP
26+
STUN Packet: Binding request l=76 attrs=4 id=OuJFwaMs+G7QrfBu
27+
STUN Packet: Binding success response l=48 attrs=3 id=OuJFwaMs+G7QrfBu
28+
STUN Packet: Binding request l=80 attrs=5 id=cyFG476hFBbaeza+
29+
STUN Packet: Binding success response l=48 attrs=3 id=cyFG476hFBbaeza+
30+
ConnectionProbingPacket::Syn(DataLen=1434)
31+
ConnectionProbingPacket::Syn(DataLen=1434)
32+
ConnectionProbingPacket::Syn(DataLen=1418)
33+
ConnectionProbingPacket::Syn(DataLen=1402)
34+
ConnectionProbingPacket::Syn(DataLen=1386)
35+
ConnectionProbingPacket::Syn(DataLen=1370)
36+
ConnectionProbingPacket::Syn(DataLen=1354)
37+
STUN Packet: Binding request l=76 attrs=4 id=nv5JP7xN12fgbyXu
38+
STUN Packet: Binding request l=76 attrs=4 id=hvRAX7bbtFhz4GZP
39+
STUN Packet: Binding success response l=48 attrs=3 id=hvRAX7bbtFhz4GZP
40+
STUN Packet: Binding request l=76 attrs=4 id=BXVF8Y8k7Bex1R5Y
41+
STUN Packet: Binding success response l=48 attrs=3 id=BXVF8Y8k7Bex1R5Y
42+
ConnectionProbingPacket::Syn(DataLen=1334)
43+
ConnectionProbingPacket::Syn(DataLen=1318)
44+
ConnectionProbingPacket::Syn(DataLen=1302)
45+
ConnectionProbingPacket::Syn(DataLen=1286)
46+
ConnectionProbingPacket::Syn(DataLen=1270)
47+
ConnectionProbingPacket::Syn(DataLen=1254)
48+
ConnectionProbingPacket::Syn(DataLen=1434)
49+
ConnectionProbingPacket::Ack(AcceptedSize=1434, Appendix=0)
50+
ConnectionProbingPacket::Ack(AcceptedSize=1334, Appendix=0)
51+
RTP: UDPKeepAlive Seq: 2, ts: 0, ssrc: 0
52+
|00000000 09000000 64000000 00000000| ........d....... 00000000
53+
|401f0000 00000000 0a000000 58020000| @...........X... 00000010
54+
|88130000 00000000 0000| .......... 00000020
55+
...
56+
RTP: MuxDCTControl Seq: 5, ts: 0, ssrc: 1024
57+
|14c10af4 01640064 00020000 002e004d| .....d.d.......M 00000000
58+
|6963726f 736f6674 3a3a4261 7369783a| icrosoft::Basix: 00000010
59+
|3a446374 3a3a4368 616e6e65 6c3a3a43| :Dct::Channel::C 00000020
60+
|6c617373 3a3a436f 6e74726f 6c000000| lass::Control... 00000030
61+
|00020000 00020000 00|
62+
```
63+
64+
To decrypt into new PCAP file
65+
66+
```sh
67+
$ cargo run --bin pcap_parser -- --srtp-key <SRTP KEY BASE64> --decrypt-pcap <TARGET PLAINTEXT PCAP> <PATH TO PCAP>
68+
Using SRTP key: Some("<SRTP KEY>")
69+
PCAP Decrypt path: Some("plaintext.pcap")
70+
STUN Packet: Binding request l=76 attrs=4 id=hvRAX7bbtFhz4GZP
71+
STUN Packet: Binding request l=76 attrs=4 id=Q/ZCmawz/1kmzU2P
72+
STUN Packet: Binding request l=76 attrs=4 id=nv5JP7xN12fgbyXu
73+
STUN Packet: Binding request l=76 attrs=4 id=hvRAX7bbtFhz4GZP
74+
STUN Packet: Binding request l=76 attrs=4 id=OuJFwaMs+G7QrfBu
75+
STUN Packet: Binding success response l=48 attrs=3 id=OuJFwaMs+G7QrfBu
76+
STUN Packet: Binding request l=80 attrs=5 id=cyFG476hFBbaeza+
77+
STUN Packet: Binding success response l=48 attrs=3 id=cyFG476hFBbaeza+
78+
STUN Packet: Binding request l=76 attrs=4 id=nv5JP7xN12fgbyXu
79+
STUN Packet: Binding request l=76 attrs=4 id=hvRAX7bbtFhz4GZP
80+
STUN Packet: Binding success response l=48 attrs=3 id=hvRAX7bbtFhz4GZP
81+
STUN Packet: Binding request l=76 attrs=4 id=BXVF8Y8k7Bex1R5Y
82+
STUN Packet: Binding success response l=48 attrs=3 id=BXVF8Y8k7Bex1R5Y
1683
```

gamestreaming/Cargo.toml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,18 @@ edition = "2018"
77
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
88

99
[dependencies]
10+
base64 = "0.13.0"
1011
reqwest = { version = "0.10", features = ["json"] }
1112
serde = { version = "1.0.117", features = ["derive"] }
1213
serde_json = "1.0.59"
13-
correlation_vector = { git = "https://github.com/tuxuser/CorrelationVector-Rust.git", branch = "master" }
14+
hexdump = "0.1.1"
1415
uuid = { version = "0.8.1", features = ["v4"] }
15-
xal = { path = "../xal" }
16+
pnet = "0.27.2"
17+
webrtc-rs-stun = "0.1.13"
18+
webrtc-rs-rtp = "0.1.0"
19+
webrtc-srtp = { path = "../webrtc-srtp" }
20+
xal = { path = "../xal" }
21+
teredo = { path = "../teredo" }
22+
correlation_vector = { git = "https://github.com/OpenXbox/CorrelationVector-Rust.git", branch = "master" }
23+
byteorder = "1.4.3"
24+
bitflags = "1.2.1"

gamestreaming/src/crypto.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use std::convert::TryInto;
2+
3+
/// Implementation of MS-SRTP
4+
/// Source: https://docs.microsoft.com/en-us/openspecs/office_protocols/ms-srtp/bf622cc1-9fb5-4fa2-b18d-239a84dcca65
5+
///
6+
/// SRTP requires that each endpoint in an SRTP session maintain cryptographic contexts. For more information, see
7+
/// [RFC3711] section 3.2.3. This protocol maintains cryptographic contexts differently from SRTP [RFC3711].
8+
///
9+
/// This protocol maintains two cryptographic contexts per SRTP session:
10+
///
11+
/// - One for all media streams on the send direction.
12+
/// - One for all media streams on the receive direction.
13+
///
14+
/// This protocol supports multiple media streams sharing the same SRTP session. Each media stream MUST be uniquely
15+
/// identified by one Synchronization Source (SSRC). This protocol maintains per SSRC transform independent
16+
/// parameters in cryptographic contexts, as specified in section 3.1.3.2.
17+
///
18+
/// When sending or receiving an SRTP packet, this protocol first uses the SRTP session and direction to identify
19+
/// the cryptographic context, then uses the SSRC in the packet to decide the per SSRC transform independent
20+
/// parameters in the cryptographic context.
21+
22+
use crate::webrtc::srtp::{protection_profile, context};
23+
use crate::webrtc::rtp::header::Header;
24+
25+
type Error = Box<dyn std::error::Error>;
26+
type Result<T> = std::result::Result<T, Error>;
27+
28+
pub struct MsSrtpCryptoContext {
29+
crypto_ctx_in: context::Context,
30+
crypto_ctx_out: context::Context,
31+
}
32+
33+
impl MsSrtpCryptoContext {
34+
pub fn new(master_key: [u8; 16], master_salt: [u8; 14]) -> Result<Self> {
35+
Ok(Self {
36+
crypto_ctx_in: context::Context::new(
37+
&master_key,
38+
&master_salt,
39+
protection_profile::ProtectionProfile::AEADAES128GCM_MS_SRTP,
40+
None,
41+
None,
42+
)?,
43+
crypto_ctx_out: context::Context::new(
44+
&master_key,
45+
&master_salt,
46+
protection_profile::ProtectionProfile::AEADAES128GCM_MS_SRTP,
47+
None,
48+
None,
49+
)?,
50+
})
51+
}
52+
53+
pub fn from_base64(master_bytes: &str) -> Result<Self> {
54+
let master_bytes = base64::decode(master_bytes)?;
55+
Self::new(
56+
master_bytes[..16].try_into()?,
57+
master_bytes[16..].try_into()?
58+
)
59+
}
60+
61+
pub fn decrypt_rtp_with_header(
62+
&mut self,
63+
encrypted: &[u8],
64+
header: &Header
65+
) -> Result<Vec<u8>> {
66+
Ok(self.crypto_ctx_out.decrypt_rtp_with_header(encrypted, header)?)
67+
}
68+
69+
pub fn decrypt_rtp(&mut self, encrypted: &[u8]) -> Result<Vec<u8>> {
70+
Ok(self.crypto_ctx_in.decrypt_rtp(encrypted)?)
71+
}
72+
73+
pub fn encrypt_rtp_with_header(
74+
&mut self,
75+
plaintext: &[u8],
76+
header: &Header
77+
) -> Result<Vec<u8>> {
78+
Ok(self.crypto_ctx_out.encrypt_rtp_with_header(plaintext, header)?)
79+
}
80+
81+
pub fn encrypt_rtp(&mut self, plaintext: &[u8]) -> Result<Vec<u8>> {
82+
Ok(self.crypto_ctx_out.encrypt_rtp(plaintext)?)
83+
}
84+
85+
pub fn decrypt_rtp_as_host(&mut self, encrypted: &[u8]) -> Result<Vec<u8>> {
86+
Ok(self.crypto_ctx_out.decrypt_rtp(encrypted)?)
87+
}
88+
89+
pub fn encrypt_rtp_as_host(&mut self, encrypted: &[u8]) -> Result<Vec<u8>> {
90+
Ok(self.crypto_ctx_in.decrypt_rtp(encrypted)?)
91+
}
92+
}
93+
94+
#[cfg(test)]
95+
mod test {
96+
use super::MsSrtpCryptoContext;
97+
98+
pub const SRTP_KEY: &str = "RdHzuLLVGuO1aHILIEVJ1UzR7RWVioepmpy+9SRf";
99+
100+
#[test]
101+
fn test_decrypt() {
102+
let data = include_bytes!("../testdata/rtp_connection_probing.bin");
103+
let mut context = MsSrtpCryptoContext::from_base64(SRTP_KEY)
104+
.expect("Failed to initialize crypto context");
105+
106+
assert_eq!(data.len(), 1364);
107+
108+
let decrypted = context.decrypt_rtp(data)
109+
.expect("Failed to decrypt packet");
110+
111+
assert_eq!(decrypted.len(), 1348);
112+
}
113+
}

gamestreaming/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1+
pub extern crate pnet;
2+
pub extern crate teredo;
3+
pub extern crate byteorder;
4+
#[macro_use]
5+
pub extern crate bitflags;
6+
7+
pub mod crypto;
18
pub mod models;
9+
pub mod packets;
10+
pub mod webrtc;
211

312
#[cfg(test)]
413
mod tests {

gamestreaming/src/packets/audio.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#[derive(Debug, Clone, PartialEq)]
2+
pub enum AudioPacketType {
3+
ServerHandshake = 1,
4+
ClientHandshake = 2,
5+
Control = 3,
6+
Data = 4,
7+
}
8+
9+
#[derive(Debug, Clone, PartialEq)]
10+
pub enum AudioCodec {
11+
Opus = 0,
12+
PCM = 1,
13+
AAC = 2
14+
}
15+
16+
#[derive(Debug, Clone, PartialEq)]
17+
pub enum AudioControlFlags {
18+
StopStream = 0x08,
19+
StartStream = 0x10,
20+
Reinitialize = 0x40,
21+
}
22+
23+
#[derive(Debug, Clone, PartialEq)]
24+
pub struct PCMAudioFormat {
25+
pub bits: u32,
26+
pub is_float: u32,
27+
}
28+
29+
#[derive(Debug, Clone, PartialEq)]
30+
pub struct AudioFormat {
31+
pub channels: u32,
32+
pub frequency: u32,
33+
pub codec: u32,
34+
pub pcm_format: Option<PCMAudioFormat>,
35+
}
36+
37+
#[derive(Debug, Clone, PartialEq)]
38+
pub struct AudioServerHandshake {
39+
pub protocol_version: u32,
40+
pub reference_timestamp: u64,
41+
pub format_count: u32,
42+
pub formats: Box<[AudioFormat]>
43+
}
44+
45+
#[derive(Debug, Clone, PartialEq)]
46+
pub struct AudioClientHandshake {
47+
pub initial_frame_id: u32,
48+
pub requested_format: AudioFormat
49+
}
50+
51+
#[derive(Debug, Clone, PartialEq)]
52+
pub struct AudioControl {
53+
pub flags: u32,
54+
}
55+
56+
#[derive(Debug, Clone, PartialEq)]
57+
pub struct AudioData {
58+
pub flags: u32,
59+
pub frame_id: u32,
60+
pub timestamp: u64,
61+
pub data_size: u32,
62+
pub data: Vec<u8>
63+
}
64+
65+
#[derive(Debug, Clone, PartialEq)]
66+
pub enum AudioPacket {
67+
ServerHandshake(AudioServerHandshake),
68+
ClientHandshake(AudioClientHandshake),
69+
Control(AudioControl),
70+
Data(AudioData)
71+
}

gamestreaming/src/packets/input.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#[derive(Debug, Clone, PartialEq)]
2+
pub enum InputPacketType {
3+
ServerHandshakeV3 = 1,
4+
ClientHandshakeV3 = 2,
5+
FrameAck = 3,
6+
FrameV3 = 4,
7+
8+
ServerHandshakeV4 = 5,
9+
ClientHandshakeV4 = 6,
10+
FrameV4 = 7,
11+
}
12+
13+
#[derive(Debug, Clone, PartialEq)]
14+
pub struct InputServerHandshake {
15+
pub min_protocol_version: u32,
16+
pub max_protocol_version: u32,
17+
pub desktop_width: u32,
18+
pub desktop_height: u32,
19+
pub maximum_touches: u32,
20+
pub initial_frame_id: u32,
21+
}
22+
23+
#[derive(Debug, Clone, PartialEq)]
24+
pub struct InputClientHandshake {
25+
pub min_protocol_version: u32,
26+
pub max_protocol_version: u32,
27+
pub maximum_touches: u32,
28+
pub reference_timestamp: u64,
29+
}
30+
31+
#[derive(Debug, Clone, PartialEq)]
32+
pub enum InputPacket {
33+
ServerHandshake(InputServerHandshake),
34+
ClientHandshake(InputClientHandshake),
35+
FrameAck,
36+
Frame
37+
}

0 commit comments

Comments
 (0)