NotificationManager: decrypt_push

GitOrigin-RevId: 9f623bf065d54f402ca824d40e0515111330b7a7
This commit is contained in:
Arseny Smirnov 2019-01-29 19:16:52 +04:00
parent 429d709ea1
commit b481ce8465
9 changed files with 115 additions and 16 deletions

View File

@ -128,18 +128,16 @@ Status Transport::read_crypto_impl(int X, MutableSlice message, const AuthKey &a
auto *prefix = reinterpret_cast<PrefixT *>(header->data); auto *prefix = reinterpret_cast<PrefixT *>(header->data);
*prefix_ptr = prefix; *prefix_ptr = prefix;
size_t data_size = prefix->message_data_length + sizeof(PrefixT); size_t data_size = prefix->message_data_length + sizeof(PrefixT);
bool is_length_ok = prefix->message_data_length % 4 == 0; bool is_length_ok = true;
UInt128 real_message_key; UInt128 real_message_key;
if (info->version == 1) { if (info->version == 1) {
is_length_ok &= !info->check_mod4 || prefix->message_data_length % 4 == 0;
auto expected_size = calc_crypto_size<HeaderT>(data_size); auto expected_size = calc_crypto_size<HeaderT>(data_size);
is_length_ok = (is_length_ok & (expected_size == message.size())) != 0; 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); auto check_size = data_size * is_length_ok + tail_size * (1 - is_length_ok);
std::tie(info->message_ack, real_message_key) = calc_message_ack_and_key(*header, check_size); std::tie(info->message_ack, real_message_key) = calc_message_ack_and_key(*header, check_size);
} else { } 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); std::tie(info->message_ack, real_message_key) = calc_message_key2(auth_key, X, to_decrypt);
} }
@ -153,10 +151,30 @@ Status Transport::read_crypto_impl(int X, MutableSlice message, const AuthKey &a
<< format::as_hex_dump(header->message_key) << format::as_hex_dump(header->message_key)
<< "] [expected=" << format::as_hex_dump(real_message_key) << "]"); << "] [expected=" << format::as_hex_dump(real_message_key) << "]");
} }
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_ok) { if (!is_length_ok) {
return Status::Error(PSLICE() << "Invalid mtproto message: invalid length " << tag("total_size", message.size()) return Status::Error(PSLICE() << "Invalid mtproto message: invalid length " << tag("total_size", message.size())
<< tag("message_data_length", prefix->message_data_length)); << tag("message_data_length", prefix->message_data_length));
} }
}
*data = MutableSlice(header->data, data_size); *data = MutableSlice(header->data, data_size);
return Status::OK(); return Status::OK();

View File

@ -132,9 +132,10 @@ struct PacketInfo {
uint64 message_id; uint64 message_id;
int32 seq_no; int32 seq_no;
int32 version = 1; int32 version{1};
bool no_crypto_flag; bool no_crypto_flag;
bool is_creator = false; bool is_creator{false};
bool check_mod4{true};
}; };
class Transport { class Transport {

View File

@ -334,7 +334,7 @@ std::pair<int64, string> DhHandshake::gen_key() {
return std::pair<int64, string>(key_id, std::move(key)); return std::pair<int64, string>(key_id, std::move(key));
} }
int64 DhHandshake::calc_key_id(const string &auth_key) { int64 DhHandshake::calc_key_id(Slice auth_key) {
UInt<160> auth_key_sha1; UInt<160> auth_key_sha1;
sha1(auth_key, auth_key_sha1.raw); sha1(auth_key, auth_key_sha1.raw);
return as<int64>(auth_key_sha1.raw + 12); return as<int64>(auth_key_sha1.raw + 12);

View File

@ -83,7 +83,7 @@ class DhHandshake {
std::pair<int64, string> gen_key(); std::pair<int64, string> gen_key();
static int64 calc_key_id(const string &auth_key); static int64 calc_key_id(Slice auth_key);
enum Flags { HasConfig = 1, HasGA = 2 }; enum Flags { HasConfig = 1, HasGA = 2 };

View File

@ -254,9 +254,7 @@ void DeviceTokenManager::register_device(tl_object_ptr<td_api::DeviceToken> devi
info.encryption_key.resize(ENCRYPTION_KEY_LENGTH); info.encryption_key.resize(ENCRYPTION_KEY_LENGTH);
while (true) { while (true) {
Random::secure_bytes(info.encryption_key); Random::secure_bytes(info.encryption_key);
uint8 sha1_buf[20]; info.encryption_key_id = DhHandshake::calc_key_id(info.encryption_key);
sha1(info.encryption_key, sha1_buf);
info.encryption_key_id = as<int64>(sha1_buf + 12);
if (info.encryption_key_id <= -MIN_ENCRYPTION_KEY_ID || info.encryption_key_id >= MIN_ENCRYPTION_KEY_ID) { if (info.encryption_key_id <= -MIN_ENCRYPTION_KEY_ID || info.encryption_key_id >= MIN_ENCRYPTION_KEY_ID) {
// ensure that encryption key ID never collide with anything // ensure that encryption key ID never collide with anything
break; break;

View File

@ -16,6 +16,9 @@
#include "td/telegram/Td.h" #include "td/telegram/Td.h"
#include "td/telegram/TdDb.h" #include "td/telegram/TdDb.h"
#include "td/mtproto/Transport.h"
#include "td/mtproto/AuthKey.h"
#include "td/utils/as.h" #include "td/utils/as.h"
#include "td/utils/base64.h" #include "td/utils/base64.h"
#include "td/utils/format.h" #include "td/utils/format.h"
@ -2134,6 +2137,53 @@ Result<int64> NotificationManager::get_push_receiver_id(string payload) {
return Status::Error(200, "Unsupported push notification"); return Status::Error(200, "Unsupported push notification");
} }
Result<string> NotificationManager::decrypt_push(int64 encryption_key_id, string encryption_key, string push) {
auto r_json_value = json_decode(push);
if (r_json_value.is_error()) {
return Status::Error(400, "Failed to parse payload as JSON object");
}
auto json_value = r_json_value.move_as_ok();
if (json_value.type() != JsonValue::Type::Object) {
return Status::Error(400, "Expected JSON object");
}
for (auto &field_value : json_value.get_object()) {
if (field_value.first == "p") {
auto encrypted_payload = std::move(field_value.second);
if (encrypted_payload.type() != JsonValue::Type::String) {
return Status::Error(400, "Expected encrypted payload as a String");
}
Slice data = encrypted_payload.get_string();
if (data.size() < 12) {
return Status::Error(400, "Encrypted payload is too small");
}
auto r_decoded = base64url_decode(data);
if (r_decoded.is_error()) {
return Status::Error(400, "Failed to base64url-decode payload");
}
return decrypt_push_payload(encryption_key_id, std::move(encryption_key), r_decoded.move_as_ok());
}
}
return Status::Error(400, "No 'p'(payload) field found in push");
}
Result<string> NotificationManager::decrypt_push_payload(int64 encryption_key_id, string encryption_key,
string payload) {
mtproto::AuthKey auth_key(encryption_key_id, std::move(encryption_key));
mtproto::PacketInfo packet_info;
packet_info.version = 2;
packet_info.type = mtproto::PacketInfo::EndToEnd;
packet_info.is_creator = true;
packet_info.check_mod4 = false;
TRY_RESULT(result, mtproto::Transport::read(payload, auth_key, &packet_info));
if (result.type() != mtproto::Transport::ReadResult::Packet) {
return Status::Error(400, "Wrong packet type");
}
return result.packet().str();
}
void NotificationManager::before_get_difference() { void NotificationManager::before_get_difference() {
if (is_disabled()) { if (is_disabled()) {
return; return;

View File

@ -91,8 +91,9 @@ class NotificationManager : public Actor {
void process_push_notification(string payload, Promise<Unit> &&promise); void process_push_notification(string payload, Promise<Unit> &&promise);
static Result<int64> get_push_receiver_id(string payload); static Result<int64> get_push_receiver_id(string push);
static Result<string> decrypt_push(int64 encryption_key_id, string encryption_key, string push);
static Result<string> decrypt_push_payload(int64 encryption_key_id, string encryption_key, string payload);
void before_get_difference(); void before_get_difference();
void after_get_difference(); void after_get_difference();

View File

@ -38,6 +38,9 @@ class As {
std::memcpy(&res, ptr_, sizeof(T)); std::memcpy(&res, ptr_, sizeof(T));
return res; return res;
} }
bool operator==(const As &other) const {
return this->operator T() == other.operator T();
}
private: private:
void *ptr_; void *ptr_;

View File

@ -15,6 +15,7 @@
#include "td/mtproto/HandshakeConnection.h" #include "td/mtproto/HandshakeConnection.h"
#include "td/mtproto/PingConnection.h" #include "td/mtproto/PingConnection.h"
#include "td/mtproto/RawConnection.h" #include "td/mtproto/RawConnection.h"
#include "td/mtproto/Transport.h"
#include "td/net/GetHostByNameActor.h" #include "td/net/GetHostByNameActor.h"
#include "td/net/Socks5.h" #include "td/net/Socks5.h"
@ -23,7 +24,10 @@
#include "td/telegram/ConfigManager.h" #include "td/telegram/ConfigManager.h"
#include "td/telegram/net/DcId.h" #include "td/telegram/net/DcId.h"
#include "td/telegram/net/PublicRsaKeyShared.h" #include "td/telegram/net/PublicRsaKeyShared.h"
#include "td/telegram/NotificationManager.h"
#include "td/utils/as.h"
#include "td/utils/base64.h"
#include "td/utils/logging.h" #include "td/utils/logging.h"
#include "td/utils/port/IPAddress.h" #include "td/utils/port/IPAddress.h"
#include "td/utils/port/SocketFd.h" #include "td/utils/port/SocketFd.h"
@ -426,3 +430,27 @@ TEST(Mtproto, socks5) {
} }
sched.finish(); sched.finish();
} }
TEST(Mtproto, notifications) {
string push =
"eyJwIjoiSkRnQ3NMRWxEaWhyVWRRN1pYM3J1WVU4TlRBMFhMb0N6UWRNdzJ1cWlqMkdRbVR1WXVvYXhUeFJHaG1QQm8yVElYZFBzX2N3b2RIb3lY"
"b2drVjM1dVl0UzdWeElNX1FNMDRKMG1mV3ZZWm4zbEtaVlJ0aFVBNGhYUWlaN0pfWDMyZDBLQUlEOWgzRnZwRjNXUFRHQWRaVkdFYzg3bnFPZ3hD"
"NUNMRkM2SU9fZmVqcEpaV2RDRlhBWWpwc1k2aktrbVNRdFZ1MzE5ZW04UFVieXZudFpfdTNud2hjQ0czMk96TGp4S1kyS1lzU21JZm1GMzRmTmw1"
"QUxaa2JvY2s2cE5rZEdrak9qYmRLckJyU0ZtWU8tQ0FsRE10dEplZFFnY1U5bVJQdU80b1d2NG5sb1VXS19zSlNTaXdIWEZyb1pWTnZTeFJ0Z1dN"
"ZyJ9";
string key =
"uBa5yu01a-nJJeqsR3yeqMs6fJLYXjecYzFcvS6jIwS3nefBIr95LWrTm-IbRBNDLrkISz1Sv0KYpDzhU8WFRk1D0V_"
"qyO7XsbDPyrYxRBpGxofJUINSjb1uCxoSdoh1_F0UXEA2fWWKKVxL0DKUQssZfbVj3AbRglsWpH-jDK1oc6eBydRiS3i4j-"
"H0yJkEMoKRgaF9NaYI4u26oIQ-Ez46kTVU-R7e3acdofOJKm7HIKan_5ZMg82Dvec2M6vc_"
"I54Vs28iBx8IbBO1y5z9WSScgW3JCvFFKP2MXIu7Jow5-cpUx6jXdzwRUb9RDApwAFKi45zpv8eb3uPCDAmIQ";
string decrypted_payload =
"fwAAAHsibG9jX2tleSI6Ik1FU1NBR0VfVEVYVCIsImxvY19hcmdzIjpbIkFyc2VueSBTbWlybm92IiwiYWJjZGVmZyJdLCJjdXN0b20iOnsibXNn"
"X2lkIjoiNTkwMDQ3IiwiZnJvbV9pZCI6IjYyODE0In0sImJhZGdlIjoiNDA5In0";
push = base64url_decode(push).move_as_ok();
key = base64url_decode(key).move_as_ok();
decrypted_payload = base64url_decode(decrypted_payload).move_as_ok();
auto key_id = DhHandshake::calc_key_id(key);
ASSERT_EQ(key_id, NotificationManager::get_push_receiver_id(push).ok());
ASSERT_EQ(decrypted_payload, NotificationManager::decrypt_push(key_id, key, push).ok());
}