Skip to main content

serenity_voice_model/
binary.rs

1//! Binary serialization/deserialization for DAVE protocol opcodes.
2//!
3//! Per the DAVE protocol specification, opcodes 25-30 use binary WebSocket frames
4//! with the following structure:
5//! - Server-to-client (25, 27, 29, 30): `[sequence_number: u16][opcode: u8][payload]`
6//! - Client-to-server (26, 28): `[opcode: u8][payload]`
7
8use crate::opcode::Opcode;
9use crate::payload::*;
10
11/// Error type for binary serialization/deserialization
12#[derive(Debug)]
13pub enum BinaryError {
14    /// Not enough bytes to parse the message
15    InsufficientData,
16    /// Invalid opcode value
17    InvalidOpcode(u8),
18    /// Invalid operation type for proposals
19    InvalidOperationType(u8),
20    /// General parse error
21    ParseError(String),
22}
23
24impl std::fmt::Display for BinaryError {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        match self {
27            Self::InsufficientData => write!(f, "Insufficient data to parse binary message"),
28            Self::InvalidOpcode(op) => write!(f, "Invalid opcode: {}", op),
29            Self::InvalidOperationType(op) => write!(f, "Invalid operation type: {}", op),
30            Self::ParseError(msg) => write!(f, "Parse error: {}", msg),
31        }
32    }
33}
34
35impl std::error::Error for BinaryError {}
36
37/// Parse a u16 from big-endian bytes
38fn read_u16(data: &[u8]) -> Result<u16, BinaryError> {
39    if data.len() < 2 {
40        return Err(BinaryError::InsufficientData);
41    }
42    Ok(u16::from_be_bytes([data[0], data[1]]))
43}
44
45/// Deserialize a binary message from Discord (opcodes 25, 27, 29, 30)
46pub fn deserialize_binary_event(data: &[u8]) -> Result<crate::Event, BinaryError> {
47    // TODO: When upgrading to voice v8, the minimum size will be 3 bytes
48    // (sequence number: u16, opcode: u8). The length checks and indexes below
49    // will also need to be updated accordingly.
50    if data.len() < 1 {
51        return Err(BinaryError::InsufficientData);
52    }
53
54    let opcode = data[0];
55
56    // Log raw binary data for debugging (first 16 bytes)
57    #[cfg(debug_assertions)]
58    eprintln!(
59        "[DAVE Binary] Received {} bytes: opcode={}, data={:02X?}",
60        data.len(),
61        opcode,
62        &data[..data.len().min(16)]
63    );
64
65    match opcode {
66        25 => {
67            // DaveMlsExternalSender
68            let external_sender = data[1..].to_vec();
69            Ok(crate::Event::DaveMlsExternalSender(DaveMlsExternalSender {
70                external_sender,
71            }))
72        },
73        27 => {
74            // DaveMlsProposals
75            if data.len() < 2 {
76                return Err(BinaryError::InsufficientData);
77            }
78            let operation_type = match data[1] {
79                0 => DaveMlsProposalsOperationType::Append,
80                1 => DaveMlsProposalsOperationType::Revoke,
81                other => return Err(BinaryError::InvalidOperationType(other)),
82            };
83            let proposals = data[2..].to_vec();
84            Ok(crate::Event::DaveMlsProposals(DaveMlsProposals {
85                operation_type,
86                proposals,
87            }))
88        },
89        29 => {
90            // DaveMlsAnnounceCommitTransition
91            if data.len() < 3 {
92                return Err(BinaryError::InsufficientData);
93            }
94            let transition_id = read_u16(&data[1..3])?;
95            let commit_message = data[3..].to_vec();
96            Ok(crate::Event::DaveMlsAnnounceCommitTransition(DaveMlsAnnounceCommitTransition {
97                transition_id,
98                commit_message,
99            }))
100        },
101        30 => {
102            // DaveMlsWelcome
103            if data.len() < 3 {
104                return Err(BinaryError::InsufficientData);
105            }
106            let transition_id = read_u16(&data[1..3])?;
107            let welcome = data[3..].to_vec();
108            Ok(crate::Event::DaveMlsWelcome(DaveMlsWelcome {
109                transition_id,
110                welcome,
111            }))
112        },
113        // Unknown opcodes: Log and skip (might be new Discord protocol extensions)
114        other => {
115            #[cfg(debug_assertions)]
116            eprintln!(
117                "[DAVE Binary] Skipping unknown opcode {}: {:02X?}",
118                other,
119                &data[..data.len().min(32)]
120            );
121
122            Err(BinaryError::InvalidOpcode(other))
123        },
124    }
125}
126
127/// Serialize a binary message to Discord (opcodes 26, 28)
128pub fn serialize_binary_event(event: &crate::Event) -> Result<Vec<u8>, BinaryError> {
129    match event {
130        crate::Event::DaveMlsKeyPackage(payload) => {
131            // Opcode 26: [opcode: u8][key_package: Vec<u8>]
132            let mut data = Vec::with_capacity(1 + payload.key_package.len());
133            data.push(Opcode::DaveMlsKeyPackage as u8);
134            data.extend_from_slice(&payload.key_package);
135            Ok(data)
136        },
137        crate::Event::DaveMlsCommitWelcome(payload) => {
138            // Opcode 28: [opcode: u8][commit: Vec<u8>][welcome: Option<Vec<u8>>]
139            // The welcome is included if present, following MLS TLS encoding for optional values
140            let mut data = Vec::with_capacity(
141                1 + payload.commit.len() + payload.welcome.as_ref().map_or(0, |w| w.len()),
142            );
143            data.push(Opcode::DaveMlsCommitWelcome as u8);
144            data.extend_from_slice(&payload.commit);
145
146            if let Some(welcome) = &payload.welcome {
147                data.extend_from_slice(welcome);
148            }
149            Ok(data)
150        },
151        _ => Err(BinaryError::ParseError("Event is not a binary DAVE opcode".to_string())),
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158
159    #[test]
160    fn test_deserialize_external_sender() {
161        let data = vec![
162            25,   // opcode = 25
163            0xDE, 0xAD, 0xBE, 0xEF, // external_sender data
164        ];
165
166        let event = deserialize_binary_event(&data).unwrap();
167        match event {
168            crate::Event::DaveMlsExternalSender(payload) => {
169                assert_eq!(payload.external_sender, vec![0xDE, 0xAD, 0xBE, 0xEF]);
170            },
171            _ => panic!("Wrong event type"),
172        }
173    }
174
175    #[test]
176    fn test_deserialize_proposals() {
177        let data = vec![
178            27,   // opcode = 27
179            0,    // operation_type = Append
180            0xCA, 0xFE, // proposals data
181        ];
182
183        let event = deserialize_binary_event(&data).unwrap();
184        match event {
185            crate::Event::DaveMlsProposals(payload) => {
186                assert!(matches!(payload.operation_type, DaveMlsProposalsOperationType::Append));
187                assert_eq!(payload.proposals, vec![0xCA, 0xFE]);
188            },
189            _ => panic!("Wrong event type"),
190        }
191    }
192
193    #[test]
194    fn test_serialize_key_package() {
195        let event = crate::Event::DaveMlsKeyPackage(DaveMlsKeyPackage {
196            key_package: vec![0xAA, 0xBB, 0xCC],
197        });
198
199        let data = serialize_binary_event(&event).unwrap();
200        assert_eq!(data, vec![26, 0xAA, 0xBB, 0xCC]);
201    }
202
203    #[test]
204    fn test_serialize_commit_welcome() {
205        let event = crate::Event::DaveMlsCommitWelcome(DaveMlsCommitWelcome {
206            commit: vec![0x11, 0x22],
207            welcome: Some(vec![0x33, 0x44]),
208        });
209
210        let data = serialize_binary_event(&event).unwrap();
211        assert_eq!(data, vec![28, 0x11, 0x22, 0x33, 0x44]);
212    }
213}