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 // 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 #ifndef NEARBY_PRESENCE_NP_CPP_FFI_INCLUDE_NP_PROTOCOL_H_ 16 #define NEARBY_PRESENCE_NP_CPP_FFI_INCLUDE_NP_PROTOCOL_H_ 17 18 #include <array> 19 #include <cstddef> 20 #include <cstdint> 21 #include <memory> 22 #include <span> 23 #include <string> 24 #include <utility> 25 #include <vector> 26 27 #include "absl/status/status.h" 28 #include "absl/status/statusor.h" 29 #include "absl/strings/str_format.h" 30 #include "absl/strings/string_view.h" 31 #include "np_cpp_ffi_types.h" 32 33 // This namespace provides a C++ API surface to the Rust nearby protocol 34 // implementation. This is a wrapper over the np_ffi::internal namespace defined 35 // in the headers np_cpp_ffi_functions.h and np_cpp_ffi_types.h which are 36 // autogenerated by cbindgen based on the rust crate np_c_ffi. 37 // 38 // Classes in this namespace which have explicitly deleted copy-assignment and 39 // copy-constructor functions are backed by a C handle allocated in the 40 // underlying rust library. These handles are managed by the C++ classes which 41 // hold them and will be freed automatically when the corresponding type which 42 // currently owns the handle goes out of scope. 43 // 44 // A note on conventions in this API surface: 45 // 46 // Function prefixed with `Into` will transfer ownership of the current instance 47 // into a the newly returned object type, meaning that the previous instance is 48 // no longer valid. Attempting to use a type which has already been moved out of 49 // will result in the panic handler being invoked, which by default triggers 50 // std::abort(); 51 // 52 // Functions prefixed with `As` will not transfer ownership and instead return a 53 // casted version of the previous object. 54 // 55 // Functions prefixed with `Try` are fallible and will return an 56 // absl::StatusOr<T> result that needs to be checked before its used 57 // 58 // DO NOT DIRECTLY access the types defined in np_ffi::internal::*, these are 59 // auto-generated and are easy to mis-use. Instead use the public types exposed 60 // via the `nearby_protocol` namespace. 61 namespace nearby_protocol { 62 63 // Re-exporting cbindgen generated types which are used in the public API 64 using np_ffi::internal::ActionType; 65 using np_ffi::internal::AddV0CredentialToSlabResult; 66 using np_ffi::internal::AddV0DEResult; 67 using np_ffi::internal::AddV1CredentialToSlabResult; 68 using np_ffi::internal::AdvertisementBuilderKind; 69 using np_ffi::internal::CreateCredentialBookResultKind; 70 using np_ffi::internal::CreateV1SectionBuilderResultKind; 71 using np_ffi::internal::CurrentHandleAllocations; 72 using np_ffi::internal::DeserializeAdvertisementResultKind; 73 using np_ffi::internal::DeserializedV0AdvertisementKind; 74 using np_ffi::internal::DeserializedV0IdentityDetails; 75 using np_ffi::internal::DeserializedV0IdentityKind; 76 using np_ffi::internal::DeserializedV1IdentityDetails; 77 using np_ffi::internal::DeserializedV1IdentityKind; 78 using np_ffi::internal::GetV0DEResultKind; 79 using np_ffi::internal::PanicReason; 80 using np_ffi::internal::SerializeV0AdvertisementResultKind; 81 using np_ffi::internal::SerializeV1AdvertisementResultKind; 82 using np_ffi::internal::V0DataElementKind; 83 using np_ffi::internal::V1VerificationMode; 84 85 const uint8_t MAX_ADV_PAYLOAD_SIZE = 255; 86 const uint8_t MAX_V1_DE_PAYLOAD_SIZE = 127; 87 const uint8_t KEY_SEED_SIZE = 32; 88 const uint8_t HMAC_TAG_SIZE = 32; 89 const uint8_t PUBLIC_SIGNING_KEY_SIZE = 32; 90 const uint8_t DERIVED_SALT_SIZE = 16; 91 92 // All of the types defined in this header 93 class RawAdvertisementPayload; 94 class CredentialBook; 95 class CredentialSlab; 96 class Deserializer; 97 class DeserializeAdvertisementResult; 98 class MatchedCredentialData; 99 class V0MatchableCredential; 100 class V1MatchableCredential; 101 102 // V0 Classes 103 class DeserializedV0Advertisement; 104 class LegibleDeserializedV0Advertisement; 105 class V0DataElement; 106 class V0Payload; 107 class V0Actions; 108 109 // V1 Classes 110 class DeserializedV1Advertisement; 111 class DeserializedV1Section; 112 class V1DataElement; 113 114 // Global static singleton class used to customize the deserialization library. 115 // If no values are set, then the default values will be used. In most cases the 116 // default max instance values won't need to be changed unless you are running 117 // in a constrained resources environment, and need to specify an upper limit 118 // on memory consumption. See np_ffi_global_config_* functions in 119 // np_cpp_ffi_functions.h for more info 120 class GlobalConfig { 121 public: 122 // This class provides the static methods needed for global configuration, 123 // so it does not need to be constructable, cloneable, or assignable. 124 GlobalConfig(const GlobalConfig &) = delete; 125 void operator=(const GlobalConfig &) = delete; 126 GlobalConfig() = delete; 127 128 // Provides a user specified panic handler. This method will only have an 129 // effect on the global panic-handler the first time it's called, and this 130 // method will return `true` to indicate that the panic handler was 131 // successfully set. All subsequent calls to this method will simply ignore 132 // the argument and return `false`. see np_ffi_global_config_panic_handler in 133 // np_cpp_ffi_functions.h for more info 134 [[nodiscard]] static bool SetPanicHandler(void (*handler)(PanicReason)); 135 136 // Sets an override to the number of shards employed in the libraries internal 137 // handle maps, used to set an upper limit on memory consumption, see 138 // np_ffi_global_config_set_num_shards in np_cpp_ffi_functions.h for more info 139 static void SetNumShards(uint8_t num_shards); 140 141 // Checks the current count of all outstanding handle allocations, useful for 142 // debugging, logging, and testing 143 static CurrentHandleAllocations GetCurrentHandleAllocationCount(); 144 }; 145 146 // Holds the credentials used in the construction of a credential book 147 // using CredentialBook::TryCreateFromSlab() 148 class CredentialSlab { 149 public: 150 // Don't allow copy constructor or copy-assignment since 151 // this class wraps a handle to externally allocated resources. 152 CredentialSlab(const CredentialSlab &other) = delete; 153 CredentialSlab &operator=(const CredentialSlab &other) = delete; 154 155 // Move constructor and move assignment are needed in order to wrap this class 156 // in absl::StatusOr 157 CredentialSlab(CredentialSlab &&other) noexcept; 158 CredentialSlab &operator=(CredentialSlab &&other) noexcept; 159 160 // Creates a new instance of a CredentialSlab, returns the CredentialSlab on 161 // success or a Status code on failure 162 CredentialSlab(); 163 164 // The destructor for a CredentialSlab, this will be called when a 165 // CredentialSlab instance goes out of scope and will free the underlying 166 // resources 167 ~CredentialSlab(); 168 169 // Adds a V0 credential to the slab 170 void AddV0Credential(V0MatchableCredential v0_cred); 171 172 // Adds a V1 credential to the slab 173 [[nodiscard]] absl::Status AddV1Credential(V1MatchableCredential v1_cred); 174 175 private: 176 friend class CredentialBook; 177 np_ffi::internal::CredentialSlab credential_slab_; 178 bool moved_; 179 }; 180 181 // Holds the credentials used when decrypting data of an advertisement. 182 // This needs to be passed to Deserializer::DeserializeAdvertisement() when 183 // attempting to deserialize a payload 184 class CredentialBook { 185 public: 186 // Don't allow copy constructor, copy-assignment or default constructor, since 187 // this class wraps a handle to externally allocated resources. 188 CredentialBook() = delete; 189 CredentialBook(const CredentialBook &other) = delete; 190 CredentialBook &operator=(const CredentialBook &other) = delete; 191 192 // Move constructor and move assignment are needed in order to wrap this class 193 // in absl::StatusOr 194 CredentialBook(CredentialBook &&other) noexcept; 195 CredentialBook &operator=(CredentialBook &&other) noexcept; 196 197 // The destructor for a CredentialBook, this will be called when a 198 // CredentialBook instance goes out of scope and will free the underlying 199 // resources 200 ~CredentialBook(); 201 202 // Creates a new instance of a CredentialBook from a CredentialSlab, 203 // returning the CredentialBook on success or a Status code on failure. 204 // The passed credential-slab will be deallocated if this operation 205 // is successful. 206 CredentialBook(CredentialSlab &slab); 207 208 private: 209 friend class Deserializer; 210 np_ffi::internal::CredentialBook credential_book_; 211 bool moved_; 212 }; 213 214 // Holds data associated with a specific credential which will be returned to 215 // the caller when it is successfully matched with an advertisement. 216 class MatchedCredentialData { 217 public: 218 // Creates matched credential data from a provided credential_id used to 219 // correlate the data back to its full credential data, and the metadata byte 220 // buffer as copied from the given span over bytes. The span must be valid 221 // until the corresponding Add_*_Credential is called using this data. 222 // 223 // Safety: this is safe if the span is over a valid buffer of bytes. The copy 224 // from the memory address isn't atomic, so concurrent modification of the 225 // array from another thread would cause undefined behavior. 226 [[nodiscard]] MatchedCredentialData( 227 uint32_t cred_id, std::span<const uint8_t> encrypted_metadata_bytes); 228 229 private: 230 np_ffi::internal::FfiMatchedCredential data_{}; 231 friend class V0MatchableCredential; 232 friend class V1MatchableCredential; 233 }; 234 235 // Holds the v0 credential data needed by the deserializer to decrypt 236 // advertisements, along with some provided matched data that will be returned 237 // back to the caller upon a successful credential match. 238 class V0MatchableCredential { 239 public: 240 // Creates a new V0MatchableCredential from a key seed, its calculated hmac 241 // value and some match data. 242 [[nodiscard]] V0MatchableCredential( 243 std::array<uint8_t, KEY_SEED_SIZE> key_seed, 244 std::array<uint8_t, HMAC_TAG_SIZE> legacy_metadata_key_hmac, 245 MatchedCredentialData matched_credential_data); 246 247 private: 248 friend class CredentialSlab; 249 np_ffi::internal::V0MatchableCredential internal_{}; 250 }; 251 252 // Holds the v1 credential data needed by the deserializer to decrypt 253 // advertisements, along with some provided matched data that will be returned 254 // back to the caller upon a successful credential match. 255 class V1MatchableCredential { 256 public: 257 V1MatchableCredential() = delete; 258 259 // Creates a new V1MatchableCredential from key material, its calculated hmac 260 // value and some match data. 261 [[nodiscard]] V1MatchableCredential( 262 std::array<uint8_t, KEY_SEED_SIZE> key_seed, 263 std::array<uint8_t, HMAC_TAG_SIZE> expected_unsigned_metadata_key_hmac, 264 std::array<uint8_t, HMAC_TAG_SIZE> expected_signed_metadata_key_hmac, 265 std::array<uint8_t, PUBLIC_SIGNING_KEY_SIZE> pub_key, 266 MatchedCredentialData matched_credential_data); 267 268 private: 269 friend class CredentialSlab; 270 np_ffi::internal::V1MatchableCredential internal_{}; 271 }; 272 273 // Holds the V0 credential data needed to encrypt advertisements. 274 class V0BroadcastCredential { 275 public: 276 V0BroadcastCredential() = delete; 277 278 // Creates a new V0Broadcast credential with the given 279 // key seed and identity token. 280 [[nodiscard]] V0BroadcastCredential(std::array<uint8_t, 32> key_seed, 281 std::array<uint8_t, 14> identity_token); 282 283 private: 284 friend class V0AdvertisementBuilder; 285 np_ffi::internal::V0BroadcastCredential internal_; 286 }; 287 288 // Representation of a buffer of bytes returned from and passed to Rust library 289 // deserialization APIs. `N` is the max size of the buffer but its actual 290 // contents can be up to N in size. 291 template <size_t N> 292 class ByteBuffer { 293 public: 294 // Constructs a ByteBuffer from an std::array of bytes 295 template <size_t M> ByteBuffer(std::array<uint8_t,M> data)296 [[nodiscard]] constexpr explicit ByteBuffer(std::array<uint8_t, M> data) { 297 static_assert(N >= M); 298 np_ffi::internal::ByteBuffer<N> internal = 299 np_ffi::internal::ByteBuffer<N>(); 300 internal.len = M; 301 std::copy(data.begin(), data.end(), internal.bytes); 302 internal_ = internal; 303 } 304 305 // Creates a ByteBuffer from a std::vector<uint8_t> of bytes, returning an 306 // absl::OutOfRangeError in the case where bytes is too large to fit into the 307 // buffer. On success the returned type contains a copy of the provided bytes. TryFromSpan(std::span<const uint8_t> bytes)308 [[nodiscard]] static absl::StatusOr<ByteBuffer<N>> TryFromSpan( 309 std::span<const uint8_t> bytes) { 310 if (bytes.size() > N) { 311 return absl::OutOfRangeError( 312 absl::StrFormat("Provided bytes of length %d will not fit into a " 313 "ByteBuffer<N> of size N=%d", 314 bytes.size(), N)); 315 } 316 np_ffi::internal::ByteBuffer<N> internal = 317 np_ffi::internal::ByteBuffer<N>(); 318 internal.len = bytes.size(); 319 std::copy(std::begin(bytes), std::end(bytes), internal.bytes); 320 return ByteBuffer(internal); 321 } 322 323 // Creates a ByteBuffer from a absl::string_view of bytes, returning an 324 // absl::OutOfRangeError in the case where bytes is too large to fit into the 325 // buffer. On success the returned type contains a copy of the provided bytes. TryFromString(absl::string_view bytes)326 [[nodiscard]] static absl::StatusOr<ByteBuffer<N>> TryFromString( 327 absl::string_view bytes) { 328 if (bytes.length() > N) { 329 return absl::OutOfRangeError( 330 absl::StrFormat("Provided bytes of length %d will not fit into a " 331 "ByteBuffer<N> of size N=%d", 332 bytes.length(), N)); 333 } 334 np_ffi::internal::ByteBuffer<N> internal = 335 np_ffi::internal::ByteBuffer<N>(); 336 internal.len = bytes.length(); 337 std::copy(std::begin(bytes), std::end(bytes), internal.bytes); 338 return ByteBuffer(internal); 339 } 340 341 // Helper method to convert the ByteBuffer into a vector. The vector will 342 // contain a copy of the bytes and won't share the underlying buffer. ToVector()343 [[nodiscard]] std::vector<uint8_t> ToVector() const { 344 std::vector<uint8_t> result(internal_.bytes, 345 internal_.bytes + internal_.len); 346 return result; 347 } 348 349 // Helper method to convert the ByteBuffer into a std::string. The returned 350 // string will contain a copy of the bytes and won't share the underlying 351 // buffer. ToString()352 [[nodiscard]] std::string ToString() const { 353 std::string result; 354 result.assign(internal_.bytes, internal_.bytes + internal_.len); 355 return result; 356 } 357 // Constructor for a fixed length buffer of bytes from its internal struct 358 // data representation consisting of a length and array of unint8_t bytes. ByteBuffer(np_ffi::internal::ByteBuffer<N> internal)359 [[nodiscard]] explicit ByteBuffer(np_ffi::internal::ByteBuffer<N> internal) 360 : internal_(internal) {} 361 362 private: 363 friend class V0AdvertisementBuilder; 364 friend class V1DataElement; 365 friend class Deserializer; 366 np_ffi::internal::ByteBuffer<N> internal_; 367 }; 368 369 class RawAdvertisementPayload { 370 public: 371 // Creates a RawAdvertisementPayload from a ByteBuffer. RawAdvertisementPayload(ByteBuffer<MAX_ADV_PAYLOAD_SIZE> bytes)372 explicit constexpr RawAdvertisementPayload( 373 ByteBuffer<MAX_ADV_PAYLOAD_SIZE> bytes) 374 : buffer_(bytes) {} 375 376 private: 377 ByteBuffer<MAX_ADV_PAYLOAD_SIZE> buffer_; 378 friend class Deserializer; 379 }; 380 381 // A global static Deserializer, configured by GlobalConfig and used to 382 // deserialize advertisement payloads 383 class Deserializer { 384 public: 385 // Attempts to deserialize an advertisement with the given service-data 386 // payload (presumed to be under the NP service UUID) using credentials pulled 387 // from the given credential-book. See np_ffi_deserialize_advertisement in 388 // np_cpp_ffi_functions.h for more info 389 [[nodiscard]] static DeserializeAdvertisementResult DeserializeAdvertisement( 390 const RawAdvertisementPayload &payload, 391 const CredentialBook &credential_book); 392 }; 393 394 // The result type returned from Deserializer::DeserializeAdvertisement(). Can 395 // be used to further process the advertisement and inspect its contents 396 class DeserializeAdvertisementResult { 397 public: 398 // Don't allow copy constructor, copy-assignment or default constructor, since 399 // this class wraps a handle to externally allocated resources. 400 DeserializeAdvertisementResult() = delete; 401 DeserializeAdvertisementResult(const DeserializeAdvertisementResult &other) = 402 delete; 403 DeserializeAdvertisementResult &operator=( 404 const DeserializeAdvertisementResult &other) = delete; 405 406 // Move constructor and move assignment operators 407 DeserializeAdvertisementResult( 408 DeserializeAdvertisementResult &&other) noexcept; 409 DeserializeAdvertisementResult &operator=( 410 DeserializeAdvertisementResult &&other) noexcept; 411 412 // Frees the underlying resources of the result. 413 ~DeserializeAdvertisementResult(); 414 415 // Returns the DeserializeAdvertisementResultKind of the Result 416 [[nodiscard]] DeserializeAdvertisementResultKind GetKind() const; 417 418 // Casts a `DeserializeAdvertisementResult` to the `V0` variant, panicking in 419 // the case where the passed value is of a different enum variant. This can 420 // only be called once. When called, this object is moved into the 421 // returned 'DeserializedV0Advertisement' and this object is no longer valid. 422 [[nodiscard]] DeserializedV0Advertisement IntoV0(); 423 424 // Casts a `DeserializeAdvertisementResult` to the `V1` variant, panicking in 425 // the case where the passed value is of a different enum variant. This can 426 // only be called once. After this is cast into a `V1` variant this result 427 // is no longer valid. 428 [[nodiscard]] DeserializedV1Advertisement IntoV1(); 429 430 private: 431 friend class Deserializer; DeserializeAdvertisementResult(np_ffi::internal::DeserializeAdvertisementResult result)432 explicit DeserializeAdvertisementResult( 433 np_ffi::internal::DeserializeAdvertisementResult result) 434 : result_(result), moved_(false) {} 435 436 np_ffi::internal::DeserializeAdvertisementResult result_; 437 bool moved_; 438 }; 439 440 // A deserialized V0 advertisement payload 441 class DeserializedV0Advertisement { 442 public: 443 // Don't allow copy constructor, copy-assignment or default constructor, since 444 // this class wraps a handle to externally allocated resources. 445 DeserializedV0Advertisement() = delete; 446 DeserializedV0Advertisement(const DeserializedV0Advertisement &other) = 447 delete; 448 DeserializedV0Advertisement &operator=( 449 const DeserializedV0Advertisement &other) = delete; 450 451 // Move constructor and move assignment operators 452 DeserializedV0Advertisement(DeserializedV0Advertisement &&other) noexcept; 453 DeserializedV0Advertisement &operator=( 454 DeserializedV0Advertisement &&other) noexcept; 455 456 // The destructor which will be called when a DeserializedV0Advertisement 457 // instance goes out of scope, and will free the underlying resources 458 ~DeserializedV0Advertisement(); 459 460 // Returns the DeserializedV0AdvertisementKind of the advertisement 461 [[nodiscard]] DeserializedV0AdvertisementKind GetKind() const; 462 463 // Casts a `DeserializedV0Advertisement` to the `Legible` variant, panicking 464 // in the case where the passed value is of a different enum variant. 465 // After calling this the object is moved into the returned 466 // `LegibleDeserializedV0Advertisement`, and this object is no longer valid. 467 [[nodiscard]] LegibleDeserializedV0Advertisement IntoLegible(); 468 469 private: 470 friend class DeserializeAdvertisementResult; DeserializedV0Advertisement(np_ffi::internal::DeserializedV0Advertisement v0_advertisement)471 explicit DeserializedV0Advertisement( 472 np_ffi::internal::DeserializedV0Advertisement v0_advertisement) 473 : v0_advertisement_(v0_advertisement), moved_(false) {} 474 475 np_ffi::internal::DeserializedV0Advertisement v0_advertisement_; 476 bool moved_; 477 }; 478 479 // A Legible deserialized V0 advertisement, which means the contents of it are 480 // either plaintext OR have already been decrypted successfully by a matching 481 // credential in the provided CredentialBook 482 class LegibleDeserializedV0Advertisement { 483 public: 484 // Don't allow copy constructor, copy-assignment or default constructor, since 485 // this class wraps a handle to externally allocated resources. 486 LegibleDeserializedV0Advertisement() = delete; 487 LegibleDeserializedV0Advertisement( 488 const LegibleDeserializedV0Advertisement &other) = delete; 489 LegibleDeserializedV0Advertisement &operator=( 490 const LegibleDeserializedV0Advertisement &other) = delete; 491 492 // Move constructor and move assignment operators 493 LegibleDeserializedV0Advertisement( 494 LegibleDeserializedV0Advertisement &&other) noexcept; 495 LegibleDeserializedV0Advertisement &operator=( 496 LegibleDeserializedV0Advertisement &&other) noexcept; 497 498 // The destructor which will be called when a this instance goes out of scope, 499 // and will free the underlying parent handle. 500 ~LegibleDeserializedV0Advertisement(); 501 502 // Returns just the kind of identity (public/encrypted) 503 // associated with the advertisement 504 [[nodiscard]] DeserializedV0IdentityKind GetIdentityKind() const; 505 // Returns the number of data elements in the advertisement 506 [[nodiscard]] uint8_t GetNumberOfDataElements() const; 507 // Returns just the data-element payload of the advertisement 508 [[nodiscard]] V0Payload IntoPayload(); 509 510 private: 511 friend class DeserializedV0Advertisement; LegibleDeserializedV0Advertisement(np_ffi::internal::LegibleDeserializedV0Advertisement legible_v0_advertisement)512 explicit LegibleDeserializedV0Advertisement( 513 np_ffi::internal::LegibleDeserializedV0Advertisement 514 legible_v0_advertisement) 515 : legible_v0_advertisement_(legible_v0_advertisement), moved_(false) {} 516 517 np_ffi::internal::LegibleDeserializedV0Advertisement 518 legible_v0_advertisement_; 519 bool moved_; 520 }; 521 522 // A data element payload of a Deserialized V0 Advertisement. 523 class V0Payload { 524 public: 525 // Don't allow copy constructor, copy-assignment or default constructor, since 526 // this class wraps a handle to externally allocated resources. 527 V0Payload() = delete; 528 V0Payload(const V0Payload &other) = delete; 529 V0Payload &operator=(const V0Payload &other) = delete; 530 531 // Move constructor and move assignment operators 532 V0Payload(V0Payload &&other) noexcept; 533 V0Payload &operator=(V0Payload &&other) noexcept; 534 535 // Frees the underlying handle when this goes out of scope 536 ~V0Payload(); 537 538 // Tries to retrieve the data element at the given index, returns the data 539 // element if it exists otherwise returns an Error status code 540 [[nodiscard]] absl::StatusOr<V0DataElement> TryGetDataElement( 541 uint8_t index) const; 542 543 // Decrypts the metadata of the credential which matched with this 544 // advertisement, or returns an error if the metadata key is invalid and 545 // unable to successfully decrypt the metadata. 546 [[nodiscard]] absl::StatusOr<std::vector<uint8_t>> TryDecryptMetadata() const; 547 548 // Gets the details of the identity data element of this payload or returns an 549 // error if the payload does not have an identity (public advertisement) 550 [[nodiscard]] absl::StatusOr<DeserializedV0IdentityDetails> 551 TryGetIdentityDetails() const; 552 553 private: 554 friend class LegibleDeserializedV0Advertisement; V0Payload(np_ffi::internal::V0Payload v0_payload)555 explicit V0Payload(np_ffi::internal::V0Payload v0_payload) 556 : v0_payload_(v0_payload), moved_(false) {} 557 558 np_ffi::internal::V0Payload v0_payload_; 559 bool moved_; 560 }; 561 562 // A Tx Power [transmission power] between -100dBm and 20dBm 563 class TxPower { 564 public: 565 TxPower() = delete; 566 567 // Gets the value of this tx power as a signed byte. 568 [[nodiscard]] int8_t GetAsI8() const; 569 570 // Attempts to construct a tx power with the given value contained 571 // in a signed byte. If the number is not within the representable 572 // range, this method will return an invalid argument error. 573 [[nodiscard]] static absl::StatusOr<TxPower> TryBuildFromI8(int8_t value); 574 575 private: 576 friend class V0DataElement; TxPower(np_ffi::internal::TxPower tx_power)577 explicit TxPower(np_ffi::internal::TxPower tx_power) : tx_power_(tx_power) {} 578 np_ffi::internal::TxPower tx_power_; 579 }; 580 581 // A single V0 data element 582 class V0DataElement { 583 public: 584 // Yields the V0DataElementKind of the data element 585 [[nodiscard]] V0DataElementKind GetKind() const; 586 // Casts the V0DataElement into the TxPower variant, panicking in the case 587 // where the data element is of a different enum variant 588 [[nodiscard]] TxPower AsTxPower() const; 589 // Casts the V0DataElement into the Actions variant, panicking in the case 590 // where the data element is of a different enum variant 591 [[nodiscard]] V0Actions AsActions() const; 592 593 // Constructs a Tx Power V0 data element 594 explicit V0DataElement(TxPower tx_power); 595 // Constructs an Actions V0 data element 596 explicit V0DataElement(V0Actions actions); 597 598 private: 599 friend class V0AdvertisementBuilder; 600 friend class V0Payload; V0DataElement(np_ffi::internal::V0DataElement v0_data_element)601 explicit V0DataElement(np_ffi::internal::V0DataElement v0_data_element) 602 : v0_data_element_(v0_data_element) {} 603 np_ffi::internal::V0DataElement v0_data_element_; 604 }; 605 606 // A V0 Actions Data Element 607 class V0Actions { 608 public: 609 V0Actions() = delete; 610 611 // Gets the V0 Action bits as represented by a u32 where the last 8 bits are 612 // always 0 since V0 actions can only hold up to 24 bits. 613 [[nodiscard]] uint32_t GetAsU32() const; 614 615 /// Return whether a boolean action type is present in this data element 616 [[nodiscard]] bool HasAction(ActionType action) const; 617 618 /// Attempts to set the given action bit to the given boolean value. 619 /// This operation may fail with an invalid argument error 620 /// if the requested action bit may not be set given the encoding 621 /// of the containing advertisement. 622 /// In this case, the action bits will be unaltered by this call. 623 absl::Status TrySetAction(ActionType action, bool value); 624 625 // Constructs an all-zeroed V0 actions DE for the given advertisement builder 626 // kind. 627 [[nodiscard]] static V0Actions BuildNewZeroed(AdvertisementBuilderKind kind); 628 629 private: 630 friend class V0DataElement; V0Actions(np_ffi::internal::V0Actions actions)631 explicit V0Actions(np_ffi::internal::V0Actions actions) : actions_(actions) {} 632 np_ffi::internal::V0Actions actions_; 633 }; 634 635 // A deserialized V1 Advertisement payload 636 class DeserializedV1Advertisement { 637 public: 638 // Don't allow copy constructor, copy-assignment or default constructor, since 639 // this class wraps a handle to externally allocated resources. 640 DeserializedV1Advertisement() = delete; 641 DeserializedV1Advertisement(const DeserializedV1Advertisement &other) = 642 delete; 643 DeserializedV1Advertisement &operator=( 644 const DeserializedV1Advertisement &other) = delete; 645 646 // Move constructor and move assignment operators 647 DeserializedV1Advertisement(DeserializedV1Advertisement &&other) noexcept; 648 DeserializedV1Advertisement &operator=( 649 DeserializedV1Advertisement &&other) noexcept; 650 651 // Gets the number of legible sections on a deserialized V1 advertisement. 652 // This is usable as an iteration bound for the section_index of TryGetSection 653 [[nodiscard]] uint8_t GetNumLegibleSections() const; 654 // Gets the number of sections on a deserialized V1 advertisement which 655 // were unable to be decrypted with the credentials that the receiver 656 // possesses 657 [[nodiscard]] uint8_t GetNumUndecryptableSections() const; 658 // Tries to get the section with the given index in a deserialized V1 659 // advertisement. Returns a error code in the result of an invalid index 660 [[nodiscard]] absl::StatusOr<DeserializedV1Section> TryGetSection( 661 uint8_t section_index) const; 662 663 private: 664 friend class DeserializeAdvertisementResult; 665 explicit DeserializedV1Advertisement( 666 np_ffi::internal::DeserializedV1Advertisement v1_advertisement); 667 668 std::shared_ptr<np_ffi::internal::DeserializedV1Advertisement> 669 v1_advertisement_; 670 }; 671 672 // A Deserialized V1 Section of an advertisement 673 class DeserializedV1Section { 674 public: 675 // Returns the number of data elements present in the section 676 [[nodiscard]] uint8_t NumberOfDataElements() const; 677 678 // Returns the DeserializedV1IdentityKind of the identity 679 [[nodiscard]] DeserializedV1IdentityKind GetIdentityKind() const; 680 681 // Tries to get the data element in the section at the given index 682 [[nodiscard]] absl::StatusOr<V1DataElement> TryGetDataElement( 683 uint8_t index) const; 684 685 // Decrypts the metadata of the credential which matched with this section 686 // or returns an error in the case that the metadata could not be decrypted 687 [[nodiscard]] absl::StatusOr<std::vector<uint8_t>> TryDecryptMetadata() const; 688 689 // Gets the details of the identity data element of this section or returns an 690 // error if the section does not contain an identity (public section) 691 [[nodiscard]] absl::StatusOr<DeserializedV1IdentityDetails> 692 GetIdentityDetails() const; 693 694 // Attempts to derive a 16-byte DE salt for a DE in this section with the 695 // given DE offset. This operation may fail if the passed offset is 255 696 // (causes overflow) or if the section is leveraging a public identity, and 697 // hence, doesn't have an associated salt. The offset should come from a 698 // particular deserialized v1 de via `V1DataElement::GetOffset()` 699 [[nodiscard]] absl::StatusOr<std::array<uint8_t, DERIVED_SALT_SIZE>> 700 DeriveSaltForOffset(uint8_t offset) const; 701 702 private: 703 friend class DeserializedV1Advertisement; DeserializedV1Section(np_ffi::internal::DeserializedV1Section section,std::shared_ptr<np_ffi::internal::DeserializedV1Advertisement> owning_v1_advertisement)704 explicit DeserializedV1Section( 705 np_ffi::internal::DeserializedV1Section section, 706 std::shared_ptr<np_ffi::internal::DeserializedV1Advertisement> 707 owning_v1_advertisement) 708 : section_(section), 709 owning_v1_advertisement_(std::move(owning_v1_advertisement)) {} 710 np_ffi::internal::DeserializedV1Section section_; 711 std::shared_ptr<np_ffi::internal::DeserializedV1Advertisement> 712 owning_v1_advertisement_; 713 }; 714 715 // A V1 Data Element 716 class V1DataElement { 717 public: 718 // Yields the unsigned 32-bit integer V1 DE type code 719 [[nodiscard]] uint32_t GetDataElementTypeCode() const; 720 // Yields the payload bytes of the data element 721 [[nodiscard]] ByteBuffer<MAX_V1_DE_PAYLOAD_SIZE> GetPayload() const; 722 /// Gets the offset for this V1 data element. 723 [[nodiscard]] uint8_t GetOffset() const; 724 725 private: 726 friend class DeserializedV1Section; V1DataElement(np_ffi::internal::V1DataElement v1_data_element)727 explicit V1DataElement(np_ffi::internal::V1DataElement v1_data_element) 728 : v1_data_element_(v1_data_element) {} 729 np_ffi::internal::V1DataElement v1_data_element_; 730 }; 731 732 // A builder for V0 advertisements 733 class V0AdvertisementBuilder { 734 public: 735 V0AdvertisementBuilder() = delete; 736 // Don't allow copy constructor or copy assignment, since that would result in 737 // the underlying handle being freed multiple times 738 V0AdvertisementBuilder(const V0AdvertisementBuilder &other) = delete; 739 V0AdvertisementBuilder &operator=(const V0AdvertisementBuilder &other) = 740 delete; 741 742 // Move constructor and move assignment operators 743 V0AdvertisementBuilder(V0AdvertisementBuilder &&other) noexcept; 744 V0AdvertisementBuilder &operator=(V0AdvertisementBuilder &&other) noexcept; 745 746 // Frees the underlying resources of the adv builder. 747 ~V0AdvertisementBuilder(); 748 749 // Tries to add the given data element to the advertisement builder. 750 // May fail with: 751 // - An invalid argument code if: 752 // - The adv builder handle is invalid 753 // OR 754 // - The identity type of the adv builder is invalid 755 // for the data element we're attempting to add. 756 // - A resource exhausted error if there's no remaining adv space. 757 absl::Status TryAddDE(V0DataElement de); 758 759 // Attempts to serialize the contents of the advertisement 760 // builder to bytes. This operation will always result in the 761 // contents behind the handle for this instance being deallocated. 762 // 763 // This operation may return an out-of-range error in the case 764 // where the advertisement contents are of a size inappropriate 765 // for LDT encryption in an encrypted V0 advertisement. 766 [[nodiscard]] absl::StatusOr<ByteBuffer<24>> TrySerialize(); 767 768 // Creates a new V0 advertisement builder for a public advertisement, 769 // or returns a Status code on failure. 770 [[nodiscard]] static V0AdvertisementBuilder CreatePublic(); 771 772 // Creates a new V0 advertisement builder for an encrypted advertisement, 773 // or returns a Status code on failure. 774 [[nodiscard]] static V0AdvertisementBuilder CreateEncrypted( 775 V0BroadcastCredential broadcast_cred, std::array<uint8_t, 2> salt); 776 777 private: V0AdvertisementBuilder(np_ffi::internal::V0AdvertisementBuilder adv_builder)778 explicit V0AdvertisementBuilder( 779 np_ffi::internal::V0AdvertisementBuilder adv_builder) 780 : adv_builder_(adv_builder), moved_(false) {} 781 782 np_ffi::internal::V0AdvertisementBuilder adv_builder_; 783 bool moved_; 784 }; 785 786 } // namespace nearby_protocol 787 788 #endif // NEARBY_PRESENCE_NP_CPP_FFI_INCLUDE_NP_PROTOCOL_H_ 789