Remove broken CdnDecrypter

This commit is contained in:
Lonami Exo 2021-09-17 20:55:27 +02:00
parent b3c23e343a
commit 3bc46e8072
5 changed files with 11 additions and 142 deletions

View File

@ -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.

View File

@ -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

View File

@ -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',

View File

@ -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

View File

@ -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()