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 #pragma once
16 #include <fuzzer/FuzzedDataProvider.h>
17
18 #include <functional>
19
20 #include "pw_bluetooth_sapphire/internal/host/gap/peer.h"
21
22 namespace bt {
23 namespace testing {
24
MakePublicDeviceAddress(FuzzedDataProvider & fdp)25 inline DeviceAddress MakePublicDeviceAddress(FuzzedDataProvider& fdp) {
26 DeviceAddressBytes device_address_bytes;
27 fdp.ConsumeData(&device_address_bytes, sizeof(device_address_bytes));
28 return DeviceAddress(fdp.PickValueInArray({DeviceAddress::Type::kBREDR,
29 DeviceAddress::Type::kLEPublic}),
30 device_address_bytes);
31 }
32
33 } // namespace testing
34
35 namespace gap::testing {
36
37 class PeerFuzzer final {
38 public:
39 // Core Spec v5.2, Vol 6, Part B, Section 2.3.4.9
40 static constexpr size_t kMaxLeAdvDataLength = 1650;
41
42 // Create a PeerFuzzer that mutates |peer| using |fuzzed_data_provider|. Both
43 // arguments must outlive this object.
PeerFuzzer(FuzzedDataProvider & fuzzed_data_provider,Peer & peer)44 PeerFuzzer(FuzzedDataProvider& fuzzed_data_provider, Peer& peer)
45 : fuzzed_data_provider_(fuzzed_data_provider), peer_(peer) {}
46
47 // Use the FuzzedDataProvider with which this object was constructed to choose
48 // a member function that is then used to mutate the corresponding Peer field.
FuzzOneField()49 void FuzzOneField() {
50 // The decltype is easier to read than void (&PeerFuzzer::*)(), but this
51 // function isn't special and should not pick itself
52 using FuzzMemberFunction = decltype(&PeerFuzzer::FuzzOneField);
53 constexpr FuzzMemberFunction kFuzzFunctions[] = {
54 &PeerFuzzer::LEDataSetAdvertisingData,
55 &PeerFuzzer::LEDataRegisterInitializingConnection,
56 &PeerFuzzer::LEDataRegisterConnection,
57 &PeerFuzzer::LEDataSetConnectionParameters,
58 &PeerFuzzer::LEDataSetPreferredConnectionParameters,
59 &PeerFuzzer::LEDataSetBondData,
60 &PeerFuzzer::LEDataClearBondData,
61 &PeerFuzzer::LEDataSetFeatures,
62 &PeerFuzzer::LEDataSetServiceChangedGattData,
63 &PeerFuzzer::LEDataSetAutoConnectBehavior,
64 &PeerFuzzer::BrEdrDataSetInquiryData,
65 &PeerFuzzer::BrEdrDataSetInquiryDataWithRssi,
66 &PeerFuzzer::BrEdrDataSetInquiryDataFromExtendedInquiryResult,
67 &PeerFuzzer::BrEdrDataRegisterInitializingConnection,
68 &PeerFuzzer::BrEdrDataUnregisterInitializingConnection,
69 &PeerFuzzer::BrEdrDataRegisterConnection,
70 &PeerFuzzer::BrEdrUnregisterConnection,
71 &PeerFuzzer::BrEdrDataSetBondData,
72 &PeerFuzzer::BrEdrDataClearBondData,
73 &PeerFuzzer::BrEdrDataAddService,
74 &PeerFuzzer::RegisterName,
75 &PeerFuzzer::SetFeaturePage,
76 &PeerFuzzer::set_last_page_number,
77 &PeerFuzzer::set_version,
78 &PeerFuzzer::set_identity_known,
79 &PeerFuzzer::StoreBrEdrCrossTransportKey,
80 &PeerFuzzer::set_connectable,
81 };
82 std::invoke(fdp().PickValueInArray(kFuzzFunctions), this);
83 }
84
LEDataSetAdvertisingData()85 void LEDataSetAdvertisingData() {
86 peer_.MutLe().SetAdvertisingData(
87 fdp().ConsumeIntegral<uint8_t>(),
88 DynamicByteBuffer(
89 BufferView(fdp().ConsumeBytes<uint8_t>(kMaxLeAdvDataLength))),
90 pw::chrono::SystemClock::time_point());
91 }
92
LEDataRegisterInitializingConnection()93 void LEDataRegisterInitializingConnection() {
94 if (peer_.connectable() && fdp().ConsumeBool()) {
95 le_init_conn_tokens_.emplace_back(
96 peer_.MutLe().RegisterInitializingConnection());
97 } else if (!le_init_conn_tokens_.empty()) {
98 le_init_conn_tokens_.pop_back();
99 }
100 }
101
LEDataRegisterConnection()102 void LEDataRegisterConnection() {
103 if (peer_.connectable() && fdp().ConsumeBool()) {
104 le_conn_tokens_.emplace_back(peer_.MutLe().RegisterConnection());
105 } else if (!le_conn_tokens_.empty()) {
106 le_conn_tokens_.pop_back();
107 }
108 }
109
LEDataSetConnectionParameters()110 void LEDataSetConnectionParameters() {
111 if (!peer_.connectable()) {
112 return;
113 }
114 const hci_spec::LEConnectionParameters conn_params(
115 fdp().ConsumeIntegral<uint16_t>(),
116 fdp().ConsumeIntegral<uint16_t>(),
117 fdp().ConsumeIntegral<uint16_t>());
118 peer_.MutLe().SetConnectionParameters(conn_params);
119 }
120
LEDataSetPreferredConnectionParameters()121 void LEDataSetPreferredConnectionParameters() {
122 if (!peer_.connectable()) {
123 return;
124 }
125 const hci_spec::LEPreferredConnectionParameters conn_params(
126 fdp().ConsumeIntegral<uint16_t>(),
127 fdp().ConsumeIntegral<uint16_t>(),
128 fdp().ConsumeIntegral<uint16_t>(),
129 fdp().ConsumeIntegral<uint16_t>());
130 peer_.MutLe().SetPreferredConnectionParameters(conn_params);
131 }
132
LEDataSetBondData()133 void LEDataSetBondData() {
134 if (!peer_.connectable()) {
135 return;
136 }
137 sm::PairingData data;
138 auto do_if_fdp = [&](auto action) {
139 if (fdp().ConsumeBool()) {
140 action();
141 }
142 };
143 do_if_fdp([&] {
144 data.identity_address = bt::testing::MakePublicDeviceAddress(fdp());
145 });
146 do_if_fdp([&] { data.local_ltk = MakeLtk(); });
147 do_if_fdp([&] { data.peer_ltk = MakeLtk(); });
148 do_if_fdp([&] { data.cross_transport_key = MakeLtk(); });
149 do_if_fdp([&] { data.irk = MakeKey(); });
150 do_if_fdp([&] { data.csrk = MakeKey(); });
151 peer_.MutLe().SetBondData(data);
152 }
153
LEDataClearBondData()154 void LEDataClearBondData() {
155 if (!peer_.le() || !peer_.le()->bonded()) {
156 return;
157 }
158 peer_.MutLe().ClearBondData();
159 }
160
LEDataSetFeatures()161 void LEDataSetFeatures() {
162 hci_spec::LESupportedFeatures features = {};
163 fdp().ConsumeData(&features, sizeof(features));
164 peer_.MutLe().SetFeatures(features);
165 }
166
LEDataSetServiceChangedGattData()167 void LEDataSetServiceChangedGattData() {
168 peer_.MutLe().set_service_changed_gatt_data(
169 {fdp().ConsumeBool(), fdp().ConsumeBool()});
170 }
171
LEDataSetAutoConnectBehavior()172 void LEDataSetAutoConnectBehavior() {
173 peer_.MutLe().set_auto_connect_behavior(fdp().PickValueInArray(
174 {Peer::AutoConnectBehavior::kAlways,
175 Peer::AutoConnectBehavior::kSkipUntilNextConnection}));
176 }
177
BrEdrDataSetInquiryData()178 void BrEdrDataSetInquiryData() {
179 if (!peer_.identity_known()) {
180 return;
181 }
182 StaticPacket<pw::bluetooth::emboss::InquiryResultWriter> inquiry_data;
183 fdp().ConsumeData(inquiry_data.mutable_data().mutable_data(),
184 inquiry_data.data().size());
185 inquiry_data.view().bd_addr().CopyFrom(peer_.address().value().view());
186 peer_.MutBrEdr().SetInquiryData(inquiry_data.view());
187 }
188
BrEdrDataSetInquiryDataWithRssi()189 void BrEdrDataSetInquiryDataWithRssi() {
190 StaticPacket<pw::bluetooth::emboss::InquiryResultWithRssiWriter>
191 inquiry_data;
192 fdp().ConsumeData(inquiry_data.mutable_data().mutable_data(),
193 inquiry_data.data().size());
194 inquiry_data.view().bd_addr().CopyFrom(peer_.address().value().view());
195 peer_.MutBrEdr().SetInquiryData(inquiry_data.view());
196 }
197
BrEdrDataSetInquiryDataFromExtendedInquiryResult()198 void BrEdrDataSetInquiryDataFromExtendedInquiryResult() {
199 if (!peer_.identity_known()) {
200 return;
201 }
202 StaticPacket<pw::bluetooth::emboss::ExtendedInquiryResultEventWriter>
203 inquiry_data;
204 fdp().ConsumeData(inquiry_data.mutable_data().mutable_data(),
205 inquiry_data.data().size());
206 inquiry_data.view().bd_addr().CopyFrom(peer_.address().value().view());
207 peer_.MutBrEdr().SetInquiryData(inquiry_data.view());
208 }
209
BrEdrDataRegisterInitializingConnection()210 void BrEdrDataRegisterInitializingConnection() {
211 if (!peer_.identity_known() || !peer_.connectable() ||
212 bredr_conn_token_.has_value()) {
213 return;
214 }
215 bredr_init_conn_tokens_.emplace_back(
216 peer_.MutBrEdr().RegisterInitializingConnection());
217 }
218
BrEdrDataUnregisterInitializingConnection()219 void BrEdrDataUnregisterInitializingConnection() {
220 if (!bredr_init_conn_tokens_.empty()) {
221 bredr_init_conn_tokens_.pop_back();
222 }
223 }
224
BrEdrDataRegisterConnection()225 void BrEdrDataRegisterConnection() {
226 if (!peer_.identity_known() || !peer_.connectable()) {
227 return;
228 }
229
230 // Only 1 BR/EDR connection may be registered at a time.
231 bredr_conn_token_.reset();
232 bredr_conn_token_ = peer_.MutBrEdr().RegisterConnection();
233 }
234
BrEdrUnregisterConnection()235 void BrEdrUnregisterConnection() { bredr_conn_token_.reset(); }
236
BrEdrDataSetBondData()237 void BrEdrDataSetBondData() {
238 if (!peer_.identity_known() || !peer_.connectable()) {
239 return;
240 }
241 peer_.MutBrEdr().SetBondData(MakeLtk());
242 }
243
BrEdrDataClearBondData()244 void BrEdrDataClearBondData() {
245 if (!peer_.bredr() || !peer_.bredr()->bonded()) {
246 return;
247 }
248 peer_.MutBrEdr().ClearBondData();
249 }
250
BrEdrDataAddService()251 void BrEdrDataAddService() {
252 if (!peer_.identity_known() || !peer_.connectable()) {
253 return;
254 }
255 UUID uuid;
256 fdp().ConsumeData(&uuid, sizeof(uuid));
257 peer_.MutBrEdr().AddService(uuid);
258 }
259
RegisterName()260 void RegisterName() { peer_.RegisterName(fdp().ConsumeRandomLengthString()); }
261
SetFeaturePage()262 void SetFeaturePage() {
263 peer_.SetFeaturePage(
264 fdp().ConsumeIntegralInRange<size_t>(
265 0, bt::hci_spec::LMPFeatureSet::kMaxLastPageNumber),
266 fdp().ConsumeIntegral<uint64_t>());
267 }
268
set_last_page_number()269 void set_last_page_number() {
270 peer_.set_last_page_number(fdp().ConsumeIntegral<uint8_t>());
271 }
272
set_version()273 void set_version() {
274 peer_.set_version(
275 pw::bluetooth::emboss::CoreSpecificationVersion{
276 fdp().ConsumeIntegral<uint8_t>()},
277 fdp().ConsumeIntegral<uint16_t>(),
278 fdp().ConsumeIntegral<uint16_t>());
279 }
280
set_identity_known()281 void set_identity_known() { peer_.set_identity_known(fdp().ConsumeBool()); }
282
StoreBrEdrCrossTransportKey()283 void StoreBrEdrCrossTransportKey() {
284 if (!peer_.identity_known()) {
285 return;
286 }
287 if (!peer_.connectable()) {
288 return;
289 }
290 peer_.StoreBrEdrCrossTransportKey(MakeLtk());
291 }
292
set_connectable()293 void set_connectable() {
294 // It doesn't make sense to make a peer unconnectable and it fires lots of
295 // asserts.
296 peer_.set_connectable(true);
297 }
298
299 private:
fdp()300 FuzzedDataProvider& fdp() { return fuzzed_data_provider_; }
301
MakeKey()302 sm::Key MakeKey() {
303 // Actual value of the key is not fuzzed.
304 return sm::Key(MakeSecurityProperties(), {});
305 }
306
MakeLtk()307 sm::LTK MakeLtk() {
308 // Actual value of the key is not fuzzed.
309 return sm::LTK(MakeSecurityProperties(), {});
310 }
311
MakeSecurityProperties()312 sm::SecurityProperties MakeSecurityProperties() {
313 sm::SecurityProperties security(
314 fdp().ConsumeBool(),
315 fdp().ConsumeBool(),
316 fdp().ConsumeBool(),
317 fdp().ConsumeIntegralInRange<size_t>(0, sm::kMaxEncryptionKeySize));
318 return security;
319 }
320
321 FuzzedDataProvider& fuzzed_data_provider_;
322 Peer& peer_;
323 std::vector<Peer::ConnectionToken> le_conn_tokens_;
324 std::vector<Peer::InitializingConnectionToken> le_init_conn_tokens_;
325 std::optional<Peer::ConnectionToken> bredr_conn_token_;
326 std::vector<Peer::InitializingConnectionToken> bredr_init_conn_tokens_;
327 };
328
329 } // namespace gap::testing
330 } // namespace bt
331