summaryrefslogtreecommitdiff
path: root/protocols/Telegram/tdlib/td/td/mtproto/Transport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Telegram/tdlib/td/td/mtproto/Transport.cpp')
-rw-r--r--protocols/Telegram/tdlib/td/td/mtproto/Transport.cpp304
1 files changed, 231 insertions, 73 deletions
diff --git a/protocols/Telegram/tdlib/td/td/mtproto/Transport.cpp b/protocols/Telegram/tdlib/td/td/mtproto/Transport.cpp
index 0e65c82ee7..82247af84e 100644
--- a/protocols/Telegram/tdlib/td/td/mtproto/Transport.cpp
+++ b/protocols/Telegram/tdlib/td/td/mtproto/Transport.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,26 +7,134 @@
#include "td/mtproto/Transport.h"
#include "td/mtproto/AuthKey.h"
-#include "td/mtproto/crypto.h"
+#include "td/mtproto/KDF.h"
+#include "td/utils/as.h"
#include "td/utils/crypto.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
+#include "td/utils/misc.h"
#include "td/utils/Random.h"
+#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
#include <array>
+#include <tuple>
namespace td {
+
+int VERBOSITY_NAME(raw_mtproto) = VERBOSITY_NAME(DEBUG) + 10;
+
namespace mtproto {
-// mtproto v1.0
+#pragma pack(push, 4)
+#if TD_MSVC
+#pragma warning(push)
+#pragma warning(disable : 4200)
+#endif
+
+struct CryptoHeader {
+ uint64 auth_key_id;
+ UInt128 message_key;
+
+ // encrypted part
+ uint64 salt;
+ uint64 session_id;
+
+ // It is weird to generate message_id and seq_no while writing a packet.
+ //
+ // uint64 message_id;
+ // uint32 seq_no;
+ // uint32 message_data_length;
+ uint8 data[0]; // use compiler extension
+
+ static size_t encrypted_header_size() {
+ return sizeof(salt) + sizeof(session_id);
+ }
+
+ uint8 *encrypt_begin() {
+ return reinterpret_cast<uint8 *>(&salt);
+ }
+
+ const uint8 *encrypt_begin() const {
+ return reinterpret_cast<const uint8 *>(&salt);
+ }
+
+ CryptoHeader() = delete;
+ CryptoHeader(const CryptoHeader &) = delete;
+ CryptoHeader(CryptoHeader &&) = delete;
+ CryptoHeader &operator=(const CryptoHeader &) = delete;
+ CryptoHeader &operator=(CryptoHeader &&) = delete;
+ ~CryptoHeader() = delete;
+};
+
+struct CryptoPrefix {
+ uint64 message_id;
+ uint32 seq_no;
+ uint32 message_data_length;
+};
+
+struct EndToEndHeader {
+ uint64 auth_key_id;
+ UInt128 message_key;
+
+ // encrypted part
+ // uint32 message_data_length;
+ uint8 data[0]; // use compiler extension
+
+ static size_t encrypted_header_size() {
+ return 0;
+ }
+
+ uint8 *encrypt_begin() {
+ return reinterpret_cast<uint8 *>(&data);
+ }
+
+ const uint8 *encrypt_begin() const {
+ return reinterpret_cast<const uint8 *>(&data);
+ }
+
+ EndToEndHeader() = delete;
+ EndToEndHeader(const EndToEndHeader &) = delete;
+ EndToEndHeader(EndToEndHeader &&) = delete;
+ EndToEndHeader &operator=(const EndToEndHeader &) = delete;
+ EndToEndHeader &operator=(EndToEndHeader &&) = delete;
+ ~EndToEndHeader() = delete;
+};
+
+struct EndToEndPrefix {
+ uint32 message_data_length;
+};
+
+struct NoCryptoHeader {
+ uint64 auth_key_id;
+
+ // message_id is removed from CryptoHeader. Should be removed from here too.
+ //
+ // int64 message_id;
+ // uint32 message_data_length;
+ uint8 data[0]; // use compiler extension
+
+ NoCryptoHeader() = delete;
+ NoCryptoHeader(const NoCryptoHeader &) = delete;
+ NoCryptoHeader(NoCryptoHeader &&) = delete;
+ NoCryptoHeader &operator=(const NoCryptoHeader &) = delete;
+ NoCryptoHeader &operator=(NoCryptoHeader &&) = delete;
+ ~NoCryptoHeader() = delete;
+};
+
+#if TD_MSVC
+#pragma warning(pop)
+#endif
+#pragma pack(pop)
+
+// MTProto v1.0
template <class HeaderT>
-std::tuple<uint32, UInt128> Transport::calc_message_ack_and_key(const HeaderT &head, size_t data_size) {
+std::pair<uint32, UInt128> Transport::calc_message_ack_and_key(const HeaderT &head, size_t data_size) {
Slice part(head.encrypt_begin(), head.data + data_size);
UInt<160> message_sha1;
sha1(part, message_sha1.raw);
- return std::make_tuple(as<uint32>(message_sha1.raw) | (1u << 31), as<UInt128>(message_sha1.raw + 4));
+ return std::make_pair(as<uint32>(message_sha1.raw) | (1u << 31), as<UInt128>(message_sha1.raw + 4));
}
template <class HeaderT>
@@ -36,31 +144,29 @@ size_t Transport::calc_crypto_size(size_t data_size) {
return raw_size + ((enc_size + data_size + 15) & ~15);
}
-// mtproto v2.0
-std::tuple<uint32, UInt128> Transport::calc_message_key2(const AuthKey &auth_key, int X, Slice to_encrypt) {
+// MTProto v2.0
+std::pair<uint32, UInt128> Transport::calc_message_key2(const AuthKey &auth_key, int X, Slice to_encrypt) {
// msg_key_large = SHA256 (substr (auth_key, 88+x, 32) + plaintext + random_padding);
Sha256State state;
- sha256_init(&state);
- sha256_update(Slice(auth_key.key()).substr(88 + X, 32), &state);
- sha256_update(to_encrypt, &state);
+ state.init();
+ state.feed(Slice(auth_key.key()).substr(88 + X, 32));
+ state.feed(to_encrypt);
uint8 msg_key_large_raw[32];
MutableSlice msg_key_large(msg_key_large_raw, sizeof(msg_key_large_raw));
- sha256_final(&state, msg_key_large);
+ state.extract(msg_key_large, true);
// msg_key = substr (msg_key_large, 8, 16);
- UInt128 res_raw;
- MutableSlice res(res_raw.raw, sizeof(res_raw.raw));
- res.copy_from(msg_key_large.substr(8, 16));
+ UInt128 res;
+ as_slice(res).copy_from(msg_key_large.substr(8, 16));
- return std::make_tuple(as<uint32>(msg_key_large_raw) | (1u << 31), res_raw);
+ return std::make_pair(as<uint32>(msg_key_large_raw) | (1u << 31), res);
}
-template <class HeaderT>
-size_t Transport::calc_crypto_size2(size_t data_size) {
- size_t enc_size = HeaderT::encrypted_header_size();
- size_t raw_size = sizeof(HeaderT) - enc_size;
+namespace {
+size_t do_calc_crypto_size2_basic(size_t data_size, size_t enc_size, size_t raw_size) {
size_t encrypted_size = (enc_size + data_size + 12 + 15) & ~15;
+
std::array<size_t, 10> sizes{{64, 128, 192, 256, 384, 512, 768, 1024, 1280}};
for (auto size : sizes) {
if (encrypted_size <= size) {
@@ -72,19 +178,41 @@ size_t Transport::calc_crypto_size2(size_t data_size) {
return raw_size + encrypted_size;
}
+size_t do_calc_crypto_size2_rand(size_t data_size, size_t enc_size, size_t raw_size) {
+ size_t rand_data_size = Random::secure_uint32() & 0xff;
+ size_t encrypted_size = (enc_size + data_size + rand_data_size + 12 + 15) & ~15;
+ return raw_size + encrypted_size;
+}
+} // namespace
+
+template <class HeaderT>
+size_t Transport::calc_crypto_size2(size_t data_size, PacketInfo *info) {
+ if (info->size != 0) {
+ return info->size;
+ }
+
+ size_t enc_size = HeaderT::encrypted_header_size();
+ size_t raw_size = sizeof(HeaderT) - enc_size;
+ if (info->use_random_padding) {
+ info->size = narrow_cast<uint32>(do_calc_crypto_size2_rand(data_size, enc_size, raw_size));
+ } else {
+ info->size = narrow_cast<uint32>(do_calc_crypto_size2_basic(data_size, enc_size, raw_size));
+ }
+ return info->size;
+}
+
size_t Transport::calc_no_crypto_size(size_t data_size) {
return sizeof(NoCryptoHeader) + data_size;
}
Status Transport::read_no_crypto(MutableSlice message, PacketInfo *info, MutableSlice *data) {
if (message.size() < sizeof(NoCryptoHeader)) {
- return Status::Error(PSLICE() << "Invalid mtproto message: too small [message.size()=" << message.size()
+ return Status::Error(PSLICE() << "Invalid MTProto message: too small [message.size() = " << message.size()
<< "] < [sizeof(NoCryptoHeader) = " << sizeof(NoCryptoHeader) << "]");
}
- auto &header = as<NoCryptoHeader>(message.begin());
size_t data_size = message.size() - sizeof(NoCryptoHeader);
CHECK(message.size() == calc_no_crypto_size(data_size));
- *data = MutableSlice(header.data, data_size);
+ *data = MutableSlice(message.begin() + sizeof(NoCryptoHeader), data_size);
return Status::OK();
}
@@ -92,21 +220,19 @@ template <class HeaderT, class PrefixT>
Status Transport::read_crypto_impl(int X, MutableSlice message, const AuthKey &auth_key, HeaderT **header_ptr,
PrefixT **prefix_ptr, MutableSlice *data, PacketInfo *info) {
if (message.size() < sizeof(HeaderT)) {
- return Status::Error(PSLICE() << "Invalid mtproto message: too small [message.size()=" << message.size()
+ return Status::Error(PSLICE() << "Invalid MTProto message: too small [message.size() = " << message.size()
<< "] < [sizeof(HeaderT) = " << sizeof(HeaderT) << "]");
}
- auto *header = &as<HeaderT>(message.begin());
+ //FIXME: rewrite without reinterpret cast
+ auto *header = reinterpret_cast<HeaderT *>(message.begin());
*header_ptr = header;
auto to_decrypt = MutableSlice(header->encrypt_begin(), message.uend());
- if (to_decrypt.size() % 16 != 0) {
- return Status::Error(PSLICE() << "Invalid mtproto message: size of encrypted part is not multiple of 16 [size="
- << to_decrypt.size() << "]");
- }
+ to_decrypt.remove_suffix(to_decrypt.size() & 15);
if (header->auth_key_id != auth_key.id()) {
- return Status::Error(PSLICE() << "Invalid mtproto message: auth_key_id mismatch [found="
+ return Status::Error(PSLICE() << "Invalid MTProto message: auth_key_id mismatch [found = "
<< format::as_hex(header->auth_key_id)
- << "] [expected=" << format::as_hex(auth_key.id()) << "]");
+ << "] [expected = " << format::as_hex(auth_key.id()) << "]");
}
UInt256 aes_key;
@@ -117,43 +243,61 @@ Status Transport::read_crypto_impl(int X, MutableSlice message, const AuthKey &a
KDF2(auth_key.key(), header->message_key, X, &aes_key, &aes_iv);
}
- aes_ige_decrypt(aes_key, &aes_iv, to_decrypt, to_decrypt);
+ aes_ige_decrypt(as_slice(aes_key), as_slice(aes_iv), to_decrypt, to_decrypt);
size_t tail_size = message.end() - reinterpret_cast<char *>(header->data);
if (tail_size < sizeof(PrefixT)) {
return Status::Error("Too small encrypted part");
}
- auto *prefix = &as<PrefixT>(header->data);
+ //FIXME: rewrite without reinterpret cast
+ auto *prefix = reinterpret_cast<PrefixT *>(header->data);
*prefix_ptr = prefix;
size_t data_size = prefix->message_data_length + sizeof(PrefixT);
- bool is_length_ok = prefix->message_data_length % 4 == 0;
+ bool is_length_bad = false;
UInt128 real_message_key;
if (info->version == 1) {
+ is_length_bad |= info->check_mod4 && prefix->message_data_length % 4 != 0;
auto expected_size = calc_crypto_size<HeaderT>(data_size);
- is_length_ok = (is_length_ok & (expected_size == message.size())) != 0;
- auto check_size = data_size * is_length_ok + tail_size * (1 - is_length_ok);
+ is_length_bad |= expected_size != message.size();
+ auto check_size = data_size * (1 - is_length_bad) + tail_size * is_length_bad;
std::tie(info->message_ack, real_message_key) = calc_message_ack_and_key(*header, check_size);
} else {
- size_t pad_size = tail_size - data_size;
- is_length_ok = (is_length_ok & (tail_size - sizeof(PrefixT) >= prefix->message_data_length) & (12 <= pad_size) &
- (pad_size <= 1024)) != 0;
std::tie(info->message_ack, real_message_key) = calc_message_key2(auth_key, X, to_decrypt);
}
- bool is_key_ok = true;
+ int is_key_bad = false;
for (size_t i = 0; i < sizeof(real_message_key.raw); i++) {
- is_key_ok &= real_message_key.raw[i] == header->message_key.raw[i];
+ is_key_bad |= real_message_key.raw[i] ^ header->message_key.raw[i];
}
-
- if (!is_key_ok) {
- return Status::Error(PSLICE() << "Invalid mtproto message: message_key mismatch [found="
+ if (is_key_bad != 0) {
+ return Status::Error(PSLICE() << "Invalid MTProto message: message_key mismatch [found = "
<< format::as_hex_dump(header->message_key)
- << "] [expected=" << format::as_hex_dump(real_message_key) << "]");
+ << "] [expected = " << format::as_hex_dump(real_message_key) << "]");
}
- if (!is_length_ok) {
- return Status::Error(PSLICE() << "Invalid mtproto message: invalid length " << tag("total_size", message.size())
- << tag("message_data_length", prefix->message_data_length));
+
+ if (info->version == 2) {
+ if (info->check_mod4 && prefix->message_data_length % 4 != 0) {
+ return Status::Error(PSLICE() << "Invalid MTProto message: invalid length (not divisible by four)"
+ << tag("total_size", message.size())
+ << tag("message_data_length", prefix->message_data_length));
+ }
+ if (tail_size - sizeof(PrefixT) < prefix->message_data_length) {
+ return Status::Error(PSLICE() << "Invalid MTProto message: invalid length (message_data_length is too big)"
+ << tag("total_size", message.size())
+ << tag("message_data_length", prefix->message_data_length));
+ }
+ size_t pad_size = tail_size - data_size;
+ if (pad_size < 12 || pad_size > 1024) {
+ return Status::Error(PSLICE() << "Invalid MTProto message: invalid length (invalid padding length)"
+ << tag("padding_size", pad_size) << tag("total_size", message.size())
+ << tag("message_data_length", prefix->message_data_length));
+ }
+ } else {
+ if (is_length_bad) {
+ return Status::Error(PSLICE() << "Invalid MTProto message: invalid length " << tag("total_size", message.size())
+ << tag("message_data_length", prefix->message_data_length));
+ }
}
*data = MutableSlice(header->data, data_size);
@@ -191,24 +335,26 @@ size_t Transport::write_no_crypto(const Storer &storer, PacketInfo *info, Mutabl
if (size > dest.size()) {
return size;
}
- auto &header = as<NoCryptoHeader>(dest.begin());
- header.auth_key_id = 0;
- storer.store(header.data);
+ // NoCryptoHeader
+ as<uint64>(dest.begin()) = 0;
+ auto real_size = storer.store(dest.ubegin() + sizeof(uint64));
+ CHECK(real_size == storer.size());
return size;
}
template <class HeaderT>
void Transport::write_crypto_impl(int X, const Storer &storer, const AuthKey &auth_key, PacketInfo *info,
HeaderT *header, size_t data_size) {
- storer.store(header->data);
- VLOG(raw_mtproto) << "SEND" << format::as_hex_dump<4>(Slice(header->data, data_size));
- // LOG(ERROR) << "SEND" << format::as_hex_dump<4>(Slice(header->data, data_size)) << info->version;
+ auto real_data_size = storer.store(header->data);
+ CHECK(real_data_size == data_size);
+ VLOG(raw_mtproto) << "Send packet of size " << data_size << " to session " << format::as_hex(info->session_id) << ":"
+ << format::as_hex_dump<4>(Slice(header->data, data_size));
size_t size = 0;
if (info->version == 1) {
size = calc_crypto_size<HeaderT>(data_size);
} else {
- size = calc_crypto_size2<HeaderT>(data_size);
+ size = calc_crypto_size2<HeaderT>(data_size, info);
}
size_t pad_size = size - (sizeof(HeaderT) + data_size);
@@ -232,7 +378,7 @@ void Transport::write_crypto_impl(int X, const Storer &storer, const AuthKey &au
KDF2(auth_key.key(), header->message_key, X, &aes_key, &aes_iv);
}
- aes_ige_encrypt(aes_key, &aes_iv, to_encrypt, to_encrypt);
+ aes_ige_encrypt(as_slice(aes_key), as_slice(aes_iv), to_encrypt, to_encrypt);
}
size_t Transport::write_crypto(const Storer &storer, const AuthKey &auth_key, PacketInfo *info, MutableSlice dest) {
@@ -241,13 +387,14 @@ size_t Transport::write_crypto(const Storer &storer, const AuthKey &auth_key, Pa
if (info->version == 1) {
size = calc_crypto_size<CryptoHeader>(data_size);
} else {
- size = calc_crypto_size2<CryptoHeader>(data_size);
+ size = calc_crypto_size2<CryptoHeader>(data_size, info);
}
if (size > dest.size()) {
return size;
}
- auto &header = as<CryptoHeader>(dest.begin());
+ //FIXME: rewrite without reinterpret cast
+ auto &header = *reinterpret_cast<CryptoHeader *>(dest.begin());
header.auth_key_id = auth_key.id();
header.salt = info->salt;
header.session_id = info->session_id;
@@ -263,13 +410,14 @@ size_t Transport::write_e2e_crypto(const Storer &storer, const AuthKey &auth_key
if (info->version == 1) {
size = calc_crypto_size<EndToEndHeader>(data_size);
} else {
- size = calc_crypto_size2<EndToEndHeader>(data_size);
+ size = calc_crypto_size2<EndToEndHeader>(data_size, info);
}
if (size > dest.size()) {
return size;
}
- auto &header = as<EndToEndHeader>(dest.begin());
+ //FIXME: rewrite without reinterpret cast
+ auto &header = *reinterpret_cast<EndToEndHeader *>(dest.begin());
header.auth_key_id = auth_key.id();
write_crypto_impl(info->is_creator || info->version == 1 ? 0 : 8, storer, auth_key, info, &header, data_size);
@@ -279,33 +427,42 @@ size_t Transport::write_e2e_crypto(const Storer &storer, const AuthKey &auth_key
Result<uint64> Transport::read_auth_key_id(Slice message) {
if (message.size() < 8) {
- return Status::Error(PSLICE() << "Invalid mtproto message: smaller than 8 bytes [size=" << message.size() << "]");
+ return Status::Error(PSLICE() << "Invalid MTProto message: smaller than 8 bytes [size = " << message.size() << "]");
}
return as<uint64>(message.begin());
}
-Status Transport::read(MutableSlice message, const AuthKey &auth_key, PacketInfo *info, MutableSlice *data,
- int32 *error_code) {
- if (message.size() < 8) {
- if (message.size() == 4) {
- *error_code = as<int32>(message.begin());
- return Status::OK();
+Result<Transport::ReadResult> Transport::read(MutableSlice message, const AuthKey &auth_key, PacketInfo *info) {
+ if (message.size() < 12) {
+ if (message.size() < 4) {
+ return Status::Error(PSLICE() << "Invalid MTProto message: smaller than 4 bytes [size = " << message.size()
+ << "]");
+ }
+
+ int32 code = as<int32>(message.begin());
+ if (code == 0) {
+ return ReadResult::make_nop();
+ } else if (code == -1 && message.size() >= 8) {
+ return ReadResult::make_quick_ack(as<uint32>(message.begin() + 4));
+ } else {
+ return ReadResult::make_error(code);
}
- return Status::Error(PSLICE() << "Invalid mtproto message: smaller than 8 bytes [size=" << message.size() << "]");
}
+
info->auth_key_id = as<int64>(message.begin());
info->no_crypto_flag = info->auth_key_id == 0;
+ MutableSlice data;
if (info->type == PacketInfo::EndToEnd) {
- return read_e2e_crypto(message, auth_key, info, data);
- }
- if (info->no_crypto_flag) {
- return read_no_crypto(message, info, data);
+ TRY_STATUS(read_e2e_crypto(message, auth_key, info, &data));
+ } else if (info->no_crypto_flag) {
+ TRY_STATUS(read_no_crypto(message, info, &data));
} else {
if (auth_key.empty()) {
- return Status::Error("Failed to decrypt mtproto message: auth key is empty");
+ return Status::Error("Failed to decrypt MTProto message: auth key is empty");
}
- return read_crypto(message, auth_key, info, data);
+ TRY_STATUS(read_crypto(message, auth_key, info, &data));
}
+ return ReadResult::make_packet(data);
}
size_t Transport::write(const Storer &storer, const AuthKey &auth_key, PacketInfo *info, MutableSlice dest) {
@@ -319,5 +476,6 @@ size_t Transport::write(const Storer &storer, const AuthKey &auth_key, PacketInf
return write_crypto(storer, auth_key, info, dest);
}
}
+
} // namespace mtproto
} // namespace td