| // Copyright 2023 The Pigweed Authors |
| // |
| // 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 |
| // |
| // https://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. |
| |
| #include "pw_bluetooth_sapphire/internal/host/hci/bredr_connection.h" |
| |
| #include "pw_bluetooth_sapphire/internal/host/transport/transport.h" |
| |
| namespace bt::hci { |
| |
| BrEdrConnection::BrEdrConnection(hci_spec::ConnectionHandle handle, |
| const DeviceAddress& local_address, |
| const DeviceAddress& peer_address, |
| pw::bluetooth::emboss::ConnectionRole role, |
| const Transport::WeakPtr& hci) |
| : AclConnection(handle, local_address, peer_address, role, hci), |
| WeakSelf(this) { |
| BT_ASSERT(local_address.type() == DeviceAddress::Type::kBREDR); |
| BT_ASSERT(peer_address.type() == DeviceAddress::Type::kBREDR); |
| BT_ASSERT(hci.is_alive()); |
| BT_ASSERT(hci->acl_data_channel()); |
| } |
| |
| bool BrEdrConnection::StartEncryption() { |
| if (state() != Connection::State::kConnected) { |
| bt_log(DEBUG, "hci", "connection closed; cannot start encryption"); |
| return false; |
| } |
| |
| BT_ASSERT(ltk().has_value() == ltk_type_.has_value()); |
| if (!ltk().has_value()) { |
| bt_log( |
| DEBUG, |
| "hci", |
| "connection link key type has not been set; not starting encryption"); |
| return false; |
| } |
| |
| auto cmd = EmbossCommandPacket::New< |
| pw::bluetooth::emboss::SetConnectionEncryptionCommandWriter>( |
| hci_spec::kSetConnectionEncryption); |
| auto params = cmd.view_t(); |
| params.connection_handle().Write(handle()); |
| params.encryption_enable().Write( |
| pw::bluetooth::emboss::GenericEnableParam::ENABLE); |
| |
| auto self = GetWeakPtr(); |
| auto event_cb = [self, handle = handle()](auto id, const EventPacket& event) { |
| if (!self.is_alive()) { |
| return; |
| } |
| |
| Result<> result = event.ToResult(); |
| if (bt_is_error(result, |
| ERROR, |
| "hci-bredr", |
| "could not set encryption on link %#.04x", |
| handle)) { |
| if (self->encryption_change_callback()) { |
| self->encryption_change_callback()(result.take_error()); |
| } |
| return; |
| } |
| bt_log(DEBUG, "hci-bredr", "requested encryption start on %#.04x", handle); |
| }; |
| |
| if (!hci().is_alive()) { |
| return false; |
| } |
| return hci()->command_channel()->SendCommand( |
| std::move(cmd), std::move(event_cb), hci_spec::kCommandStatusEventCode); |
| } |
| |
| void BrEdrConnection::HandleEncryptionStatus(Result<bool> result, |
| bool key_refreshed) { |
| bool enabled = result.is_ok() && result.value() && !key_refreshed; |
| if (enabled) { |
| ValidateEncryptionKeySize([self = GetWeakPtr()](Result<> key_valid_status) { |
| if (self.is_alive()) { |
| self->HandleEncryptionStatusValidated( |
| key_valid_status.is_ok() ? Result<bool>(fit::ok(true)) |
| : key_valid_status.take_error()); |
| } |
| }); |
| return; |
| } |
| HandleEncryptionStatusValidated(result); |
| } |
| |
| void BrEdrConnection::HandleEncryptionStatusValidated(Result<bool> result) { |
| // Core Spec Vol 3, Part C, 5.2.2.1.1 and 5.2.2.2.1 mention disconnecting the |
| // link after pairing failures (supported by TS GAP/SEC/SEM/BV-10-C), but do |
| // not specify actions to take after encryption failures. We'll choose to |
| // disconnect ACL links after encryption failure. |
| if (result.is_error()) { |
| Disconnect(pw::bluetooth::emboss::StatusCode::AUTHENTICATION_FAILURE); |
| } |
| |
| if (!encryption_change_callback()) { |
| bt_log(DEBUG, |
| "hci", |
| "%#.4x: no encryption status callback assigned", |
| handle()); |
| return; |
| } |
| encryption_change_callback()(result); |
| } |
| |
| void BrEdrConnection::ValidateEncryptionKeySize( |
| hci::ResultFunction<> key_size_validity_cb) { |
| BT_ASSERT(state() == Connection::State::kConnected); |
| |
| auto cmd = EmbossCommandPacket::New< |
| pw::bluetooth::emboss::ReadEncryptionKeySizeCommandWriter>( |
| hci_spec::kReadEncryptionKeySize); |
| cmd.view_t().connection_handle().Write(handle()); |
| |
| auto event_cb = [self = GetWeakPtr(), |
| valid_cb = std::move(key_size_validity_cb)]( |
| auto, const EventPacket& event) { |
| if (!self.is_alive()) { |
| return; |
| } |
| |
| Result<> result = event.ToResult(); |
| if (!bt_is_error(result, |
| ERROR, |
| "hci", |
| "Could not read ACL encryption key size on %#.4x", |
| self->handle())) { |
| const auto& return_params = |
| *event.return_params<hci_spec::ReadEncryptionKeySizeReturnParams>(); |
| const auto key_size = return_params.key_size; |
| bt_log(TRACE, |
| "hci", |
| "%#.4x: encryption key size %hhu", |
| self->handle(), |
| key_size); |
| |
| if (key_size < hci_spec::kMinEncryptionKeySize) { |
| bt_log(WARN, |
| "hci", |
| "%#.4x: encryption key size %hhu insufficient", |
| self->handle(), |
| key_size); |
| result = ToResult(HostError::kInsufficientSecurity); |
| } |
| } |
| valid_cb(result); |
| }; |
| hci()->command_channel()->SendCommand(std::move(cmd), std::move(event_cb)); |
| } |
| |
| } // namespace bt::hci |