Skip to content

Commit

Permalink
Merge 940fa01 into c4a29d2
Browse files Browse the repository at this point in the history
  • Loading branch information
postlund committed Oct 16, 2019
2 parents c4a29d2 + 940fa01 commit e216784
Show file tree
Hide file tree
Showing 8 changed files with 451 additions and 33 deletions.
15 changes: 12 additions & 3 deletions pyatv/mrp/connection.py
Expand Up @@ -67,7 +67,16 @@ def send(self, message):
self._transport.write(data)
_LOGGER.debug('>> Send: Protobuf=%s', message)

# TODO: read messages from buffer with loop
def send_raw(self, data):
"""Send message to device."""
log_binary(_LOGGER, '>> Send raw', Data=data)
if self._chacha:
data = self._chacha.encrypt(data)
log_binary(_LOGGER, '>> Send raw', Encrypted=data)

data = write_variant(len(data)) + data
self._transport.write(data)

def data_received(self, data):
"""Message was received from device."""
# A message might be split over several reads, so we store a buffer and
Expand All @@ -89,7 +98,7 @@ def data_received(self, data):
try:
self._handle_message(data)
except Exception: # pylint: disable=broad-except
_LOGGER.error('Failed to handle message')
_LOGGER.exception('Failed to handle message')

def _handle_message(self, data):
if self._chacha:
Expand All @@ -100,4 +109,4 @@ def _handle_message(self, data):
parsed.ParseFromString(data)
_LOGGER.debug('<< Receive: Protobuf=%s', parsed)
if self.listener:
self.listener.message_received(parsed)
self.listener.message_received(parsed, data)
16 changes: 11 additions & 5 deletions pyatv/mrp/messages.py
Expand Up @@ -20,15 +20,21 @@ def device_information(name, identifier):
# pylint: disable=no-member
message = create(protobuf.DEVICE_INFO_MESSAGE)
info = message.inner()
info.uniqueIdentifier = identifier
info.name = name
info.localizedModelName = 'iPhone'
info.systemBuildVersion = '14G60'
info.allowsPairing = True
info.applicationBundleIdentifier = 'com.apple.TVRemote'
info.applicationBundleVersion = '273.12'
info.protocolVersion = 1
info.lastSupportedMessageType = 58
info.localizedModelName = 'iPhone'
info.name = name
info.protocolVersion = 1
info.sharedQueueVersion = 2
info.supportsACL = True
info.supportsExtendedMotion = True
info.supportsSharedQueue = True
info.supportsSystemPairing = True
info.systemBuildVersion = '14G60'
info.systemMediaApplication = "com.apple.TVMusic"
info.uniqueIdentifier = identifier
return message


Expand Down
12 changes: 10 additions & 2 deletions pyatv/mrp/protocol.py
Expand Up @@ -50,7 +50,7 @@ def add_listener(self, listener, message_type, data=None, one_shot=False):
lst[message_type].append(Listener(listener, data))

# TODO: This method is too big for its own good. Must split and clean up.
async def start(self):
async def start(self, skip_initial_messages=False):
"""Connect to device and listen to incoming messages."""
if self.connection.connected:
return
Expand All @@ -67,9 +67,17 @@ async def start(self):
# device will not respond with anything
msg = messages.device_information(
'pyatv', self.srp.pairing_id.decode())

await self.send_and_receive(msg)
self._initial_message_sent = True

# This is a hack to support re-use of a protocol object in
# proxy (will be removed/refactored later)
if skip_initial_messages:
return

await self._connect_and_encrypt()

# This should be the first message sent after encryption has
# been enabled
await self.send(messages.set_ready_state())
Expand Down Expand Up @@ -169,7 +177,7 @@ async def _receive(self, identifier, timeout):
del self._outstanding[identifier]
return response

def message_received(self, message):
def message_received(self, message, _):
"""Message was received from device."""
# If the message identifer is outstanding, then someone is
# waiting for the respone so we save it here
Expand Down
3 changes: 1 addition & 2 deletions scripts/autogen_protobuf_extensions.py
Expand Up @@ -84,7 +84,7 @@ def extract_message_info():


def extract_unreferenced_messages():

"""Get messages not referenced anywhere."""
base_path = BASE_PACKAGE.replace('.', '/')

for filename in os.listdir(base_path):
Expand Down Expand Up @@ -122,7 +122,6 @@ def main():
'{0} = ProtocolMessage.{0}'.format(
info.const))


# Look for remaining messages
for module_name, message_name in extract_unreferenced_messages():
if message_name not in message_names:
Expand Down
49 changes: 30 additions & 19 deletions scripts/chacha20_decrypt.py
Expand Up @@ -9,38 +9,49 @@
# Data decrypted!
# - Nounce : 000000000000000000000000
# - Key : 7f0a54c5b15ccafc4927582c11d3394a55c95e489e72d12222a91b06f34c6094
# - Data : 080b200082012d0a2b6b4d5254656c65766973696f6e52656d6f74654e6f77506c6179696e67417274776f726b4368616e676564
# - Data : 080b200082012d0a2b6b4d5254656c65766973696f6e52656d6f74654e6f7750
# 6c6179696e67417274776f726b4368616e676564
#
"""Manually decrypt some ChaCha20Poly1305 encrypted data."""

import sys
import binascii
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305


# Example data
DATA = "E8E786E4E673A38808063FEFDAF79D66B99C0745DA45C704DE51D4855FAE752F3B92ECA29E1DF5EF6B48C72BE4E87FC660BF5CE57D768365E60397E7C97D16AB233441B9"
DATA = "E8E786E4E673A38808063FEFDAF79D66B99C0745DA45C704DE51D4855FAE752F3" \
"B92ECA29E1DF5EF6B48C72BE4E87FC660BF5CE57D768365E60397E7C97D16AB233441B9"
KEYS = ["70743888c9eda99d1a24c5a94a26f85a0fec26f4cba16f919e5c23f892ecfbab",
"7f0a54c5b15ccafc4927582c11d3394a55c95e489e72d12222a91b06f34c6094"]


def decrypt(data, nounce, key):
data = binascii.unhexlify(data.replace(' ', ''))
inputKey = binascii.unhexlify(key.replace(' ', ''))
inputNonce = b'\x00\x00\x00\x00' + nounce.to_bytes(length=8, byteorder='little')
chacha = ChaCha20Poly1305(inputKey)
try:
print('Trying key {0} with nounce {1}'.format(inputKey, inputNonce))
decryptedData = chacha.decrypt(inputNonce, data, None)
print('Data decrypted!\n - Nounce : {0}\n - Key : {1}\n - Data : {2}'.format(
binascii.hexlify(inputNonce).decode(),
binascii.hexlify(inputKey).decode(),
binascii.hexlify(decryptedData).decode()))
sys.exit(0)
except Exception as ex:
pass
"""Decrypt data with specified key and nounce."""
data = binascii.unhexlify(data.replace(' ', ''))
input_key = binascii.unhexlify(key.replace(' ', ''))
input_nonce = b'\x00\x00\x00\x00' + nounce.to_bytes(
length=8, byteorder='little')
chacha = ChaCha20Poly1305(input_key)
try:
print('Trying key {0} with nounce {1}'.format(input_key, input_nonce))
decrypted_data = chacha.decrypt(input_nonce, data, None)
print('Data decrypted!\n - Nounce : {0}'
'\n - Key : {1}\n - Data : {2}'.format(
binascii.hexlify(input_nonce).decode(),
binascii.hexlify(input_key).decode(),
binascii.hexlify(decrypted_data).decode()))
sys.exit(0)
except Exception: # pylint: disable=broad-except
pass


def main(key_set):
"""Script starts here."""
for key in key_set:
for nounce in range(128):
decrypt(DATA, nounce, key)


if __name__ == '__main__':
for key in KEYS:
for nounce in range(128):
decrypt(DATA, nounce, key)
main(KEYS)
1 change: 1 addition & 0 deletions scripts/print_protobuf.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
"""Decode and print a protobuf message from hex."""

import sys
import binascii
Expand Down

0 comments on commit e216784

Please sign in to comment.