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)
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, )
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()
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)
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()
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
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)
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))
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
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
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
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
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 )
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