• 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_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(&ltk()->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