| // Copyright 2023 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "nearby_protocol.h" |
| |
| #include <bitset> |
| #include <iostream> |
| |
| #include "absl/strings/escaping.h" |
| |
| static void SamplePanicHandler(nearby_protocol::PanicReason reason); |
| |
| void HandleAdvertisementResult(nearby_protocol::DeserializeAdvertisementResult); |
| |
| void HandleV0Adv(nearby_protocol::DeserializedV0Advertisement); |
| void HandleLegibleV0Adv(nearby_protocol::LegibleDeserializedV0Advertisement); |
| void HandleV0IdentityKind(nearby_protocol::DeserializedV0IdentityKind); |
| void HandleDataElement(nearby_protocol::V0DataElement); |
| |
| void HandleV1Adv(nearby_protocol::DeserializedV1Advertisement); |
| void HandleV1Section(nearby_protocol::DeserializedV1Section); |
| void HandleV1DataElement(nearby_protocol::V1DataElement); |
| |
| int main() { |
| auto result = |
| nearby_protocol::GlobalConfig::SetPanicHandler(SamplePanicHandler); |
| if (result) { |
| std::cout << "Successfully registered panic handler\n"; |
| } else { |
| std::cout << "Failed register panic handler\n"; |
| return -1; |
| } |
| nearby_protocol::GlobalConfig::SetNumShards(4); |
| |
| std::cout << "\n========= Example V0 Adv ==========\n"; |
| std::cout << "Hex bytes: 00031503260046\n\n"; |
| |
| // Create an empty credential slab and verify that it is successful |
| auto cred_slab_result = nearby_protocol::CredentialSlab::TryCreate(); |
| if (!cred_slab_result.ok()) { |
| std::cout << cred_slab_result.status().ToString(); |
| return -1; |
| } |
| |
| // Create an empty credential book from the empty slab, and verify success. |
| auto cred_book_result = nearby_protocol::CredentialBook::TryCreateFromSlab(cred_slab_result.value()); |
| if (!cred_book_result.ok()) { |
| std::cout << cred_book_result.status().ToString(); |
| return -1; |
| } |
| |
| auto v0_byte_string = "00" // Adv Header |
| "03" // Public DE header |
| "1503" // Length 1 Tx Power DE with value 3 |
| "260046"; // Length 2 Actions |
| |
| auto v0_bytes = absl::HexStringToBytes(v0_byte_string); |
| auto v0_buffer = nearby_protocol::ByteBuffer<255>::CopyFrom(v0_bytes); |
| nearby_protocol::RawAdvertisementPayload v0_payload(v0_buffer.value()); |
| |
| // Try to deserialize a V0 payload |
| auto deserialize_v0_result = |
| nearby_protocol::Deserializer::DeserializeAdvertisement( |
| v0_payload, cred_book_result.value()); |
| HandleAdvertisementResult(std::move(deserialize_v0_result)); |
| |
| std::cout << "\n========= Example V1 Adv ==========\n"; |
| std::cout << "Hex bytes: 20040326004603031505\n\n"; |
| |
| auto v1_byte_string = "20" // V1 Advertisement header |
| "04" // Section Header |
| "03" // Public Identity DE header |
| "260046";// Length 2 Actions DE |
| |
| auto v1_bytes = absl::HexStringToBytes(v1_byte_string); |
| auto v1_buffer = nearby_protocol::ByteBuffer<255>::CopyFrom(v1_bytes); |
| nearby_protocol::RawAdvertisementPayload v1_payload(v1_buffer.value()); |
| |
| // Try to deserialize a V1 payload |
| auto deserialize_v1_result = |
| nearby_protocol::Deserializer::DeserializeAdvertisement( |
| v1_payload, cred_book_result.value()); |
| HandleAdvertisementResult(std::move(deserialize_v1_result)); |
| |
| std::cout << "\n========= User input sample ==========\n\n"; |
| while (true) { |
| std::string user_input; |
| std::cout << "Enter the hex of the advertisement you would like to parse " |
| "(see above examples): "; |
| std::cin >> user_input; |
| auto bytes = absl::HexStringToBytes(user_input); |
| auto buffer = nearby_protocol::ByteBuffer<255>::CopyFrom(bytes); |
| if (!buffer.ok()) { |
| std::cout << "Too many bytes provided, must fit into a max length 255 " |
| "byte BLE advertisement\n"; |
| continue; |
| } |
| nearby_protocol::RawAdvertisementPayload user_input_payload(buffer.value()); |
| |
| // Try to deserialize user input |
| auto user_input_result = |
| nearby_protocol::Deserializer::DeserializeAdvertisement( |
| user_input_payload, cred_book_result.value()); |
| HandleAdvertisementResult(std::move(user_input_result)); |
| |
| char choice; |
| do { |
| std::cout << "Do you want to continue? (Y/N) "; |
| std::cin >> choice; |
| } while (choice != 'Y' && choice != 'N' && choice != 'n' && choice != 'y'); |
| |
| if (choice == 'N' || choice == 'n') { |
| return 0; |
| } |
| } |
| } |
| |
| static void SamplePanicHandler(nearby_protocol::PanicReason reason) { |
| std::cout << "Panicking! Reason: "; |
| switch (reason) { |
| case nearby_protocol::PanicReason::EnumCastFailed: { |
| std::cout << "EnumCastFailed \n"; |
| break; |
| } |
| case nearby_protocol::PanicReason::AssertFailed: { |
| std::cout << "AssertFailed \n"; |
| break; |
| } |
| case nearby_protocol::PanicReason::InvalidActionBits: { |
| std::cout << "InvalidActionBits \n"; |
| break; |
| } |
| } |
| std::abort(); |
| } |
| |
| void HandleAdvertisementResult( |
| nearby_protocol::DeserializeAdvertisementResult result) { |
| switch (result.GetKind()) { |
| case nearby_protocol::DeserializeAdvertisementResultKind::Error: |
| std::cout << "Error in deserializing advertisement!\n"; |
| break; |
| case nearby_protocol::DeserializeAdvertisementResultKind::V0: |
| std::cout << "Successfully deserialized a V0 advertisement!\n"; |
| HandleV0Adv(result.IntoV0()); |
| break; |
| case nearby_protocol::DeserializeAdvertisementResultKind::V1: |
| std::cout << "Successfully deserialized a V1 advertisement\n"; |
| HandleV1Adv(result.IntoV1()); |
| break; |
| } |
| } |
| |
| void HandleV0Adv(nearby_protocol::DeserializedV0Advertisement result) { |
| switch (result.GetKind()) { |
| case nearby_protocol::DeserializedV0AdvertisementKind::Legible: |
| std::cout << "\tThe Advertisement is plaintext \n"; |
| HandleLegibleV0Adv(result.IntoLegible()); |
| break; |
| case nearby_protocol::DeserializedV0AdvertisementKind::NoMatchingCredentials: |
| std::cout << "\tNo matching credentials found for this adv\n"; |
| return; |
| } |
| } |
| |
| void HandleLegibleV0Adv( |
| nearby_protocol::LegibleDeserializedV0Advertisement legible_adv) { |
| HandleV0IdentityKind(legible_adv.GetIdentityKind()); |
| |
| auto num_des = legible_adv.GetNumberOfDataElements(); |
| std::cout << "\t\tAdv contains " << unsigned(num_des) << " data elements \n"; |
| auto payload = legible_adv.IntoPayload(); |
| for (int i = 0; i < num_des; i++) { |
| auto de_result = payload.TryGetDataElement(i); |
| if (!de_result.ok()) { |
| std::cout << "\t\tError getting DE at index: " << i << "\n"; |
| return; |
| } |
| std::cout << "\t\tSuccessfully retrieved De at index " << i << "\n"; |
| HandleDataElement(de_result.value()); |
| } |
| } |
| |
| void HandleV0IdentityKind(nearby_protocol::DeserializedV0IdentityKind identity) { |
| switch (identity) { |
| case np_ffi::internal::DeserializedV0IdentityKind::Plaintext: { |
| std::cout << "\t\tIdentity is Plaintext\n"; |
| break; |
| } |
| case np_ffi::internal::DeserializedV0IdentityKind::Decrypted: { |
| std::cout << "\t\tIdentity is Encrypted\n"; |
| break; |
| } |
| } |
| } |
| |
| void HandleDataElement(nearby_protocol::V0DataElement de) { |
| switch (de.GetKind()) { |
| case nearby_protocol::V0DataElementKind::TxPower: { |
| std::cout << "\t\t\tDE Type is TxPower\n"; |
| auto tx_power = de.AsTxPower(); |
| std::cout << "\t\t\tpower: " << int(tx_power.tx_power) << "\n"; |
| return; |
| } |
| case nearby_protocol::V0DataElementKind::Actions: { |
| std::cout << "\t\t\tDE Type is Actions\n"; |
| auto actions = de.AsActions(); |
| std::cout << "\t\t\tactions: " << std::bitset<32>(actions.GetAsU32()) |
| << "\n"; |
| return; |
| } |
| } |
| } |
| |
| void HandleV1Adv(nearby_protocol::DeserializedV1Advertisement adv) { |
| auto legible_sections = adv.GetNumLegibleSections(); |
| std::cout << "\tAdv has " << unsigned(legible_sections) |
| << " legible sections \n"; |
| |
| auto encrypted_sections = adv.GetNumUndecryptableSections(); |
| std::cout << "\tAdv has " << unsigned(encrypted_sections) |
| << " undecryptable sections\n"; |
| |
| for (auto i = 0; i < legible_sections; i++) { |
| auto section_result = adv.TryGetSection(i); |
| if (!section_result.ok()) { |
| std::cout << "\tError getting Section at index: " << i << "\n"; |
| return; |
| } |
| std::cout << "\tSuccessfully retrieved section at index " << i << "\n"; |
| HandleV1Section(section_result.value()); |
| } |
| } |
| |
| void HandleV1Section(nearby_protocol::DeserializedV1Section section) { |
| switch (section.GetIdentityKind()) { |
| case np_ffi::internal::DeserializedV1IdentityKind::Plaintext: { |
| std::cout << "\t\tIdentity is Plaintext\n"; |
| break; |
| } |
| case np_ffi::internal::DeserializedV1IdentityKind::Decrypted: { |
| std::cout << "\t\tIdentity is Encrypted\n"; |
| break; |
| } |
| } |
| |
| auto num_des = section.NumberOfDataElements(); |
| std::cout << "\t\tSection has " << unsigned(num_des) << " data elements \n"; |
| for (auto i = 0; i < num_des; i++) { |
| auto de_result = section.TryGetDataElement(i); |
| if (!de_result.ok()) { |
| std::cout << "\t\tError getting de at index: " << i << "\n"; |
| return; |
| } |
| std::cout << "\t\tSuccessfully retrieved data element at index " << i |
| << "\n"; |
| HandleV1DataElement(de_result.value()); |
| } |
| } |
| |
| void HandleV1DataElement(nearby_protocol::V1DataElement de) { |
| std::cout << "\t\t\tData Element type code: " |
| << unsigned(de.GetDataElementTypeCode()) << "\n"; |
| std::cout << "\t\t\tPayload bytes as hex: " |
| << absl::BytesToHexString(de.GetPayload().ToString()) << "\n"; |
| } |