Add context menu action to request encryption keys
This commit is contained in:
parent
9a0e18dea7
commit
278eccc040
7 changed files with 312 additions and 10 deletions
275
src/Olm.cpp
275
src/Olm.cpp
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "Cache.h"
|
||||
#include "Logging.hpp"
|
||||
#include "MatrixClient.h"
|
||||
|
||||
using namespace mtx::crypto;
|
||||
|
||||
|
|
@ -49,9 +50,22 @@ handle_to_device_messages(const std::vector<nlohmann::json> &msgs)
|
|||
"validation error for olm message: {} {}", e.what(), msg.dump(2));
|
||||
}
|
||||
|
||||
// TODO: Move this event type into matrix-structs
|
||||
} else if (msg_type == "m.room_key_request") {
|
||||
} else if (msg_type == to_string(mtx::events::EventType::RoomKeyRequest)) {
|
||||
nhlog::crypto()->warn("handling key request event: {}", msg.dump(2));
|
||||
try {
|
||||
mtx::events::msg::KeyRequest req = msg;
|
||||
if (req.action == mtx::events::msg::RequestAction::Request)
|
||||
handle_key_request_message(std::move(req));
|
||||
else
|
||||
nhlog::crypto()->warn(
|
||||
"ignore key request (unhandled action): {}",
|
||||
req.request_id);
|
||||
} catch (const nlohmann::json::exception &e) {
|
||||
nhlog::crypto()->warn(
|
||||
"parsing error for key_request message: {} {}",
|
||||
e.what(),
|
||||
msg.dump(2));
|
||||
}
|
||||
} else {
|
||||
nhlog::crypto()->warn("unhandled event: {}", msg.dump(2));
|
||||
}
|
||||
|
|
@ -256,4 +270,261 @@ mark_keys_as_published()
|
|||
cache::client()->saveOlmAccount(olm::client()->save(STORAGE_SECRET_KEY));
|
||||
}
|
||||
|
||||
void
|
||||
request_keys(const std::string &room_id, const std::string &event_id)
|
||||
{
|
||||
nhlog::crypto()->info("requesting keys for event {} at {}", event_id, room_id);
|
||||
|
||||
http::v2::client()->get_event(
|
||||
room_id,
|
||||
event_id,
|
||||
[event_id, room_id](const mtx::events::collections::TimelineEvents &res,
|
||||
mtx::http::RequestErr err) {
|
||||
using namespace mtx::events;
|
||||
|
||||
if (err) {
|
||||
nhlog::net()->warn(
|
||||
"failed to retrieve event {} from {}", event_id, room_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mpark::holds_alternative<EncryptedEvent<msg::Encrypted>>(res)) {
|
||||
nhlog::net()->info(
|
||||
"retrieved event is not encrypted: {} from {}", event_id, room_id);
|
||||
return;
|
||||
}
|
||||
|
||||
olm::send_key_request_for(room_id,
|
||||
mpark::get<EncryptedEvent<msg::Encrypted>>(res));
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
send_key_request_for(const std::string &room_id,
|
||||
const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &e)
|
||||
{
|
||||
using namespace mtx::events;
|
||||
|
||||
nhlog::crypto()->debug("sending key request: {}", json(e).dump(2));
|
||||
auto payload = json{{"action", "request"},
|
||||
{"request_id", http::v2::client()->generate_txn_id()},
|
||||
{"requesting_device_id", http::v2::client()->device_id()},
|
||||
{"body",
|
||||
{{"algorithm", MEGOLM_ALGO},
|
||||
{"room_id", room_id},
|
||||
{"sender_key", e.content.sender_key},
|
||||
{"session_id", e.content.session_id}}}};
|
||||
|
||||
json body;
|
||||
body["messages"][e.sender] = json::object();
|
||||
body["messages"][e.sender][e.content.device_id] = payload;
|
||||
|
||||
nhlog::crypto()->debug("m.room_key_request: {}", body.dump(2));
|
||||
|
||||
http::v2::client()->send_to_device(
|
||||
"m.room_key_request", body, [e](mtx::http::RequestErr err) {
|
||||
if (err) {
|
||||
nhlog::net()->warn("failed to send "
|
||||
"send_to_device "
|
||||
"message: {}",
|
||||
err->matrix_error.error);
|
||||
}
|
||||
|
||||
nhlog::net()->info(
|
||||
"m.room_key_request sent to {}:{}", e.sender, e.content.device_id);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
handle_key_request_message(const mtx::events::msg::KeyRequest &req)
|
||||
{
|
||||
if (req.algorithm != MEGOLM_ALGO) {
|
||||
nhlog::crypto()->info("ignoring key request {} with invalid algorithm: {}",
|
||||
req.request_id,
|
||||
req.algorithm);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we were the sender of the session being requested.
|
||||
if (req.sender_key != olm::client()->identity_keys().curve25519) {
|
||||
nhlog::crypto()->info("ignoring key request {} because we were not the sender: "
|
||||
"\nrequested({}) ours({})",
|
||||
req.request_id,
|
||||
req.sender_key,
|
||||
olm::client()->identity_keys().curve25519);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we have the keys for the requested session.
|
||||
if (!cache::client()->outboundMegolmSessionExists(req.room_id)) {
|
||||
nhlog::crypto()->warn("requested session not found in room: {}", req.room_id);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that the requested session_id and the one we have saved match.
|
||||
const auto session = cache::client()->getOutboundMegolmSession(req.room_id);
|
||||
if (req.session_id != session.data.session_id) {
|
||||
nhlog::crypto()->warn("session id of retrieved session doesn't match the request: "
|
||||
"requested({}), ours({})",
|
||||
req.session_id,
|
||||
session.data.session_id);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Prepare the m.room_key event.
|
||||
//
|
||||
auto payload = json{{"algorithm", "m.megolm.v1.aes-sha2"},
|
||||
{"room_id", req.room_id},
|
||||
{"session_id", req.session_id},
|
||||
{"session_key", session.data.session_key}};
|
||||
|
||||
send_megolm_key_to_device(req.sender, req.requesting_device_id, payload);
|
||||
}
|
||||
|
||||
void
|
||||
send_megolm_key_to_device(const std::string &user_id,
|
||||
const std::string &device_id,
|
||||
const json &payload)
|
||||
{
|
||||
mtx::requests::QueryKeys req;
|
||||
req.device_keys[user_id] = {device_id};
|
||||
|
||||
http::v2::client()->query_keys(
|
||||
req,
|
||||
[payload, user_id, device_id](const mtx::responses::QueryKeys &res,
|
||||
mtx::http::RequestErr err) {
|
||||
if (err) {
|
||||
nhlog::net()->warn("failed to query device keys: {} {}",
|
||||
err->matrix_error.error,
|
||||
static_cast<int>(err->status_code));
|
||||
return;
|
||||
}
|
||||
|
||||
nhlog::net()->warn("retrieved device keys from {}, {}", user_id, device_id);
|
||||
|
||||
if (res.device_keys.empty()) {
|
||||
nhlog::net()->warn("no devices retrieved {}", user_id);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = res.device_keys.begin()->second;
|
||||
if (device.empty()) {
|
||||
nhlog::net()->warn("no keys retrieved from user, device {}", user_id);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto device_keys = device.begin()->second.keys;
|
||||
const auto curveKey = "curve25519:" + device_id;
|
||||
const auto edKey = "ed25519:" + device_id;
|
||||
|
||||
if ((device_keys.find(curveKey) == device_keys.end()) ||
|
||||
(device_keys.find(edKey) == device_keys.end())) {
|
||||
nhlog::net()->info("ignoring malformed keys for device {}", device_id);
|
||||
return;
|
||||
}
|
||||
|
||||
DevicePublicKeys pks;
|
||||
pks.ed25519 = device_keys.at(edKey);
|
||||
pks.curve25519 = device_keys.at(curveKey);
|
||||
|
||||
try {
|
||||
if (!mtx::crypto::verify_identity_signature(json(device.begin()->second),
|
||||
DeviceId(device_id),
|
||||
UserId(user_id))) {
|
||||
nhlog::crypto()->warn("failed to verify identity keys: {}",
|
||||
json(device).dump(2));
|
||||
return;
|
||||
}
|
||||
} catch (const json::exception &e) {
|
||||
nhlog::crypto()->warn("failed to parse device key json: {}", e.what());
|
||||
return;
|
||||
} catch (const mtx::crypto::olm_exception &e) {
|
||||
nhlog::crypto()->warn("failed to verify device key json: {}", e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
auto room_key = olm::client()
|
||||
->create_room_key_event(UserId(user_id), pks.ed25519, payload)
|
||||
.dump();
|
||||
|
||||
http::v2::client()->claim_keys(
|
||||
user_id,
|
||||
{device_id},
|
||||
[room_key, user_id, device_id, pks](const mtx::responses::ClaimKeys &res,
|
||||
mtx::http::RequestErr err) {
|
||||
if (err) {
|
||||
nhlog::net()->warn("claim keys error: {} {} {}",
|
||||
err->matrix_error.error,
|
||||
err->parse_error,
|
||||
static_cast<int>(err->status_code));
|
||||
return;
|
||||
}
|
||||
|
||||
nhlog::net()->info("claimed keys for {}", user_id);
|
||||
|
||||
if (res.one_time_keys.size() == 0) {
|
||||
nhlog::net()->info("no one-time keys found for user_id: {}",
|
||||
user_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.one_time_keys.find(user_id) == res.one_time_keys.end()) {
|
||||
nhlog::net()->info("no one-time keys found for user_id: {}",
|
||||
user_id);
|
||||
return;
|
||||
}
|
||||
|
||||
auto retrieved_devices = res.one_time_keys.at(user_id);
|
||||
if (retrieved_devices.empty()) {
|
||||
nhlog::net()->info("claiming keys for {}: no retrieved devices",
|
||||
device_id);
|
||||
return;
|
||||
}
|
||||
|
||||
json body;
|
||||
body["messages"][user_id] = json::object();
|
||||
|
||||
auto device = retrieved_devices.begin()->second;
|
||||
nhlog::net()->info("{} : \n {}", device_id, device.dump(2));
|
||||
|
||||
json device_msg;
|
||||
|
||||
try {
|
||||
auto olm_session = olm::client()->create_outbound_session(
|
||||
pks.curve25519, device.begin()->at("key"));
|
||||
|
||||
auto device_msg = olm::client()->create_olm_encrypted_content(
|
||||
olm_session.get(), room_key, pks.curve25519);
|
||||
|
||||
cache::client()->saveOlmSession(pks.curve25519,
|
||||
std::move(olm_session));
|
||||
} catch (const json::exception &e) {
|
||||
nhlog::crypto()->warn("creating outbound session: {}",
|
||||
e.what());
|
||||
return;
|
||||
} catch (const mtx::crypto::olm_exception &e) {
|
||||
nhlog::crypto()->warn("creating outbound session: {}",
|
||||
e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
body["messages"][user_id][device_id] = device_msg;
|
||||
|
||||
nhlog::net()->info("send_to_device: {}", user_id);
|
||||
http::v2::client()->send_to_device(
|
||||
"m.room.encrypted", body, [user_id](mtx::http::RequestErr err) {
|
||||
if (err) {
|
||||
nhlog::net()->warn("failed to send "
|
||||
"send_to_device "
|
||||
"message: {}",
|
||||
err->matrix_error.error);
|
||||
}
|
||||
|
||||
nhlog::net()->info("m.room_key send to {}", user_id);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace olm
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue