Skip to content

Commit 8009003

Browse files
vt-dVitamin
authored andcommitted
Add DAVE protocol support to serenity-voice-model (#3475)
Co-authored-by: Vitamin <cbayush6@gmail.com>
1 parent 9354958 commit 8009003

File tree

4 files changed

+330
-1
lines changed

4 files changed

+330
-1
lines changed

voice-model/src/event/from.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,69 @@ impl From<ClientDisconnect> for Event {
6666
Event::ClientDisconnect(i)
6767
}
6868
}
69+
70+
impl From<DaveTransitionReady> for Event {
71+
fn from(i: DaveTransitionReady) -> Self {
72+
Event::DaveTransitionReady(i)
73+
}
74+
}
75+
76+
impl From<DavePrepareEpoch> for Event {
77+
fn from(i: DavePrepareEpoch) -> Self {
78+
Event::DavePrepareEpoch(i)
79+
}
80+
}
81+
82+
impl From<DaveMlsExternalSender> for Event {
83+
fn from(i: DaveMlsExternalSender) -> Self {
84+
Event::DaveMlsExternalSender(i)
85+
}
86+
}
87+
88+
impl From<DaveMlsKeyPackage> for Event {
89+
fn from(i: DaveMlsKeyPackage) -> Self {
90+
Event::DaveMlsKeyPackage(i)
91+
}
92+
}
93+
94+
impl From<DaveMlsProposals> for Event {
95+
fn from(i: DaveMlsProposals) -> Self {
96+
Event::DaveMlsProposals(i)
97+
}
98+
}
99+
100+
impl From<DaveMlsCommitWelcome> for Event {
101+
fn from(i: DaveMlsCommitWelcome) -> Self {
102+
Event::DaveMlsCommitWelcome(i)
103+
}
104+
}
105+
106+
impl From<DaveMlsWelcome> for Event {
107+
fn from(i: DaveMlsWelcome) -> Self {
108+
Event::DaveMlsWelcome(i)
109+
}
110+
}
111+
112+
impl From<DavePrepareTransition> for Event {
113+
fn from(i: DavePrepareTransition) -> Self {
114+
Event::DavePrepareTransition(i)
115+
}
116+
}
117+
118+
impl From<DaveExecuteTransition> for Event {
119+
fn from(i: DaveExecuteTransition) -> Self {
120+
Event::DaveExecuteTransition(i)
121+
}
122+
}
123+
124+
impl From<DaveMlsAnnounceCommitTransition> for Event {
125+
fn from(i: DaveMlsAnnounceCommitTransition) -> Self {
126+
Event::DaveMlsAnnounceCommitTransition(i)
127+
}
128+
}
129+
130+
impl From<DaveMlsInvalidCommitWelcome> for Event {
131+
fn from(i: DaveMlsInvalidCommitWelcome) -> Self {
132+
Event::DaveMlsInvalidCommitWelcome(i)
133+
}
134+
}

voice-model/src/event/mod.rs

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,28 @@ pub enum Event {
4040
ClientConnect(ClientConnect),
4141
/// Status update in the current channel, indicating that a user has disconnected.
4242
ClientDisconnect(ClientDisconnect),
43+
/// DAVE: Signals the bot is ready for group operations after epoch transition.
44+
DaveTransitionReady(DaveTransitionReady),
45+
/// DAVE: Notifies of an upcoming epoch change.
46+
DavePrepareEpoch(DavePrepareEpoch),
47+
/// DAVE: Provides the external sender package for MLS group initialization.
48+
DaveMlsExternalSender(DaveMlsExternalSender),
49+
/// DAVE: Sends the key package for MLS group participation.
50+
DaveMlsKeyPackage(DaveMlsKeyPackage),
51+
/// DAVE: Provides proposals for group member changes.
52+
DaveMlsProposals(DaveMlsProposals),
53+
/// DAVE: Provides a commit with optional welcome for group transitions.
54+
DaveMlsCommitWelcome(DaveMlsCommitWelcome),
55+
/// DAVE: Provides the welcome message for new members.
56+
DaveMlsWelcome(DaveMlsWelcome),
57+
/// DAVE: Prepares for a protocol transition.
58+
DavePrepareTransition(DavePrepareTransition),
59+
/// DAVE: Executes a prepared protocol transition.
60+
DaveExecuteTransition(DaveExecuteTransition),
61+
/// DAVE: Announces a commit for group transition.
62+
DaveMlsAnnounceCommitTransition(DaveMlsAnnounceCommitTransition),
63+
/// DAVE: Reports an invalid commit or welcome message.
64+
DaveMlsInvalidCommitWelcome(DaveMlsInvalidCommitWelcome),
4365
}
4466

4567
impl Event {
@@ -58,6 +80,17 @@ impl Event {
5880
Resumed => Opcode::Resumed,
5981
ClientConnect(_) => Opcode::ClientConnect,
6082
ClientDisconnect(_) => Opcode::ClientDisconnect,
83+
DaveTransitionReady(_) => Opcode::DaveTransitionReady,
84+
DavePrepareEpoch(_) => Opcode::DavePrepareEpoch,
85+
DaveMlsExternalSender(_) => Opcode::DaveMlsExternalSender,
86+
DaveMlsKeyPackage(_) => Opcode::DaveMlsKeyPackage,
87+
DaveMlsProposals(_) => Opcode::DaveMlsProposals,
88+
DaveMlsCommitWelcome(_) => Opcode::DaveMlsCommitWelcome,
89+
DaveMlsWelcome(_) => Opcode::DaveMlsWelcome,
90+
DavePrepareTransition(_) => Opcode::DavePrepareTransition,
91+
DaveExecuteTransition(_) => Opcode::DaveExecuteTransition,
92+
DaveMlsAnnounceCommitTransition(_) => Opcode::DaveMlsAnnounceCommitTransition,
93+
DaveMlsInvalidCommitWelcome(_) => Opcode::DaveMlsInvalidCommitWelcome,
6194
}
6295
}
6396
}
@@ -85,6 +118,17 @@ impl Serialize for Event {
85118
Resumed => s.serialize_field("d", &None::<()>)?,
86119
ClientConnect(e) => s.serialize_field("d", e)?,
87120
ClientDisconnect(e) => s.serialize_field("d", e)?,
121+
DaveTransitionReady(e) => s.serialize_field("d", e)?,
122+
DavePrepareEpoch(e) => s.serialize_field("d", e)?,
123+
DaveMlsExternalSender(e) => s.serialize_field("d", e)?,
124+
DaveMlsKeyPackage(e) => s.serialize_field("d", e)?,
125+
DaveMlsProposals(e) => s.serialize_field("d", e)?,
126+
DaveMlsCommitWelcome(e) => s.serialize_field("d", e)?,
127+
DaveMlsWelcome(e) => s.serialize_field("d", e)?,
128+
DavePrepareTransition(e) => s.serialize_field("d", e)?,
129+
DaveExecuteTransition(e) => s.serialize_field("d", e)?,
130+
DaveMlsAnnounceCommitTransition(e) => s.serialize_field("d", e)?,
131+
DaveMlsInvalidCommitWelcome(e) => s.serialize_field("d", e)?,
88132
}
89133

90134
s.end()
@@ -115,7 +159,7 @@ impl<'de> Visitor<'de> for EventVisitor {
115159
let valid_op = Opcode::deserialize(des).map_err(|_| {
116160
DeError::invalid_value(
117161
Unexpected::Unsigned(raw.into()),
118-
&"opcode in [0--9] + [12--13]",
162+
&"opcode in [0--9] + [12--13] + [21--24] + [26--31]",
119163
)
120164
})?;
121165
op = Some(valid_op);
@@ -143,6 +187,28 @@ impl<'de> Visitor<'de> for EventVisitor {
143187
return Ok(map.next_value::<ClientConnect>()?.into()),
144188
Some(Opcode::ClientDisconnect) =>
145189
return Ok(map.next_value::<ClientDisconnect>()?.into()),
190+
Some(Opcode::DaveTransitionReady) =>
191+
return Ok(map.next_value::<DaveTransitionReady>()?.into()),
192+
Some(Opcode::DavePrepareEpoch) =>
193+
return Ok(map.next_value::<DavePrepareEpoch>()?.into()),
194+
Some(Opcode::DaveMlsExternalSender) =>
195+
return Ok(map.next_value::<DaveMlsExternalSender>()?.into()),
196+
Some(Opcode::DaveMlsKeyPackage) =>
197+
return Ok(map.next_value::<DaveMlsKeyPackage>()?.into()),
198+
Some(Opcode::DaveMlsProposals) =>
199+
return Ok(map.next_value::<DaveMlsProposals>()?.into()),
200+
Some(Opcode::DaveMlsCommitWelcome) =>
201+
return Ok(map.next_value::<DaveMlsCommitWelcome>()?.into()),
202+
Some(Opcode::DaveMlsWelcome) =>
203+
return Ok(map.next_value::<DaveMlsWelcome>()?.into()),
204+
Some(Opcode::DavePrepareTransition) =>
205+
return Ok(map.next_value::<DavePrepareTransition>()?.into()),
206+
Some(Opcode::DaveExecuteTransition) =>
207+
return Ok(map.next_value::<DaveExecuteTransition>()?.into()),
208+
Some(Opcode::DaveMlsAnnounceCommitTransition) =>
209+
return Ok(map.next_value::<DaveMlsAnnounceCommitTransition>()?.into()),
210+
Some(Opcode::DaveMlsInvalidCommitWelcome) =>
211+
return Ok(map.next_value::<DaveMlsInvalidCommitWelcome>()?.into()),
146212
None => {
147213
d = Some(map.next_value::<&RawValue>()?);
148214
},
@@ -178,6 +244,25 @@ impl<'de> Visitor<'de> for EventVisitor {
178244
Opcode::Resumed => Ok(Event::Resumed),
179245
Opcode::ClientConnect => serde_json::from_str::<ClientConnect>(d).map(Into::into),
180246
Opcode::ClientDisconnect => serde_json::from_str::<ClientDisconnect>(d).map(Into::into),
247+
Opcode::DaveTransitionReady =>
248+
serde_json::from_str::<DaveTransitionReady>(d).map(Into::into),
249+
Opcode::DavePrepareEpoch => serde_json::from_str::<DavePrepareEpoch>(d).map(Into::into),
250+
Opcode::DaveMlsExternalSender =>
251+
serde_json::from_str::<DaveMlsExternalSender>(d).map(Into::into),
252+
Opcode::DaveMlsKeyPackage =>
253+
serde_json::from_str::<DaveMlsKeyPackage>(d).map(Into::into),
254+
Opcode::DaveMlsProposals => serde_json::from_str::<DaveMlsProposals>(d).map(Into::into),
255+
Opcode::DaveMlsCommitWelcome =>
256+
serde_json::from_str::<DaveMlsCommitWelcome>(d).map(Into::into),
257+
Opcode::DaveMlsWelcome => serde_json::from_str::<DaveMlsWelcome>(d).map(Into::into),
258+
Opcode::DavePrepareTransition =>
259+
serde_json::from_str::<DavePrepareTransition>(d).map(Into::into),
260+
Opcode::DaveExecuteTransition =>
261+
serde_json::from_str::<DaveExecuteTransition>(d).map(Into::into),
262+
Opcode::DaveMlsAnnounceCommitTransition =>
263+
serde_json::from_str::<DaveMlsAnnounceCommitTransition>(d).map(Into::into),
264+
Opcode::DaveMlsInvalidCommitWelcome =>
265+
serde_json::from_str::<DaveMlsInvalidCommitWelcome>(d).map(Into::into),
181266
})
182267
.map_err(DeError::custom)
183268
}

voice-model/src/opcode.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,26 @@ pub enum Opcode {
3333
ClientConnect = 12,
3434
/// Message indicating that another user has disconnected from the voice channel.
3535
ClientDisconnect = 13,
36+
/// DAVE: Signals the bot is ready for group operations after epoch transition.
37+
DaveTransitionReady = 23,
38+
/// DAVE: Notifies of an upcoming epoch change.
39+
DavePrepareEpoch = 24,
40+
/// DAVE: Provides the external sender package for MLS group initialization.
41+
DaveMlsExternalSender = 25,
42+
/// DAVE: Sends the key package for MLS group participation.
43+
DaveMlsKeyPackage = 26,
44+
/// DAVE: Provides proposals for group member changes (add/remove).
45+
DaveMlsProposals = 27,
46+
/// DAVE: Provides a commit with optional welcome for group transitions.
47+
DaveMlsCommitWelcome = 28,
48+
/// DAVE: Provides the welcome message for new members.
49+
DaveMlsWelcome = 30,
50+
/// DAVE: Prepares for a protocol transition.
51+
DavePrepareTransition = 21,
52+
/// DAVE: Executes a prepared protocol transition.
53+
DaveExecuteTransition = 22,
54+
/// DAVE: Announces a commit for group transition.
55+
DaveMlsAnnounceCommitTransition = 29,
56+
/// DAVE: Reports an invalid commit or welcome message.
57+
DaveMlsInvalidCommitWelcome = 31,
3658
}

voice-model/src/payload.rs

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,159 @@ pub struct Speaking {
132132
/// Used alongside the SSRC to map individual packets to their sender.
133133
pub user_id: Option<UserId>,
134134
}
135+
136+
/// DAVE protocol version field in the Identify payload.
137+
///
138+
/// Signals to Discord that the bot supports end-to-end encryption
139+
/// and which protocol version(s) it supports.
140+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
141+
#[serde(transparent)]
142+
pub struct MaxDaveProtocolVersion(pub u16);
143+
144+
impl MaxDaveProtocolVersion {
145+
/// Protocol version 1 - Current DAVE protocol version
146+
pub const V1: Self = Self(1);
147+
}
148+
149+
impl Default for MaxDaveProtocolVersion {
150+
fn default() -> Self {
151+
Self::V1
152+
}
153+
}
154+
155+
/// External sender package for DAVE MLS group creation.
156+
///
157+
/// Received from the server (Opcode 25) containing the voice gateway's
158+
/// credential and signature public key for the MLS group's external_senders extension.
159+
#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
160+
pub struct DaveMlsExternalSender {
161+
/// Serialized ExternalSender containing signature_key and credential.
162+
pub external_sender: Vec<u8>,
163+
}
164+
165+
/// Key package for DAVE MLS group participation.
166+
///
167+
/// Sent to the server (Opcode 26) during the join handshake.
168+
/// The basic credential identity is the big-endian 64-bit user ID.
169+
#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
170+
pub struct DaveMlsKeyPackage {
171+
/// Serialized MLSMessage containing the KeyPackage (see RFC 9420).
172+
pub key_package: Vec<u8>,
173+
}
174+
175+
/// Operation type for MLS proposals.
176+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
177+
#[serde(rename_all = "snake_case")]
178+
#[repr(u8)]
179+
pub enum DaveMlsProposalsOperationType {
180+
/// Append proposals to the pending proposal list.
181+
Append = 0,
182+
/// Revoke previously sent proposals by their reference.
183+
Revoke = 1,
184+
}
185+
186+
/// Proposals for group member changes.
187+
///
188+
/// Received from the server (Opcode 27) for add/remove operations.
189+
/// Contains either proposals to append or proposal refs to revoke.
190+
#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
191+
pub struct DaveMlsProposals {
192+
/// The type of operation (append or revoke).
193+
pub operation_type: DaveMlsProposalsOperationType,
194+
/// Serialized proposal data.
195+
/// For append: serialized MLSMessage proposal messages.
196+
/// For revoke: serialized ProposalRef values.
197+
pub proposals: Vec<u8>,
198+
}
199+
200+
/// Commit with optional welcome message for group transitions.
201+
///
202+
/// Sent by the client (Opcode 28) after processing proposals.
203+
/// When at least one add proposal is handled, the welcome message MUST be included.
204+
#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
205+
pub struct DaveMlsCommitWelcome {
206+
/// Serialized MLS commit message (see RFC 9420 MLSMessage and Commit definitions).
207+
pub commit: Vec<u8>,
208+
/// Optional MLS Welcome message for new members (see RFC 9420 Welcome definition).
209+
/// Required when the commit includes one or more add proposals.
210+
#[serde(skip_serializing_if = "Option::is_none")]
211+
pub welcome: Option<Vec<u8>>,
212+
}
213+
214+
/// Welcome message for new members joining the group.
215+
///
216+
/// Received from the server (Opcode 30) as confirmation of successful join.
217+
/// Includes the transition ID for the group transition.
218+
#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
219+
pub struct DaveMlsWelcome {
220+
/// The transition ID for this group transition.
221+
pub transition_id: u16,
222+
/// Serialized MLS Welcome message (see RFC 9420 Welcome definition).
223+
pub welcome: Vec<u8>,
224+
}
225+
226+
/// Prepare epoch notification before group transition.
227+
///
228+
/// Received from the server (Opcode 24) to signal an upcoming epoch change.
229+
/// When `epoch` is 1, this indicates a new MLS group is to be created.
230+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
231+
pub struct DavePrepareEpoch {
232+
/// The protocol version for the upcoming epoch.
233+
pub protocol_version: u16,
234+
/// The new epoch number after the transition.
235+
pub epoch: u64,
236+
}
237+
238+
/// Transition ready confirmation.
239+
///
240+
/// Sent by the client (Opcode 23) to confirm it's ready for a previously announced transition.
241+
/// The client sends this after processing a commit or preparing receive-side decryptors.
242+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
243+
pub struct DaveTransitionReady {
244+
/// The transition ID the client is ready to execute.
245+
pub transition_id: u16,
246+
}
247+
248+
/// Prepare protocol transition notification.
249+
///
250+
/// Received from the server (Opcode 21) to announce an upcoming protocol transition.
251+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
252+
pub struct DavePrepareTransition {
253+
/// The protocol version for the transition.
254+
pub protocol_version: u16,
255+
/// The transition ID for this transition.
256+
pub transition_id: u16,
257+
}
258+
259+
/// Execute protocol transition notification.
260+
///
261+
/// Received from the server (Opcode 22) to execute a previously prepared transition.
262+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
263+
pub struct DaveExecuteTransition {
264+
/// The transition ID for this transition.
265+
pub transition_id: u16,
266+
}
267+
268+
/// Announce commit for group transition.
269+
///
270+
/// Received from the server (Opcode 29) to broadcast an MLS commit for moving to the next epoch.
271+
/// The commit is one received from a group member via dave_mls_commit_welcome (Opcode 28).
272+
#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
273+
pub struct DaveMlsAnnounceCommitTransition {
274+
/// Sequence number for the announcement.
275+
pub sequence_number: u16,
276+
/// The transition ID for this group epoch change.
277+
pub transition_id: u16,
278+
/// Serialized MLS commit message (see RFC 9420 MLSMessage and Commit definitions).
279+
pub commit_message: Vec<u8>,
280+
}
281+
282+
/// Invalid commit or welcome report.
283+
///
284+
/// Sent to the server (Opcode 31) to report an invalid commit or welcome message.
285+
/// This asks the voice gateway to remove and re-add the member to recover.
286+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
287+
pub struct DaveMlsInvalidCommitWelcome {
288+
/// The transition ID in which the invalid Commit or Welcome was received.
289+
pub transition_id: u16,
290+
}

0 commit comments

Comments
 (0)