1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://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,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <array>
16 #include <cstdint>
17 #include <random>
18 #include <span>
19 #include <utility>
20 #include <vector>
21
22 #include "fuzztest/domain_core.h"
23 #include "fuzztest/fuzztest.h"
24 #include "gtest/gtest.h"
25 #include "nearby_protocol.h"
26 #include "np_cpp_ffi_types.h"
27 #include "shared_test_util.h"
28
29 // redefine test data as std::vector types since fuzztest does not support
30 // template class use in its input domains (ie: std::array<T, N>), and we want
31 // to use our test data to seed the fuzzer
32 static std::vector<uint8_t> V0AdvEmptyVec(V0AdvEmptyBytes.begin(),
33 V0AdvEmptyBytes.end());
34 static std::vector<uint8_t> V1AdvEmptyVec(V1AdvEmptyBytes.begin(),
35 V1AdvEmptyBytes.end());
36 static std::vector<uint8_t> V0AdvPlaintextVec(V0AdvPlaintextBytes.begin(),
37 V0AdvPlaintextBytes.end());
38 static std::vector<uint8_t> V0AdvPlaintextMultiDeVec(
39 V0AdvPlaintextMultiDeBytes.begin(), V0AdvPlaintextMultiDeBytes.end());
40 static std::vector<uint8_t> V1AdvPlaintextVec(V1AdvPlaintextBytes.begin(),
41 V1AdvPlaintextBytes.end());
42 static std::vector<uint8_t> V0AdvEncryptedVec(V0AdvEncryptedBytes.begin(),
43 V0AdvEncryptedBytes.end());
44 static std::vector<uint8_t> V1AdvEncryptedVec(V1AdvEncryptedBytes.begin(),
45 V1AdvEncryptedBytes.end());
46
47 void HandleAdvertisementResult(nearby_protocol::DeserializeAdvertisementResult);
48
PlaintextDeserializer(std::span<const uint8_t> adv_bytes)49 void PlaintextDeserializer(std::span<const uint8_t> adv_bytes) {
50 nearby_protocol::CredentialSlab slab;
51 nearby_protocol::CredentialBook book(slab);
52 auto buffer = nearby_protocol::ByteBuffer<255>::TryFromSpan(adv_bytes);
53 EXPECT_TRUE(buffer.ok());
54
55 nearby_protocol::RawAdvertisementPayload payload(
56 (nearby_protocol::ByteBuffer<255>(*buffer)));
57 auto deserialize_result =
58 nearby_protocol::Deserializer::DeserializeAdvertisement(payload, book);
59
60 // Since we are seeding with valid data, we can add extra calls into the
61 // result processing APIs to ensure none of the internal asserts are
62 // triggered.
63 HandleAdvertisementResult(std::move(deserialize_result));
64 }
65
66 FUZZ_TEST(NpCppDeserializationFuzzers, PlaintextDeserializer)
67 .WithDomains(fuzztest::Arbitrary<std::vector<uint8_t>>()
68 .WithMinSize(0)
69 .WithMaxSize(255))
70 .WithSeeds({V0AdvEmptyVec, V1AdvEmptyVec, V0AdvPlaintextVec,
71 V0AdvPlaintextMultiDeVec, V1AdvPlaintextVec, V0AdvEncryptedVec,
72 V1AdvEncryptedVec});
73
74 // The data which is automatically generated by the fuzzer
75 struct IdentityData {
76 uint32_t credential_id;
77 std::array<uint8_t, 32> key_seed;
78 std::array<uint8_t, 32> legacy_metadata_key_hmac;
79 std::array<uint8_t, 32> expected_unsigned_identity_token_hmac;
80 std::array<uint8_t, 32> expected_signed_identity_token_hmac;
81 std::array<uint8_t, 32> pub_key;
82 std::vector<uint8_t> encrypted_metadata_bytes;
83 };
84
85 static struct IdentityData V0TestCaseIdentityData {
86 .credential_id = static_cast<uint32_t>(rand()), .key_seed = V0AdvKeySeed,
87 .legacy_metadata_key_hmac = V0AdvLegacyIdentityTokenHmac,
88 .encrypted_metadata_bytes = V0AdvEncryptedMetadata
89 };
90
91 static struct IdentityData V1TestCaseIdentityData {
92 .credential_id = static_cast<uint32_t>(rand()), .key_seed = V1AdvKeySeed,
93 .expected_unsigned_identity_token_hmac =
94 V1AdvExpectedMicExtendedSaltIdentityTokenHmac,
95 .expected_signed_identity_token_hmac =
96 V1AdvExpectedSignatureIdentityTokenHmac,
97 .pub_key = V1AdvPublicKey, .encrypted_metadata_bytes = V1AdvEncryptedMetadata,
98 };
99
100 // Now lets try feeding the fuzzer some credential data that can successfully
101 // decrypt advertisements to improve its efficiency, and provide extra coverage
102 // on on credential/iteration code paths.
103 // TODO: Add more interesting credential seed data once we have C++
104 // serialization APIs, ie multiple sections with multiple valid credentials
105 // and combinations of matching and undecryptable sections etc.
DeserializeWithCredentials(std::span<const IdentityData> identities,std::span<const uint8_t> adv_bytes)106 void DeserializeWithCredentials(std::span<const IdentityData> identities,
107 std::span<const uint8_t> adv_bytes) {
108 nearby_protocol::CredentialSlab slab;
109 // populate book with fuzzer generated credential data
110 for (auto data : identities) {
111 nearby_protocol::MatchedCredentialData match_data(
112 123, data.encrypted_metadata_bytes);
113 nearby_protocol::V0MatchableCredential v0_cred(
114 data.key_seed, data.legacy_metadata_key_hmac, match_data);
115 // adding v0 credentials is infallible
116 slab.AddV0Credential(v0_cred);
117
118 nearby_protocol::V1MatchableCredential v1_cred(
119 data.key_seed, data.expected_unsigned_identity_token_hmac,
120 data.expected_signed_identity_token_hmac, data.pub_key, match_data);
121 [[maybe_unused]] auto result = slab.AddV1Credential(v1_cred);
122 }
123
124 nearby_protocol::CredentialBook book(slab);
125 auto buffer = nearby_protocol::ByteBuffer<255>::TryFromSpan(adv_bytes);
126 EXPECT_TRUE(buffer.ok());
127
128 nearby_protocol::RawAdvertisementPayload payload(
129 (nearby_protocol::ByteBuffer<255>(*buffer)));
130 auto deserialize_result =
131 nearby_protocol::Deserializer::DeserializeAdvertisement(payload, book);
132
133 // Since we are seeding with valid data, we can add extra calls into the
134 // result processing APIs to ensure none of the internal asserts are
135 // triggered.
136 HandleAdvertisementResult(std::move(deserialize_result));
137 }
138
139 std::vector<std::tuple<std::vector<IdentityData>, std::vector<uint8_t>>>
DeserializeWithCredentialSeedData()140 DeserializeWithCredentialSeedData() {
141 return {
142 {{{V0TestCaseIdentityData, V1TestCaseIdentityData}, V0AdvEncryptedVec},
143 {{V0TestCaseIdentityData, V1TestCaseIdentityData}, V1AdvEncryptedVec}},
144 };
145 }
146
TEST(NpCppDeserializationFuzzers,InvalidPublicKeyInCredential)147 TEST(NpCppDeserializationFuzzers, InvalidPublicKeyInCredential) {
148 std::vector<IdentityData> identities;
149 identities.push_back(
150 {1804289383,
151 {17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
152 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17},
153 {136, 51, 222, 213, 77, 0, 146, 232, 128, 112, 213,
154 31, 24, 236, 34, 69, 117, 124, 36, 223, 227, 140,
155 178, 222, 119, 182, 120, 133, 252, 165, 103, 77},
156 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
157 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
158 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
159 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
160 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
161 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
162 fuzztest::ToByteArray("")});
163 identities.push_back({846930886,
164 {49, 67, 99, 30, 202, 232, 151, 75, 150, 80, 204,
165 28, 72, 37, 14, 129, 88, 6, 129, 81, 249, 235,
166 37, 35, 3, 212, 151, 109, 149, 25, 145, 57},
167 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
168 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
169 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
171 {28, 188, 235, 220, 23, 181, 145, 229, 7, 157, 112,
172 193, 232, 75, 204, 219, 75, 15, 118, 131, 89, 98,
173 10, 45, 85, 11, 59, 54, 164, 146, 139, 19},
174 {109, 13, 182, 9, 16, 177, 83, 196, 126, 16, 22,
175 20, 156, 159, 242, 20, 15, 236, 83, 118, 227, 7,
176 217, 211, 158, 174, 231, 69, 44, 3, 236, 109},
177 fuzztest::ToByteArray("")});
178
179 DeserializeWithCredentials(
180 identities,
181 fuzztest::ToByteArray(
182 " g\221\020\010\255iF\004]"
183 "\256m\267\367\\\323\270\254\360\277u\220\001\276s3\244v\204J\t"
184 "\017+"
185 "\231G\337\213F\312\026\316\023\265nS\256("
186 "VD\016\246\215\353\241\021\257N\033\340\216\365\272\220O."
187 "\224\374\336\246\177]"
188 "\3107\265\357\312\254\213\237\033\324\306\021\205\323g92\321\202"
189 "\312"
190 "N\271F\003\203hV\013\314\375z*\276\007"));
191 }
192
TEST(NpCppDeserializationFuzzers,PlaintextDeserializerRegression)193 TEST(NpCppDeserializationFuzzers, PlaintextDeserializerRegression) {
194 PlaintextDeserializer(fuzztest::ToByteArray(
195 " 4\221\020\005\255iF\004]"
196 "\256\275m\267\367\\\270\254\360\277u\220\001G\337\213F\312\026\316\023"
197 "\265nS\256(VD\016\243\215\353\241\021\257N\020\340\216\365\272\220O."
198 "\224\374\336\246\177]"
199 "\3107\267\357\312\254\213\237\033\324\306\021\205\323g92\321\202\312N"
200 "\271D\003\203hV\013\314\375z*\276\007"));
201 }
202
203 FUZZ_TEST(NpCppDeserializationFuzzers, DeserializeWithCredentials)
204 .WithDomains(fuzztest::Arbitrary<std::vector<IdentityData>>()
205 .WithMinSize(0)
206 .WithMaxSize(1000),
207 fuzztest::Arbitrary<std::vector<uint8_t>>()
208 .WithMinSize(0)
209 .WithMaxSize(255))
210 .WithSeeds(DeserializeWithCredentialSeedData);
211
212 // Lets encourage the fuzzer to try with a lot of credentials and make sure
213 // nothing falls apart. By default the vec ranges are somewhere between 1-20
214 // even with setting a max of 1000, so setting a high minimum here to ensure the
215 // higher end of credential iteration is hit.
216 FUZZ_TEST(NpCppDeserializationFuzzersLotsOfCredentials,
217 DeserializeWithCredentials)
218 .WithDomains(
219 fuzztest::Arbitrary<std::vector<IdentityData>>().WithMinSize(10000),
220 fuzztest::Arbitrary<std::vector<uint8_t>>().WithMinSize(0).WithMaxSize(
221 255));
222
223 // Helpers to trigger result processing code paths.
224 void HandleV0Adv(nearby_protocol::DeserializedV0Advertisement);
225 void HandleLegibleV0Adv(nearby_protocol::LegibleDeserializedV0Advertisement);
226 void HandleV0IdentityKind(nearby_protocol::DeserializedV0IdentityKind);
227 void HandleDataElement(nearby_protocol::V0DataElement);
228
229 void HandleV1Adv(nearby_protocol::DeserializedV1Advertisement);
230 void HandleV1Section(nearby_protocol::DeserializedV1Section);
231 void HandleV1DataElement(nearby_protocol::V1DataElement);
232
HandleAdvertisementResult(nearby_protocol::DeserializeAdvertisementResult result)233 void HandleAdvertisementResult(
234 nearby_protocol::DeserializeAdvertisementResult result) {
235 switch (result.GetKind()) {
236 case nearby_protocol::DeserializeAdvertisementResultKind::Error:
237 break;
238 case nearby_protocol::DeserializeAdvertisementResultKind::V0:
239 HandleV0Adv(result.IntoV0());
240 break;
241 case nearby_protocol::DeserializeAdvertisementResultKind::V1:
242 HandleV1Adv(result.IntoV1());
243 break;
244 }
245 }
246
HandleV0Adv(nearby_protocol::DeserializedV0Advertisement result)247 void HandleV0Adv(nearby_protocol::DeserializedV0Advertisement result) {
248 switch (result.GetKind()) {
249 case nearby_protocol::DeserializedV0AdvertisementKind::Legible:
250 HandleLegibleV0Adv(result.IntoLegible());
251 break;
252 case nearby_protocol::DeserializedV0AdvertisementKind::
253 NoMatchingCredentials:
254 break;
255 }
256 }
257
HandleLegibleV0Adv(nearby_protocol::LegibleDeserializedV0Advertisement legible_adv)258 void HandleLegibleV0Adv(
259 nearby_protocol::LegibleDeserializedV0Advertisement legible_adv) {
260 auto num_des = legible_adv.GetNumberOfDataElements();
261 auto payload = legible_adv.IntoPayload();
262 for (int i = 0; i < num_des; i++) {
263 auto de_result = payload.TryGetDataElement(i);
264 if (!de_result.ok()) {
265 return;
266 }
267 HandleDataElement(de_result.value());
268 }
269 }
270
HandleDataElement(nearby_protocol::V0DataElement de)271 void HandleDataElement(nearby_protocol::V0DataElement de) {
272 switch (de.GetKind()) {
273 case nearby_protocol::V0DataElementKind::TxPower: {
274 [[maybe_unused]] auto tx_power = de.AsTxPower();
275 break;
276 }
277 case nearby_protocol::V0DataElementKind::Actions: {
278 [[maybe_unused]] auto actions = de.AsActions();
279 break;
280 }
281 }
282 }
283
HandleV1Adv(nearby_protocol::DeserializedV1Advertisement adv)284 void HandleV1Adv(nearby_protocol::DeserializedV1Advertisement adv) {
285 auto legible_sections = adv.GetNumLegibleSections();
286 [[maybe_unused]] auto encrypted_sections = adv.GetNumUndecryptableSections();
287 for (auto i = 0; i < legible_sections; i++) {
288 auto section_result = adv.TryGetSection(i);
289 if (!section_result.ok()) {
290 return;
291 }
292 HandleV1Section(section_result.value());
293 }
294 }
295
HandleV1Section(nearby_protocol::DeserializedV1Section section)296 void HandleV1Section(nearby_protocol::DeserializedV1Section section) {
297 auto num_des = section.NumberOfDataElements();
298 for (auto i = 0; i < num_des; i++) {
299 auto de_result = section.TryGetDataElement(i);
300 if (!de_result.ok()) {
301 return;
302 }
303 }
304 }
305