Exemplo n.º 1
0
def new_server_session(keys, pin):
    """Create SRP server session."""
    context = SRPContext(
        "Pair-Setup",
        str(pin),
        prime=constants.PRIME_3072,
        generator=constants.PRIME_3072_GEN,
        hash_func=hashlib.sha512,
        bits_salt=128,
        bits_random=512,
    )
    username, verifier, salt = context.get_user_data_triplet()

    context_server = SRPContext(
        username,
        prime=constants.PRIME_3072,
        generator=constants.PRIME_3072_GEN,
        hash_func=hashlib.sha512,
        bits_salt=128,
        bits_random=512,
    )

    session = SRPServerSession(
        context_server, verifier, binascii.hexlify(keys.auth).decode()
    )

    return session, salt
Exemplo n.º 2
0
def get_user_data_triplet(username, password):
    """Print out user data triplet: username, password verifier, salt."""
    context = SRPContext(username, password)
    username, password_verifier, salt = context.get_user_data_triplet(
        base64=True)

    click.secho('Username: %s' % username)
    click.secho('Password verifier: %s' % password_verifier)
    click.secho('Salt: %s' % salt)
Exemplo n.º 3
0
from srptools import SRPContext, SRPServerSession, SRPClientSession

username = '******'
password = '******'

### at the client, where the password is known

srp_context = SRPContext(username, password)  # gen, prime
user, password_verifier, salt = srp_context.get_user_data_triplet()
gen = srp_context.generator
prime = srp_context.prime

#####
# HERE WE ARE IN THE SERVER
# client send the server user, password_verifier, salt, gen, prime
server_context = SRPContext(username, prime=prime, generator=gen)

###########
# authentication starts here
server_session = SRPServerSession(server_context, password_verifier)

server_public_B = server_session.public  # to be sent to the client

#####
# HERE WE ARE IN THE CLIENT
client_session = SRPClientSession(srp_context)
client_public_A = client_session.public

# the client has received B
# the server has received A
Exemplo n.º 4
0
except socket.error as msg:
    print('Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1])
    sys.exit()
print('Socket bind complete')

s.listen(10)
print('Socket now listening')

#wait to accept a connection - blocking call
while 1:
    conn, addr = s.accept()
    print('A new padding test requested by ' + addr[0] + ':' + str(addr[1]))

    #this should be done offline, only once, the server should store somewhere these data
    context = SRPContext(USERNAME, PASSWORD)
    username, password_verifier, server_salt = context.get_user_data_triplet()
    print(username)
    print(password_verifier)
    print(server_salt)

    prime = context.prime
    gen = context.generator
    print(prime)
    print(gen)

    # generate user-based server parameters
    server_session = SRPServerSession(
        SRPContext(username, prime=prime, generator=gen), password_verifier)
    server_public_B = server_session.public
    print(server_public_B)
Exemplo n.º 5
0
class ProxyMrpAppleTV(asyncio.Protocol):  # pylint: disable=too-many-instance-attributes  # noqa
    """Implementation of a fake MRP Apple TV."""
    def __init__(self, loop, credentials, atv_device_id):
        """Initialize a new instance of ProxyMrpAppleTV."""
        self.loop = loop
        self.credentials = credentials
        self.atv_device_id = atv_device_id
        self.server = None
        self.buffer = b''
        self.has_paired = False
        self.transport = None
        self.chacha = None
        self.connection = None
        self.input_key = None
        self.output_key = None
        self.mapping = {
            protobuf.DEVICE_INFO_MESSAGE: self.handle_device_info,
            protobuf.CRYPTO_PAIRING_MESSAGE: self.handle_crypto_pairing,
        }

        self._shared = None
        self._session_key = None
        self._signing_key = SigningKey(32 * b'\x01')
        self._auth_private = self._signing_key.to_seed()
        self._auth_public = self._signing_key.get_verifying_key().to_bytes()
        self._verify_private = curve25519.Private(secret=32 * b'\x01')
        self._verify_public = self._verify_private.get_public()

        self.context = SRPContext('Pair-Setup',
                                  str(1111),
                                  prime=constants.PRIME_3072,
                                  generator=constants.PRIME_3072_GEN,
                                  hash_func=hashlib.sha512,
                                  bits_salt=128)
        self.username, self.verifier, self.salt = \
            self.context.get_user_data_triplet()

        context_server = SRPContext('Pair-Setup',
                                    prime=constants.PRIME_3072,
                                    generator=constants.PRIME_3072_GEN,
                                    hash_func=hashlib.sha512,
                                    bits_salt=128)

        self._session = SRPServerSession(
            context_server, self.verifier,
            binascii.hexlify(self._auth_private).decode())

    def start(self, address, port):
        """Start the proxy instance."""
        # Establish connection to ATV
        self.connection = MrpConnection(address, port, self.loop)
        protocol = MrpProtocol(
            self.loop, self.connection, SRPAuthHandler(),
            MrpService(None, port, credentials=self.credentials))
        self.loop.run_until_complete(
            protocol.start(skip_initial_messages=True))
        self.connection.listener = self

        # Setup server used to publish a fake MRP server
        coro = self.loop.create_server(lambda: self, '0.0.0.0')
        self.server = self.loop.run_until_complete(coro)
        _LOGGER.info('Started MRP server at port %d', self.port)

    @property
    def port(self):
        """Port used by MRP proxy server."""
        return self.server.sockets[0].getsockname()[1]

    def connection_made(self, transport):
        """Client did connect to proxy."""
        self.transport = transport

    def _send(self, message):
        data = message.SerializeToString()
        _LOGGER.info('<<(DECRYPTED): %s', message)
        if self.chacha:
            data = self.chacha.encrypt(data)
            log_binary(_LOGGER, '<<(ENCRYPTED)', Message=message)

        length = variant.write_variant(len(data))
        self.transport.write(length + data)

    def _send_raw(self, raw):
        parsed = protobuf.ProtocolMessage()
        parsed.ParseFromString(raw)

        log_binary(_LOGGER, 'ATV->APP', Raw=raw)
        _LOGGER.info('ATV->APP Parsed: %s', parsed)
        if self.chacha:
            raw = self.chacha.encrypt(raw)
            log_binary(_LOGGER, 'ATV->APP', Encrypted=raw)

        length = variant.write_variant(len(raw))
        try:
            self.transport.write(length + raw)
        except Exception:  # pylint: disable=broad-except
            _LOGGER.exception('Failed to send to app')

    def message_received(self, _, raw):
        """Message received from ATV."""
        self._send_raw(raw)

    def data_received(self, data):
        """Message received from iOS app/client."""
        self.buffer += data

        while self.buffer:
            length, raw = variant.read_variant(self.buffer)
            if len(raw) < length:
                break

            data = raw[:length]
            self.buffer = raw[length:]
            if self.chacha:
                log_binary(_LOGGER, 'ENC Phone->ATV', Encrypted=data)
                data = self.chacha.decrypt(data)

            parsed = protobuf.ProtocolMessage()
            parsed.ParseFromString(data)
            _LOGGER.info('(DEC Phone->ATV): %s', parsed)

            try:

                def unhandled_message(_, raw):
                    self.connection.send_raw(raw)

                self.mapping.get(parsed.type, unhandled_message)(parsed, data)
            except Exception:  # pylint: disable=broad-except
                _LOGGER.exception('Error while dispatching message')

    def handle_device_info(self, message, _):
        """Handle received device information message."""
        _LOGGER.debug('Received device info message')

        # TODO: Consolidate this better with message.device_information(...)
        resp = messages.create(protobuf.DEVICE_INFO_MESSAGE)
        resp.identifier = message.identifier
        resp.inner().uniqueIdentifier = self.atv_device_id.decode()
        resp.inner().name = 'ATVProxy'
        resp.inner().systemBuildVersion = '15K600'
        resp.inner().applicationBundleIdentifier = 'com.apple.mediaremoted'
        resp.inner().protocolVersion = 1
        resp.inner().lastSupportedMessageType = 58
        resp.inner().supportsSystemPairing = True
        resp.inner().allowsPairing = True
        resp.inner().systemMediaApplication = "com.apple.TVMusic"
        resp.inner().supportsACL = True
        resp.inner().supportsSharedQueue = True
        resp.inner().supportsExtendedMotion = True
        resp.inner().sharedQueueVersion = 2
        self._send(resp)

    def handle_crypto_pairing(self, message, _):
        """Handle incoming crypto pairing message."""
        _LOGGER.debug('Received crypto pairing message')
        pairing_data = tlv8.read_tlv(message.inner().pairingData)
        seqno = pairing_data[tlv8.TLV_SEQ_NO][0]
        getattr(self, "_seqno_" + str(seqno))(pairing_data)

    def _seqno_1(self, pairing_data):
        if self.has_paired:
            server_pub_key = self._verify_public.serialize()
            client_pub_key = pairing_data[tlv8.TLV_PUBLIC_KEY]

            self._shared = self._verify_private.get_shared_key(
                curve25519.Public(client_pub_key), hashfunc=lambda x: x)

            session_key = hkdf_expand('Pair-Verify-Encrypt-Salt',
                                      'Pair-Verify-Encrypt-Info', self._shared)

            info = server_pub_key + self.atv_device_id + client_pub_key
            signature = SigningKey(self._signing_key.to_seed()).sign(info)

            tlv = tlv8.write_tlv({
                tlv8.TLV_IDENTIFIER: self.atv_device_id,
                tlv8.TLV_SIGNATURE: signature
            })

            chacha = chacha20.Chacha20Cipher(session_key, session_key)
            encrypted = chacha.encrypt(tlv, nounce='PV-Msg02'.encode())

            msg = messages.crypto_pairing({
                tlv8.TLV_SEQ_NO: b'\x02',
                tlv8.TLV_PUBLIC_KEY: server_pub_key,
                tlv8.TLV_ENCRYPTED_DATA: encrypted
            })

            self.output_key = hkdf_expand('MediaRemote-Salt',
                                          'MediaRemote-Write-Encryption-Key',
                                          self._shared)

            self.input_key = hkdf_expand('MediaRemote-Salt',
                                         'MediaRemote-Read-Encryption-Key',
                                         self._shared)

            log_binary(_LOGGER,
                       'Keys',
                       Output=self.output_key,
                       Input=self.input_key)

        else:
            msg = messages.crypto_pairing({
                tlv8.TLV_SALT:
                binascii.unhexlify(self.salt),
                tlv8.TLV_PUBLIC_KEY:
                binascii.unhexlify(self._session.public),
                tlv8.TLV_SEQ_NO:
                b'\x02'
            })

        self._send(msg)

    def _seqno_3(self, pairing_data):
        if self.has_paired:
            self._send(messages.crypto_pairing({tlv8.TLV_SEQ_NO: b'\x04'}))
            self.chacha = chacha20.Chacha20Cipher(self.input_key,
                                                  self.output_key)
        else:
            pubkey = binascii.hexlify(
                pairing_data[tlv8.TLV_PUBLIC_KEY]).decode()
            self._session.process(pubkey, self.salt)

            proof = binascii.unhexlify(self._session.key_proof_hash)
            assert self._session.verify_proof(
                binascii.hexlify(pairing_data[tlv8.TLV_PROOF]))

            msg = messages.crypto_pairing({
                tlv8.TLV_PROOF: proof,
                tlv8.TLV_SEQ_NO: b'\x04'
            })
            self._send(msg)

    def _seqno_5(self, _):
        self._session_key = hkdf_expand('Pair-Setup-Encrypt-Salt',
                                        'Pair-Setup-Encrypt-Info',
                                        binascii.unhexlify(self._session.key))

        acc_device_x = hkdf_expand('Pair-Setup-Accessory-Sign-Salt',
                                   'Pair-Setup-Accessory-Sign-Info',
                                   binascii.unhexlify(self._session.key))

        device_info = acc_device_x + self.atv_device_id + self._auth_public
        signature = self._signing_key.sign(device_info)

        tlv = tlv8.write_tlv({
            tlv8.TLV_IDENTIFIER: self.atv_device_id,
            tlv8.TLV_PUBLIC_KEY: self._auth_public,
            tlv8.TLV_SIGNATURE: signature
        })

        chacha = chacha20.Chacha20Cipher(self._session_key, self._session_key)
        encrypted = chacha.encrypt(tlv, nounce='PS-Msg06'.encode())

        msg = messages.crypto_pairing({
            tlv8.TLV_SEQ_NO: b'\x06',
            tlv8.TLV_ENCRYPTED_DATA: encrypted,
        })
        self.has_paired = True

        self._send(msg)