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_bluetooth_sapphire/internal/host/transport/transport.h"
18
19 namespace bt::hci {
20
21 namespace {
22
23 template <CommandChannel::EventCallbackResult (
24 AclConnection::*EventHandlerMethod)(const EventPacket&)>
BindEventHandler(const WeakPtr<AclConnection> & conn)25 CommandChannel::EventCallback BindEventHandler(
26 const WeakPtr<AclConnection>& conn) {
27 return [conn](const EventPacket& event) {
28 if (conn.is_alive()) {
29 return (conn.get().*EventHandlerMethod)(event);
30 }
31 return CommandChannel::EventCallbackResult::kRemove;
32 };
33 }
34
35 } // namespace
36
AclConnection(hci_spec::ConnectionHandle handle,const DeviceAddress & local_address,const DeviceAddress & peer_address,pw::bluetooth::emboss::ConnectionRole role,const Transport::WeakPtr & hci)37 AclConnection::AclConnection(hci_spec::ConnectionHandle handle,
38 const DeviceAddress& local_address,
39 const DeviceAddress& peer_address,
40 pw::bluetooth::emboss::ConnectionRole role,
41 const Transport::WeakPtr& hci)
42 : Connection(handle,
43 local_address,
44 peer_address,
45 hci,
46 [handle, hci] {
47 AclConnection::OnDisconnectionComplete(handle, hci);
48 }),
49 role_(role),
50 weak_self_(this) {
51 auto self = weak_self_.GetWeakPtr();
52 enc_change_id_ = hci->command_channel()->AddEventHandler(
53 hci_spec::kEncryptionChangeEventCode,
54 BindEventHandler<&AclConnection::OnEncryptionChangeEvent>(self));
55 enc_key_refresh_cmpl_id_ = hci->command_channel()->AddEventHandler(
56 hci_spec::kEncryptionKeyRefreshCompleteEventCode,
57 BindEventHandler<&AclConnection::OnEncryptionKeyRefreshCompleteEvent>(
58 self));
59 }
60
~AclConnection()61 AclConnection::~AclConnection() {
62 // Unregister HCI event handlers.
63 hci()->command_channel()->RemoveEventHandler(enc_change_id_);
64 hci()->command_channel()->RemoveEventHandler(enc_key_refresh_cmpl_id_);
65 }
66
OnDisconnectionComplete(hci_spec::ConnectionHandle handle,const Transport::WeakPtr & hci)67 void AclConnection::OnDisconnectionComplete(hci_spec::ConnectionHandle handle,
68 const Transport::WeakPtr& hci) {
69 if (!hci.is_alive()) {
70 return;
71 }
72 // Notify ACL data channel that packets have been flushed from controller
73 // buffer.
74 hci->acl_data_channel()->ClearControllerPacketCount(handle);
75 }
76
OnEncryptionChangeEvent(const EventPacket & event)77 CommandChannel::EventCallbackResult AclConnection::OnEncryptionChangeEvent(
78 const EventPacket& event) {
79 PW_CHECK(event.event_code() == hci_spec::kEncryptionChangeEventCode);
80
81 auto params =
82 event
83 .unchecked_view<pw::bluetooth::emboss::EncryptionChangeEventV1View>();
84 if (!params.Ok()) {
85 bt_log(WARN, "hci", "malformed encryption change event");
86 return CommandChannel::EventCallbackResult::kContinue;
87 }
88
89 hci_spec::ConnectionHandle handle = params.connection_handle().Read();
90
91 // Silently ignore the event as it isn't meant for this connection.
92 if (handle != this->handle()) {
93 return CommandChannel::EventCallbackResult::kContinue;
94 }
95
96 if (state() != Connection::State::kConnected) {
97 bt_log(DEBUG, "hci", "encryption change ignored for closed connection");
98 return CommandChannel::EventCallbackResult::kContinue;
99 }
100
101 Result<> result = event.ToResult();
102 encryption_status_ = params.encryption_enabled().Read();
103 bool encryption_enabled =
104 encryption_status_ != pw::bluetooth::emboss::EncryptionStatus::OFF;
105
106 bt_log(DEBUG,
107 "hci",
108 "encryption change (%s) %s",
109 encryption_enabled ? "enabled" : "disabled",
110 bt_str(result));
111
112 // If peer and local Secure Connections support are present, the pairing logic
113 // needs to verify that the status received in the Encryption Changed event is
114 // for AES encryption.
115 if (use_secure_connections_ &&
116 encryption_status_ !=
117 pw::bluetooth::emboss::EncryptionStatus::ON_WITH_AES_FOR_BREDR) {
118 bt_log(DEBUG,
119 "hci",
120 "BR/EDR Secure Connection must use AES encryption. Closing "
121 "connection...");
122 HandleEncryptionStatus(fit::error(Error(HostError::kInsufficientSecurity)),
123 /*key_refreshed=*/false);
124 return CommandChannel::EventCallbackResult::kContinue;
125 }
126
127 HandleEncryptionStatus(result.is_ok()
128 ? Result<bool>(fit::ok(encryption_enabled))
129 : result.take_error(),
130 /*key_refreshed=*/false);
131 return CommandChannel::EventCallbackResult::kContinue;
132 }
133
134 CommandChannel::EventCallbackResult
OnEncryptionKeyRefreshCompleteEvent(const EventPacket & event)135 AclConnection::OnEncryptionKeyRefreshCompleteEvent(const EventPacket& event) {
136 const auto params =
137 event
138 .view<pw::bluetooth::emboss::EncryptionKeyRefreshCompleteEventView>();
139 const hci_spec::ConnectionHandle handle = params.connection_handle().Read();
140
141 // Silently ignore this event as it isn't meant for this connection.
142 if (handle != this->handle()) {
143 return CommandChannel::EventCallbackResult::kContinue;
144 }
145
146 if (state() != Connection::State::kConnected) {
147 bt_log(
148 DEBUG, "hci", "encryption key refresh ignored for closed connection");
149 return CommandChannel::EventCallbackResult::kContinue;
150 }
151
152 Result<> status = event.ToResult();
153 bt_log(DEBUG, "hci", "encryption key refresh %s", bt_str(status));
154
155 // Report that encryption got disabled on failure status. The accuracy of this
156 // isn't that important since the link will be disconnected.
157 HandleEncryptionStatus(status.is_ok()
158 ? Result<bool>(fit::ok(/*enabled=*/true))
159 : status.take_error(),
160 /*key_refreshed=*/true);
161
162 return CommandChannel::EventCallbackResult::kContinue;
163 }
164
165 } // namespace bt::hci
166