From 5e6c81a98ea1983df35537d3ca7e92183c86b640 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 25 Mar 2025 00:11:21 +0500 Subject: [PATCH] Parse e2e participant keys. --- Telegram/SourceFiles/calls/calls_instance.cpp | 42 ++++++++ Telegram/SourceFiles/calls/calls_instance.h | 15 +++ .../calls/group/calls_group_call.cpp | 102 ++++++++++++++---- .../calls/group/calls_group_call.h | 22 ++++ .../calls/group/calls_group_common.cpp | 9 +- .../calls/group/calls_group_common.h | 9 +- Telegram/SourceFiles/data/data_group_call.cpp | 7 ++ Telegram/SourceFiles/data/data_group_call.h | 5 + Telegram/SourceFiles/tde2e/tde2e_api.cpp | 17 ++- Telegram/SourceFiles/tde2e/tde2e_api.h | 28 +++++ .../window/window_session_controller.cpp | 13 ++- 11 files changed, 240 insertions(+), 29 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp index 65dc10fb39..f9ab9ebe68 100644 --- a/Telegram/SourceFiles/calls/calls_instance.cpp +++ b/Telegram/SourceFiles/calls/calls_instance.cpp @@ -230,6 +230,30 @@ void Instance::startOrJoinGroupCall( }); } +void Instance::startOrJoinConferenceCall( + std::shared_ptr show, + StartConferenceCallArgs args) { + destroyCurrentCall(); + + auto call = std::make_unique( + _delegate.get(), + Calls::Group::ConferenceInfo{ + .call = args.call, + .linkSlug = args.linkSlug, + .joinMessageId = args.joinMessageId, + }); + const auto raw = call.get(); + + args.call->peer()->session().account().sessionChanges( + ) | rpl::start_with_next([=] { + destroyGroupCall(raw); + }, raw->lifetime()); + + _currentGroupCallPanel = std::make_unique(raw); + _currentGroupCall = std::move(call); + _currentGroupCallChanges.fire_copy(raw); +} + void Instance::confirmLeaveCurrent( std::shared_ptr show, not_null peer, @@ -409,6 +433,24 @@ void Instance::createGroupCall( _currentGroupCallChanges.fire_copy(raw); } +void Instance::createConferenceCall(Group::ConferenceInfo info) { + destroyCurrentCall(); + + auto call = std::make_unique( + _delegate.get(), + std::move(info)); + const auto raw = call.get(); + + raw->peer()->session().account().sessionChanges( + ) | rpl::start_with_next([=] { + destroyGroupCall(raw); + }, raw->lifetime()); + + _currentGroupCallPanel = std::make_unique(raw); + _currentGroupCall = std::move(call); + _currentGroupCallChanges.fire_copy(raw); +} + void Instance::refreshDhConfig() { Expects(_currentCall != nullptr); diff --git a/Telegram/SourceFiles/calls/calls_instance.h b/Telegram/SourceFiles/calls/calls_instance.h index d52a0e9a23..724140b60b 100644 --- a/Telegram/SourceFiles/calls/calls_instance.h +++ b/Telegram/SourceFiles/calls/calls_instance.h @@ -13,6 +13,10 @@ namespace crl { class semaphore; } // namespace crl +namespace Data { +class GroupCall; +} // namespace Data + namespace Platform { enum class PermissionType; } // namespace Platform @@ -31,6 +35,7 @@ class Show; namespace Calls::Group { struct JoinInfo; +struct ConferenceInfo; class Panel; class ChooseJoinAsProcess; class StartRtmpProcess; @@ -59,6 +64,12 @@ struct StartGroupCallArgs { bool scheduleNeeded = false; }; +struct StartConferenceCallArgs { + std::shared_ptr call; + QString linkSlug; + MsgId joinMessageId; +}; + class Instance final : public base::has_weak_ptr { public: Instance(); @@ -69,6 +80,9 @@ public: std::shared_ptr show, not_null peer, StartGroupCallArgs args); + void startOrJoinConferenceCall( + std::shared_ptr show, + StartConferenceCallArgs args); void showStartWithRtmp( std::shared_ptr show, not_null peer); @@ -121,6 +135,7 @@ private: void createGroupCall( Group::JoinInfo info, const MTPInputGroupCall &inputCall); + void createConferenceCall(Group::ConferenceInfo info); void destroyGroupCall(not_null call); void confirmLeaveCurrent( std::shared_ptr show, diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.cpp b/Telegram/SourceFiles/calls/group/calls_group_call.cpp index ab1b80117c..268b356527 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_call.cpp @@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "base/global_shortcuts.h" #include "base/random.h" +#include "tde2e/tde2e_api.h" #include "webrtc/webrtc_video_track.h" #include "webrtc/webrtc_create_adm.h" #include "webrtc/webrtc_environment.h" @@ -418,6 +419,21 @@ std::shared_ptr ParseVideoParams( return data; } +std::shared_ptr ParseParticipantState( + const MTPDgroupCallParticipant &data) { + if (!data.vpublic_key() || data.vpeer().type() != mtpc_peerUser) { + return nullptr; + } + const auto &v = *data.vpublic_key(); + const auto userId = data.vpeer().c_peerUser().vuser_id().v; + using State = TdE2E::ParticipantState; + return std::make_shared(State{ + .id = uint64(userId), + .key = { .a = v.h.h, .b = v.h.l, .c = v.l.h, .d = v.l.l }, + }); + +} + GroupCall::LoadPartTask::LoadPartTask( base::weak_ptr call, int64 time, @@ -569,18 +585,38 @@ GroupCall::GroupCall( not_null delegate, Group::JoinInfo info, const MTPInputGroupCall &inputCall) +: GroupCall(delegate, info, {}, inputCall) { +} + +GroupCall::GroupCall( + not_null delegate, + Group::ConferenceInfo info) +: GroupCall(delegate, Group::JoinInfo{ + .peer = info.call->peer(), + .joinAs = info.call->peer(), +}, info, info.call->input()) { +} + +GroupCall::GroupCall( + not_null delegate, + Group::JoinInfo join, + Group::ConferenceInfo conference, + const MTPInputGroupCall &inputCall) : _delegate(delegate) -, _peer(info.peer) +, _conferenceCall(conference.call) +, _peer(join.peer) , _history(_peer->owner().history(_peer)) , _api(&_peer->session().mtp()) -, _joinAs(info.joinAs) -, _possibleJoinAs(std::move(info.possibleJoinAs)) -, _joinHash(info.joinHash) -, _rtmpUrl(info.rtmpInfo.url) -, _rtmpKey(info.rtmpInfo.key) +, _joinAs(join.joinAs) +, _possibleJoinAs(std::move(join.possibleJoinAs)) +, _joinHash(join.joinHash) +, _conferenceLinkSlug(conference.linkSlug) +, _conferenceJoinMessageId(conference.joinMessageId) +, _rtmpUrl(join.rtmpInfo.url) +, _rtmpKey(join.rtmpInfo.key) , _canManage(Data::CanManageGroupCallValue(_peer)) , _id(inputCall.c_inputGroupCall().vid().v) -, _scheduleDate(info.scheduleDate) +, _scheduleDate(join.scheduleDate) , _lastSpokeCheckTimer([=] { checkLastSpoke(); }) , _checkJoinedTimer([=] { checkJoined(); }) , _playbackDeviceId( @@ -601,9 +637,14 @@ GroupCall::GroupCall( Webrtc::DeviceIdOrDefault(Core::App().settings().cameraDeviceIdValue())) , _pushToTalkCancelTimer([=] { pushToTalkCancel(); }) , _connectingSoundTimer([=] { playConnectingSoundOnce(); }) -, _listenersHidden(info.rtmp) -, _rtmp(info.rtmp) +, _listenersHidden(join.rtmp) +, _rtmp(join.rtmp) , _rtmpVolume(Group::kDefaultVolume) { + if (_conferenceCall) { + _e2eState = std::make_unique( + TdE2E::CreateCallState()); + } + _muted.value( ) | rpl::combine_previous( ) | rpl::start_with_next([=](MuteState previous, MuteState state) { @@ -657,9 +698,9 @@ GroupCall::GroupCall( setupOutgoingVideo(); if (_id) { - join(inputCall); + this->join(inputCall); } else { - start(info.scheduleDate, info.rtmp); + start(join.scheduleDate, join.rtmp); } if (_scheduleDate) { saveDefaultJoinAs(joinAs()); @@ -1055,6 +1096,9 @@ void GroupCall::setRtmpInfo(const Calls::Group::RtmpInfo &value) { } Data::GroupCall *GroupCall::lookupReal() const { + if (_conferenceCall) { + return _conferenceCall.get(); + } const auto real = _peer->groupCall(); return (real && real->id() == _id) ? real : nullptr; } @@ -1373,14 +1417,24 @@ void GroupCall::rejoin(not_null as) { : Flag::f_invite_hash) | (wasVideoStopped ? Flag::f_video_stopped - : Flag(0)); + : Flag(0)) + | (_conferenceJoinMessageId ? Flag::f_invite_msg_id : Flag()) + | (_e2eState ? Flag::f_public_key : Flag()); + const auto publicKey = _e2eState + ? _e2eState->myKey + : TdE2E::PublicKey(); _api.request(MTPphone_JoinGroupCall( MTP_flags(flags), - inputCall(), + (_conferenceLinkSlug.isEmpty() + ? inputCall() + : MTP_inputGroupCallSlug( + MTP_string(_conferenceLinkSlug))), joinAs()->input, MTP_string(_joinHash), - MTPint256(), // public_key - MTPint(), // invite_msg_id + MTP_int256( + MTP_int128(publicKey.a, publicKey.b), + MTP_int128(publicKey.c, publicKey.d)), + MTP_int(_conferenceJoinMessageId.bare), MTP_dataJSON(MTP_bytes(json)) )).done([=]( const MTPUpdates &updates, @@ -1586,6 +1640,7 @@ void GroupCall::applyMeInCallLocally() { : participant ? participant->raisedHandRating : FindLocalRaisedHandRating(real->participants()); + const auto publicKey = _e2eState ? _e2eState->myKey : TdE2E::PublicKey(); const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0)) | (lastActive ? Flag::f_active_date : Flag(0)) | (_joinState.ssrc ? Flag(0) : Flag::f_left) @@ -1594,7 +1649,8 @@ void GroupCall::applyMeInCallLocally() { | Flag::f_volume // Without flag the volume is reset to 100%. | Flag::f_volume_by_admin // Self volume can only be set by admin. | ((muted() != MuteState::Active) ? Flag::f_muted : Flag(0)) - | (raisedHandRating > 0 ? Flag::f_raise_hand_rating : Flag(0)); + | (raisedHandRating > 0 ? Flag::f_raise_hand_rating : Flag(0)) + | (_e2eState ? Flag::f_public_key : Flag(0)); real->applyLocalUpdate( MTP_updateGroupCallParticipants( inputCall(), @@ -1611,7 +1667,9 @@ void GroupCall::applyMeInCallLocally() { MTP_long(raisedHandRating), MTPGroupCallParticipantVideo(), MTPGroupCallParticipantVideo(), - AssertIsDebug() MTPint256())), // public_key + MTP_int256( + MTP_int128(publicKey.a, publicKey.b), + MTP_int128(publicKey.c, publicKey.d)))), MTP_int(0)).c_updateGroupCallParticipants()); } @@ -1642,7 +1700,11 @@ void GroupCall::applyParticipantLocally( | (participantPeer == joinAs() ? Flag::f_self : Flag(0)) | (participant->raisedHandRating ? Flag::f_raise_hand_rating - : Flag(0)); + : Flag(0)) + | (participant->e2eState ? Flag::f_public_key : Flag(0)); + const auto publicKey = participant->e2eState + ? participant->e2eState->key + : TdE2E::PublicKey(); _peer->groupCall()->applyLocalUpdate( MTP_updateGroupCallParticipants( inputCall(), @@ -1659,7 +1721,9 @@ void GroupCall::applyParticipantLocally( MTP_long(participant->raisedHandRating), MTPGroupCallParticipantVideo(), MTPGroupCallParticipantVideo(), - AssertIsDebug() MTPint256())), // public_key + MTP_int256( + MTP_int128(publicKey.a, publicKey.b), + MTP_int128(publicKey.c, publicKey.d)))), MTP_int(0)).c_updateGroupCallParticipants()); } diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.h b/Telegram/SourceFiles/calls/group/calls_group_call.h index 8f93793bd9..a3c8d07c12 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.h +++ b/Telegram/SourceFiles/calls/group/calls_group_call.h @@ -42,6 +42,11 @@ struct GroupCallParticipant; class GroupCall; } // namespace Data +namespace TdE2E { +struct ParticipantState; +struct CallState; +} // namespace TdE2E + namespace Calls { namespace Group { @@ -49,6 +54,7 @@ struct MuteRequest; struct VolumeRequest; struct ParticipantState; struct JoinInfo; +struct ConferenceInfo; struct RejoinEvent; struct RtmpInfo; enum class VideoQuality; @@ -164,6 +170,8 @@ struct ParticipantVideoParams; const tl::conditional &camera, const tl::conditional &screen, const std::shared_ptr &existing); +[[nodiscard]] std::shared_ptr ParseParticipantState( + const MTPDgroupCallParticipant &data); [[nodiscard]] const std::string &GetCameraEndpoint( const std::shared_ptr ¶ms); @@ -217,6 +225,9 @@ public: not_null delegate, Group::JoinInfo info, const MTPInputGroupCall &inputCall); + GroupCall( + not_null delegate, + Group::ConferenceInfo info); ~GroupCall(); [[nodiscard]] CallId id() const { @@ -469,6 +480,12 @@ private: return true; } + GroupCall( + not_null delegate, + Group::JoinInfo join, + Group::ConferenceInfo conference, + const MTPInputGroupCall &inputCall); + void broadcastPartStart(std::shared_ptr task); void broadcastPartCancel(not_null task); void mediaChannelDescriptionsStart( @@ -575,6 +592,7 @@ private: [[nodiscard]] MTPInputGroupCall inputCall() const; const not_null _delegate; + const std::shared_ptr _conferenceCall; not_null _peer; // Can change in legacy group migration. rpl::event_stream _peerStream; not_null _history; // Can change in legacy group migration. @@ -601,9 +619,13 @@ private: rpl::variable> _joinAs; std::vector> _possibleJoinAs; QString _joinHash; + QString _conferenceLinkSlug; + MsgId _conferenceJoinMessageId; int64 _serverTimeMs = 0; crl::time _serverTimeMsGotAt = 0; + std::unique_ptr _e2eState; + QString _rtmpUrl; QString _rtmpKey; diff --git a/Telegram/SourceFiles/calls/group/calls_group_common.cpp b/Telegram/SourceFiles/calls/group/calls_group_common.cpp index 75e6de992f..20223c9ebf 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_common.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_common.cpp @@ -52,7 +52,8 @@ object_ptr ScreenSharingPrivacyRequestBox() { void ConferenceCallJoinConfirm( not_null box, - std::shared_ptr call) { + std::shared_ptr call, + Fn join) { box->setTitle(tr::lng_confcall_join_title()); box->addRow( @@ -62,7 +63,11 @@ void ConferenceCallJoinConfirm( st::boxLabel)); box->addButton(tr::lng_confcall_join_button(), [=] { - + const auto weak = Ui::MakeWeak(box); + join(); + if (const auto strong = weak.data()) { + strong->closeBox(); + } }); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); diff --git a/Telegram/SourceFiles/calls/group/calls_group_common.h b/Telegram/SourceFiles/calls/group/calls_group_common.h index 114c43e6d5..32d403ed6f 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_common.h +++ b/Telegram/SourceFiles/calls/group/calls_group_common.h @@ -65,6 +65,12 @@ struct JoinInfo { bool rtmp = false; }; +struct ConferenceInfo { + std::shared_ptr call; + QString linkSlug; + MsgId joinMessageId; +}; + enum class PanelMode { Default, Wide, @@ -99,6 +105,7 @@ using StickedTooltips = base::flags; void ConferenceCallJoinConfirm( not_null box, - std::shared_ptr call); + std::shared_ptr call, + Fn join); } // namespace Calls::Group diff --git a/Telegram/SourceFiles/data/data_group_call.cpp b/Telegram/SourceFiles/data/data_group_call.cpp index c30eaeebaa..fc8f9fc260 100644 --- a/Telegram/SourceFiles/data/data_group_call.cpp +++ b/Telegram/SourceFiles/data/data_group_call.cpp @@ -622,15 +622,22 @@ void GroupCall::applyParticipantsSlice( const auto existingVideoParams = (i != end(_participants)) ? i->videoParams : nullptr; + const auto existingState = (i != end(_participants)) + ? i->e2eState + : nullptr; auto videoParams = localUpdate ? existingVideoParams : Calls::ParseVideoParams( data.vvideo(), data.vpresentation(), existingVideoParams); + auto e2eState = localUpdate + ? existingState + : Calls::ParseParticipantState(data); const auto value = Participant{ .peer = participantPeer, .videoParams = std::move(videoParams), + .e2eState = std::move(e2eState), .date = data.vdate().v, .lastActive = lastActive, .raisedHandRating = raisedHandRating, diff --git a/Telegram/SourceFiles/data/data_group_call.h b/Telegram/SourceFiles/data/data_group_call.h index 8b7d16158d..d283764b12 100644 --- a/Telegram/SourceFiles/data/data_group_call.h +++ b/Telegram/SourceFiles/data/data_group_call.h @@ -17,6 +17,10 @@ namespace Calls { struct ParticipantVideoParams; } // namespace Calls +namespace TdE2E { +struct ParticipantState; +} // namespace TdE2E + namespace Data { [[nodiscard]] const std::string &RtmpEndpointId(); @@ -29,6 +33,7 @@ struct LastSpokeTimes { struct GroupCallParticipant { not_null peer; std::shared_ptr videoParams; + std::shared_ptr e2eState; TimeId date = 0; TimeId lastActive = 0; uint64 raisedHandRating = 0; diff --git a/Telegram/SourceFiles/tde2e/tde2e_api.cpp b/Telegram/SourceFiles/tde2e/tde2e_api.cpp index 109536ed33..f5c8f12001 100644 --- a/Telegram/SourceFiles/tde2e/tde2e_api.cpp +++ b/Telegram/SourceFiles/tde2e/tde2e_api.cpp @@ -7,16 +7,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "tde2e/tde2e_api.h" -#include "base/algorithm.h" +#include "base/assertion.h" #include namespace TdE2E { -QByteArray GeneratePrivateKey() { - const auto result = tde2e_api::key_generate_temporary_private_key(); +CallState CreateCallState() { + const auto id = tde2e_api::key_generate_temporary_private_key(); + Assert(id.is_ok()); + const auto key = tde2e_api::key_to_public_key(id.value()); + Assert(key.is_ok()); - return {}; + auto result = CallState{ + .myKeyId = PrivateKeyId{ .v = uint64(id.value()) }, + }; + Assert(key.value().size() == sizeof(result.myKey)); + memcpy(&result.myKey, key.value().data(), 32); + + return result; } } // namespace TdE2E diff --git a/Telegram/SourceFiles/tde2e/tde2e_api.h b/Telegram/SourceFiles/tde2e/tde2e_api.h index 73e0063250..689a5aa86c 100644 --- a/Telegram/SourceFiles/tde2e/tde2e_api.h +++ b/Telegram/SourceFiles/tde2e/tde2e_api.h @@ -7,3 +7,31 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/basic_types.h" + +namespace TdE2E { + +struct PrivateKeyId { + uint64 v = 0; +}; + +struct PublicKey { + uint64 a = 0; + uint64 b = 0; + uint64 c = 0; + uint64 d = 0; +}; + +struct ParticipantState { + uint64 id = 0; + PublicKey key; +}; + +struct CallState { + PrivateKeyId myKeyId; + PublicKey myKey; +}; + +[[nodiscard]] CallState CreateCallState(); + +} // namespace TdE2E diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index c4167c2ca8..514f4dd859 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -853,7 +853,7 @@ void SessionNavigation::resolveConferenceCall(const QString &slug) { MTP_int(limit) )).done([=](const MTPphone_GroupCall &result) { _conferenceCallRequestId = 0; - _conferenceCallSlug = QString(); + const auto slug = base::take(_conferenceCallSlug); result.data().vcall().match([&](const auto &data) { const auto call = std::make_shared( @@ -863,8 +863,15 @@ void SessionNavigation::resolveConferenceCall(const QString &slug) { TimeId(), // scheduleDate false); // rtmp call->processFullCall(result); - uiShow()->show( - Box(Calls::Group::ConferenceCallJoinConfirm, call)); + const auto join = [=] { + Core::App().calls().startOrJoinConferenceCall( + uiShow(), + { .call = call, .linkSlug = slug }); + }; + uiShow()->show(Box( + Calls::Group::ConferenceCallJoinConfirm, + call, + join)); }); }).fail([=] { _conferenceCallRequestId = 0;