Add context menu action to request encryption keys

This commit is contained in:
Konstantinos Sideris 2018-07-07 23:59:23 +03:00
parent 9a0e18dea7
commit 278eccc040
7 changed files with 312 additions and 10 deletions

View file

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