Skip to content

Commit

Permalink
fix: not emitting on_voice_ready when bot is in vc first, and user jo…
Browse files Browse the repository at this point in the history
…ins second, establishing MLS group
  • Loading branch information
braindigitalis committed Oct 9, 2024
1 parent a2fd8a7 commit 0ddc18a
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 25 deletions.
4 changes: 1 addition & 3 deletions src/dpp/dave/session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,9 +365,7 @@ catch (const std::exception& e) {
return failed_t{};
}

std::optional<roster_map> session::process_welcome(
std::vector<uint8_t> welcome,
std::set<std::string> const& recognizedUserIDs) noexcept
std::optional<roster_map> session::process_welcome(std::vector<uint8_t> welcome, std::set<std::string> const& recognizedUserIDs) noexcept
try {
if (!has_cryptographic_state_for_welcome()) {
creator.log(dpp::ll_warning, "Missing local crypto state necessary to process MLS welcome");
Expand Down
4 changes: 4 additions & 0 deletions src/dpp/voice/enabled/displayable_code.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ namespace dpp {

std::string generate_displayable_code(const std::vector<uint8_t> &data, size_t desired_length = 30, size_t group_size = 5) {

if (data.empty()) {
return "";
}

const size_t group_modulus = std::pow(10, group_size);
std::stringstream result;

Expand Down
68 changes: 46 additions & 22 deletions src/dpp/voice/enabled/handle_frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ constexpr dave::decryptor::duration RATCHET_EXPIRY = 10s;

void discord_voice_client::update_ratchets(bool force) {

if (!mls_state || !mls_state->dave_session) {
return;
}

/**
* https://www.ietf.org/archive/id/draft-ietf-mls-protocol-14.html#name-epoch-authenticators
* 9.7. Epoch Authenticators
* The main MLS key schedule provides a per-epoch epoch_authenticator. If one member of the group is being impersonated by an active attacker,
* the epoch_authenticator computed by their client will differ from those computed by the other group members.
*/
mls_state->privacy_code = generate_displayable_code(mls_state->dave_session->get_last_epoch_authenticator());
if (!mls_state->privacy_code.empty()) {
log(ll_info, "New E2EE Privacy Code: " + mls_state->privacy_code);
}

if (!force && !is_end_to_end_encrypted()) {
/* Ratchet update not forced, and not already established DAVE MLS, bail here */
return;
Expand All @@ -52,35 +67,36 @@ void discord_voice_client::update_ratchets(bool force) {
*/
log(ll_debug, "Updating MLS ratchets for " + std::to_string(dave_mls_user_list.size() + 1) + " user(s)");
for (const auto& user : dave_mls_user_list) {
log(ll_debug, "Setting decryptor key ratchet for user: " + user + ", protocol version: " + std::to_string(mls_state->dave_session->get_protocol_version()));
dpp::snowflake u{user};
if (u == creator->me.id) {
continue;
}
decryptor_list::iterator decryptor;
if (force) {
/* Forced update of all ratchets (new group) - erase all snowflakes if they exist, insert new ones */
mls_state->decryptors.erase(u);
if (mls_state->decryptors.erase(u) == 1) {
log(ll_debug, "Replacing decryptor key ratchet for EXISTING user: " + user + ", protocol version: " + std::to_string(mls_state->dave_session->get_protocol_version()));
} else {
log(ll_debug, "Inserting decryptor key ratchet for NEW user: " + user + ", protocol version: " + std::to_string(mls_state->dave_session->get_protocol_version()));
}
auto [iter, inserted] = mls_state->decryptors.emplace(u, std::make_unique<dpp::dave::decryptor>(*creator));
decryptor = iter;
} else {
/* New user join/old user leave - insert new ratchets if they don't exist */
decryptor = mls_state->decryptors.find(u);
if (decryptor == mls_state->decryptors.end()) {
log(ll_debug, "Inserting decryptor key ratchet for NEW user: " + user + ", protocol version: " + std::to_string(mls_state->dave_session->get_protocol_version()));
auto [iter, inserted] = mls_state->decryptors.emplace(u, std::make_unique<dpp::dave::decryptor>(*creator));
decryptor = iter;
}
}
decryptor->second->transition_to_key_ratchet(mls_state->dave_session->get_key_ratchet(user), RATCHET_EXPIRY);
}
/* No expiry on sender! It's up to the receiver to decide when to discard their old keys */
mls_state->encryptor->set_key_ratchet(mls_state->dave_session->get_key_ratchet(creator->me.id.str()));

/**
* https://www.ietf.org/archive/id/draft-ietf-mls-protocol-14.html#name-epoch-authenticators
* 9.7. Epoch Authenticators
* The main MLS key schedule provides a per-epoch epoch_authenticator. If one member of the group is being impersonated by an active attacker,
* the epoch_authenticator computed by their client will differ from those computed by the other group members.
*/
mls_state->privacy_code = generate_displayable_code(mls_state->dave_session->get_last_epoch_authenticator());
log(ll_info, "New E2EE Privacy Code: " + mls_state->privacy_code);
if (mls_state->encryptor) {
log(ll_debug, "Setting key ratchet for sending audio...");
mls_state->encryptor->set_key_ratchet(mls_state->dave_session->get_key_ratchet(creator->me.id.str()));
}
}

bool discord_voice_client::handle_frame(const std::string &data, ws_opcode opcode) {
Expand Down Expand Up @@ -128,21 +144,29 @@ bool discord_voice_client::handle_frame(const std::string &data, ws_opcode opcod
}
break;
case voice_client_dave_mls_welcome: {
bool is_ready = !get_privacy_code().empty();
this->mls_state->transition_id = dave_header.get_transition_id();
log(ll_debug, "voice_client_dave_mls_welcome with transition id " + std::to_string(this->mls_state->transition_id));
dave_mls_user_list.erase(creator->me.id.str());
auto r = mls_state->dave_session->process_welcome(dave_header.get_data(), dave_mls_user_list);
if (r.has_value()) {
update_ratchets(true);
json obj = {
{ "op", voice_client_dave_transition_ready },
update_ratchets(true);
json obj = {
{ "op", voice_client_dave_transition_ready },
{
"d",
{
"d",
{
{ "transition_id", this->mls_state->transition_id },
}
{ "transition_id", this->mls_state->transition_id },
}
};
this->write(obj.dump(-1, ' ', false, json::error_handler_t::replace), OP_TEXT);
}
};
this->write(obj.dump(-1, ' ', false, json::error_handler_t::replace), OP_TEXT);
if (!is_ready) {
if (!creator->on_voice_ready.empty()) {
voice_ready_t rdy(nullptr, data);
rdy.voice_client = this;
rdy.voice_channel_id = this->channel_id;
creator->on_voice_ready.call(rdy);
}
}
}
break;
Expand Down

0 comments on commit 0ddc18a

Please sign in to comment.