Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 67555d0f authored by Jakub Pawlowski's avatar Jakub Pawlowski Committed by Automerger Merge Worker
Browse files

Cert testcases for PTS LE Security am: ceb7ba7e

Original change: https://android-review.googlesource.com/c/platform/system/bt/+/1354646

Change-Id: I3f73aab89ecb7712c40d23c4540a2ff0a2f683ce
parents c4b9361f ceb7ba7e
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ from bluetooth_packets_python3 import l2cap_packets
from bluetooth_packets_python3.l2cap_packets import CommandCode, LeCommandCode
from cert.capture import Capture
from cert.matchers import L2capMatchers
from cert.matchers import SecurityMatchers
from security.facade_pb2 import UiMsgType


class HalCaptures(object):
@@ -142,3 +144,16 @@ class L2capCaptures(object):
    def _extract_credit_based_connection_response(packet):
        frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.LE_CREDIT_BASED_CONNECTION_RESPONSE)
        return l2cap_packets.LeCreditBasedConnectionResponseView(frame)


class SecurityCaptures(object):

    @staticmethod
    def DisplayPasskey():
        return Capture(SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY), SecurityCaptures._extract_passkey)

    @staticmethod
    def _extract_passkey(event):
        if event is None:
            return None
        return event.numeric_value
+11 −0
Original line number Diff line number Diff line
@@ -583,3 +583,14 @@ class L2capMatchers(object):
        response = l2cap_packets.LeCreditBasedConnectionResponseView(frame)
        return response.GetResult() == result and (result != LeCreditBasedConnectionResponseResult.SUCCESS or
                                                   response.GetDestinationCid() != 0)


class SecurityMatchers(object):

    @staticmethod
    def UiMsg(type):
        return lambda event: True if event.message_type == type else False

    @staticmethod
    def BondMsg(type):
        return lambda event: True if event.message_type == type else False
+16 −4
Original line number Diff line number Diff line
@@ -17,9 +17,11 @@
import logging

from bluetooth_packets_python3 import hci_packets
from cert.captures import SecurityCaptures
from cert.closable import Closable
from cert.closable import safeClose
from cert.event_stream import EventStream
from cert.truth import assertThat
from datetime import timedelta
from facade import common_pb2 as common
from google.protobuf import empty_pb2 as empty_proto
@@ -46,13 +48,23 @@ class PyLeSecurity(Closable):
        self._ui_event_stream = EventStream(self._device.security.FetchUiEvents(empty_proto.Empty()))
        self._bond_event_stream = EventStream(self._device.security.FetchBondEvents(empty_proto.Empty()))

    def get_ui_stream(self):
        return self._ui_event_stream

    def get_bond_stream(self):
        return self._bond_event_stream

    def wait_for_bond_event(self, expected_bond_event, timeout=timedelta(seconds=3)):
        self._bond_event_stream.assert_event_occurs(
            match_fn=lambda event: event.message_type == expected_bond_event, timeout=timeout)
        assertThat(self._bond_event_stream).emits(
            lambda event: event.message_type == expected_bond_event, timeout=timeout)

    def wait_for_ui_event(self, expected_ui_event, timeout=timedelta(seconds=3)):
        self._ui_event_stream.assert_event_occurs(
            match_fn=lambda event: event.message_type == expected_ui_event, timeout=timeout)
        assertThat(self._ui_event_stream).emits(lambda event: event.message_type == expected_ui_event, timeout=timeout)

    def wait_for_ui_event_passkey(self, timeout=timedelta(seconds=3)):
        display_passkey_capture = SecurityCaptures.DisplayPasskey()
        assertThat(self._ui_event_stream).emits(display_passkey_capture, timeout=timeout)
        return display_passkey_capture.get()

    def close(self):
        if self._ui_event_stream is not None:
+3 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ from bluetooth_packets_python3 import hci_packets
from cert.closable import Closable
from cert.closable import safeClose
from cert.event_stream import EventStream
from cert.truth import assertThat
from facade import common_pb2 as common
from google.protobuf import empty_pb2 as empty_proto
from hci.facade import facade_pb2 as hci_facade
@@ -129,7 +130,7 @@ class PySecurity(Closable):
            return False

        logging.info("DUT: Waiting for expected UI event")
        self._ui_event_stream.assert_event_occurs(get_unique_id)
        assertThat(self._ui_event_stream).emits(get_unique_id)
        # TODO(optedoblivion): Make UiCallbackType dynamic for PASSKEY when added
        self.send_ui_callback(cert_address, UiCallbackType.YES_NO, reply_boolean, ui_id)

@@ -143,7 +144,7 @@ class PySecurity(Closable):
            for Cert it isn't needed.
        """
        logging.info("DUT: Waiting for Bond Event")
        self._bond_event_stream.assert_event_occurs(lambda event: event.message_type == expected_bond_event)
        assertThat(self._bond_event_stream).emits(lambda event: event.message_type == expected_bond_event)

    def enforce_security_policy(self, address, type, policy):
        """
+152 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ from bluetooth_packets_python3 import hci_packets
from cert.event_stream import EventStream
from cert.gd_base_test import GdBaseTestClass
from cert.matchers import HciMatchers
from cert.matchers import SecurityMatchers
from cert.metadata import metadata
from cert.py_hci import PyHci
from cert.py_le_security import PyLeSecurity
@@ -453,3 +454,154 @@ class LeSecurityTest(GdBaseTestClass):

        # 3. IUT and Lower Tester perform phase 2 of the Just Works pairing and establish an encrypted link with the generated LTK.
        self.cert_security.wait_for_bond_event(expected_bond_event=BondMsgType.DEVICE_BONDED)

    @metadata(
        pts_test_id="SM/MAS/SCPK/BV-01-C", pts_test_name="Passkey Entry, IUT Initiator, Secure Connections – Success")
    def test_passkey_entry_iut_initiator_secure_connections(self):
        """
            Verify that the IUT supporting LE Secure Connections performs the Passkey Entry pairing procedure correctly as master, initiator.
        """
        self._prepare_cert_for_connection()

        self.dut.security.SetLeIoCapability(
            LeIoCapabilityMessage(capabilities=LeIoCapabilityMessage.LeIoCapabilities.DISPLAY_ONLY))
        self.dut.security.SetOobDataPresent(OobDataMessage(data_present=OobDataPresent.NOT_PRESENT))
        self.dut.security.SetLeAuthReq(LeAuthReqMsg(auth_req=0x08))

        self.cert.security.SetLeIoCapability(
            LeIoCapabilityMessage(capabilities=LeIoCapabilityMessage.LeIoCapabilities.KEYBOARD_ONLY))
        self.cert.security.SetOobDataPresent(OobDataMessage(data_present=OobDataPresent.NOT_PRESENT))
        self.cert.security.SetLeAuthReq(LeAuthReqMsg(auth_req=0x0C))

        # 1. IUT transmits Pairing Request command with:
        # a. IO capability set to “DisplayOnly” or “KeyboardOnly”
        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
        # c. AuthReq bonding flag set to ‘00’, the MITM flag set to ‘0’ and Secure Connections flag set to '1'. Keypress bit is set to '1' if supported
        self.dut.security.CreateBondLe(self.cert_address)

        assertThat(self.cert_security.get_ui_stream()).emits(SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT))

        # 2. Lower Tester responds with a Pairing Response command, with:
        # a. IO capability set to “KeyboardOnly”
        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
        # c. AuthReq bonding flag set to ‘00’, the MITM flag set to ‘1’, Secure Connections flag set to '1' and all reserved bits are set to ‘0’. Keypress bit is set to '1' if supported by the IUT.
        self.cert.security.SendUiCallback(
            UiCallbackMsg(
                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))

        assertThat(self.cert_security.get_ui_stream()).emits(SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY))

        # 3. During the phase 2 pairing, the IUT displays the 6-digit passkey while the Lower Tester prompts user to enter the 6-digit passkey. If the IUT’s IO capabilities are “KeyboardOnly” the passkey is not displayed and both IUT and Lower Tester enter the same 6-digit passkey. If Keypress bit is set, pairing keypress notifications are sent by the Lower Tester.
        passkey = self.dut_security.wait_for_ui_event_passkey()

        if passkey == 0:
            print("Passkey did not arrive into test")

        # 4. IUT and Lower Tester use the same 6-digit passkey.
        self.cert.security.SendUiCallback(
            UiCallbackMsg(
                message_type=UiCallbackType.PASSKEY, numeric_value=passkey, unique_id=1, address=self.dut_address))

        # 5. IUT and Lower Tester perform phase 2 of the Passkey Entry pairing procedure and establish an encrypted link with the LTK generated in phase 2.
        assertThat(self.dut_security.get_bond_stream()).emits(
            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED), timeout=timedelta(seconds=10))

    @metadata(
        pts_test_id="SM/SLA/SCPK/BV-02-C", pts_test_name="Passkey Entry, IUT Responder, Secure Connections – Success")
    def test_passkey_entry_iut_responder_secure_connections(self):
        """
            Verify that the IUT supporting LE Secure Connections is able to perform the Passkey Entry pairing procedure correctly when acting as slave, responder.
        """
        self._prepare_dut_for_connection()

        self.dut.security.SetLeIoCapability(
            LeIoCapabilityMessage(capabilities=LeIoCapabilityMessage.LeIoCapabilities.DISPLAY_ONLY))
        self.dut.security.SetOobDataPresent(OobDataMessage(data_present=OobDataPresent.NOT_PRESENT))
        self.dut.security.SetLeAuthReq(LeAuthReqMsg(auth_req=0x08))

        self.cert.security.SetLeIoCapability(
            LeIoCapabilityMessage(capabilities=LeIoCapabilityMessage.LeIoCapabilities.KEYBOARD_DISPLAY))
        self.cert.security.SetOobDataPresent(OobDataMessage(data_present=OobDataPresent.NOT_PRESENT))
        self.cert.security.SetLeAuthReq(LeAuthReqMsg(auth_req=0x0D))

        # 1. Lower Tester transmits Pairing Request command with:
        # a. IO capability set to “KeyboardDisplay”
        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
        # c. AuthReq bonding flag set to the value indicated in the IXIT [7] for ‘Bonding Flags’, and the MITM flag set to ‘1’ Secure Connections flag set to '1' and all reserved bits are set to ‘0’
        self.cert.security.CreateBondLe(self.dut_address)

        assertThat(self.dut_security.get_ui_stream()).emits(SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT))

        # 2. IUT responds with a Pairing Response command, with:
        # a. IO capability set to “KeyboardOnly” or “KeyboardDisplay” or “DisplayYesNo” or “DisplayOnly”
        # b. Secure Connections flag set to '1'. Keypress bit is set to '1' if supported by IUT
        self.dut.security.SendUiCallback(
            UiCallbackMsg(
                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))

        # 3. During the phase 2 passkey pairing process, Lower Tester displays the 6-digit passkey while the IUT prompts user to enter the 6-digit passkey. If the IO capabilities of the IUT are “DisplayYesNo” or “DisplayOnly” the IUT displays the 6-digit passkey while the Lower Tester enters the 6-digit passkey. If Keypress bit is set, pairing keypress notifications are send by the IUT
        passkey = self.dut_security.wait_for_ui_event_passkey()

        if passkey == 0:
            print("Passkey did not arrive into test")

        assertThat(self.cert_security.get_ui_stream()).emits(SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY))

        # 4. IUT and Lower Tester use the same pre-defined 6-digit passkey.
        self.cert.security.SendUiCallback(
            UiCallbackMsg(
                message_type=UiCallbackType.PASSKEY, numeric_value=passkey, unique_id=1, address=self.dut_address))

        # 5. IUT and Lower Tester perform phase 2 of the LE pairing and establish an encrypted link with the LTK generated in phase 2.
        assertThat(self.dut_security.get_bond_stream()).emits(
            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED), timeout=timedelta(seconds=10))

    @metadata(
        pts_test_id="SM/SLA/SCPK/BV-03-C",
        pts_test_name="Passkey Entry, IUT Responder, Secure Connections – Handle AuthReq Flag RFU Correctly")
    def test_passkey_entry_iut_responder_secure_connections_auth_req_rfu(self):
        """
            Verify that the IUT supporting LE Secure Connections is able to perform the Passkey Entry pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as slave, responder.
        """
        self._prepare_dut_for_connection()

        self.dut.security.SetLeIoCapability(
            LeIoCapabilityMessage(capabilities=LeIoCapabilityMessage.LeIoCapabilities.KEYBOARD_ONLY))
        self.dut.security.SetOobDataPresent(OobDataMessage(data_present=OobDataPresent.NOT_PRESENT))
        self.dut.security.SetLeAuthReq(LeAuthReqMsg(auth_req=0x08))

        self.cert.security.SetLeIoCapability(
            LeIoCapabilityMessage(capabilities=LeIoCapabilityMessage.LeIoCapabilities.DISPLAY_ONLY))
        self.cert.security.SetOobDataPresent(OobDataMessage(data_present=OobDataPresent.NOT_PRESENT))
        self.cert.security.SetLeAuthReq(LeAuthReqMsg(auth_req=0x0C))

        # 1. Lower Tester transmits Pairing Request command with:
        # a. IO Capability set to ”KeyboardOnly”
        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
        # c. MITM set to ‘1’ and all reserved bits are set to a random value
        self.cert.security.CreateBondLe(self.dut_address)

        assertThat(self.dut_security.get_ui_stream()).emits(SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT))

        # 2. IUT responds with a Pairing Response command, with:
        # a. IO Capability set to “KeyboardOnly” or “DisplayOnly”
        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
        # c. All reserved bits are set to ‘0’
        self.dut.security.SendUiCallback(
            UiCallbackMsg(
                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))

        passkey = self.cert_security.wait_for_ui_event_passkey()

        if passkey == 0:
            print("Passkey did not arrive into test")

        assertThat(self.dut_security.get_ui_stream()).emits(SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY))

        self.dut.security.SendUiCallback(
            UiCallbackMsg(
                message_type=UiCallbackType.PASSKEY, numeric_value=passkey, unique_id=1, address=self.cert_address))

        # 3. IUT and Lower Tester perform phase 2 of the Passkey Entry pairing and establish an encrypted link with the generated LTK.
        assertThat(self.dut_security.get_bond_stream()).emits(
            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED), timeout=timedelta(seconds=10))
Loading