class CertSecurity(PySecurity): """ Contain all of the certification stack logic for sending and receiving HCI commands following the Classic Pairing flows. """ _io_cap_lookup = { IoCapabilities.DISPLAY_ONLY: hci_packets.IoCapability.DISPLAY_ONLY, IoCapabilities.DISPLAY_YES_NO_IO_CAP: hci_packets.IoCapability.DISPLAY_YES_NO, IoCapabilities.KEYBOARD_ONLY: hci_packets.IoCapability.KEYBOARD_ONLY, IoCapabilities.NO_INPUT_NO_OUTPUT: hci_packets.IoCapability.NO_INPUT_NO_OUTPUT, } _auth_req_lookup = { AuthenticationRequirements.NO_BONDING: hci_packets.AuthenticationRequirements.NO_BONDING, AuthenticationRequirements.NO_BONDING_MITM_PROTECTION: hci_packets.AuthenticationRequirements.NO_BONDING_MITM_PROTECTION, AuthenticationRequirements.DEDICATED_BONDING: hci_packets.AuthenticationRequirements.DEDICATED_BONDING, AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION: hci_packets.AuthenticationRequirements. DEDICATED_BONDING_MITM_PROTECTION, AuthenticationRequirements.GENERAL_BONDING: hci_packets.AuthenticationRequirements.GENERAL_BONDING, AuthenticationRequirements.GENERAL_BONDING_MITM_PROTECTION: hci_packets.AuthenticationRequirements.GENERAL_BONDING_MITM_PROTECTION, } _oob_present_lookup = { OobDataPresent.NOT_PRESENT: hci_packets.OobDataPresent.NOT_PRESENT, OobDataPresent.P192_PRESENT: hci_packets.OobDataPresent.P_192_PRESENT, OobDataPresent.P256_PRESENT: hci_packets.OobDataPresent.P_256_PRESENT, OobDataPresent.P192_AND_256_PRESENT: hci_packets.OobDataPresent.P_192_AND_256_PRESENT, } _hci_event_stream = None _io_caps = hci_packets.IoCapability.DISPLAY_ONLY _oob_data = hci_packets.OobDataPresent.NOT_PRESENT _auth_reqs = hci_packets.AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION _hci = None def _enqueue_hci_command(self, command, expect_complete): if (expect_complete): self._hci.send_command_with_complete(command) else: self._hci.send_command_with_status(command) def __init__(self, device): """ Don't call super b/c the gRPC stream setup will crash test """ logging.info("Cert: Init") self._device = device self._device.wait_channel_ready() self._hci = PyHci(device) self._hci.register_for_events( hci_packets.EventCode.LINK_KEY_REQUEST, hci_packets.EventCode.IO_CAPABILITY_REQUEST, hci_packets.EventCode.IO_CAPABILITY_RESPONSE, hci_packets.EventCode.USER_PASSKEY_NOTIFICATION, hci_packets.EventCode.USER_PASSKEY_REQUEST, hci_packets.EventCode.USER_CONFIRMATION_REQUEST, hci_packets.EventCode.REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION, hci_packets.EventCode.LINK_KEY_NOTIFICATION, hci_packets.EventCode.SIMPLE_PAIRING_COMPLETE) self._hci_event_stream = self._hci.get_event_stream() def create_bond(self, address, type): """ Creates a bond from the cert perspective """ logging.info("Cert: Creating bond to '%s' from '%s'" % (str(address), str(self._device.address))) # TODO(optedoblivion): Trigger connection to Send AuthenticationRequested def remove_bond(self, address, type): """ We store the link key locally in the test and pretend So to remove_bond we need to Remove the "stored" data """ pass def set_io_capabilities(self, io_capabilities): """ Set the IO Capabilities used for the cert """ logging.info( "Cert: setting IO Capabilities data to '%s'" % self._io_capabilities_name_lookup.get(io_capabilities, "ERROR")) self._io_caps = self._io_cap_lookup.get( io_capabilities, hci_packets.IoCapability.DISPLAY_YES_NO) def set_authentication_requirements(self, auth_reqs): """ Establish authentication requirements for the stack """ logging.info("Cert: setting Authentication Requirements data to '%s'" % self._auth_reqs_name_lookup.get(auth_reqs, "ERROR")) self._auth_reqs = self._auth_req_lookup.get( auth_reqs, hci_packets.AuthenticationRequirements.GENERAL_BONDING) def set_oob_data(self, data): """ Set the Out-of-band data for SSP pairing """ logging.info("Cert: setting OOB data present to '%s'" % data) self._oob_data = self._oob_present_lookup.get( data, hci_packets.OobDataPresent.NOT_PRESENT) def send_ui_callback(self, address, callback_type, b, uid): """ Pretend to answer the pairing dailog as a user """ logging.info("Cert: Send user input callback uid:%d; response: %s" % (uid, b)) # TODO(optedoblivion): Make callback and set it to the module def enable_secure_simple_pairing(self): """ This is called when you want to enable SSP for testing """ logging.info("Cert: Sending WRITE_SIMPLE_PAIRING_MODE [True]") self._enqueue_hci_command( hci_packets.WriteSimplePairingModeBuilder( hci_packets.Enable.ENABLED), True) logging.info("Cert: Waiting for controller response") assertThat(self._hci_event_stream).emits( lambda msg: b'\x0e\x04\x01\x56\x0c' in msg.event) def accept_pairing(self, dut_address, reply_boolean): """ Here we handle the pairing events at the HCI level """ logging.info("Cert: Waiting for LINK_KEY_REQUEST") assertThat(self._hci_event_stream).emits(HciMatchers.LinkKeyRequest()) logging.info("Cert: Sending LINK_KEY_REQUEST_NEGATIVE_REPLY") self._enqueue_hci_command( hci_packets.LinkKeyRequestNegativeReplyBuilder( dut_address.decode('utf8')), True) logging.info("Cert: Waiting for IO_CAPABILITY_REQUEST") assertThat(self._hci_event_stream).emits( HciMatchers.IoCapabilityRequest()) logging.info("Cert: Sending IO_CAPABILITY_REQUEST_REPLY") self._enqueue_hci_command( hci_packets.IoCapabilityRequestReplyBuilder( dut_address.decode('utf8'), self._io_caps, self._oob_data, self._auth_reqs), True) logging.info("Cert: Waiting for USER_CONFIRMATION_REQUEST") assertThat(self._hci_event_stream).emits( HciMatchers.UserConfirmationRequest()) logging.info("Cert: Sending Simulated User Response '%s'" % reply_boolean) if reply_boolean: logging.info("Cert: Sending USER_CONFIRMATION_REQUEST_REPLY") self._enqueue_hci_command( hci_packets.UserConfirmationRequestReplyBuilder( dut_address.decode('utf8')), True) logging.info("Cert: Waiting for SIMPLE_PAIRING_COMPLETE") assertThat(self._hci_event_stream).emits( HciMatchers.SimplePairingComplete()) logging.info("Cert: Waiting for LINK_KEY_NOTIFICATION") assertThat(self._hci_event_stream).emits( HciMatchers.LinkKeyNotification()) else: logging.info( "Cert: Sending USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY") self._enqueue_hci_command( hci_packets.UserConfirmationRequestNegativeReplyBuilder( dut_address.decode('utf8')), True) logging.info("Cert: Waiting for SIMPLE_PAIRING_COMPLETE") assertThat(self._hci_event_stream).emits( HciMatchers.SimplePairingComplete()) def on_user_input(self, dut_address, reply_boolean, expected_ui_event): """ Cert doesn't need the test to respond to the ui event Cert responds in accept pairing """ pass def wait_for_bond_event(self, expected_bond_event): """ A bond event will be triggered once the bond process is complete. For the DUT we need to wait for it, for Cert it isn't needed. """ pass def enforce_security_policy(self, address, type, policy): """ Pass for now """ pass def wait_for_enforce_security_event(self, expected_enforce_security_event): """ Cert side needs to pass """ pass def close(self): safeClose(self._hci)
class PyL2cap(Closable): def __init__(self, device, cert_address, has_security=False): self._device = device self._cert_address = cert_address self._hci = PyHci(device) self._l2cap_stream = EventStream( self._device.l2cap.FetchL2capData(empty_proto.Empty())) self._security_connection_event_stream = EventStream( self._device.l2cap.FetchSecurityConnectionEvents( empty_proto.Empty())) if has_security == False: self._hci.register_for_events( hci_packets.EventCode.LINK_KEY_REQUEST) def close(self): safeClose(self._l2cap_stream) safeClose(self._security_connection_event_stream) safeClose(self._hci) def register_dynamic_channel( self, psm=0x33, mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC): self._device.l2cap.SetDynamicChannel( l2cap_facade_pb2.SetEnableDynamicChannelRequest( psm=psm, retransmission_mode=mode)) return PyL2capChannel(self._device, psm, self._l2cap_stream) def connect_dynamic_channel_to_cert( self, psm=0x33, mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC): """ Send open Dynamic channel request to CERT. Get a future for connection result, to be used after CERT accepts request """ self.register_dynamic_channel(psm, mode) response_future = self._device.l2cap.OpenChannel.future( l2cap_facade_pb2.OpenChannelRequest(psm=psm, remote=self._cert_address, mode=mode)) return _ClassicConnectionResponseFutureWrapper(response_future, self._device, psm, self._l2cap_stream) def get_channel_queue_buffer_size(self): return self._device.l2cap.GetChannelQueueDepth( empty_proto.Empty()).size def initiate_connection_for_security(self): """ Establish an ACL for the specific purpose of pairing devices """ self._device.l2cap.InitiateConnectionForSecurity(self._cert_address) def get_security_connection_event_stream(self): """ Stream of Link related events. Events are returned with an address. Events map to the LinkSecurityInterfaceListener callbacks """ return self._security_connection_event_stream def security_link_hold(self): """ Holds open the ACL indefinitely allowing for the security handshake to take place """ self._device.l2cap.SecurityLinkHold(self._cert_address) def security_link_ensure_authenticated(self): """ Triggers authentication process by sending HCI event AUTHENTICATION_REQUESTED """ self._device.l2cap.SecurityLinkEnsureAuthenticated(self._cert_address) def security_link_release(self): """ Releases a Held open ACL allowing for the ACL to time out after the default time """ self._device.l2cap.SecurityLinkRelease(self._cert_address) def security_link_disconnect(self): """ Immediately release and disconnect ACL """ self._device.l2cap.SecurityLinkDisconnect(self._cert_address) def verify_security_connection(self): """ Verify that we get a connection and a link key request """ assertThat(self.get_security_connection_event_stream()).emits( lambda event: event.event_type == LinkSecurityInterfaceCallbackEventType.ON_CONNECTED) assertThat(self._hci.get_event_stream()).emits( HciMatchers.LinkKeyRequest())
class NeighborTest(GdBaseTestClass): def setup_class(self): super().setup_class(dut_module='HCI_INTERFACES', cert_module='HCI') def setup_test(self): super().setup_test() self.cert_hci = PyHci(self.cert, acl_streaming=True) self.cert_hci.send_command_with_complete( hci_packets.WriteScanEnableBuilder( hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN)) self.cert_name = b'Im_A_Cert' self.cert_address = self.cert_hci.read_own_address() self.cert_name += b'@' + self.cert_address.encode('utf8') self.dut_neighbor = PyNeighbor(self.dut) def teardown_test(self): self.cert_hci.close() super().teardown_test() def _set_name(self): padded_name = self.cert_name while len(padded_name) < 248: padded_name = padded_name + b'\0' self.cert_hci.send_command_with_complete( hci_packets.WriteLocalNameBuilder(padded_name)) assertThat(self.cert_hci.get_event_stream()).emits( HciMatchers.CommandComplete(OpCode.WRITE_LOCAL_NAME)) def test_inquiry_from_dut(self): inquiry_msg = neighbor_facade.InquiryMsg( inquiry_mode=neighbor_facade.DiscoverabilityMode.GENERAL, result_mode=neighbor_facade.ResultMode.STANDARD, length_1_28s=3, max_results=0) session = self.dut_neighbor.set_inquiry_mode(inquiry_msg) self.cert_hci.send_command_with_complete( hci_packets.WriteScanEnableBuilder( hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN)) assertThat(session).emits(NeighborMatchers.InquiryResult( self.cert_address), timeout=timedelta(seconds=10)) def test_inquiry_rssi_from_dut(self): inquiry_msg = neighbor_facade.InquiryMsg( inquiry_mode=neighbor_facade.DiscoverabilityMode.GENERAL, result_mode=neighbor_facade.ResultMode.RSSI, length_1_28s=6, max_results=0) session = self.dut_neighbor.set_inquiry_mode(inquiry_msg) self.cert_hci.send_command_with_complete( hci_packets.WriteScanEnableBuilder( hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN)) assertThat(session).emits(NeighborMatchers.InquiryResultwithRssi( self.cert_address), timeout=timedelta(seconds=10)) def test_inquiry_extended_from_dut(self): self._set_name() gap_name = hci_packets.GapData() gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME gap_name.data = list(bytes(self.cert_name)) gap_data = list([gap_name]) self.cert_hci.send_command_with_complete( hci_packets.WriteExtendedInquiryResponseBuilder( hci_packets.FecRequired.NOT_REQUIRED, gap_data)) inquiry_msg = neighbor_facade.InquiryMsg( inquiry_mode=neighbor_facade.DiscoverabilityMode.GENERAL, result_mode=neighbor_facade.ResultMode.EXTENDED, length_1_28s=8, max_results=0) session = self.dut_neighbor.set_inquiry_mode(inquiry_msg) self.cert_hci.send_command_with_complete( hci_packets.WriteScanEnableBuilder( hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN)) assertThat(session).emits(NeighborMatchers.ExtendedInquiryResult( self.cert_address), timeout=timedelta(seconds=10)) def test_remote_name(self): self._set_name() session = self.dut_neighbor.get_remote_name(self.cert_address) session.verify_name(self.cert_name)
class DirectHciTest(GdBaseTestClass): def setup_class(self): super().setup_class(dut_module='HCI', cert_module='HAL') def setup_test(self): super().setup_test() self.dut_hci = PyHci(self.dut, acl_streaming=True) self.cert_hal = PyHal(self.cert) self.cert_hal.send_hci_command(ResetBuilder()) def teardown_test(self): self.dut_hci.close() self.cert_hal.close() super().teardown_test() def enqueue_acl_data(self, handle, pb_flag, b_flag, data): acl = AclBuilder(handle, pb_flag, b_flag, RawBuilder(data)) self.dut.hci.SendAcl(common.Data(payload=bytes(acl.Serialize()))) def test_local_hci_cmd_and_event(self): # Loopback mode responds with ACL and SCO connection complete self.dut_hci.register_for_events(EventCode.LOOPBACK_COMMAND) self.dut_hci.send_command( WriteLoopbackModeBuilder(LoopbackMode.ENABLE_LOCAL)) self.dut_hci.send_command(ReadLocalNameBuilder()) assertThat(self.dut_hci.get_event_stream()).emits( HciMatchers.LoopbackOf(ReadLocalNameBuilder())) def test_inquiry_from_dut(self): self.dut_hci.register_for_events(EventCode.INQUIRY_RESULT) self.cert_hal.enable_inquiry_and_page_scan() lap = Lap() lap.lap = 0x33 self.dut_hci.send_command(InquiryBuilder(lap, 0x30, 0xff)) assertThat(self.dut_hci.get_event_stream()).emits( HciMatchers.EventWithCode(EventCode.INQUIRY_RESULT)) def test_le_ad_scan_cert_advertises(self): self.dut_hci.register_for_le_events( SubeventCode.EXTENDED_ADVERTISING_REPORT, SubeventCode.ADVERTISING_REPORT) # DUT Scans self.dut_hci.send_command( LeSetRandomAddressBuilder('0D:05:04:03:02:01')) phy_scan_params = PhyScanParameters() phy_scan_params.le_scan_interval = 6553 phy_scan_params.le_scan_window = 6553 phy_scan_params.le_scan_type = LeScanType.ACTIVE self.dut_hci.send_command( LeSetExtendedScanParametersBuilder( OwnAddressType.RANDOM_DEVICE_ADDRESS, LeScanningFilterPolicy.ACCEPT_ALL, 1, [phy_scan_params])) self.dut_hci.send_command( LeSetExtendedScanEnableBuilder(Enable.ENABLED, FilterDuplicates.DISABLED, 0, 0)) # CERT Advertises advertising_handle = 0 self.cert_hal.send_hci_command( LeSetExtendedAdvertisingLegacyParametersBuilder( advertising_handle, LegacyAdvertisingProperties.ADV_IND, 512, 768, 7, OwnAddressType.RANDOM_DEVICE_ADDRESS, PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, 'A6:A5:A4:A3:A2:A1', AdvertisingFilterPolicy.ALL_DEVICES, 0xF7, 1, # SID Enable.DISABLED # Scan request notification )) self.cert_hal.send_hci_command( LeSetExtendedAdvertisingRandomAddressBuilder( advertising_handle, '0C:05:04:03:02:01')) gap_name = GapData() gap_name.data_type = GapDataType.COMPLETE_LOCAL_NAME gap_name.data = list(bytes(b'Im_A_Cert')) self.cert_hal.send_hci_command( LeSetExtendedAdvertisingDataBuilder( advertising_handle, Operation.COMPLETE_ADVERTISEMENT, FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_name])) gap_short_name = GapData() gap_short_name.data_type = GapDataType.SHORTENED_LOCAL_NAME gap_short_name.data = list(bytes(b'Im_A_C')) self.cert_hal.send_hci_command( LeSetExtendedAdvertisingScanResponseBuilder( advertising_handle, Operation.COMPLETE_ADVERTISEMENT, FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_short_name])) enabled_set = EnabledSet() enabled_set.advertising_handle = 0 enabled_set.duration = 0 enabled_set.max_extended_advertising_events = 0 self.cert_hal.send_hci_command( LeSetExtendedAdvertisingEnableBuilder(Enable.ENABLED, [enabled_set])) assertThat(self.dut_hci.get_le_event_stream()).emits( lambda packet: b'Im_A_Cert' in packet.payload) self.cert_hal.send_hci_command( LeSetExtendedAdvertisingEnableBuilder(Enable.DISABLED, [enabled_set])) self.dut_hci.send_command( LeSetExtendedScanEnableBuilder(Enable.DISABLED, FilterDuplicates.DISABLED, 0, 0)) def _verify_le_connection_complete(self): cert_conn_complete_capture = HalCaptures.LeConnectionCompleteCapture() assertThat(self.cert_hal.get_hci_event_stream()).emits( cert_conn_complete_capture) cert_handle = cert_conn_complete_capture.get().GetConnectionHandle() dut_conn_complete_capture = HciCaptures.LeConnectionCompleteCapture() assertThat(self.dut_hci.get_le_event_stream()).emits( dut_conn_complete_capture) dut_handle = dut_conn_complete_capture.get().GetConnectionHandle() return (dut_handle, cert_handle) @staticmethod def _create_phy_scan_params(): phy_scan_params = LeCreateConnPhyScanParameters() phy_scan_params.scan_interval = 0x60 phy_scan_params.scan_window = 0x30 phy_scan_params.conn_interval_min = 0x18 phy_scan_params.conn_interval_max = 0x28 phy_scan_params.conn_latency = 0 phy_scan_params.supervision_timeout = 0x1f4 phy_scan_params.min_ce_length = 0 phy_scan_params.max_ce_length = 0 return phy_scan_params def test_le_connection_dut_advertises(self): self.dut_hci.register_for_le_events( SubeventCode.CONNECTION_COMPLETE, SubeventCode.ADVERTISING_SET_TERMINATED, SubeventCode.ENHANCED_CONNECTION_COMPLETE, SubeventCode.READ_REMOTE_FEATURES_COMPLETE) # Cert Connects self.cert_hal.send_hci_command( LeSetRandomAddressBuilder('0C:05:04:03:02:01')) phy_scan_params = DirectHciTest._create_phy_scan_params() self.cert_hal.send_hci_command( LeExtendedCreateConnectionBuilder( InitiatorFilterPolicy.USE_PEER_ADDRESS, OwnAddressType.RANDOM_DEVICE_ADDRESS, AddressType.RANDOM_DEVICE_ADDRESS, '0D:05:04:03:02:01', 1, [phy_scan_params])) advertisement = self.dut_hci.create_advertisement( 0, '0D:05:04:03:02:01') advertisement.set_data(b'Im_The_DUT') advertisement.set_scan_response(b'Im_The_D') advertisement.start() (dut_handle, cert_handle) = self._verify_le_connection_complete() self.dut_hci.send_command(LeReadRemoteFeaturesBuilder(dut_handle)) assertThat(self.dut_hci.get_le_event_stream()).emits( lambda packet: packet.payload[0] == int( EventCode.LE_META_EVENT) and packet.payload[2] == int( SubeventCode.READ_REMOTE_FEATURES_COMPLETE)) # Send ACL Data self.enqueue_acl_data( dut_handle, PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE, BroadcastFlag.POINT_TO_POINT, bytes(b'Just SomeAclData')) self.cert_hal.send_acl_first(cert_handle, bytes(b'Just SomeMoreAclData')) assertThat( self.cert_hal.get_acl_stream()).emits(lambda packet: logging.debug( packet.payload) or b'SomeAclData' in packet.payload) assertThat(self.dut_hci.get_raw_acl_stream()).emits( lambda packet: logging.debug( packet.payload) or b'SomeMoreAclData' in packet.payload) def test_le_connect_list_connection_cert_advertises(self): self.dut_hci.register_for_le_events( SubeventCode.CONNECTION_COMPLETE, SubeventCode.ENHANCED_CONNECTION_COMPLETE) # DUT Connects self.dut_hci.send_command( LeSetRandomAddressBuilder('0D:05:04:03:02:01')) self.dut_hci.send_command( LeAddDeviceToConnectListBuilder(ConnectListAddressType.RANDOM, '0C:05:04:03:02:01')) phy_scan_params = DirectHciTest._create_phy_scan_params() self.dut_hci.send_command( LeExtendedCreateConnectionBuilder( InitiatorFilterPolicy.USE_CONNECT_LIST, OwnAddressType.RANDOM_DEVICE_ADDRESS, AddressType.RANDOM_DEVICE_ADDRESS, 'BA:D5:A4:A3:A2:A1', 1, [phy_scan_params])) advertisement = self.cert_hal.create_advertisement( 1, '0C:05:04:03:02:01', min_interval=512, max_interval=768, peer_address='A6:A5:A4:A3:A2:A1', tx_power=0x7f, sid=0) advertisement.set_data(b'Im_A_Cert') advertisement.start() # LeConnectionComplete self._verify_le_connection_complete() def test_connection_dut_connects(self): self.dut_hci.send_command(WritePageTimeoutBuilder(0x4000)) self.cert_hal.enable_inquiry_and_page_scan() address = self.cert_hal.read_own_address() self.dut_hci.initiate_connection(address) cert_acl = self.cert_hal.accept_connection() dut_acl = self.dut_hci.complete_connection() # Send ACL Data dut_acl.send_first(b'Just SomeAclData') cert_acl.send_first(b'Just SomeMoreAclData') assertThat(self.cert_hal.get_acl_stream()).emits( lambda packet: b'SomeAclData' in packet.payload) assertThat(self.dut_hci.get_raw_acl_stream()).emits( lambda packet: b'SomeMoreAclData' in packet.payload) def test_connection_cert_connects(self): self.cert_hal.send_hci_command(WritePageTimeoutBuilder(0x4000)) self.dut_hci.enable_inquiry_and_page_scan() address = self.dut_hci.read_own_address() self.cert_hal.initiate_connection(address) dut_acl = self.dut_hci.accept_connection() cert_acl = self.cert_hal.complete_connection() # Send ACL Data dut_acl.send_first(b'This is just SomeAclData') cert_acl.send_first(b'This is just SomeMoreAclData') assertThat(self.cert_hal.get_acl_stream()).emits( lambda packet: b'SomeAclData' in packet.payload) assertThat(self.dut_hci.get_raw_acl_stream()).emits( lambda packet: b'SomeMoreAclData' in packet.payload)
class DirectHciTest(GdBaseTestClass): def setup_class(self): super().setup_class(dut_module='HCI', cert_module='HAL') def setup_test(self): super().setup_test() self.dut_hci = PyHci(self.dut, acl_streaming=True) self.cert_hal = PyHal(self.cert) self.cert_hal.send_hci_command(hci_packets.ResetBuilder().Serialize()) def teardown_test(self): self.dut_hci.close() self.cert_hal.close() super().teardown_test() def send_hal_hci_command(self, command): self.cert_hal.send_hci_command(bytes(command.Serialize())) def enqueue_acl_data(self, handle, pb_flag, b_flag, acl): acl_msg = hci_facade.AclMsg(handle=int(handle), packet_boundary_flag=int(pb_flag), broadcast_flag=int(b_flag), data=acl) self.dut.hci.SendAclData(acl_msg) def send_hal_acl_data(self, handle, pb_flag, b_flag, acl): lower = handle & 0xff upper = (handle >> 8) & 0xf upper = upper | int(pb_flag) & 0x3 upper = upper | ((int(b_flag) & 0x3) << 2) lower_length = len(acl) & 0xff upper_length = (len(acl) & 0xff00) >> 8 concatenated = bytes([lower, upper, lower_length, upper_length] + list(acl)) self.cert_hal.send_acl(concatenated) def test_local_hci_cmd_and_event(self): # Loopback mode responds with ACL and SCO connection complete self.dut_hci.register_for_events( hci_packets.EventCode.LOOPBACK_COMMAND) self.dut_hci.send_command_with_complete( hci_packets.WriteLoopbackModeBuilder( hci_packets.LoopbackMode.ENABLE_LOCAL)) cmd2loop = hci_packets.ReadLocalNameBuilder() self.dut_hci.send_command_with_complete(cmd2loop) looped_bytes = bytes(cmd2loop.Serialize()) assertThat(self.dut_hci.get_event_stream()).emits( lambda packet: looped_bytes in packet.event) def test_inquiry_from_dut(self): self.dut_hci.register_for_events(hci_packets.EventCode.INQUIRY_RESULT) self.send_hal_hci_command( hci_packets.WriteScanEnableBuilder( hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN)) lap = hci_packets.Lap() lap.lap = 0x33 self.dut_hci.send_command_with_status( hci_packets.InquiryBuilder(lap, 0x30, 0xff)) assertThat(self.dut_hci.get_event_stream()).emits( HciMatchers.EventWithCode(hci_packets.EventCode.INQUIRY_RESULT)) def test_le_ad_scan_cert_advertises(self): self.dut_hci.register_for_le_events( hci_packets.SubeventCode.EXTENDED_ADVERTISING_REPORT) self.dut_hci.register_for_le_events( hci_packets.SubeventCode.ADVERTISING_REPORT) # DUT Scans self.dut_hci.send_command_with_complete( hci_packets.LeSetRandomAddressBuilder('0D:05:04:03:02:01')) phy_scan_params = hci_packets.PhyScanParameters() phy_scan_params.le_scan_interval = 6553 phy_scan_params.le_scan_window = 6553 phy_scan_params.le_scan_type = hci_packets.LeScanType.ACTIVE self.dut_hci.send_command_with_complete( hci_packets.LeSetExtendedScanParametersBuilder( hci_packets.AddressType.RANDOM_DEVICE_ADDRESS, hci_packets.LeSetScanningFilterPolicy.ACCEPT_ALL, 1, [phy_scan_params])) self.dut_hci.send_command_with_complete( hci_packets.LeSetExtendedScanEnableBuilder( hci_packets.Enable.ENABLED, hci_packets.FilterDuplicates.DISABLED, 0, 0)) # CERT Advertises advertising_handle = 0 self.send_hal_hci_command( hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder( advertising_handle, hci_packets.LegacyAdvertisingProperties.ADV_IND, 512, 768, 7, hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS, hci_packets.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, 'A6:A5:A4:A3:A2:A1', hci_packets.AdvertisingFilterPolicy.ALL_DEVICES, 0xF7, 1, # SID hci_packets.Enable.DISABLED # Scan request notification )) self.send_hal_hci_command( hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder( advertising_handle, '0C:05:04:03:02:01')) gap_name = hci_packets.GapData() gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME gap_name.data = list(bytes(b'Im_A_Cert!')) # TODO: Fix and remove ! self.send_hal_hci_command( hci_packets.LeSetExtendedAdvertisingDataBuilder( advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT, hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_name])) gap_short_name = hci_packets.GapData() gap_short_name.data_type = hci_packets.GapDataType.SHORTENED_LOCAL_NAME gap_short_name.data = list(bytes(b'Im_A_C')) self.send_hal_hci_command( hci_packets.LeSetExtendedAdvertisingScanResponseBuilder( advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT, hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_short_name])) enabled_set = hci_packets.EnabledSet() enabled_set.advertising_handle = 0 enabled_set.duration = 0 enabled_set.max_extended_advertising_events = 0 self.send_hal_hci_command( hci_packets.LeSetExtendedAdvertisingEnableBuilder( hci_packets.Enable.ENABLED, [enabled_set])) assertThat(self.dut_hci.get_le_event_stream()).emits( lambda packet: b'Im_A_Cert' in packet.event) self.send_hal_hci_command( hci_packets.LeSetExtendedAdvertisingEnableBuilder( hci_packets.Enable.DISABLED, [enabled_set])) self.dut_hci.send_command_with_complete( hci_packets.LeSetExtendedScanEnableBuilder( hci_packets.Enable.DISABLED, hci_packets.FilterDuplicates.DISABLED, 0, 0)) def _verify_le_connection_complete(self): cert_conn_complete_capture = HalCaptures.LeConnectionCompleteCapture() assertThat(self.cert_hal.get_hci_event_stream()).emits( cert_conn_complete_capture) cert_handle = cert_conn_complete_capture.get().GetConnectionHandle() dut_conn_complete_capture = HciCaptures.LeConnectionCompleteCapture() assertThat(self.dut_hci.get_le_event_stream()).emits( dut_conn_complete_capture) dut_handle = dut_conn_complete_capture.get().GetConnectionHandle() return (dut_handle, cert_handle) @staticmethod def _create_phy_scan_params(): phy_scan_params = hci_packets.LeCreateConnPhyScanParameters() phy_scan_params.scan_interval = 0x60 phy_scan_params.scan_window = 0x30 phy_scan_params.conn_interval_min = 0x18 phy_scan_params.conn_interval_max = 0x28 phy_scan_params.conn_latency = 0 phy_scan_params.supervision_timeout = 0x1f4 phy_scan_params.min_ce_length = 0 phy_scan_params.max_ce_length = 0 return phy_scan_params def test_le_connection_dut_advertises(self): self.dut_hci.register_for_le_events( hci_packets.SubeventCode.CONNECTION_COMPLETE) # Cert Connects self.send_hal_hci_command( hci_packets.LeSetRandomAddressBuilder('0C:05:04:03:02:01')) phy_scan_params = DirectHciTest._create_phy_scan_params() self.send_hal_hci_command( hci_packets.LeExtendedCreateConnectionBuilder( hci_packets.InitiatorFilterPolicy.USE_PEER_ADDRESS, hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS, hci_packets.AddressType.RANDOM_DEVICE_ADDRESS, '0D:05:04:03:02:01', 1, [phy_scan_params])) # DUT Advertises advertising_handle = 0 self.dut_hci.send_command_with_complete( hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder( advertising_handle, hci_packets.LegacyAdvertisingProperties.ADV_IND, 400, 450, 7, hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS, hci_packets.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, '00:00:00:00:00:00', hci_packets.AdvertisingFilterPolicy.ALL_DEVICES, 0xF8, 1, #SID hci_packets.Enable.DISABLED # Scan request notification )) self.dut_hci.send_command_with_complete( hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder( advertising_handle, '0D:05:04:03:02:01')) gap_name = hci_packets.GapData() gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME gap_name.data = list(bytes(b'Im_The_DUT!')) # TODO: Fix and remove ! self.dut_hci.send_command_with_complete( hci_packets.LeSetExtendedAdvertisingDataBuilder( advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT, hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_name])) gap_short_name = hci_packets.GapData() gap_short_name.data_type = hci_packets.GapDataType.SHORTENED_LOCAL_NAME gap_short_name.data = list(bytes(b'Im_The_D')) self.dut_hci.send_command_with_complete( hci_packets.LeSetExtendedAdvertisingScanResponseBuilder( advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT, hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_short_name])) enabled_set = hci_packets.EnabledSet() enabled_set.advertising_handle = advertising_handle enabled_set.duration = 0 enabled_set.max_extended_advertising_events = 0 self.dut_hci.send_command_with_complete( hci_packets.LeSetExtendedAdvertisingEnableBuilder( hci_packets.Enable.ENABLED, [enabled_set])) # Check for success of Enable assertThat(self.dut_hci.get_event_stream()).emits( HciMatchers.CommandComplete( hci_packets.OpCode.LE_SET_EXTENDED_ADVERTISING_ENABLE)) (dut_handle, cert_handle) = self._verify_le_connection_complete() # Send ACL Data self.enqueue_acl_data( dut_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE, hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'Just SomeAclData')) self.send_hal_acl_data( cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE, hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'Just SomeMoreAclData')) assertThat( self.cert_hal.get_acl_stream()).emits(lambda packet: logging.debug( packet.payload) or b'SomeAclData' in packet.payload) assertThat(self.dut_hci.get_raw_acl_stream()).emits( lambda packet: logging.debug( packet.data) or b'SomeMoreAclData' in packet.data) def test_le_white_list_connection_cert_advertises(self): self.dut_hci.register_for_le_events( hci_packets.SubeventCode.CONNECTION_COMPLETE) # DUT Connects self.dut_hci.send_command_with_complete( hci_packets.LeSetRandomAddressBuilder('0D:05:04:03:02:01')) self.dut_hci.send_command_with_complete( hci_packets.LeAddDeviceToWhiteListBuilder( hci_packets.WhiteListAddressType.RANDOM, '0C:05:04:03:02:01')) phy_scan_params = DirectHciTest._create_phy_scan_params() self.dut_hci.send_command_with_status( hci_packets.LeExtendedCreateConnectionBuilder( hci_packets.InitiatorFilterPolicy.USE_WHITE_LIST, hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS, hci_packets.AddressType.RANDOM_DEVICE_ADDRESS, 'BA:D5:A4:A3:A2:A1', 1, [phy_scan_params])) # CERT Advertises advertising_handle = 1 self.send_hal_hci_command( hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder( advertising_handle, hci_packets.LegacyAdvertisingProperties.ADV_IND, 512, 768, 7, hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS, hci_packets.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, 'A6:A5:A4:A3:A2:A1', hci_packets.AdvertisingFilterPolicy.ALL_DEVICES, 0x7F, 0, # SID hci_packets.Enable.DISABLED # Scan request notification )) self.send_hal_hci_command( hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder( advertising_handle, '0C:05:04:03:02:01')) gap_name = hci_packets.GapData() gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME gap_name.data = list(bytes(b'Im_A_Cert!')) # TODO: Fix and remove ! self.send_hal_hci_command( hci_packets.LeSetExtendedAdvertisingDataBuilder( advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT, hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_name])) enabled_set = hci_packets.EnabledSet() enabled_set.advertising_handle = 1 enabled_set.duration = 0 enabled_set.max_extended_advertising_events = 0 self.send_hal_hci_command( hci_packets.LeSetExtendedAdvertisingEnableBuilder( hci_packets.Enable.ENABLED, [enabled_set])) # LeConnectionComplete self._verify_le_connection_complete() def _verify_connection_complete(self): cert_connection_complete_capture = HalCaptures.ConnectionCompleteCapture( ) assertThat(self.cert_hal.get_hci_event_stream()).emits( cert_connection_complete_capture) cert_handle = cert_connection_complete_capture.get( ).GetConnectionHandle() dut_connection_complete_capture = HciCaptures.ConnectionCompleteCapture( ) assertThat(self.dut_hci.get_event_stream()).emits( dut_connection_complete_capture) dut_handle = dut_connection_complete_capture.get().GetConnectionHandle( ) return (dut_handle, cert_handle) def test_connection_dut_connects(self): self.dut_hci.send_command_with_complete( hci_packets.WritePageTimeoutBuilder(0x4000)) # CERT Enables scans and gets its address self.send_hal_hci_command(hci_packets.ReadBdAddrBuilder()) cert_read_bd_addr_capture = HalCaptures.ReadBdAddrCompleteCapture() assertThat(self.cert_hal.get_hci_event_stream()).emits( cert_read_bd_addr_capture) address = cert_read_bd_addr_capture.get().GetBdAddr() self.send_hal_hci_command( hci_packets.WriteScanEnableBuilder( hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN)) # DUT Connects self.dut_hci.send_command_with_status( hci_packets.CreateConnectionBuilder( address, 0xcc18, # Packet Type hci_packets.PageScanRepetitionMode.R0, 0, hci_packets.ClockOffsetValid.INVALID, hci_packets.CreateConnectionRoleSwitch.ALLOW_ROLE_SWITCH)) # Cert Accepts connect_request_capture = HalCaptures.ConnectionRequestCapture() assertThat(self.cert_hal.get_hci_event_stream()).emits( connect_request_capture, timeout=timedelta(seconds=20)) connection_request = connect_request_capture.get() self.send_hal_hci_command( hci_packets.AcceptConnectionRequestBuilder( connection_request.GetBdAddr(), hci_packets.AcceptConnectionRequestRole.REMAIN_SLAVE)) (dut_handle, cert_handle) = self._verify_connection_complete() # Send ACL Data self.enqueue_acl_data( dut_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE, hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'Just SomeAclData')) self.send_hal_acl_data( cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE, hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'Just SomeMoreAclData')) assertThat(self.cert_hal.get_acl_stream()).emits( lambda packet: b'SomeAclData' in packet.payload) assertThat(self.dut_hci.get_raw_acl_stream()).emits( lambda packet: b'SomeMoreAclData' in packet.data) def test_connection_cert_connects(self): self.send_hal_hci_command(hci_packets.WritePageTimeoutBuilder(0x4000)) # DUT Enables scans and gets its address self.dut_hci.send_command_with_complete( hci_packets.WriteScanEnableBuilder( hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN)) self.dut_hci.send_command_with_complete( hci_packets.ReadBdAddrBuilder()) read_bd_addr_capture = HciCaptures.ReadBdAddrCompleteCapture() assertThat(self.dut_hci.get_event_stream()).emits(read_bd_addr_capture) address = read_bd_addr_capture.get().GetBdAddr() # Cert Connects self.send_hal_hci_command( hci_packets.CreateConnectionBuilder( address, 0xcc18, # Packet Type hci_packets.PageScanRepetitionMode.R0, 0, hci_packets.ClockOffsetValid.INVALID, hci_packets.CreateConnectionRoleSwitch.ALLOW_ROLE_SWITCH)) # DUT Accepts connection_request_capture = HciCaptures.ConnectionRequestCapture() assertThat(self.dut_hci.get_event_stream()).emits( connection_request_capture, timeout=timedelta(seconds=20)) connection_request = connection_request_capture.get() self.dut_hci.send_command_with_status( hci_packets.AcceptConnectionRequestBuilder( connection_request.GetBdAddr(), hci_packets.AcceptConnectionRequestRole.REMAIN_SLAVE)) (dut_handle, cert_handle) = self._verify_connection_complete() # Send ACL Data self.enqueue_acl_data( dut_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE, hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'This is just SomeAclData')) self.send_hal_acl_data( cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE, hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'This is just SomeMoreAclData')) assertThat(self.cert_hal.get_acl_stream()).emits( lambda packet: b'SomeAclData' in packet.payload) assertThat(self.dut_hci.get_raw_acl_stream()).emits( lambda packet: b'SomeMoreAclData' in packet.data)
class CertSecurity(PySecurity): """ Contain all of the certification stack logic for sending and receiving HCI commands following the Classic Pairing flows. """ _io_cap_lookup = { IoCapabilities.DISPLAY_ONLY: hci_packets.IoCapability.DISPLAY_ONLY, IoCapabilities.DISPLAY_YES_NO_IO_CAP: hci_packets.IoCapability.DISPLAY_YES_NO, IoCapabilities.KEYBOARD_ONLY: hci_packets.IoCapability.KEYBOARD_ONLY, IoCapabilities.NO_INPUT_NO_OUTPUT: hci_packets.IoCapability.NO_INPUT_NO_OUTPUT, } _auth_req_lookup = { AuthenticationRequirements.NO_BONDING: hci_packets.AuthenticationRequirements.NO_BONDING, AuthenticationRequirements.NO_BONDING_MITM_PROTECTION: hci_packets.AuthenticationRequirements.NO_BONDING_MITM_PROTECTION, AuthenticationRequirements.DEDICATED_BONDING: hci_packets.AuthenticationRequirements.DEDICATED_BONDING, AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION: hci_packets.AuthenticationRequirements. DEDICATED_BONDING_MITM_PROTECTION, AuthenticationRequirements.GENERAL_BONDING: hci_packets.AuthenticationRequirements.GENERAL_BONDING, AuthenticationRequirements.GENERAL_BONDING_MITM_PROTECTION: hci_packets.AuthenticationRequirements.GENERAL_BONDING_MITM_PROTECTION, } _oob_present_lookup = { OobDataPresent.NOT_PRESENT: hci_packets.OobDataPresent.NOT_PRESENT, OobDataPresent.P192_PRESENT: hci_packets.OobDataPresent.P_192_PRESENT, OobDataPresent.P256_PRESENT: hci_packets.OobDataPresent.P_256_PRESENT, OobDataPresent.P192_AND_256_PRESENT: hci_packets.OobDataPresent.P_192_AND_256_PRESENT, } _hci_event_stream = None _io_caps = hci_packets.IoCapability.DISPLAY_ONLY _auth_reqs = hci_packets.AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION _secure_connections_enabled = False _hci = None MAX_PIN_LENGTH = 16 MIN_PIN_LENGTH = 1 def _enqueue_hci_command(self, command, expect_complete): if (expect_complete): self._hci.send_command(command) else: self._hci.send_command(command) def __init__(self, device): """ Don't call super b/c the gRPC stream setup will crash test """ logging.info("Cert: Init") self._device = device self._device.wait_channel_ready() self._hci = PyHci(device) self._hci.register_for_events( hci_packets.EventCode.ENCRYPTION_CHANGE, hci_packets.EventCode.CHANGE_CONNECTION_LINK_KEY_COMPLETE, hci_packets.EventCode.CENTRAL_LINK_KEY_COMPLETE, hci_packets.EventCode.RETURN_LINK_KEYS, hci_packets.EventCode.PIN_CODE_REQUEST, hci_packets.EventCode.LINK_KEY_REQUEST, hci_packets.EventCode.LINK_KEY_NOTIFICATION, hci_packets.EventCode.ENCRYPTION_KEY_REFRESH_COMPLETE, hci_packets.EventCode.IO_CAPABILITY_REQUEST, hci_packets.EventCode.IO_CAPABILITY_RESPONSE, hci_packets.EventCode.REMOTE_OOB_DATA_REQUEST, hci_packets.EventCode.SIMPLE_PAIRING_COMPLETE, hci_packets.EventCode.USER_PASSKEY_NOTIFICATION, hci_packets.EventCode.KEYPRESS_NOTIFICATION, hci_packets.EventCode.USER_CONFIRMATION_REQUEST, hci_packets.EventCode.USER_PASSKEY_REQUEST, hci_packets.EventCode.REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION) self._hci_event_stream = self._hci.get_event_stream() def create_bond(self, address, type): """ Creates a bond from the cert perspective """ logging.info("Cert: Creating bond to '%s' from '%s'" % (str(address), str(self._device.address))) # TODO(optedoblivion): Trigger connection to Send AuthenticationRequested def remove_bond(self, address, type): """ We store the link key locally in the test and pretend So to remove_bond we need to Remove the "stored" data """ pass def set_io_capabilities(self, io_capabilities): """ Set the IO Capabilities used for the cert """ logging.info( "Cert: setting IO Capabilities data to '%s'" % self._io_capabilities_name_lookup.get(io_capabilities, "ERROR")) self._io_caps = self._io_cap_lookup.get( io_capabilities, hci_packets.IoCapability.DISPLAY_ONLY) def set_authentication_requirements(self, auth_reqs): """ Establish authentication requirements for the stack """ logging.info("Cert: setting Authentication Requirements data to '%s'" % self._auth_reqs_name_lookup.get(auth_reqs, "ERROR")) self._auth_reqs = self._auth_req_lookup.get( auth_reqs, hci_packets.AuthenticationRequirements.GENERAL_BONDING) def get_oob_data_from_controller(self, pb_oob_data_type): """ Get the Out-of-band data for SSP pairing :param pb_oob_data_type: Type of data needed :return: a tuple of bytes (192c,192r,256c,256r) with increasing security; bytes may be all 0s depending on pb_oob_data_type value """ oob_data_type = self._oob_present_lookup[pb_oob_data_type] if (oob_data_type == hci_packets.OobDataPresent.NOT_PRESENT): logging.warn("No data present, no need to call get_oob_data") return ([0 for i in range(0, 16)], [0 for i in range(0, 16)], [0 for i in range(0, 16)], [0 for i in range(0, 16)]) logging.info("Cert: Requesting OOB data") if oob_data_type == hci_packets.OobDataPresent.P_192_PRESENT: # If host and controller supports secure connections we always used ReadLocalOobExtendedDataRequest if self._secure_connections_enabled: logging.info("Cert: Requesting P192 Data; secure connections") complete_capture = HciCaptures.ReadLocalOobExtendedDataCompleteCapture( ) self._enqueue_hci_command( hci_packets.ReadLocalOobExtendedDataBuilder(), True) logging.info("Cert: Waiting for OOB response from controller") assertThat(self._hci_event_stream).emits(complete_capture) command_complete = complete_capture.get() complete = hci_packets.ReadLocalOobExtendedDataCompleteView( command_complete) return (list(complete.GetC192()), list(complete.GetR192()), [0 for i in range(0, 16)], [0 for i in range(0, 16)]) # else we use ReadLocalDataRequest else: logging.info( "Cert: Requesting P192 Data; no secure connections") complete_capture = HciCaptures.ReadLocalOobDataCompleteCapture( ) self._enqueue_hci_command( hci_packets.ReadLocalOobDataBuilder(), True) logging.info("Cert: Waiting for OOB response from controller") assertThat(self._hci_event_stream).emits(complete_capture) command_complete = complete_capture.get() complete = hci_packets.ReadLocalOobDataCompleteView( command_complete) return (list(complete.GetC()), list(complete.GetR()), [0 for i in range(0, 16)], [0 for i in range(0, 16)]) # Must be secure connection compatible to use these elif oob_data_type == hci_packets.OobDataPresent.P_256_PRESENT: logging.info( "Cert: Requesting P256 Extended Data; secure connections") complete_capture = HciCaptures.ReadLocalOobExtendedDataCompleteCapture( ) self._enqueue_hci_command( hci_packets.ReadLocalOobExtendedDataBuilder(), True) logging.info("Cert: Waiting for OOB response from controller") assertThat(self._hci_event_stream).emits(complete_capture) command_complete = complete_capture.get() complete = hci_packets.ReadLocalOobExtendedDataCompleteView( command_complete) return ([0 for i in range(0, 16)], [0 for i in range(0, 16)], list(complete.GetC256()), list(complete.GetR256())) else: # Both logging.info( "Cert: Requesting P192 AND P256 Extended Data; secure connections" ) complete_capture = HciCaptures.ReadLocalOobExtendedDataCompleteCapture( ) self._enqueue_hci_command( hci_packets.ReadLocalOobExtendedDataBuilder(), True) logging.info("Cert: Waiting for OOB response from controller") assertThat(self._hci_event_stream).emits(complete_capture) command_complete = complete_capture.get() complete = hci_packets.ReadLocalOobExtendedDataCompleteView( command_complete) return (list(complete.GetC192()), list(complete.GetR192()), list(complete.GetC256()), list(complete.GetR256())) def input_passkey(self, address, passkey): """ Pretend to answer the pairing dialog as a user """ logging.info("Cert: Waiting for PASSKEY request") assertThat(self._hci_event_stream).emits( HciMatchers.EventWithCode( hci_packets.EventCode.USER_PASSKEY_REQUEST)) logging.info("Cert: Send user input passkey %d for %s" % (passkey, address)) peer = address.decode('utf-8') self._enqueue_hci_command( hci_packets.SendKeypressNotificationBuilder( peer, hci_packets.KeypressNotificationType.ENTRY_STARTED), True) self._enqueue_hci_command( hci_packets.SendKeypressNotificationBuilder( peer, hci_packets.KeypressNotificationType.DIGIT_ENTERED), True) self._enqueue_hci_command( hci_packets.SendKeypressNotificationBuilder( peer, hci_packets.KeypressNotificationType.DIGIT_ENTERED), True) self._enqueue_hci_command( hci_packets.SendKeypressNotificationBuilder( peer, hci_packets.KeypressNotificationType.CLEARED), True) self._enqueue_hci_command( hci_packets.SendKeypressNotificationBuilder( peer, hci_packets.KeypressNotificationType.DIGIT_ENTERED), True) self._enqueue_hci_command( hci_packets.SendKeypressNotificationBuilder( peer, hci_packets.KeypressNotificationType.DIGIT_ENTERED), True) self._enqueue_hci_command( hci_packets.SendKeypressNotificationBuilder( peer, hci_packets.KeypressNotificationType.DIGIT_ERASED), True) self._enqueue_hci_command( hci_packets.SendKeypressNotificationBuilder( peer, hci_packets.KeypressNotificationType.DIGIT_ENTERED), True) self._enqueue_hci_command( hci_packets.SendKeypressNotificationBuilder( peer, hci_packets.KeypressNotificationType.DIGIT_ENTERED), True) self._enqueue_hci_command( hci_packets.SendKeypressNotificationBuilder( peer, hci_packets.KeypressNotificationType.DIGIT_ENTERED), True) self._enqueue_hci_command( hci_packets.SendKeypressNotificationBuilder( peer, hci_packets.KeypressNotificationType.DIGIT_ENTERED), True) self._enqueue_hci_command( hci_packets.SendKeypressNotificationBuilder( peer, hci_packets.KeypressNotificationType.DIGIT_ENTERED), True) self._enqueue_hci_command( hci_packets.SendKeypressNotificationBuilder( peer, hci_packets.KeypressNotificationType.ENTRY_COMPLETED), True) self._enqueue_hci_command( hci_packets.UserPasskeyRequestReplyBuilder(peer, passkey), True) def input_pin(self, address, pin): """ Pretend to answer the pairing dialog as a user """ if len(pin) > self.MAX_PIN_LENGTH or len(pin) < self.MIN_PIN_LENGTH: raise Exception("Pin code must be within range") logging.info("Cert: Waiting for PIN request") assertThat(self._hci_event_stream).emits(HciMatchers.PinCodeRequest()) logging.info("Cert: Send user input PIN %s for %s" % (pin.decode(), address)) peer = address.decode('utf-8') pin_list = list(pin) # Pad for i in range(self.MAX_PIN_LENGTH - len(pin_list)): pin_list.append(0) self._enqueue_hci_command( hci_packets.PinCodeRequestReplyBuilder(peer, len(pin), pin_list), True) def __send_ui_callback(self, address, callback_type, b, uid, pin): """ Pretend to answer the pairing dailog as a user """ logging.info("Cert: Send user input callback uid:%d; response: %s" % (uid, b)) # TODO(optedoblivion): Make callback and set it to the module def enable_secure_simple_pairing(self): """ This is called when you want to enable SSP for testing """ logging.info("Cert: Sending WRITE_SIMPLE_PAIRING_MODE [True]") self._enqueue_hci_command( hci_packets.WriteSimplePairingModeBuilder( hci_packets.Enable.ENABLED), True) logging.info("Cert: Waiting for controller response") assertThat(self._hci_event_stream).emits( HciMatchers.CommandComplete( hci_packets.OpCode.WRITE_SIMPLE_PAIRING_MODE)) def enable_secure_connections(self): """ This is called when you want to enable secure connections support """ logging.info( "Cert: Sending WRITE_SECURE_CONNECTIONS_HOST_SUPPORT [True]") self._enqueue_hci_command( hci_packets.WriteSecureConnectionsHostSupportBuilder( hci_packets.Enable.ENABLED), True) logging.info("Cert: Waiting for controller response") assertThat(self._hci_event_stream).emits( HciMatchers.CommandComplete( hci_packets.OpCode.WRITE_SECURE_CONNECTIONS_HOST_SUPPORT)) # TODO(optedoblivion): Figure this out and remove (see classic_pairing_handler.cc) #self._secure_connections_enabled = True def send_io_caps(self, address): logging.info("Cert: Waiting for IO_CAPABILITY_REQUEST") assertThat(self._hci_event_stream).emits( HciMatchers.IoCapabilityRequest()) logging.info("Cert: Sending IO_CAPABILITY_REQUEST_REPLY") oob_data_present = hci_packets.OobDataPresent.NOT_PRESENT self._enqueue_hci_command( hci_packets.IoCapabilityRequestReplyBuilder( address.decode('utf8'), self._io_caps, oob_data_present, self._auth_reqs), True) def accept_pairing(self, dut_address, reply_boolean): """ Here we handle the pairing events at the HCI level """ logging.info("Cert: Waiting for LINK_KEY_REQUEST") assertThat(self._hci_event_stream).emits(HciMatchers.LinkKeyRequest()) logging.info("Cert: Sending LINK_KEY_REQUEST_NEGATIVE_REPLY") self._enqueue_hci_command( hci_packets.LinkKeyRequestNegativeReplyBuilder( dut_address.decode('utf8')), True) self.send_io_caps(dut_address) logging.info("Cert: Waiting for USER_CONFIRMATION_REQUEST") assertThat(self._hci_event_stream).emits( HciMatchers.UserConfirmationRequest()) logging.info("Cert: Sending Simulated User Response '%s'" % reply_boolean) if reply_boolean: logging.info("Cert: Sending USER_CONFIRMATION_REQUEST_REPLY") self._enqueue_hci_command( hci_packets.UserConfirmationRequestReplyBuilder( dut_address.decode('utf8')), True) logging.info("Cert: Waiting for SIMPLE_PAIRING_COMPLETE") assertThat(self._hci_event_stream).emits( HciMatchers.SimplePairingComplete()) logging.info("Cert: Waiting for LINK_KEY_NOTIFICATION") assertThat(self._hci_event_stream).emits( HciMatchers.LinkKeyNotification()) else: logging.info( "Cert: Sending USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY") self._enqueue_hci_command( hci_packets.UserConfirmationRequestNegativeReplyBuilder( dut_address.decode('utf8')), True) logging.info("Cert: Waiting for SIMPLE_PAIRING_COMPLETE") assertThat(self._hci_event_stream).emits( HciMatchers.SimplePairingComplete()) def accept_oob_pairing(self, dut_address): logging.info("Cert: Waiting for IO_CAPABILITY_RESPONSE") assertThat(self._hci_event_stream).emits( HciMatchers.IoCapabilityResponse()) self.send_io_caps(dut_address) logging.info("Cert: Waiting for SIMPLE_PAIRING_COMPLETE") ssp_complete_capture = HciCaptures.SimplePairingCompleteCapture() assertThat(self._hci_event_stream).emits(ssp_complete_capture) ssp_complete = ssp_complete_capture.get() logging.info(ssp_complete.GetStatus()) assertThat(ssp_complete.GetStatus()).isEqualTo( hci_packets.ErrorCode.SUCCESS) def on_user_input(self, dut_address, reply_boolean, expected_ui_event): """ Cert doesn't need the test to respond to the ui event Cert responds in accept pairing """ pass def wait_for_bond_event(self, expected_bond_event): """ A bond event will be triggered once the bond process is complete. For the DUT we need to wait for it, for Cert it isn't needed. """ pass def enforce_security_policy(self, address, type, policy): """ Pass for now """ pass def wait_for_enforce_security_event(self, expected_enforce_security_event): """ Cert side needs to pass """ pass def wait_for_disconnect_event(self): """ Cert side needs to pass """ logging.info("Cert: Waiting for DISCONNECT_COMPLETE") assertThat(self._hci_event_stream).emits( HciMatchers.DisconnectionComplete()) def close(self): safeClose(self._hci)