mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-07-15 10:32:28 +03:00
Initial attempt at Perfect Forward Secrecy
This commit is contained in:
parent
9cf5506ee4
commit
260e006abe
|
@ -21,3 +21,9 @@ class AuthKey:
|
||||||
new_nonce = new_nonce.to_bytes(32, 'little', signed=True)
|
new_nonce = new_nonce.to_bytes(32, 'little', signed=True)
|
||||||
data = new_nonce + struct.pack('<BQ', number, self.aux_hash)
|
data = new_nonce + struct.pack('<BQ', number, self.aux_hash)
|
||||||
return utils.calc_msg_key(data)
|
return utils.calc_msg_key(data)
|
||||||
|
|
||||||
|
|
||||||
|
class TempAuthKey(AuthKey):
|
||||||
|
def __init__(self, data, expires_at):
|
||||||
|
super().__init__(data)
|
||||||
|
self.expires_at = expires_at
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
"""Various helpers not related to the Telegram API itself"""
|
"""Various helpers not related to the Telegram API itself"""
|
||||||
|
import struct
|
||||||
from hashlib import sha1, sha256
|
from hashlib import sha1, sha256
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# region Multiple utilities
|
# region Multiple utilities
|
||||||
|
|
||||||
|
|
||||||
def generate_random_long(signed=True):
|
def random_long(signed=True):
|
||||||
"""Generates a random long integer (8 bytes), which is optionally signed"""
|
"""Generates a random long integer (8 bytes), which is optionally signed"""
|
||||||
return int.from_bytes(os.urandom(8), signed=signed, byteorder='little')
|
return struct.unpack('<q' if signed else '<Q', os.urandom(8))[0]
|
||||||
|
|
||||||
|
|
||||||
def ensure_parent_dir_exists(file_path):
|
def ensure_parent_dir_exists(file_path):
|
||||||
|
@ -72,4 +73,14 @@ def get_password_hash(pw, current_salt):
|
||||||
pw_hash = current_salt + data + current_salt
|
pw_hash = current_salt + data + current_salt
|
||||||
return sha256(pw_hash).digest()
|
return sha256(pw_hash).digest()
|
||||||
|
|
||||||
|
|
||||||
|
def reinterpret(value, fmt, endian='<'):
|
||||||
|
"""Reinterprets the given value from its original format to its new
|
||||||
|
format, for instance, reinterpret(-1, 'qQ') -> -1 as unsigned.
|
||||||
|
"""
|
||||||
|
return struct.unpack(
|
||||||
|
endian + fmt[1], struct.pack(endian + fmt[0], value)
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
import os
|
import os
|
||||||
|
import struct
|
||||||
import time
|
import time
|
||||||
|
from datetime import datetime, timedelta
|
||||||
from hashlib import sha1
|
from hashlib import sha1
|
||||||
|
|
||||||
from ..tl.types import (
|
|
||||||
ResPQ, PQInnerData, ServerDHParamsFail, ServerDHParamsOk,
|
|
||||||
ServerDHInnerData, ClientDHInnerData, DhGenOk, DhGenRetry, DhGenFail
|
|
||||||
)
|
|
||||||
from .. import helpers as utils
|
from .. import helpers as utils
|
||||||
from ..crypto import AES, AuthKey, Factorization
|
from ..crypto import AES, AuthKey, Factorization
|
||||||
from ..crypto import rsa
|
from ..crypto import rsa
|
||||||
from ..errors import SecurityError
|
from ..errors import SecurityError
|
||||||
from ..extensions import BinaryReader
|
from ..extensions import BinaryReader
|
||||||
from ..network import MtProtoPlainSender
|
from ..network import MtProtoPlainSender
|
||||||
|
from ..tl import TLMessage, TLObject
|
||||||
from ..tl.functions import (
|
from ..tl.functions import (
|
||||||
ReqPqRequest, ReqDHParamsRequest, SetClientDHParamsRequest
|
ReqPqRequest, ReqDHParamsRequest, SetClientDHParamsRequest
|
||||||
)
|
)
|
||||||
|
from ..tl.functions.auth import BindTempAuthKeyRequest
|
||||||
|
from ..tl.types import (
|
||||||
|
ResPQ, PQInnerData, ServerDHParamsFail, ServerDHParamsOk,
|
||||||
|
ServerDHInnerData, ClientDHInnerData, DhGenOk, DhGenRetry, DhGenFail,
|
||||||
|
BindAuthKeyInner
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def do_authentication(connection, retries=5):
|
def do_authentication(connection, retries=5):
|
||||||
|
@ -190,6 +195,65 @@ def _do_authentication(connection):
|
||||||
raise NotImplementedError('DH Gen unknown: {}'.format(dh_gen))
|
raise NotImplementedError('DH Gen unknown: {}'.format(dh_gen))
|
||||||
|
|
||||||
|
|
||||||
|
def bind_temp_auth_key(temp_auth_key, auth_key, sender):
|
||||||
|
"""
|
||||||
|
:param TempAuthKey temp_auth_key:
|
||||||
|
:param AuthKey auth_key:
|
||||||
|
:param MtProtoSender sender:
|
||||||
|
"""
|
||||||
|
assert sender.session.auth_key.key_id == temp_auth_key.key_id
|
||||||
|
bind_inner = BindAuthKeyInner(
|
||||||
|
nonce=utils.random_long(),
|
||||||
|
temp_auth_key_id=utils.reinterpret(temp_auth_key.key_id, 'Qq'),
|
||||||
|
perm_auth_key_id=utils.reinterpret(sender.session.auth_key.key_id, 'Qq'),
|
||||||
|
temp_session_id=utils.reinterpret(sender.session.id, 'Qq'),
|
||||||
|
expires_at=datetime.now() + timedelta(days=1)
|
||||||
|
)
|
||||||
|
inner_message = TLMessage(sender.session, bind_inner)
|
||||||
|
inner_message.seq_no = 0
|
||||||
|
|
||||||
|
# TODO Copy paste from MtProtoSender._send_message
|
||||||
|
plain_text = (
|
||||||
|
os.urandom(16) # random unsigned salt + random unsigned id
|
||||||
|
+ inner_message.to_bytes()
|
||||||
|
)
|
||||||
|
msg_key = utils.calc_msg_key(plain_text)
|
||||||
|
key_id = struct.pack('<Q', auth_key.key_id)
|
||||||
|
key, iv = utils.calc_key(auth_key.key, msg_key, client=True)
|
||||||
|
cipher_text = AES.encrypt_ige(plain_text, key, iv)
|
||||||
|
encrypted_inner_message = key_id + msg_key + cipher_text
|
||||||
|
|
||||||
|
# Now perform the actual request
|
||||||
|
request = BindTempAuthKeyRequest(
|
||||||
|
perm_auth_key_id=utils.reinterpret(auth_key.key_id, 'Qq'),
|
||||||
|
nonce=bind_inner.nonce,
|
||||||
|
expires_at=bind_inner.expires_at,
|
||||||
|
encrypted_message=TLObject.serialize_bytes(encrypted_inner_message)
|
||||||
|
)
|
||||||
|
|
||||||
|
message = TLMessage(sender.session, request)
|
||||||
|
message.msg_id = inner_message.msg_id
|
||||||
|
|
||||||
|
# TODO Copy paste from MtProtoSender._send_message
|
||||||
|
plain_text = \
|
||||||
|
struct.pack('<QQ', sender.session.salt, sender.session.id) \
|
||||||
|
+ message.to_bytes()
|
||||||
|
|
||||||
|
msg_key = utils.calc_msg_key(plain_text)
|
||||||
|
key_id = struct.pack('<Q', temp_auth_key.key_id)
|
||||||
|
key, iv = utils.calc_key(temp_auth_key.key, msg_key, client=True)
|
||||||
|
cipher_text = AES.encrypt_ige(plain_text, key, iv)
|
||||||
|
encrypted_message = key_id + msg_key + cipher_text
|
||||||
|
|
||||||
|
sender._pending_receive[message.msg_id] = message
|
||||||
|
sender.connection.send(encrypted_message)
|
||||||
|
while not request.confirm_received.is_set():
|
||||||
|
sender.receive(update_state=None)
|
||||||
|
|
||||||
|
print('Success?')
|
||||||
|
print(request.result.stringify())
|
||||||
|
|
||||||
|
|
||||||
def get_int(byte_array, signed=True):
|
def get_int(byte_array, signed=True):
|
||||||
"""Gets the specified integer from its byte array.
|
"""Gets the specified integer from its byte array.
|
||||||
This should be used by the authenticator,
|
This should be used by the authenticator,
|
||||||
|
|
|
@ -601,7 +601,7 @@ class TelegramBareClient:
|
||||||
is_large = file_size > 10 * 1024 * 1024
|
is_large = file_size > 10 * 1024 * 1024
|
||||||
part_count = (file_size + part_size - 1) // part_size
|
part_count = (file_size + part_size - 1) // part_size
|
||||||
|
|
||||||
file_id = utils.generate_random_long()
|
file_id = utils.random_long()
|
||||||
hash_md5 = md5()
|
hash_md5 = md5()
|
||||||
|
|
||||||
stream = open(file, 'rb') if isinstance(file, str) else BytesIO(file)
|
stream = open(file, 'rb') if isinstance(file, str) else BytesIO(file)
|
||||||
|
|
|
@ -58,7 +58,7 @@ class Session:
|
||||||
self._msg_id_lock = Lock()
|
self._msg_id_lock = Lock()
|
||||||
self._save_lock = Lock()
|
self._save_lock = Lock()
|
||||||
|
|
||||||
self.id = helpers.generate_random_long(signed=False)
|
self.id = helpers.random_long(signed=False)
|
||||||
self._sequence = 0
|
self._sequence = 0
|
||||||
self.time_offset = 0
|
self.time_offset = 0
|
||||||
self._last_msg_id = 0 # Long
|
self._last_msg_id = 0 # Long
|
||||||
|
|
|
@ -51,6 +51,8 @@ destroy_auth_key_ok#f660e1d4 = DestroyAuthKeyRes;
|
||||||
destroy_auth_key_none#0a9f2259 = DestroyAuthKeyRes;
|
destroy_auth_key_none#0a9f2259 = DestroyAuthKeyRes;
|
||||||
destroy_auth_key_fail#ea109b13 = DestroyAuthKeyRes;
|
destroy_auth_key_fail#ea109b13 = DestroyAuthKeyRes;
|
||||||
|
|
||||||
|
bind_auth_key_inner#75a3f765 nonce:long temp_auth_key_id:long perm_auth_key_id:long temp_session_id:long expires_at:int = BindAuthKeyInner;
|
||||||
|
|
||||||
---functions---
|
---functions---
|
||||||
|
|
||||||
req_pq#60469778 nonce:int128 = ResPQ;
|
req_pq#60469778 nonce:int128 = ResPQ;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user