diff --git a/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py b/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py
index 988adeca93..4369d7681e 100644
--- a/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py
+++ b/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py
@@ -462,7 +462,7 @@ def addTextSerialize(lst, dct, dataLetter):
 
       templateArgument = ''
       if (isTemplate != ''):
-          templateArgument = '<mtpRequest>'
+          templateArgument = '<MTP::SecureRequest>'
 
       result += 'void Serialize_' + name + '(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, uint32 iflag) {\n';
       if (len(conditions)):
diff --git a/Telegram/SourceFiles/mtproto/auth_key.cpp b/Telegram/SourceFiles/mtproto/auth_key.cpp
index 633d1345f5..b9ef48800c 100644
--- a/Telegram/SourceFiles/mtproto/auth_key.cpp
+++ b/Telegram/SourceFiles/mtproto/auth_key.cpp
@@ -92,14 +92,22 @@ void aesIgeDecryptRaw(const void *src, void *dst, uint32 len, const void *key, c
 	AES_ige_encrypt(static_cast<const uchar*>(src), static_cast<uchar*>(dst), len, &aes, aes_iv, AES_DECRYPT);
 }
 
-void aesCtrEncrypt(void *data, uint32 len, const void *key, CTRState *state) {
+void aesCtrEncrypt(bytes::span data, const void *key, CTRState *state) {
 	AES_KEY aes;
 	AES_set_encrypt_key(static_cast<const uchar*>(key), 256, &aes);
 
 	static_assert(CTRState::IvecSize == AES_BLOCK_SIZE, "Wrong size of ctr ivec!");
 	static_assert(CTRState::EcountSize == AES_BLOCK_SIZE, "Wrong size of ctr ecount!");
 
-	CRYPTO_ctr128_encrypt(static_cast<const uchar*>(data), static_cast<uchar*>(data), len, &aes, state->ivec, state->ecount, &state->num, (block128_f) AES_encrypt);
+	CRYPTO_ctr128_encrypt(
+		reinterpret_cast<const uchar*>(data.data()),
+		reinterpret_cast<uchar*>(data.data()),
+		data.size(),
+		&aes,
+		state->ivec,
+		state->ecount,
+		&state->num,
+		(block128_f)AES_encrypt);
 }
 
 } // namespace MTP
diff --git a/Telegram/SourceFiles/mtproto/auth_key.h b/Telegram/SourceFiles/mtproto/auth_key.h
index c6a92eadc5..dcc84291b0 100644
--- a/Telegram/SourceFiles/mtproto/auth_key.h
+++ b/Telegram/SourceFiles/mtproto/auth_key.h
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include <array>
 #include <memory>
+#include "base/bytes.h"
 
 namespace MTP {
 
@@ -145,6 +146,6 @@ struct CTRState {
 	uint32 num = 0;
 	uchar ecount[EcountSize] = { 0 };
 };
-void aesCtrEncrypt(void *data, uint32 len, const void *key, CTRState *state);
+void aesCtrEncrypt(bytes::span data, const void *key, CTRState *state);
 
 } // namespace MTP
diff --git a/Telegram/SourceFiles/mtproto/concurrent_sender.cpp b/Telegram/SourceFiles/mtproto/concurrent_sender.cpp
index 043053da14..a1f8b67ef1 100644
--- a/Telegram/SourceFiles/mtproto/concurrent_sender.cpp
+++ b/Telegram/SourceFiles/mtproto/concurrent_sender.cpp
@@ -110,7 +110,7 @@ auto ConcurrentSender::with_instance(Method &&method)
 
 ConcurrentSender::RequestBuilder::RequestBuilder(
 	not_null<ConcurrentSender*> sender,
-	mtpRequest &&serialized) noexcept
+	SecureRequest &&serialized) noexcept
 : _sender(sender)
 , _serialized(std::move(serialized)) {
 }
diff --git a/Telegram/SourceFiles/mtproto/concurrent_sender.h b/Telegram/SourceFiles/mtproto/concurrent_sender.h
index 3afc11600a..aed431a2c8 100644
--- a/Telegram/SourceFiles/mtproto/concurrent_sender.h
+++ b/Telegram/SourceFiles/mtproto/concurrent_sender.h
@@ -61,7 +61,7 @@ class ConcurrentSender : public base::has_weak_ptr {
 	protected:
 		RequestBuilder(
 			not_null<ConcurrentSender*> sender,
-			mtpRequest &&serialized) noexcept;
+			SecureRequest &&serialized) noexcept;
 
 		void setToDC(ShiftedDcId dcId) noexcept;
 		void setCanWait(TimeMs ms) noexcept;
@@ -74,7 +74,7 @@ class ConcurrentSender : public base::has_weak_ptr {
 
 	private:
 		not_null<ConcurrentSender*> _sender;
-		mtpRequest _serialized;
+		SecureRequest _serialized;
 		ShiftedDcId _dcId = 0;
 		TimeMs _canWait = 0;
 
@@ -224,7 +224,7 @@ template <typename Request>
 ConcurrentSender::SpecificRequestBuilder<Request>::SpecificRequestBuilder(
 	not_null<ConcurrentSender*> sender,
 	Request &&request
-) noexcept : RequestBuilder(sender, mtpRequestData::serialize(request)) {
+) noexcept : RequestBuilder(sender, SecureRequest::Serialize(request)) {
 }
 
 template <typename Request>
diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp
index 87d9c63299..ddbbdaf817 100644
--- a/Telegram/SourceFiles/mtproto/connection.cpp
+++ b/Telegram/SourceFiles/mtproto/connection.cpp
@@ -210,9 +210,9 @@ ModExpFirst CreateModExp(
 	return result;
 }
 
-void wrapInvokeAfter(mtpRequest &to, const mtpRequest &from, const mtpRequestMap &haveSent, int32 skipBeforeRequest = 0) {
-	mtpMsgId afterId(*(mtpMsgId*)(from->after->data() + 4));
-	mtpRequestMap::const_iterator i = afterId ? haveSent.constFind(afterId) : haveSent.cend();
+void wrapInvokeAfter(SecureRequest &to, const SecureRequest &from, const RequestMap &haveSent, int32 skipBeforeRequest = 0) {
+	const auto afterId = *(mtpMsgId*)(from->after->data() + 4);
+	const auto i = afterId ? haveSent.constFind(afterId) : haveSent.cend();
 	int32 size = to->size(), lenInInts = (from.innerLength() >> 2), headlen = 4, fulllen = headlen + lenInInts;
 	if (i == haveSent.constEnd()) { // no invoke after or such msg was not sent or was completed recently
 		to->resize(size + fulllen + skipBeforeRequest);
@@ -508,17 +508,16 @@ void ConnectionPrivate::resetSession() { // recreate all msg_id and msg_seqno
 	QWriteLocker locker2(sessionData->toResendMutex());
 	QWriteLocker locker3(sessionData->toSendMutex());
 	QWriteLocker locker4(sessionData->wereAckedMutex());
-	mtpRequestMap &haveSent(sessionData->haveSentMap());
-	mtpRequestIdsMap &toResend(sessionData->toResendMap());
-	mtpPreRequestMap &toSend(sessionData->toSendMap());
-	mtpRequestIdsMap &wereAcked(sessionData->wereAckedMap());
+	auto &haveSent = sessionData->haveSentMap();
+	auto &toResend = sessionData->toResendMap();
+	auto &toSend = sessionData->toSendMap();
+	auto &wereAcked = sessionData->wereAckedMap();
 
-	mtpMsgId newId = msgid();
-	mtpRequestMap setSeqNumbers;
-	typedef QMap<mtpMsgId, mtpMsgId> Replaces;
-	Replaces replaces;
-	for (mtpRequestMap::const_iterator i = haveSent.cbegin(), e = haveSent.cend(); i != e; ++i) {
-		if (!mtpRequestData::isSentContainer(i.value())) {
+	auto newId = msgid();
+	auto setSeqNumbers = RequestMap();
+	auto replaces = QMap<mtpMsgId, mtpMsgId>();
+	for (auto i = haveSent.cbegin(), e = haveSent.cend(); i != e; ++i) {
+		if (!i.value().isSentContainer()) {
 			if (!*(mtpMsgId*)(i.value()->constData() + 4)) continue;
 
 			mtpMsgId id = i.key();
@@ -541,11 +540,11 @@ void ConnectionPrivate::resetSession() { // recreate all msg_id and msg_seqno
 			setSeqNumbers.insert(id, i.value());
 		}
 	}
-	for (mtpRequestIdsMap::const_iterator i = toResend.cbegin(), e = toResend.cend(); i != e; ++i) { // collect all non-container requests
-		mtpPreRequestMap::const_iterator j = toSend.constFind(i.value());
+	for (auto i = toResend.cbegin(), e = toResend.cend(); i != e; ++i) { // collect all non-container requests
+		const auto j = toSend.constFind(i.value());
 		if (j == toSend.cend()) continue;
 
-		if (!mtpRequestData::isSentContainer(j.value())) {
+		if (!j.value().isSentContainer()) {
 			if (!*(mtpMsgId*)(j.value()->constData() + 4)) continue;
 
 			mtpMsgId id = i.key();
@@ -573,36 +572,36 @@ void ConnectionPrivate::resetSession() { // recreate all msg_id and msg_seqno
 	DEBUG_LOG(("MTP Info: creating new session after bad_msg_notification, setting random server_session %1").arg(session));
 	sessionData->setSession(session);
 
-	for (mtpRequestMap::const_iterator i = setSeqNumbers.cbegin(), e = setSeqNumbers.cend(); i != e; ++i) { // generate new seq_numbers
+	for (auto i = setSeqNumbers.cbegin(), e = setSeqNumbers.cend(); i != e; ++i) { // generate new seq_numbers
 		bool wasNeedAck = (*(i.value()->data() + 6) & 1);
 		*(i.value()->data() + 6) = sessionData->nextRequestSeqNumber(wasNeedAck);
 	}
 	if (!replaces.isEmpty()) {
-		for (Replaces::const_iterator i = replaces.cbegin(), e = replaces.cend(); i != e; ++i) { // replace msgIds keys in all data structs
-			mtpRequestMap::iterator j = haveSent.find(i.key());
+		for (auto i = replaces.cbegin(), e = replaces.cend(); i != e; ++i) { // replace msgIds keys in all data structs
+			const auto j = haveSent.find(i.key());
 			if (j != haveSent.cend()) {
-				mtpRequest req = j.value();
+				const auto req = j.value();
 				haveSent.erase(j);
 				haveSent.insert(i.value(), req);
 			}
-			mtpRequestIdsMap::iterator k = toResend.find(i.key());
+			const auto k = toResend.find(i.key());
 			if (k != toResend.cend()) {
-				mtpRequestId req = k.value();
+				const auto req = k.value();
 				toResend.erase(k);
 				toResend.insert(i.value(), req);
 			}
-			k = wereAcked.find(i.key());
-			if (k != wereAcked.cend()) {
-				mtpRequestId req = k.value();
-				wereAcked.erase(k);
+			const auto l = wereAcked.find(i.key());
+			if (l != wereAcked.cend()) {
+				const auto req = l.value();
+				wereAcked.erase(l);
 				wereAcked.insert(i.value(), req);
 			}
 		}
-		for (mtpRequestMap::const_iterator i = haveSent.cbegin(), e = haveSent.cend(); i != e; ++i) { // replace msgIds in saved containers
-			if (mtpRequestData::isSentContainer(i.value())) {
-				mtpMsgId *ids = (mtpMsgId *)(i.value()->data() + 8);
+		for (auto i = haveSent.cbegin(), e = haveSent.cend(); i != e; ++i) { // replace msgIds in saved containers
+			if (i.value().isSentContainer()) {
+				mtpMsgId *ids = (mtpMsgId*)(i.value()->data() + 8);
 				for (uint32 j = 0, l = (i.value()->size() - 8) >> 1; j < l; ++j) {
-					Replaces::const_iterator k = replaces.constFind(ids[j]);
+					const auto k = replaces.constFind(ids[j]);
 					if (k != replaces.cend()) {
 						ids[j] = k.value();
 					}
@@ -621,24 +620,24 @@ void ConnectionPrivate::resetSession() { // recreate all msg_id and msg_seqno
 	emit sessionResetDone();
 }
 
-mtpMsgId ConnectionPrivate::prepareToSend(mtpRequest &request, mtpMsgId currentLastId) {
+mtpMsgId ConnectionPrivate::prepareToSend(SecureRequest &request, mtpMsgId currentLastId) {
 	if (request->size() < 9) return 0;
 	mtpMsgId msgId = *(mtpMsgId*)(request->constData() + 4);
 	if (msgId) { // resending this request
 		QWriteLocker locker(sessionData->toResendMutex());
-		mtpRequestIdsMap &toResend(sessionData->toResendMap());
-		mtpRequestIdsMap::iterator i = toResend.find(msgId);
+		auto &toResend = sessionData->toResendMap();
+		const auto i = toResend.find(msgId);
 		if (i != toResend.cend()) {
 			toResend.erase(i);
 		}
 	} else {
 		msgId = *(mtpMsgId*)(request->data() + 4) = currentLastId;
-		*(request->data() + 6) = sessionData->nextRequestSeqNumber(mtpRequestData::needAck(request));
+		*(request->data() + 6) = sessionData->nextRequestSeqNumber(request.needAck());
 	}
 	return msgId;
 }
 
-mtpMsgId ConnectionPrivate::replaceMsgId(mtpRequest &request, mtpMsgId newId) {
+mtpMsgId ConnectionPrivate::replaceMsgId(SecureRequest &request, mtpMsgId newId) {
 	if (request->size() < 9) return 0;
 
 	mtpMsgId oldMsgId = *(mtpMsgId*)(request->constData() + 4);
@@ -647,45 +646,45 @@ mtpMsgId ConnectionPrivate::replaceMsgId(mtpRequest &request, mtpMsgId newId) {
 			QWriteLocker locker(sessionData->toResendMutex());
 			// haveSentMutex() and wereAckedMutex() were locked in tryToSend()
 
-			mtpRequestIdsMap &toResend(sessionData->toResendMap());
-			mtpRequestIdsMap &wereAcked(sessionData->wereAckedMap());
-			mtpRequestMap &haveSent(sessionData->haveSentMap());
+			auto &toResend = sessionData->toResendMap();
+			auto &wereAcked = sessionData->wereAckedMap();
+			auto &haveSent = sessionData->haveSentMap();
 
 			while (true) {
 				if (toResend.constFind(newId) == toResend.cend() && wereAcked.constFind(newId) == wereAcked.cend() && haveSent.constFind(newId) == haveSent.cend()) {
 					break;
 				}
-				mtpMsgId m = msgid();
+				const auto m = msgid();
 				if (m <= newId) break; // wtf
 
 				newId = m;
 			}
 
-			mtpRequestIdsMap::iterator i = toResend.find(oldMsgId);
+			const auto i = toResend.find(oldMsgId);
 			if (i != toResend.cend()) {
-				mtpRequestId req = i.value();
+				const auto req = i.value();
 				toResend.erase(i);
 				toResend.insert(newId, req);
 			}
 
-			mtpRequestIdsMap::iterator j = wereAcked.find(oldMsgId);
+			const auto j = wereAcked.find(oldMsgId);
 			if (j != wereAcked.cend()) {
-				mtpRequestId req = j.value();
+				const auto req = j.value();
 				wereAcked.erase(j);
 				wereAcked.insert(newId, req);
 			}
 
-			mtpRequestMap::iterator k = haveSent.find(oldMsgId);
+			const auto k = haveSent.find(oldMsgId);
 			if (k != haveSent.cend()) {
-				mtpRequest req = k.value();
+				const auto req = k.value();
 				haveSent.erase(k);
 				haveSent.insert(newId, req);
 			}
 
-			for (k = haveSent.begin(); k != haveSent.cend(); ++k) {
-				mtpRequest req(k.value());
-				if (mtpRequestData::isSentContainer(req)) {
-					mtpMsgId *ids = (mtpMsgId *)(req->data() + 8);
+			for (auto l = haveSent.begin(); l != haveSent.cend(); ++l) {
+				const auto req = l.value();
+				if (req.isSentContainer()) {
+					const auto ids = (mtpMsgId *)(req->data() + 8);
 					for (uint32 i = 0, l = (req->size() - 8) >> 1; i < l; ++i) {
 						if (ids[i] == oldMsgId) {
 							ids[i] = newId;
@@ -694,20 +693,20 @@ mtpMsgId ConnectionPrivate::replaceMsgId(mtpRequest &request, mtpMsgId newId) {
 				}
 			}
 		} else {
-			*(request->data() + 6) = sessionData->nextRequestSeqNumber(mtpRequestData::needAck(request));
+			*(request->data() + 6) = sessionData->nextRequestSeqNumber(request.needAck());
 		}
 		*(mtpMsgId*)(request->data() + 4) = newId;
 	}
 	return newId;
 }
 
-mtpMsgId ConnectionPrivate::placeToContainer(mtpRequest &toSendRequest, mtpMsgId &bigMsgId, mtpMsgId *&haveSentArr, mtpRequest &req) {
+mtpMsgId ConnectionPrivate::placeToContainer(SecureRequest &toSendRequest, mtpMsgId &bigMsgId, mtpMsgId *&haveSentArr, SecureRequest &req) {
 	mtpMsgId msgId = prepareToSend(req, bigMsgId);
 	if (msgId > bigMsgId) msgId = replaceMsgId(req, bigMsgId);
 	if (msgId >= bigMsgId) bigMsgId = msgid();
 	*(haveSentArr++) = msgId;
 
-	uint32 from = toSendRequest->size(), len = mtpRequestData::messageSize(req);
+	uint32 from = toSendRequest->size(), len = req.messageSize();
 	toSendRequest->resize(from + len);
 	memcpy(toSendRequest->data() + from, req->constData() + 4, len * sizeof(mtpPrime));
 
@@ -720,10 +719,10 @@ void ConnectionPrivate::tryToSend() {
 		return;
 	}
 
-	bool needsLayer = !_connectionOptions->inited;
-	int32 state = getState();
-	bool prependOnly = (state != ConnectedState);
-	mtpRequest pingRequest;
+	auto needsLayer = !_connectionOptions->inited;
+	auto state = getState();
+	auto prependOnly = (state != ConnectedState);
+	auto pingRequest = SecureRequest();
 	if (_shiftedDcId == BareDcId(_shiftedDcId)) { // main session
 		if (!prependOnly && !_pingIdToSend && !_pingId && _pingSendAt <= getms(true)) {
 			_pingIdToSend = rand_value<mtpPingId>();
@@ -731,17 +730,17 @@ void ConnectionPrivate::tryToSend() {
 	}
 	if (_pingIdToSend) {
 		if (prependOnly || _shiftedDcId != BareDcId(_shiftedDcId)) {
-			MTPPing ping(MTPping(MTP_long(_pingIdToSend)));
-			uint32 pingSize = ping.innerLength() >> 2; // copy from Session::send
-			pingRequest = mtpRequestData::prepare(pingSize);
-			ping.write(*pingRequest);
-			DEBUG_LOG(("MTP Info: sending ping, ping_id: %1").arg(_pingIdToSend));
+			pingRequest = SecureRequest::Serialize(MTPPing(
+				MTP_long(_pingIdToSend)
+			));
+			DEBUG_LOG(("MTP Info: sending ping, ping_id: %1"
+				).arg(_pingIdToSend));
 		} else {
-			MTPPing_delay_disconnect ping(MTP_long(_pingIdToSend), MTP_int(kPingDelayDisconnect));
-			uint32 pingSize = ping.innerLength() >> 2; // copy from Session::send
-			pingRequest = mtpRequestData::prepare(pingSize);
-			ping.write(*pingRequest);
-			DEBUG_LOG(("MTP Info: sending ping_delay_disconnect, ping_id: %1").arg(_pingIdToSend));
+			pingRequest = SecureRequest::Serialize(MTPPing_delay_disconnect(
+				MTP_long(_pingIdToSend),
+				MTP_int(kPingDelayDisconnect)));
+			DEBUG_LOG(("MTP Info: sending ping_delay_disconnect, "
+				"ping_id: %1").arg(_pingIdToSend));
 		}
 
 		pingRequest->msDate = getms(true); // > 0 - can send without container
@@ -763,24 +762,18 @@ void ConnectionPrivate::tryToSend() {
 		}
 	}
 
-	mtpRequest ackRequest, resendRequest, stateRequest, httpWaitRequest;
+	SecureRequest ackRequest, resendRequest, stateRequest, httpWaitRequest;
 	if (!prependOnly && !ackRequestData.isEmpty()) {
-		MTPMsgsAck ack(MTP_msgs_ack(MTP_vector<MTPlong>(ackRequestData)));
-
-		ackRequest = mtpRequestData::prepare(ack.innerLength() >> 2);
-		ack.write(*ackRequest);
-
+		ackRequest = SecureRequest::Serialize(MTPMsgsAck(
+			MTP_msgs_ack(MTP_vector<MTPlong>(ackRequestData))));
 		ackRequest->msDate = getms(true); // > 0 - can send without container
 		ackRequest->requestId = 0; // dont add to haveSent / wereAcked maps
 
 		ackRequestData.clear();
 	}
 	if (!prependOnly && !resendRequestData.isEmpty()) {
-		MTPMsgResendReq resend(MTP_msg_resend_req(MTP_vector<MTPlong>(resendRequestData)));
-
-		resendRequest = mtpRequestData::prepare(resend.innerLength() >> 2);
-		resend.write(*resendRequest);
-
+		resendRequest = SecureRequest::Serialize(MTPMsgResendReq(
+			MTP_msg_resend_req(MTP_vector<MTPlong>(resendRequestData))));
 		resendRequest->msDate = getms(true); // > 0 - can send without container
 		resendRequest->requestId = 0; // dont add to haveSent / wereAcked maps
 
@@ -790,36 +783,30 @@ void ConnectionPrivate::tryToSend() {
 		QVector<MTPlong> stateReq;
 		{
 			QWriteLocker locker(sessionData->stateRequestMutex());
-			mtpMsgIdsSet &ids(sessionData->stateRequestMap());
+			auto &ids = sessionData->stateRequestMap();
 			if (!ids.isEmpty()) {
 				stateReq.reserve(ids.size());
-				for (mtpMsgIdsSet::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) {
+				for (auto i = ids.cbegin(), e = ids.cend(); i != e; ++i) {
 					stateReq.push_back(MTP_long(i.key()));
 				}
 			}
 			ids.clear();
 		}
 		if (!stateReq.isEmpty()) {
-			MTPMsgsStateReq req(MTP_msgs_state_req(MTP_vector<MTPlong>(stateReq)));
-
-			stateRequest = mtpRequestData::prepare(req.innerLength() >> 2);
-			req.write(*stateRequest);
-
+			stateRequest = SecureRequest::Serialize(MTPMsgsStateReq(
+				MTP_msgs_state_req(MTP_vector<MTPlong>(stateReq))));
 			stateRequest->msDate = getms(true); // > 0 - can send without container
 			stateRequest->requestId = GetNextRequestId();// add to haveSent / wereAcked maps, but don't add to requestMap
 		}
 		if (_connection->usingHttpWait()) {
-			MTPHttpWait req(MTP_http_wait(MTP_int(100), MTP_int(30), MTP_int(25000)));
-
-			httpWaitRequest = mtpRequestData::prepare(req.innerLength() >> 2);
-			req.write(*httpWaitRequest);
-
+			httpWaitRequest = SecureRequest::Serialize(MTPHttpWait(
+				MTP_http_wait(MTP_int(100), MTP_int(30), MTP_int(25000))));
 			httpWaitRequest->msDate = getms(true); // > 0 - can send without container
 			httpWaitRequest->requestId = 0; // dont add to haveSent / wereAcked maps
 		}
 	}
 
-	MTPInitConnection<mtpRequest> initWrapper;
+	MTPInitConnection<SecureRequest> initWrapper;
 	int32 initSize = 0, initSizeInInts = 0;
 	if (needsLayer) {
 		Assert(_connectionOptions != nullptr);
@@ -845,8 +832,8 @@ void ConnectionPrivate::tryToSend() {
 				MTP_string(_connectionOptions->proxy.host),
 				MTP_int(_connectionOptions->proxy.port))
 			: MTPInputClientProxy();
-		using Flag = MTPInitConnection<mtpRequest>::Flag;
-		initWrapper = MTPInitConnection<mtpRequest>(
+		using Flag = MTPInitConnection<SecureRequest>::Flag;
+		initWrapper = MTPInitConnection<SecureRequest>(
 			MTP_flags(mtprotoProxy ? Flag::f_proxy : Flag(0)),
 			MTP_int(ApiId),
 			MTP_string(deviceModel),
@@ -856,17 +843,18 @@ void ConnectionPrivate::tryToSend() {
 			MTP_string(langPack),
 			MTP_string(cloudLangCode),
 			clientProxyFields,
-			mtpRequest());
+			SecureRequest());
 		initSizeInInts = (initWrapper.innerLength() >> 2) + 2;
 		initSize = initSizeInInts * sizeof(mtpPrime);
 	}
 
 	bool needAnyResponse = false;
-	mtpRequest toSendRequest;
+	SecureRequest toSendRequest;
 	{
 		QWriteLocker locker1(sessionData->toSendMutex());
 
-		mtpPreRequestMap toSendDummy, &toSend(prependOnly ? toSendDummy : sessionData->toSendMap());
+		auto toSendDummy = PreRequestMap();
+		auto &toSend = prependOnly ? toSendDummy : sessionData->toSendMap();
 		if (prependOnly) locker1.unlock();
 
 		uint32 toSendCount = toSend.size();
@@ -878,7 +866,7 @@ void ConnectionPrivate::tryToSend() {
 
 		if (!toSendCount) return; // nothing to send
 
-		mtpRequest first = pingRequest ? pingRequest : (ackRequest ? ackRequest : (resendRequest ? resendRequest : (stateRequest ? stateRequest : (httpWaitRequest ? httpWaitRequest : toSend.cbegin().value()))));
+		auto first = pingRequest ? pingRequest : (ackRequest ? ackRequest : (resendRequest ? resendRequest : (stateRequest ? stateRequest : (httpWaitRequest ? httpWaitRequest : toSend.cbegin().value()))));
 		if (toSendCount == 1 && first->msDate > 0) { // if can send without container
 			toSendRequest = first;
 			if (!prependOnly) {
@@ -895,32 +883,35 @@ void ConnectionPrivate::tryToSend() {
 			}
 
 			if (toSendRequest->requestId) {
-				if (mtpRequestData::needAck(toSendRequest)) {
-					toSendRequest->msDate = mtpRequestData::isStateRequest(toSendRequest) ? 0 : getms(true);
+				if (toSendRequest.needAck()) {
+					toSendRequest->msDate = toSendRequest.isStateRequest() ? 0 : getms(true);
 
 					QWriteLocker locker2(sessionData->haveSentMutex());
-					mtpRequestMap &haveSent(sessionData->haveSentMap());
+					auto &haveSent = sessionData->haveSentMap();
 					haveSent.insert(msgId, toSendRequest);
 
 					if (needsLayer && !toSendRequest->needsLayer) needsLayer = false;
 					if (toSendRequest->after) {
-						int32 toSendSize = toSendRequest.innerLength() >> 2;
-						mtpRequest wrappedRequest(mtpRequestData::prepare(toSendSize, toSendSize + 3)); // cons + msg_id
+						const auto toSendSize = toSendRequest.innerLength() >> 2;
+						auto wrappedRequest = SecureRequest::Prepare(
+							toSendSize,
+							toSendSize + 3);
 						wrappedRequest->resize(4);
 						memcpy(wrappedRequest->data(), toSendRequest->constData(), 4 * sizeof(mtpPrime));
 						wrapInvokeAfter(wrappedRequest, toSendRequest, haveSent);
-						toSendRequest = wrappedRequest;
+						toSendRequest = std::move(wrappedRequest);
 					}
 					if (needsLayer) {
-						int32 noWrapSize = (toSendRequest.innerLength() >> 2), toSendSize = noWrapSize + initSizeInInts;
-						mtpRequest wrappedRequest(mtpRequestData::prepare(toSendSize));
+						const auto noWrapSize = (toSendRequest.innerLength() >> 2);
+						const auto toSendSize = noWrapSize + initSizeInInts;
+						auto wrappedRequest = SecureRequest::Prepare(toSendSize);
 						memcpy(wrappedRequest->data(), toSendRequest->constData(), 7 * sizeof(mtpPrime)); // all except length
 						wrappedRequest->push_back(mtpc_invokeWithLayer);
 						wrappedRequest->push_back(internal::CurrentLayer);
 						initWrapper.write(*wrappedRequest);
 						wrappedRequest->resize(wrappedRequest->size() + noWrapSize);
 						memcpy(wrappedRequest->data() + wrappedRequest->size() - noWrapSize, toSendRequest->constData() + 8, noWrapSize * sizeof(mtpPrime));
-						toSendRequest = wrappedRequest;
+						toSendRequest = std::move(wrappedRequest);
 					}
 
 					needAnyResponse = true;
@@ -932,13 +923,13 @@ void ConnectionPrivate::tryToSend() {
 		} else { // send in container
 			bool willNeedInit = false;
 			uint32 containerSize = 1 + 1, idsWrapSize = (toSendCount << 1); // cons + vector size, idsWrapSize - size of "request-like" wrap for msgId vector
-			if (pingRequest) containerSize += mtpRequestData::messageSize(pingRequest);
-			if (ackRequest) containerSize += mtpRequestData::messageSize(ackRequest);
-			if (resendRequest) containerSize += mtpRequestData::messageSize(resendRequest);
-			if (stateRequest) containerSize += mtpRequestData::messageSize(stateRequest);
-			if (httpWaitRequest) containerSize += mtpRequestData::messageSize(httpWaitRequest);
-			for (mtpPreRequestMap::iterator i = toSend.begin(), e = toSend.end(); i != e; ++i) {
-				containerSize += mtpRequestData::messageSize(i.value());
+			if (pingRequest) containerSize += pingRequest.messageSize();
+			if (ackRequest) containerSize += ackRequest.messageSize();
+			if (resendRequest) containerSize += resendRequest.messageSize();
+			if (stateRequest) containerSize += stateRequest.messageSize();
+			if (httpWaitRequest) containerSize += httpWaitRequest.messageSize();
+			for (auto i = toSend.begin(), e = toSend.end(); i != e; ++i) {
+				containerSize += i.value().messageSize();
 				if (needsLayer && i.value()->needsLayer) {
 					containerSize += initSizeInInts;
 					willNeedInit = true;
@@ -951,22 +942,28 @@ void ConnectionPrivate::tryToSend() {
 				initSerialized.push_back(internal::CurrentLayer);
 				initWrapper.write(initSerialized);
 			}
-			toSendRequest = mtpRequestData::prepare(containerSize, containerSize + 3 * toSend.size()); // prepare container + each in invoke after
+			// prepare container + each in invoke after
+			toSendRequest = SecureRequest::Prepare(
+				containerSize,
+				containerSize + 3 * toSend.size());
 			toSendRequest->push_back(mtpc_msg_container);
 			toSendRequest->push_back(toSendCount);
 
 			mtpMsgId bigMsgId = msgid(); // check for a valid container
 
-			QWriteLocker locker2(sessionData->haveSentMutex()); // the fact of this lock is used in replaceMsgId()
-			mtpRequestMap &haveSent(sessionData->haveSentMap());
+			// the fact of this lock is used in replaceMsgId()
+			QWriteLocker locker2(sessionData->haveSentMutex());
+			auto &haveSent = sessionData->haveSentMap();
 
-			QWriteLocker locker3(sessionData->wereAckedMutex()); // the fact of this lock is used in replaceMsgId()
-			mtpRequestIdsMap &wereAcked(sessionData->wereAckedMap());
+			// the fact of this lock is used in replaceMsgId()
+			QWriteLocker locker3(sessionData->wereAckedMutex());
+			auto &wereAcked = sessionData->wereAckedMap();
 
-			mtpRequest haveSentIdsWrap(mtpRequestData::prepare(idsWrapSize)); // prepare "request-like" wrap for msgId vector
+			// prepare "request-like" wrap for msgId vector
+			auto haveSentIdsWrap = SecureRequest::Prepare(idsWrapSize);
 			haveSentIdsWrap->requestId = 0;
 			haveSentIdsWrap->resize(haveSentIdsWrap->size() + idsWrapSize);
-			mtpMsgId *haveSentArr = (mtpMsgId*)(haveSentIdsWrap->data() + 8);
+			auto haveSentArr = (mtpMsgId*)(haveSentIdsWrap->data() + 8);
 
 			if (pingRequest) {
 				_pingMsgId = placeToContainer(toSendRequest, bigMsgId, haveSentArr, pingRequest);
@@ -974,16 +971,16 @@ void ConnectionPrivate::tryToSend() {
 			} else if (resendRequest || stateRequest) {
 				needAnyResponse = true;
 			}
-			for (mtpPreRequestMap::iterator i = toSend.begin(), e = toSend.end(); i != e; ++i) {
-				mtpRequest &req(i.value());
-				mtpMsgId msgId = prepareToSend(req, bigMsgId);
+			for (auto i = toSend.begin(), e = toSend.end(); i != e; ++i) {
+				auto &req = i.value();
+				auto msgId = prepareToSend(req, bigMsgId);
 				if (msgId > bigMsgId) msgId = replaceMsgId(req, bigMsgId);
 				if (msgId >= bigMsgId) bigMsgId = msgid();
 				*(haveSentArr++) = msgId;
 				bool added = false;
 				if (req->requestId) {
-					if (mtpRequestData::needAck(req)) {
-						req->msDate = mtpRequestData::isStateRequest(req) ? 0 : getms(true);
+					if (req.needAck()) {
+						req->msDate = req.isStateRequest() ? 0 : getms(true);
 						int32 reqNeedsLayer = (needsLayer && req->needsLayer) ? toSendRequest->size() : 0;
 						if (req->after) {
 							wrapInvokeAfter(toSendRequest, req, haveSent, reqNeedsLayer ? initSizeInInts : 0);
@@ -993,7 +990,7 @@ void ConnectionPrivate::tryToSend() {
 							}
 							added = true;
 						} else if (reqNeedsLayer) {
-							toSendRequest->resize(reqNeedsLayer + initSizeInInts + mtpRequestData::messageSize(req));
+							toSendRequest->resize(reqNeedsLayer + initSizeInInts + req.messageSize());
 							memcpy(toSendRequest->data() + reqNeedsLayer, req->constData() + 4, 4 * sizeof(mtpPrime));
 							memcpy(toSendRequest->data() + reqNeedsLayer + 4, initSerialized.constData(), initSize);
 							memcpy(toSendRequest->data() + reqNeedsLayer + 4 + initSizeInInts, req->constData() + 8, req.innerLength());
@@ -1008,7 +1005,7 @@ void ConnectionPrivate::tryToSend() {
 					}
 				}
 				if (!added) {
-					uint32 from = toSendRequest->size(), len = mtpRequestData::messageSize(req);
+					uint32 from = toSendRequest->size(), len = req.messageSize();
 					toSendRequest->resize(from + len);
 					memcpy(toSendRequest->data() + from, req->constData() + 4, len * sizeof(mtpPrime));
 				}
@@ -1029,8 +1026,10 @@ void ConnectionPrivate::tryToSend() {
 			toSend.clear();
 		}
 	}
-	mtpRequestData::padding(toSendRequest);
-	sendRequest(toSendRequest, needAnyResponse, lockFinished);
+	sendSecureRequest(
+		std::move(toSendRequest),
+		needAnyResponse,
+		lockFinished);
 }
 
 void ConnectionPrivate::retryByTimer() {
@@ -1709,12 +1708,12 @@ ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPr
 				|| (errorCode == 64); // bad container
 			if (errorCode == 64) { // bad container!
 				if (Logs::DebugEnabled()) {
-					mtpRequest request;
+					SecureRequest request;
 					{
 						QWriteLocker locker(sessionData->haveSentMutex());
-						mtpRequestMap &haveSent(sessionData->haveSentMap());
+						auto &haveSent = sessionData->haveSentMap();
 
-						mtpRequestMap::const_iterator i = haveSent.constFind(resendId);
+						const auto i = haveSent.constFind(resendId);
 						if (i == haveSent.cend()) {
 							LOG(("Message Error: Container not found!"));
 						} else {
@@ -1722,9 +1721,9 @@ ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPr
 						}
 					}
 					if (request) {
-						if (mtpRequestData::isSentContainer(request)) {
+						if (request.isSentContainer()) {
 							QStringList lst;
-							const auto ids = (const mtpMsgId *)(request->constData() + 8);
+							const auto ids = (const mtpMsgId*)(request->constData() + 8);
 							for (uint32 i = 0, l = (request->size() - 8) >> 1; i < l; ++i) {
 								lst.push_back(QString::number(ids[i]));
 							}
@@ -1834,8 +1833,8 @@ ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPr
 			auto maxRecv = receivedIds.max();
 
 			QReadLocker locker(sessionData->wereAckedMutex());
-			const mtpRequestIdsMap &wereAcked(sessionData->wereAckedMap());
-			mtpRequestIdsMap::const_iterator wereAckedEnd(wereAcked.cend());
+			const auto &wereAcked = sessionData->wereAckedMap();
+			const auto wereAckedEnd = wereAcked.cend();
 
 			for (uint32 i = 0, l = idsCount; i < l; ++i) {
 				char state = 0;
@@ -1875,11 +1874,11 @@ ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPr
 		auto &states = data.vinfo.v;
 
 		DEBUG_LOG(("Message Info: msg state received, msgId %1, reqMsgId: %2, HEX states %3").arg(msgId).arg(reqMsgId).arg(Logs::mb(states.data(), states.length()).str()));
-		mtpRequest requestBuffer;
+		SecureRequest requestBuffer;
 		{ // find this request in session-shared sent requests map
 			QReadLocker locker(sessionData->haveSentMutex());
-			const mtpRequestMap &haveSent(sessionData->haveSentMap());
-			mtpRequestMap::const_iterator replyTo = haveSent.constFind(reqMsgId);
+			const auto &haveSent = sessionData->haveSentMap();
+			const auto replyTo = haveSent.constFind(reqMsgId);
 			if (replyTo == haveSent.cend()) { // do not look in toResend, because we do not resend msgs_state_req requests
 				DEBUG_LOG(("Message Error: such message was not sent recently %1").arg(reqMsgId));
 				return (badTime ? HandleResult::Ignored : HandleResult::Success);
@@ -2087,9 +2086,9 @@ ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPr
 		QVector<quint64> toResend;
 		{
 			QReadLocker locker(sessionData->haveSentMutex());
-			const mtpRequestMap &haveSent(sessionData->haveSentMap());
+			const auto &haveSent = sessionData->haveSentMap();
 			toResend.reserve(haveSent.size());
-			for (mtpRequestMap::const_iterator i = haveSent.cbegin(), e = haveSent.cend(); i != e; ++i) {
+			for (auto i = haveSent.cbegin(), e = haveSent.cend(); i != e; ++i) {
 				if (i.key() >= firstMsgId) break;
 				if (i.value()->requestId) toResend.push_back(i.key());
 			}
@@ -2247,15 +2246,15 @@ void ConnectionPrivate::requestsAcked(const QVector<MTPlong> &ids, bool byRespon
 	QVector<MTPlong> toAckMore;
 	{
 		QWriteLocker locker1(sessionData->wereAckedMutex());
-		mtpRequestIdsMap &wereAcked(sessionData->wereAckedMap());
+		auto &wereAcked = sessionData->wereAckedMap();
 
 		{
 			QWriteLocker locker2(sessionData->haveSentMutex());
-			mtpRequestMap &haveSent(sessionData->haveSentMap());
+			auto &haveSent = sessionData->haveSentMap();
 
 			for (uint32 i = 0; i < idsCount; ++i) {
 				mtpMsgId msgId = ids[i].v;
-				mtpRequestMap::iterator req = haveSent.find(msgId);
+				const auto req = haveSent.find(msgId);
 				if (req != haveSent.cend()) {
 					if (!req.value()->msDate) {
 						DEBUG_LOG(("Message Info: container ack received, msgId %1").arg(ids[i].v));
@@ -2282,18 +2281,18 @@ void ConnectionPrivate::requestsAcked(const QVector<MTPlong> &ids, bool byRespon
 				} else {
 					DEBUG_LOG(("Message Info: msgId %1 was not found in recent sent, while acking requests, searching in resend...").arg(msgId));
 					QWriteLocker locker3(sessionData->toResendMutex());
-					mtpRequestIdsMap &toResend(sessionData->toResendMap());
-					mtpRequestIdsMap::iterator reqIt = toResend.find(msgId);
+					auto &toResend = sessionData->toResendMap();
+					const auto reqIt = toResend.find(msgId);
 					if (reqIt != toResend.cend()) {
-						mtpRequestId reqId = reqIt.value();
+						const auto reqId = reqIt.value();
 						bool moveToAcked = byResponse;
 						if (!moveToAcked) { // ignore ACK, if we need a response (if we have a handler)
 							moveToAcked = !_instance->hasCallbacks(reqId);
 						}
 						if (moveToAcked) {
 							QWriteLocker locker4(sessionData->toSendMutex());
-							mtpPreRequestMap &toSend(sessionData->toSendMap());
-							mtpPreRequestMap::iterator req = toSend.find(reqId);
+							auto &toSend = sessionData->toSendMap();
+							const auto req = toSend.find(reqId);
 							if (req != toSend.cend()) {
 								wereAcked.insert(msgId, req.value()->requestId);
 								if (req.value()->requestId != reqId) {
@@ -2356,13 +2355,13 @@ void ConnectionPrivate::handleMsgsStates(const QVector<MTPlong> &ids, const QByt
 		uint64 requestMsgId = ids[i].v;
 		{
 			QReadLocker locker(sessionData->haveSentMutex());
-			const mtpRequestMap &haveSent(sessionData->haveSentMap());
-			mtpRequestMap::const_iterator haveSentEnd = haveSent.cend();
+			const auto &haveSent = sessionData->haveSentMap();
+			const auto haveSentEnd = haveSent.cend();
 			if (haveSent.find(requestMsgId) == haveSentEnd) {
 				DEBUG_LOG(("Message Info: state was received for msgId %1, but request is not found, looking in resent requests...").arg(requestMsgId));
 				QWriteLocker locker2(sessionData->toResendMutex());
-				mtpRequestIdsMap &toResend(sessionData->toResendMap());
-				mtpRequestIdsMap::iterator reqIt = toResend.find(requestMsgId);
+				auto &toResend = sessionData->toResendMap();
+				const auto reqIt = toResend.find(requestMsgId);
 				if (reqIt != toResend.cend()) {
 					if ((state & 0x07) != 0x04) { // was received
 						DEBUG_LOG(("Message Info: state was received for msgId %1, state %2, already resending in container").arg(requestMsgId).arg((int32)state));
@@ -2532,7 +2531,6 @@ void ConnectionPrivate::updateAuthKey() 	{
 
 	_authKeyData = std::make_unique<ConnectionPrivate::AuthKeyCreateData>();
 	_authKeyStrings = std::make_unique<ConnectionPrivate::AuthKeyCreateStrings>();
-	_authKeyData->req_num = 0;
 	_authKeyData->nonce = rand_value<MTPint128>();
 
 	MTPReq_pq_multi req_pq;
@@ -2544,7 +2542,7 @@ void ConnectionPrivate::updateAuthKey() 	{
 
 	DEBUG_LOG(("AuthKey Info: sending Req_pq..."));
 	lockFinished.unlock();
-	sendRequestNotSecure(req_pq);
+	sendNotSecureRequest(req_pq);
 }
 
 void ConnectionPrivate::clearMessages() {
@@ -2558,7 +2556,7 @@ void ConnectionPrivate::pqAnswered() {
 	DEBUG_LOG(("AuthKey Info: receiving Req_pq answer..."));
 
 	MTPReq_pq::ResponseType res_pq;
-	if (!readResponseNotSecure(res_pq)) {
+	if (!readNotSecureResponse(res_pq)) {
 		return restart();
 	}
 
@@ -2619,7 +2617,7 @@ void ConnectionPrivate::pqAnswered() {
 	req_DH_params.vp = p_q_inner.c_p_q_inner_data_dc().vp;
 	req_DH_params.vq = p_q_inner.c_p_q_inner_data_dc().vq;
 	req_DH_params.vencrypted_data = MTP_bytes(dhEncString);
-	sendRequestNotSecure(req_DH_params);
+	sendNotSecureRequest(req_DH_params);
 }
 
 bytes::vector ConnectionPrivate::encryptPQInnerRSA(
@@ -2658,7 +2656,7 @@ void ConnectionPrivate::dhParamsAnswered() {
 	DEBUG_LOG(("AuthKey Info: receiving Req_DH_params answer..."));
 
 	MTPReq_DH_params::ResponseType res_DH_params;
-	if (!readResponseNotSecure(res_DH_params)) {
+	if (!readNotSecureResponse(res_DH_params)) {
 		return restart();
 	}
 
@@ -2808,7 +2806,7 @@ void ConnectionPrivate::dhClientParamsSend() {
 	req_client_DH_params.vencrypted_data = MTP_string(std::move(sdhEncString));
 
 	DEBUG_LOG(("AuthKey Info: sending Req_client_DH_params..."));
-	sendRequestNotSecure(req_client_DH_params);
+	sendNotSecureRequest(req_client_DH_params);
 }
 
 std::string ConnectionPrivate::encryptClientDHInner(const MTPClient_DH_Inner_Data &data) {
@@ -2845,7 +2843,7 @@ void ConnectionPrivate::dhClientParamsAnswered() {
 	DEBUG_LOG(("AuthKey Info: receiving Req_client_DH_params answer..."));
 
 	MTPSet_client_DH_params::ResponseType res_client_DH_params;
-	if (!readResponseNotSecure(res_client_DH_params)) {
+	if (!readNotSecureResponse(res_client_DH_params)) {
 		lockFinished.unlock();
 		return restart();
 	}
@@ -3045,38 +3043,23 @@ void ConnectionPrivate::handleError(int errorCode) {
 void ConnectionPrivate::onReadyData() {
 }
 
-template <typename TRequest>
-void ConnectionPrivate::sendRequestNotSecure(const TRequest &request) {
-	try {
-		mtpBuffer buffer;
-		uint32 requestSize = request.innerLength() >> 2;
+template <typename Request>
+void ConnectionPrivate::sendNotSecureRequest(const Request &request) {
+	auto packet = _connection->prepareNotSecurePacket(request);
 
-		buffer.resize(0);
-		buffer.reserve(8 + requestSize);
-		buffer.push_back(0); // tcp packet len
-		buffer.push_back(0); // tcp packet num
-		buffer.push_back(0);
-		buffer.push_back(0);
-		buffer.push_back(_authKeyData->req_num);
-		buffer.push_back(unixtime());
-		buffer.push_back(requestSize * 4);
-		request.write(buffer);
-		buffer.push_back(0); // tcp crc32 hash
-		++_authKeyData->msgs_sent;
+	DEBUG_LOG(("AuthKey Info: sending request, size: %1, time: %3"
+		).arg(packet.size() - 8
+		).arg(packet[5]));
 
-		DEBUG_LOG(("AuthKey Info: sending request, size: %1, num: %2, time: %3").arg(requestSize).arg(_authKeyData->req_num).arg(buffer[5]));
+	const auto bytesSize = packet.size() * sizeof(mtpPrime);
 
-		_connection->sendData(buffer);
+	_connection->sendData(std::move(packet));
 
-		onSentSome(buffer.size() * sizeof(mtpPrime));
-
-	} catch (Exception &) {
-		return restart();
-	}
+	onSentSome(bytesSize);
 }
 
-template <typename TResponse>
-bool ConnectionPrivate::readResponseNotSecure(TResponse &response) {
+template <typename Response>
+bool ConnectionPrivate::readNotSecureResponse(Response &response) {
 	onReceivedSome();
 
 	try {
@@ -3114,12 +3097,20 @@ bool ConnectionPrivate::readResponseNotSecure(TResponse &response) {
 	return true;
 }
 
-bool ConnectionPrivate::sendRequest(mtpRequest &request, bool needAnyResponse, QReadLocker &lockFinished) {
+bool ConnectionPrivate::sendSecureRequest(
+		SecureRequest &&request,
+		bool needAnyResponse,
+		QReadLocker &lockFinished) {
+	request.addPadding(_connection->requiresExtendedPadding());
 	uint32 fullSize = request->size();
-	if (fullSize < 9) return false;
+	if (fullSize < 9) {
+		return false;
+	}
 
-	auto messageSize = mtpRequestData::messageSize(request);
-	if (messageSize < 5 || fullSize < messageSize + 4) return false;
+	auto messageSize = request.messageSize();
+	if (messageSize < 5 || fullSize < messageSize + 4) {
+		return false;
+	}
 
 	auto lock = ReadLockerAttempt(sessionData->keyMutex());
 	if (!lock) {
@@ -3153,14 +3144,21 @@ bool ConnectionPrivate::sendRequest(mtpRequest &request, bool needAnyResponse, Q
 
 	uchar encryptedSHA[20];
 	MTPint128 &msgKey(*(MTPint128*)(encryptedSHA + 4));
-	hashSha1(request->constData(), (fullSize - padding) * sizeof(mtpPrime), encryptedSHA);
+	hashSha1(
+		request->constData(),
+		(fullSize - padding) * sizeof(mtpPrime),
+		encryptedSHA);
 
-	mtpBuffer result;
-	result.resize(9 + fullSize);
-	*((uint64*)&result[2]) = keyId;
-	*((MTPint128*)&result[4]) = msgKey;
+	auto packet = _connection->prepareSecurePacket(keyId, msgKey, fullSize);
+	const auto prefix = packet.size();
+	packet.resize(prefix + fullSize);
 
-	aesIgeEncrypt_oldmtp(request->constData(), &result[8], fullSize * sizeof(mtpPrime), key, msgKey);
+	aesIgeEncrypt_oldmtp(
+		request->constData(),
+		&packet[prefix],
+		fullSize * sizeof(mtpPrime),
+		key,
+		msgKey);
 #else // TDESKTOP_MTPROTO_OLD
 	uchar encryptedSHA256[32];
 	MTPint128 &msgKey(*(MTPint128*)(encryptedSHA256 + 8));
@@ -3171,21 +3169,25 @@ bool ConnectionPrivate::sendRequest(mtpRequest &request, bool needAnyResponse, Q
 	SHA256_Update(&msgKeyLargeContext, request->constData(), fullSize * sizeof(mtpPrime));
 	SHA256_Final(encryptedSHA256, &msgKeyLargeContext);
 
-	mtpBuffer result;
-	result.resize(9 + fullSize);
-	*((uint64*)&result[2]) = keyId;
-	*((MTPint128*)&result[4]) = msgKey;
+	auto packet = _connection->prepareSecurePacket(keyId, msgKey, fullSize);
+	const auto prefix = packet.size();
+	packet.resize(prefix + fullSize);
 
-	aesIgeEncrypt(request->constData(), &result[8], fullSize * sizeof(mtpPrime), key, msgKey);
+	aesIgeEncrypt(
+		request->constData(),
+		&packet[prefix],
+		fullSize * sizeof(mtpPrime),
+		key,
+		msgKey);
 #endif // TDESKTOP_MTPROTO_OLD
 
 	DEBUG_LOG(("MTP Info: sending request, size: %1, num: %2, time: %3").arg(fullSize + 6).arg((*request)[4]).arg((*request)[5]));
 
 	_connection->setSentEncrypted();
-	_connection->sendData(result);
+	_connection->sendData(std::move(packet));
 
 	if (needAnyResponse) {
-		onSentSome(result.size() * sizeof(mtpPrime));
+		onSentSome((prefix + fullSize) * sizeof(mtpPrime));
 	}
 
 	return true;
@@ -3195,20 +3197,24 @@ mtpRequestId ConnectionPrivate::wasSent(mtpMsgId msgId) const {
 	if (msgId == _pingMsgId) return mtpRequestId(0xFFFFFFFF);
 	{
 		QReadLocker locker(sessionData->haveSentMutex());
-		const mtpRequestMap &haveSent(sessionData->haveSentMap());
-		mtpRequestMap::const_iterator i = haveSent.constFind(msgId);
-		if (i != haveSent.cend()) return i.value()->requestId ? i.value()->requestId : mtpRequestId(0xFFFFFFFF);
+		const auto &haveSent = sessionData->haveSentMap();
+		const auto i = haveSent.constFind(msgId);
+		if (i != haveSent.cend()) {
+			return i.value()->requestId
+				? i.value()->requestId
+				: mtpRequestId(0xFFFFFFFF);
+		}
 	}
 	{
 		QReadLocker locker(sessionData->toResendMutex());
-		const mtpRequestIdsMap &toResend(sessionData->toResendMap());
-		mtpRequestIdsMap::const_iterator i = toResend.constFind(msgId);
+		const auto &toResend = sessionData->toResendMap();
+		const auto i = toResend.constFind(msgId);
 		if (i != toResend.cend()) return i.value();
 	}
 	{
 		QReadLocker locker(sessionData->wereAckedMutex());
-		const mtpRequestIdsMap &wereAcked(sessionData->wereAckedMap());
-		mtpRequestIdsMap::const_iterator i = wereAcked.constFind(msgId);
+		const auto &wereAcked = sessionData->wereAckedMap();
+		const auto i = wereAcked.constFind(msgId);
 		if (i != wereAcked.cend()) return i.value();
 	}
 	return 0;
diff --git a/Telegram/SourceFiles/mtproto/connection.h b/Telegram/SourceFiles/mtproto/connection.h
index 2f4c8b9b86..10a7ffe7c7 100644
--- a/Telegram/SourceFiles/mtproto/connection.h
+++ b/Telegram/SourceFiles/mtproto/connection.h
@@ -169,11 +169,18 @@ private:
 	void removeTestConnection(not_null<AbstractConnection*> connection);
 	int16 getProtocolDcId() const;
 
-	mtpMsgId placeToContainer(mtpRequest &toSendRequest, mtpMsgId &bigMsgId, mtpMsgId *&haveSentArr, mtpRequest &req);
-	mtpMsgId prepareToSend(mtpRequest &request, mtpMsgId currentLastId);
-	mtpMsgId replaceMsgId(mtpRequest &request, mtpMsgId newId);
+	mtpMsgId placeToContainer(
+		SecureRequest &toSendRequest,
+		mtpMsgId &bigMsgId,
+		mtpMsgId *&haveSentArr,
+		SecureRequest &req);
+	mtpMsgId prepareToSend(SecureRequest &request, mtpMsgId currentLastId);
+	mtpMsgId replaceMsgId(SecureRequest &request, mtpMsgId newId);
 
-	bool sendRequest(mtpRequest &request, bool needAnyResponse, QReadLocker &lockFinished);
+	bool sendSecureRequest(
+		SecureRequest &&request,
+		bool needAnyResponse,
+		QReadLocker &lockFinished);
 	mtpRequestId wasSent(mtpMsgId msgId) const;
 
 	enum class HandleResult {
@@ -207,11 +214,11 @@ private:
 	void resend(quint64 msgId, qint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false);
 	void resendMany(QVector<quint64> msgIds, qint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false);
 
-	template <typename TRequest>
-	void sendRequestNotSecure(const TRequest &request);
+	template <typename Request>
+	void sendNotSecureRequest(const Request &request);
 
-	template <typename TResponse>
-	bool readResponseNotSecure(TResponse &response);
+	template <typename Response>
+	bool readNotSecureResponse(Response &response);
 
 	not_null<Instance*> _instance;
 	DcType _dcType = DcType::Regular;
@@ -281,9 +288,6 @@ private:
 		uchar aesKey[32] = { 0 };
 		uchar aesIV[32] = { 0 };
 		MTPlong auth_key_hash;
-
-		uint32 req_num = 0; // sent not encrypted request number
-		uint32 msgs_sent = 0;
 	};
 	struct AuthKeyCreateStrings {
 		bytes::vector dh_prime;
diff --git a/Telegram/SourceFiles/mtproto/connection_abstract.cpp b/Telegram/SourceFiles/mtproto/connection_abstract.cpp
index b4b8ff0776..2e1bdd75d0 100644
--- a/Telegram/SourceFiles/mtproto/connection_abstract.cpp
+++ b/Telegram/SourceFiles/mtproto/connection_abstract.cpp
@@ -107,30 +107,34 @@ ConnectionPointer::~ConnectionPointer() {
 	reset();
 }
 
-AbstractConnection::~AbstractConnection() {
+mtpBuffer AbstractConnection::prepareSecurePacket(
+		uint64 keyId,
+		MTPint128 msgKey,
+		uint32 size) const {
+	auto result = mtpBuffer();
+	constexpr auto kTcpPrefixInts = 2;
+	constexpr auto kAuthKeyIdPosition = kTcpPrefixInts;
+	constexpr auto kAuthKeyIdInts = 2;
+	constexpr auto kMessageKeyPosition = kAuthKeyIdPosition
+		+ kAuthKeyIdInts;
+	constexpr auto kMessageKeyInts = 4;
+	constexpr auto kPrefixInts = kTcpPrefixInts
+		+ kAuthKeyIdInts
+		+ kMessageKeyInts;
+	constexpr auto kTcpPostfixInts = 4;
+	result.reserve(kPrefixInts + size + kTcpPostfixInts);
+	result.resize(kPrefixInts);
+	*reinterpret_cast<uint64*>(&result[kAuthKeyIdPosition]) = keyId;
+	*reinterpret_cast<MTPint128*>(&result[kMessageKeyPosition]) = msgKey;
+	return result;
 }
 
-mtpBuffer AbstractConnection::preparePQFake(const MTPint128 &nonce) {
-	MTPReq_pq req_pq(nonce);
-	mtpBuffer buffer;
-	uint32 requestSize = req_pq.innerLength() >> 2;
-
-	buffer.resize(0);
-	buffer.reserve(8 + requestSize);
-	buffer.push_back(0); // tcp packet len
-	buffer.push_back(0); // tcp packet num
-	buffer.push_back(0);
-	buffer.push_back(0);
-	buffer.push_back(0);
-	buffer.push_back(unixtime());
-	buffer.push_back(requestSize * 4);
-	req_pq.write(buffer);
-	buffer.push_back(0); // tcp crc32 hash
-
-	return buffer;
+mtpBuffer AbstractConnection::preparePQFake(const MTPint128 &nonce) const {
+	return prepareNotSecurePacket(MTPReq_pq(nonce));
 }
 
-MTPResPQ AbstractConnection::readPQFakeReply(const mtpBuffer &buffer) {
+MTPResPQ AbstractConnection::readPQFakeReply(
+		const mtpBuffer &buffer) const {
 	const mtpPrime *answer(buffer.constData());
 	uint32 len = buffer.size();
 	if (len < 5) {
diff --git a/Telegram/SourceFiles/mtproto/connection_abstract.h b/Telegram/SourceFiles/mtproto/connection_abstract.h
index d7bb8dd09c..02c1b898b5 100644
--- a/Telegram/SourceFiles/mtproto/connection_abstract.h
+++ b/Telegram/SourceFiles/mtproto/connection_abstract.h
@@ -59,7 +59,7 @@ public:
 		const ProxyData &proxy);
 	AbstractConnection(const AbstractConnection &other) = delete;
 	AbstractConnection &operator=(const AbstractConnection &other) = delete;
-	virtual ~AbstractConnection() = 0;
+	virtual ~AbstractConnection() = default;
 
 	// virtual constructor
 	static ConnectionPointer Create(
@@ -72,7 +72,7 @@ public:
 
 	virtual TimeMs pingTime() const = 0;
 	virtual TimeMs fullConnectTimeout() const = 0;
-	virtual void sendData(mtpBuffer &buffer) = 0; // has size + 3, buffer[0] = len, buffer[1] = packetnum, buffer[last] = crc32
+	virtual void sendData(mtpBuffer &&buffer) = 0;
 	virtual void disconnectFromServer() = 0;
 	virtual void connectToServer(
 		const QString &ip,
@@ -86,6 +86,9 @@ public:
 	virtual bool needHttpWait() {
 		return false;
 	}
+	virtual bool requiresExtendedPadding() const {
+		return false;
+	}
 
 	virtual int32 debugState() const = 0;
 
@@ -101,6 +104,13 @@ public:
 		return _receivedQueue;
 	}
 
+	template <typename Request>
+	mtpBuffer prepareNotSecurePacket(const Request &request) const;
+	mtpBuffer prepareSecurePacket(
+		uint64 keyId,
+		MTPint128 msgKey,
+		uint32 size) const;
+
 	// Used to emit error(...) with no real code from the server.
 	static constexpr auto kErrorCodeOther = -499;
 
@@ -121,10 +131,49 @@ protected:
 
 	// first we always send fake MTPReq_pq to see if connection works at all
 	// we send them simultaneously through TCP/HTTP/IPv4/IPv6 to choose the working one
-	static mtpBuffer preparePQFake(const MTPint128 &nonce);
-	static MTPResPQ readPQFakeReply(const mtpBuffer &buffer);
+	mtpBuffer preparePQFake(const MTPint128 &nonce) const;
+	MTPResPQ readPQFakeReply(const mtpBuffer &buffer) const;
 
 };
 
+template <typename Request>
+mtpBuffer AbstractConnection::prepareNotSecurePacket(const Request &request) const {
+	const auto intsSize = request.innerLength() >> 2;
+	const auto intsPadding = requiresExtendedPadding()
+		? uint32(rand_value<uchar>() & 0x3F)
+		: 0;
+
+	auto result = mtpBuffer();
+	constexpr auto kTcpPrefixInts = 2;
+	constexpr auto kAuthKeyIdInts = 2;
+	constexpr auto kMessageIdInts = 2;
+	constexpr auto kMessageLengthInts = 1;
+	constexpr auto kPrefixInts = kTcpPrefixInts
+		+ kAuthKeyIdInts
+		+ kMessageIdInts
+		+ kMessageLengthInts;
+	constexpr auto kTcpPostfixInts = 4;
+
+	result.reserve(kPrefixInts + intsSize + intsPadding + kTcpPostfixInts);
+	result.resize(kPrefixInts);
+
+	const auto messageId = &result[kTcpPrefixInts + kAuthKeyIdInts];
+	*reinterpret_cast<mtpMsgId*>(messageId) = msgid();
+
+	request.write(result);
+
+	const auto messageLength = messageId + kMessageIdInts;
+	*messageLength = (result.size() - kPrefixInts + intsPadding) << 2;
+
+	if (intsPadding > 0) {
+		result.resize(result.size() + intsPadding);
+		memset_rand(
+			result.data() + result.size() - intsPadding,
+			intsPadding * sizeof(mtpPrime));
+	}
+
+	return result;
+}
+
 } // namespace internal
 } // namespace MTP
diff --git a/Telegram/SourceFiles/mtproto/connection_http.cpp b/Telegram/SourceFiles/mtproto/connection_http.cpp
index 7029dacf16..7882a09cef 100644
--- a/Telegram/SourceFiles/mtproto/connection_http.cpp
+++ b/Telegram/SourceFiles/mtproto/connection_http.cpp
@@ -29,17 +29,14 @@ ConnectionPointer HttpConnection::clone(const ProxyData &proxy) {
 	return ConnectionPointer::New<HttpConnection>(thread(), proxy);
 }
 
-void HttpConnection::sendData(mtpBuffer &buffer) {
-	if (_status == Status::Finished) return;
+void HttpConnection::sendData(mtpBuffer &&buffer) {
+	Expects(buffer.size() > 2);
 
-	if (buffer.size() < 3) {
-		LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime)));
-		TCP_LOG(("TCP Error: bad packet %1").arg(Logs::mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str()));
-		emit error(kErrorCodeOther);
+	if (_status == Status::Finished) {
 		return;
 	}
 
-	int32 requestSize = (buffer.size() - 3) * sizeof(mtpPrime);
+	int32 requestSize = (buffer.size() - 2) * sizeof(mtpPrime);
 
 	QNetworkRequest request(url());
 	request.setHeader(QNetworkRequest::ContentLengthHeader, QVariant(requestSize));
@@ -77,7 +74,7 @@ void HttpConnection::connectToServer(
 		this,
 		&HttpConnection::requestFinished);
 
-	mtpBuffer buffer(preparePQFake(_checkNonce));
+	auto buffer = preparePQFake(_checkNonce);
 
 	DEBUG_LOG(("HTTP Info: "
 		"dc:%1 - Sending fake req_pq to '%2'"
@@ -85,7 +82,7 @@ void HttpConnection::connectToServer(
 		).arg(url().toDisplayString()));
 
 	_pingTime = getms();
-	sendData(buffer);
+	sendData(std::move(buffer));
 }
 
 mtpBuffer HttpConnection::handleResponse(QNetworkReply *reply) {
diff --git a/Telegram/SourceFiles/mtproto/connection_http.h b/Telegram/SourceFiles/mtproto/connection_http.h
index 3a8ed3e922..6e92d20f7f 100644
--- a/Telegram/SourceFiles/mtproto/connection_http.h
+++ b/Telegram/SourceFiles/mtproto/connection_http.h
@@ -20,7 +20,7 @@ public:
 
 	TimeMs pingTime() const override;
 	TimeMs fullConnectTimeout() const override;
-	void sendData(mtpBuffer &buffer) override;
+	void sendData(mtpBuffer &&buffer) override;
 	void disconnectFromServer() override;
 	void connectToServer(
 		const QString &address,
diff --git a/Telegram/SourceFiles/mtproto/connection_resolving.cpp b/Telegram/SourceFiles/mtproto/connection_resolving.cpp
index 5f528a7a05..b32a2e2756 100644
--- a/Telegram/SourceFiles/mtproto/connection_resolving.cpp
+++ b/Telegram/SourceFiles/mtproto/connection_resolving.cpp
@@ -189,10 +189,16 @@ TimeMs ResolvingConnection::fullConnectTimeout() const {
 	return kOneConnectionTimeout * qMax(int(_proxy.resolvedIPs.size()), 1);
 }
 
-void ResolvingConnection::sendData(mtpBuffer &buffer) {
+void ResolvingConnection::sendData(mtpBuffer &&buffer) {
 	Expects(_child != nullptr);
 
-	_child->sendData(buffer);
+	_child->sendData(std::move(buffer));
+}
+
+bool ResolvingConnection::requiresExtendedPadding() const {
+	Expects(_child != nullptr);
+
+	return _child->requiresExtendedPadding();
 }
 
 void ResolvingConnection::disconnectFromServer() {
diff --git a/Telegram/SourceFiles/mtproto/connection_resolving.h b/Telegram/SourceFiles/mtproto/connection_resolving.h
index ed80ba3d55..bd8df9aa49 100644
--- a/Telegram/SourceFiles/mtproto/connection_resolving.h
+++ b/Telegram/SourceFiles/mtproto/connection_resolving.h
@@ -26,7 +26,7 @@ public:
 
 	TimeMs pingTime() const override;
 	TimeMs fullConnectTimeout() const override;
-	void sendData(mtpBuffer &buffer) override;
+	void sendData(mtpBuffer &&buffer) override;
 	void disconnectFromServer() override;
 	void connectToServer(
 		const QString &address,
@@ -34,6 +34,7 @@ public:
 		const bytes::vector &protocolSecret,
 		int16 protocolDcId) override;
 	bool isConnected() const override;
+	bool requiresExtendedPadding() const override;
 
 	int32 debugState() const override;
 
diff --git a/Telegram/SourceFiles/mtproto/connection_tcp.cpp b/Telegram/SourceFiles/mtproto/connection_tcp.cpp
index 0b6ce1af67..978c08623e 100644
--- a/Telegram/SourceFiles/mtproto/connection_tcp.cpp
+++ b/Telegram/SourceFiles/mtproto/connection_tcp.cpp
@@ -37,6 +37,147 @@ const auto QTcpSocket_error = ErrorSignal(&QAbstractSocket::error);
 
 } // namespace
 
+class TcpConnection::Protocol {
+public:
+	static std::unique_ptr<Protocol> Create(bytes::vector &&secret);
+
+	virtual uint32 id() const = 0;
+	virtual bool requiresExtendedPadding() const = 0;
+	virtual bool supportsArbitraryLength() const = 0;
+	virtual void prepareKey(bytes::span key, bytes::const_span source) = 0;
+	virtual bytes::span finalizePacket(mtpBuffer &buffer) = 0;
+
+	virtual ~Protocol() = default;
+
+private:
+	class Version0;
+	class Version1;
+	class VersionD;
+
+};
+
+class TcpConnection::Protocol::Version0 : public Protocol {
+public:
+	uint32 id() const override;
+	bool requiresExtendedPadding() const override;
+	bool supportsArbitraryLength() const override;
+	void prepareKey(bytes::span key, bytes::const_span source) override;
+	bytes::span finalizePacket(mtpBuffer &buffer) override;
+
+};
+
+uint32 TcpConnection::Protocol::Version0::id() const {
+	return 0xEFEFEFEFU;
+}
+
+bool TcpConnection::Protocol::Version0::requiresExtendedPadding() const {
+	return false;
+}
+
+bool TcpConnection::Protocol::Version0::supportsArbitraryLength() const {
+	return false;
+}
+
+void TcpConnection::Protocol::Version0::prepareKey(
+		bytes::span key,
+		bytes::const_span source) {
+	bytes::copy(key, source);
+}
+
+bytes::span TcpConnection::Protocol::Version0::finalizePacket(
+		mtpBuffer &buffer) {
+	Expects(buffer.size() > 2 && buffer.size() < 0x1000003U);
+
+	const auto intsSize = uint32(buffer.size() - 2);
+	const auto bytesSize = intsSize * sizeof(mtpPrime);
+	const auto data = reinterpret_cast<uchar*>(&buffer[0]);
+	const auto added = [&] {
+		if (intsSize < 0x7F) {
+			data[7] = uchar(intsSize);
+			return 1;
+		}
+		data[4] = uchar(0x7F);
+		data[5] = uchar(intsSize & 0xFF);
+		data[6] = uchar((intsSize >> 8) & 0xFF);
+		data[7] = uchar((intsSize >> 16) & 0xFF);
+		return 4;
+	}();
+	return bytes::make_span(buffer).subspan(8 - added, added + bytesSize);
+}
+
+class TcpConnection::Protocol::Version1 : public Version0 {
+public:
+	explicit Version1(bytes::vector &&secret);
+
+	bool requiresExtendedPadding() const override;
+	void prepareKey(bytes::span key, bytes::const_span source) override;
+
+private:
+	bytes::vector _secret;
+
+};
+
+TcpConnection::Protocol::Version1::Version1(bytes::vector &&secret)
+: _secret(std::move(secret)) {
+}
+
+bool TcpConnection::Protocol::Version1::requiresExtendedPadding() const {
+	return true;
+}
+
+void TcpConnection::Protocol::Version1::prepareKey(
+		bytes::span key,
+		bytes::const_span source) {
+	const auto payload = bytes::concatenate(source, _secret);
+	bytes::copy(key, openssl::Sha256(payload));
+}
+
+class TcpConnection::Protocol::VersionD : public Version1 {
+public:
+	using Version1::Version1;
+
+	uint32 id() const override;
+	bool supportsArbitraryLength() const override;
+	bytes::span finalizePacket(mtpBuffer &buffer) override;
+
+};
+
+uint32 TcpConnection::Protocol::VersionD::id() const {
+	return 0xDDDDDDDDU;
+}
+
+bool TcpConnection::Protocol::VersionD::supportsArbitraryLength() const {
+	return true;
+}
+
+bytes::span TcpConnection::Protocol::VersionD::finalizePacket(
+		mtpBuffer &buffer) {
+	Expects(buffer.size() > 2 && buffer.size() < 0x1000003U);
+
+	const auto intsSize = uint32(buffer.size() - 2);
+	const auto padding = rand_value<uint32>() & 0x0F;
+	const auto bytesSize = intsSize * sizeof(mtpPrime) + padding;
+	buffer[1] = bytesSize;
+	for (auto added = 0; added < padding; added += 4) {
+		buffer.push_back(rand_value<mtpPrime>());
+	}
+
+	return bytes::make_span(buffer).subspan(4, 4 + bytesSize);
+}
+
+auto TcpConnection::Protocol::Create(bytes::vector &&secret)
+-> std::unique_ptr<Protocol> {
+	if (secret.size() == 17 && static_cast<uchar>(secret[0]) == 0xDD) {
+		return std::make_unique<VersionD>(
+			bytes::make_vector(bytes::make_span(secret).subspan(1)));
+	} else if (secret.size() == 16) {
+		return std::make_unique<Version1>(std::move(secret));
+	} else if (secret.empty()) {
+		return std::make_unique<Version0>();
+	}
+	Unexpected("Secret bytes in TcpConnection::Protocol::Create.");
+}
+
 TcpConnection::TcpConnection(QThread *thread, const ProxyData &proxy)
 : AbstractConnection(thread, proxy)
 , _currentPosition(reinterpret_cast<char*>(_shortBuffer))
@@ -99,7 +240,10 @@ void TcpConnection::socketRead() {
 		}
 		int32 bytes = (int32)_socket.read(_currentPosition, toRead);
 		if (bytes > 0) {
-			aesCtrEncrypt(_currentPosition, bytes, _receiveKey, &_receiveState);
+			aesCtrEncrypt(
+				bytes::make_span(_currentPosition, bytes),
+				_receiveKey,
+				&_receiveState);
 			TCP_LOG(("TCP Info: read %1 bytes").arg(bytes));
 
 			_packetRead += bytes;
@@ -182,11 +326,10 @@ mtpBuffer TcpConnection::handleResponse(const char *packet, uint32 length) {
 	if (size == 1) {
 		LOG(("TCP Error: "
 			"error packet received, endpoint: '%1:%2', "
-			"protocolDcId: %3, secret_len: %4, code = %5"
+			"protocolDcId: %3, code = %4"
 			).arg(_address.isEmpty() ? ("proxy_" + _proxy.host) : _address
 			).arg(_address.isEmpty() ? _proxy.port : _port
 			).arg(_protocolDcId
-			).arg(_protocolSecret.size()
 			).arg(*packetdata));
 		return mtpBuffer(1, *packetdata);
 	}
@@ -247,7 +390,7 @@ void TcpConnection::socketConnected() {
 		).arg(_address + ':' + QString::number(_port)));
 
 	_pingTime = getms();
-	sendData(buffer);
+	sendData(std::move(buffer));
 }
 
 void TcpConnection::socketDisconnected() {
@@ -256,20 +399,23 @@ void TcpConnection::socketDisconnected() {
 	}
 }
 
-void TcpConnection::sendData(mtpBuffer &buffer) {
-	if (_status == Status::Finished) return;
+bool TcpConnection::requiresExtendedPadding() const {
+	Expects(_protocol != nullptr);
 
-	if (buffer.size() < 3) {
-		LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime)));
-		TCP_LOG(("TCP Error: bad packet %1").arg(Logs::mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str()));
-		emit error(kErrorCodeOther);
-		return;
+	return _protocol->requiresExtendedPadding();
+}
+
+void TcpConnection::sendData(mtpBuffer &&buffer) {
+	Expects(buffer.size() > 2);
+
+	if (_status != Status::Finished) {
+		sendBuffer(std::move(buffer));
 	}
-
-	sendBuffer(buffer);
 }
 
 void TcpConnection::writeConnectionStart() {
+	Expects(_protocol != nullptr);
+
 	// prepare random part
 	auto nonceBytes = bytes::vector(64);
 	const auto nonce = bytes::make_span(nonceBytes);
@@ -282,6 +428,7 @@ void TcpConnection::writeConnectionStart() {
 	const auto reserved12 = 0x54534F50U;
 	const auto reserved13 = 0x20544547U;
 	const auto reserved14 = 0xEEEEEEEEU;
+	const auto reserved15 = 0xDDDDDDDDU;
 	const auto reserved21 = 0x00000000U;
 	do {
 		bytes::set_random(nonce);
@@ -290,21 +437,11 @@ void TcpConnection::writeConnectionStart() {
 		|| *first == reserved12
 		|| *first == reserved13
 		|| *first == reserved14
+		|| *first == reserved15
 		|| *second == reserved21);
 
-	const auto prepareKey = [&](bytes::span key, bytes::const_span from) {
-		if (_protocolSecret.size() == 16) {
-			const auto payload = bytes::concatenate(from, _protocolSecret);
-			bytes::copy(key, openssl::Sha256(payload));
-		} else if (_protocolSecret.empty()) {
-			bytes::copy(key, from);
-		} else {
-			bytes::set_with_const(key, gsl::byte{});
-		}
-	};
-
 	// prepare encryption key/iv
-	prepareKey(
+	_protocol->prepareKey(
 		bytes::make_span(_sendKey),
 		nonce.subspan(8, CTRState::KeySize));
 	bytes::copy(
@@ -316,7 +453,7 @@ void TcpConnection::writeConnectionStart() {
 	const auto reversed = bytes::make_span(reversedBytes);
 	bytes::copy(reversed, nonce.subspan(8, reversed.size()));
 	std::reverse(reversed.begin(), reversed.end());
-	prepareKey(
+	_protocol->prepareKey(
 		bytes::make_span(_receiveKey),
 		reversed.subspan(0, CTRState::KeySize));
 	bytes::copy(
@@ -325,40 +462,32 @@ void TcpConnection::writeConnectionStart() {
 
 	// write protocol and dc ids
 	const auto protocol = reinterpret_cast<uint32*>(nonce.data() + 56);
-	*protocol = 0xEFEFEFEFU;
+	*protocol = _protocol->id();
 	const auto dcId = reinterpret_cast<int16*>(nonce.data() + 60);
 	*dcId = _protocolDcId;
 
 	_socket.write(reinterpret_cast<const char*>(nonce.data()), 56);
-	aesCtrEncrypt(nonce.data(), 64, _sendKey, &_sendState);
+	aesCtrEncrypt(nonce, _sendKey, &_sendState);
 	_socket.write(reinterpret_cast<const char*>(nonce.subspan(56).data()), 8);
 }
 
-void TcpConnection::sendBuffer(mtpBuffer &buffer) {
+void TcpConnection::sendBuffer(mtpBuffer &&buffer) {
 	if (!_packetIndex++) {
 		writeConnectionStart();
 	}
 
-	uint32 size = buffer.size() - 3, len = size * 4;
-	char *data = reinterpret_cast<char*>(&buffer[0]);
-	if (size < 0x7f) {
-		data[7] = char(size);
-		TCP_LOG(("TCP Info: write %1 packet %2").arg(_packetIndex).arg(len + 1));
-
-		aesCtrEncrypt(data + 7, len + 1, _sendKey, &_sendState);
-		_socket.write(data + 7, len + 1);
-	} else {
-		data[4] = 0x7f;
-		reinterpret_cast<uchar*>(data)[5] = uchar(size & 0xFF);
-		reinterpret_cast<uchar*>(data)[6] = uchar((size >> 8) & 0xFF);
-		reinterpret_cast<uchar*>(data)[7] = uchar((size >> 16) & 0xFF);
-		TCP_LOG(("TCP Info: write %1 packet %2").arg(_packetIndex).arg(len + 4));
-
-		aesCtrEncrypt(data + 4, len + 4, _sendKey, &_sendState);
-		_socket.write(data + 4, len + 4);
-	}
+	// buffer: 2 available int-s + data + available int.
+	const auto bytes = _protocol->finalizePacket(buffer);
+	TCP_LOG(("TCP Info: write %1 packet %2"
+		).arg(_packetIndex
+		).arg(bytes.size()));
+	aesCtrEncrypt(bytes, _sendKey, &_sendState);
+	_socket.write(
+		reinterpret_cast<const char*>(bytes.data()),
+		bytes.size());
 }
 
+
 void TcpConnection::disconnectFromServer() {
 	if (_status == Status::Finished) return;
 	_status = Status::Finished;
@@ -377,13 +506,14 @@ void TcpConnection::connectToServer(
 		int16 protocolDcId) {
 	Expects(_address.isEmpty());
 	Expects(_port == 0);
-	Expects(_protocolSecret.empty());
+	Expects(_protocol == nullptr);
 	Expects(_protocolDcId == 0);
 
 	if (_proxy.type == ProxyData::Type::Mtproto) {
 		_address = _proxy.host;
 		_port = _proxy.port;
-		_protocolSecret = ProtocolSecretFromPassword(_proxy.password);
+		_protocol = Protocol::Create(
+			ProtocolSecretFromPassword(_proxy.password));
 
 		DEBUG_LOG(("TCP Info: "
 			"dc:%1 - Connecting to proxy '%2'"
@@ -392,7 +522,7 @@ void TcpConnection::connectToServer(
 	} else {
 		_address = address;
 		_port = port;
-		_protocolSecret = protocolSecret;
+		_protocol = Protocol::Create(base::duplicate(protocolSecret));
 
 		DEBUG_LOG(("TCP Info: "
 			"dc:%1 - Connecting to '%2'"
@@ -479,5 +609,7 @@ void TcpConnection::socketError(QAbstractSocket::SocketError e) {
 	emit error(kErrorCodeOther);
 }
 
+TcpConnection::~TcpConnection() = default;
+
 } // namespace internal
 } // namespace MTP
diff --git a/Telegram/SourceFiles/mtproto/connection_tcp.h b/Telegram/SourceFiles/mtproto/connection_tcp.h
index cbe399653e..c98c66087e 100644
--- a/Telegram/SourceFiles/mtproto/connection_tcp.h
+++ b/Telegram/SourceFiles/mtproto/connection_tcp.h
@@ -24,7 +24,7 @@ public:
 
 	TimeMs pingTime() const override;
 	TimeMs fullConnectTimeout() const override;
-	void sendData(mtpBuffer &buffer) override;
+	void sendData(mtpBuffer &&buffer) override;
 	void disconnectFromServer() override;
 	void connectToServer(
 		const QString &address,
@@ -32,12 +32,15 @@ public:
 		const bytes::vector &protocolSecret,
 		int16 protocolDcId) override;
 	bool isConnected() const override;
+	bool requiresExtendedPadding() const override;
 
 	int32 debugState() const override;
 
 	QString transport() const override;
 	QString tag() const override;
 
+	~TcpConnection();
+
 private:
 	enum class Status {
 		Waiting = 0,
@@ -62,7 +65,7 @@ private:
 		return *reinterpret_cast<uint32*>(ch);
 	}
 
-	void sendBuffer(mtpBuffer &buffer);
+	void sendBuffer(mtpBuffer &&buffer);
 
 	QTcpSocket _socket;
 	uint32 _packetIndex = 0; // sent packet number
@@ -78,8 +81,9 @@ private:
 	CTRState _sendState;
 	uchar _receiveKey[CTRState::KeySize];
 	CTRState _receiveState;
+	class Protocol;
+	std::unique_ptr<Protocol> _protocol;
 	int16 _protocolDcId = 0;
-	bytes::vector _protocolSecret;
 
 	Status _status = Status::Waiting;
 	MTPint128 _checkNonce;
diff --git a/Telegram/SourceFiles/mtproto/core_types.cpp b/Telegram/SourceFiles/mtproto/core_types.cpp
index b54241c043..8a27619508 100644
--- a/Telegram/SourceFiles/mtproto/core_types.cpp
+++ b/Telegram/SourceFiles/mtproto/core_types.cpp
@@ -9,6 +9,144 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "zlib.h"
 
+namespace MTP {
+namespace {
+
+uint32 CountPaddingAmountInInts(uint32 requestSize, bool extended) {
+#ifdef TDESKTOP_MTPROTO_OLD
+	return ((8 + requestSize) & 0x03)
+		? (4 - ((8 + requestSize) & 0x03))
+		: 0;
+#else // TDESKTOP_MTPROTO_OLD
+	auto result = ((8 + requestSize) & 0x03)
+		? (4 - ((8 + requestSize) & 0x03))
+		: 0;
+
+	// At least 12 bytes of random padding.
+	if (result < 3) {
+		result += 4;
+	}
+
+	if (extended) {
+		// Some more random padding.
+		result += ((rand_value<uchar>() & 0x0F) << 2);
+	}
+
+	return result;
+#endif // TDESKTOP_MTPROTO_OLD
+}
+
+} // namespace
+
+SecureRequest::SecureRequest(const details::SecureRequestCreateTag &tag)
+: _data(std::make_shared<SecureRequestData>(tag)) {
+}
+
+SecureRequest SecureRequest::Prepare(uint32 size, uint32 reserveSize) {
+	const auto finalSize = std::max(size, reserveSize);
+
+	auto result = SecureRequest(details::SecureRequestCreateTag{});
+	result->reserve(kMessageBodyPosition + finalSize);
+	result->resize(kMessageBodyPosition);
+	result->back() = (size << 2);
+	return result;
+}
+
+uint32 SecureRequest::innerLength() const {
+	if (!_data || _data->size() <= kMessageBodyPosition) {
+		return 0;
+	}
+	return (*_data)[kMessageLengthPosition];
+}
+
+void SecureRequest::write(mtpBuffer &to) const {
+	if (!_data || _data->size() <= kMessageBodyPosition) {
+		return;
+	}
+	uint32 was = to.size(), s = innerLength() / sizeof(mtpPrime);
+	to.resize(was + s);
+	memcpy(
+		to.data() + was,
+		_data->constData() + kMessageBodyPosition,
+		s * sizeof(mtpPrime));
+}
+
+SecureRequestData *SecureRequest::operator->() const {
+	Expects(_data != nullptr);
+
+	return _data.get();
+}
+
+SecureRequestData &SecureRequest::operator*() const {
+	Expects(_data != nullptr);
+
+	return *_data;
+}
+
+SecureRequest::operator bool() const {
+	return (_data != nullptr);
+}
+
+void SecureRequest::addPadding(bool extended) {
+	if (_data->size() <= kMessageBodyPosition) return;
+
+	const auto requestSize = (innerLength() >> 2);
+	const auto padding = CountPaddingAmountInInts(requestSize, extended);
+	const auto fullSize = kMessageBodyPosition + requestSize + padding;
+	if (uint32(_data->size()) != fullSize) {
+		_data->resize(fullSize);
+		if (padding > 0) {
+			memset_rand(
+				_data->data() + (fullSize - padding),
+				padding * sizeof(mtpPrime));
+		}
+	}
+}
+
+uint32 SecureRequest::messageSize() const {
+	if (_data->size() <= kMessageBodyPosition) {
+		return 0;
+	}
+	const auto ints = (innerLength() >> 2);
+	return kMessageIdInts + kSeqNoInts + kMessageLengthInts + ints;
+}
+
+bool SecureRequest::isSentContainer() const {
+	if (_data->size() <= kMessageBodyPosition) {
+		return false;
+	}
+	return (!_data->msDate && !(*_data)[kSeqNoPosition]); // msDate = 0, seqNo = 0
+}
+
+bool SecureRequest::isStateRequest() const {
+	if (_data->size() <= kMessageBodyPosition) {
+		return false;
+	}
+	const auto type = mtpTypeId((*_data)[kMessageBodyPosition]);
+	return (type == mtpc_msgs_state_req);
+}
+
+bool SecureRequest::needAck() const {
+	if (_data->size() <= kMessageBodyPosition) {
+		return false;
+	}
+	const auto type = mtpTypeId((*_data)[kMessageBodyPosition]);
+	switch (type) {
+	case mtpc_msg_container:
+	case mtpc_msgs_ack:
+	case mtpc_http_wait:
+	case mtpc_bad_msg_notification:
+	case mtpc_msgs_all_info:
+	case mtpc_msgs_state_info:
+	case mtpc_msg_detailed_info:
+	case mtpc_msg_new_detailed_info:
+		return false;
+	}
+	return true;
+}
+
+} // namespace MTP
+
 Exception::Exception(const QString &msg) noexcept : _msg(msg.toUtf8()) {
 	LOG(("Exception: %1").arg(msg));
 }
@@ -91,85 +229,6 @@ void MTPstring::write(mtpBuffer &to) const {
 	memcpy(buf, v.constData(), l);
 }
 
-uint32 mtpRequest::innerLength() const { // for template MTP requests and MTPBoxed instanciation
-	const auto value = get();
-	if (!value || value->size() < 9) {
-		return 0;
-	}
-	return value->at(7);
-}
-
-void mtpRequest::write(mtpBuffer &to) const {
-	const auto value = get();
-	if (!value || value->size() < 9) {
-		return;
-	}
-	uint32 was = to.size(), s = innerLength() / sizeof(mtpPrime);
-	to.resize(was + s);
-	memcpy(to.data() + was, value->constData() + 8, s * sizeof(mtpPrime));
-}
-
-bool mtpRequestData::isSentContainer(const mtpRequest &request) { // "request-like" wrap for msgIds vector
-	if (request->size() < 9) return false;
-	return (!request->msDate && !(*request)[6]); // msDate = 0, seqNo = 0
-}
-
-bool mtpRequestData::isStateRequest(const mtpRequest &request) {
-	if (request->size() < 9) return false;
-	return (mtpTypeId((*request)[8]) == mtpc_msgs_state_req);
-}
-
-bool mtpRequestData::needAckByType(mtpTypeId type) {
-	switch (type) {
-	case mtpc_msg_container:
-	case mtpc_msgs_ack:
-	case mtpc_http_wait:
-	case mtpc_bad_msg_notification:
-	case mtpc_msgs_all_info:
-	case mtpc_msgs_state_info:
-	case mtpc_msg_detailed_info:
-	case mtpc_msg_new_detailed_info:
-	return false;
-	}
-	return true;
-}
-
-mtpRequest mtpRequestData::prepare(uint32 requestSize, uint32 maxSize) {
-	if (!maxSize) maxSize = requestSize;
-	mtpRequest result(new mtpRequestData(true));
-	result->reserve(8 + maxSize + _padding(maxSize)); // 2: salt, 2: session_id, 2: msg_id, 1: seq_no, 1: message_length
-	result->resize(7);
-	result->push_back(requestSize << 2);
-	return result;
-}
-
-void mtpRequestData::padding(mtpRequest &request) {
-	if (request->size() < 9) return;
-
-	uint32 requestSize = (request.innerLength() >> 2), padding = _padding(requestSize), fullSize = 8 + requestSize + padding; // 2: salt, 2: session_id, 2: msg_id, 1: seq_no, 1: message_length
-	if (uint32(request->size()) != fullSize) {
-		request->resize(fullSize);
-		if (padding) {
-			memset_rand(request->data() + (fullSize - padding), padding * sizeof(mtpPrime));
-		}
-	}
-}
-
-uint32 mtpRequestData::_padding(uint32 requestSize) {
-#ifdef TDESKTOP_MTPROTO_OLD
-	return ((8 + requestSize) & 0x03) ? (4 - ((8 + requestSize) & 0x03)) : 0;
-#else // TDESKTOP_MTPROTO_OLD
-	auto result = ((8 + requestSize) & 0x03) ? (4 - ((8 + requestSize) & 0x03)) : 0;
-
-	// At least 12 bytes of random padding.
-	if (result < 3) {
-		result += 4;
-	}
-
-	return result;
-#endif // TDESKTOP_MTPROTO_OLD
-}
-
 void mtpTextSerializeCore(MTPStringLogger &to, const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons, uint32 level, mtpPrime vcons) {
 	switch (mtpTypeId(cons)) {
 	case mtpc_int: {
diff --git a/Telegram/SourceFiles/mtproto/core_types.h b/Telegram/SourceFiles/mtproto/core_types.h
index d5ae244c98..1658a26ddc 100644
--- a/Telegram/SourceFiles/mtproto/core_types.h
+++ b/Telegram/SourceFiles/mtproto/core_types.h
@@ -19,6 +19,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "base/algorithm.h"
 #include "base/assertion.h"
 
+using mtpPrime = int32;
+using mtpRequestId = int32;
+using mtpMsgId = uint64;
+using mtpPingId = uint64;
+
+using mtpBuffer = QVector<mtpPrime>;
+using mtpTypeId = uint32;
+
 namespace MTP {
 
 // type DcId represents actual data center id, while in most cases
@@ -51,89 +59,6 @@ constexpr int GetDcIdShift(ShiftedDcId shiftedDcId) {
 
 } // namespace MTP
 
-using mtpPrime = int32;
-using mtpRequestId = int32;
-using mtpMsgId = uint64;
-using mtpPingId = uint64;
-
-using mtpBuffer = QVector<mtpPrime>;
-using mtpTypeId = uint32;
-
-class mtpRequestData;
-class mtpRequest : public std::shared_ptr<mtpRequestData> {
-public:
-	mtpRequest() = default;
-    explicit mtpRequest(mtpRequestData *ptr)
-	: std::shared_ptr<mtpRequestData>(ptr) {
-	}
-
-	uint32 innerLength() const;
-	void write(mtpBuffer &to) const;
-
-	using ResponseType = void; // don't know real response type =(
-
-};
-
-class mtpRequestData : public mtpBuffer {
-public:
-	// in toSend: = 0 - must send in container, > 0 - can send without container
-	// in haveSent: = 0 - container with msgIds, > 0 - when was sent
-	int64 msDate = 0;
-
-	mtpRequestId requestId = 0;
-	mtpRequest after;
-	bool needsLayer = false;
-
-	mtpRequestData(bool/* sure*/) {
-	}
-
-	static mtpRequest prepare(uint32 requestSize, uint32 maxSize = 0);
-	static void padding(mtpRequest &request);
-
-	template <typename TRequest>
-	static mtpRequest serialize(const TRequest &request) {
-		const auto requestSize = request.innerLength() >> 2;
-		auto serialized = prepare(requestSize);
-		request.write(*serialized);
-		return serialized;
-	}
-
-	static uint32 messageSize(const mtpRequest &request) {
-		if (request->size() < 9) return 0;
-		return 4 + (request.innerLength() >> 2); // 2: msg_id, 1: seq_no, q: message_length
-	}
-
-	static bool isSentContainer(const mtpRequest &request); // "request-like" wrap for msgIds vector
-	static bool isStateRequest(const mtpRequest &request);
-	static bool needAck(const mtpRequest &request) {
-		if (request->size() < 9) return false;
-		return mtpRequestData::needAckByType((*request)[8]);
-	}
-	static bool needAckByType(mtpTypeId type);
-
-private:
-	static uint32 _padding(uint32 requestSize);
-
-};
-
-using mtpPreRequestMap = QMap<mtpRequestId, mtpRequest>;
-using mtpRequestMap = QMap<mtpMsgId, mtpRequest>;
-using mtpMsgIdsSet = QMap<mtpMsgId, bool>;
-
-class mtpRequestIdsMap : public QMap<mtpMsgId, mtpRequestId> {
-public:
-	using ParentType = QMap<mtpMsgId, mtpRequestId>;
-
-	mtpMsgId min() const {
-		return size() ? cbegin().key() : 0;
-	}
-
-	mtpMsgId max() const {
-		ParentType::const_iterator e(cend());
-		return size() ? (--e).key() : 0;
-	}
-};
-
 class Exception : public std::exception {
 public:
 	explicit Exception(const QString &msg) noexcept;
@@ -349,6 +274,100 @@ class MTPBoxed<MTPBoxed<T> > {
 	typename T::CantMakeBoxedBoxedType v;
 };
 
+namespace MTP {
+namespace details {
+
+struct SecureRequestCreateTag {
+};
+
+} // namespace details
+
+template <typename T>
+struct is_boxed : std::false_type {
+};
+
+template <typename T>
+struct is_boxed<MTPBoxed<T>> : std::true_type {
+};
+
+template <typename T>
+constexpr bool is_boxed_v = is_boxed<T>::value;
+
+class SecureRequestData;
+class SecureRequest {
+public:
+	SecureRequest() = default;
+
+	static constexpr auto kSaltInts = 2;
+	static constexpr auto kSessionIdInts = 2;
+	static constexpr auto kMessageIdInts = 2;
+	static constexpr auto kSeqNoPosition = kSaltInts
+		+ kSessionIdInts
+		+ kMessageIdInts;
+	static constexpr auto kSeqNoInts = 1;
+	static constexpr auto kMessageLengthPosition = kSeqNoPosition
+		+ kSeqNoInts;
+	static constexpr auto kMessageLengthInts = 1;
+	static constexpr auto kMessageBodyPosition = kMessageLengthPosition
+		+ kMessageLengthInts;
+
+	static SecureRequest Prepare(uint32 size, uint32 reserveSize = 0);
+
+	template <
+		typename Request,
+		typename = std::enable_if_t<is_boxed_v<Request>>>
+	static SecureRequest Serialize(const Request &request);
+
+	// For template MTP requests and MTPBoxed instanciation.
+	uint32 innerLength() const;
+	void write(mtpBuffer &to) const;
+
+	SecureRequestData *operator->() const;
+	SecureRequestData &operator*() const;
+	explicit operator bool() const;
+
+	void addPadding(bool extended);
+	uint32 messageSize() const;
+
+	// "request-like" wrap for msgIds vector
+	bool isSentContainer() const;
+	bool isStateRequest() const;
+	bool needAck() const;
+
+	using ResponseType = void; // don't know real response type =(
+
+private:
+	explicit SecureRequest(const details::SecureRequestCreateTag &);
+
+	std::shared_ptr<SecureRequestData> _data;
+
+};
+
+class SecureRequestData : public mtpBuffer {
+public:
+	explicit SecureRequestData(const details::SecureRequestCreateTag &) {
+	}
+
+	// in toSend: = 0 - must send in container, > 0 - can send without container
+	// in haveSent: = 0 - container with msgIds, > 0 - when was sent
+	int64 msDate = 0;
+
+	mtpRequestId requestId = 0;
+	SecureRequest after;
+	bool needsLayer = false;
+
+};
+
+template <typename Request, typename>
+SecureRequest SecureRequest::Serialize(const Request &request) {
+	const auto requestSize = request.innerLength() >> 2;
+	auto serialized = Prepare(requestSize);
+	request.write(*serialized);
+	return serialized;
+}
+
+} // namespace MTP
+
 class MTPint {
 public:
 	int32 v = 0;
diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.cpp b/Telegram/SourceFiles/mtproto/mtp_instance.cpp
index ea35f22353..67d8d69186 100644
--- a/Telegram/SourceFiles/mtproto/mtp_instance.cpp
+++ b/Telegram/SourceFiles/mtproto/mtp_instance.cpp
@@ -78,7 +78,7 @@ public:
 
 	void sendRequest(
 		mtpRequestId requestId,
-		mtpRequest &&request,
+		SecureRequest &&request,
 		RPCResponseHandler &&callbacks,
 		ShiftedDcId shiftedDcId,
 		TimeMs msCanWait,
@@ -88,9 +88,9 @@ public:
 	void unregisterRequest(mtpRequestId requestId);
 	void storeRequest(
 		mtpRequestId requestId,
-		const mtpRequest &request,
+		const SecureRequest &request,
 		RPCResponseHandler &&callbacks);
-	mtpRequest getRequest(mtpRequestId requestId);
+	SecureRequest getRequest(mtpRequestId requestId);
 	void clearCallbacksDelayed(std::vector<RPCCallbackClear> &&ids);
 	void execCallback(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end);
 	bool hasCallbacks(mtpRequestId requestId);
@@ -199,7 +199,7 @@ private:
 	std::map<mtpRequestId, RPCResponseHandler> _parserMap;
 	QMutex _parserMapLock;
 
-	std::map<mtpRequestId, mtpRequest> _requestMap;
+	std::map<mtpRequestId, SecureRequest> _requestMap;
 	QReadWriteLock _requestMapLock;
 
 	std::deque<std::pair<mtpRequestId, TimeMs>> _delayedRequests;
@@ -831,7 +831,7 @@ void Instance::Private::checkDelayedRequests() {
 			continue;
 		}
 
-		auto request = mtpRequest();
+		auto request = SecureRequest();
 		{
 			QReadLocker locker(&_requestMapLock);
 			auto it = _requestMap.find(requestId);
@@ -852,7 +852,7 @@ void Instance::Private::checkDelayedRequests() {
 
 void Instance::Private::sendRequest(
 		mtpRequestId requestId,
-		mtpRequest &&request,
+		SecureRequest &&request,
 		RPCResponseHandler &&callbacks,
 		ShiftedDcId shiftedDcId,
 		TimeMs msCanWait,
@@ -900,7 +900,7 @@ void Instance::Private::unregisterRequest(mtpRequestId requestId) {
 
 void Instance::Private::storeRequest(
 		mtpRequestId requestId,
-		const mtpRequest &request,
+		const SecureRequest &request,
 		RPCResponseHandler &&callbacks) {
 	if (callbacks.onDone || callbacks.onFail) {
 		QMutexLocker locker(&_parserMapLock);
@@ -912,8 +912,8 @@ void Instance::Private::storeRequest(
 	}
 }
 
-mtpRequest Instance::Private::getRequest(mtpRequestId requestId) {
-	auto result = mtpRequest();
+SecureRequest Instance::Private::getRequest(mtpRequestId requestId) {
+	auto result = SecureRequest();
 	{
 		QReadLocker locker(&_requestMapLock);
 		auto it = _requestMap.find(requestId);
@@ -1217,7 +1217,7 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e
 			newdcWithShift = ShiftDcId(newdcWithShift, GetDcIdShift(dcWithShift));
 		}
 
-		auto request = mtpRequest();
+		auto request = SecureRequest();
 		{
 			QReadLocker locker(&_requestMapLock);
 			auto it = _requestMap.find(requestId);
@@ -1288,7 +1288,7 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e
 		if (badGuestDc) _badGuestDcRequests.insert(requestId);
 		return true;
 	} else if (err == qstr("CONNECTION_NOT_INITED") || err == qstr("CONNECTION_LAYER_INVALID")) {
-		mtpRequest request;
+		SecureRequest request;
 		{
 			QReadLocker locker(&_requestMapLock);
 			auto it = _requestMap.find(requestId);
@@ -1313,7 +1313,7 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e
 	} else if (err == qstr("CONNECTION_LANG_CODE_INVALID")) {
 		Lang::CurrentCloudManager().resetToDefault();
 	} else if (err == qstr("MSG_WAIT_FAILED")) {
-		mtpRequest request;
+		SecureRequest request;
 		{
 			QReadLocker locker(&_requestMapLock);
 			auto it = _requestMap.find(requestId);
@@ -1332,7 +1332,7 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e
 			if (const auto afterDcId = queryRequestByDc(request->after->requestId)) {
 				dcWithShift = *shiftedDcId;
 				if (*shiftedDcId != *afterDcId) {
-					request->after = mtpRequest();
+					request->after = SecureRequest();
 				}
 			} else {
 				LOG(("MTP Error: could not find dependent request %1 by dc").arg(request->after->requestId));
@@ -1676,7 +1676,7 @@ void Instance::onKeyDestroyed(qint32 shiftedDcId) {
 }
 void Instance::sendRequest(
 		mtpRequestId requestId,
-		mtpRequest &&request,
+		SecureRequest &&request,
 		RPCResponseHandler &&callbacks,
 		ShiftedDcId shiftedDcId,
 		TimeMs msCanWait,
diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.h b/Telegram/SourceFiles/mtproto/mtp_instance.h
index 7c71e6865e..be489553b5 100644
--- a/Telegram/SourceFiles/mtproto/mtp_instance.h
+++ b/Telegram/SourceFiles/mtproto/mtp_instance.h
@@ -60,9 +60,9 @@ public:
 
 	not_null<DcOptions*> dcOptions();
 
-	template <typename TRequest>
+	template <typename Request>
 	mtpRequestId send(
-			const TRequest &request,
+			const Request &request,
 			RPCResponseHandler &&callbacks = {},
 			ShiftedDcId shiftedDcId = 0,
 			TimeMs msCanWait = 0,
@@ -70,7 +70,7 @@ public:
 		const auto requestId = GetNextRequestId();
 		sendSerialized(
 			requestId,
-			mtpRequestData::serialize(request),
+			SecureRequest::Serialize(request),
 			std::move(callbacks),
 			shiftedDcId,
 			msCanWait,
@@ -78,9 +78,9 @@ public:
 		return requestId;
 	}
 
-	template <typename TRequest>
+	template <typename Request>
 	mtpRequestId send(
-			const TRequest &request,
+			const Request &request,
 			RPCDoneHandlerPtr &&onDone,
 			RPCFailHandlerPtr &&onFail = nullptr,
 			ShiftedDcId shiftedDcId = 0,
@@ -94,14 +94,14 @@ public:
 			afterRequestId);
 	}
 
-	template <typename TRequest>
+	template <typename Request>
 	mtpRequestId sendProtocolMessage(
 			ShiftedDcId shiftedDcId,
-			const TRequest &request) {
+			const Request &request) {
 		const auto requestId = GetNextRequestId();
 		sendRequest(
 			requestId,
-			mtpRequestData::serialize(request),
+			SecureRequest::Serialize(request),
 			{},
 			shiftedDcId,
 			0,
@@ -112,7 +112,7 @@ public:
 
 	void sendSerialized(
 			mtpRequestId requestId,
-			mtpRequest &&request,
+			SecureRequest &&request,
 			RPCResponseHandler &&callbacks,
 			ShiftedDcId shiftedDcId,
 			TimeMs msCanWait,
@@ -195,7 +195,7 @@ private slots:
 private:
 	void sendRequest(
 		mtpRequestId requestId,
-		mtpRequest &&request,
+		SecureRequest &&request,
 		RPCResponseHandler &&callbacks,
 		ShiftedDcId shiftedDcId,
 		TimeMs msCanWait,
diff --git a/Telegram/SourceFiles/mtproto/session.cpp b/Telegram/SourceFiles/mtproto/session.cpp
index b12888b294..c0bec07aff 100644
--- a/Telegram/SourceFiles/mtproto/session.cpp
+++ b/Telegram/SourceFiles/mtproto/session.cpp
@@ -290,14 +290,14 @@ void Session::checkRequestsByTimer() {
 
 	{
 		QReadLocker locker(data.haveSentMutex());
-		mtpRequestMap &haveSent(data.haveSentMap());
-		uint32 haveSentCount(haveSent.size());
+		auto &haveSent = data.haveSentMap();
+		const auto haveSentCount = haveSent.size();
 		auto ms = getms(true);
-		for (mtpRequestMap::iterator i = haveSent.begin(), e = haveSent.end(); i != e; ++i) {
-			mtpRequest &req(i.value());
+		for (auto i = haveSent.begin(), e = haveSent.end(); i != e; ++i) {
+			auto &req = i.value();
 			if (req->msDate > 0) {
 				if (req->msDate + MTPCheckResendTimeout < ms) { // need to resend or check state
-					if (mtpRequestData::messageSize(req) < MTPResendThreshold) { // resend
+					if (req.messageSize() < MTPResendThreshold) { // resend
 						resendingIds.reserve(haveSentCount);
 						resendingIds.push_back(i.key());
 					} else {
@@ -396,8 +396,8 @@ int32 Session::requestState(mtpRequestId requestId) const {
 	if (!requestId) return MTP::RequestSent;
 
 	QWriteLocker locker(data.toSendMutex());
-	const mtpPreRequestMap &toSend(data.toSendMap());
-	mtpPreRequestMap::const_iterator i = toSend.constFind(requestId);
+	const auto &toSend = data.toSendMap();
+	const auto i = toSend.constFind(requestId);
 	if (i != toSend.cend()) {
 		return MTP::RequestSending;
 	} else {
@@ -433,10 +433,10 @@ QString Session::transport() const {
 }
 
 mtpRequestId Session::resend(quint64 msgId, qint64 msCanWait, bool forceContainer, bool sendMsgStateInfo) {
-	mtpRequest request;
+	SecureRequest request;
 	{
 		QWriteLocker locker(data.haveSentMutex());
-		mtpRequestMap &haveSent(data.haveSentMap());
+		auto &haveSent = data.haveSentMap();
 
 		auto i = haveSent.find(msgId);
 		if (i == haveSent.end()) {
@@ -458,14 +458,14 @@ mtpRequestId Session::resend(quint64 msgId, qint64 msCanWait, bool forceContaine
 		request = i.value();
 		haveSent.erase(i);
 	}
-	if (mtpRequestData::isSentContainer(request)) { // for container just resend all messages we can
+	if (request.isSentContainer()) { // for container just resend all messages we can
 		DEBUG_LOG(("Message Info: resending container from haveSent, msgId %1").arg(msgId));
 		const mtpMsgId *ids = (const mtpMsgId *)(request->constData() + 8);
 		for (uint32 i = 0, l = (request->size() - 8) >> 1; i < l; ++i) {
 			resend(ids[i], 10, true);
 		}
 		return 0xFFFFFFFF;
-	} else if (!mtpRequestData::isStateRequest(request)) {
+	} else if (!request.isStateRequest()) {
 		request->msDate = forceContainer ? 0 : getms(true);
 		sendPrepared(request, msCanWait, false);
 		{
@@ -488,10 +488,12 @@ void Session::resendAll() {
 	QVector<mtpMsgId> toResend;
 	{
 		QReadLocker locker(data.haveSentMutex());
-		const mtpRequestMap &haveSent(data.haveSentMap());
+		const auto &haveSent = data.haveSentMap();
 		toResend.reserve(haveSent.size());
-		for (mtpRequestMap::const_iterator i = haveSent.cbegin(), e = haveSent.cend(); i != e; ++i) {
-			if (i.value()->requestId) toResend.push_back(i.key());
+		for (auto i = haveSent.cbegin(), e = haveSent.cend(); i != e; ++i) {
+			if (i.value()->requestId) {
+				toResend.push_back(i.key());
+			}
 		}
 	}
 	for (uint32 i = 0, l = toResend.size(); i < l; ++i) {
@@ -500,7 +502,7 @@ void Session::resendAll() {
 }
 
 void Session::sendPrepared(
-		const mtpRequest &request,
+		const SecureRequest &request,
 		TimeMs msCanWait,
 		bool newRequest) {
 	DEBUG_LOG(("MTP Info: adding request to toSendMap, msCanWait %1"
diff --git a/Telegram/SourceFiles/mtproto/session.h b/Telegram/SourceFiles/mtproto/session.h
index 531ce6c094..ce4dbb0c25 100644
--- a/Telegram/SourceFiles/mtproto/session.h
+++ b/Telegram/SourceFiles/mtproto/session.h
@@ -21,6 +21,24 @@ namespace internal {
 class Dcenter;
 class Connection;
 
+using PreRequestMap = QMap<mtpRequestId, SecureRequest>;
+using RequestMap = QMap<mtpMsgId, SecureRequest>;
+
+class RequestIdsMap : public QMap<mtpMsgId, mtpRequestId> {
+public:
+	using ParentType = QMap<mtpMsgId, mtpRequestId>;
+
+	mtpMsgId min() const {
+		return size() ? cbegin().key() : 0;
+	}
+
+	mtpMsgId max() const {
+		ParentType::const_iterator e(cend());
+		return size() ? (--e).key() : 0;
+	}
+
+};
+
 class ReceivedMsgIds {
 public:
 	bool registerMsgId(mtpMsgId msgId, bool needAck) {
@@ -191,22 +209,22 @@ public:
 		return &_stateRequestLock;
 	}
 
-	mtpPreRequestMap &toSendMap() {
+	PreRequestMap &toSendMap() {
 		return _toSend;
 	}
-	const mtpPreRequestMap &toSendMap() const {
+	const PreRequestMap &toSendMap() const {
 		return _toSend;
 	}
-	mtpRequestMap &haveSentMap() {
+	RequestMap &haveSentMap() {
 		return _haveSent;
 	}
-	const mtpRequestMap &haveSentMap() const {
+	const RequestMap &haveSentMap() const {
 		return _haveSent;
 	}
-	mtpRequestIdsMap &toResendMap() { // msgId -> requestId, on which toSend: requestId -> request for resended requests
+	RequestIdsMap &toResendMap() { // msgId -> requestId, on which toSend: requestId -> request for resended requests
 		return _toResend;
 	}
-	const mtpRequestIdsMap &toResendMap() const {
+	const RequestIdsMap &toResendMap() const {
 		return _toResend;
 	}
 	ReceivedMsgIds &receivedIdsSet() {
@@ -215,10 +233,10 @@ public:
 	const ReceivedMsgIds &receivedIdsSet() const {
 		return _receivedIds;
 	}
-	mtpRequestIdsMap &wereAckedMap() {
+	RequestIdsMap &wereAckedMap() {
 		return _wereAcked;
 	}
-	const mtpRequestIdsMap &wereAckedMap() const {
+	const RequestIdsMap &wereAckedMap() const {
 		return _wereAcked;
 	}
 	QMap<mtpRequestId, SerializedMessage> &haveReceivedResponses() {
@@ -233,10 +251,10 @@ public:
 	const QList<SerializedMessage> &haveReceivedUpdates() const {
 		return _receivedUpdates;
 	}
-	mtpMsgIdsSet &stateRequestMap() {
+	QMap<mtpMsgId, bool> &stateRequestMap() {
 		return _stateRequest;
 	}
-	const mtpMsgIdsSet &stateRequestMap() const {
+	const QMap<mtpMsgId, bool> &stateRequestMap() const {
 		return _stateRequest;
 	}
 
@@ -269,12 +287,12 @@ private:
 	bool _layerInited = false;
 	ConnectionOptions _options;
 
-	mtpPreRequestMap _toSend; // map of request_id -> request, that is waiting to be sent
-	mtpRequestMap _haveSent; // map of msg_id -> request, that was sent, msDate = 0 for msgs_state_req (no resend / state req), msDate = 0, seqNo = 0 for containers
-	mtpRequestIdsMap _toResend; // map of msg_id -> request_id, that request_id -> request lies in toSend and is waiting to be resent
+	PreRequestMap _toSend; // map of request_id -> request, that is waiting to be sent
+	RequestMap _haveSent; // map of msg_id -> request, that was sent, msDate = 0 for msgs_state_req (no resend / state req), msDate = 0, seqNo = 0 for containers
+	RequestIdsMap _toResend; // map of msg_id -> request_id, that request_id -> request lies in toSend and is waiting to be resent
 	ReceivedMsgIds _receivedIds; // set of received msg_id's, for checking new msg_ids
-	mtpRequestIdsMap _wereAcked; // map of msg_id -> request_id, this msg_ids already were acked or do not need ack
-	mtpMsgIdsSet _stateRequest; // set of msg_id's, whose state should be requested
+	RequestIdsMap _wereAcked; // map of msg_id -> request_id, this msg_ids already were acked or do not need ack
+	QMap<mtpMsgId, bool> _stateRequest; // set of msg_id's, whose state should be requested
 
 	QMap<mtpRequestId, SerializedMessage> _receivedResponses; // map of request_id -> response that should be processed in the main thread
 	QList<SerializedMessage> _receivedUpdates; // list of updates that should be processed in the main thread
@@ -321,7 +339,7 @@ public:
 
 	// Nulls msgId and seqNo in request, if newRequest = true.
 	void sendPrepared(
-		const mtpRequest &request,
+		const SecureRequest &request,
 		TimeMs msCanWait = 0,
 		bool newRequest = true);
 
diff --git a/Telegram/SourceFiles/storage/file_download.cpp b/Telegram/SourceFiles/storage/file_download.cpp
index c6e9fe4dde..4953693ea1 100644
--- a/Telegram/SourceFiles/storage/file_download.cpp
+++ b/Telegram/SourceFiles/storage/file_download.cpp
@@ -583,8 +583,8 @@ void mtpFileLoader::cdnPartLoaded(const MTPupload_CdnFile &result, mtpRequestId
 	state.ivec[12] = static_cast<uchar>((counterOffset >> 24) & 0xFF);
 
 	auto decryptInPlace = result.c_upload_cdnFile().vbytes.v;
-	MTP::aesCtrEncrypt(decryptInPlace.data(), decryptInPlace.size(), key.data(), &state);
 	auto buffer = bytes::make_span(decryptInPlace);
+	MTP::aesCtrEncrypt(buffer, key.data(), &state);
 
 	switch (checkCdnFileHash(offset, buffer)) {
 	case CheckCdnHashResult::NoHash: {