forked from mirror/nheko
parent
b89257a34b
commit
626c680911
@ -0,0 +1,65 @@ |
||||
#pragma once |
||||
|
||||
#include <memory> |
||||
#include <mtxclient/crypto/client.hpp> |
||||
|
||||
constexpr auto OLM_ALGO = "m.olm.v1.curve25519-aes-sha2"; |
||||
|
||||
namespace olm { |
||||
|
||||
struct OlmCipherContent |
||||
{ |
||||
std::string body; |
||||
uint8_t type; |
||||
}; |
||||
|
||||
inline void |
||||
from_json(const nlohmann::json &obj, OlmCipherContent &msg) |
||||
{ |
||||
msg.body = obj.at("body"); |
||||
msg.type = obj.at("type"); |
||||
} |
||||
|
||||
struct OlmMessage |
||||
{ |
||||
std::string sender_key; |
||||
std::string sender; |
||||
|
||||
using RecipientKey = std::string; |
||||
std::map<RecipientKey, OlmCipherContent> ciphertext; |
||||
}; |
||||
|
||||
inline void |
||||
from_json(const nlohmann::json &obj, OlmMessage &msg) |
||||
{ |
||||
if (obj.at("type") != "m.room.encrypted") |
||||
throw std::invalid_argument("invalid type for olm message"); |
||||
|
||||
if (obj.at("content").at("algorithm") != OLM_ALGO) |
||||
throw std::invalid_argument("invalid algorithm for olm message"); |
||||
|
||||
msg.sender = obj.at("sender"); |
||||
msg.sender_key = obj.at("content").at("sender_key"); |
||||
msg.ciphertext = |
||||
obj.at("content").at("ciphertext").get<std::map<std::string, OlmCipherContent>>(); |
||||
} |
||||
|
||||
mtx::crypto::OlmClient * |
||||
client(); |
||||
|
||||
void |
||||
handle_to_device_messages(const std::vector<nlohmann::json> &msgs); |
||||
|
||||
void |
||||
handle_olm_message(const OlmMessage &msg); |
||||
|
||||
void |
||||
handle_olm_normal_message(const std::string &sender, |
||||
const std::string &sender_key, |
||||
const OlmCipherContent &content); |
||||
|
||||
void |
||||
handle_pre_key_olm_message(const std::string &sender, |
||||
const std::string &sender_key, |
||||
const OlmCipherContent &content); |
||||
} // namespace olm
|
@ -0,0 +1,139 @@ |
||||
#include "Olm.hpp" |
||||
|
||||
#include "Cache.h" |
||||
#include "Logging.hpp" |
||||
|
||||
using namespace mtx::crypto; |
||||
|
||||
namespace { |
||||
auto client_ = std::make_unique<mtx::crypto::OlmClient>(); |
||||
} |
||||
|
||||
namespace olm { |
||||
|
||||
mtx::crypto::OlmClient * |
||||
client() |
||||
{ |
||||
return client_.get(); |
||||
} |
||||
|
||||
void |
||||
handle_to_device_messages(const std::vector<nlohmann::json> &msgs) |
||||
{ |
||||
if (msgs.empty()) |
||||
return; |
||||
|
||||
log::crypto()->info("received {} to_device messages", msgs.size()); |
||||
|
||||
for (const auto &msg : msgs) { |
||||
try { |
||||
OlmMessage olm_msg = msg; |
||||
handle_olm_message(std::move(olm_msg)); |
||||
} catch (const nlohmann::json::exception &e) { |
||||
log::crypto()->warn( |
||||
"parsing error for olm message: {} {}", e.what(), msg.dump(2)); |
||||
} catch (const std::invalid_argument &e) { |
||||
log::crypto()->warn( |
||||
"validation error for olm message: {} {}", e.what(), msg.dump(2)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void |
||||
handle_olm_message(const OlmMessage &msg) |
||||
{ |
||||
log::crypto()->info("sender : {}", msg.sender); |
||||
log::crypto()->info("sender_key: {}", msg.sender_key); |
||||
|
||||
const auto my_key = olm::client()->identity_keys().curve25519; |
||||
|
||||
for (const auto &cipher : msg.ciphertext) { |
||||
// We skip messages not meant for the current device.
|
||||
if (cipher.first != my_key) |
||||
continue; |
||||
|
||||
const auto type = cipher.second.type; |
||||
log::crypto()->info("type: {}", type == 0 ? "OLM_PRE_KEY" : "OLM_MESSAGE"); |
||||
|
||||
if (type == OLM_MESSAGE_TYPE_PRE_KEY) |
||||
handle_pre_key_olm_message(msg.sender, msg.sender_key, cipher.second); |
||||
else |
||||
handle_olm_normal_message(msg.sender, msg.sender_key, cipher.second); |
||||
} |
||||
} |
||||
|
||||
void |
||||
handle_pre_key_olm_message(const std::string &sender, |
||||
const std::string &sender_key, |
||||
const OlmCipherContent &content) |
||||
{ |
||||
log::crypto()->info("opening olm session with {}", sender); |
||||
|
||||
OlmSessionPtr inbound_session = nullptr; |
||||
try { |
||||
inbound_session = olm::client()->create_inbound_session(content.body); |
||||
} catch (const olm_exception &e) { |
||||
log::crypto()->critical( |
||||
"failed to create inbound session with {}: {}", sender, e.what()); |
||||
return; |
||||
} |
||||
|
||||
if (!matches_inbound_session_from(inbound_session.get(), sender_key, content.body)) { |
||||
log::crypto()->warn("inbound olm session doesn't match sender's key ({})", sender); |
||||
return; |
||||
} |
||||
|
||||
mtx::crypto::BinaryBuf output; |
||||
try { |
||||
output = olm::client()->decrypt_message( |
||||
inbound_session.get(), OLM_MESSAGE_TYPE_PRE_KEY, content.body); |
||||
} catch (const olm_exception &e) { |
||||
log::crypto()->critical( |
||||
"failed to decrypt olm message {}: {}", content.body, e.what()); |
||||
return; |
||||
} |
||||
|
||||
auto plaintext = json::parse(std::string((char *)output.data(), output.size())); |
||||
log::crypto()->info("decrypted message: \n {}", plaintext.dump(2)); |
||||
|
||||
std::string room_id, session_id, session_key; |
||||
try { |
||||
room_id = plaintext.at("content").at("room_id"); |
||||
session_id = plaintext.at("content").at("session_id"); |
||||
session_key = plaintext.at("content").at("session_key"); |
||||
} catch (const nlohmann::json::exception &e) { |
||||
log::crypto()->critical( |
||||
"failed to parse plaintext olm message: {} {}", e.what(), plaintext.dump(2)); |
||||
return; |
||||
} |
||||
|
||||
MegolmSessionIndex index; |
||||
index.room_id = room_id; |
||||
index.session_id = session_id; |
||||
index.sender_key = sender_key; |
||||
|
||||
if (!cache::client()->inboundMegolmSessionExists(index)) { |
||||
auto megolm_session = olm::client()->init_inbound_group_session(session_key); |
||||
|
||||
try { |
||||
cache::client()->saveInboundMegolmSession(index, std::move(megolm_session)); |
||||
} catch (const lmdb::error &e) { |
||||
log::crypto()->critical("failed to save inbound megolm session: {}", |
||||
e.what()); |
||||
return; |
||||
} |
||||
|
||||
log::crypto()->info("established inbound megolm session ({}, {})", room_id, sender); |
||||
} else { |
||||
log::crypto()->warn( |
||||
"inbound megolm session already exists ({}, {})", room_id, sender); |
||||
} |
||||
} |
||||
|
||||
void |
||||
handle_olm_normal_message(const std::string &, const std::string &, const OlmCipherContent &) |
||||
{ |
||||
log::crypto()->warn("olm(1) not implemeted yet"); |
||||
} |
||||
|
||||
} // namespace olm
|
Loading…
Reference in new issue