Add TOFU (Trust On First Use) mode to encryption

This commit is contained in:
Nicolas Werner 2021-05-07 17:01:03 +02:00
parent 0d0709ccd3
commit 2df4c532ed
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
2 changed files with 74 additions and 21 deletions

View file

@ -3334,23 +3334,27 @@ Cache::statusMessage(const std::string &user_id)
void
to_json(json &j, const UserKeyCache &info)
{
j["device_keys"] = info.device_keys;
j["master_keys"] = info.master_keys;
j["user_signing_keys"] = info.user_signing_keys;
j["self_signing_keys"] = info.self_signing_keys;
j["updated_at"] = info.updated_at;
j["last_changed"] = info.last_changed;
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;
j["last_changed"] = info.last_changed;
}
void
from_json(const json &j, UserKeyCache &info)
{
info.device_keys = j.value("device_keys", std::map<std::string, mtx::crypto::DeviceKeys>{});
info.master_keys = j.value("master_keys", mtx::crypto::CrossSigningKeys{});
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", "");
info.last_changed = j.value("last_changed", "");
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", "");
info.last_changed = j.value("last_changed", "");
}
std::optional<UserKeyCache>
@ -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;
}
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(update).dump());
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;

View file

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