Exemple #1
0
 def __init__(self, http, airplay_player):
     """Initialize a new AirPlayInternal instance."""
     self.player = airplay_player
     self.identifier = None
     self.srp = SRPAuthHandler()
     self.verifier = AuthenticationVerifier(http, self.srp)
     self.auther = DeviceAuthenticator(http, self.srp)
Exemple #2
0
    async def _setup_session(self):
        # Do auth-setup if MFiSAP encryption is supported by receiver
        if EncryptionType.MFiSAP in self._encryption_types:
            await self.rtsp.auth_setup()
        elif self.credentials:
            _LOGGER.debug("Verifying connection with credentials %s",
                          self.credentials)
            srp = SRPAuthHandler(self.credentials)
            srp.initialize()
            verifier = AirPlayPairingVerifier(self.rtsp.connection, srp)
            await verifier.verify_authed()

        await self.rtsp.announce()

        resp = await self.rtsp.setup(self.control_client.port,
                                     self.timing_client.port)
        _, options = parse_transport(resp.headers["Transport"])
        self.context.timing_port = int(options.get("timing_port", 0))
        self.context.control_port = int(options["control_port"])
        self.context.rtsp_session = resp.headers["Session"]
        self.context.server_port = int(options["server_port"])

        _LOGGER.debug(
            "Remote ports: control=%d, timing=%d, server=%d",
            self.context.control_port,
            self.context.timing_port,
            self.context.server_port,
        )
Exemple #3
0
async def test_verify_invalid(airplay_device, client_connection):
    srp = SRPAuthHandler(LegacyCredentials(IDENTIFIER, INVALID_AUTH_KEY))
    srp.initialize()

    verifier = AirPlayPairingVerifier(client_connection, srp)
    with pytest.raises(AuthenticationError):
        await verifier.verify_authed()
Exemple #4
0
async def test_pairing_failed(airplay_device, client_connection):
    srp = SRPAuthHandler(LegacyCredentials(IDENTIFIER, INVALID_AUTH_KEY))
    srp.initialize()

    pairing_procedure = AirPlayPairingProcedure(client_connection, srp)
    await pairing_procedure.start_pairing()
    with pytest.raises(AuthenticationError):
        await pairing_procedure.finish_pairing(DEVICE_IDENTIFIER, DEVICE_PIN)
Exemple #5
0
async def test_verify_authenticated(airplay_device, client_connection):
    srp = SRPAuthHandler(
        LegacyCredentials(IDENTIFIER, binascii.unhexlify(DEVICE_AUTH_KEY))
    )
    srp.initialize()

    verifier = AirPlayPairingVerifier(client_connection, srp)
    assert await verifier.verify_authed()
Exemple #6
0
class AirPlayPairingHandler(PairingHandler):
    """Base class for API used to pair with an Apple TV."""
    def __init__(self, config, session, _):
        """Initialize a new MrpPairingHandler."""
        super().__init__(session, config.get_service(Protocol.AirPlay))
        self.srp = SRPAuthHandler()
        self.http = net.HttpSession(
            session, 'http://{0}:{1}/'.format(config.address,
                                              self.service.port))
        self.auther = DeviceAuthenticator(self.http, self.srp)
        self.auth_data = self._setup_credentials()
        self.srp.initialize(binascii.unhexlify(self.auth_data.seed))
        self.pairing_complete = False
        self.pin_code = None

    def _setup_credentials(self):
        credentials = self.service.credentials

        # If service has credentials, use those. Otherwise generate new.
        if self.service.credentials is None:
            identifier, seed = new_credentials()
            credentials = '{0}:{1}'.format(identifier, seed.decode().upper())
        else:
            split = credentials.split(':')
            identifier = split[0]
            seed = split[1]
        return AuthData(identifier, seed, credentials)

    @property
    def has_paired(self):
        """If a successful pairing has been performed."""
        return self.pairing_complete

    async def begin(self):
        """Start pairing process."""
        _LOGGER.debug('Starting AirPlay pairing with credentials %s',
                      self.auth_data.credentials)
        self.pairing_complete = False
        return await self.auther.start_authentication()

    async def finish(self):
        """Stop pairing process."""
        if not self.pin_code:
            raise exceptions.DeviceAuthenticationError('no pin given')

        if await self.auther.finish_authentication(self.auth_data.identifier,
                                                   self.pin_code):
            self.service.credentials = self.auth_data.credentials
            self.pairing_complete = True

    def pin(self, pin):
        """Pin code used for pairing."""
        self.pin_code = str(pin).zfill(4)

    @property
    def device_provides_pin(self):
        """Return True if remote device presents PIN code, else False."""
        return True
Exemple #7
0
async def test_pairing_successful(airplay_device, client_connection):
    srp = SRPAuthHandler(
        LegacyCredentials(IDENTIFIER, binascii.unhexlify(DEVICE_AUTH_KEY))
    )
    srp.initialize()

    pairing_procedure = AirPlayPairingProcedure(client_connection, srp)
    await pairing_procedure.start_pairing()
    assert await pairing_procedure.finish_pairing(DEVICE_IDENTIFIER, DEVICE_PIN)
Exemple #8
0
class AirPlayInternal(AirPlay):
    """Implementation of API for AirPlay support."""

    def __init__(self, http, airplay_player):
        """Initialize a new AirPlayInternal instance."""
        self.player = airplay_player
        self.identifier = None
        self.srp = SRPAuthHandler()
        self.verifier = AuthenticationVerifier(http, self.srp)
        self.auther = DeviceAuthenticator(http, self.srp)

    @asyncio.coroutine
    def generate_credentials(self):
        """Create new credentials for authentication.

        Credentials that have been authenticated shall be saved and loaded with
        load_credentials before playing anything. If credentials are lost,
        authentication must be performed again.
        """
        identifier, seed = new_credentials()
        return '{0}:{1}'.format(identifier, seed.decode().upper())

    @asyncio.coroutine
    def load_credentials(self, credentials):
        """Load existing credentials."""
        split = credentials.split(':')
        self.identifier = split[0]
        self.srp.initialize(binascii.unhexlify(split[1]))
        _LOGGER.debug('Loaded AirPlay credentials: %s', credentials)

    def verify_authenticated(self):
        """Check if loaded credentials are verified."""
        return self.verifier.verify_authed()

    def start_authentication(self):
        """Begin authentication proces (show PIN on screen)."""
        return self.auther.start_authentication()

    def finish_authentication(self, pin):
        """End authentication process with PIN code."""
        return self.auther.finish_authentication(self.identifier, pin)

    @asyncio.coroutine
    def play_url(self, url, **kwargs):
        """Play media from an URL on the device.

        Note: This method will not yield until the media has finished playing.
        The Apple TV requires the request to stay open during the entire
        play duration.
        """
        # If credentials have been loaded, do device verification first
        if self.identifier:
            yield from self.verify_authenticated()

        position = 0 if 'position' not in kwargs else int(kwargs['position'])
        return (yield from self.player.play_url(url, position))
Exemple #9
0
    async def _player(self, connection: HttpConnection) -> AirPlayPlayer:
        player = AirPlayPlayer(connection)

        # If credentials have been loaded, do device verification first
        if self.credentials:
            srp = SRPAuthHandler(self.credentials)
            srp.initialize()
            verifier = AirPlayPairingVerifier(connection, srp)
            await verifier.verify_authed()

        return player
Exemple #10
0
 def __init__(self, config, session, _):
     """Initialize a new MrpPairingHandler."""
     super().__init__(session, config.get_service(Protocol.AirPlay))
     self.srp = SRPAuthHandler()
     self.http = net.HttpSession(
         session, f"http://{config.address}:{self.service.port}/")
     self.authenticator = DeviceAuthenticator(self.http, self.srp)
     self.auth_data = self._setup_credentials()
     self.srp.initialize(binascii.unhexlify(self.auth_data.seed))
     self.pin_code = None
     self._has_paired = False
Exemple #11
0
    async def _player(self, session: ClientSession) -> AirPlayPlayer:
        http = net.HttpSession(
            session, f"http://{self.config.address}:{self.service.port}/")
        player = AirPlayPlayer(self.loop, http)

        # If credentials have been loaded, do device verification first
        if self.credentials:
            srp = SRPAuthHandler()
            srp.initialize(binascii.unhexlify(self.credentials))
            verifier = AuthenticationVerifier(http, srp)
            await verifier.verify_authed()

        return player
Exemple #12
0
    async def _player(self, session):
        http = net.HttpSession(
            session, 'http://{0}:{1}/'.format(
                self.config.address, self.service.port))
        player = AirPlayPlayer(self.loop, http)

        # If credentials have been loaded, do device verification first
        credentials = self._get_credentials()
        if credentials:
            srp = SRPAuthHandler()
            srp.initialize(binascii.unhexlify(credentials))
            verifier = AuthenticationVerifier(http, srp)
            await verifier.verify_authed()

        return player
Exemple #13
0
    async def begin(self) -> None:
        """Start pairing process."""
        _LOGGER.debug("Starting AirPlay pairing with credentials %s", self.credentials)

        srp: SRPAuthHandler = SRPAuthHandler(self.credentials)
        srp.initialize()

        self.http = await http_connect(self.address, self.service.port)
        self.pairing_procedure = AirPlayPairingProcedure(self.http, srp)

        self._has_paired = False
        return await error_handler(
            self.pairing_procedure.start_pairing, exceptions.PairingError
        )
Exemple #14
0
class AirPlayPairingHandler(PairingHandler):
    """Base class for API used to pair with an Apple TV."""
    def __init__(self, config, session_manager: net.ClientSessionManager, _):
        """Initialize a new MrpPairingHandler."""
        super().__init__(session_manager, config.get_service(Protocol.AirPlay))
        self.srp = SRPAuthHandler()
        self.http = net.HttpSession(
            session_manager.session,
            f"http://{config.address}:{self.service.port}/")
        self.authenticator = DeviceAuthenticator(self.http, self.srp)
        self.auth_data = self._setup_credentials()
        self.srp.initialize(binascii.unhexlify(self.auth_data.seed))
        self.pin_code = None
        self._has_paired = False

    def _setup_credentials(self):
        credentials = self.service.credentials

        # If service has credentials, use those. Otherwise generate new.
        if self.service.credentials is None:
            identifier, seed = new_credentials()
            credentials = f"{identifier}:{seed.decode().upper()}"
        else:
            split = credentials.split(":")
            identifier = split[0]
            seed = split[1]
        return AuthData(identifier, seed, credentials)

    @property
    def has_paired(self):
        """If a successful pairing has been performed."""
        return self._has_paired

    def begin(self):
        """Start pairing process."""
        _LOGGER.debug("Starting AirPlay pairing with credentials %s",
                      self.auth_data.credentials)
        self.pairing_complete = False
        return error_handler(self.authenticator.start_authentication,
                             exceptions.PairingError)

    async def finish(self):
        """Stop pairing process."""
        if not self.pin_code:
            raise exceptions.PairingError("no pin given")

        await error_handler(
            self.authenticator.finish_authentication,
            exceptions.PairingError,
            self.auth_data.identifier,
            self.pin_code,
        )

        self.service.credentials = self.auth_data.credentials
        self._has_paired = True

    def pin(self, pin):
        """Pin code used for pairing."""
        self.pin_code = str(pin).zfill(4)
        _LOGGER.debug("AirPlay PIN changed to %s", self.pin_code)

    @property
    def device_provides_pin(self):
        """Return True if remote device presents PIN code, else False."""
        return True