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/bredr_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
BrEdrConnection(hci_spec::ConnectionHandle handle,const DeviceAddress & local_address,const DeviceAddress & peer_address,pw::bluetooth::emboss::ConnectionRole role,const Transport::WeakPtr & hci)23 BrEdrConnection::BrEdrConnection(hci_spec::ConnectionHandle handle,
24 const DeviceAddress& local_address,
25 const DeviceAddress& peer_address,
26 pw::bluetooth::emboss::ConnectionRole role,
27 const Transport::WeakPtr& hci)
28 : AclConnection(handle, local_address, peer_address, role, hci),
29 WeakSelf(this) {
30 PW_CHECK(local_address.type() == DeviceAddress::Type::kBREDR);
31 PW_CHECK(peer_address.type() == DeviceAddress::Type::kBREDR);
32 PW_CHECK(hci.is_alive());
33 PW_CHECK(hci->acl_data_channel());
34 }
35
StartEncryption()36 bool BrEdrConnection::StartEncryption() {
37 if (state() != Connection::State::kConnected) {
38 bt_log(DEBUG, "hci", "connection closed; cannot start encryption");
39 return false;
40 }
41
42 PW_CHECK(ltk().has_value() == ltk_type_.has_value());
43 if (!ltk().has_value()) {
44 bt_log(
45 DEBUG,
46 "hci",
47 "connection link key type has not been set; not starting encryption");
48 return false;
49 }
50
51 auto cmd = CommandPacket::New<
52 pw::bluetooth::emboss::SetConnectionEncryptionCommandWriter>(
53 hci_spec::kSetConnectionEncryption);
54 auto params = cmd.view_t();
55 params.connection_handle().Write(handle());
56 params.encryption_enable().Write(
57 pw::bluetooth::emboss::GenericEnableParam::ENABLE);
58
59 auto self = GetWeakPtr();
60 auto event_cb = [self, handle = handle()](auto, const EventPacket& event) {
61 if (!self.is_alive()) {
62 return;
63 }
64
65 Result<> result = event.ToResult();
66 if (bt_is_error(result,
67 ERROR,
68 "hci-bredr",
69 "could not set encryption on link %#.04x",
70 handle)) {
71 if (self->encryption_change_callback()) {
72 self->encryption_change_callback()(result.take_error());
73 }
74 return;
75 }
76 bt_log(DEBUG, "hci-bredr", "requested encryption start on %#.04x", handle);
77 };
78
79 if (!hci().is_alive()) {
80 return false;
81 }
82 return hci()->command_channel()->SendCommand(
83 std::move(cmd), std::move(event_cb), hci_spec::kCommandStatusEventCode);
84 }
85
HandleEncryptionStatus(Result<bool> result,bool key_refreshed)86 void BrEdrConnection::HandleEncryptionStatus(Result<bool> result,
87 bool key_refreshed) {
88 bool enabled = result.is_ok() && result.value() && !key_refreshed;
89 if (enabled) {
90 ValidateEncryptionKeySize([self = GetWeakPtr()](Result<> key_valid_status) {
91 if (self.is_alive()) {
92 self->HandleEncryptionStatusValidated(
93 key_valid_status.is_ok() ? Result<bool>(fit::ok(true))
94 : key_valid_status.take_error());
95 }
96 });
97 return;
98 }
99 HandleEncryptionStatusValidated(result);
100 }
101
HandleEncryptionStatusValidated(Result<bool> result)102 void BrEdrConnection::HandleEncryptionStatusValidated(Result<bool> result) {
103 // Core Spec Vol 3, Part C, 5.2.2.1.1 and 5.2.2.2.1 mention disconnecting the
104 // link after pairing failures (supported by TS GAP/SEC/SEM/BV-10-C), but do
105 // not specify actions to take after encryption failures. We'll choose to
106 // disconnect ACL links after encryption failure.
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
ValidateEncryptionKeySize(hci::ResultFunction<> key_size_validity_cb)121 void BrEdrConnection::ValidateEncryptionKeySize(
122 hci::ResultFunction<> key_size_validity_cb) {
123 PW_CHECK(state() == Connection::State::kConnected);
124
125 auto cmd = CommandPacket::New<
126 pw::bluetooth::emboss::ReadEncryptionKeySizeCommandWriter>(
127 hci_spec::kReadEncryptionKeySize);
128 cmd.view_t().connection_handle().Write(handle());
129
130 auto event_cb = [self = GetWeakPtr(),
131 valid_cb = std::move(key_size_validity_cb)](
132 auto, const EventPacket& event) {
133 if (!self.is_alive()) {
134 return;
135 }
136
137 Result<> result = event.ToResult();
138 if (!bt_is_error(result,
139 ERROR,
140 "hci",
141 "Could not read ACL encryption key size on %#.4x",
142 self->handle())) {
143 const auto return_params =
144 event.view<pw::bluetooth::emboss::
145 ReadEncryptionKeySizeCommandCompleteEventView>();
146 uint8_t key_size = return_params.key_size().Read();
147 bt_log(TRACE,
148 "hci",
149 "%#.4x: encryption key size %hhu",
150 self->handle(),
151 key_size);
152
153 if (key_size < hci_spec::kMinEncryptionKeySize) {
154 bt_log(WARN,
155 "hci",
156 "%#.4x: encryption key size %hhu insufficient",
157 self->handle(),
158 key_size);
159 result = ToResult(HostError::kInsufficientSecurity);
160 }
161 }
162 valid_cb(result);
163 };
164 hci()->command_channel()->SendCommand(std::move(cmd), std::move(event_cb));
165 }
166
167 } // namespace bt::hci
168