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

Commit c20ba738 authored by Jakub Pawlowski's avatar Jakub Pawlowski
Browse files

First Out Of Band PTS test

Implementation of SM/MAS/SCOB/BV-01-C

This is first LE Pairing test that is establishing bond multiple times.
In order to make it work, bond removal had to be fixed, together with
link timer. Up till now, the LE connection was kept 20 seconds after
last use. After this patch, the link timeout is only one second, just
like the current android behavior.

Tag: #gd-refactor
Bug: 155399771
Test: gd/cert/run --host LeSecurityTest
Change-Id: I2398f7a50474b61d3147f0afcab35154fe87db80
parent 87abacd7
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ class ParameterProvider {
    return std::chrono::seconds(20);
  }
  virtual std::chrono::milliseconds GetLeLinkIdleDisconnectTimeout() {
    return std::chrono::seconds(20);
    return std::chrono::seconds(1);
  }
  virtual uint16_t GetLeMps() {
    return 251;
+70 −0
Original line number Diff line number Diff line
@@ -34,12 +34,14 @@ from neighbor.facade import facade_pb2 as neighbor_facade
from security.cert.cert_security import CertSecurity
from security.facade_pb2 import AuthenticationRequirements
from security.facade_pb2 import BondMsgType
from security.facade_pb2 import OobDataMessage
from security.facade_pb2 import UiCallbackMsg
from security.facade_pb2 import UiCallbackType
from security.facade_pb2 import UiMsgType
from security.facade_pb2 import LeAuthRequirementsMessage
from security.facade_pb2 import LeIoCapabilityMessage
from security.facade_pb2 import LeOobDataPresentMessage
import time
from bluetooth_packets_python3.hci_packets import OpCode

LeIoCapabilities = LeIoCapabilityMessage.LeIoCapabilities
@@ -51,6 +53,7 @@ NO_INPUT_NO_OUTPUT = LeIoCapabilityMessage(capabilities=LeIoCapabilities.NO_INPU
KEYBOARD_DISPLAY = LeIoCapabilityMessage(capabilities=LeIoCapabilities.KEYBOARD_DISPLAY)

OOB_NOT_PRESENT = LeOobDataPresentMessage(data_present=LeOobDataFlag.NOT_PRESENT)
OOB_PRESENT = LeOobDataPresentMessage(data_present=LeOobDataFlag.PRESENT)


class LeSecurityTest(GdBaseTestClass):
@@ -635,3 +638,70 @@ 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.
        assertThat(self.dut_security.get_bond_stream()).emits(
            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED), timeout=timedelta(seconds=10))

    @metadata(
        pts_test_id="SM/MAS/SCOB/BV-01-C", pts_test_name="Out of Band, IUT Initiator, Secure Connections – Success")
    def test_out_of_band_iut_initiator_secure_connections(self):
        """
            Verify that the IUT supporting LE Secure Connections performs the Out-of-Band pairing procedure correctly as master, initiator.
        """

        oob_combinations = [(OOB_NOT_PRESENT, OOB_PRESENT), (OOB_PRESENT, OOB_NOT_PRESENT), (OOB_PRESENT, OOB_PRESENT)]

        for (dut_oob_flag, cert_oob_flag) in oob_combinations:
            print("oob flag combination dut: " + str(dut_oob_flag) + ", cert: " + str(cert_oob_flag))

            self._prepare_cert_for_connection()

            if dut_oob_flag == LeOobDataFlag.PRESENT:
                oobdata = self.cert.security.GetOutOfBandData(empty_proto.Empty())
                self.dut.security.SetOutOfBandData(
                    OobDataMessage(
                        address=self.cert_address,
                        le_sc_confirmation_value=oobdata.le_sc_confirmation_value,
                        le_sc_random_value=oobdata.le_sc_random_value))

            if cert_oob_flag == LeOobDataFlag.PRESENT:
                oobdata = self.dut.security.GetOutOfBandData(empty_proto.Empty())
                self.cert.security.SetOutOfBandData(
                    OobDataMessage(
                        address=self.dut_address,
                        le_sc_confirmation_value=oobdata.le_sc_confirmation_value,
                        le_sc_random_value=oobdata.le_sc_random_value))

            self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
            self.dut.security.SetLeOobDataPresent(dut_oob_flag)
            self.dut_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1)

            self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
            self.cert.security.SetLeOobDataPresent(cert_oob_flag)
            self.cert_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1)

            # 1. IUT transmits a Pairing Request command with OOB data flag set to either 0x00 or 0x01, and Secure Connections flag set to '1'.
            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 Secure Connections flag set to '1' and OOB data flag set to either 0x00 or 0x01.
            self.cert.security.SendUiCallback(
                UiCallbackMsg(
                    message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))

            # 3. IUT uses the 128-bit value generated by the Lower Tester as the confirm value. Similarly, the Lower Tester uses the 128-bit value generated by the IUT as the confirm value.

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

            assertThat(self.cert_security.get_bond_stream()).emits(
                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED), timeout=timedelta(seconds=10))

            self.dut.security.RemoveBond(self.cert_address)
            self.cert.security.RemoveBond(self.dut_address)

            assertThat(self.dut_security.get_bond_stream()).emits(SecurityMatchers.BondMsg(BondMsgType.DEVICE_UNBONDED))

            #disconnection should happen after one second timeout, sleep for 2 just to be sure
            #TODO(jpawlowski): wait for disconnection event instead of sleeping.
            time.sleep(2)
+36 −1
Original line number Diff line number Diff line
@@ -107,6 +107,13 @@ void SecurityManagerImpl::CreateBondLe(hci::AddressWithType address) {

  pending_le_pairing_.address_ = address;

  LeFixedChannelEntry* stored_chan = FindStoredLeChannel(address);
  if (stored_chan) {
    // We are already connected
    ConnectionIsReadyStartPairing(stored_chan);
    return;
  }

  l2cap_manager_le_->ConnectServices(
      address, common::BindOnce(&SecurityManagerImpl::OnConnectionFailureLe, common::Unretained(this)),
      security_handler_);
@@ -119,8 +126,11 @@ void SecurityManagerImpl::CancelBond(hci::AddressWithType device) {
    pairing_handler_map_.erase(entry);
    cancel_me->Cancel();
  }

  auto record = security_database_.FindOrCreate(device);
  record->CancelPairing();

  WipeLePairingHandler();
}

void SecurityManagerImpl::RemoveBond(hci::AddressWithType device) {
@@ -130,6 +140,8 @@ void SecurityManagerImpl::RemoveBond(hci::AddressWithType device) {
  // Signal disconnect
  // Remove security record
  // Signal Remove from database

  NotifyDeviceUnbonded(device);
}

void SecurityManagerImpl::SetUserInterfaceHandler(UI* user_interface, os::Handler* handler) {
@@ -418,6 +430,8 @@ void SecurityManagerImpl::OnSmpCommandLe(hci::AddressWithType device) {

    LOG_INFO("start of security request handling!");

    stored_chan->channel_->Acquire();

    PairingRequestView pairing_request = PairingRequestView::Create(temp_cmd_view);
    auto& enqueue_buffer = stored_chan->enqueue_buffer_;

@@ -471,7 +485,6 @@ void SecurityManagerImpl::OnConnectionOpenLe(std::unique_ptr<l2cap::le::FixedCha
  all_channels_.push_back({std::move(channel_param), std::move(enqueue_buffer_temp)});
  auto& stored_channel = all_channels_.back();
  auto& channel = stored_channel.channel_;
  auto& enqueue_buffer = stored_channel.enqueue_buffer_;

  channel->RegisterOnCloseCallback(
      security_handler_,
@@ -484,6 +497,15 @@ void SecurityManagerImpl::OnConnectionOpenLe(std::unique_ptr<l2cap::le::FixedCha
    return;
  }

  ConnectionIsReadyStartPairing(&stored_channel);
}

void SecurityManagerImpl::ConnectionIsReadyStartPairing(LeFixedChannelEntry* stored_channel) {
  auto& channel = stored_channel->channel_;
  auto& enqueue_buffer = stored_channel->enqueue_buffer_;

  stored_channel->channel_->Acquire();

  std::optional<InitialInformations::out_of_band_data> remote_oob_data = std::nullopt;
  if (remote_oob_data_address_.has_value() && remote_oob_data_address_.value() == channel->GetDevice())
    remote_oob_data = InitialInformations::out_of_band_data{.le_sc_c = remote_oob_data_le_sc_c_.value(),
@@ -574,6 +596,11 @@ SecurityManagerImpl::SecurityManagerImpl(
void SecurityManagerImpl::OnPairingFinished(security::PairingResultOrFailure pairing_result) {
  LOG_INFO(" ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ Received pairing result");

  LeFixedChannelEntry* stored_chan = FindStoredLeChannel(pending_le_pairing_.address_);
  if (stored_chan) {
    stored_chan->channel_->Release();
  }

  if (std::holds_alternative<PairingFailure>(pairing_result)) {
    PairingFailure failure = std::get<PairingFailure>(pairing_result);
    LOG_INFO(" ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ failure message: %s",
@@ -584,6 +611,14 @@ void SecurityManagerImpl::OnPairingFinished(security::PairingResultOrFailure pai
  auto result = std::get<PairingResult>(pairing_result);
  LOG_INFO("Pairing with %s was successful", result.connection_address.ToString().c_str());
  NotifyDeviceBonded(result.connection_address);

  security_handler_->CallOn(this, &SecurityManagerImpl::WipeLePairingHandler);
}

void SecurityManagerImpl::WipeLePairingHandler() {
  pending_le_pairing_.handler_.reset();
  pending_le_pairing_.connection_handle_ = 0;
  pending_le_pairing_.address_ = hci::AddressWithType();
}

// Facade Configuration API functions
+2 −0
Original line number Diff line number Diff line
@@ -211,6 +211,8 @@ class SecurityManagerImpl : public channel::ISecurityManagerChannelListener, pub
      l2cap::classic::SecurityPolicy policy,
      l2cap::classic::SecurityEnforcementInterface::ResultCallback result_callback,
      bool try_meet_requirements);
  void ConnectionIsReadyStartPairing(LeFixedChannelEntry* stored_channel);
  void WipeLePairingHandler();

  os::Handler* security_handler_ __attribute__((unused));
  l2cap::le::L2capLeModule* l2cap_le_module_ __attribute__((unused));