Merge master and fix conflicts
This commit is contained in:
commit
3f567a8da7
87 changed files with 5667 additions and 2989 deletions
216
src/Olm.cpp
216
src/Olm.cpp
|
|
@ -123,7 +123,17 @@ handle_to_device_messages(const std::vector<mtx::events::collections::DeviceEven
|
|||
if (msg_type == to_string(mtx::events::EventType::RoomEncrypted)) {
|
||||
try {
|
||||
olm::OlmMessage olm_msg = j_msg;
|
||||
handle_olm_message(std::move(olm_msg));
|
||||
cache::client()->query_keys(
|
||||
olm_msg.sender,
|
||||
[olm_msg](const UserKeyCache &userKeys, mtx::http::RequestErr e) {
|
||||
if (e) {
|
||||
nhlog::crypto()->error(
|
||||
"Failed to query user keys, dropping olm "
|
||||
"message");
|
||||
return;
|
||||
}
|
||||
handle_olm_message(std::move(olm_msg), userKeys);
|
||||
});
|
||||
} catch (const nlohmann::json::exception &e) {
|
||||
nhlog::crypto()->warn(
|
||||
"parsing error for olm message: {} {}", e.what(), j_msg.dump(2));
|
||||
|
|
@ -197,13 +207,15 @@ handle_to_device_messages(const std::vector<mtx::events::collections::DeviceEven
|
|||
}
|
||||
|
||||
void
|
||||
handle_olm_message(const OlmMessage &msg)
|
||||
handle_olm_message(const OlmMessage &msg, const UserKeyCache &otherUserDeviceKeys)
|
||||
{
|
||||
nhlog::crypto()->info("sender : {}", msg.sender);
|
||||
nhlog::crypto()->info("sender_key: {}", msg.sender_key);
|
||||
|
||||
const auto my_key = olm::client()->identity_keys().curve25519;
|
||||
|
||||
bool failed_decryption = false;
|
||||
|
||||
for (const auto &cipher : msg.ciphertext) {
|
||||
// We skip messages not meant for the current device.
|
||||
if (cipher.first != my_key) {
|
||||
|
|
@ -224,6 +236,7 @@ handle_olm_message(const OlmMessage &msg)
|
|||
msg.sender, msg.sender_key, cipher.second);
|
||||
} else {
|
||||
nhlog::crypto()->error("Undecryptable olm message!");
|
||||
failed_decryption = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
@ -231,6 +244,57 @@ handle_olm_message(const OlmMessage &msg)
|
|||
if (!payload.is_null()) {
|
||||
mtx::events::collections::DeviceEvents device_event;
|
||||
|
||||
// Other properties are included in order to prevent an attacker from
|
||||
// publishing someone else's curve25519 keys as their own and subsequently
|
||||
// claiming to have sent messages which they didn't. sender must correspond
|
||||
// to the user who sent the event, recipient to the local user, and
|
||||
// recipient_keys to the local ed25519 key.
|
||||
std::string receiver_ed25519 = payload["recipient_keys"]["ed25519"];
|
||||
if (receiver_ed25519.empty() ||
|
||||
receiver_ed25519 != olm::client()->identity_keys().ed25519) {
|
||||
nhlog::crypto()->warn(
|
||||
"Decrypted event doesn't include our ed25519: {}",
|
||||
payload.dump());
|
||||
return;
|
||||
}
|
||||
std::string receiver = payload["recipient"];
|
||||
if (receiver.empty() || receiver != http::client()->user_id().to_string()) {
|
||||
nhlog::crypto()->warn(
|
||||
"Decrypted event doesn't include our user_id: {}",
|
||||
payload.dump());
|
||||
return;
|
||||
}
|
||||
|
||||
// Clients must confirm that the sender_key and the ed25519 field value
|
||||
// under the keys property match the keys returned by /keys/query for the
|
||||
// given user, and must also verify the signature of the payload. Without
|
||||
// this check, a client cannot be sure that the sender device owns the
|
||||
// private part of the ed25519 key it claims to have in the Olm payload.
|
||||
// This is crucial when the ed25519 key corresponds to a verified device.
|
||||
std::string sender_ed25519 = payload["keys"]["ed25519"];
|
||||
if (sender_ed25519.empty()) {
|
||||
nhlog::crypto()->warn(
|
||||
"Decrypted event doesn't include sender ed25519: {}",
|
||||
payload.dump());
|
||||
return;
|
||||
}
|
||||
|
||||
bool from_their_device = false;
|
||||
for (auto [device_id, key] : otherUserDeviceKeys.device_keys) {
|
||||
if (key.keys.at("curve25519:" + device_id) == msg.sender_key) {
|
||||
if (key.keys.at("ed25519:" + device_id) == sender_ed25519) {
|
||||
from_their_device = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!from_their_device) {
|
||||
nhlog::crypto()->warn("Decrypted event isn't sent from a device "
|
||||
"listed by that user! {}",
|
||||
payload.dump());
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::string msg_type = payload["type"];
|
||||
json event_array = json::array();
|
||||
|
|
@ -242,7 +306,7 @@ handle_olm_message(const OlmMessage &msg)
|
|||
if (temp_events.empty()) {
|
||||
nhlog::crypto()->warn("Decrypted unknown event: {}",
|
||||
payload.dump());
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
device_event = temp_events.at(0);
|
||||
}
|
||||
|
|
@ -276,17 +340,20 @@ handle_olm_message(const OlmMessage &msg)
|
|||
ChatPage::instance()->receivedDeviceVerificationDone(e8->content);
|
||||
} else if (auto roomKey =
|
||||
std::get_if<DeviceEvent<msg::RoomKey>>(&device_event)) {
|
||||
create_inbound_megolm_session(*roomKey, msg.sender_key);
|
||||
create_inbound_megolm_session(
|
||||
*roomKey, msg.sender_key, sender_ed25519);
|
||||
} else if (auto forwardedRoomKey =
|
||||
std::get_if<DeviceEvent<msg::ForwardedRoomKey>>(
|
||||
&device_event)) {
|
||||
forwardedRoomKey->content.forwarding_curve25519_key_chain.push_back(
|
||||
msg.sender_key);
|
||||
import_inbound_megolm_session(*forwardedRoomKey);
|
||||
} else if (auto e =
|
||||
std::get_if<DeviceEvent<msg::SecretSend>>(&device_event)) {
|
||||
auto local_user = http::client()->user_id();
|
||||
|
||||
if (msg.sender != local_user.to_string())
|
||||
continue;
|
||||
return;
|
||||
|
||||
auto secret_name =
|
||||
request_id_to_secret_name.find(e->content.request_id);
|
||||
|
|
@ -306,7 +373,7 @@ handle_olm_message(const OlmMessage &msg)
|
|||
cache::verificationStatus(local_user.to_string());
|
||||
|
||||
if (!verificationStatus)
|
||||
continue;
|
||||
return;
|
||||
|
||||
auto deviceKeys = cache::userKeys(local_user.to_string());
|
||||
std::string sender_device_id;
|
||||
|
|
@ -344,7 +411,6 @@ handle_olm_message(const OlmMessage &msg)
|
|||
"for secrect "
|
||||
"'{}'",
|
||||
name);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -360,27 +426,28 @@ handle_olm_message(const OlmMessage &msg)
|
|||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
failed_decryption = true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
auto otherUserDeviceKeys = cache::userKeys(msg.sender);
|
||||
if (failed_decryption) {
|
||||
try {
|
||||
std::map<std::string, std::vector<std::string>> targets;
|
||||
for (auto [device_id, key] : otherUserDeviceKeys.device_keys) {
|
||||
if (key.keys.at("curve25519:" + device_id) == msg.sender_key)
|
||||
targets[msg.sender].push_back(device_id);
|
||||
}
|
||||
|
||||
if (!otherUserDeviceKeys)
|
||||
return;
|
||||
|
||||
std::map<std::string, std::vector<std::string>> targets;
|
||||
for (auto [device_id, key] : otherUserDeviceKeys->device_keys) {
|
||||
if (key.keys.at("curve25519:" + device_id) == msg.sender_key)
|
||||
targets[msg.sender].push_back(device_id);
|
||||
send_encrypted_to_device_messages(
|
||||
targets, mtx::events::DeviceEvent<mtx::events::msg::Dummy>{}, true);
|
||||
nhlog::crypto()->info("Recovering from broken olm channel with {}:{}",
|
||||
msg.sender,
|
||||
msg.sender_key);
|
||||
} catch (std::exception &e) {
|
||||
nhlog::crypto()->error("Failed to recover from broken olm sessions: {}",
|
||||
e.what());
|
||||
}
|
||||
|
||||
send_encrypted_to_device_messages(
|
||||
targets, mtx::events::DeviceEvent<mtx::events::msg::Dummy>{}, true);
|
||||
nhlog::crypto()->info(
|
||||
"Recovering from broken olm channel with {}:{}", msg.sender, msg.sender_key);
|
||||
} catch (std::exception &e) {
|
||||
nhlog::crypto()->error("Failed to recover from broken olm sessions: {}", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -450,7 +517,7 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
|
|||
|
||||
std::map<std::string, std::vector<std::string>> sendSessionTo;
|
||||
mtx::crypto::OutboundGroupSessionPtr session = nullptr;
|
||||
OutboundGroupSessionData group_session_data;
|
||||
GroupSessionData group_session_data;
|
||||
|
||||
if (cache::outboundMegolmSessionExists(room_id)) {
|
||||
auto res = cache::getOutboundMegolmSession(room_id);
|
||||
|
|
@ -519,7 +586,8 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
|
|||
} else {
|
||||
// compare devices
|
||||
bool device_removed = false;
|
||||
for (const auto &dev : session_member_it->second.devices) {
|
||||
for (const auto &dev :
|
||||
session_member_it->second.deviceids) {
|
||||
if (!member_it->second ||
|
||||
!member_it->second->device_keys.count(
|
||||
dev.first)) {
|
||||
|
|
@ -541,7 +609,7 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
|
|||
if (member_it->second)
|
||||
for (const auto &dev :
|
||||
member_it->second->device_keys)
|
||||
if (!session_member_it->second.devices
|
||||
if (!session_member_it->second.deviceids
|
||||
.count(dev.first) &&
|
||||
(member_it->first != own_user_id ||
|
||||
dev.first != device_id))
|
||||
|
|
@ -571,32 +639,28 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
|
|||
const auto session_key = mtx::crypto::session_key(session.get());
|
||||
|
||||
// Saving the new megolm session.
|
||||
OutboundGroupSessionData session_data{};
|
||||
session_data.session_id = mtx::crypto::session_id(session.get());
|
||||
session_data.session_key = mtx::crypto::session_key(session.get());
|
||||
session_data.message_index = 0;
|
||||
session_data.timestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
GroupSessionData session_data{};
|
||||
session_data.message_index = 0;
|
||||
session_data.timestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
session_data.sender_claimed_ed25519_key = olm::client()->identity_keys().ed25519;
|
||||
|
||||
sendSessionTo.clear();
|
||||
|
||||
for (const auto &[user, devices] : members) {
|
||||
sendSessionTo[user] = {};
|
||||
session_data.initially.keys[user] = {};
|
||||
session_data.currently.keys[user] = {};
|
||||
if (devices) {
|
||||
for (const auto &[device_id_, key] : devices->device_keys) {
|
||||
(void)key;
|
||||
if (device_id != device_id_ || user != own_user_id) {
|
||||
sendSessionTo[user].push_back(device_id_);
|
||||
session_data.initially.keys[user]
|
||||
.devices[device_id_] = 0;
|
||||
session_data.currently.keys[user]
|
||||
.deviceids[device_id_] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cache::saveOutboundMegolmSession(room_id, session_data, session);
|
||||
group_session_data = std::move(session_data);
|
||||
|
||||
{
|
||||
MegolmSessionIndex index;
|
||||
index.room_id = room_id;
|
||||
|
|
@ -604,8 +668,12 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
|
|||
index.sender_key = olm::client()->identity_keys().curve25519;
|
||||
auto megolm_session =
|
||||
olm::client()->init_inbound_group_session(session_key);
|
||||
cache::saveInboundMegolmSession(index, std::move(megolm_session));
|
||||
cache::saveInboundMegolmSession(
|
||||
index, std::move(megolm_session), session_data);
|
||||
}
|
||||
|
||||
cache::saveOutboundMegolmSession(room_id, session_data, session);
|
||||
group_session_data = std::move(session_data);
|
||||
}
|
||||
|
||||
mtx::events::DeviceEvent<mtx::events::msg::RoomKey> megolm_payload{};
|
||||
|
|
@ -641,8 +709,8 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
|
|||
group_session_data.currently.keys[user] = {};
|
||||
|
||||
for (const auto &device_id_ : devices) {
|
||||
if (!group_session_data.currently.keys[user].devices.count(device_id_))
|
||||
group_session_data.currently.keys[user].devices[device_id_] =
|
||||
if (!group_session_data.currently.keys[user].deviceids.count(device_id_))
|
||||
group_session_data.currently.keys[user].deviceids[device_id_] =
|
||||
group_session_data.message_index;
|
||||
}
|
||||
}
|
||||
|
|
@ -704,7 +772,8 @@ try_olm_decryption(const std::string &sender_key, const mtx::events::msg::OlmCip
|
|||
|
||||
void
|
||||
create_inbound_megolm_session(const mtx::events::DeviceEvent<mtx::events::msg::RoomKey> &roomKey,
|
||||
const std::string &sender_key)
|
||||
const std::string &sender_key,
|
||||
const std::string &sender_ed25519)
|
||||
{
|
||||
MegolmSessionIndex index;
|
||||
index.room_id = roomKey.content.room_id;
|
||||
|
|
@ -712,9 +781,13 @@ create_inbound_megolm_session(const mtx::events::DeviceEvent<mtx::events::msg::R
|
|||
index.sender_key = sender_key;
|
||||
|
||||
try {
|
||||
GroupSessionData data{};
|
||||
data.forwarding_curve25519_key_chain = {sender_key};
|
||||
data.sender_claimed_ed25519_key = sender_ed25519;
|
||||
|
||||
auto megolm_session =
|
||||
olm::client()->init_inbound_group_session(roomKey.content.session_key);
|
||||
cache::saveInboundMegolmSession(index, std::move(megolm_session));
|
||||
cache::saveInboundMegolmSession(index, std::move(megolm_session), data);
|
||||
} catch (const lmdb::error &e) {
|
||||
nhlog::crypto()->critical("failed to save inbound megolm session: {}", e.what());
|
||||
return;
|
||||
|
|
@ -741,7 +814,13 @@ import_inbound_megolm_session(
|
|||
try {
|
||||
auto megolm_session =
|
||||
olm::client()->import_inbound_group_session(roomKey.content.session_key);
|
||||
cache::saveInboundMegolmSession(index, std::move(megolm_session));
|
||||
|
||||
GroupSessionData data{};
|
||||
data.forwarding_curve25519_key_chain =
|
||||
roomKey.content.forwarding_curve25519_key_chain;
|
||||
data.sender_claimed_ed25519_key = roomKey.content.sender_claimed_ed25519_key;
|
||||
|
||||
cache::saveInboundMegolmSession(index, std::move(megolm_session), data);
|
||||
} catch (const lmdb::error &e) {
|
||||
nhlog::crypto()->critical("failed to save inbound megolm session: {}", e.what());
|
||||
return;
|
||||
|
|
@ -817,21 +896,16 @@ handle_key_request_message(const mtx::events::DeviceEvent<mtx::events::msg::KeyR
|
|||
return;
|
||||
}
|
||||
|
||||
// Check if we were the sender of the session being requested.
|
||||
if (req.content.sender_key != olm::client()->identity_keys().curve25519) {
|
||||
nhlog::crypto()->debug("ignoring key request {} because we were not the sender: "
|
||||
"\nrequested({}) ours({})",
|
||||
req.content.request_id,
|
||||
req.content.sender_key,
|
||||
olm::client()->identity_keys().curve25519);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we have the keys for the requested session.
|
||||
auto outboundSession = cache::getOutboundMegolmSession(req.content.room_id);
|
||||
if (!outboundSession.session) {
|
||||
nhlog::crypto()->warn("requested session not found in room: {}",
|
||||
req.content.room_id);
|
||||
// Check if we were the sender of the session being requested (unless it is actually us
|
||||
// requesting the session).
|
||||
if (req.sender != http::client()->user_id().to_string() &&
|
||||
req.content.sender_key != olm::client()->identity_keys().curve25519) {
|
||||
nhlog::crypto()->debug(
|
||||
"ignoring key request {} because we did not create the requested session: "
|
||||
"\nrequested({}) ours({})",
|
||||
req.content.request_id,
|
||||
req.content.sender_key,
|
||||
olm::client()->identity_keys().curve25519);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -839,7 +913,15 @@ handle_key_request_message(const mtx::events::DeviceEvent<mtx::events::msg::KeyR
|
|||
MegolmSessionIndex index{};
|
||||
index.room_id = req.content.room_id;
|
||||
index.session_id = req.content.session_id;
|
||||
index.sender_key = olm::client()->identity_keys().curve25519;
|
||||
index.sender_key = req.content.sender_key;
|
||||
|
||||
// Check if we have the keys for the requested session.
|
||||
auto sessionData = cache::getMegolmSessionData(index);
|
||||
if (!sessionData) {
|
||||
nhlog::crypto()->warn("requested session not found in room: {}",
|
||||
req.content.room_id);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto session = cache::getInboundMegolmSession(index);
|
||||
if (!session) {
|
||||
|
|
@ -873,12 +955,12 @@ handle_key_request_message(const mtx::events::DeviceEvent<mtx::events::msg::KeyR
|
|||
|
||||
bool shouldSeeKeys = false;
|
||||
uint64_t minimumIndex = -1;
|
||||
if (outboundSession.data.currently.keys.count(req.sender)) {
|
||||
if (outboundSession.data.currently.keys.at(req.sender)
|
||||
.devices.count(req.content.requesting_device_id)) {
|
||||
if (sessionData->currently.keys.count(req.sender)) {
|
||||
if (sessionData->currently.keys.at(req.sender)
|
||||
.deviceids.count(req.content.requesting_device_id)) {
|
||||
shouldSeeKeys = true;
|
||||
minimumIndex = outboundSession.data.currently.keys.at(req.sender)
|
||||
.devices.at(req.content.requesting_device_id);
|
||||
minimumIndex = sessionData->currently.keys.at(req.sender)
|
||||
.deviceids.at(req.content.requesting_device_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -906,8 +988,9 @@ handle_key_request_message(const mtx::events::DeviceEvent<mtx::events::msg::KeyR
|
|||
forward_key.sender_key = index.sender_key;
|
||||
|
||||
// TODO(Nico): Figure out if this is correct
|
||||
forward_key.sender_claimed_ed25519_key = olm::client()->identity_keys().ed25519;
|
||||
forward_key.forwarding_curve25519_key_chain = {};
|
||||
forward_key.sender_claimed_ed25519_key = sessionData->sender_claimed_ed25519_key;
|
||||
forward_key.forwarding_curve25519_key_chain =
|
||||
sessionData->forwarding_curve25519_key_chain;
|
||||
|
||||
send_megolm_key_to_device(
|
||||
req.sender, req.content.requesting_device_id, forward_key);
|
||||
|
|
@ -928,6 +1011,7 @@ send_megolm_key_to_device(const std::string &user_id,
|
|||
std::map<std::string, std::vector<std::string>> targets;
|
||||
targets[user_id] = {device_id};
|
||||
send_encrypted_to_device_messages(targets, room_key);
|
||||
nhlog::crypto()->debug("Forwarded key to {}:{}", user_id, device_id);
|
||||
}
|
||||
|
||||
DecryptionResult
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue