Page MenuHomePhorge

D295.1775619383.diff
No OneTemporary

Size
14 KB
Referenced Files
None
Subscribers
None

D295.1775619383.diff

diff --git a/src/base/kazv-triggers.hpp b/src/base/kazv-triggers.hpp
--- a/src/base/kazv-triggers.hpp
+++ b/src/base/kazv-triggers.hpp
@@ -75,6 +75,8 @@
Response response;
};
+ struct VerificationTrackerModelChanged {};
+
using KazvTrigger = std::variant<
// use this for placeholder of "no events yet"
// otherwise the first LoginSuccessful event cannot be detected
@@ -93,6 +95,8 @@
LoginSuccessful, LoginFailed,
// storage
SaveEventsRequested,
+ // encryption
+ VerificationTrackerModelChanged,
// general
UnrecognizedResponse
diff --git a/src/client/actions/encryption.hpp b/src/client/actions/encryption.hpp
--- a/src/client/actions/encryption.hpp
+++ b/src/client/actions/encryption.hpp
@@ -35,4 +35,6 @@
ClientResult updateClient(ClientModel m, PrepareForSharingRoomKeyAction a);
ClientResult updateClient(ClientModel m, ImportFromKeyBackupFileAction a);
+
+ ClientResult updateClient(ClientModel m, SetVerificationTrackerModelAction a);
}
diff --git a/src/client/actions/encryption.cpp b/src/client/actions/encryption.cpp
--- a/src/client/actions/encryption.cpp
+++ b/src/client/actions/encryption.cpp
@@ -709,4 +709,10 @@
},
}}};
};
+
+ ClientResult updateClient(ClientModel m, [[maybe_unused]] NotifyVerificationTrackerModelAction a)
+ {
+ m.addTrigger(VerificationTrackerModelChanged{});
+ return {std::move(m), lager::noop};
+ }
}
diff --git a/src/client/actions/sync.cpp b/src/client/actions/sync.cpp
--- a/src/client/actions/sync.cpp
+++ b/src/client/actions/sync.cpp
@@ -21,6 +21,7 @@
#include "encryption.hpp"
#include "status-utils.hpp"
+#include "verification-tracker.hpp"
namespace Kazv
{
@@ -241,6 +242,26 @@
return {};
}
+ [[nodiscard]] static json popVerificationEvents(ClientModel &m)
+ {
+ auto oldToDevice = std::move(m.toDevice);
+ auto toDeviceVerificationEvents = intoImmer(
+ EventList{},
+ zug::filter([](const auto &e) {
+ return VerificationTracker::isVerificationEvent(e);
+ }),
+ oldToDevice);
+ m.toDevice = intoImmer(
+ EventList{},
+ zug::filter([](const auto &e) {
+ return !VerificationTracker::isVerificationEvent(e);
+ }),
+ std::move(oldToDevice));
+ return json::object({
+ {"toDevice", toDeviceVerificationEvents}
+ });
+ }
+
ClientResult processResponse(ClientModel m, SyncResponse r)
{
if (! r.success()) {
@@ -284,6 +305,9 @@
auto is = r.dataStr("is");
auto isInitialSync = is == "initial";
+ json ve = json::object({
+ {"toDevice", json::array()},
+ });
if (m.crypto) {
kzo.client.dbg() << "E2EE is on. Processing device lists and one-time key counts." << std::endl;
@@ -318,9 +342,14 @@
auto model = tryDecryptEvents(std::move(m));
m = std::move(model);
+ ve = popVerificationEvents(m);
}
- return { std::move(m), lager::noop };
+ return { std::move(m), [ve=std::move(ve)](auto &&) {
+ return EffectStatus(true, json{
+ {"verificationEvents", ve},
+ });
+ } };
}
ClientResult updateClient(ClientModel m, SetShouldSyncAction a)
diff --git a/src/client/client-model.hpp b/src/client/client-model.hpp
--- a/src/client/client-model.hpp
+++ b/src/client/client-model.hpp
@@ -22,7 +22,7 @@
#include <file-desc.hpp>
#include <crypto.hpp>
-
+#include <verification-tracker.hpp>
#include <serialization/immer-flex-vector.hpp>
#include <serialization/immer-box.hpp>
#include <serialization/immer-map.hpp>
@@ -592,6 +592,11 @@
std::string password;
};
+ /**
+ * Notify that the verification tracker model has been changed.
+ */
+ struct NotifyVerificationTrackerModelAction {};
+
struct GetUserProfileAction
{
std::string userId;
diff --git a/src/client/client.hpp b/src/client/client.hpp
--- a/src/client/client.hpp
+++ b/src/client/client.hpp
@@ -21,6 +21,7 @@
#include "room/room.hpp"
#include "notification-handler.hpp"
+#include <verification-tracker.hpp>
namespace Kazv
{
@@ -43,6 +44,28 @@
* If the Client is constructed from a cursor, copy-constructing another
* Client is safe only from the same thread as this Client.
*
+ * ## Device verification integration
+ *
+ * The `startSyncing()` function will automatically feed verification events
+ * received from sync into the VerificationTracker, and send outbound events
+ * according to the result returned from the VerificationTracker.
+ *
+ * The verification processing functions in this class (readyForVerification(),
+ * cancelVerification(), confirmVerificationSasMatch(),
+ * denyVerificationSasMatch()) will also automatically send outbound
+ * events according to the result returned from the VerificationTracker.
+ *
+ * Additionally, this class will cause the VerificationTrackerModelChanged
+ * trigger to be emitted when appropriate.
+ * If you use a `lager::sensor` to observe the `VerificationTracker::model`,
+ * you should call `lager::commit()` on the `lager::sensor` **in the event loop
+ * thread** after you received VerificationTrackerModelChanged.
+ *
+ * You must always access VerificationTracker::model from the event loop thread,
+ * because the modifications to VerificationTracker always happen in the event
+ * loop thread. However, once you have a copy of the VerificationTrackerModel,
+ * you are free to copy it and pass it onto other threads.
+ *
* ## Error handling
*
* A lot of functions in Client and Room are asynchronous actions.
@@ -61,7 +84,7 @@
public:
using ActionT = ClientAction;
- using DepsT = lager::deps<JobInterface &, EventInterface &, SdkModelCursorKey, RandomInterface &
+ using DepsT = lager::deps<JobInterface &, EventInterface &, SdkModelCursorKey, RandomInterface &, VerificationTracker &
#ifdef KAZV_USE_THREAD_SAFETY_HELPER
, EventLoopThreadIdKeeper &
#endif
@@ -657,6 +680,64 @@
*/
PromiseT importFromKeyBackupFile(std::string fileContent, std::string password) const;
+ /**
+ * Process verification events from a sync result.
+ *
+ * This will be called automatically after a sync.
+ *
+ * @param toDeviceEvents The list of to-device verification events received from sync.
+ * @return A Promise that resolves when the processing is done. After it resolves,
+ * actions will be dispatched to send any pending to-device events in the
+ * VerificationTracker's model.
+ */
+ PromiseT processVerificationEventsFromSync(EventList toDeviceEvents) const;
+
+ /**
+ * Request an outgoing verification using to-device message.
+ *
+ * This will automatically fetch the device keys if we do not yet have
+ * them.
+ *
+ * @return A Promise that resolves when the outgoing request is sent,
+ * or when there is an error.
+ */
+ PromiseT requestOutgoingToDeviceVerification(std::string userId, std::string deviceId) const;
+
+ /**
+ * Signal that the user is ready for an incoming verification request.
+ *
+ * This will automatically fetch the device keys if we do not yet have
+ * them.
+ *
+ * @return A Promise that resolves when the ready event is sent,
+ * or when there is an error.
+ */
+ PromiseT readyForVerification(std::string userId, std::string deviceId) const;
+
+ /**
+ * Cancel a verification process.
+ *
+ * @return A Promise that resolves when the cancel event is sent,
+ * or when there is an error.
+ */
+ PromiseT cancelVerification(std::string userId, std::string deviceId) const;
+
+ /**
+ * Confirm an sas match for a verification process.
+ *
+ * @return A Promise that resolves when the next event is sent,
+ * or when there is an error.
+ */
+ PromiseT confirmVerificationSasMatch(std::string userId, std::string deviceId);
+
+ /**
+ * Deny an sas match for a verification process.
+ *
+ * @return A Promise that resolves when the next event is sent,
+ * or when there is an error.
+ */
+ PromiseT denyVerificationSasMatch(std::string userId, std::string deviceId);
+
private:
void syncForever(std::optional<int> retryTime = std::nullopt) const;
diff --git a/src/client/client.cpp b/src/client/client.cpp
--- a/src/client/client.cpp
+++ b/src/client/client.cpp
@@ -8,7 +8,7 @@
#include <filesystem>
#include <algorithm>
-
+#include <chrono>
#include <lager/constant.hpp>
#include "client.hpp"
@@ -309,6 +309,36 @@
return p1;
}
+ auto Client::processVerificationEventsFromSync(EventList toDeviceEvents) const -> PromiseT
+ {
+ if (!m_deps) {
+ return m_ctx.createResolvedPromise(false);
+ }
+ bool hasCrypto{clientCursor().map([](const auto &c) {
+ return c.crypto.has_value();
+ }).make().get()};
+ if (!hasCrypto) {
+ return m_ctx.createResolvedPromise(false);
+ }
+ return m_ctx.createResolvedPromise(true)
+ .then([that=toEventLoop(), ves=toDeviceEvents](auto &&) {
+ auto &vt = lager::get<VerificationTracker>(that.m_deps.value());
+ auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::system_clock::now().time_since_epoch()
+ ).count();
+ auto es = vt.processIncoming(now, ves);
+ for (auto ed : es) {
+ // We don't actually need to wait for these events to
+ // be sent before we proceed into next sync cycle.
+ that.m_ctx.dispatch(SendToDeviceMessageAction{
+ ed.event,
+ {{ed.toUserId, {ed.toDeviceId}}}
+ });
+ }
+ return that.m_ctx.dispatch(NotifyVerificationTrackerModelAction{});
+ });
+ }
+
auto Client::syncForever(std::optional<int> retryTime) const -> void
{
KAZV_VERIFY_THREAD_ID();
@@ -355,8 +385,21 @@
: that.m_ctx.createResolvedPromise(true);
});
+ auto processVerificationEventsRes = syncRes
+ .then([that=toEventLoop()](EffectStatus stat) {
+ if (!stat.success() || !that.m_deps ||
+ !that.clientCursor().map([](const auto &c) {
+ return c.crypto.has_value();
+ }).make().get()) {
+ return that.m_ctx.createResolvedPromise(stat);
+ }
+ kzo.client.dbg() << "processVerificationEvents: " << stat.data().get().dump() << std::endl;
+ EventList ves = stat.data().get().at("verificationEvents").at("toDevice").template get<EventList>();
+ return that.processVerificationEventsFromSync(ves);
+ });
+
m_ctx.promiseInterface()
- .all(std::vector<PromiseT>{uploadOneTimeKeysRes, queryKeysRes})
+ .all(std::vector<PromiseT>{uploadOneTimeKeysRes, queryKeysRes, processVerificationEventsRes})
.then([that=toEventLoop(), retryTime](auto stat) {
if (stat.success()) {
that.syncForever(); // reset retry time
diff --git a/src/client/clientfwd.hpp b/src/client/clientfwd.hpp
--- a/src/client/clientfwd.hpp
+++ b/src/client/clientfwd.hpp
@@ -72,6 +72,7 @@
struct SetTrustLevelNeededToSendKeysAction;
struct PrepareForSharingRoomKeyAction;
struct ImportFromKeyBackupFileAction;
+ struct NotifyVerificationTrackerModelAction;
struct GetUserProfileAction;
struct SetAvatarUrlAction;
@@ -142,6 +143,7 @@
SetTrustLevelNeededToSendKeysAction,
PrepareForSharingRoomKeyAction,
ImportFromKeyBackupFileAction,
+ NotifyVerificationTrackerModelAction,
GetUserProfileAction,
SetAvatarUrlAction,
diff --git a/src/client/sdk.hpp b/src/client/sdk.hpp
--- a/src/client/sdk.hpp
+++ b/src/client/sdk.hpp
@@ -16,7 +16,7 @@
#include "sdk-model-cursor-tag.hpp"
#include "client.hpp"
#include "thread-safety-helper.hpp"
-
+#include "verification-tracker.hpp"
#include "random-generator.hpp"
@@ -43,7 +43,8 @@
std::ref(detail::declref<JobInterface>()),
std::ref(detail::declref<EventInterface>()),
lager::dep::as<SdkModelCursorKey>(std::declval<std::function<CursorTSP()>>()),
- std::ref(detail::declref<RandomInterface>())
+ std::ref(detail::declref<RandomInterface>()),
+ std::ref(detail::declref<VerificationTracker>())
#ifdef KAZV_USE_THREAD_SAFETY_HELPER
, std::ref(detail::declref<EventLoopThreadIdKeeper>())
#endif
@@ -51,7 +52,7 @@
std::declval<Enhancers>()...)
);
- using DepsT = lager::deps<JobInterface &, EventInterface &, SdkModelCursorKey, RandomInterface &
+ using DepsT = lager::deps<JobInterface &, EventInterface &, SdkModelCursorKey, RandomInterface &, VerificationTracker &
#ifdef KAZV_USE_THREAD_SAFETY_HELPER
, EventLoopThreadIdKeeper &
#endif
@@ -143,6 +144,7 @@
Xform &&xform,
Enhancers &&...enhancers)
: rg(RandomInterface{RandomDeviceGenerator{}})
+ , vt(VerificationUtils::DeviceIdentity())
, store(makeStore<ActionT>(
std::move(model),
&ModelT::update,
@@ -152,7 +154,8 @@
std::ref(eventEmitter),
lager::dep::as<SdkModelCursorKey>(
std::function<CursorTSP()>([this] { return sdk; })),
- std::ref(rg.value())
+ std::ref(rg.value()),
+ std::ref(vt)
#ifdef KAZV_USE_THREAD_SAFETY_HELPER
, std::ref(keeper)
#endif
@@ -171,6 +174,7 @@
EventLoopThreadIdKeeper keeper;
#endif
std::optional<RandomInterface> rg;
+ VerificationTracker vt;
StoreT store;
CursorTSP sdk;
};

File Metadata

Mime Type
text/plain
Expires
Tue, Apr 7, 8:36 PM (13 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1296730
Default Alt Text
D295.1775619383.diff (14 KB)

Event Timeline