mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-24 12:08:50 +03:00
Upgrade trust of megolm sessions when receiving RoomKey
Before we only did that, when we basically didn't have the key yet. But since we usually get sent a RoomKey when a new message is sent after we sign in, we were discarding, that those messages should usually now be trusted.
This commit is contained in:
parent
5ddc11d9b4
commit
ff82452816
6 changed files with 72 additions and 29 deletions
|
@ -21,6 +21,7 @@ Image {
|
||||||
case Crypto.TOFU:
|
case Crypto.TOFU:
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled.svg?";
|
return "image://colorimage/:/icons/icons/ui/shield-filled.svg?";
|
||||||
case Crypto.Unverified:
|
case Crypto.Unverified:
|
||||||
|
case Crypto.MessageUnverified:
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled-exclamation-mark.svg?";
|
return "image://colorimage/:/icons/icons/ui/shield-filled-exclamation-mark.svg?";
|
||||||
default:
|
default:
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?";
|
return "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?";
|
||||||
|
@ -39,8 +40,10 @@ Image {
|
||||||
return qsTr("Encrypted by a verified device");
|
return qsTr("Encrypted by a verified device");
|
||||||
case Crypto.TOFU:
|
case Crypto.TOFU:
|
||||||
return qsTr("Encrypted by an unverified device, but you have trusted that user so far.");
|
return qsTr("Encrypted by an unverified device, but you have trusted that user so far.");
|
||||||
|
case Crypto.MessageUnverified:
|
||||||
|
return qsTr("Key is from an untrusted source like forwarded from another user or the online key backup. For this reason we can't verify who sent the message.");
|
||||||
default:
|
default:
|
||||||
return qsTr("Encrypted by an unverified device or the key is from an untrusted source like the key backup.");
|
return qsTr("Encrypted by an unverified device.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToolTip.visible: stateImg.hovered
|
ToolTip.visible: stateImg.hovered
|
||||||
|
|
|
@ -924,9 +924,29 @@ Cache::saveInboundMegolmSession(const MegolmSessionIndex &index,
|
||||||
std::string_view value;
|
std::string_view value;
|
||||||
if (inboundMegolmSessionDb_.get(txn, key, value)) {
|
if (inboundMegolmSessionDb_.get(txn, key, value)) {
|
||||||
auto oldSession = unpickle<InboundSessionObject>(std::string(value), pickle_secret_);
|
auto oldSession = unpickle<InboundSessionObject>(std::string(value), pickle_secret_);
|
||||||
if (olm_inbound_group_session_first_known_index(session.get()) >
|
|
||||||
olm_inbound_group_session_first_known_index(oldSession.get())) {
|
auto newIndex = olm_inbound_group_session_first_known_index(session.get());
|
||||||
nhlog::crypto()->warn("Not storing inbound session with newer first known index");
|
auto oldIndex = olm_inbound_group_session_first_known_index(oldSession.get());
|
||||||
|
|
||||||
|
// merge trusted > untrusted
|
||||||
|
// first known index minimum
|
||||||
|
if (megolmSessionDataDb_.get(txn, key, value)) {
|
||||||
|
auto oldData = nlohmann::json::parse(value).get<GroupSessionData>();
|
||||||
|
if (oldData.trusted && newIndex >= oldIndex) {
|
||||||
|
nhlog::crypto()->warn(
|
||||||
|
"Not storing inbound session of lesser trust or bigger index.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
oldData.trusted = data.trusted || oldData.trusted;
|
||||||
|
|
||||||
|
if (newIndex < oldIndex) {
|
||||||
|
inboundMegolmSessionDb_.put(txn, key, pickled);
|
||||||
|
oldData.message_index = newIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
megolmSessionDataDb_.put(txn, key, nlohmann::json(oldData).dump());
|
||||||
|
txn.commit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,12 @@ QML_NAMED_ELEMENT(Crypto)
|
||||||
//! How much a participant is trusted.
|
//! How much a participant is trusted.
|
||||||
enum Trust
|
enum Trust
|
||||||
{
|
{
|
||||||
Unverified, //! Device unverified or master key changed.
|
Unverified, //! Device unverified or master key changed.
|
||||||
TOFU, //! Device is signed by the sender, but the user is not verified, but they never
|
MessageUnverified, //! Only for messages. The sender might be trusted, but we don't know, who
|
||||||
//! changed the master key.
|
//! was the sender for the message.
|
||||||
Verified, //! User was verified and has crosssigned this device or device is verified.
|
TOFU, //! Device is signed by the sender, but the user is not verified, but they never
|
||||||
|
//! changed the master key.
|
||||||
|
Verified, //! User was verified and has crosssigned this device or device is verified.
|
||||||
};
|
};
|
||||||
Q_ENUM_NS(Trust)
|
Q_ENUM_NS(Trust)
|
||||||
}
|
}
|
||||||
|
@ -50,10 +52,9 @@ struct GroupSessionData
|
||||||
uint64_t timestamp = 0;
|
uint64_t timestamp = 0;
|
||||||
uint32_t message_index = 0;
|
uint32_t message_index = 0;
|
||||||
|
|
||||||
// If we got the session via key sharing or forwarding, we can usually trust it.
|
// We generally don't trust keys unless they were sent to us by the original sender and include
|
||||||
// If it came from asymmetric key backup, it is not trusted.
|
// that senders signature.
|
||||||
// TODO(Nico): What about forwards? They might come from key backup?
|
bool trusted = false;
|
||||||
bool trusted = true;
|
|
||||||
|
|
||||||
// the original 25519 key
|
// the original 25519 key
|
||||||
std::string sender_key;
|
std::string sender_key;
|
||||||
|
|
|
@ -629,6 +629,7 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
|
||||||
// Saving the new megolm session.
|
// Saving the new megolm session.
|
||||||
GroupSessionData session_data{};
|
GroupSessionData session_data{};
|
||||||
session_data.message_index = 0;
|
session_data.message_index = 0;
|
||||||
|
session_data.trusted = true;
|
||||||
session_data.timestamp = QDateTime::currentMSecsSinceEpoch();
|
session_data.timestamp = QDateTime::currentMSecsSinceEpoch();
|
||||||
session_data.sender_claimed_ed25519_key = olm::client()->identity_keys().ed25519;
|
session_data.sender_claimed_ed25519_key = olm::client()->identity_keys().ed25519;
|
||||||
session_data.sender_key = olm::client()->identity_keys().curve25519;
|
session_data.sender_key = olm::client()->identity_keys().curve25519;
|
||||||
|
@ -753,13 +754,16 @@ create_inbound_megolm_session(const mtx::events::DeviceEvent<mtx::events::msg::R
|
||||||
index.session_id = roomKey.content.session_id;
|
index.session_id = roomKey.content.session_id;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
auto megolm_session =
|
||||||
|
olm::client()->init_inbound_group_session(roomKey.content.session_key);
|
||||||
|
|
||||||
GroupSessionData data{};
|
GroupSessionData data{};
|
||||||
data.forwarding_curve25519_key_chain = {sender_key};
|
data.forwarding_curve25519_key_chain = {sender_key};
|
||||||
data.sender_claimed_ed25519_key = sender_ed25519;
|
data.sender_claimed_ed25519_key = sender_ed25519;
|
||||||
data.sender_key = sender_key;
|
data.sender_key = sender_key;
|
||||||
|
|
||||||
auto megolm_session =
|
data.trusted = olm_inbound_group_session_is_verified(megolm_session.get());
|
||||||
olm::client()->init_inbound_group_session(roomKey.content.session_key);
|
|
||||||
backup_session_key(index, data, megolm_session);
|
backup_session_key(index, data, megolm_session);
|
||||||
cache::saveInboundMegolmSession(index, std::move(megolm_session), data);
|
cache::saveInboundMegolmSession(index, std::move(megolm_session), data);
|
||||||
} catch (const lmdb::error &e) {
|
} catch (const lmdb::error &e) {
|
||||||
|
@ -792,14 +796,9 @@ import_inbound_megolm_session(
|
||||||
data.forwarding_curve25519_key_chain = roomKey.content.forwarding_curve25519_key_chain;
|
data.forwarding_curve25519_key_chain = roomKey.content.forwarding_curve25519_key_chain;
|
||||||
data.sender_claimed_ed25519_key = roomKey.content.sender_claimed_ed25519_key;
|
data.sender_claimed_ed25519_key = roomKey.content.sender_claimed_ed25519_key;
|
||||||
data.sender_key = roomKey.content.sender_key;
|
data.sender_key = roomKey.content.sender_key;
|
||||||
// may have come from online key backup, so we can't trust it...
|
// Keys from online key backup won't have a signature, so they will be untrusted. But the
|
||||||
data.trusted = false;
|
// original sender might send us a signed session.
|
||||||
// if we got it forwarded from the sender, assume it is trusted. They may still have
|
data.trusted = olm_inbound_group_session_is_verified(megolm_session.get());
|
||||||
// used key backup, but it is unlikely.
|
|
||||||
if (roomKey.content.forwarding_curve25519_key_chain.size() == 1 &&
|
|
||||||
roomKey.content.forwarding_curve25519_key_chain.back() == roomKey.content.sender_key) {
|
|
||||||
data.trusted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
backup_session_key(index, data, megolm_session);
|
backup_session_key(index, data, megolm_session);
|
||||||
cache::saveInboundMegolmSession(index, std::move(megolm_session), data);
|
cache::saveInboundMegolmSession(index, std::move(megolm_session), data);
|
||||||
|
@ -1023,6 +1022,7 @@ lookup_keybackup(const std::string &room, const std::string &session_id)
|
||||||
data.forwarding_curve25519_key_chain = session.forwarding_curve25519_key_chain;
|
data.forwarding_curve25519_key_chain = session.forwarding_curve25519_key_chain;
|
||||||
data.sender_claimed_ed25519_key = session.sender_claimed_keys["ed25519"];
|
data.sender_claimed_ed25519_key = session.sender_claimed_keys["ed25519"];
|
||||||
data.sender_key = session.sender_key;
|
data.sender_key = session.sender_key;
|
||||||
|
|
||||||
// online key backup can't be trusted, because anyone can upload to it.
|
// online key backup can't be trusted, because anyone can upload to it.
|
||||||
data.trusted = false;
|
data.trusted = false;
|
||||||
|
|
||||||
|
@ -1285,15 +1285,33 @@ decryptEvent(const MegolmSessionIndex &index,
|
||||||
}
|
}
|
||||||
|
|
||||||
crypto::Trust
|
crypto::Trust
|
||||||
calculate_trust(const std::string &user_id, const MegolmSessionIndex &index)
|
calculate_trust(const std::string &user_id,
|
||||||
|
const std::string &room_id,
|
||||||
|
const mtx::events::msg::Encrypted &event)
|
||||||
{
|
{
|
||||||
auto status = cache::client()->verificationStatus(user_id);
|
auto index = MegolmSessionIndex(room_id, event);
|
||||||
auto megolmData = cache::client()->getMegolmSessionData(index);
|
auto megolmData = cache::client()->getMegolmSessionData(index);
|
||||||
crypto::Trust trustlevel = crypto::Trust::Unverified;
|
crypto::Trust trustlevel = crypto::Trust::MessageUnverified;
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto session = cache::client()->getInboundMegolmSession(index);
|
||||||
|
if (!session) {
|
||||||
|
return trustlevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
olm::client()->decrypt_group_message(session.get(), event.ciphertext);
|
||||||
|
} catch (const lmdb::error &e) {
|
||||||
|
return trustlevel;
|
||||||
|
} catch (const mtx::crypto::olm_exception &e) {
|
||||||
|
return trustlevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto status = cache::client()->verificationStatus(user_id);
|
||||||
|
|
||||||
if (megolmData && megolmData->trusted &&
|
if (megolmData && megolmData->trusted &&
|
||||||
status.verified_device_keys.count(megolmData->sender_key))
|
status.verified_device_keys.count(megolmData->sender_key)) {
|
||||||
trustlevel = status.verified_device_keys.at(megolmData->sender_key);
|
trustlevel = status.verified_device_keys.at(megolmData->sender_key);
|
||||||
|
}
|
||||||
|
|
||||||
return trustlevel;
|
return trustlevel;
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,9 @@ decryptEvent(const MegolmSessionIndex &index,
|
||||||
const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &event,
|
const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &event,
|
||||||
bool dont_write_db = false);
|
bool dont_write_db = false);
|
||||||
crypto::Trust
|
crypto::Trust
|
||||||
calculate_trust(const std::string &user_id, const MegolmSessionIndex &index);
|
calculate_trust(const std::string &user_id,
|
||||||
|
const std::string &room_id,
|
||||||
|
const mtx::events::msg::Encrypted &event);
|
||||||
|
|
||||||
void
|
void
|
||||||
mark_keys_as_published();
|
mark_keys_as_published();
|
||||||
|
|
|
@ -854,8 +854,7 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
|
||||||
std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
|
std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
|
||||||
&*encrypted_event)) {
|
&*encrypted_event)) {
|
||||||
return olm::calculate_trust(
|
return olm::calculate_trust(
|
||||||
encrypted->sender,
|
encrypted->sender, room_id_.toStdString(), encrypted->content);
|
||||||
MegolmSessionIndex(room_id_.toStdString(), encrypted->content));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return crypto::Trust::Unverified;
|
return crypto::Trust::Unverified;
|
||||||
|
|
Loading…
Reference in a new issue