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