From f91e22f4bb2aff2412d204447774261a270a4592 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 6 Oct 2020 20:33:30 +0200 Subject: [PATCH] Encryption Key size negotiation - PTS tests This is implementation of remaining Encryption Key Size negotiation PTS tests. Bug: 142341141 Tag: #gd-refactor Test: gd/cert/run --host LeSecurityTest:test_min_encryption_key_size_less_than_min Change-Id: I9ef35d2ef143b076365b956234febbc2ba668c95 --- gd/cert/matchers.py | 4 +- gd/hci/acl_manager/classic_impl.h | 2 +- gd/security/cert/le_security_test.py | 113 +++++++++++++++++++++++++- gd/security/facade.cc | 3 +- gd/security/facade.proto | 1 + gd/security/internal/security_manager_impl.cc | 9 +- gd/security/internal/security_manager_impl.h | 2 +- gd/security/pairing_failure.h | 4 +- gd/security/pairing_handler_le.cc | 10 ++- gd/security/security_manager_listener.h | 3 +- main/shim/btm_api.cc | 3 +- 11 files changed, 139 insertions(+), 15 deletions(-) diff --git a/gd/cert/matchers.py b/gd/cert/matchers.py index 3a4af3c5a..edeecf6c6 100644 --- a/gd/cert/matchers.py +++ b/gd/cert/matchers.py @@ -652,8 +652,8 @@ class SecurityMatchers(object): return lambda event: True if event.message_type == type and (address == None or address == event.peer) else False @staticmethod - def BondMsg(type, address=None): - return lambda event: True if event.message_type == type and (address == None or address == event.peer) else False + def BondMsg(type, address=None, reason=None): + return lambda event: True if event.message_type == type and (address == None or address == event.peer) and (reason == None or reason == event.reason) else False @staticmethod def HelperMsg(type, address=None): diff --git a/gd/hci/acl_manager/classic_impl.h b/gd/hci/acl_manager/classic_impl.h index cae587af8..1ad76ea45 100644 --- a/gd/hci/acl_manager/classic_impl.h +++ b/gd/hci/acl_manager/classic_impl.h @@ -499,7 +499,7 @@ struct classic_impl : public DisconnectorForLe, public security::ISecurityManage void OnDeviceBonded(bluetooth::hci::AddressWithType device) override {} void OnDeviceUnbonded(bluetooth::hci::AddressWithType device) override {} - void OnDeviceBondFailed(bluetooth::hci::AddressWithType device) override {} + void OnDeviceBondFailed(bluetooth::hci::AddressWithType device, security::PairingFailure status) override {} void OnEncryptionStateChanged(EncryptionChangeView encryption_change_view) override { if (!encryption_change_view.IsValid()) { diff --git a/gd/security/cert/le_security_test.py b/gd/security/cert/le_security_test.py index 5da4faa33..939bebf2f 100644 --- a/gd/security/cert/le_security_test.py +++ b/gd/security/cert/le_security_test.py @@ -16,6 +16,7 @@ import time from bluetooth_packets_python3 import hci_packets +from bluetooth_packets_python3 import security_packets from cert.event_stream import EventStream from cert.gd_base_test import GdBaseTestClass from cert.matchers import HciMatchers @@ -45,6 +46,7 @@ from security.facade_pb2 import LeMaximumEncryptionKeySizeMessage import time from bluetooth_packets_python3.hci_packets import OpCode +from bluetooth_packets_python3.security_packets import PairingFailedReason LeIoCapabilities = LeIoCapabilityMessage.LeIoCapabilities LeOobDataFlag = LeOobDataPresentMessage.LeOobDataFlag @@ -474,7 +476,7 @@ class LeSecurityTest(GdBaseTestClass): @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(self): + def test_min_encryption_key_size_equal_to_max_iut_initiator(self): """ Verify that the IUT uses correct key size during encryption as initiator. """ @@ -508,6 +510,115 @@ class LeSecurityTest(GdBaseTestClass): 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): """ diff --git a/gd/security/facade.cc b/gd/security/facade.cc index 4733651df..3078cff19 100644 --- a/gd/security/facade.cc +++ b/gd/security/facade.cc @@ -423,11 +423,12 @@ class SecurityModuleFacadeService : public SecurityModuleFacade::Service, public bond_events_.OnIncomingEvent(unbonded); } - void OnDeviceBondFailed(hci::AddressWithType peer) override { + void OnDeviceBondFailed(hci::AddressWithType peer, PairingFailure status) override { LOG_INFO("%s", peer.ToString().c_str()); BondMsg bond_failed; *bond_failed.mutable_peer() = ToFacadeAddressWithType(peer); bond_failed.set_message_type(BondMsgType::DEVICE_BOND_FAILED); + bond_failed.set_reason(static_cast(status.reason)); bond_events_.OnIncomingEvent(bond_failed); } diff --git a/gd/security/facade.proto b/gd/security/facade.proto index 65accd1ff..299ff9793 100644 --- a/gd/security/facade.proto +++ b/gd/security/facade.proto @@ -76,6 +76,7 @@ enum BondMsgType { message BondMsg { BondMsgType message_type = 1; facade.BluetoothAddressWithType peer = 2; + uint32 reason = 3; } enum HelperMsgType { DEVICE_DISCONNECTED = 0; } diff --git a/gd/security/internal/security_manager_impl.cc b/gd/security/internal/security_manager_impl.cc index 05f91237d..5cccd4181 100644 --- a/gd/security/internal/security_manager_impl.cc +++ b/gd/security/internal/security_manager_impl.cc @@ -200,10 +200,10 @@ void SecurityManagerImpl::NotifyDeviceBonded(hci::AddressWithType device) { } } -void SecurityManagerImpl::NotifyDeviceBondFailed(hci::AddressWithType device, PairingResultOrFailure status) { +void SecurityManagerImpl::NotifyDeviceBondFailed(hci::AddressWithType device, PairingFailure status) { for (auto& iter : listeners_) { - iter.second->Post(common::Bind(&ISecurityManagerListener::OnDeviceBondFailed, common::Unretained(iter.first), - device /*, status */)); + iter.second->Post( + common::Bind(&ISecurityManagerListener::OnDeviceBondFailed, common::Unretained(iter.first), device, status)); } } @@ -405,7 +405,7 @@ void SecurityManagerImpl::OnPairingHandlerComplete(hci::Address address, Pairing if (!std::holds_alternative(status)) { NotifyDeviceBonded(remote); } else { - NotifyDeviceBondFailed(remote, status); + NotifyDeviceBondFailed(remote, std::get(status)); } auto record = this->security_database_.FindOrCreate(remote); record->CancelPairing(); @@ -660,6 +660,7 @@ void SecurityManagerImpl::OnPairingFinished(security::PairingResultOrFailure pai PairingFailure failure = std::get(pairing_result); LOG_INFO(" ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ failure message: %s", failure.message.c_str()); + NotifyDeviceBondFailed(stored_chan->channel_->GetDevice(), failure); return; } diff --git a/gd/security/internal/security_manager_impl.h b/gd/security/internal/security_manager_impl.h index 97ee93478..a1c5392eb 100644 --- a/gd/security/internal/security_manager_impl.h +++ b/gd/security/internal/security_manager_impl.h @@ -200,7 +200,7 @@ class SecurityManagerImpl : public channel::ISecurityManagerChannelListener, pub os::Handler* user_interface_handler_ = nullptr; void NotifyDeviceBonded(hci::AddressWithType device); - void NotifyDeviceBondFailed(hci::AddressWithType device, PairingResultOrFailure status); + void NotifyDeviceBondFailed(hci::AddressWithType device, PairingFailure status); void NotifyDeviceUnbonded(hci::AddressWithType device); void NotifyEncryptionStateChanged(hci::EncryptionChangeView encryption_change_view); diff --git a/gd/security/pairing_failure.h b/gd/security/pairing_failure.h index f7bbbae92..fc1a4f909 100644 --- a/gd/security/pairing_failure.h +++ b/gd/security/pairing_failure.h @@ -37,11 +37,11 @@ struct PairingFailure { std::string message; /* If failure is due to mismatch of received code, this contains the received opcode */ - Code received_code_; + Code received_code_{0}; /* if the failure is due to "SMP failure", this field contains the reson code */ - PairingFailedReason reason; + PairingFailedReason reason{0}; PairingFailure(/*const base::Location& location, */ const std::string& message) : /*location(location), */ message(message) {} diff --git a/gd/security/pairing_handler_le.cc b/gd/security/pairing_handler_le.cc index 3dee2de27..3912c3b89 100644 --- a/gd/security/pairing_handler_le.cc +++ b/gd/security/pairing_handler_le.cc @@ -72,7 +72,8 @@ void PairingHandlerLe::PairingMain(InitialInformations i) { std::min(pairing_request.GetMaximumEncryptionKeySize(), pairing_response.GetMaximumEncryptionKeySize()); if (key_size < 7 || key_size > 16) { LOG_WARN("Resulting key size is bad %d", key_size); - i.OnPairingFinished(PairingFailure("Resulting key size is bad")); + SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::ENCRYPTION_KEY_SIZE)); + i.OnPairingFinished(PairingFailure("Resulting key size is bad", PairingFailedReason::ENCRYPTION_KEY_SIZE)); return; } if (key_size != 16) { @@ -284,6 +285,13 @@ Phase1ResultOrFailure PairingHandlerLe::ExchangePairingFeature(const InitialInfo pairing_request = std::get(request); } + uint8_t key_size = pairing_request->GetMaximumEncryptionKeySize(); + if (key_size < 7 || key_size > 16) { + LOG_WARN("Resulting key size is bad %d", key_size); + SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::ENCRYPTION_KEY_SIZE)); + return PairingFailure("Resulting key size is bad", PairingFailedReason::ENCRYPTION_KEY_SIZE); + } + // Send Pairing Request const auto& x = i.myPairingCapabilities; // basically pairing_response_builder = my_first_packet; diff --git a/gd/security/security_manager_listener.h b/gd/security/security_manager_listener.h index 9cad4aac3..206b2d77d 100644 --- a/gd/security/security_manager_listener.h +++ b/gd/security/security_manager_listener.h @@ -20,6 +20,7 @@ #include "common/callback.h" #include "hci/acl_manager.h" +#include "security/pairing_failure.h" namespace bluetooth { namespace security { @@ -50,7 +51,7 @@ class ISecurityManagerListener { * * @param address of the device that failed to bond */ - virtual void OnDeviceBondFailed(bluetooth::hci::AddressWithType device) = 0; + virtual void OnDeviceBondFailed(bluetooth::hci::AddressWithType device, PairingFailure status) = 0; /** * Called as a result of a failure during the bonding process. diff --git a/main/shim/btm_api.cc b/main/shim/btm_api.cc index fa965670a..7a75460c1 100644 --- a/main/shim/btm_api.cc +++ b/main/shim/btm_api.cc @@ -486,7 +486,8 @@ class ShimBondListener : public bluetooth::security::ISecurityManagerListener { bluetooth::ToRawAddress(device.GetAddress())); } - void OnDeviceBondFailed(bluetooth::hci::AddressWithType device) override { + void OnDeviceBondFailed(bluetooth::hci::AddressWithType device, + bluetooth::security::PairingFailure status) override { auto it = address_name_map_.find(device); bt_bdname_t tmp_name; if (it != address_name_map_.end()) { -- 2.11.0