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