1 // Copyright 2023 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 //     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,
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 <bitset>
16 #include <iostream>
17 #include <string>
18 #include <utility>
19 
20 #include "absl/strings/escaping.h"
21 #include "nearby_protocol.h"
22 #include "np_cpp_ffi_types.h"
23 
24 void SamplePanicHandler(nearby_protocol::PanicReason reason);
25 
26 void HandleAdvertisementResult(
27     nearby_protocol::DeserializeAdvertisementResult /*result*/);
28 
29 void HandleV0Adv(nearby_protocol::DeserializedV0Advertisement /*result*/);
30 void HandleLegibleV0Adv(
31     nearby_protocol::LegibleDeserializedV0Advertisement /*legible_adv*/);
32 void HandleV0IdentityKind(
33     nearby_protocol::DeserializedV0IdentityKind /*identity*/);
34 void HandleDataElement(nearby_protocol::V0DataElement /*de*/);
35 
36 void HandleV1Adv(nearby_protocol::DeserializedV1Advertisement /*adv*/);
37 void HandleV1Section(const nearby_protocol::DeserializedV1Section& /*section*/);
38 void HandleV1DataElement(nearby_protocol::V1DataElement /*de*/);
39 
main()40 int main() {
41   auto result =
42       nearby_protocol::GlobalConfig::SetPanicHandler(SamplePanicHandler);
43   if (result) {
44     std::cout << "Successfully registered panic handler\n";
45   } else {
46     std::cout << "Failed register panic handler\n";
47     return -1;
48   }
49   nearby_protocol::GlobalConfig::SetNumShards(4);
50 
51   std::cout << "\n========= Example V0 Adv ==========\n";
52   std::cout << "Hex bytes: 00031503260046\n\n";
53 
54   nearby_protocol::CredentialSlab credential_slab;
55   nearby_protocol::CredentialBook credential_book(credential_slab);
56 
57   const auto* v0_byte_string =
58       "00"       // Adv Header
59       "03"       // Public DE header
60       "1503"     // Length 1 Tx Power DE with value 3
61       "260046";  // Length 2 Actions
62 
63   std::string v0_bytes;
64   if (!absl::HexStringToBytes(v0_byte_string, &v0_bytes)) {
65     return -1;
66   }
67   auto v0_buffer = nearby_protocol::ByteBuffer<255>::TryFromString(v0_bytes);
68   nearby_protocol::RawAdvertisementPayload const v0_payload(v0_buffer.value());
69 
70   // Try to deserialize a V0 payload
71   auto deserialize_v0_result =
72       nearby_protocol::Deserializer::DeserializeAdvertisement(v0_payload,
73                                                               credential_book);
74   HandleAdvertisementResult(std::move(deserialize_v0_result));
75 
76   std::cout << "\n========= Example V1 Adv ==========\n";
77   std::cout << "Hex bytes: 20040326004603031505\n\n";
78 
79   const auto* v1_byte_string =
80       "20"       // V1 Advertisement header
81       "04"       // Section Header
82       "03"       // Public Identity DE header
83       "260046";  // Length 2 Actions DE
84 
85   std::string v1_bytes;
86   if (!absl::HexStringToBytes(v1_byte_string, &v1_bytes)) {
87     return -1;
88   }
89   auto v1_buffer = nearby_protocol::ByteBuffer<255>::TryFromString(v1_bytes);
90   nearby_protocol::RawAdvertisementPayload const v1_payload(v1_buffer.value());
91 
92   // Try to deserialize a V1 payload
93   auto deserialize_v1_result =
94       nearby_protocol::Deserializer::DeserializeAdvertisement(v1_payload,
95                                                               credential_book);
96   HandleAdvertisementResult(std::move(deserialize_v1_result));
97 
98   std::cout << "\n========= User input sample ==========\n\n";
99   while (true) {
100     std::string user_input;
101     std::cout << "Enter the hex of the advertisement you would like to parse "
102                  "(see above examples): ";
103     std::cin >> user_input;
104     std::string bytes;
105     auto hex_result = absl::HexStringToBytes(user_input, &bytes);
106     if (!hex_result) {
107       std::cout << "Provided string is not valid hex";
108       continue;
109     }
110     auto buffer = nearby_protocol::ByteBuffer<255>::TryFromString(bytes);
111     if (!buffer.ok()) {
112       std::cout << "Too many bytes provided, must fit into a max length 255 "
113                    "byte BLE advertisement\n";
114       continue;
115     }
116     nearby_protocol::RawAdvertisementPayload const user_input_payload(
117         buffer.value());
118 
119     // Try to deserialize user input
120     auto user_input_result =
121         nearby_protocol::Deserializer::DeserializeAdvertisement(
122             user_input_payload, credential_book);
123     HandleAdvertisementResult(std::move(user_input_result));
124 
125     char choice;
126     do {
127       std::cout << "Do you want to continue? (Y/N) ";
128       std::cin >> choice;
129     } while (choice != 'Y' && choice != 'N' && choice != 'n' && choice != 'y');
130 
131     if (choice == 'N' || choice == 'n') {
132       return 0;
133     }
134   }
135 }
136 
SamplePanicHandler(nearby_protocol::PanicReason reason)137 void SamplePanicHandler(nearby_protocol::PanicReason reason) {
138   std::cout << "Panicking! Reason: ";
139   switch (reason) {
140     case nearby_protocol::PanicReason::EnumCastFailed: {
141       std::cout << "EnumCastFailed \n";
142       break;
143     }
144     case nearby_protocol::PanicReason::AssertFailed: {
145       std::cout << "AssertFailed \n";
146       break;
147     }
148     case nearby_protocol::PanicReason::InvalidStackDataStructure: {
149       std::cout << "InvalidStackDataStructure \n";
150       break;
151     }
152     case np_ffi::internal::PanicReason::ExceededMaxHandleAllocations:
153       std::cout << "ExceededMaxHandleAllocations \n";
154       break;
155   }
156   std::abort();
157 }
158 
HandleAdvertisementResult(nearby_protocol::DeserializeAdvertisementResult result)159 void HandleAdvertisementResult(
160     nearby_protocol::DeserializeAdvertisementResult result) {
161   switch (result.GetKind()) {
162     case nearby_protocol::DeserializeAdvertisementResultKind::Error:
163       std::cout << "Error in deserializing advertisement!\n";
164       break;
165     case nearby_protocol::DeserializeAdvertisementResultKind::V0:
166       std::cout << "Successfully deserialized a V0 advertisement!\n";
167       HandleV0Adv(result.IntoV0());
168       break;
169     case nearby_protocol::DeserializeAdvertisementResultKind::V1:
170       std::cout << "Successfully deserialized a V1 advertisement\n";
171       HandleV1Adv(result.IntoV1());
172       break;
173   }
174 }
175 
HandleV0Adv(nearby_protocol::DeserializedV0Advertisement result)176 void HandleV0Adv(nearby_protocol::DeserializedV0Advertisement result) {
177   switch (result.GetKind()) {
178     case nearby_protocol::DeserializedV0AdvertisementKind::Legible:
179       std::cout << "\tThe Advertisement is plaintext \n";
180       HandleLegibleV0Adv(result.IntoLegible());
181       break;
182     case nearby_protocol::DeserializedV0AdvertisementKind::
183         NoMatchingCredentials:
184       std::cout << "\tNo matching credentials found for this adv\n";
185       return;
186   }
187 }
188 
HandleLegibleV0Adv(nearby_protocol::LegibleDeserializedV0Advertisement legible_adv)189 void HandleLegibleV0Adv(
190     nearby_protocol::LegibleDeserializedV0Advertisement legible_adv) {
191   HandleV0IdentityKind(legible_adv.GetIdentityKind());
192 
193   auto num_des = legible_adv.GetNumberOfDataElements();
194   std::cout << "\t\tAdv contains " << static_cast<unsigned>(num_des)
195             << " data elements \n";
196   auto payload = legible_adv.IntoPayload();
197   for (int i = 0; i < num_des; i++) {
198     auto de_result = payload.TryGetDataElement(i);
199     if (!de_result.ok()) {
200       std::cout << "\t\tError getting DE at index: " << i << "\n";
201       return;
202     }
203     std::cout << "\t\tSuccessfully retrieved De at index " << i << "\n";
204     HandleDataElement(de_result.value());
205   }
206 }
207 
HandleV0IdentityKind(nearby_protocol::DeserializedV0IdentityKind identity)208 void HandleV0IdentityKind(
209     nearby_protocol::DeserializedV0IdentityKind identity) {
210   switch (identity) {
211     case np_ffi::internal::DeserializedV0IdentityKind::Plaintext: {
212       std::cout << "\t\tIdentity is Plaintext\n";
213       break;
214     }
215     case np_ffi::internal::DeserializedV0IdentityKind::Decrypted: {
216       std::cout << "\t\tIdentity is Encrypted\n";
217       break;
218     }
219   }
220 }
221 
HandleDataElement(nearby_protocol::V0DataElement de)222 void HandleDataElement(nearby_protocol::V0DataElement de) {
223   switch (de.GetKind()) {
224     case nearby_protocol::V0DataElementKind::TxPower: {
225       std::cout << "\t\t\tDE Type is TxPower\n";
226       auto tx_power = de.AsTxPower();
227       std::cout << "\t\t\tpower: " << static_cast<int>(tx_power.GetAsI8())
228                 << "\n";
229       return;
230     }
231     case nearby_protocol::V0DataElementKind::Actions: {
232       std::cout << "\t\t\tDE Type is Actions\n";
233       auto actions = de.AsActions();
234       std::cout << "\t\t\tactions: " << std::bitset<32>(actions.GetAsU32())
235                 << "\n";
236       return;
237     }
238   }
239 }
240 
HandleV1Adv(nearby_protocol::DeserializedV1Advertisement adv)241 void HandleV1Adv(nearby_protocol::DeserializedV1Advertisement adv) {
242   auto legible_sections = adv.GetNumLegibleSections();
243   std::cout << "\tAdv has " << static_cast<unsigned>(legible_sections)
244             << " legible sections \n";
245 
246   auto encrypted_sections = adv.GetNumUndecryptableSections();
247   std::cout << "\tAdv has " << static_cast<unsigned>(encrypted_sections)
248             << " undecryptable sections\n";
249 
250   for (auto i = 0; i < legible_sections; i++) {
251     auto section_result = adv.TryGetSection(i);
252     if (!section_result.ok()) {
253       std::cout << "\tError getting Section at index: " << i << "\n";
254       return;
255     }
256     std::cout << "\tSuccessfully retrieved section at index " << i << "\n";
257     HandleV1Section(section_result.value());
258   }
259 }
260 
HandleV1Section(const nearby_protocol::DeserializedV1Section & section)261 void HandleV1Section(const nearby_protocol::DeserializedV1Section& section) {
262   switch (section.GetIdentityKind()) {
263     case np_ffi::internal::DeserializedV1IdentityKind::Plaintext: {
264       std::cout << "\t\tIdentity is Plaintext\n";
265       break;
266     }
267     case np_ffi::internal::DeserializedV1IdentityKind::Decrypted: {
268       std::cout << "\t\tIdentity is Encrypted\n";
269       break;
270     }
271   }
272 
273   auto num_des = section.NumberOfDataElements();
274   std::cout << "\t\tSection has " << static_cast<unsigned>(num_des)
275             << " data elements \n";
276   for (auto i = 0; i < num_des; i++) {
277     auto de_result = section.TryGetDataElement(i);
278     if (!de_result.ok()) {
279       std::cout << "\t\tError getting de at index: " << i << "\n";
280       return;
281     }
282     std::cout << "\t\tSuccessfully retrieved data element at index " << i
283               << "\n";
284     HandleV1DataElement(de_result.value());
285   }
286 }
287 
HandleV1DataElement(nearby_protocol::V1DataElement de)288 void HandleV1DataElement(nearby_protocol::V1DataElement de) {
289   std::cout << "\t\t\tData Element type code: "
290             << static_cast<unsigned>(de.GetDataElementTypeCode()) << "\n";
291   std::cout << "\t\t\tPayload bytes as hex: "
292             << absl::BytesToHexString(de.GetPayload().ToString()) << "\n";
293 }
294