• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_bluetooth_sapphire/internal/host/hci/acl_connection.h"
16 
17 #include <pw_assert/check.h>
18 
19 #include "pw_bluetooth_sapphire/internal/host/transport/transport.h"
20 
21 namespace bt::hci {
22 
23 namespace {
24 
25 template <CommandChannel::EventCallbackResult (
26     AclConnection::*EventHandlerMethod)(const EventPacket&)>
BindEventHandler(const WeakPtr<AclConnection> & conn)27 CommandChannel::EventCallback BindEventHandler(
28     const WeakPtr<AclConnection>& conn) {
29   return [conn](const EventPacket& event) {
30     if (conn.is_alive()) {
31       return (conn.get().*EventHandlerMethod)(event);
32     }
33     return CommandChannel::EventCallbackResult::kRemove;
34   };
35 }
36 
37 }  // namespace
38 
AclConnection(hci_spec::ConnectionHandle handle,const DeviceAddress & local_address,const DeviceAddress & peer_address,pw::bluetooth::emboss::ConnectionRole role,const Transport::WeakPtr & hci)39 AclConnection::AclConnection(hci_spec::ConnectionHandle handle,
40                              const DeviceAddress& local_address,
41                              const DeviceAddress& peer_address,
42                              pw::bluetooth::emboss::ConnectionRole role,
43                              const Transport::WeakPtr& hci)
44     : Connection(handle,
45                  local_address,
46                  peer_address,
47                  hci,
48                  [handle, hci] {
49                    AclConnection::OnDisconnectionComplete(handle, hci);
50                  }),
51       role_(role),
52       weak_self_(this) {
53   auto self = weak_self_.GetWeakPtr();
54   enc_change_id_ = hci->command_channel()->AddEventHandler(
55       hci_spec::kEncryptionChangeEventCode,
56       BindEventHandler<&AclConnection::OnEncryptionChangeEvent>(self));
57   enc_key_refresh_cmpl_id_ = hci->command_channel()->AddEventHandler(
58       hci_spec::kEncryptionKeyRefreshCompleteEventCode,
59       BindEventHandler<&AclConnection::OnEncryptionKeyRefreshCompleteEvent>(
60           self));
61 }
62 
~AclConnection()63 AclConnection::~AclConnection() {
64   // Unregister HCI event handlers.
65   hci()->command_channel()->RemoveEventHandler(enc_change_id_);
66   hci()->command_channel()->RemoveEventHandler(enc_key_refresh_cmpl_id_);
67 }
68 
OnDisconnectionComplete(hci_spec::ConnectionHandle handle,const Transport::WeakPtr & hci)69 void AclConnection::OnDisconnectionComplete(hci_spec::ConnectionHandle handle,
70                                             const Transport::WeakPtr& hci) {
71   if (!hci.is_alive()) {
72     return;
73   }
74   // Notify ACL data channel that packets have been flushed from controller
75   // buffer.
76   hci->acl_data_channel()->ClearControllerPacketCount(handle);
77 }
78 
OnEncryptionChangeEvent(const EventPacket & event)79 CommandChannel::EventCallbackResult AclConnection::OnEncryptionChangeEvent(
80     const EventPacket& event) {
81   PW_CHECK(event.event_code() == hci_spec::kEncryptionChangeEventCode);
82 
83   auto params =
84       event
85           .unchecked_view<pw::bluetooth::emboss::EncryptionChangeEventV1View>();
86   if (!params.Ok()) {
87     bt_log(WARN, "hci", "malformed encryption change event");
88     return CommandChannel::EventCallbackResult::kContinue;
89   }
90 
91   hci_spec::ConnectionHandle handle = params.connection_handle().Read();
92 
93   // Silently ignore the event as it isn't meant for this connection.
94   if (handle != this->handle()) {
95     return CommandChannel::EventCallbackResult::kContinue;
96   }
97 
98   if (state() != Connection::State::kConnected) {
99     bt_log(DEBUG, "hci", "encryption change ignored for closed connection");
100     return CommandChannel::EventCallbackResult::kContinue;
101   }
102 
103   Result<> result = event.ToResult();
104   encryption_status_ = params.encryption_enabled().Read();
105   bool encryption_enabled =
106       encryption_status_ != pw::bluetooth::emboss::EncryptionStatus::OFF;
107 
108   bt_log(DEBUG,
109          "hci",
110          "encryption change (%s) %s",
111          encryption_enabled ? "enabled" : "disabled",
112          bt_str(result));
113 
114   // If peer and local Secure Connections support are present, the pairing logic
115   // needs to verify that the status received in the Encryption Changed event is
116   // for AES encryption.
117   if (use_secure_connections_ &&
118       encryption_status_ !=
119           pw::bluetooth::emboss::EncryptionStatus::ON_WITH_AES_FOR_BREDR) {
120     bt_log(DEBUG,
121            "hci",
122            "BR/EDR Secure Connection must use AES encryption. Closing "
123            "connection...");
124     HandleEncryptionStatus(fit::error(Error(HostError::kInsufficientSecurity)),
125                            /*key_refreshed=*/false);
126     return CommandChannel::EventCallbackResult::kContinue;
127   }
128 
129   HandleEncryptionStatus(result.is_ok()
130                              ? Result<bool>(fit::ok(encryption_enabled))
131                              : result.take_error(),
132                          /*key_refreshed=*/false);
133   return CommandChannel::EventCallbackResult::kContinue;
134 }
135 
136 CommandChannel::EventCallbackResult
OnEncryptionKeyRefreshCompleteEvent(const EventPacket & event)137 AclConnection::OnEncryptionKeyRefreshCompleteEvent(const EventPacket& event) {
138   const auto params =
139       event
140           .view<pw::bluetooth::emboss::EncryptionKeyRefreshCompleteEventView>();
141   const hci_spec::ConnectionHandle handle = params.connection_handle().Read();
142 
143   // Silently ignore this event as it isn't meant for this connection.
144   if (handle != this->handle()) {
145     return CommandChannel::EventCallbackResult::kContinue;
146   }
147 
148   if (state() != Connection::State::kConnected) {
149     bt_log(
150         DEBUG, "hci", "encryption key refresh ignored for closed connection");
151     return CommandChannel::EventCallbackResult::kContinue;
152   }
153 
154   Result<> status = event.ToResult();
155   bt_log(DEBUG, "hci", "encryption key refresh %s", bt_str(status));
156 
157   // Report that encryption got disabled on failure status. The accuracy of this
158   // isn't that important since the link will be disconnected.
159   HandleEncryptionStatus(status.is_ok()
160                              ? Result<bool>(fit::ok(/*enabled=*/true))
161                              : status.take_error(),
162                          /*key_refreshed=*/true);
163 
164   return CommandChannel::EventCallbackResult::kContinue;
165 }
166 
167 }  // namespace bt::hci
168