mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +03:00
Add TOFU (Trust On First Use) mode to encryption
This commit is contained in:
parent
0d0709ccd3
commit
2df4c532ed
2 changed files with 74 additions and 21 deletions
|
@ -3335,7 +3335,9 @@ void
|
|||
to_json(json &j, const UserKeyCache &info)
|
||||
{
|
||||
j["device_keys"] = info.device_keys;
|
||||
j["seen_device_keys"] = info.seen_device_keys;
|
||||
j["master_keys"] = info.master_keys;
|
||||
j["master_key_changed"] = info.master_key_changed;
|
||||
j["user_signing_keys"] = info.user_signing_keys;
|
||||
j["self_signing_keys"] = info.self_signing_keys;
|
||||
j["updated_at"] = info.updated_at;
|
||||
|
@ -3346,7 +3348,9 @@ void
|
|||
from_json(const json &j, UserKeyCache &info)
|
||||
{
|
||||
info.device_keys = j.value("device_keys", std::map<std::string, mtx::crypto::DeviceKeys>{});
|
||||
info.seen_device_keys = j.value("seen_device_keys", std::set<std::string>{});
|
||||
info.master_keys = j.value("master_keys", mtx::crypto::CrossSigningKeys{});
|
||||
info.master_key_changed = j.value("master_key_changed", false);
|
||||
info.user_signing_keys = j.value("user_signing_keys", mtx::crypto::CrossSigningKeys{});
|
||||
info.self_signing_keys = j.value("self_signing_keys", mtx::crypto::CrossSigningKeys{});
|
||||
info.updated_at = j.value("updated_at", "");
|
||||
|
@ -3393,17 +3397,57 @@ Cache::updateUserKeys(const std::string &sync_token, const mtx::responses::Query
|
|||
for (auto &[user, update] : updates) {
|
||||
nhlog::db()->debug("Updated user keys: {}", user);
|
||||
|
||||
auto updateToWrite = update;
|
||||
|
||||
std::string_view oldKeys;
|
||||
auto res = db.get(txn, user, oldKeys);
|
||||
|
||||
if (res) {
|
||||
auto last_changed = json::parse(oldKeys).get<UserKeyCache>().last_changed;
|
||||
updateToWrite = json::parse(oldKeys).get<UserKeyCache>();
|
||||
auto last_changed = updateToWrite.last_changed;
|
||||
// skip if we are tracking this and expect it to be up to date with the last
|
||||
// sync token
|
||||
if (!last_changed.empty() && last_changed != sync_token)
|
||||
continue;
|
||||
|
||||
if (!updateToWrite.master_keys.keys.empty() &&
|
||||
update.master_keys.keys != updateToWrite.master_keys.keys) {
|
||||
updateToWrite.master_key_changed = true;
|
||||
}
|
||||
db.put(txn, user, json(update).dump());
|
||||
|
||||
updateToWrite.master_keys = update.master_keys;
|
||||
updateToWrite.self_signing_keys = update.self_signing_keys;
|
||||
updateToWrite.user_signing_keys = update.user_signing_keys;
|
||||
|
||||
// If we have keys for the device already, only update the signatures.
|
||||
for (const auto &[device_id, device_keys] : update.device_keys) {
|
||||
if (updateToWrite.device_keys.count(device_id) &&
|
||||
updateToWrite.device_keys.at(device_id).keys ==
|
||||
device_keys.keys) {
|
||||
updateToWrite.device_keys.at(device_id).signatures =
|
||||
device_keys.signatures;
|
||||
} else {
|
||||
bool keyReused = false;
|
||||
for (const auto &[key_id, key] : device_keys.keys) {
|
||||
(void)key_id;
|
||||
if (updateToWrite.seen_device_keys.count(key)) {
|
||||
keyReused = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!updateToWrite.device_keys.count(device_id) &&
|
||||
!keyReused)
|
||||
updateToWrite.device_keys[device_id] = device_keys;
|
||||
}
|
||||
|
||||
for (const auto &[key_id, key] : device_keys.keys) {
|
||||
(void)key_id;
|
||||
updateToWrite.seen_device_keys.insert(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
db.put(txn, user, json(updateToWrite).dump());
|
||||
}
|
||||
|
||||
txn.commit();
|
||||
|
@ -3715,19 +3759,23 @@ Cache::verificationStatus(const std::string &user_id)
|
|||
auto master_keys = ourKeys->master_keys.keys;
|
||||
|
||||
if (user_id != local_user) {
|
||||
if (!verifyAtLeastOneSig(
|
||||
ourKeys->user_signing_keys, master_keys, local_user))
|
||||
return status;
|
||||
bool theirMasterKeyVerified =
|
||||
verifyAtLeastOneSig(
|
||||
ourKeys->user_signing_keys, master_keys, local_user) &&
|
||||
verifyAtLeastOneSig(
|
||||
theirKeys->master_keys, ourKeys->user_signing_keys.keys, local_user);
|
||||
|
||||
if (!verifyAtLeastOneSig(
|
||||
theirKeys->master_keys, ourKeys->user_signing_keys.keys, local_user))
|
||||
if (theirMasterKeyVerified)
|
||||
trustlevel = crypto::Trust::Verified;
|
||||
else if (!theirKeys->master_key_changed)
|
||||
trustlevel = crypto::Trust::TOFU;
|
||||
else
|
||||
return status;
|
||||
|
||||
master_keys = theirKeys->master_keys.keys;
|
||||
}
|
||||
|
||||
trustlevel = crypto::Trust::Verified;
|
||||
status.user_verified = crypto::Trust::Verified;
|
||||
status.user_verified = trustlevel;
|
||||
|
||||
if (!verifyAtLeastOneSig(theirKeys->self_signing_keys, master_keys, user_id))
|
||||
return status;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#include <mtx/events/encrypted.hpp>
|
||||
#include <mtx/responses/crypto.hpp>
|
||||
|
@ -123,12 +124,16 @@ struct UserKeyCache
|
|||
{
|
||||
//! Device id to device keys
|
||||
std::map<std::string, mtx::crypto::DeviceKeys> device_keys;
|
||||
//! corss signing keys
|
||||
//! cross signing keys
|
||||
mtx::crypto::CrossSigningKeys master_keys, user_signing_keys, self_signing_keys;
|
||||
//! Sync token when nheko last fetched the keys
|
||||
std::string updated_at;
|
||||
//! Sync token when the keys last changed. updated != last_changed means they are outdated.
|
||||
std::string last_changed;
|
||||
//! if the master key has ever changed
|
||||
bool master_key_changed = false;
|
||||
//! Device keys that were already used at least once
|
||||
std::set<std::string> seen_device_keys;
|
||||
};
|
||||
|
||||
void
|
||||
|
|
Loading…
Reference in a new issue