def unpack_message(session, reader): """Unpacks a message following MtProto 2.0 guidelines""" # See https://core.telegram.org/mtproto/description if reader.read_long(signed=False) != session.auth_key.key_id: raise SecurityError('Server replied with an invalid auth key') msg_key = reader.read(16) aes_key, aes_iv = calc_key(session.auth_key.key, msg_key, False) data = BinaryReader(AES.decrypt_ige(reader.read(), aes_key, aes_iv)) data.read_long() # remote_salt if data.read_long() != session.id: raise SecurityError('Server replied with a wrong session ID') remote_msg_id = data.read_long() remote_sequence = data.read_int() msg_len = data.read_int() message = data.read(msg_len) # https://core.telegram.org/mtproto/security_guidelines # Sections "checking sha256 hash" and "message length" if msg_key != sha256(session.auth_key.key[96:96 + 32] + data.get_bytes()).digest()[8:24]: raise SecurityError("Received msg_key doesn't match with expected one") return message, remote_msg_id, remote_sequence
async def complete_rekey(self, peer, action: DecryptedMessageActionCommitKey): peer = self.get_secret_chat(peer) if peer.rekeying[0] != 2 or self.temp_rekeyed_secret_chats.get( action.exchange_id, None): return if self.temp_rekeyed_secret_chats.get( action.exchange_id) != action.key_fingerprint: message = DecryptedMessageService( action=DecryptedMessageActionAbortKey( exchange_id=action.exchange_id, )) message = await self.encrypt_secret_message(peer, message) await self.client( SendEncryptedServiceRequest( InputEncryptedChat(peer.id, peer.access_hash), message)) raise SecurityError("Invalid Key fingerprint") self._log.debug(f'Completing rekeying secret chat {peer}') peer.rekeying = [0] peer.auth_key = self.temp_rekeyed_secret_chats[action.exchange_id] peer.ttr = 100 peer.updated = time() del self.temp_rekeyed_secret_chats[action.exchange_id] message = DecryptedMessageService(action=DecryptedMessageActionNoop()) message = await self.encrypt_secret_message(peer, message) await self.client( SendEncryptedServiceRequest( InputEncryptedChat(peer.id, peer.access_hash), message)) self._log.debug(f'Secret chat {peer} rekeyed succrsfully')
async def commit_rekey(self, peer, action: DecryptedMessageActionAcceptKey): peer = self.get_secret_chat(peer) if peer.rekeying[0] != 1 or not self._temp_rekeyed_secret_chats.get(action.exchange_id, None): peer.rekeying = [0] return self._log.debug(f'Committing rekeying secret chat {peer}') dh_config = await self.get_dh_config() g_b = int.from_bytes(action.g_b, 'big', signed=False) self.check_g_a(g_b, dh_config.p) res = pow(g_b, self._temp_rekeyed_secret_chats[action.exchange_id], dh_config.p) auth_key = res.to_bytes(256, 'big', signed=False) key_fingerprint = struct.unpack('<q', sha1(auth_key).digest()[-8:])[0] if key_fingerprint != action.key_fingerprint: message = DecryptedMessageService(action=DecryptedMessageActionAbortKey( exchange_id=action.exchange_id, )) message = await self.encrypt_secret_message(peer, message) await self.client(SendEncryptedServiceRequest(InputEncryptedChat(peer.id, peer.access_hash), message)) raise SecurityError("Invalid Key fingerprint") message = DecryptedMessageService(action=DecryptedMessageActionCommitKey( exchange_id=action.exchange_id, key_fingerprint=key_fingerprint )) message = await self.encrypt_secret_message(peer, message) await self.client(SendEncryptedServiceRequest(InputEncryptedChat(peer.id, peer.access_hash), message)) del self._temp_rekeyed_secret_chats[action.exchange_id] peer.rekeying = [0] peer.auth_key = auth_key peer.ttl = 100 peer.updated = time()
def decrypt_mtproto1(self, message_key, chat_id, encrypted_data): aes_key, aes_iv = _old_calc_key( self.get_secret_chat(chat_id).auth_key, message_key, True) decrypted_data = AES.decrypt_ige(encrypted_data, aes_key, aes_iv) message_data_length = struct.unpack('<I', decrypted_data[:4])[0] message_data = decrypted_data[4:message_data_length + 4] if message_data_length > len(decrypted_data): raise SecurityError("message data length is too big") if message_key != sha1( decrypted_data[:4 + message_data_length]).digest()[-16:]: raise SecurityError("Message key mismatch") if len(decrypted_data) - 4 - message_data_length > 15: raise SecurityError("Difference is too big") if len(decrypted_data) % 16 != 0: raise SecurityError("Decrypted data can not be divided by 16") return BinaryReader(message_data).tgread_object()
def decrypt_mtproto2(self, message_key, chat_id, encrypted_data): peer = self.get_secret_chat(chat_id) aes_key, aes_iv = MTProtoState._calc_key(peer.auth_key, message_key, not peer.admin) decrypted_data = AES.decrypt_ige(encrypted_data, aes_key, aes_iv) message_data_length = struct.unpack('<I', decrypted_data[:4])[0] message_data = decrypted_data[4:message_data_length + 4] if message_data_length > len(decrypted_data): raise SecurityError("message data length is too big") is_admin = (8 if peer.admin else 0) first_str = peer.auth_key[88 + is_admin:88 + 32 + is_admin] if message_key != sha256(first_str + decrypted_data).digest()[8:24]: raise SecurityError("Message key mismatch") if len(decrypted_data) - 4 - message_data_length < 12: raise SecurityError("Padding is too small") if len(decrypted_data) % 16 != 0: raise SecurityError("Decrpyted data not divisble by 16") return BinaryReader(message_data).tgread_object()
async def download_secret_media(self, message): if not message.file or not isinstance(message.file, EncryptedFile): return b"" key_fingerprint = message.file.key_fingerprint key = message.media.key iv = message.media.iv digest = md5(key + iv).digest() fingerprint = int.from_bytes(digest[:4], byteorder="little", signed=True) ^ int.from_bytes(digest[4:8], byteorder="little", signed=True) if fingerprint != key_fingerprint: raise SecurityError("Wrong fingerprint") media = await self.client.download_file(InputEncryptedFileLocation(message.file.id, message.file.access_hash), key=message.media.key, iv=message.media.iv) return media