Skip to content

Commit

Permalink
Merge branch 'dev' of github.com:brainboxdotcc/DPP
Browse files Browse the repository at this point in the history
  • Loading branch information
braindigitalis committed Oct 14, 2024
2 parents 20bc7cb + b720cc6 commit 8030123
Show file tree
Hide file tree
Showing 18 changed files with 876 additions and 381 deletions.
3 changes: 2 additions & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@
"chacha",
"nullopt",
"chrono",
"ciphersuite"
"ciphersuite",
"rmap"
],
"flagWords": [
"hte"
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM ubuntu:noble@sha256:b359f1067efa76f37863778f7b6d0e8d911e3ee8efa807ad01fbf5dc1ef9006b
FROM ubuntu:noble@sha256:ab64a8382e935382638764d8719362bb50ee418d944c1f3d26e0c99fae49a345

RUN apt-get update && apt-get install --no-install-recommends -y libssl-dev zlib1g-dev libopus-dev cmake pkg-config g++ gcc git make && apt-get clean && rm -rf /var/lib/apt/lists/*

Expand Down
3 changes: 2 additions & 1 deletion buildtools/classes/Generator/SyncGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public function checkForChanges(): bool
*/
public function generateHeaderDef(string $returnType, string $currentFunction, string $parameters, string $noDefaults, string $parameterTypes, string $parameterNames): string
{
return "$returnType {$currentFunction}_sync($parameters);\n\n";
return "DPP_DEPRECATED(\"Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html\") $returnType {$currentFunction}_sync($parameters);\n\n";
}

/**
Expand All @@ -107,6 +107,7 @@ public function getCommentArray(): array
return [
" * \memberof dpp::cluster",
" * @throw dpp::rest_exception upon failure to execute REST function",
" * @deprecated This function is deprecated, please use coroutines instead.",
" * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread.",
" * Avoid direct use of this function inside an event handler.",
];
Expand Down
9 changes: 7 additions & 2 deletions docpages/advanced_reference/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

At present our roadmap is:

*Short term (6 months):*: Stabilise coroutine support and release it as stable a feature
## Short term (1 month)
* Implement user apps support

*Long term*: Continue development of the library to implement Discord new features as they add them. Discord does not share their internal roadmap with library developers, so we are informed of these new features shortly before they become public given enough time to implement them. This is our permanent ongoing goal.
## Medium term (6 months)
* Stabilise DAVE E2EE support and release it as stable a feature

## Long term
* Continue development of the library to implement Discord new features as they add them. Discord does not share their internal roadmap with library developers, so we are informed of these new features shortly before they become public given enough time to implement them. This is our permanent ongoing goal.
618 changes: 410 additions & 208 deletions include/dpp/cluster_sync_calls.h

Large diffs are not rendered by default.

55 changes: 51 additions & 4 deletions include/dpp/discordvoiceclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,6 @@ enum voice_websocket_opcode_t : uint8_t {
voice_opcode_connection_hello = 8,
voice_opcode_connection_resumed = 9,
voice_opcode_multiple_clients_connect = 11,
voice_opcode_client_connect = 12,
voice_opcode_client_disconnect = 13,
voice_opcode_media_sink = 15,
voice_client_flags = 18,
Expand All @@ -167,7 +166,7 @@ enum voice_websocket_opcode_t : uint8_t {
voice_client_dave_mls_key_package = 26,
voice_client_dave_mls_proposals = 27,
voice_client_dave_mls_commit_message = 28,
voice_client_dave_announce_commit_transaction = 29,
voice_client_dave_announce_commit_transition = 29,
voice_client_dave_mls_welcome = 30,
voice_client_dave_mls_invalid_commit_welcome = 31,
};
Expand Down Expand Up @@ -207,7 +206,7 @@ struct dave_binary_header_t {
[[nodiscard]] std::vector<uint8_t> get_data() const;

/**
* Get transition ID for process_welcome
* Get transition ID for process_commit and process_welcome
*
* @return Transition ID
*/
Expand All @@ -216,7 +215,7 @@ struct dave_binary_header_t {
private:
/**
* @brief Transition id, only valid when the opcode is
* welcome state. Use get_transition_id() to obtain value.
* commit and welcome state. Use get_transition_id() to obtain value.
*/
uint16_t transition_id;
};
Expand Down Expand Up @@ -478,6 +477,12 @@ class DPP_EXPORT discord_voice_client : public websocket_client
*/
std::set<std::string> dave_mls_user_list;

/**
* @brief The list of users that have left the voice channel but
* not yet removed from MLS group.
*/
std::set<std::string> dave_mls_pending_remove_list;

/**
* @brief File descriptor for UDP connection
*/
Expand Down Expand Up @@ -678,6 +683,12 @@ class DPP_EXPORT discord_voice_client : public websocket_client
*/
size_t encode(uint8_t *input, size_t inDataSize, uint8_t *output, size_t &outDataSize);

/**
* Updates DAVE MLS ratchets for users in the VC
* @param force True to force updating of ratchets regardless of state
*/
void update_ratchets(bool force = false);

public:

/**
Expand Down Expand Up @@ -1184,6 +1195,42 @@ class DPP_EXPORT discord_voice_client : public websocket_client
* which internally uses scrypt.
*/
void get_user_privacy_code(const dpp::snowflake user, privacy_code_callback_t callback) const;

/**
* @brief Notify gateway ready for a DAVE transition.
*
* Fires Voice Ready event when appropriate.
*
* https://daveprotocol.com/#commit-handling
*
* @param data Websocket frame data
*/
void ready_for_transition(const std::string &data);

/**
* @brief Reset dave session, send voice_client_dave_mls_invalid_commit_welcome
* payload with current transition Id and our new key package to gateway.
*
* https://daveprotocol.com/#recovery-from-invalid-commit-or-welcome
*/
void recover_from_invalid_commit_welcome();

/**
* @brief Execute pending protocol upgrade/downgrade to/from dave.
* @return true if did an upgrade/downgrade
*/
bool execute_pending_upgrade_downgrade();

/**
* @brief Reset dave session and prepare initial session group.
*/
void reinit_dave_mls_group();

/**
* @brief Process roster map from commit/welcome.
* @param rmap Roster map
*/
void process_mls_group_rosters(const std::map<uint64_t, std::vector<uint8_t>>& rmap);
};

} // namespace dpp
Expand Down
8 changes: 4 additions & 4 deletions include/dpp/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
************************************************************************************/
#pragma once

#if !defined(DPP_VERSION_LONG)
#define DPP_VERSION_LONG 0x00100032
#define DPP_VERSION_SHORT 100032
#define DPP_VERSION_TEXT "D++ 10.0.32 (22-Sep-2024)"
#ifndef DPP_VERSION_LONG
#define DPP_VERSION_LONG 0x00100033
#define DPP_VERSION_SHORT 100033
#define DPP_VERSION_TEXT "D++ 10.0.33 (13-Oct-2024)"

#define DPP_VERSION_MAJOR ((DPP_VERSION_LONG & 0x00ff0000) >> 16)
#define DPP_VERSION_MINOR ((DPP_VERSION_LONG & 0x0000ff00) >> 8)
Expand Down
37 changes: 14 additions & 23 deletions library-vcpkg/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,7 @@

add_compile_definitions(HAVE_VOICE)

if (HAVE_VOICE)
file(GLOB THE_SOURCES "${DPP_ROOT_PATH}/src/dpp/events/*.cpp" "${modules_dir}/dpp/voice/enabled/*.cpp" "${DPP_ROOT_PATH}/dpp/dave/*.cpp" "${DPP_ROOT_PATH}/src/dpp/cluster/*.cpp" "${DPP_ROOT_PATH}/src/dpp/*.cpp" "${DPP_ROOT_PATH}/src/dpp/*.rc")
else()
file(GLOB THE_SOURCES "${DPP_ROOT_PATH}/src/dpp/events/*.cpp" "${modules_dir}/dpp/voice/stub/*.cpp" "${DPP_ROOT_PATH}/src/dpp/cluster/*.cpp" "${DPP_ROOT_PATH}/src/dpp/*.cpp" "${DPP_ROOT_PATH}/src/dpp/*.rc")
endif()

file(GLOB THE_SOURCES "${DPP_ROOT_PATH}/src/dpp/events/*.cpp" "${modules_dir}/dpp/voice/enabled/*.cpp" "${DPP_ROOT_PATH}/dpp/dave/*.cpp" "${DPP_ROOT_PATH}/src/dpp/cluster/*.cpp" "${DPP_ROOT_PATH}/src/dpp/*.cpp" "${DPP_ROOT_PATH}/src/dpp/*.rc")

set(LIB_NAME "${PROJECT_NAME}")

Expand Down Expand Up @@ -87,12 +82,10 @@ target_include_directories(
"$<INSTALL_INTERFACE:include>"
)

if (HAVE_VOICE)
add_subdirectory("${DPP_ROOT_PATH}/mlspp" "mlspp")
include_directories("${DPP_ROOT_PATH}/mlspp/include")
include_directories("${DPP_ROOT_PATH}/mlspp/lib/bytes/include")
include_directories("${DPP_ROOT_PATH}/mlspp/lib/hpke/include")
endif()
add_subdirectory("${DPP_ROOT_PATH}/mlspp" "mlspp")
include_directories("${DPP_ROOT_PATH}/mlspp/include")
include_directories("${DPP_ROOT_PATH}/mlspp/lib/bytes/include")
include_directories("${DPP_ROOT_PATH}/mlspp/lib/hpke/include")

set_target_properties(
"${LIB_NAME}" PROPERTIES
Expand Down Expand Up @@ -120,17 +113,15 @@ target_link_libraries(
$<$<TARGET_EXISTS:Threads::Threads>:Threads::Threads>
)

if (HAVE_VOICE)
# Private statically linked dependencies
target_link_libraries(
${LIB_NAME} PRIVATE
mlspp
mls_vectors
bytes
tls_syntax
hpke
)
endif()
# Private statically linked dependencies
target_link_libraries(
${LIB_NAME} PRIVATE
mlspp
mls_vectors
bytes
tls_syntax
hpke
)

set(CONFIG_FILE_NAME "${PROJECT_NAME}Config.cmake")
set(EXPORTED_TARGETS_NAME "${PROJECT_NAME}Targets")
Expand Down
3 changes: 1 addition & 2 deletions src/dpp/dave/decryptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ namespace dpp::dave {

constexpr auto kStatsInterval = 10s;

void decryptor::transition_to_key_ratchet(std::unique_ptr<key_ratchet_interface> keyRatchet,
duration transitionExpiry)
void decryptor::transition_to_key_ratchet(std::unique_ptr<key_ratchet_interface> keyRatchet, duration transitionExpiry)
{
if (keyRatchet) {
creator.log(dpp::ll_trace, "Transitioning to new key ratchet, expiry: " + std::to_string(transitionExpiry.count()));
Expand Down
12 changes: 2 additions & 10 deletions src/dpp/dave/encryptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ using namespace std::chrono_literals;

namespace dpp::dave {

constexpr auto kStatsInterval = 10s;

void encryptor::set_key_ratchet(std::unique_ptr<key_ratchet_interface> keyRatchet)
{
std::lock_guard<std::mutex> lock(keyGenMutex_);
Expand All @@ -56,12 +54,7 @@ void encryptor::set_passthrough_mode(bool passthroughMode)
update_current_protocol_version(passthroughMode ? 0 : max_protocol_version());
}

encryptor::result_code encryptor::encrypt(media_type mediaType,
uint32_t ssrc,
array_view<const uint8_t> frame,
array_view<uint8_t> encryptedFrame,
size_t* bytesWritten)
{
encryptor::result_code encryptor::encrypt(media_type mediaType, uint32_t ssrc, array_view<const uint8_t> frame, array_view<uint8_t> encryptedFrame, size_t* bytesWritten) {
if (mediaType != media_audio && mediaType != media_video) {
creator.log(dpp::ll_warning, "encrypt failed, invalid media type: " + std::to_string(static_cast<int>(mediaType)));
return result_code::rc_encryption_failure;
Expand Down Expand Up @@ -270,8 +263,7 @@ encryptor::cryptor_and_nonce encryptor::get_next_cryptor_and_nonce()
return {nullptr, 0};
}

auto generation = compute_wrapped_generation(currentKeyGeneration_,
++truncatedNonce_ >> RATCHET_GENERATION_SHIFT_BITS);
auto generation = compute_wrapped_generation(currentKeyGeneration_, ++truncatedNonce_ >> RATCHET_GENERATION_SHIFT_BITS);

if (generation != currentKeyGeneration_ || !cryptor_) {
currentKeyGeneration_ = generation;
Expand Down
22 changes: 22 additions & 0 deletions src/dpp/dave/frame_processors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,35 @@

namespace dpp::dave {

#if defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
/**
* @brief ARM does not have a builtin for overflow detecting add
* This implements a non-UB version of that.
* @param carry_in Input carry from previous add
* @param a First operand
* @param b Second operand
* @param result Output result
* @return True if overflow occured, false if it didn't
*/
inline uint8_t addcarry_size_t(size_t carry_in, size_t a, size_t b, size_t* result) {
size_t partial_sum = a + b;
uint8_t carry1 = (partial_sum < a);
size_t final_sum = partial_sum + carry_in;
uint8_t carry2 = (final_sum < partial_sum);
*result = final_sum;
return carry1 || carry2;
}
#endif

std::pair<bool, size_t> OverflowAdd(size_t a, size_t b)
{
size_t res;
#if defined(_MSC_VER) && defined(_M_X64)
bool didOverflow = _addcarry_u64(0, a, b, &res);
#elif defined(_MSC_VER) && defined(_M_IX86)
bool didOverflow = _addcarry_u32(0, a, b, &res);
#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
bool didOverflow = addcarry_size_t(0, a, b, &res);
#else
bool didOverflow = __builtin_add_overflow(a, b, &res);
#endif
Expand Down
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
17 changes: 12 additions & 5 deletions src/dpp/discordvoiceclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,9 @@ bool discord_voice_client::is_playing() {
}

uint16_t dave_binary_header_t::get_transition_id() const {
if (opcode != voice_client_dave_mls_welcome) {
throw dpp::logic_exception("Can't get transition ID from buffer that is not of type voice_client_dave_mls_welcome(30)");
bool has_transition_id = opcode == voice_client_dave_mls_welcome || opcode == voice_client_dave_announce_commit_transition;
if (!has_transition_id) {
throw dpp::logic_exception("Can't get transition ID from buffer that is not of type voice_client_dave_announce_commit_transition(29) or voice_client_dave_mls_welcome(30)");
}
return transition_id;
}
Expand All @@ -96,7 +97,9 @@ dave_binary_header_t::dave_binary_header_t(const std::string& buffer) {
seq = (buffer[0] << 8) | buffer[1];
opcode = buffer[2];
transition_id = (buffer[3] << 8) | buffer[4];
package.assign(buffer.begin() + (opcode == voice_client_dave_mls_welcome ? 5 : 3), buffer.end());

bool has_transition_id = opcode == voice_client_dave_mls_welcome || opcode == voice_client_dave_announce_commit_transition;
package.assign(buffer.begin() + (has_transition_id ? 5 : 3), buffer.end());
}

std::vector<uint8_t> dave_binary_header_t::get_data() const {
Expand Down Expand Up @@ -127,13 +130,17 @@ void discord_voice_client::get_user_privacy_code(const dpp::snowflake user, priv

bool discord_voice_client::is_end_to_end_encrypted() const {
#ifdef HAVE_VOICE
if (mls_state == nullptr) {
if (mls_state == nullptr || mls_state->encryptor == nullptr) {
return false;
}

bool has_pending_downgrade = mls_state->pending_transition.is_pending && mls_state->pending_transition.protocol_version != dave_version_1;

return !has_pending_downgrade && !mls_state->privacy_code.empty();
/*
* A dave_version 0 should be enough to know we're in non-e2ee session, we should also check for pending downgrade and
* whether session encryptor actually has key rachet set to encrypt opus packets.
*/
return !has_pending_downgrade && dave_version != dave_version_none && mls_state->encryptor->has_key_ratchet();
#else
return false;
#endif
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
Loading

0 comments on commit 8030123

Please sign in to comment.