scuffle_rtmp/handshake/complex/
mod.rs

1//! This module contains the complex handshake for the RTMP protocol.
2//!
3//! Unfortunately there doesn't seem to be a good spec sheet for this.
4//! This implementation is based on this Chinese forum post because it's the best we could find:
5//! <https://blog.csdn.net/win_lin/article/details/13006803>
6
7use std::io::{self, Seek, Write};
8
9use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
10use bytes::{BufMut, Bytes, BytesMut};
11use digest::DigestProcessor;
12use rand::Rng;
13use scuffle_bytes_util::BytesCursorExt;
14
15use super::{RTMP_HANDSHAKE_SIZE, RtmpVersion, ServerHandshakeState, TIME_VERSION_LENGTH, current_time};
16
17pub mod digest;
18pub mod error;
19
20/// This is some magic number, I do not know why its 0x04050001, however, the
21/// reference implementation uses this value.
22pub const RTMP_SERVER_VERSION: u32 = 0x04050001;
23
24/// This is the length of the digest.
25/// There is a lot of random data before and after the digest, however, the
26/// digest is always 32 bytes.
27pub const RTMP_DIGEST_LENGTH: usize = 32;
28
29/// This is the first half of the server key.
30pub const RTMP_SERVER_KEY_FIRST_HALF: &[u8] = b"Genuine Adobe Flash Media Server 001";
31
32/// This is the first half of the client key.
33pub const RTMP_CLIENT_KEY_FIRST_HALF: &[u8] = b"Genuine Adobe Flash Player 001";
34
35/// This is the second half of the server/client key.
36/// Used for the complex handshake.
37pub const RTMP_SERVER_KEY: &[u8] = &[
38    0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20,
39    0x4d, 0x65, 0x64, 0x69, 0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, 0xf0, 0xee, 0xc2, 0x4a,
40    0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, 0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab,
41    0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae,
42];
43
44/// The schema version.
45///
46/// For the complex handshake the schema is either 0 or 1.
47/// A chunk is 764 bytes. (1536 - 8) / 2 = 764
48/// A schema of 0 means the digest is after the key, thus the digest is at
49/// offset 776 bytes (768 + 8). A schema of 1 means the digest is before the key
50/// thus the offset is at offset 8 bytes (0 + 8). Where 8 bytes is the time and
51/// version. (4 bytes each) The schema is determined by the client.
52/// The server will always use the schema the client uses.
53#[derive(Debug, PartialEq, Eq, Clone, Copy)]
54pub enum SchemaVersion {
55    /// Schema 0.
56    Schema0,
57    /// Schema 1.
58    Schema1,
59}
60
61/// Complex Handshake Server.
62pub struct ComplexHandshakeServer {
63    version: RtmpVersion,
64    requested_version: RtmpVersion,
65    state: ServerHandshakeState,
66    schema_version: SchemaVersion,
67    c1_digest: Bytes,
68    c1_timestamp: u32,
69    c1_version: u32,
70}
71
72impl Default for ComplexHandshakeServer {
73    fn default() -> Self {
74        Self {
75            state: ServerHandshakeState::ReadC0C1,
76            c1_digest: Bytes::default(),
77            c1_timestamp: 0,
78            version: RtmpVersion::Version3,
79            requested_version: RtmpVersion(0),
80            c1_version: 0,
81            schema_version: SchemaVersion::Schema0,
82        }
83    }
84}
85
86impl ComplexHandshakeServer {
87    /// Returns true if the handshake is finished.
88    pub fn is_finished(&self) -> bool {
89        self.state == ServerHandshakeState::Finish
90    }
91
92    /// Perform the complex handshake.
93    pub fn handshake(&mut self, input: &mut io::Cursor<Bytes>, output: &mut Vec<u8>) -> Result<(), crate::error::RtmpError> {
94        match self.state {
95            ServerHandshakeState::ReadC0C1 => {
96                self.read_c0(input)?;
97                self.read_c1(input)?;
98                self.write_s0(output)?;
99                self.write_s1(output)?;
100                self.write_s2(output)?;
101                self.state = ServerHandshakeState::ReadC2;
102            }
103            ServerHandshakeState::ReadC2 => {
104                self.read_c2(input)?;
105                self.state = ServerHandshakeState::Finish;
106            }
107            ServerHandshakeState::Finish => {}
108        }
109
110        Ok(())
111    }
112
113    fn read_c0(&mut self, input: &mut io::Cursor<Bytes>) -> Result<(), crate::error::RtmpError> {
114        // Version (8 bits): In C0, this field identifies the RTMP version
115        // requested by the client.
116        self.requested_version = RtmpVersion(input.read_u8()?);
117
118        // We only support version 3 for now.
119        // Therefore we set the version to 3.
120        self.version = RtmpVersion::Version3;
121
122        Ok(())
123    }
124
125    fn read_c1(&mut self, input: &mut io::Cursor<Bytes>) -> Result<(), crate::error::RtmpError> {
126        let c1_bytes = input.extract_bytes(RTMP_HANDSHAKE_SIZE)?;
127
128        // The first 4 bytes of C1 are the timestamp.
129        self.c1_timestamp = (&c1_bytes[0..4]).read_u32::<BigEndian>()?;
130
131        // The next 4 bytes are a version number.
132        self.c1_version = (&c1_bytes[4..8]).read_u32::<BigEndian>()?;
133
134        // The following 764 bytes are either the digest or the key.
135        let data_digest = DigestProcessor::new(c1_bytes, RTMP_CLIENT_KEY_FIRST_HALF);
136
137        let (c1_digest_data, schema_version) = data_digest.read_digest()?;
138
139        self.c1_digest = c1_digest_data;
140        self.schema_version = schema_version;
141
142        Ok(())
143    }
144
145    fn read_c2(&mut self, input: &mut io::Cursor<Bytes>) -> Result<(), crate::error::RtmpError> {
146        // We don't care too much about the data in C2, so we just read it
147        // and discard it.
148        input.seek_relative(RTMP_HANDSHAKE_SIZE as i64)?;
149
150        Ok(())
151    }
152
153    fn write_s0(&mut self, output: &mut Vec<u8>) -> Result<(), crate::error::RtmpError> {
154        // The version of the protocol used in the handshake.
155        // This server is using version 3 of the protocol.
156        output.write_u8(self.version.0)?; // 8 bits version
157
158        Ok(())
159    }
160
161    fn write_s1(&self, output: &mut Vec<u8>) -> Result<(), crate::error::RtmpError> {
162        let mut writer = BytesMut::new().writer();
163
164        // The first 4 bytes of S1 are the timestamp.
165        writer.write_u32::<BigEndian>(current_time())?;
166
167        // The next 4 bytes are a version number.
168        writer.write_u32::<BigEndian>(RTMP_SERVER_VERSION)?;
169
170        // We then write 1528 bytes of random data.
171        // 764 bytes for the digest, 764 bytes for the key.
172        let mut rng = rand::rng();
173        for _ in 0..RTMP_HANDSHAKE_SIZE - TIME_VERSION_LENGTH {
174            writer.write_u8(rng.random())?;
175        }
176
177        // The digest is loaded with the data that we just generated.
178        let data_digest = DigestProcessor::new(writer.into_inner().freeze(), RTMP_SERVER_KEY_FIRST_HALF);
179
180        // We use the same schema version as the client and then write the result of the digest to the main writer.
181        data_digest.generate_and_fill_digest(self.schema_version)?.write_to(output)?;
182
183        Ok(())
184    }
185
186    fn write_s2(&self, output: &mut Vec<u8>) -> Result<(), crate::error::RtmpError> {
187        let start = output.len();
188
189        // We write the current time to the first 4 bytes.
190        output.write_u32::<BigEndian>(current_time())?;
191
192        // We write the timestamp from C1 to the next 4 bytes.
193        output.write_u32::<BigEndian>(self.c1_timestamp)?;
194
195        // We then write 1528 bytes of random data.
196        // 764 bytes for the digest, 764 bytes for the key.
197        let mut rng = rand::rng();
198
199        // RTMP_HANDSHAKE_SIZE - TIME_VERSION_LENGTH because we already
200        // wrote 8 bytes. (timestamp and c1 timestamp)
201        for _ in 0..RTMP_HANDSHAKE_SIZE - RTMP_DIGEST_LENGTH - TIME_VERSION_LENGTH {
202            output.write_u8(rng.random())?;
203        }
204
205        // The digest is loaded with the data that we just generated.
206        // This digest is used to generate the key. (digest of c1)
207        let key_digest = DigestProcessor::new(Bytes::new(), RTMP_SERVER_KEY);
208
209        // Create a digest of the random data using a key generated from the digest of
210        // C1.
211        let key = key_digest.make_digest(&self.c1_digest, &[])?;
212        let data_digest = DigestProcessor::new(Bytes::new(), &key);
213
214        // We then generate a digest using the key and the random data
215        // We then extract the first 1504 bytes of the data.
216        // RTMP_HANDSHAKE_SIZE - 32 = 1504
217        // 32 is the size of the digest. for C2S2
218        let digest = data_digest.make_digest(&output[start..start + RTMP_HANDSHAKE_SIZE - RTMP_DIGEST_LENGTH], &[])?;
219
220        // Write the random data  to the main writer.
221        // Total Write = 1536 bytes (1504 + 32)
222        output.write_all(&digest)?; // 32 bytes of digest
223
224        Ok(())
225    }
226}