blob: 1d273dafbc5ab3a505a7bba9d1cb20d5ba966205 [file] [log] [blame]
// 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";
}