mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-07-10 08:02:23 +03:00
Remove broken CdnDecrypter
This commit is contained in:
parent
b3c23e343a
commit
3bc46e8072
|
@ -249,3 +249,9 @@ The TelegramClient is no longer made out of mixins
|
||||||
If you were relying on any of the individual mixins that made up the client, such as
|
If you were relying on any of the individual mixins that made up the client, such as
|
||||||
``UserMethods`` inside the ``telethon.client`` subpackage, those are now gone.
|
``UserMethods`` inside the ``telethon.client`` subpackage, those are now gone.
|
||||||
There is a single ``TelegramClient`` class now, containing everything you need.
|
There is a single ``TelegramClient`` class now, containing everything you need.
|
||||||
|
|
||||||
|
|
||||||
|
CdnDecrypter has been removed
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
It was not really working and was more intended to be an implementation detail than anything else.
|
||||||
|
|
|
@ -428,31 +428,26 @@ def _auth_key_callback(self: 'TelegramClient', auth_key):
|
||||||
self.session.save()
|
self.session.save()
|
||||||
|
|
||||||
|
|
||||||
async def _get_dc(self: 'TelegramClient', dc_id, cdn=False):
|
async def _get_dc(self: 'TelegramClient', dc_id):
|
||||||
"""Gets the Data Center (DC) associated to 'dc_id'"""
|
"""Gets the Data Center (DC) associated to 'dc_id'"""
|
||||||
cls = self.__class__
|
cls = self.__class__
|
||||||
if not cls._config:
|
if not cls._config:
|
||||||
cls._config = await self(_tl.fn.help.GetConfig())
|
cls._config = await self(_tl.fn.help.GetConfig())
|
||||||
|
|
||||||
if cdn and not self._cdn_config:
|
|
||||||
cls._cdn_config = await self(_tl.fn.help.GetCdnConfig())
|
|
||||||
for pk in cls._cdn_config.public_keys:
|
|
||||||
rsa.add_key(pk.public_key)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return next(
|
return next(
|
||||||
dc for dc in cls._config.dc_options
|
dc for dc in cls._config.dc_options
|
||||||
if dc.id == dc_id
|
if dc.id == dc_id
|
||||||
and bool(dc.ipv6) == self._use_ipv6 and bool(dc.cdn) == cdn
|
and bool(dc.ipv6) == self._use_ipv6 and not dc.cdn
|
||||||
)
|
)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
self._log[__name__].warning(
|
self._log[__name__].warning(
|
||||||
'Failed to get DC %s (cdn = %s) with use_ipv6 = %s; retrying ignoring IPv6 check',
|
'Failed to get DC %swith use_ipv6 = %s; retrying ignoring IPv6 check',
|
||||||
dc_id, cdn, self._use_ipv6
|
dc_id, self._use_ipv6
|
||||||
)
|
)
|
||||||
return next(
|
return next(
|
||||||
dc for dc in cls._config.dc_options
|
dc for dc in cls._config.dc_options
|
||||||
if dc.id == dc_id and bool(dc.cdn) == cdn
|
if dc.id == dc_id and not dc.cdn
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _create_exported_sender(self: 'TelegramClient', dc_id):
|
async def _create_exported_sender(self: 'TelegramClient', dc_id):
|
||||||
|
@ -538,29 +533,3 @@ async def _clean_exported_senders(self: 'TelegramClient'):
|
||||||
# Disconnect should never raise
|
# Disconnect should never raise
|
||||||
await sender.disconnect()
|
await sender.disconnect()
|
||||||
state.mark_disconnected()
|
state.mark_disconnected()
|
||||||
|
|
||||||
async def _get_cdn_client(self: 'TelegramClient', cdn_redirect):
|
|
||||||
"""Similar to ._borrow_exported_client, but for CDNs"""
|
|
||||||
# TODO Implement
|
|
||||||
raise NotImplementedError
|
|
||||||
session = self._exported_sessions.get(cdn_redirect.dc_id)
|
|
||||||
if not session:
|
|
||||||
dc = await _get_dc(self, cdn_redirect.dc_id, cdn=True)
|
|
||||||
session = self.session.clone()
|
|
||||||
await session.set_dc(dc.id, dc.ip_address, dc.port)
|
|
||||||
self._exported_sessions[cdn_redirect.dc_id] = session
|
|
||||||
|
|
||||||
self._log[__name__].info('Creating new CDN client')
|
|
||||||
client = TelegramBaseClient(
|
|
||||||
session, self.api_id, self.api_hash,
|
|
||||||
proxy=self._sender.connection.conn.proxy,
|
|
||||||
timeout=self._sender.connection.get_timeout()
|
|
||||||
)
|
|
||||||
|
|
||||||
# This will make use of the new RSA keys for this specific CDN.
|
|
||||||
#
|
|
||||||
# We won't be calling GetConfig because it's only called
|
|
||||||
# when needed by ._get_dc, and also it's static so it's likely
|
|
||||||
# set already. Avoid invoking non-CDN methods by not syncing updates.
|
|
||||||
client.connect(_sync_updates=False)
|
|
||||||
return client
|
|
||||||
|
|
|
@ -2761,7 +2761,6 @@ class TelegramClient:
|
||||||
|
|
||||||
# Cached server configuration (with .dc_options), can be "global"
|
# Cached server configuration (with .dc_options), can be "global"
|
||||||
_config = None
|
_config = None
|
||||||
_cdn_config = None
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self: 'TelegramClient',
|
self: 'TelegramClient',
|
||||||
|
|
|
@ -7,4 +7,3 @@ from .aes import AES
|
||||||
from .aesctr import AESModeCTR
|
from .aesctr import AESModeCTR
|
||||||
from .authkey import AuthKey
|
from .authkey import AuthKey
|
||||||
from .factorization import Factorization
|
from .factorization import Factorization
|
||||||
from .cdndecrypter import CdnDecrypter
|
|
||||||
|
|
|
@ -1,104 +0,0 @@
|
||||||
"""
|
|
||||||
This module holds the CdnDecrypter utility class.
|
|
||||||
"""
|
|
||||||
from hashlib import sha256
|
|
||||||
|
|
||||||
from .. import _tl
|
|
||||||
from .._crypto import AESModeCTR
|
|
||||||
from ..errors import CdnFileTamperedError
|
|
||||||
|
|
||||||
|
|
||||||
class CdnDecrypter:
|
|
||||||
"""
|
|
||||||
Used when downloading a file results in a 'FileCdnRedirect' to
|
|
||||||
both prepare the redirect, decrypt the file as it downloads, and
|
|
||||||
ensure the file hasn't been tampered. https://core.telegram.org/cdn
|
|
||||||
"""
|
|
||||||
def __init__(self, cdn_client, file_token, cdn_aes, cdn_file_hashes):
|
|
||||||
"""
|
|
||||||
Initializes the CDN decrypter.
|
|
||||||
|
|
||||||
:param cdn_client: a client connected to a CDN.
|
|
||||||
:param file_token: the token of the file to be used.
|
|
||||||
:param cdn_aes: the AES CTR used to decrypt the file.
|
|
||||||
:param cdn_file_hashes: the hashes the decrypted file must match.
|
|
||||||
"""
|
|
||||||
self.client = cdn_client
|
|
||||||
self.file_token = file_token
|
|
||||||
self.cdn_aes = cdn_aes
|
|
||||||
self.cdn_file_hashes = cdn_file_hashes
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
async def prepare_decrypter(client, cdn_client, cdn_redirect):
|
|
||||||
"""
|
|
||||||
Prepares a new CDN decrypter.
|
|
||||||
|
|
||||||
:param client: a TelegramClient connected to the main servers.
|
|
||||||
:param cdn_client: a new client connected to the CDN.
|
|
||||||
:param cdn_redirect: the redirect file object that caused this call.
|
|
||||||
:return: (CdnDecrypter, first chunk file data)
|
|
||||||
"""
|
|
||||||
cdn_aes = AESModeCTR(
|
|
||||||
key=cdn_redirect.encryption_key,
|
|
||||||
# 12 first bytes of the IV..4 bytes of the offset (0, big endian)
|
|
||||||
iv=cdn_redirect.encryption_iv[:12] + bytes(4)
|
|
||||||
)
|
|
||||||
|
|
||||||
# We assume that cdn_redirect.cdn_file_hashes are ordered by offset,
|
|
||||||
# and that there will be enough of these to retrieve the whole file.
|
|
||||||
decrypter = CdnDecrypter(
|
|
||||||
cdn_client, cdn_redirect.file_token,
|
|
||||||
cdn_aes, cdn_redirect.cdn_file_hashes
|
|
||||||
)
|
|
||||||
|
|
||||||
cdn_file = await cdn_client(_tl.fn.upload.GetCdnFile(
|
|
||||||
file_token=cdn_redirect.file_token,
|
|
||||||
offset=cdn_redirect.cdn_file_hashes[0].offset,
|
|
||||||
limit=cdn_redirect.cdn_file_hashes[0].limit
|
|
||||||
))
|
|
||||||
if isinstance(cdn_file, _tl.upload.CdnFileReuploadNeeded):
|
|
||||||
# We need to use the original client here
|
|
||||||
await client(_tl.fn.upload.ReuploadCdnFile(
|
|
||||||
file_token=cdn_redirect.file_token,
|
|
||||||
request_token=cdn_file.request_token
|
|
||||||
))
|
|
||||||
|
|
||||||
# We want to always return a valid upload.CdnFile
|
|
||||||
cdn_file = decrypter.get_file()
|
|
||||||
else:
|
|
||||||
cdn_file.bytes = decrypter.cdn_aes.encrypt(cdn_file.bytes)
|
|
||||||
cdn_hash = decrypter.cdn_file_hashes.pop(0)
|
|
||||||
decrypter.check(cdn_file.bytes, cdn_hash)
|
|
||||||
|
|
||||||
return decrypter, cdn_file
|
|
||||||
|
|
||||||
def get_file(self):
|
|
||||||
"""
|
|
||||||
Calls GetCdnFile and decrypts its bytes.
|
|
||||||
Also ensures that the file hasn't been tampered.
|
|
||||||
|
|
||||||
:return: the CdnFile result.
|
|
||||||
"""
|
|
||||||
if self.cdn_file_hashes:
|
|
||||||
cdn_hash = self.cdn_file_hashes.pop(0)
|
|
||||||
cdn_file = self.client(_tl.fn.upload.GetCdnFile(
|
|
||||||
self.file_token, cdn_hash.offset, cdn_hash.limit
|
|
||||||
))
|
|
||||||
cdn_file.bytes = self.cdn_aes.encrypt(cdn_file.bytes)
|
|
||||||
self.check(cdn_file.bytes, cdn_hash)
|
|
||||||
else:
|
|
||||||
cdn_file = _tl.upload.CdnFile(bytes(0))
|
|
||||||
|
|
||||||
return cdn_file
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def check(data, cdn_hash):
|
|
||||||
"""
|
|
||||||
Checks the integrity of the given data.
|
|
||||||
Raises CdnFileTamperedError if the integrity check fails.
|
|
||||||
|
|
||||||
:param data: the data to be hashed.
|
|
||||||
:param cdn_hash: the expected hash.
|
|
||||||
"""
|
|
||||||
if sha256(data).digest() != cdn_hash.hash:
|
|
||||||
raise CdnFileTamperedError()
|
|
Loading…
Reference in New Issue
Block a user