def _recv_messages(self): for message in self.connection: if not self.connected: break if self.channel_key: if self.channel_hmac: try: message = crypto.symmetric_decrypt_HMAC( message, self.channel_key, self.channel_hmac) except RuntimeError as e: self._LOG.exception(e) break else: message = crypto.symmetric_decrypt(message, self.channel_key) gevent.spawn(self._parse_message, message) self.idle() if not self._seen_logon and self.channel_secured: if self.wait_event('disconnected', timeout=5) is not None: return gevent.spawn(self.disconnect)
def test_encryption_legacy(self): message = b'My secret message' key = b'9' * 32 cyphertext = crypto.symmetric_encrypt(message, key) dmessage = crypto.symmetric_decrypt(cyphertext, key) self.assertEqual(message, dmessage)
def decrypt_filenames(self, depot_key): """Decrypt all filenames in the manifest :param depot_key: depot key :type depot_key: bytes :raises: :class:`RuntimeError` """ if not self.metadata.filenames_encrypted: return try: for m in self.payload.mappings: m.filename = symmetric_decrypt(b64decode(m.filename), depot_key) if m.linktarget: m.linktarget = symmetric_decrypt(b64decode(m.linktarget), depot_key) except Exception: raise RuntimeError("Unable to decrypt filename for depot manifest") self.metadata.filenames_encrypted = False
def get_chunk(self, app_id, depot_id, chunk_id): """Download a single content chunk :param app_id: App ID :type app_id: int :param depot_id: Depot ID :type depot_id: int :param chunk_id: Chunk ID :type chunk_id: int :returns: chunk data :rtype: bytes :raises SteamError: error message """ if (depot_id, chunk_id) not in self._chunk_cache: resp = self.cdn_cmd('depot', '%s/chunk/%s' % (depot_id, chunk_id)) data = symmetric_decrypt(resp.content, self.get_depot_key(app_id, depot_id)) if data[:2] == b'VZ': if data[-2:] != b'zv': raise SteamError("VZ: Invalid footer: %s" % repr(data[-2:])) if data[2:3] != b'a': raise SteamError("VZ: Invalid version: %s" % repr(data[2:3])) vzfilter = lzma._decode_filter_properties( lzma.FILTER_LZMA1, data[7:12]) vzdec = lzma.LZMADecompressor(lzma.FORMAT_RAW, filters=[vzfilter]) checksum, decompressed_size = struct.unpack( '<II', data[-10:-2]) # decompress_size is needed since lzma will sometime produce longer output # [12:-9] is need as sometimes lzma will produce shorter output # together they get us the right data data = vzdec.decompress(data[12:-9])[:decompressed_size] if crc32(data) != checksum: raise SteamError( "VZ: CRC32 checksum doesn't match for decompressed data" ) else: with ZipFile(BytesIO(data)) as zf: data = zf.read(zf.filelist[0]) self._chunk_cache[(depot_id, chunk_id)] = data return self._chunk_cache[(depot_id, chunk_id)]
def test_encryption(self): message = b'My secret message' key = b'9' * 32 hmac = b'3' * 16 # legacy cyphertext = crypto.symmetric_encrypt(message, key) dmessage = crypto.symmetric_decrypt(cyphertext, key) self.assertEqual(message, dmessage) # with HMAC cyphertext = crypto.symmetric_encrypt_HMAC(message, key, hmac) dmessage = crypto.symmetric_decrypt_HMAC(cyphertext, key, hmac) self.assertEqual(message, dmessage) # failing HMAC check with self.assertRaises(RuntimeError): crypto.symmetric_decrypt_HMAC(cyphertext, key, b'4'*16)
def decrypt_filenames(self, depot_key): """Decrypt all filenames in the manifest :param depot_key: depot key :type depot_key: bytes :raises: :class:`RuntimeError` """ if not self.metadata.filenames_encrypted: return for mapping in self.payload.mappings: filename = b64decode(mapping.filename) try: filename = symmetric_decrypt(filename, depot_key) except Exception: RuntimeError("Unable to decrypt filename for depot manifest") mapping.filename = filename self.metadata.filenames_encrypted = False
def _recv_messages(self): for message in self.connection: if not self.connected: break if self.channel_key: if self.channel_hmac: try: message = crypto.symmetric_decrypt_HMAC(message, self.channel_key, self.channel_hmac) except RuntimeError as e: self._LOG.exception(e) break else: message = crypto.symmetric_decrypt(message, self.channel_key) gevent.spawn(self._parse_message, message) gevent.idle() if not self._seen_logon and self.channel_secured: if self.wait_event('disconnected', timeout=5) is not None: return gevent.spawn(self.disconnect)