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