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