Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#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");
}
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
mtx::events::msg::Encrypted
encrypt_group_message(const std::string &room_id,
const std::string &device_id,
const std::string &body)
{
using namespace mtx::events;
// Always chech before for existence.
auto res = cache::client()->getOutboundMegolmSession(room_id);
auto payload = olm::client()->encrypt_group_message(res.session, body);
// Prepare the m.room.encrypted event.
msg::Encrypted data;
data.ciphertext = std::string((char *)payload.data(), payload.size());
data.sender_key = olm::client()->identity_keys().curve25519;
data.session_id = res.data.session_id;
data.device_id = device_id;
auto message_index = olm_outbound_group_session_message_index(res.session);
log::crypto()->info("next message_index {}", message_index);
// We need to re-pickle the session after we send a message to save the new message_index.
cache::client()->updateOutboundMegolmSession(room_id, message_index);
return data;
}