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