def encrypt(self, data, offset=None, length=None): """Encrypts the given data with the current key""" if offset is None: offset = 0 if length is None: length = len(data) with BinaryWriter() as writer: # Write SHA writer.write(utils.sha1(data[offset:offset + length])) # Write data writer.write(data[offset:offset + length]) # Add padding if required if length < 235: writer.write(os.urandom(235 - length)) result = int.from_bytes(writer.get_bytes(), byteorder='big') result = pow(result, self.e, self.m) # If the result byte count is less than 256, since the byte order is big, # the non-used bytes on the left will be 0 and act as padding, # without need of any additional checks return int.to_bytes(result, length=256, byteorder='big', signed=False)
def __init__(self, data): self.key = data with BinaryReader(utils.sha1(self.key)) as reader: self.aux_hash = reader.read_long(signed=False) reader.read(4) self.key_id = reader.read_long(signed=False)
def test_sha1(): string = 'Example string' hashsum = utils.sha1(string.encode('utf-8')) 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 {})'\ .format(expected, hashsum)
def do_authentication(transport): """Executes the authentication process with the Telegram servers. If no error is rose, returns both the authorization key and the time offset""" sender = MtProtoPlainSender(transport) # Step 1 sending: PQ Request nonce = os.urandom(16) with BinaryWriter() as writer: writer.write_int(0x60469778, signed=False) # Constructor number writer.write(nonce) sender.send(writer.get_bytes()) # Step 1 response: PQ Request pq, pq_bytes, server_nonce, fingerprints = None, None, None, [] with BinaryReader(sender.receive()) as reader: response_code = reader.read_int(signed=False) if response_code != 0x05162463: raise AssertionError('Invalid response code: {}'.format( hex(response_code))) nonce_from_server = reader.read(16) if nonce_from_server != nonce: raise AssertionError('Invalid nonce from server') server_nonce = reader.read(16) pq_bytes = reader.tgread_bytes() pq = get_int(pq_bytes) vector_id = reader.read_int() if vector_id != 0x1cb5c415: raise AssertionError('Invalid vector constructor ID: {}'.format( hex(response_code))) fingerprints = [] fingerprint_count = reader.read_int() for _ in range(fingerprint_count): fingerprints.append(reader.read(8)) # Step 2 sending: DH Exchange new_nonce = os.urandom(32) p, q = Factorizator.factorize(pq) with BinaryWriter() as pq_inner_data_writer: pq_inner_data_writer.write_int( 0x83c95aec, signed=False) # PQ Inner Data pq_inner_data_writer.tgwrite_bytes(get_byte_array(pq, signed=False)) pq_inner_data_writer.tgwrite_bytes( get_byte_array( min(p, q), signed=False)) pq_inner_data_writer.tgwrite_bytes( get_byte_array( max(p, q), signed=False)) pq_inner_data_writer.write(nonce) pq_inner_data_writer.write(server_nonce) pq_inner_data_writer.write(new_nonce) cipher_text, target_fingerprint = None, None for fingerprint in fingerprints: cipher_text = RSA.encrypt( get_fingerprint_text(fingerprint), pq_inner_data_writer.get_bytes()) if cipher_text is not None: target_fingerprint = fingerprint break if cipher_text is None: raise AssertionError( 'Could not find a valid key for fingerprints: {}' .format(', '.join([get_fingerprint_text(f) for f in fingerprints]))) with BinaryWriter() as req_dh_params_writer: req_dh_params_writer.write_int( 0xd712e4be, signed=False) # Req DH Params req_dh_params_writer.write(nonce) req_dh_params_writer.write(server_nonce) req_dh_params_writer.tgwrite_bytes( get_byte_array( min(p, q), signed=False)) req_dh_params_writer.tgwrite_bytes( get_byte_array( max(p, q), signed=False)) req_dh_params_writer.write(target_fingerprint) req_dh_params_writer.tgwrite_bytes(cipher_text) req_dh_params_bytes = req_dh_params_writer.get_bytes() sender.send(req_dh_params_bytes) # Step 2 response: DH Exchange encrypted_answer = None with BinaryReader(sender.receive()) as reader: response_code = reader.read_int(signed=False) if response_code == 0x79cb045d: raise AssertionError('Server DH params fail: TODO') if response_code != 0xd0e8075c: raise AssertionError('Invalid response code: {}'.format( hex(response_code))) nonce_from_server = reader.read(16) if nonce_from_server != nonce: raise NotImplementedError('Invalid nonce from server') server_nonce_from_server = reader.read(16) if server_nonce_from_server != server_nonce: raise NotImplementedError('Invalid server nonce from server') encrypted_answer = reader.tgread_bytes() # Step 3 sending: Complete DH Exchange key, iv = utils.generate_key_data_from_nonces(server_nonce, new_nonce) plain_text_answer = AES.decrypt_ige(encrypted_answer, key, iv) g, dh_prime, ga, time_offset = None, None, None, None with BinaryReader(plain_text_answer) as dh_inner_data_reader: dh_inner_data_reader.read(20) # hashsum code = dh_inner_data_reader.read_int(signed=False) if code != 0xb5890dba: raise AssertionError('Invalid DH Inner Data code: {}'.format(code)) nonce_from_server1 = dh_inner_data_reader.read(16) if nonce_from_server1 != nonce: raise AssertionError('Invalid nonce in encrypted answer') server_nonce_from_server1 = dh_inner_data_reader.read(16) if server_nonce_from_server1 != server_nonce: raise AssertionError('Invalid server nonce in encrypted answer') g = dh_inner_data_reader.read_int() dh_prime = get_int(dh_inner_data_reader.tgread_bytes(), signed=False) ga = get_int(dh_inner_data_reader.tgread_bytes(), signed=False) server_time = dh_inner_data_reader.read_int() time_offset = server_time - int(time.time()) b = get_int(os.urandom(2048), signed=False) gb = pow(g, b, dh_prime) gab = pow(ga, b, dh_prime) # Prepare client DH Inner Data with BinaryWriter() as client_dh_inner_data_writer: client_dh_inner_data_writer.write_int( 0x6643b654, signed=False) # Client DH Inner Data client_dh_inner_data_writer.write(nonce) client_dh_inner_data_writer.write(server_nonce) client_dh_inner_data_writer.write_long(0) # TODO retry_id client_dh_inner_data_writer.tgwrite_bytes( get_byte_array( gb, signed=False)) with BinaryWriter() as client_dh_inner_data_with_hash_writer: client_dh_inner_data_with_hash_writer.write( utils.sha1(client_dh_inner_data_writer.get_bytes())) client_dh_inner_data_with_hash_writer.write( client_dh_inner_data_writer.get_bytes()) client_dh_inner_data_bytes = client_dh_inner_data_with_hash_writer.get_bytes( ) # Encryption client_dh_inner_data_encrypted_bytes = AES.encrypt_ige( client_dh_inner_data_bytes, key, iv) # Prepare Set client DH params with BinaryWriter() as set_client_dh_params_writer: set_client_dh_params_writer.write_int(0xf5045f1f, signed=False) set_client_dh_params_writer.write(nonce) set_client_dh_params_writer.write(server_nonce) set_client_dh_params_writer.tgwrite_bytes( client_dh_inner_data_encrypted_bytes) set_client_dh_params_bytes = set_client_dh_params_writer.get_bytes() sender.send(set_client_dh_params_bytes) # Step 3 response: Complete DH Exchange with BinaryReader(sender.receive()) as reader: code = reader.read_int(signed=False) if code == 0x3bcbf734: # DH Gen OK nonce_from_server = reader.read(16) if nonce_from_server != nonce: raise NotImplementedError('Invalid nonce from server') server_nonce_from_server = reader.read(16) if server_nonce_from_server != server_nonce: raise NotImplementedError('Invalid server nonce from server') new_nonce_hash1 = reader.read(16) auth_key = AuthKey(get_byte_array(gab, signed=False)) new_nonce_hash_calculated = auth_key.calc_new_nonce_hash(new_nonce, 1) if new_nonce_hash1 != new_nonce_hash_calculated: raise AssertionError('Invalid new nonce hash') return auth_key, time_offset elif code == 0x46dc1fb9: # DH Gen Retry raise NotImplementedError('dh_gen_retry') elif code == 0xa69dae02: # DH Gen Fail raise NotImplementedError('dh_gen_fail') else: raise AssertionError('DH Gen unknown: {}'.format(hex(code)))