Parse e2e participant keys.

This commit is contained in:
John Preston 2025-03-25 00:11:21 +05:00
parent 214cc83d4a
commit 5e6c81a98e
11 changed files with 240 additions and 29 deletions

View file

@ -230,6 +230,30 @@ void Instance::startOrJoinGroupCall(
});
}
void Instance::startOrJoinConferenceCall(
std::shared_ptr<Ui::Show> show,
StartConferenceCallArgs args) {
destroyCurrentCall();
auto call = std::make_unique<GroupCall>(
_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<Group::Panel>(raw);
_currentGroupCall = std::move(call);
_currentGroupCallChanges.fire_copy(raw);
}
void Instance::confirmLeaveCurrent(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
@ -409,6 +433,24 @@ void Instance::createGroupCall(
_currentGroupCallChanges.fire_copy(raw);
}
void Instance::createConferenceCall(Group::ConferenceInfo info) {
destroyCurrentCall();
auto call = std::make_unique<GroupCall>(
_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<Group::Panel>(raw);
_currentGroupCall = std::move(call);
_currentGroupCallChanges.fire_copy(raw);
}
void Instance::refreshDhConfig() {
Expects(_currentCall != nullptr);

View file

@ -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<Data::GroupCall> call;
QString linkSlug;
MsgId joinMessageId;
};
class Instance final : public base::has_weak_ptr {
public:
Instance();
@ -69,6 +80,9 @@ public:
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
StartGroupCallArgs args);
void startOrJoinConferenceCall(
std::shared_ptr<Ui::Show> show,
StartConferenceCallArgs args);
void showStartWithRtmp(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer);
@ -121,6 +135,7 @@ private:
void createGroupCall(
Group::JoinInfo info,
const MTPInputGroupCall &inputCall);
void createConferenceCall(Group::ConferenceInfo info);
void destroyGroupCall(not_null<GroupCall*> call);
void confirmLeaveCurrent(
std::shared_ptr<Ui::Show> show,

View file

@ -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<ParticipantVideoParams> ParseVideoParams(
return data;
}
std::shared_ptr<TdE2E::ParticipantState> 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>(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<GroupCall> call,
int64 time,
@ -569,18 +585,38 @@ GroupCall::GroupCall(
not_null<Delegate*> delegate,
Group::JoinInfo info,
const MTPInputGroupCall &inputCall)
: GroupCall(delegate, info, {}, inputCall) {
}
GroupCall::GroupCall(
not_null<Delegate*> 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*> 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::CallState>(
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<PeerData*> 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());
}

View file

@ -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<MTPGroupCallParticipantVideo> &camera,
const tl::conditional<MTPGroupCallParticipantVideo> &screen,
const std::shared_ptr<ParticipantVideoParams> &existing);
[[nodiscard]] std::shared_ptr<TdE2E::ParticipantState> ParseParticipantState(
const MTPDgroupCallParticipant &data);
[[nodiscard]] const std::string &GetCameraEndpoint(
const std::shared_ptr<ParticipantVideoParams> &params);
@ -217,6 +225,9 @@ public:
not_null<Delegate*> delegate,
Group::JoinInfo info,
const MTPInputGroupCall &inputCall);
GroupCall(
not_null<Delegate*> delegate,
Group::ConferenceInfo info);
~GroupCall();
[[nodiscard]] CallId id() const {
@ -469,6 +480,12 @@ private:
return true;
}
GroupCall(
not_null<Delegate*> delegate,
Group::JoinInfo join,
Group::ConferenceInfo conference,
const MTPInputGroupCall &inputCall);
void broadcastPartStart(std::shared_ptr<LoadPartTask> task);
void broadcastPartCancel(not_null<LoadPartTask*> task);
void mediaChannelDescriptionsStart(
@ -575,6 +592,7 @@ private:
[[nodiscard]] MTPInputGroupCall inputCall() const;
const not_null<Delegate*> _delegate;
const std::shared_ptr<Data::GroupCall> _conferenceCall;
not_null<PeerData*> _peer; // Can change in legacy group migration.
rpl::event_stream<PeerData*> _peerStream;
not_null<History*> _history; // Can change in legacy group migration.
@ -601,9 +619,13 @@ private:
rpl::variable<not_null<PeerData*>> _joinAs;
std::vector<not_null<PeerData*>> _possibleJoinAs;
QString _joinHash;
QString _conferenceLinkSlug;
MsgId _conferenceJoinMessageId;
int64 _serverTimeMs = 0;
crl::time _serverTimeMsGotAt = 0;
std::unique_ptr<TdE2E::CallState> _e2eState;
QString _rtmpUrl;
QString _rtmpKey;

View file

@ -52,7 +52,8 @@ object_ptr<Ui::GenericBox> ScreenSharingPrivacyRequestBox() {
void ConferenceCallJoinConfirm(
not_null<Ui::GenericBox*> box,
std::shared_ptr<Data::GroupCall> call) {
std::shared_ptr<Data::GroupCall> call,
Fn<void()> 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();

View file

@ -65,6 +65,12 @@ struct JoinInfo {
bool rtmp = false;
};
struct ConferenceInfo {
std::shared_ptr<Data::GroupCall> call;
QString linkSlug;
MsgId joinMessageId;
};
enum class PanelMode {
Default,
Wide,
@ -99,6 +105,7 @@ using StickedTooltips = base::flags<StickedTooltip>;
void ConferenceCallJoinConfirm(
not_null<Ui::GenericBox*> box,
std::shared_ptr<Data::GroupCall> call);
std::shared_ptr<Data::GroupCall> call,
Fn<void()> join);
} // namespace Calls::Group

View file

@ -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,

View file

@ -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<PeerData*> peer;
std::shared_ptr<Calls::ParticipantVideoParams> videoParams;
std::shared_ptr<TdE2E::ParticipantState> e2eState;
TimeId date = 0;
TimeId lastActive = 0;
uint64 raisedHandRating = 0;

View file

@ -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 <tde2e/td/e2e/e2e_api.h>
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

View file

@ -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

View file

@ -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<Data::GroupCall>(
@ -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;