| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "connection.h" |
| |
| #include <endian.h> |
| |
| #include "command_channel.h" |
| #include "defaults.h" |
| #include "lib/async/default.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/log.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/hci.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/status.h" |
| #include "src/lib/fxl/strings/string_printf.h" |
| #include "transport.h" |
| |
| namespace bt::hci { |
| |
| // Production implementation of the Connection class against a HCI transport. |
| class ConnectionImpl final : public Connection { |
| public: |
| ConnectionImpl(ConnectionHandle handle, LinkType ll_type, Role role, |
| const DeviceAddress& local_address, const DeviceAddress& peer_address, |
| fxl::WeakPtr<Transport> hci); |
| ~ConnectionImpl() override; |
| |
| // Connection overrides: |
| fxl::WeakPtr<Connection> WeakPtr() override; |
| bool StartEncryption() override; |
| State state() const { return conn_state_; } |
| |
| // Sends HCI Disconnect command with |reason|. Takes over handling of HCI |
| // Disconnection Complete event with a lambda so that connection instance can be safely destroyed |
| // immediately. |
| void Disconnect(StatusCode reason) override; |
| |
| void set_state(State state) { conn_state_ = state; }; |
| |
| private: |
| // Start the BR/EDR link layer encryption. |ltk_| and |ltk_type_| must have already been set and |
| // may be used for bonding and detecting the security properties following the encryption enable. |
| bool BrEdrStartEncryption(); |
| |
| // Start the LE link layer authentication procedure using the given |ltk|. |
| bool LEStartEncryption(const LinkKey& ltk); |
| |
| // Called when encryption is enabled or disabled as a result of the link layer |
| // encryption "start" or "pause" procedure. If |status| indicates failure, |
| // then this method will disconnect the link before notifying the encryption |
| // change handler. |
| void HandleEncryptionStatus(Status status, bool enabled); |
| |
| // Request the current encryption key size and call |key_size_validity_cb| |
| // when the controller responds. |key_size_validity_cb| will be called with a |
| // success only if the link is encrypted with a key of size at least |
| // |hci::kMinEncryptionKeySize|. Only valid for ACL-U connections. |
| void ValidateAclEncryptionKeySize(hci::StatusCallback key_size_validity_cb); |
| |
| // HCI event handlers. |
| CommandChannel::EventCallbackResult OnEncryptionChangeEvent(const EventPacket& event); |
| CommandChannel::EventCallbackResult OnEncryptionKeyRefreshCompleteEvent(const EventPacket& event); |
| CommandChannel::EventCallbackResult OnLELongTermKeyRequestEvent(const EventPacket& event); |
| CommandChannel::EventCallbackResult OnDisconnectionCompleteEvent(const EventPacket& event); |
| |
| // Checks |event|, unregisters link, and clears pending packets count. |
| // If the disconnection was initiated by the peer, call |peer_disconnect_callback|. |
| // Returns true if event was valid and for this connection. |
| // This method is static so that it can be called in an event handler |
| // after this object has been destroyed. |
| static CommandChannel::EventCallbackResult OnDisconnectionComplete( |
| fxl::WeakPtr<ConnectionImpl> self, ConnectionHandle handle, fxl::WeakPtr<Transport> hci, |
| const EventPacket& event); |
| |
| fit::thread_checker thread_checker_; |
| |
| // IDs for encryption related HCI event handlers. |
| CommandChannel::EventHandlerId enc_change_id_; |
| CommandChannel::EventHandlerId enc_key_refresh_cmpl_id_; |
| CommandChannel::EventHandlerId le_ltk_request_id_; |
| |
| // The underlying HCI transport. |
| fxl::WeakPtr<Transport> hci_; |
| |
| State conn_state_; |
| |
| // Keep this as the last member to make sure that all weak pointers are |
| // invalidated before other members get destroyed. |
| fxl::WeakPtrFactory<ConnectionImpl> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ConnectionImpl); |
| }; |
| |
| namespace { |
| |
| template < |
| CommandChannel::EventCallbackResult (ConnectionImpl::*EventHandlerMethod)(const EventPacket&)> |
| CommandChannel::EventCallback BindEventHandler(fxl::WeakPtr<ConnectionImpl> conn) { |
| return [conn](const auto& event) { |
| if (conn) { |
| return ((conn.get())->*EventHandlerMethod)(event); |
| } |
| return CommandChannel::EventCallbackResult::kRemove; |
| }; |
| } |
| |
| } // namespace |
| |
| // ====== Connection member methods ===== |
| |
| std::string Connection::LinkTypeToString(Connection::LinkType type) { |
| switch (type) { |
| case Connection::LinkType::kACL: |
| return "ACL"; |
| case Connection::LinkType::kSCO: |
| return "SCO"; |
| case Connection::LinkType::kESCO: |
| return "ESCO"; |
| case Connection::LinkType::kLE: |
| return "LE"; |
| } |
| |
| ZX_PANIC("invalid link type: %u", static_cast<unsigned int>(type)); |
| return "(invalid)"; |
| } |
| |
| // static |
| std::unique_ptr<Connection> Connection::CreateLE(ConnectionHandle handle, Role role, |
| const DeviceAddress& local_address, |
| const DeviceAddress& peer_address, |
| const LEConnectionParameters& params, |
| fxl::WeakPtr<Transport> hci) { |
| ZX_DEBUG_ASSERT(local_address.type() != DeviceAddress::Type::kBREDR); |
| ZX_DEBUG_ASSERT(peer_address.type() != DeviceAddress::Type::kBREDR); |
| auto conn = std::make_unique<ConnectionImpl>(handle, LinkType::kLE, role, local_address, |
| peer_address, std::move(hci)); |
| conn->set_low_energy_parameters(params); |
| return conn; |
| } |
| |
| // static |
| std::unique_ptr<Connection> Connection::CreateACL(ConnectionHandle handle, Role role, |
| const DeviceAddress& local_address, |
| const DeviceAddress& peer_address, |
| fxl::WeakPtr<Transport> hci) { |
| ZX_DEBUG_ASSERT(local_address.type() == DeviceAddress::Type::kBREDR); |
| ZX_DEBUG_ASSERT(peer_address.type() == DeviceAddress::Type::kBREDR); |
| auto conn = std::make_unique<ConnectionImpl>(handle, LinkType::kACL, role, local_address, |
| peer_address, std::move(hci)); |
| return conn; |
| } |
| |
| std::unique_ptr<Connection> Connection::CreateSCO(hci::LinkType link_type, ConnectionHandle handle, |
| const DeviceAddress& local_address, |
| const DeviceAddress& peer_address, |
| fxl::WeakPtr<Transport> hci) { |
| ZX_ASSERT(local_address.type() == DeviceAddress::Type::kBREDR); |
| ZX_ASSERT(peer_address.type() == DeviceAddress::Type::kBREDR); |
| ZX_ASSERT(link_type == hci::LinkType::kSCO || link_type == hci::LinkType::kExtendedSCO); |
| |
| LinkType conn_type = link_type == hci::LinkType::kSCO ? LinkType::kSCO : LinkType::kESCO; |
| |
| // TODO(fxb/61070): remove role for SCO connections, as it has no meaning |
| auto conn = std::make_unique<ConnectionImpl>(handle, conn_type, Role::kMaster, local_address, |
| peer_address, std::move(hci)); |
| return conn; |
| } |
| |
| Connection::Connection(ConnectionHandle handle, LinkType ll_type, Role role, |
| const DeviceAddress& local_address, const DeviceAddress& peer_address) |
| : ll_type_(ll_type), |
| handle_(handle), |
| role_(role), |
| local_address_(local_address), |
| peer_address_(peer_address) { |
| ZX_DEBUG_ASSERT(handle_); |
| } |
| |
| std::string Connection::ToString() const { |
| std::string params = ""; |
| if (ll_type() == LinkType::kLE) { |
| params = ", " + le_params_.ToString(); |
| } |
| return fxl::StringPrintf("(%s link - handle: %#.4x, role: %s, address: %s%s)", |
| LinkTypeToString(ll_type_).c_str(), handle_, |
| role_ == Role::kMaster ? "master" : "slave", |
| peer_address_.ToString().c_str(), params.c_str()); |
| } |
| |
| // ====== ConnectionImpl member methods ====== |
| |
| ConnectionImpl::ConnectionImpl(ConnectionHandle handle, LinkType ll_type, Role role, |
| const DeviceAddress& local_address, |
| const DeviceAddress& peer_address, fxl::WeakPtr<Transport> hci) |
| : Connection(handle, ll_type, role, local_address, peer_address), |
| hci_(std::move(hci)), |
| conn_state_(State::kConnected), |
| weak_ptr_factory_(this) { |
| ZX_DEBUG_ASSERT(hci_); |
| |
| auto self = weak_ptr_factory_.GetWeakPtr(); |
| |
| enc_change_id_ = hci_->command_channel()->AddEventHandler( |
| kEncryptionChangeEventCode, BindEventHandler<&ConnectionImpl::OnEncryptionChangeEvent>(self)); |
| |
| enc_key_refresh_cmpl_id_ = hci_->command_channel()->AddEventHandler( |
| kEncryptionKeyRefreshCompleteEventCode, |
| BindEventHandler<&ConnectionImpl::OnEncryptionKeyRefreshCompleteEvent>(self)); |
| |
| le_ltk_request_id_ = hci_->command_channel()->AddLEMetaEventHandler( |
| kLELongTermKeyRequestSubeventCode, |
| BindEventHandler<&ConnectionImpl::OnLELongTermKeyRequestEvent>(self)); |
| |
| auto disconn_complete_handler = [self, handle, hci = hci_](auto& event) { |
| return ConnectionImpl::OnDisconnectionComplete(self, handle, hci, event); |
| }; |
| |
| hci_->command_channel()->AddEventHandler(kDisconnectionCompleteEventCode, |
| disconn_complete_handler); |
| |
| // Allow packets to be sent on this link immediately. |
| hci_->acl_data_channel()->RegisterLink(handle, ll_type); |
| } |
| |
| ConnectionImpl::~ConnectionImpl() { |
| if (conn_state_ == Connection::State::kConnected) { |
| Disconnect(StatusCode::kRemoteUserTerminatedConnection); |
| } |
| |
| // Unregister HCI event handlers. |
| hci_->command_channel()->RemoveEventHandler(enc_change_id_); |
| hci_->command_channel()->RemoveEventHandler(enc_key_refresh_cmpl_id_); |
| hci_->command_channel()->RemoveEventHandler(le_ltk_request_id_); |
| } |
| |
| fxl::WeakPtr<Connection> ConnectionImpl::WeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } |
| |
| CommandChannel::EventCallbackResult ConnectionImpl::OnDisconnectionComplete( |
| fxl::WeakPtr<ConnectionImpl> self, ConnectionHandle handle, fxl::WeakPtr<Transport> hci, |
| const EventPacket& event) { |
| ZX_DEBUG_ASSERT(event.event_code() == kDisconnectionCompleteEventCode); |
| |
| if (event.view().payload_size() != sizeof(DisconnectionCompleteEventParams)) { |
| bt_log(WARN, "hci", "malformed disconnection complete event"); |
| return CommandChannel::EventCallbackResult::kContinue; |
| } |
| |
| const auto& params = event.params<DisconnectionCompleteEventParams>(); |
| const auto event_handle = le16toh(params.connection_handle); |
| |
| // Silently ignore this event as it isn't meant for this connection. |
| if (event_handle != handle) { |
| return CommandChannel::EventCallbackResult::kContinue; |
| } |
| |
| bt_log(INFO, "hci", "disconnection complete - %s, handle: %#.4x, reason: %#.2x", |
| bt_str(event.ToStatus()), handle, params.reason); |
| |
| // Stop data flow and revoke queued packets for this connection. |
| hci->acl_data_channel()->UnregisterLink(handle); |
| |
| // Notify ACL data channel that packets have been flushed from controller buffer. |
| hci->acl_data_channel()->ClearControllerPacketCount(handle); |
| |
| if (!self) { |
| return CommandChannel::EventCallbackResult::kRemove; |
| } |
| |
| self->set_state(State::kDisconnected); |
| |
| // Peer disconnect. Callback may destroy connection. |
| if (self->peer_disconnect_callback()) { |
| self->peer_disconnect_callback()(self.get(), params.reason); |
| } |
| |
| return CommandChannel::EventCallbackResult::kRemove; |
| } |
| |
| void ConnectionImpl::Disconnect(StatusCode reason) { |
| ZX_ASSERT(conn_state_ == Connection::State::kConnected); |
| |
| conn_state_ = Connection::State::kWaitingForDisconnectionComplete; |
| |
| // Here we send a HCI_Disconnect command without waiting for it to complete. |
| auto status_cb = [](auto id, const EventPacket& event) { |
| ZX_DEBUG_ASSERT(event.event_code() == kCommandStatusEventCode); |
| hci_is_error(event, TRACE, "hci", "ignoring disconnection failure"); |
| }; |
| |
| auto disconn = CommandPacket::New(kDisconnect, sizeof(DisconnectCommandParams)); |
| auto params = disconn->mutable_payload<DisconnectCommandParams>(); |
| params->connection_handle = htole16(handle()); |
| params->reason = reason; |
| |
| bt_log(DEBUG, "hci", "disconnecting connection (handle: %#.4x, reason: %#.2x)", handle(), reason); |
| |
| // Send HCI Disconnect. |
| hci_->command_channel()->SendCommand(std::move(disconn), std::move(status_cb), |
| kCommandStatusEventCode); |
| } |
| |
| bool ConnectionImpl::StartEncryption() { |
| ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid()); |
| if (conn_state_ != Connection::State::kConnected) { |
| bt_log(DEBUG, "hci", "connection closed; cannot start encryption"); |
| return false; |
| } |
| |
| if (ll_type() != LinkType::kLE) { |
| return BrEdrStartEncryption(); |
| } |
| |
| if (role() != Role::kMaster) { |
| bt_log(DEBUG, "hci", "only the master can start encryption"); |
| return false; |
| } |
| |
| if (!ltk()) { |
| bt_log(DEBUG, "hci", "connection has no LTK; cannot start encryption"); |
| return false; |
| } |
| |
| return LEStartEncryption(*ltk()); |
| } |
| |
| bool ConnectionImpl::BrEdrStartEncryption() { |
| ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid()); |
| |
| ZX_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 = |
| CommandPacket::New(kSetConnectionEncryption, sizeof(SetConnectionEncryptionCommandParams)); |
| auto* params = cmd->mutable_payload<SetConnectionEncryptionCommandParams>(); |
| params->connection_handle = htole16(handle()); |
| params->encryption_enable = GenericEnableParam::kEnable; |
| |
| auto self = weak_ptr_factory_.GetWeakPtr(); |
| auto status_cb = [self, handle = handle()](auto id, const EventPacket& event) { |
| if (!self) { |
| return; |
| } |
| |
| const Status status = event.ToStatus(); |
| if (!bt_is_error(status, ERROR, "hci-bredr", "could not set encryption on link %#.04x", |
| handle)) { |
| bt_log(DEBUG, "hci-bredr", "requested encryption start on %#.04x", handle); |
| return; |
| } |
| |
| if (self->encryption_change_callback()) { |
| self->encryption_change_callback()(status, false); |
| } |
| }; |
| |
| return hci_->command_channel()->SendCommand(std::move(cmd), std::move(status_cb), |
| kCommandStatusEventCode) != 0u; |
| } |
| |
| bool ConnectionImpl::LEStartEncryption(const LinkKey& ltk) { |
| ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid()); |
| ZX_ASSERT(!ltk_type().has_value()); |
| |
| // TODO(fxbug.dev/801): Tell the data channel to stop data flow. |
| |
| auto cmd = CommandPacket::New(kLEStartEncryption, sizeof(LEStartEncryptionCommandParams)); |
| auto* params = cmd->mutable_payload<LEStartEncryptionCommandParams>(); |
| params->connection_handle = htole16(handle()); |
| params->random_number = htole64(ltk.rand()); |
| params->encrypted_diversifier = htole16(ltk.ediv()); |
| params->long_term_key = ltk.value(); |
| |
| auto self = weak_ptr_factory_.GetWeakPtr(); |
| auto status_cb = [self, handle = handle()](auto id, const EventPacket& event) { |
| if (!self) { |
| return; |
| } |
| |
| const Status status = event.ToStatus(); |
| if (!bt_is_error(status, ERROR, "hci-le", "could not set encryption on link %#.04x", handle)) { |
| bt_log(DEBUG, "hci-le", "requested encryption start on %#.04x", handle); |
| return; |
| } |
| |
| if (self->encryption_change_callback()) { |
| self->encryption_change_callback()(status, false); |
| } |
| }; |
| |
| return hci_->command_channel()->SendCommand(std::move(cmd), std::move(status_cb), |
| kCommandStatusEventCode) != 0u; |
| } |
| |
| void ConnectionImpl::HandleEncryptionStatus(Status status, bool enabled) { |
| // "On an authentication failure, the connection shall be automatically |
| // disconnected by the Link Layer." (HCI_LE_Start_Encryption, Vol 2, Part E, |
| // 7.8.24). We make sure of this by telling the controller to disconnect. |
| // |
| // For ACL-U, 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 (!status) { |
| Disconnect(StatusCode::kAuthenticationFailure); |
| } else { |
| // TODO(fxbug.dev/801): Tell the data channel to resume data flow. |
| } |
| |
| if (!encryption_change_callback()) { |
| bt_log(DEBUG, "hci", "%#.4x: no encryption status callback assigned", handle()); |
| return; |
| } |
| |
| encryption_change_callback()(status, enabled); |
| } |
| |
| void ConnectionImpl::ValidateAclEncryptionKeySize(hci::StatusCallback key_size_validity_cb) { |
| ZX_ASSERT(ll_type() == LinkType::kACL); |
| ZX_ASSERT(conn_state_ == Connection::State::kConnected); |
| |
| auto cmd = CommandPacket::New(kReadEncryptionKeySize, sizeof(ReadEncryptionKeySizeParams)); |
| auto* params = cmd->mutable_payload<ReadEncryptionKeySizeParams>(); |
| params->connection_handle = htole16(handle()); |
| |
| auto status_cb = [self = weak_ptr_factory_.GetWeakPtr(), |
| valid_cb = std::move(key_size_validity_cb)](auto, const EventPacket& event) { |
| if (!self) { |
| return; |
| } |
| |
| Status status = event.ToStatus(); |
| if (!bt_is_error(status, ERROR, "hci", "Could not read ACL encryption key size on %#.4x", |
| self->handle())) { |
| const auto& return_params = *event.return_params<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::kMinEncryptionKeySize) { |
| bt_log(WARN, "hci", "%#.4x: encryption key size %hhu insufficient", self->handle(), |
| key_size); |
| status = Status(HostError::kInsufficientSecurity); |
| } |
| } |
| valid_cb(status); |
| }; |
| |
| hci_->command_channel()->SendCommand(std::move(cmd), std::move(status_cb)); |
| } |
| |
| CommandChannel::EventCallbackResult ConnectionImpl::OnEncryptionChangeEvent( |
| const EventPacket& event) { |
| ZX_DEBUG_ASSERT(event.event_code() == kEncryptionChangeEventCode); |
| ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid()); |
| |
| if (event.view().payload_size() != sizeof(EncryptionChangeEventParams)) { |
| bt_log(WARN, "hci", "malformed encryption change event"); |
| return CommandChannel::EventCallbackResult::kContinue; |
| } |
| |
| const auto& params = event.params<EncryptionChangeEventParams>(); |
| hci::ConnectionHandle handle = le16toh(params.connection_handle); |
| |
| // Silently ignore the event as it isn't meant for this connection. |
| if (handle != this->handle()) { |
| return CommandChannel::EventCallbackResult::kContinue; |
| } |
| |
| if (conn_state_ != Connection::State::kConnected) { |
| bt_log(DEBUG, "hci", "encryption change ignored: connection closed"); |
| return CommandChannel::EventCallbackResult::kContinue; |
| } |
| |
| Status status(params.status); |
| bool enabled = params.encryption_enabled != EncryptionStatus::kOff; |
| |
| bt_log(DEBUG, "hci", "encryption change (%s) %s", enabled ? "enabled" : "disabled", |
| status.ToString().c_str()); |
| |
| if (ll_type() == LinkType::kACL && status && enabled) { |
| ValidateAclEncryptionKeySize([this](const Status& key_valid_status) { |
| HandleEncryptionStatus(key_valid_status, true /* enabled */); |
| }); |
| return CommandChannel::EventCallbackResult::kContinue; |
| } |
| |
| HandleEncryptionStatus(status, enabled); |
| |
| return CommandChannel::EventCallbackResult::kContinue; |
| } |
| |
| CommandChannel::EventCallbackResult ConnectionImpl::OnEncryptionKeyRefreshCompleteEvent( |
| const EventPacket& event) { |
| ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid()); |
| ZX_DEBUG_ASSERT(event.event_code() == kEncryptionKeyRefreshCompleteEventCode); |
| |
| if (event.view().payload_size() != sizeof(EncryptionKeyRefreshCompleteEventParams)) { |
| bt_log(WARN, "hci", "malformed encryption key refresh complete event"); |
| return CommandChannel::EventCallbackResult::kContinue; |
| } |
| |
| const auto& params = event.params<EncryptionKeyRefreshCompleteEventParams>(); |
| hci::ConnectionHandle handle = le16toh(params.connection_handle); |
| |
| // Silently ignore this event as it isn't meant for this connection. |
| if (handle != this->handle()) { |
| return CommandChannel::EventCallbackResult::kContinue; |
| } |
| |
| if (conn_state_ != Connection::State::kConnected) { |
| bt_log(DEBUG, "hci", "encryption key refresh ignored: connection closed"); |
| return CommandChannel::EventCallbackResult::kContinue; |
| } |
| |
| Status status(params.status); |
| |
| bt_log(DEBUG, "hci", "encryption key refresh %s", status.ToString().c_str()); |
| |
| // Report that encryption got disabled on failure status. The accuracy of this |
| // isn't that important since the link will be disconnected. |
| HandleEncryptionStatus(status, static_cast<bool>(status)); |
| |
| return CommandChannel::EventCallbackResult::kContinue; |
| } |
| |
| CommandChannel::EventCallbackResult ConnectionImpl::OnLELongTermKeyRequestEvent( |
| const EventPacket& event) { |
| ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid()); |
| ZX_DEBUG_ASSERT(event.event_code() == kLEMetaEventCode); |
| ZX_DEBUG_ASSERT(event.params<LEMetaEventParams>().subevent_code == |
| kLELongTermKeyRequestSubeventCode); |
| |
| auto* params = event.le_event_params<LELongTermKeyRequestSubeventParams>(); |
| if (!params) { |
| bt_log(WARN, "hci", "malformed LE LTK request event"); |
| return CommandChannel::EventCallbackResult::kContinue; |
| } |
| |
| hci::ConnectionHandle handle = le16toh(params->connection_handle); |
| |
| // Silently ignore the event as it isn't meant for this connection. |
| if (handle != this->handle()) { |
| return CommandChannel::EventCallbackResult::kContinue; |
| } |
| |
| // TODO(fxbug.dev/1360): Tell the data channel to stop data flow. |
| |
| std::unique_ptr<CommandPacket> cmd; |
| |
| uint64_t rand = le64toh(params->random_number); |
| uint16_t ediv = le16toh(params->encrypted_diversifier); |
| |
| bt_log(DEBUG, "hci", "LE LTK request - ediv: %#.4x, rand: %#.16lx", ediv, rand); |
| if (ltk() && ltk()->rand() == rand && ltk()->ediv() == ediv) { |
| cmd = CommandPacket::New(kLELongTermKeyRequestReply, |
| sizeof(LELongTermKeyRequestReplyCommandParams)); |
| auto* params = cmd->mutable_payload<LELongTermKeyRequestReplyCommandParams>(); |
| |
| params->connection_handle = htole16(handle); |
| params->long_term_key = ltk()->value(); |
| } else { |
| bt_log(DEBUG, "hci-le", "LTK request rejected"); |
| |
| cmd = CommandPacket::New(kLELongTermKeyRequestNegativeReply, |
| sizeof(LELongTermKeyRequestNegativeReplyCommandParams)); |
| auto* params = cmd->mutable_payload<LELongTermKeyRequestNegativeReplyCommandParams>(); |
| params->connection_handle = htole16(handle); |
| } |
| |
| auto status_cb = [](auto id, const EventPacket& event) { |
| hci_is_error(event, TRACE, "hci-le", "failed to reply to LTK request"); |
| }; |
| hci_->command_channel()->SendCommand(std::move(cmd), std::move(status_cb)); |
| return CommandChannel::EventCallbackResult::kContinue; |
| } |
| |
| } // namespace bt::hci |