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/low_energy_connection.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/transport/transport.h"
18
19 #pragma clang diagnostic ignored "-Wshadow"
20
21 namespace bt::hci {
22
LowEnergyConnection(hci_spec::ConnectionHandle handle,const DeviceAddress & local_address,const DeviceAddress & peer_address,const hci_spec::LEConnectionParameters & params,pw::bluetooth::emboss::ConnectionRole role,const Transport::WeakPtr & hci)23 LowEnergyConnection::LowEnergyConnection(
24 hci_spec::ConnectionHandle handle,
25 const DeviceAddress& local_address,
26 const DeviceAddress& peer_address,
27 const hci_spec::LEConnectionParameters& params,
28 pw::bluetooth::emboss::ConnectionRole role,
29 const Transport::WeakPtr& hci)
30 : AclConnection(handle, local_address, peer_address, role, hci),
31 WeakSelf(this),
32 parameters_(params) {
33 BT_ASSERT(local_address.type() != DeviceAddress::Type::kBREDR);
34 BT_ASSERT(peer_address.type() != DeviceAddress::Type::kBREDR);
35 BT_ASSERT(hci.is_alive());
36
37 le_ltk_request_id_ = hci->command_channel()->AddLEMetaEventHandler(
38 hci_spec::kLELongTermKeyRequestSubeventCode,
39 fit::bind_member<&LowEnergyConnection::OnLELongTermKeyRequestEvent>(
40 this));
41 }
42
~LowEnergyConnection()43 LowEnergyConnection::~LowEnergyConnection() {
44 // Unregister HCI event handlers.
45 if (hci().is_alive()) {
46 hci()->command_channel()->RemoveEventHandler(le_ltk_request_id_);
47 }
48 }
49
StartEncryption()50 bool LowEnergyConnection::StartEncryption() {
51 if (state() != Connection::State::kConnected) {
52 bt_log(DEBUG, "hci", "connection closed; cannot start encryption");
53 return false;
54 }
55 if (role() != pw::bluetooth::emboss::ConnectionRole::CENTRAL) {
56 bt_log(DEBUG, "hci", "only the central can start encryption");
57 return false;
58 }
59 if (!ltk().has_value()) {
60 bt_log(DEBUG, "hci", "connection has no LTK; cannot start encryption");
61 return false;
62 }
63
64 auto cmd = EmbossCommandPacket::New<
65 pw::bluetooth::emboss::LEEnableEncryptionCommandWriter>(
66 hci_spec::kLEStartEncryption);
67 auto params = cmd.view_t();
68 params.connection_handle().Write(handle());
69 params.random_number().Write(ltk()->rand());
70 params.encrypted_diversifier().Write(ltk()->ediv());
71 params.long_term_key().CopyFrom(
72 pw::bluetooth::emboss::LinkKeyView(<k()->value()));
73
74 auto event_cb = [self = GetWeakPtr(), handle = handle()](
75 auto id, const EventPacket& event) {
76 if (!self.is_alive()) {
77 return;
78 }
79
80 Result<> result = event.ToResult();
81 if (bt_is_error(result,
82 ERROR,
83 "hci-le",
84 "could not set encryption on link %#.04x",
85 handle)) {
86 if (self->encryption_change_callback()) {
87 self->encryption_change_callback()(result.take_error());
88 }
89 return;
90 }
91 bt_log(DEBUG, "hci-le", "requested encryption start on %#.04x", handle);
92 };
93 if (!hci().is_alive()) {
94 return false;
95 }
96 return hci()->command_channel()->SendCommand(
97 std::move(cmd), std::move(event_cb), hci_spec::kCommandStatusEventCode);
98 }
99
HandleEncryptionStatus(Result<bool> result,bool)100 void LowEnergyConnection::HandleEncryptionStatus(Result<bool> result,
101 bool /*key_refreshed*/) {
102 // "On an authentication failure, the connection shall be automatically
103 // disconnected by the Link Layer." (HCI_LE_Start_Encryption, Vol 2, Part E,
104 // 7.8.24). We make sure of this by telling the controller to disconnect.
105 if (result.is_error()) {
106 Disconnect(pw::bluetooth::emboss::StatusCode::AUTHENTICATION_FAILURE);
107 }
108
109 if (!encryption_change_callback()) {
110 bt_log(DEBUG,
111 "hci",
112 "%#.4x: no encryption status callback assigned",
113 handle());
114 return;
115 }
116 encryption_change_callback()(result);
117 }
118
119 CommandChannel::EventCallbackResult
OnLELongTermKeyRequestEvent(const EventPacket & event)120 LowEnergyConnection::OnLELongTermKeyRequestEvent(const EventPacket& event) {
121 BT_ASSERT(event.event_code() == hci_spec::kLEMetaEventCode);
122 BT_ASSERT(event.params<hci_spec::LEMetaEventParams>().subevent_code ==
123 hci_spec::kLELongTermKeyRequestSubeventCode);
124
125 auto* params =
126 event.subevent_params<hci_spec::LELongTermKeyRequestSubeventParams>();
127 if (!params) {
128 bt_log(WARN, "hci", "malformed LE LTK request event");
129 return CommandChannel::EventCallbackResult::kContinue;
130 }
131
132 hci_spec::ConnectionHandle handle = le16toh(params->connection_handle);
133
134 // Silently ignore the event as it isn't meant for this connection.
135 if (handle != this->handle()) {
136 return CommandChannel::EventCallbackResult::kContinue;
137 }
138
139 CommandChannel::CommandPacketVariant cmd;
140
141 uint64_t rand = le64toh(params->random_number);
142 uint16_t ediv = le16toh(params->encrypted_diversifier);
143
144 bt_log(
145 DEBUG, "hci", "LE LTK request - ediv: %#.4x, rand: %#.16lx", ediv, rand);
146 if (ltk() && ltk()->rand() == rand && ltk()->ediv() == ediv) {
147 cmd = CommandPacket::New(
148 hci_spec::kLELongTermKeyRequestReply,
149 sizeof(hci_spec::LELongTermKeyRequestReplyCommandParams));
150 auto* params = std::get<std::unique_ptr<CommandPacket>>(cmd)
151 ->mutable_payload<
152 hci_spec::LELongTermKeyRequestReplyCommandParams>();
153
154 params->connection_handle = htole16(handle);
155 params->long_term_key = ltk()->value();
156 } else {
157 bt_log(DEBUG, "hci-le", "LTK request rejected");
158
159 cmd = EmbossCommandPacket::New<
160 pw::bluetooth::emboss::LELongTermKeyRequestNegativeReplyCommandWriter>(
161 hci_spec::kLELongTermKeyRequestNegativeReply);
162 auto view = std::get<EmbossCommandPacket>(cmd)
163 .view<pw::bluetooth::emboss::
164 LELongTermKeyRequestNegativeReplyCommandWriter>();
165 view.connection_handle().Write(handle);
166 }
167
168 auto status_cb = [](auto id, const EventPacket& event) {
169 hci_is_error(event, TRACE, "hci-le", "failed to reply to LTK request");
170 };
171 if (!hci().is_alive()) {
172 return CommandChannel::EventCallbackResult::kRemove;
173 }
174 hci()->command_channel()->SendCommand(std::move(cmd), std::move(status_cb));
175 return CommandChannel::EventCallbackResult::kContinue;
176 }
177
178 } // namespace bt::hci
179