blob: 56690f6f05fe9daac78c736fd93f14206a8facc0 [file] [log] [blame]
#
# Copyright 2019 - The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import time
from bluetooth_packets_python3 import hci_packets
from bluetooth_packets_python3 import security_packets
from cert.event_stream import EventStream
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
from cert.truth import assertThat
from datetime import timedelta
from facade import common_pb2 as common
from hci.facade import controller_facade_pb2 as controller_facade
from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
from google.protobuf import empty_pb2 as empty_proto
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
from security.facade_pb2 import LeMaximumEncryptionKeySizeMessage
from bluetooth_packets_python3.hci_packets import OpCode
from bluetooth_packets_python3.security_packets import PairingFailedReason
from mobly import asserts
LeIoCapabilities = LeIoCapabilityMessage.LeIoCapabilities
LeOobDataFlag = LeOobDataPresentMessage.LeOobDataFlag
DISPLAY_ONLY = LeIoCapabilityMessage(capabilities=LeIoCapabilities.DISPLAY_ONLY)
KEYBOARD_ONLY = LeIoCapabilityMessage(capabilities=LeIoCapabilities.KEYBOARD_ONLY)
NO_INPUT_NO_OUTPUT = LeIoCapabilityMessage(capabilities=LeIoCapabilities.NO_INPUT_NO_OUTPUT)
KEYBOARD_DISPLAY = LeIoCapabilityMessage(capabilities=LeIoCapabilities.KEYBOARD_DISPLAY)
OOB_NOT_PRESENT = LeOobDataPresentMessage(data_present=LeOobDataFlag.NOT_PRESENT)
OOB_PRESENT = LeOobDataPresentMessage(data_present=LeOobDataFlag.PRESENT)
class LeSecurityTestBase():
"""
Collection of tests that each sample results from
different (unique) combinations of io capabilities, authentication requirements, and oob data.
"""
def setup_test(self, dut, cert):
self.dut = dut
self.cert = cert
self.dut_security = PyLeSecurity(self.dut)
self.cert_security = PyLeSecurity(self.cert)
self.dut_hci = PyHci(self.dut)
raw_addr = self.dut.hci_controller.GetMacAddress(empty_proto.Empty()).address
self.dut_address = common.BluetoothAddressWithType(
address=common.BluetoothAddress(address=raw_addr), type=common.PUBLIC_DEVICE_ADDRESS)
privacy_policy = le_initiator_address_facade.PrivacyPolicy(
address_policy=le_initiator_address_facade.AddressPolicy.USE_PUBLIC_ADDRESS,
address_with_type=self.dut_address)
self.dut.security.SetLeInitiatorAddressPolicy(privacy_policy)
self.cert_address = common.BluetoothAddressWithType(
address=common.BluetoothAddress(
address=self.cert.hci_controller.GetMacAddress(empty_proto.Empty()).address),
type=common.PUBLIC_DEVICE_ADDRESS)
cert_privacy_policy = le_initiator_address_facade.PrivacyPolicy(
address_policy=le_initiator_address_facade.AddressPolicy.USE_PUBLIC_ADDRESS,
address_with_type=self.cert_address)
self.cert.security.SetLeInitiatorAddressPolicy(cert_privacy_policy)
def teardown_test(self):
self.dut_hci.close()
self.dut_security.close()
self.cert_security.close()
def _prepare_cert_for_connection(self):
# DUT Advertises
gap_name = hci_packets.GapData()
gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
gap_name.data = list(bytes(b'Im_The_CERT'))
gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
config = le_advertising_facade.AdvertisingConfig(
advertisement=[gap_data],
interval_min=512,
interval_max=768,
advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
own_address_type=common.USE_PUBLIC_DEVICE_ADDRESS,
channel_map=7,
filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
request = le_advertising_facade.CreateAdvertiserRequest(config=config)
create_response = self.cert.hci_le_advertising_manager.CreateAdvertiser(request)
def _prepare_dut_for_connection(self):
# DUT Advertises
gap_name = hci_packets.GapData()
gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
gap_name.data = list(bytes(b'Im_The_DUT'))
gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
config = le_advertising_facade.AdvertisingConfig(
advertisement=[gap_data],
interval_min=512,
interval_max=768,
advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
own_address_type=common.USE_PUBLIC_DEVICE_ADDRESS,
channel_map=7,
filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
request = le_advertising_facade.CreateAdvertiserRequest(config=config)
create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
@metadata(pts_test_id="SM/MAS/PROT/BV-01-C", pts_test_name="SMP Time Out – IUT Initiator")
def test_le_smp_timeout_iut_initiator(self):
"""
Verify that the IUT handles the lack of pairing response after 30 seconds when acting as initiator.
"""
self._prepare_cert_for_connection()
self.dut.security.CreateBondLe(self.cert_address)
assertThat(self.dut_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BOND_FAILED, self.cert_address), timeout=timedelta(seconds=35))
@metadata(pts_test_id="SM/SLA/PROT/BV-02-C", pts_test_name="SMP Time Out – IUT Responder")
def test_le_smp_timeout_iut_responder(self):
"""
Verify that the IUT handles the lack of pairing response after 30 seconds when acting as initiator.
"""
self.cert.security.SetLeIoCapability(KEYBOARD_ONLY)
self.dut.security.SetLeIoCapability(DISPLAY_ONLY)
self._prepare_dut_for_connection()
# 1. Lower Tester transmits Pairing Request.
self.cert.security.CreateBondLe(self.dut_address)
assertThat(self.dut_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address), timeout=timedelta(seconds=35))
# 2. IUT responds with Pairing Response.
self.dut.security.SendUiCallback(
UiCallbackMsg(
message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
# 3. In phase 2, Lower Tester does not issue the expected Pairing Confirm.
# Here the cert receives DISPLAY_PASSKEY_ENTRY. By not replying to it we make sure Pairing Confirm is never sent
assertThat(self.cert_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY, self.dut_address), timeout=timedelta(seconds=5))
# 4. IUT times out 30 seconds after issued Pairing Response and reports the failure to the Upper Tester.
assertThat(self.dut_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BOND_FAILED, self.cert_address), timeout=timedelta(seconds=35))
# 5. After additionally (at least) 10 seconds the Lower Tester issues the expected Pairing Confirm.
# 6. The IUT closes the connection before receiving the delayed response or does not respond to it when it is received.
#TODO:
#assertThat(self.dut_hci.get_event_stream()).emits(HciMatchers.Disconnect())
@metadata(pts_test_id="SM/MAS/JW/BV-01-C", pts_test_name="Just Works IUT Initiator – Success")
def test_just_works_iut_initiator(self):
"""
Verify that the IUT performs the Just Works pairing procedure correctly as central, initiator when both sides do not require MITM protection.
"""
self._prepare_cert_for_connection()
self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements()
self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements()
# 1. IUT transmits Pairing Request command with:
# a. IO capability set to any IO capability
# b. OOB data flag set to 0x00 (OOB Authentication data not present)
# c. AuthReq Bonding Flags set to ‘00’ and the MITM flag set to ‘0’ and all the reserved bits are set to ‘0’
self.dut.security.CreateBondLe(self.cert_address)
assertThat(self.cert_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
# 2. Lower Tester responds with a Pairing Response command, with:
# a. IO capability set to “KeyboardDisplay”
# b. OOB data flag set to 0x00 (OOB Authentication data not present)
# c. AuthReq Bonding Flags set to ‘00’, and the MITM flag set to ‘0’ and all the reserved bits are set to ‘0’
self.cert.security.SendUiCallback(
UiCallbackMsg(
message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
# 3. IUT and Lower Tester perform phase 2 of the just works pairing procedure and establish an encrypted link with the key generated in phase 2.
assertThat(self.dut_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
@metadata(pts_test_id="SM/SLA/JW/BV-02-C", pts_test_name="Just Works IUT Responder – Success")
def test_just_works_iut_responder(self):
"""
Verify that the IUT is able to perform the Just Works pairing procedure correctly when acting as peripheral, responder.
"""
self._prepare_dut_for_connection()
self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements()
self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements()
# 1. Lower Tester transmits Pairing Request command with:
# a. IO capability set to “NoInputNoOutput”
# b. OOB data flag set to 0x00 (OOB Authentication data not present)
# c. MITM flag set to ‘0’ 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, self.cert_address))
# 2. IUT responds with a Pairing Response command, with:
# a. IO capability set to any IO capability
# b. OOB data flag set to 0x00 (OOB Authentication data not present)
self.dut.security.SendUiCallback(
UiCallbackMsg(
message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
# IUT and Lower Tester perform phase 2 of the just works pairing and establish an encrypted link with the generated STK.
assertThat(self.dut_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
@metadata(
pts_test_id="SM/SLA/JW/BI-03-C", pts_test_name="Just Works IUT Responder – Handle AuthReq flag RFU correctly")
def test_just_works_iut_responder_auth_req_rfu(self):
"""
Verify that the IUT is able to perform the Just Works pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as peripheral, responder.
"""
self._prepare_dut_for_connection()
self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements()
self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=2)
# 1. Lower Tester transmits Pairing Request command with:
# a. IO Capability set to ”NoInputNoOutput”
# b. OOB data flag set to 0x00 (OOB Authentication data not present)
# c. MITM set to ‘0’ and all reserved bits are set to ‘1’
self.cert.security.CreateBondLe(self.dut_address)
assertThat(self.dut_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
# 2. IUT responds with a Pairing Response command, with:
# a. IO capability set to any IO capability
# 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.cert_address))
# 3. IUT and Lower Tester perform phase 2 of the just works pairing and establish an encrypted link with the generated STK.
assertThat(self.dut_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
@metadata(
pts_test_id="SM/MAS/JW/BI-04-C", pts_test_name="Just Works IUT Initiator – Handle AuthReq flag RFU correctly")
def test_just_works_iut_initiator_auth_req_rfu(self):
"""
Verify that the IUT is able to perform the Just Works pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as central, initiator.
"""
self._prepare_cert_for_connection()
self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements()
self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=3)
# 1. IUT transmits a Pairing Request command with:
# a. IO Capability set to any IO Capability
# b. OOB data flag set to 0x00 (OOB Authentication data not present)
# c. All reserved bits are set to ‘0’. For the purposes of this test the Secure Connections bit and the Keypress bits in the AuthReq bonding flag set by the IUT are ignored by the Lower Tester.
self.dut.security.CreateBondLe(self.cert_address)
assertThat(self.cert_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
# 2. Lower Tester responds with a Pairing Response command, with:
# a. IO Capability set to “NoInputNoOutput”
# 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 ‘0’ and all reserved bits are set to ‘1’. The SC and Keypress bits in the AuthReq bonding flag are set to 0 by the Lower Tester for this test.
self.cert.security.SendUiCallback(
UiCallbackMsg(
message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
# 3. IUT and Lower Tester perform phase 2 of the just works pairing and establish an encrypted link with the generated STK.
assertThat(self.dut_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
@metadata(
pts_test_id="SM/MAS/SCJW/BV-01-C", pts_test_name="Just Works, IUT Initiator, Secure Connections – Success")
def test_just_works_iut_initiator_secure_connections(self):
"""
Verify that the IUT supporting LE Secure Connections performs the Just Works or Numeric Comparison pairing procedure correctly as initiator.
"""
self._prepare_cert_for_connection()
self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements(secure_connections=1)
self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements(secure_connections=1)
# 1. IUT transmits Pairing Request command with:
# a. IO capability set to any IO capability
# b. OOB data flag set to 0x00 (OOB Authentication data not present)
# c. AuthReq Bonding Flags set to ‘00’, the MITM flag set to either ‘0’ for Just Works or '1' for Numeric Comparison, Secure Connections flag set to '1' and all the reserved bits are set to ‘0’
self.dut.security.CreateBondLe(self.cert_address)
assertThat(self.cert_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
# 2. Lower Tester responds with a Pairing Response command, with:
# a. IO capability set to “KeyboardDisplay”
# b. OOB data flag set to 0x00 (OOB Authentication data not present)
# c. AuthReq Bonding Flags set to ‘00’, the MITM flag set to ‘0’, Secure Connections flag set to '1' and all the reserved bits are set to ‘0’
self.cert.security.SendUiCallback(
UiCallbackMsg(
message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
# 3. IUT and Lower Tester perform phase 2 of the Just Works or Numeric Comparison pairing procedure according to the MITM flag and IO capabilities, 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, self.cert_address))
@metadata(
pts_test_id="SM/SLA/SCJW/BV-02-C", pts_test_name="Just Works, IUT Responder, Secure Connections – Success")
def test_just_works_iut_responder_secure_connections(self):
"""
Verify that the IUT supporting LE Secure Connections is able to perform the Just Works or Numeric Comparison pairing procedure correctly when acting as responder.
"""
self._prepare_dut_for_connection()
self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements(secure_connections=1)
self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements(secure_connections=1)
# 1. Lower Tester transmits Pairing Request command with:
# a. IO capability set to “NoInputNoOutput”
# b. OOB data flag set to 0x00 (OOB Authentication data not present)
# c. AuthReq Bonding Flags set to ‘00’, MITM flag set to ‘0’, 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, self.cert_address))
# 2. IUT responds with a Pairing Response command, with:
# a. IO capability set to any IO capability
# b. AuthReq Bonding Flags set to ‘00’, MITM flag set to either ‘0’ for Just Works or '1' for Numeric Comparison, Secure Connections flag set to '1' and all reserved bits are set to ‘0’
self.dut.security.SendUiCallback(
UiCallbackMsg(
message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
# 3. UT and Lower Tester perform phase 2 of the Just Works or Numeric Comparison pairing procedure according to the MITM flag and IO capabilities, 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, self.cert_address))
@metadata(
pts_test_id="SM/SLA/SCJW/BV-03-C",
pts_test_name="Just Works, IUT Responder, Secure Connections – Handle AuthReq Flag RFU Correctly")
def test_just_works_iut_responder_secure_connections_auth_req_rfu(self):
"""
Verify that the IUT is able to perform the Just Works pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as peripheral, responder.
"""
self._prepare_dut_for_connection()
self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements(secure_connections=1)
self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=3)
# 1. Lower Tester transmits Pairing Request command with:
# a. IO Capability set to ”NoInputNoOutput”
# b. OOB data flag set to 0x00 (OOB Authentication data not present)
# c. MITM set to ‘0’ 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, self.cert_address))
# 2. IUT responds with a Pairing Response command, with:
# a. IO capability set to any IO capability
# 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.cert_address))
# 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, self.cert_address))
@metadata(
pts_test_id="SM/MAS/SCJW/BV-04-C",
pts_test_name="Just Works, IUT Initiator, Secure Connections – Handle AuthReq Flag RFU Correctly")
def test_just_works_iut_initiator_secure_connections_auth_req_rfu(self):
"""
Verify that the IUT is able to perform the Just Works pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as central, initiator.
"""
self._prepare_cert_for_connection()
self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements(secure_connections=1)
self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=3)
# 1. IUT transmits a Pairing Request command with:
# a. IO Capability set to any IO Capability
# b. OOB data flag set to 0x00 (OOB Authentication data not present)
# c. All reserved bits are set to ‘0’.
self.dut.security.CreateBondLe(self.cert_address)
assertThat(self.cert_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
# 2. Lower Tester responds with a Pairing Response command, with:
# a. IO Capability set to “NoInputNoOutput”
# 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 ‘0’ and all reserved bits are set to a random value.
self.cert.security.SendUiCallback(
UiCallbackMsg(
message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
# 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, self.cert_address))
@metadata(
pts_test_id="SM/MAS/EKS/BV-01-C",
pts_test_name="IUT initiator, Lower Tester Maximum Encryption Key Size = Min_Encryption_Key_Length")
def test_min_encryption_key_size_equal_to_max_iut_initiator(self):
"""
Verify that the IUT uses correct key size during encryption as initiator.
"""
self._prepare_cert_for_connection()
self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements(secure_connections=1)
self.dut.security.SetLeMaximumEncryptionKeySize(
LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x10))
self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1)
self.cert.security.SetLeMaximumEncryptionKeySize(
LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x07))
# 1. IUT transmits a Pairing Request
self.dut.security.CreateBondLe(self.cert_address)
assertThat(self.cert_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
# 2. Lower Tester responds with Pairing Response command with Maximum Encryption Key Size field set to Min_Encryption_Key_Length’.
self.cert.security.SendUiCallback(
UiCallbackMsg(
message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
# 3. IUT and Lower Tester perform phase 2 of the LE pairing and establish an encrypted link with the key generated in phase 2.
assertThat(self.dut_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
@metadata(
pts_test_id="SM/SLA/EKS/BV-02-C",
pts_test_name="IUT Responder, Lower Tester Maximum Encryption Key Size = Min_Encryption_Key_Length")
def test_min_encryption_key_size_equal_to_max_iut_responder(self):
"""
Verify that the IUT uses correct key size during encryption as responder.
"""
self._prepare_dut_for_connection()
self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements()
self.dut.security.SetLeMaximumEncryptionKeySize(
LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x07))
self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements()
self.cert.security.SetLeMaximumEncryptionKeySize(
LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x10))
# 1. Lower Tester initiates Pairing Request command with Maximum Encryption Key Size field set to Min_Encryption_Key_Length’.
self.cert.security.CreateBondLe(self.dut_address)
assertThat(self.dut_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
# 2. IUT responds with Pairing Response command.
self.dut.security.SendUiCallback(
UiCallbackMsg(
message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
#3. IUT and Lower Tester perform phase 2 of the LE pairing and establish an encrypted link with the key generated in phase 2.
assertThat(self.dut_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
@metadata(
pts_test_id="SM/MAS/EKS/BI-01-C",
pts_test_name="IUT initiator, Lower Tester Maximum Encryption Key Size < Min_Encryption_Key_Length")
def test_min_encryption_key_size_less_than_min_iut_initiator(self):
"""
Verify that the IUT checks that the resultant encryption key size is not smaller than the minimum key size.
"""
self._prepare_cert_for_connection()
self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements(secure_connections=1)
self.dut.security.SetLeMaximumEncryptionKeySize(
LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x10))
self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1)
self.cert.security.SetLeMaximumEncryptionKeySize(
LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x06))
# 1. IUT transmits a Pairing Request
self.dut.security.CreateBondLe(self.cert_address)
assertThat(self.cert_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
# 2. Lower Tester responds with Pairing Response command with Maximum Encryption Key Size field set to Min_Encryption_Key_Length-1’.
self.cert.security.SendUiCallback(
UiCallbackMsg(
message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
# 3. IUT transmits the Pairing Failed command.
assertThat(self.dut_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BOND_FAILED, self.cert_address,
int(PairingFailedReason.ENCRYPTION_KEY_SIZE)))
@metadata(
pts_test_id="SM/SLA/EKS/BI-02-C",
pts_test_name="IUT Responder, Lower Tester Maximum Encryption Key Size < Min_Encryption_Key_Length")
def test_min_encryption_key_size_less_than_min_iut_responder(self):
"""
Verify that the IUT uses correct key size during encryption as responder.
"""
self._prepare_dut_for_connection()
self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements()
self.dut.security.SetLeMaximumEncryptionKeySize(
LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x06))
self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements()
self.cert.security.SetLeMaximumEncryptionKeySize(
LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x10))
# 1. Lower Tester initiates Pairing Request command with Maximum Encryption Key Size field set to Min_Encryption_Key_Length-1.
self.cert.security.CreateBondLe(self.dut_address)
assertThat(self.dut_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
self.dut.security.SendUiCallback(
UiCallbackMsg(
message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
#3. IUT transmits the Pairing Failed command.
assertThat(self.cert_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BOND_FAILED, self.dut_address,
int(PairingFailedReason.ENCRYPTION_KEY_SIZE)))
@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 central, initiator.
"""
self._prepare_cert_for_connection()
self.dut.security.SetLeIoCapability(DISPLAY_ONLY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements(secure_connections=1)
self.cert.security.SetLeIoCapability(KEYBOARD_ONLY)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1)
# 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, self.dut_address))
# 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.dut_address))
assertThat(self.cert_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY, self.dut_address))
# 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, self.cert_address), 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 peripheral, responder.
"""
self._prepare_dut_for_connection()
self.dut.security.SetLeIoCapability(DISPLAY_ONLY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements(secure_connections=1)
self.cert.security.SetLeIoCapability(KEYBOARD_DISPLAY)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1)
# 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, self.cert_address))
# 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.cert_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, self.dut_address))
# 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, self.cert_address), 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 peripheral, responder.
"""
self._prepare_dut_for_connection()
self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements(secure_connections=1)
self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=3)
# 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, self.cert_address))
# 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.cert_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.cert_address))
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, self.cert_address), timeout=timedelta(seconds=10))
@metadata(
pts_test_id="SM/MAS/SCPK/BV-04-C",
pts_test_name="Passkey Entry, IUT Initiator, Secure Connections – Handle AuthReq Flag RFU Correctly")
def test_passkey_entry_iut_initiator_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 central, initiator.
"""
self._prepare_cert_for_connection()
self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.dut_security.SetLeAuthRequirements(secure_connections=1)
self.cert.security.SetLeIoCapability(KEYBOARD_ONLY)
self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=3)
# 1. IUT transmits a Pairing Request command with:
# a. IO Capability set to “DisplayOnly” or “DisplayYesNo” or “KeyboardOnly” or “KeyboardDisplay”
# b. OOB data flag set to 0x00 (OOB Authentication data not present)
# c. All reserved bits are set to ‘0’.
self.dut.security.CreateBondLe(self.cert_address)
assertThat(self.cert_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
# 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 the value indicated in the IXIT [7] for ‘Bonding Flags’ and the MITM flag set to ‘1’ and all reserved bits are set to a random value.
self.cert.security.SendUiCallback(
UiCallbackMsg(
message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
assertThat(self.cert_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY, self.dut_address))
passkey = self.dut_security.wait_for_ui_event_passkey()
self.cert.security.SendUiCallback(
UiCallbackMsg(
message_type=UiCallbackType.PASSKEY, numeric_value=passkey, unique_id=1, address=self.dut_address))
# 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, self.cert_address), 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 central, 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.GetLeOutOfBandData(empty_proto.Empty())
self.dut.security.SetOutOfBandData(
OobDataMessage(
address=self.cert_address,
confirmation_value=oobdata.confirmation_value,
random_value=oobdata.random_value))
if cert_oob_flag == LeOobDataFlag.PRESENT:
oobdata = self.dut.security.GetLeOutOfBandData(empty_proto.Empty())
self.cert.security.SetOutOfBandData(
OobDataMessage(
address=self.dut_address,
confirmation_value=oobdata.confirmation_value,
random_value=oobdata.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, self.dut_address))
# 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.dut_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, self.cert_address), timeout=timedelta(seconds=10))
assertThat(self.cert_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.dut_address), 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, self.cert_address))
self.dut_security.wait_device_disconnect(self.cert_address)
self.cert_security.wait_device_disconnect(self.dut_address)
@metadata(
pts_test_id="SM/SLA/SCOB/BV-02-C", pts_test_name="Out of Band, IUT Responder, Secure Connections – Success")
def test_out_of_band_iut_responder_secure_connections(self):
"""
Verify that the IUT supporting LE Secure Connections is able to perform the Out-of-Band pairing procedure correctly when acting as peripheral, responder.
"""
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_dut_for_connection()
if dut_oob_flag == LeOobDataFlag.PRESENT:
oobdata = self.cert.security.GetLeOutOfBandData(empty_proto.Empty())
self.dut.security.SetOutOfBandData(
OobDataMessage(
address=self.cert_address,
confirmation_value=oobdata.confirmation_value,
random_value=oobdata.random_value))
if cert_oob_flag == LeOobDataFlag.PRESENT:
oobdata = self.dut.security.GetLeOutOfBandData(empty_proto.Empty())
self.cert.security.SetOutOfBandData(
OobDataMessage(
address=self.dut_address,
confirmation_value=oobdata.confirmation_value,
random_value=oobdata.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. Lower Tester transmits a Pairing Request command with OOB data flag set to either 0x00 or 0x01, and Secure Connections flag set to '1'.
self.cert.security.CreateBondLe(self.dut_address)
assertThat(self.dut_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
# 2. IUT responds with a Pairing Response command with Secure Connections flag set to '1' and OOB data flag set to either 0x00 or 0x01.
self.dut.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.cert_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.dut_address), timeout=timedelta(seconds=10))
assertThat(self.dut_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
self.cert.security.RemoveBond(self.dut_address)
self.dut.security.RemoveBond(self.cert_address)
assertThat(self.dut_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_UNBONDED, self.cert_address))
self.cert_security.wait_device_disconnect(self.dut_address)
self.dut_security.wait_device_disconnect(self.cert_address)
@metadata(
pts_test_id="SM/SLA/SCOB/BV-03-C",
pts_test_name="Out of Band, IUT Responder, Secure Connections – Handle AuthReq Flag RFU Correctly")
def test_out_of_band_iut_responder_secure_connections_auth_req_rfu(self):
"""
Verify that the IUT supporting LE Secure Connections is able to perform the Out-of-Band pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as peripheral, responder.
"""
reserved_bits_combinations = [1, 2, 3]
for reserved_bits in reserved_bits_combinations:
print("reserved bits in cert dut: " + str(reserved_bits))
self._prepare_dut_for_connection()
oobdata = self.cert.security.GetLeOutOfBandData(empty_proto.Empty())
self.dut.security.SetOutOfBandData(
OobDataMessage(
address=self.cert_address,
confirmation_value=oobdata.confirmation_value,
random_value=oobdata.random_value))
oobdata = self.dut.security.GetLeOutOfBandData(empty_proto.Empty())
self.cert.security.SetOutOfBandData(
OobDataMessage(
address=self.dut_address,
confirmation_value=oobdata.confirmation_value,
random_value=oobdata.random_value))
self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
self.dut.security.SetLeOobDataPresent(OOB_PRESENT)
self.dut_security.SetLeAuthRequirements(bond=1, mitm=0, secure_connections=1)
self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
self.cert.security.SetLeOobDataPresent(OOB_PRESENT)
self.cert_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1, reserved_bits=reserved_bits)
# 1. Lower Tester transmits Pairing Request command with:
# a. IO Capability set to any IO capability
# b. OOB data flag set to 0x01 (OOB Authentication data from remote device present)
# c. MITM set to ‘0’, Secure Connections flag is 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, self.cert_address))
# 2. IUT responds with a Pairing Response command, with:
# a. IO Capability set to any IO capability
# b. OOB data flag set to 0x01 (OOB Authentication data present)
# c. Secure Connections flag is set to '1', All reserved bits are set to ‘0’
self.dut.security.SendUiCallback(
UiCallbackMsg(
message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
# 3. IUT and Lower Tester perform phase 2 of the OOB authenticated pairing and establish an encrypted link with the generated LTK.
assertThat(self.cert_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.dut_address), timeout=timedelta(seconds=10))
assertThat(self.dut_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
self.cert.security.RemoveBond(self.dut_address)
self.dut.security.RemoveBond(self.cert_address)
assertThat(self.dut_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_UNBONDED, self.cert_address))
self.dut_security.wait_device_disconnect(self.cert_address)
self.cert_security.wait_device_disconnect(self.dut_address)
@metadata(
pts_test_id="SM/MAS/SCOB/BV-04-C",
pts_test_name="Out of Band, IUT Initiator, Secure Connections – Handle AuthReq Flag RFU Correctly")
def test_out_of_band_iut_initiator_secure_connections_auth_req_rfu(self):
"""
Verify that the IUT supporting LE Secure Connections is able to perform the Out-of-Band pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as central, initiator.
"""
reserved_bits_combinations = [1, 2, 3]
for reserved_bits in reserved_bits_combinations:
print("reserved bits in cert dut: " + str(reserved_bits))
self._prepare_cert_for_connection()
oobdata = self.cert.security.GetLeOutOfBandData(empty_proto.Empty())
self.dut.security.SetOutOfBandData(
OobDataMessage(
address=self.cert_address,
confirmation_value=oobdata.confirmation_value,
random_value=oobdata.random_value))
oobdata = self.dut.security.GetLeOutOfBandData(empty_proto.Empty())
self.cert.security.SetOutOfBandData(
OobDataMessage(
address=self.dut_address,
confirmation_value=oobdata.confirmation_value,
random_value=oobdata.random_value))
self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
self.dut.security.SetLeOobDataPresent(OOB_PRESENT)
self.dut_security.SetLeAuthRequirements(bond=1, mitm=0, secure_connections=1, reserved_bits=0)
self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
self.cert.security.SetLeOobDataPresent(OOB_PRESENT)
self.cert_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1, reserved_bits=reserved_bits)
# 1. IUT transmits Pairing Request command with:
# a. IO Capability set to any IO capability
# b. OOB data flag set to 0x01 (OOB Authentication data present)
# c. MITM set to ‘0’, Secure Connections flag is set to '1', and all reserved bits are set to ‘0’
self.dut.security.CreateBondLe(self.cert_address)
assertThat(self.cert_security.get_ui_stream()).emits(
SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
# 2. Lower Tester responds with a Pairing Response command, with:
# a. IO Capability set to any IO capability
# b. OOB data flag set to 0x01 (OOB Authentication data present)
# c. Secure Connections flag is set to '1', and all reserved bits are set to a random value.
self.cert.security.SendUiCallback(
UiCallbackMsg(
message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
# 3. IUT and Lower Tester perform phase 2 of the OOB authenticated pairing and establish an encrypted link with the generated LTK.
assertThat(self.dut_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
assertThat(self.cert_security.get_bond_stream()).emits(
SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.dut_address), 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, self.cert_address))
self.dut_security.wait_device_disconnect(self.cert_address)
self.cert_security.wait_device_disconnect(self.dut_address)