Make lint happier

This commit is contained in:
Lonami Exo 2017-05-21 13:59:16 +02:00
parent 63c89af983
commit 02a847b64a
20 changed files with 92 additions and 84 deletions

View File

@ -70,10 +70,10 @@ If you've installed Telethon via pip, launch an interactive python3 session and
.. code:: python .. code:: python
>>> from telethon import InteractiveTelegramClient >>> from telethon import InteractiveTelegramClient
>>> # 'sessionid' can be 'yourname'. It'll be saved as yourname.session >>> # 'session_id' can be 'your_name'. It'll be saved as your_name.session
>>> # Also (obviously) replace the api_id and api_hash with your values >>> # Also (obviously) replace the api_id and api_hash with your values
... ...
>>> client = InteractiveTelegramClient('sessionid', '+34600000000', >>> client = InteractiveTelegramClient('session_id', '+34600000000',
... api_id=12345, api_hash='0123456789abcdef0123456789abcdef') ... api_id=12345, api_hash='0123456789abcdef0123456789abcdef')
================== ==================
@ -93,7 +93,7 @@ Then, simply run ``python3 try_telethon.py`` to start the interactive example.
Using Telethon Using Telethon
============== ==============
If you really want to learn how to use Telethon, it is **highly adviced** that If you really want to learn how to use Telethon, it is **highly advised** that
you take a look to the you take a look to the
`InteractiveTelegramClient <telethon/interactive_telegram_client.py>`_ file `InteractiveTelegramClient <telethon/interactive_telegram_client.py>`_ file
and check how it works. This file contains everything you'll need to build and check how it works. This file contains everything you'll need to build
@ -208,7 +208,7 @@ Once this is done, pass the proxy settings to the ``TelegramClient`` constructor
>>> from telethon import InteractiveTelegramClient >>> from telethon import InteractiveTelegramClient
>>> import socks >>> import socks
>>> client = InteractiveTelegramClient('sessionid', '+34600000000', >>> client = InteractiveTelegramClient('session_id', '+34600000000',
... api_id=12345, api_hash='0123456789abcdef0123456789abcdef', ... api_id=12345, api_hash='0123456789abcdef0123456789abcdef',
... proxy=(socks.SOCKS5, 'localhost', 4444)) ... proxy=(socks.SOCKS5, 'localhost', 4444))

View File

@ -63,20 +63,20 @@ def get_create_path_for(tlobject):
return os.path.join(out_dir, get_file_name(tlobject, add_extension=True)) return os.path.join(out_dir, get_file_name(tlobject, add_extension=True))
def get_path_for_type(type, relative_to='.'): def get_path_for_type(type_, relative_to='.'):
"""Similar to getting the path for a TLObject, it might not be possible """Similar to getting the path for a TLObject, it might not be possible
to have the TLObject itself but rather its name (the type); to have the TLObject itself but rather its name (the type);
this method works in the same way, returning a relative path""" this method works in the same way, returning a relative path"""
if type.lower() in {'int', 'long', 'int128', 'int256', 'double', if type_.lower() in {'int', 'long', 'int128', 'int256', 'double',
'vector', 'string', 'bool', 'true', 'bytes', 'date'}: 'vector', 'string', 'bool', 'true', 'bytes', 'date'}:
path = 'index.html#%s' % type.lower() path = 'index.html#%s' % type_.lower()
elif '.' in type: elif '.' in type_:
# If it's not a core type, then it has to be a custom Telegram type # If it's not a core type, then it has to be a custom Telegram type
namespace, name = type.split('.') namespace, name = type_.split('.')
path = 'types/%s/%s' % (namespace, get_file_name(name, add_extension=True)) path = 'types/%s/%s' % (namespace, get_file_name(name, add_extension=True))
else: else:
path = 'types/%s' % get_file_name(type, add_extension=True) path = 'types/%s' % get_file_name(type_, add_extension=True)
return get_relative_path(path, relative_to) return get_relative_path(path, relative_to)
@ -128,7 +128,7 @@ def build_menu(docs, filename, relative_main_index):
def generate_index(folder, original_paths): def generate_index(folder, original_paths):
"""Generates the index file for the specified folder""" """Generates the index file for the specified folder"""
# Determine the namespaces listed here (as subfolders) # Determine the namespaces listed here (as sub folders)
# and the files (.html files) that we should link to # and the files (.html files) that we should link to
namespaces = [] namespaces = []
files = [] files = []
@ -320,10 +320,10 @@ def generate_documentation(scheme_file):
docs.write_text('The following %d methods return this type as a result.' % len(functions)) docs.write_text('The following %d methods return this type as a result.' % len(functions))
docs.begin_table(2) docs.begin_table(2)
for function in functions: for func in functions:
link = get_create_path_for(function) link = get_create_path_for(func)
link = get_relative_path(link, relative_to=filename) link = get_relative_path(link, relative_to=filename)
docs.add_row(get_class_name(function), link=link) docs.add_row(get_class_name(func), link=link)
docs.end_table() docs.end_table()
docs.end_body() docs.end_body()

View File

@ -188,6 +188,7 @@ messages = result.messages</pre>
you're still able to invoke these methods manually.</p> you're still able to invoke these methods manually.</p>
</div> </div>
</div>
<script> <script>
contentDiv = document.getElementById("contentDiv"); contentDiv = document.getElementById("contentDiv");
searchDiv = document.getElementById("searchDiv"); searchDiv = document.getElementById("searchDiv");

View File

@ -1,4 +1,4 @@
from .aes import AES from .aes import AES
from .rsa import RSA, RSAServerKey from .rsa import RSA, RSAServerKey
from .auth_key import AuthKey from .auth_key import AuthKey
from .factorizator import Factorizator from .factorization import Factorization

View File

@ -1,7 +1,7 @@
from random import randint from random import randint
class Factorizator: class Factorization:
@staticmethod @staticmethod
def find_small_multiplier_lopatin(what): def find_small_multiplier_lopatin(what):
"""Finds the small multiplier by using Lopatin's method""" """Finds the small multiplier by using Lopatin's method"""
@ -25,7 +25,7 @@ class Factorizator:
x = c x = c
z = y - x if x < y else x - y z = y - x if x < y else x - y
g = Factorizator.gcd(z, what) g = Factorization.gcd(z, what)
if g != 1: if g != 1:
break break
@ -58,5 +58,5 @@ class Factorizator:
@staticmethod @staticmethod
def factorize(pq): def factorize(pq):
"""Factorizes the given number and returns both the divisor and the number divided by the divisor""" """Factorizes the given number and returns both the divisor and the number divided by the divisor"""
divisor = Factorizator.find_small_multiplier_lopatin(pq) divisor = Factorization.find_small_multiplier_lopatin(pq)
return divisor, pq // divisor return divisor, pq // divisor

View File

@ -112,7 +112,7 @@ class RPCError(Exception):
'FILE_PARTS_INVALID': 'The number of file parts is invalid.', 'FILE_PARTS_INVALID': 'The number of file parts is invalid.',
'FILE_PART_(\d+)_MISSING': 'FILE_PART_(\d+)_MISSING':
'Part {} of the file is missing from storage.', 'Part {} of the file is missing from storage.',
'MD5_CHECKSUM_INVALID': 'The MD5 checksums do not match.', 'MD5_CHECKSUM_INVALID': 'The MD5 check-sums do not match.',
'PHOTO_INVALID_DIMENSIONS': 'The photo dimensions are invalid.', 'PHOTO_INVALID_DIMENSIONS': 'The photo dimensions are invalid.',
'FIELD_NAME_INVALID': 'The field with the name FIELD_NAME is invalid.', 'FIELD_NAME_INVALID': 'The field with the name FIELD_NAME is invalid.',
'FIELD_NAME_EMPTY': 'The field with the name FIELD_NAME is missing.', 'FIELD_NAME_EMPTY': 'The field with the name FIELD_NAME is missing.',

View File

@ -41,8 +41,8 @@ def calc_msg_key(data):
return sha1(data)[4:20] return sha1(data)[4:20]
def generate_key_data_from_nonces(server_nonce, new_nonce): def generate_key_data_from_nonce(server_nonce, new_nonce):
"""Generates the key data corresponding to the given nonces""" """Generates the key data corresponding to the given nonce"""
hash1 = sha1(bytes(new_nonce + server_nonce)) hash1 = sha1(bytes(new_nonce + server_nonce))
hash2 = sha1(bytes(server_nonce + new_nonce)) hash2 = sha1(bytes(server_nonce + new_nonce))
hash3 = sha1(bytes(new_nonce + new_nonce)) hash3 = sha1(bytes(new_nonce + new_nonce))
@ -68,10 +68,11 @@ def sha256(data):
def get_password_hash(pw, current_salt): def get_password_hash(pw, current_salt):
"""Gets the password hash for the two-step verification. """Gets the password hash for the two-step verification.
curent_salt should be the byte array provided by invoking GetPasswordRequest()""" current_salt should be the byte array provided by invoking GetPasswordRequest()"""
# Passwords are encoded as UTF-8 # Passwords are encoded as UTF-8
# https://github.com/DrKLO/Telegram/blob/e31388/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java#L2003 # At https://github.com/DrKLO/Telegram/blob/e31388
# src/main/java/org/telegram/ui/LoginActivity.java#L2003
data = pw.encode('utf-8') data = pw.encode('utf-8')
pw_hash = current_salt + data + current_salt pw_hash = current_salt + data + current_salt

View File

@ -86,7 +86,7 @@ class InteractiveTelegramClient(TelegramClient):
# Display them so the user can choose # Display them so the user can choose
for i, entity in enumerate(entities): for i, entity in enumerate(entities):
i += 1 # 1-based index for normies i += 1 # 1-based index
try: try:
print('{}. {}'.format(i, get_display_name(entity))) print('{}. {}'.format(i, get_display_name(entity)))
except UnicodeEncodeError: except UnicodeEncodeError:

View File

@ -2,7 +2,7 @@ import os
import time import time
from .. import helpers as utils from .. import helpers as utils
from ..crypto import AES, RSA, AuthKey, Factorizator from ..crypto import AES, RSA, AuthKey, Factorization
from ..network import MtProtoPlainSender from ..network import MtProtoPlainSender
from ..utils import BinaryReader, BinaryWriter from ..utils import BinaryReader, BinaryWriter
@ -49,7 +49,7 @@ def do_authentication(transport):
# Step 2 sending: DH Exchange # Step 2 sending: DH Exchange
new_nonce = os.urandom(32) new_nonce = os.urandom(32)
p, q = Factorizator.factorize(pq) p, q = Factorization.factorize(pq)
with BinaryWriter() as pq_inner_data_writer: with BinaryWriter() as pq_inner_data_writer:
pq_inner_data_writer.write_int( pq_inner_data_writer.write_int(
0x83c95aec, signed=False) # PQ Inner Data 0x83c95aec, signed=False) # PQ Inner Data
@ -120,12 +120,12 @@ def do_authentication(transport):
encrypted_answer = reader.tgread_bytes() encrypted_answer = reader.tgread_bytes()
# Step 3 sending: Complete DH Exchange # Step 3 sending: Complete DH Exchange
key, iv = utils.generate_key_data_from_nonces(server_nonce, new_nonce) key, iv = utils.generate_key_data_from_nonce(server_nonce, new_nonce)
plain_text_answer = AES.decrypt_ige(encrypted_answer, key, iv) plain_text_answer = AES.decrypt_ige(encrypted_answer, key, iv)
g, dh_prime, ga, time_offset = None, None, None, None g, dh_prime, ga, time_offset = None, None, None, None
with BinaryReader(plain_text_answer) as dh_inner_data_reader: with BinaryReader(plain_text_answer) as dh_inner_data_reader:
dh_inner_data_reader.read(20) # hashsum dh_inner_data_reader.read(20) # hash sum
code = dh_inner_data_reader.read_int(signed=False) code = dh_inner_data_reader.read_int(signed=False)
if code != 0xb5890dba: if code != 0xb5890dba:
raise AssertionError('Invalid DH Inner Data code: {}'.format(code)) raise AssertionError('Invalid DH Inner Data code: {}'.format(code))

View File

@ -46,7 +46,7 @@ class MtProtoPlainSender:
# See https://core.telegram.org/mtproto/description#message-identifier-msg-id # See https://core.telegram.org/mtproto/description#message-identifier-msg-id
ms_time = int(time.time() * 1000) ms_time = int(time.time() * 1000)
new_msg_id = (((ms_time // 1000) << 32) new_msg_id = (((ms_time // 1000) << 32)
| # "must approximately equal unixtime*2^32" | # "must approximately equal unix time*2^32"
((ms_time % 1000) << 22) ((ms_time % 1000) << 22)
| # "approximate moment in time the message was created" | # "approximate moment in time the message was created"
random.randint(0, 524288) random.randint(0, 524288)

View File

@ -282,9 +282,9 @@ class MtProtoSender:
def handle_pong(self, msg_id, sequence, reader, request): def handle_pong(self, msg_id, sequence, reader, request):
self.logger.debug('Handling pong') self.logger.debug('Handling pong')
reader.read_int(signed=False) # code reader.read_int(signed=False) # code
recv_msg_id = reader.read_long(signed=False) received_msg_id = reader.read_long(signed=False)
if recv_msg_id == request.msg_id: if received_msg_id == request.msg_id:
self.logger.warning('Pong confirmed a request') self.logger.warning('Pong confirmed a request')
request.confirm_received = True request.confirm_received = True

View File

@ -116,30 +116,30 @@ def parse_message_entities(msg):
del msg[entity.offset] del msg[entity.offset]
# Iterate over all the entities but the current # Iterate over all the entities but the current
for subentity in [e for e in entities if e is not entity]: for sub_entity in [e for e in entities if e is not entity]:
# First case, one in one out: so*me_th_in*g. # First case, one in one out: so*me_th_in*g.
# In this case, the current entity length is decreased by two, # In this case, the current entity length is decreased by two,
# and all the subentities offset decreases 1 # and all the sub_entities offset decreases 1
if (subentity.offset > entity.offset and if (sub_entity.offset > entity.offset and
subentity.offset + subentity.length < sub_entity.offset + sub_entity.length <
entity.offset + entity.length): entity.offset + entity.length):
entity.length -= 2 entity.length -= 2
subentity.offset -= 1 sub_entity.offset -= 1
# Second case, both inside: so*me_th*in_g. # Second case, both inside: so*me_th*in_g.
# In this case, the current entity length is decreased by one, # In this case, the current entity length is decreased by one,
# and all the subentities offset and length decrease 1 # and all the sub_entities offset and length decrease 1
elif (entity.offset < subentity.offset < entity.offset + elif (entity.offset < sub_entity.offset < entity.offset +
entity.length < subentity.offset + subentity.length): entity.length < sub_entity.offset + sub_entity.length):
entity.length -= 1 entity.length -= 1
subentity.offset -= 1 sub_entity.offset -= 1
subentity.length -= 1 sub_entity.length -= 1
# Third case, both outside: so*me*th_in_g. # Third case, both outside: so*me*th_in_g.
# In this case, the current entity is left untouched, # In this case, the current entity is left untouched,
# and all the subentities offset decreases 2 # and all the sub_entities offset decreases 2
elif subentity.offset > entity.offset + entity.length: elif sub_entity.offset > entity.offset + entity.length:
subentity.offset -= 2 sub_entity.offset -= 2
# Finally, we can join our poor mutilated message back and return # Finally, we can join our poor mutilated message back and return
msg = ''.join(msg) msg = ''.join(msg)

View File

@ -17,8 +17,7 @@ from .tl.functions import InitConnectionRequest, InvokeWithLayerRequest
from .tl.functions.account import GetPasswordRequest from .tl.functions.account import GetPasswordRequest
from .tl.functions.auth import (CheckPasswordRequest, LogOutRequest, from .tl.functions.auth import (CheckPasswordRequest, LogOutRequest,
SendCodeRequest, SignInRequest, SendCodeRequest, SignInRequest,
SignUpRequest) SignUpRequest, ImportBotAuthorizationRequest)
from .tl.functions.auth import ImportBotAuthorizationRequest
from .tl.functions.help import GetConfigRequest from .tl.functions.help import GetConfigRequest
from .tl.functions.messages import ( from .tl.functions.messages import (
GetDialogsRequest, GetHistoryRequest, ReadHistoryRequest, SendMediaRequest, GetDialogsRequest, GetHistoryRequest, ReadHistoryRequest, SendMediaRequest,
@ -34,7 +33,7 @@ from .tl.types import (
MessageMediaContact, MessageMediaDocument, MessageMediaPhoto, MessageMediaContact, MessageMediaDocument, MessageMediaPhoto,
UserProfilePhotoEmpty) UserProfilePhotoEmpty)
from .utils import (find_user_or_chat, get_input_peer, from .utils import (find_user_or_chat, get_input_peer,
get_appropiate_part_size, get_extension) get_appropriated_part_size, get_extension)
class TelegramClient: class TelegramClient:
@ -273,7 +272,7 @@ class TelegramClient:
self.session = None self.session = None
return True return True
except: except (RPCError, ConnectionError):
# Something happened when logging out, restore the state back # Something happened when logging out, restore the state back
self.sender.logging_out = False self.sender.logging_out = False
return False return False
@ -282,8 +281,7 @@ class TelegramClient:
def list_sessions(): def list_sessions():
"""Lists all the sessions of the users who have ever connected """Lists all the sessions of the users who have ever connected
using this client and never logged out""" using this client and never logged out"""
return [path.splitext(path.basename(f))[ return [path.splitext(path.basename(f))[0]
0] # splitext = split ext (not spli text!)
for f in listdir('.') if f.endswith('.session')] for f in listdir('.') if f.endswith('.session')]
# endregion # endregion
@ -424,7 +422,7 @@ class TelegramClient:
""" """
file_size = path.getsize(file_path) file_size = path.getsize(file_path)
if not part_size_kb: if not part_size_kb:
part_size_kb = get_appropiate_part_size(file_size) part_size_kb = get_appropriated_part_size(file_size)
if part_size_kb > 512: if part_size_kb > 512:
raise ValueError('The part size must be less or equal to 512KB') raise ValueError('The part size must be less or equal to 512KB')
@ -534,7 +532,7 @@ class TelegramClient:
add_extension=True, add_extension=True,
download_big=True): download_big=True):
"""Downloads the profile photo for an user or a chat (including channels). """Downloads the profile photo for an user or a chat (including channels).
Returns False if no photo was providen, or if it was Empty""" Returns False if no photo was provided, or if it was Empty"""
if (not profile_photo or if (not profile_photo or
isinstance(profile_photo, UserProfilePhotoEmpty) or isinstance(profile_photo, UserProfilePhotoEmpty) or
@ -693,7 +691,7 @@ class TelegramClient:
if not file_size: if not file_size:
raise ValueError('A part size value must be provided') raise ValueError('A part size value must be provided')
else: else:
part_size_kb = get_appropiate_part_size(file_size) part_size_kb = get_appropriated_part_size(file_size)
part_size = int(part_size_kb * 1024) part_size = int(part_size_kb * 1024)
if part_size % 1024 != 0: if part_size % 1024 != 0:

View File

@ -31,7 +31,7 @@ class Session:
try: try:
os.remove('{}.session'.format(self.session_user_id)) os.remove('{}.session'.format(self.session_user_id))
return True return True
except: except OSError:
return False return False
@staticmethod @staticmethod
@ -54,7 +54,7 @@ class Session:
# Refer to mtproto_plain_sender.py for the original method, this is a simple copy # Refer to mtproto_plain_sender.py for the original method, this is a simple copy
ms_time = int(time.time() * 1000) ms_time = int(time.time() * 1000)
new_msg_id = (((ms_time // 1000 + self.time_offset) << 32) new_msg_id = (((ms_time // 1000 + self.time_offset) << 32)
| # "must approximately equal unixtime*2^32" | # "must approximately equal unix time*2^32"
((ms_time % 1000) << 22) ((ms_time % 1000) << 22)
| # "approximate moment in time the message was created" | # "approximate moment in time the message was created"
random.randint(0, 524288) random.randint(0, 524288)

View File

@ -77,8 +77,8 @@ def find_user_or_chat(peer, users, chats):
return None return None
def get_appropiate_part_size(file_size): def get_appropriated_part_size(file_size):
"""Gets the appropiate part size when uploading or downloading files, """Gets the appropriated part size when uploading or downloading files,
given an initial file size""" given an initial file size"""
if file_size <= 1048576: # 1MB if file_size <= 1048576: # 1MB
return 32 return 32

View File

@ -42,12 +42,12 @@ class TLObject:
([0-9a-f]+) # The constructor ID is in hexadecimal form ([0-9a-f]+) # The constructor ID is in hexadecimal form
(?:\s # After that, we want to match its arguments (name:type) (?:\s # After that, we want to match its arguments (name:type)
\{? # For handling the start of the «{X:Type}» case {? # For handling the start of the «{X:Type}» case
\w+ # The argument name will always be an alpha-only name \w+ # The argument name will always be an alpha-only name
: # Then comes the separator between name:type : # Then comes the separator between name:type
[\w\d<>#.?!]+ # The type is slightly more complex, since it's alphanumeric and it can [\w\d<>#.?!]+ # The type is slightly more complex, since it's alphanumeric and it can
# also have Vector<type>, flags:# and flags.0?default, plus :!X as type # also have Vector<type>, flags:# and flags.0?default, plus :!X as type
\}? # For handling the end of the «{X:Type}» case }? # For handling the end of the «{X:Type}» case
)* # Match 0 or more arguments )* # Match 0 or more arguments
\s # Leave a space between the arguments and the equal \s # Leave a space between the arguments and the equal
= =
@ -58,12 +58,12 @@ class TLObject:
# Sub-regex to match the arguments (sadly, it cannot be embedded in the first regex) # Sub-regex to match the arguments (sadly, it cannot be embedded in the first regex)
args_match = re.findall(r''' args_match = re.findall(r'''
(\{)? # We may or may not capture the opening brace ({)? # We may or may not capture the opening brace
(\w+) # First we capture any alpha name with length 1 or more (\w+) # First we capture any alpha name with length 1 or more
: # Which is separated from its type by a colon : # Which is separated from its type by a colon
([\w\d<>#.?!]+) # The type is slightly more complex, since it's alphanumeric and it can ([\w\d<>#.?!]+) # The type is slightly more complex, since it's alphanumeric and it can
# also have Vector<type>, flags:# and flags.0?default, plus :!X as type # also have Vector<type>, flags:# and flags.0?default, plus :!X as type
(\})? # We may or not capture the closing brace (})? # We may or not capture the closing brace
''', tl, re.IGNORECASE | re.VERBOSE) ''', tl, re.IGNORECASE | re.VERBOSE)
# Retrieve the matched arguments # Retrieve the matched arguments

View File

@ -250,7 +250,7 @@ class TLGenerator:
'"""File generated by TLObjects\' generator. All changes will be ERASED"""') '"""File generated by TLObjects\' generator. All changes will be ERASED"""')
builder.writeln() builder.writeln()
builder.writeln('from ..tl import types, functions') builder.writeln('from . import types, functions')
builder.writeln() builder.writeln()
# Create a variable to indicate which layer this is # Create a variable to indicate which layer this is

View File

@ -1,7 +1,7 @@
import unittest import unittest
import telethon.helpers as utils import telethon.helpers as utils
from telethon.crypto import AES, Factorizator from telethon.crypto import AES, Factorization
class CryptoTests(unittest.TestCase): class CryptoTests(unittest.TestCase):
@ -23,11 +23,11 @@ class CryptoTests(unittest.TestCase):
def test_sha1(): def test_sha1():
string = 'Example string' string = 'Example string'
hashsum = utils.sha1(string.encode('utf-8')) hash_sum = utils.sha1(string.encode('utf-8'))
expected = b'\nT\x92|\x8d\x06:)\x99\x04\x8e\xf8j?\xc4\x8e\xd3}m9' expected = b'\nT\x92|\x8d\x06:)\x99\x04\x8e\xf8j?\xc4\x8e\xd3}m9'
assert hashsum == expected, 'Invalid sha1 hashsum representation (should be {}, but is {})'\ assert hash_sum == expected, 'Invalid sha1 hash_sum representation (should be {}, but is {})'\
.format(expected, hashsum) .format(expected, hash_sum)
def test_aes_encrypt(self): def test_aes_encrypt(self):
value = AES.encrypt_ige(self.plain_text, self.key, self.iv) value = AES.encrypt_ige(self.plain_text, self.key, self.iv)
@ -67,8 +67,12 @@ class CryptoTests(unittest.TestCase):
msg_key = b'\xba\x1a\xcf\xda\xa8^Cbl\xfa\xb6\x0c:\x9b\xb0\xfc' msg_key = b'\xba\x1a\xcf\xda\xa8^Cbl\xfa\xb6\x0c:\x9b\xb0\xfc'
key, iv = utils.calc_key(shared_key, msg_key, client=True) key, iv = utils.calc_key(shared_key, msg_key, client=True)
expected_key = b"\xaf\xe3\x84Qm\xe0!\x0c\xd91\xe4\x9a\xa0v_gcx\xa1\xb0\xc9\xbc\x16'v\xcf,\x9dM\xae\xc6\xa5" expected_key = b"\xaf\xe3\x84Qm\xe0!\x0c\xd91\xe4\x9a\xa0v_gc" \
expected_iv = b'\xb8Q\xf3\xc5\xa3]\xc6\xdf\x9e\xe0Q\xbd"\x8d\x13\t\x0e\x9a\x9d^8\xa2\xf8\xe7\x00w\xd9\xc1\xa7\xa0\xf7\x0f' b"x\xa1\xb0\xc9\xbc\x16'v\xcf,\x9dM\xae\xc6\xa5"
expected_iv = b'\xb8Q\xf3\xc5\xa3]\xc6\xdf\x9e\xe0Q\xbd"\x8d' \
b'\x13\t\x0e\x9a\x9d^8\xa2\xf8\xe7\x00w\xd9\xc1' \
b'\xa7\xa0\xf7\x0f'
assert key == expected_key, 'Invalid key (expected ("{}"), got ("{}"))'.format( assert key == expected_key, 'Invalid key (expected ("{}"), got ("{}"))'.format(
expected_key, key) expected_key, key)
@ -79,8 +83,12 @@ class CryptoTests(unittest.TestCase):
msg_key = b'\x86m\x92i\xcf\x8b\x93\xaa\x86K\x1fi\xd04\x83]' msg_key = b'\x86m\x92i\xcf\x8b\x93\xaa\x86K\x1fi\xd04\x83]'
key, iv = utils.calc_key(shared_key, msg_key, client=False) key, iv = utils.calc_key(shared_key, msg_key, client=False)
expected_key = b'\xdd0X\xb6\x93\x8e\xc9y\xef\x83\xf8\x8cj\xa7h\x03\xe2\xc6\xb16\xc5\xbb\xfc\xe7\xdf\xd6\xb1g\xf7u\xcfk' expected_key = b'\xdd0X\xb6\x93\x8e\xc9y\xef\x83\xf8\x8cj' \
expected_iv = b'\xdcL\xc2\x18\x01J"X\x86lb\xb6\xb547\xfd\xe2a4\xb6\xaf}FS\xd7[\xe0N\r\x19\xfb\xbc' b'\xa7h\x03\xe2\xc6\xb16\xc5\xbb\xfc\xe7' \
b'\xdf\xd6\xb1g\xf7u\xcfk'
expected_iv = b'\xdcL\xc2\x18\x01J"X\x86lb\xb6\xb547\xfd' \
b'\xe2a4\xb6\xaf}FS\xd7[\xe0N\r\x19\xfb\xbc'
assert key == expected_key, 'Invalid key (expected ("{}"), got ("{}"))'.format( assert key == expected_key, 'Invalid key (expected ("{}"), got ("{}"))'.format(
expected_key, key) expected_key, key)
@ -95,11 +103,11 @@ class CryptoTests(unittest.TestCase):
value, expected) value, expected)
@staticmethod @staticmethod
def test_generate_key_data_from_nonces(): def test_generate_key_data_from_nonce():
server_nonce = b'I am the server nonce.' server_nonce = b'I am the server nonce.'
new_nonce = b'I am a new calculated nonce.' new_nonce = b'I am a new calculated nonce.'
key, iv = utils.generate_key_data_from_nonces(server_nonce, new_nonce) key, iv = utils.generate_key_data_from_nonce(server_nonce, new_nonce)
expected_key = b'?\xc4\xbd\xdf\rWU\x8a\xf5\x0f+V\xdc\x96up\x1d\xeeG\x00\x81|\x1eg\x8a\x8f{\xf0y\x80\xda\xde' expected_key = b'?\xc4\xbd\xdf\rWU\x8a\xf5\x0f+V\xdc\x96up\x1d\xeeG\x00\x81|\x1eg\x8a\x8f{\xf0y\x80\xda\xde'
expected_iv = b'Q\x9dpZ\xb7\xdd\xcb\x82_\xfa\xf4\x90\xecn\x10\x9cD\xd2\x01\x8d\x83\xa0\xa4^\xb8\x91,\x7fI am' expected_iv = b'Q\x9dpZ\xb7\xdd\xcb\x82_\xfa\xf4\x90\xecn\x10\x9cD\xd2\x01\x8d\x83\xa0\xa4^\xb8\x91,\x7fI am'
@ -109,9 +117,9 @@ class CryptoTests(unittest.TestCase):
key, expected_iv) key, expected_iv)
@staticmethod @staticmethod
def test_factorizator(): def test_factorize():
pq = 3118979781119966969 pq = 3118979781119966969
p, q = Factorizator.factorize(pq) p, q = Factorization.factorize(pq)
assert p == 1719614201, 'Factorized pair did not yield the correct result' assert p == 1719614201, 'Factorized pair did not yield the correct result'
assert q == 1813767169, 'Factorized pair did not yield the correct result' assert q == 1813767169, 'Factorized pair did not yield the correct result'

View File

@ -12,10 +12,10 @@ def run_server_echo_thread(port):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('', port)) s.bind(('', port))
s.listen(1) s.listen(1)
conn, addr = s.accept() connection, address = s.accept()
with conn: with connection:
data = conn.recv(16) data = connection.recv(16)
conn.send(data) connection.send(data)
server = threading.Thread(target=server_thread) server = threading.Thread(target=server_thread)
server.start() server.start()

View File

@ -7,18 +7,18 @@ from telethon.interactive_telegram_client import (InteractiveTelegramClient,
def load_settings(path='api/settings'): def load_settings(path='api/settings'):
"""Loads the user settings located under `api/`""" """Loads the user settings located under `api/`"""
settings = {} result = {}
with open(path, 'r', encoding='utf-8') as file: with open(path, 'r', encoding='utf-8') as file:
for line in file: for line in file:
value_pair = line.split('=') value_pair = line.split('=')
left = value_pair[0].strip() left = value_pair[0].strip()
right = value_pair[1].strip() right = value_pair[1].strip()
if right.isnumeric(): if right.isnumeric():
settings[left] = int(right) result[left] = int(right)
else: else:
settings[left] = right result[left] = right
return settings return result
if __name__ == '__main__': if __name__ == '__main__':