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 #include "nearby_protocol.h"
16
17 #include <array>
18 #include <cstddef>
19 #include <cstdint>
20 #include <cstdlib>
21 #include <iostream>
22 #include <memory>
23 #include <span>
24 #include <utility>
25 #include <vector>
26
27 #include "absl/status/status.h"
28 #include "absl/status/statusor.h"
29 #include "np_cpp_ffi_functions.h"
30 #include "np_cpp_ffi_types.h"
31
32 namespace nearby_protocol {
33
34 void panic_handler(PanicReason reason);
35
36 struct PanicHandler {
37 void (*handler)(PanicReason);
38 bool set_by_client;
39 };
40
41 PanicHandler gPanicHandler = PanicHandler{panic_handler, false};
42
43 // C++ layer internal panic handler
panic_handler(PanicReason reason)44 void panic_handler(PanicReason reason) {
45 // Give clients a chance to use their own panic handler, but if they don't
46 // terminate the process we will make sure it happens.
47 if (gPanicHandler.set_by_client) {
48 gPanicHandler.handler(reason);
49 }
50 std::abort();
51 }
52
_assert_panic(bool condition,const char * func,const char * file,int line)53 void _assert_panic(bool condition, const char* func, const char* file,
54 int line) {
55 if (!condition) {
56 std::cout << "Assert failed: \n function: " << func << "\n file: " << file
57 << "\n line: " << line << "\n";
58 panic_handler(PanicReason::AssertFailed);
59 }
60 }
61
62 #define assert_panic(e) _assert_panic(e, __func__, __FILE__, __LINE__)
63
SetPanicHandler(void (* handler)(PanicReason))64 bool GlobalConfig::SetPanicHandler(void (*handler)(PanicReason)) {
65 if (!gPanicHandler.set_by_client) {
66 gPanicHandler.handler = handler;
67 gPanicHandler.set_by_client = true;
68 return np_ffi::internal::np_ffi_global_config_panic_handler(panic_handler);
69 }
70 return false;
71 }
72
SetNumShards(const uint8_t num_shards)73 void GlobalConfig::SetNumShards(const uint8_t num_shards) {
74 np_ffi::internal::np_ffi_global_config_set_num_shards(num_shards);
75 }
76
GetCurrentHandleAllocationCount()77 CurrentHandleAllocations GlobalConfig::GetCurrentHandleAllocationCount() {
78 return np_ffi::internal::np_ffi_global_config_get_current_allocation_count();
79 }
80
CredentialSlab()81 CredentialSlab::CredentialSlab() {
82 this->credential_slab_ = np_ffi::internal::np_ffi_create_credential_slab();
83 this->moved_ = false;
84 }
85
~CredentialSlab()86 CredentialSlab::~CredentialSlab() {
87 if (!this->moved_) {
88 auto result =
89 np_ffi::internal::np_ffi_deallocate_credential_slab(credential_slab_);
90 assert_panic(result == np_ffi::internal::DeallocateResult::Success);
91 }
92 }
93
CredentialSlab(CredentialSlab && other)94 CredentialSlab::CredentialSlab(CredentialSlab&& other) noexcept
95 : credential_slab_(other.credential_slab_), moved_(other.moved_) {
96 other.credential_slab_ = {};
97 other.moved_ = true;
98 }
99
operator =(CredentialSlab && other)100 CredentialSlab& CredentialSlab::operator=(CredentialSlab&& other) noexcept {
101 if (this != &other) {
102 if (!this->moved_) {
103 auto result = np_ffi::internal::np_ffi_deallocate_credential_slab(
104 this->credential_slab_);
105 assert_panic(result == np_ffi::internal::DeallocateResult::Success);
106 }
107
108 this->credential_slab_ = other.credential_slab_;
109 this->moved_ = other.moved_;
110
111 other.credential_slab_ = {};
112 other.moved_ = true;
113 }
114 return *this;
115 }
116
AddV0Credential(const V0MatchableCredential v0_cred)117 void CredentialSlab::AddV0Credential(const V0MatchableCredential v0_cred) {
118 assert_panic(!this->moved_);
119 auto result = np_ffi::internal::np_ffi_CredentialSlab_add_v0_credential(
120 this->credential_slab_, v0_cred.internal_);
121 // Add V0 is infallible since the handle is guaranteed to be correct by the
122 // C++ wrapper
123 assert_panic(result == AddV0CredentialToSlabResult::Success);
124 }
125
AddV1Credential(const V1MatchableCredential v1_cred)126 absl::Status CredentialSlab::AddV1Credential(
127 const V1MatchableCredential v1_cred) {
128 assert_panic(!this->moved_);
129 auto result = np_ffi::internal::np_ffi_CredentialSlab_add_v1_credential(
130 this->credential_slab_, v1_cred.internal_);
131 switch (result) {
132 case AddV1CredentialToSlabResult::Success: {
133 return absl::OkStatus();
134 }
135 case AddV1CredentialToSlabResult::InvalidHandle: {
136 return absl::InvalidArgumentError(
137 "invalid credential slab handle provided");
138 }
139 case AddV1CredentialToSlabResult::InvalidPublicKeyBytes: {
140 return absl::InvalidArgumentError(
141 "Invalid public key bytes in credential");
142 }
143 }
144 }
145
CredentialBook(CredentialSlab & slab)146 CredentialBook::CredentialBook(CredentialSlab& slab) {
147 assert_panic(!slab.moved_);
148 auto result = np_ffi::internal::np_ffi_create_credential_book_from_slab(
149 slab.credential_slab_);
150 auto kind = np_ffi::internal::np_ffi_CreateCredentialBookResult_kind(result);
151 // Handle is guaranteed to be valid by the C++ wrapper
152 assert_panic(kind == CreateCredentialBookResultKind::Success);
153 slab.moved_ = true;
154 this->credential_book_ =
155 np_ffi::internal::np_ffi_CreateCredentialBookResult_into_SUCCESS(result);
156 this->moved_ = false;
157 }
158
~CredentialBook()159 CredentialBook::~CredentialBook() {
160 if (!this->moved_) {
161 auto result =
162 np_ffi::internal::np_ffi_deallocate_credential_book(credential_book_);
163 assert_panic(result == np_ffi::internal::DeallocateResult::Success);
164 }
165 }
166
CredentialBook(CredentialBook && other)167 CredentialBook::CredentialBook(CredentialBook&& other) noexcept
168 : credential_book_(other.credential_book_), moved_(other.moved_) {
169 other.credential_book_ = {};
170 other.moved_ = true;
171 }
172
operator =(CredentialBook && other)173 CredentialBook& CredentialBook::operator=(CredentialBook&& other) noexcept {
174 if (this != &other) {
175 if (!this->moved_) {
176 auto result = np_ffi::internal::np_ffi_deallocate_credential_book(
177 this->credential_book_);
178 assert_panic(result == np_ffi::internal::DeallocateResult::Success);
179 }
180
181 this->credential_book_ = other.credential_book_;
182 this->moved_ = other.moved_;
183
184 other.credential_book_ = {};
185 other.moved_ = true;
186 }
187 return *this;
188 }
189
DeserializeAdvertisement(const RawAdvertisementPayload & payload,const CredentialBook & credential_book)190 DeserializeAdvertisementResult Deserializer::DeserializeAdvertisement(
191 const RawAdvertisementPayload& payload,
192 const CredentialBook& credential_book) {
193 assert_panic(!credential_book.moved_);
194 auto result = np_ffi::internal::np_ffi_deserialize_advertisement(
195 {payload.buffer_.internal_}, credential_book.credential_book_);
196 return DeserializeAdvertisementResult(result);
197 }
198
GetKind() const199 DeserializeAdvertisementResultKind DeserializeAdvertisementResult::GetKind()
200 const {
201 assert_panic(!this->moved_);
202 return np_ffi::internal::np_ffi_DeserializeAdvertisementResult_kind(result_);
203 }
204
IntoV0()205 DeserializedV0Advertisement DeserializeAdvertisementResult::IntoV0() {
206 assert_panic(!this->moved_);
207 auto result =
208 np_ffi::internal::np_ffi_DeserializeAdvertisementResult_into_V0(result_);
209 this->moved_ = true;
210 return DeserializedV0Advertisement(result);
211 }
212
IntoV1()213 DeserializedV1Advertisement DeserializeAdvertisementResult::IntoV1() {
214 assert_panic(!this->moved_);
215 auto v1_adv =
216 np_ffi::internal::np_ffi_DeserializeAdvertisementResult_into_V1(result_);
217 this->moved_ = true;
218 return DeserializedV1Advertisement(v1_adv);
219 }
220
~DeserializeAdvertisementResult()221 DeserializeAdvertisementResult::~DeserializeAdvertisementResult() {
222 if (!this->moved_) {
223 auto result =
224 np_ffi::internal::np_ffi_deallocate_deserialize_advertisement_result(
225 result_);
226 assert_panic(result == np_ffi::internal::DeallocateResult::Success);
227 }
228 }
229
DeserializeAdvertisementResult(DeserializeAdvertisementResult && other)230 DeserializeAdvertisementResult::DeserializeAdvertisementResult(
231 DeserializeAdvertisementResult&& other) noexcept
232 : result_(other.result_), moved_(other.moved_) {
233 other.result_ = {};
234 other.moved_ = true;
235 }
236
operator =(DeserializeAdvertisementResult && other)237 DeserializeAdvertisementResult& DeserializeAdvertisementResult::operator=(
238 DeserializeAdvertisementResult&& other) noexcept {
239 if (this != &other) {
240 if (!this->moved_) {
241 auto result =
242 np_ffi::internal::np_ffi_deallocate_deserialize_advertisement_result(
243 result_);
244 assert_panic(result == np_ffi::internal::DeallocateResult::Success);
245 }
246 this->result_ = other.result_;
247 this->moved_ = other.moved_;
248
249 other.result_ = {};
250 other.moved_ = true;
251 }
252 return *this;
253 }
254
255 // V0 Stuff
DeserializedV0Advertisement(DeserializedV0Advertisement && other)256 DeserializedV0Advertisement::DeserializedV0Advertisement(
257 DeserializedV0Advertisement&& other) noexcept
258 : v0_advertisement_(other.v0_advertisement_), moved_(other.moved_) {
259 other.v0_advertisement_ = {};
260 other.moved_ = true;
261 }
262
operator =(DeserializedV0Advertisement && other)263 DeserializedV0Advertisement& DeserializedV0Advertisement::operator=(
264 DeserializedV0Advertisement&& other) noexcept {
265 if (this != &other) {
266 if (!this->moved_) {
267 auto result =
268 np_ffi::internal::np_ffi_deallocate_deserialized_V0_advertisement(
269 v0_advertisement_);
270 assert_panic(result == np_ffi::internal::DeallocateResult::Success);
271 }
272 this->v0_advertisement_ = other.v0_advertisement_;
273 this->moved_ = other.moved_;
274
275 other.v0_advertisement_ = {};
276 other.moved_ = true;
277 }
278 return *this;
279 }
280
~DeserializedV0Advertisement()281 DeserializedV0Advertisement::~DeserializedV0Advertisement() {
282 if (!this->moved_) {
283 auto result =
284 np_ffi::internal::np_ffi_deallocate_deserialized_V0_advertisement(
285 v0_advertisement_);
286 assert_panic(result == np_ffi::internal::DeallocateResult::Success);
287 }
288 }
289
290 np_ffi::internal::DeserializedV0AdvertisementKind
GetKind() const291 DeserializedV0Advertisement::GetKind() const {
292 assert_panic(!this->moved_);
293 return np_ffi::internal::np_ffi_DeserializedV0Advertisement_kind(
294 v0_advertisement_);
295 }
296
IntoLegible()297 LegibleDeserializedV0Advertisement DeserializedV0Advertisement::IntoLegible() {
298 assert_panic(!this->moved_);
299 auto result =
300 np_ffi::internal::np_ffi_DeserializedV0Advertisement_into_LEGIBLE(
301 v0_advertisement_);
302 this->moved_ = true;
303 this->v0_advertisement_ = {};
304 return LegibleDeserializedV0Advertisement(result);
305 }
306
LegibleDeserializedV0Advertisement(LegibleDeserializedV0Advertisement && other)307 LegibleDeserializedV0Advertisement::LegibleDeserializedV0Advertisement(
308 LegibleDeserializedV0Advertisement&& other) noexcept
309 : legible_v0_advertisement_(other.legible_v0_advertisement_),
310 moved_(other.moved_) {
311 other.moved_ = true;
312 other.legible_v0_advertisement_ = {};
313 }
314
315 LegibleDeserializedV0Advertisement&
operator =(LegibleDeserializedV0Advertisement && other)316 LegibleDeserializedV0Advertisement::operator=(
317 LegibleDeserializedV0Advertisement&& other) noexcept {
318 if (this != &other) {
319 if (!this->moved_) {
320 auto result =
321 np_ffi::internal::np_ffi_deallocate_legible_v0_advertisement(
322 this->legible_v0_advertisement_);
323 assert_panic(result == np_ffi::internal::DeallocateResult::Success);
324 }
325 this->legible_v0_advertisement_ = other.legible_v0_advertisement_;
326 this->moved_ = other.moved_;
327
328 other.moved_ = true;
329 other.legible_v0_advertisement_ = {};
330 }
331 return *this;
332 }
333
~LegibleDeserializedV0Advertisement()334 LegibleDeserializedV0Advertisement::~LegibleDeserializedV0Advertisement() {
335 if (!this->moved_) {
336 auto result = np_ffi::internal::np_ffi_deallocate_legible_v0_advertisement(
337 this->legible_v0_advertisement_);
338 assert_panic(result == np_ffi::internal::DeallocateResult::Success);
339 }
340 }
341
GetIdentityKind() const342 DeserializedV0IdentityKind LegibleDeserializedV0Advertisement::GetIdentityKind()
343 const {
344 assert_panic(!this->moved_);
345 auto result = np_ffi::internal::
346 np_ffi_LegibleDeserializedV0Advertisement_get_identity_kind(
347 legible_v0_advertisement_);
348 return result;
349 }
350
GetNumberOfDataElements() const351 uint8_t LegibleDeserializedV0Advertisement::GetNumberOfDataElements() const {
352 assert_panic(!this->moved_);
353 return np_ffi::internal::
354 np_ffi_LegibleDeserializedV0Advertisement_get_num_des(
355 legible_v0_advertisement_);
356 }
357
IntoPayload()358 V0Payload LegibleDeserializedV0Advertisement::IntoPayload() {
359 assert_panic(!this->moved_);
360 auto result = np_ffi_LegibleDeserializedV0Advertisement_into_payload(
361 legible_v0_advertisement_);
362 this->moved_ = true;
363 return V0Payload(result);
364 }
365
V0Payload(V0Payload && other)366 V0Payload::V0Payload(V0Payload&& other) noexcept
367 : v0_payload_(other.v0_payload_), moved_(other.moved_) {
368 other.v0_payload_ = {};
369 other.moved_ = true;
370 }
371
operator =(V0Payload && other)372 V0Payload& V0Payload::operator=(V0Payload&& other) noexcept {
373 if (this != &other) {
374 if (!this->moved_) {
375 auto result =
376 np_ffi::internal::np_ffi_deallocate_v0_payload(this->v0_payload_);
377 assert_panic(result == np_ffi::internal::DeallocateResult::Success);
378 }
379 this->v0_payload_ = other.v0_payload_;
380 this->moved_ = other.moved_;
381
382 other.moved_ = true;
383 other.v0_payload_ = {};
384 }
385 return *this;
386 }
387
~V0Payload()388 V0Payload::~V0Payload() {
389 if (!this->moved_) {
390 auto result =
391 np_ffi::internal::np_ffi_deallocate_v0_payload(this->v0_payload_);
392 assert_panic(result == np_ffi::internal::DeallocateResult::Success);
393 }
394 }
395
TryGetDataElement(const uint8_t index) const396 absl::StatusOr<V0DataElement> V0Payload::TryGetDataElement(
397 const uint8_t index) const {
398 assert_panic(!this->moved_);
399 auto result = np_ffi::internal::np_ffi_V0Payload_get_de(v0_payload_, index);
400 auto kind = np_ffi::internal::np_ffi_GetV0DEResult_kind(result);
401 switch (kind) {
402 case GetV0DEResultKind::Success: {
403 auto de = np_ffi_GetV0DEResult_into_SUCCESS(result);
404 return V0DataElement(de);
405 }
406 case GetV0DEResultKind::Error: {
407 return absl::OutOfRangeError("Invalid Data Element index");
408 }
409 }
410 }
411
MetadataResultToVec(np_ffi::internal::DecryptMetadataResult decrypt_result)412 absl::StatusOr<std::vector<uint8_t>> MetadataResultToVec(
413 np_ffi::internal::DecryptMetadataResult decrypt_result) {
414 auto kind =
415 np_ffi::internal::np_ffi_DecryptMetadataResult_kind(decrypt_result);
416 switch (kind) {
417 case np_ffi::internal::DecryptMetadataResultKind::Success: {
418 auto metadata =
419 np_ffi::internal::np_ffi_DecryptMetadataResult_into_SUCCESS(
420 decrypt_result);
421 auto parts_result =
422 np_ffi::internal::np_ffi_DecryptedMetadata_get_metadata_buffer_parts(
423 metadata);
424 // The handle is guaranteed to be valid by the C++ wrapper so this should
425 // never fail
426 assert_panic(np_ffi::internal::np_ffi_GetMetadataBufferPartsResult_kind(
427 parts_result) ==
428 np_ffi::internal::GetMetadataBufferPartsResultKind::Success);
429 auto parts =
430 np_ffi::internal::np_ffi_GetMetadataBufferPartsResult_into_SUCCESS(
431 parts_result);
432 std::vector<uint8_t> result(parts.ptr, parts.ptr + parts.len);
433
434 // Now that the contents have been copied into the vec, the underlying
435 // handle can be de-allocated
436 auto deallocate_result =
437 np_ffi::internal::np_ffi_deallocate_DecryptedMetadata(metadata);
438 assert_panic(deallocate_result ==
439 np_ffi::internal::DeallocateResult::Success);
440 return result;
441 }
442 case np_ffi::internal::DecryptMetadataResultKind::Error: {
443 return absl::InvalidArgumentError(
444 "Decrypt attempt failed, identity is missing or invalid");
445 }
446 }
447 }
448
TryGetIdentityDetails() const449 absl::StatusOr<DeserializedV0IdentityDetails> V0Payload::TryGetIdentityDetails()
450 const {
451 assert_panic(!this->moved_);
452 auto result = np_ffi::internal::np_ffi_V0Payload_get_identity_details(
453 this->v0_payload_);
454 auto kind = np_ffi::internal::np_ffi_GetV0IdentityDetailsResult_kind(result);
455 switch (kind) {
456 case np_ffi::internal::GetV0IdentityDetailsResultKind::Error: {
457 return absl::InvalidArgumentError(
458 "Identity details not available for public advertisements");
459 }
460 case np_ffi::internal::GetV0IdentityDetailsResultKind::Success: {
461 return np_ffi::internal::np_ffi_GetV0IdentityDetailsResult_into_SUCCESS(
462 result);
463 }
464 }
465 }
466
TryDecryptMetadata() const467 absl::StatusOr<std::vector<uint8_t>> V0Payload::TryDecryptMetadata() const {
468 assert_panic(!this->moved_);
469 auto decrypt_result =
470 np_ffi::internal::np_ffi_V0Payload_decrypt_metadata(this->v0_payload_);
471 return MetadataResultToVec(decrypt_result);
472 }
473
GetKind() const474 V0DataElementKind V0DataElement::GetKind() const {
475 return np_ffi::internal::np_ffi_V0DataElement_kind(v0_data_element_);
476 }
477
AsTxPower() const478 TxPower V0DataElement::AsTxPower() const {
479 return TxPower(
480 np_ffi::internal::np_ffi_V0DataElement_into_TX_POWER(v0_data_element_));
481 }
482
AsActions() const483 V0Actions V0DataElement::AsActions() const {
484 auto internal =
485 np_ffi::internal::np_ffi_V0DataElement_into_ACTIONS(v0_data_element_);
486 return V0Actions(internal);
487 }
488
V0DataElement(TxPower tx_power)489 V0DataElement::V0DataElement(TxPower tx_power) {
490 v0_data_element_ =
491 np_ffi::internal::np_ffi_TxPower_into_V0DataElement(tx_power.tx_power_);
492 }
493
V0DataElement(V0Actions actions)494 V0DataElement::V0DataElement(V0Actions actions) {
495 v0_data_element_ =
496 np_ffi::internal::np_ffi_V0Actions_into_V0DataElement(actions.actions_);
497 }
498
GetAsU32() const499 uint32_t V0Actions::GetAsU32() const {
500 return np_ffi::internal::np_ffi_V0Actions_as_u32(actions_);
501 }
502
HasAction(ActionType action) const503 bool V0Actions::HasAction(ActionType action) const {
504 return np_ffi::internal::np_ffi_V0Actions_has_action(actions_, action);
505 }
506
TrySetAction(ActionType action,bool value)507 absl::Status V0Actions::TrySetAction(ActionType action, bool value) {
508 auto result =
509 np_ffi::internal::np_ffi_V0Actions_set_action(actions_, action, value);
510 auto kind = np_ffi::internal::np_ffi_SetV0ActionResult_kind(result);
511 switch (kind) {
512 case np_ffi::internal::SetV0ActionResultKind::Success: {
513 actions_ =
514 np_ffi::internal::np_ffi_SetV0ActionResult_into_SUCCESS(result);
515 return absl::OkStatus();
516 }
517 case np_ffi::internal::SetV0ActionResultKind::Error: {
518 actions_ = np_ffi::internal::np_ffi_SetV0ActionResult_into_ERROR(result);
519 return absl::InvalidArgumentError(
520 "The requested action bit may not be set for the requested adv "
521 "encoding");
522 }
523 }
524 }
525
BuildNewZeroed(AdvertisementBuilderKind kind)526 V0Actions V0Actions::BuildNewZeroed(AdvertisementBuilderKind kind) {
527 auto actions = np_ffi::internal::np_ffi_build_new_zeroed_V0Actions(kind);
528 return V0Actions(actions);
529 }
530
GetAsI8() const531 int8_t TxPower::GetAsI8() const {
532 return np_ffi::internal::np_ffi_TxPower_as_signed_byte(tx_power_);
533 }
534
TryBuildFromI8(int8_t value)535 absl::StatusOr<TxPower> TxPower::TryBuildFromI8(int8_t value) {
536 auto result = np_ffi::internal::np_ffi_TxPower_build_from_signed_byte(value);
537 auto kind = np_ffi::internal::np_ffi_BuildTxPowerResult_kind(result);
538 switch (kind) {
539 case np_ffi::internal::BuildTxPowerResultKind::Success: {
540 return TxPower(
541 np_ffi::internal::np_ffi_BuildTxPowerResult_into_SUCCESS(result));
542 }
543 case np_ffi::internal::BuildTxPowerResultKind::OutOfRange: {
544 return absl::InvalidArgumentError(
545 "Could not build a tx power for the requested byte value.");
546 }
547 }
548 }
549
550 // This is called after all references to the shared_ptr have gone out of
551 // scope
DeallocateV1Adv(np_ffi::internal::DeserializedV1Advertisement * v1_advertisement)552 auto DeallocateV1Adv(
553 np_ffi::internal::DeserializedV1Advertisement* v1_advertisement) {
554 auto result =
555 np_ffi::internal::np_ffi_deallocate_deserialized_V1_advertisement(
556 *v1_advertisement);
557 assert_panic(result == np_ffi::internal::DeallocateResult::Success);
558 delete v1_advertisement;
559 }
560
DeserializedV1Advertisement(np_ffi::internal::DeserializedV1Advertisement v1_advertisement)561 DeserializedV1Advertisement::DeserializedV1Advertisement(
562 np_ffi::internal::DeserializedV1Advertisement v1_advertisement) {
563 v1_advertisement_ =
564 std::shared_ptr<np_ffi::internal::DeserializedV1Advertisement>(
565 new np_ffi::internal::DeserializedV1Advertisement(v1_advertisement),
566 DeallocateV1Adv);
567 }
568
DeserializedV1Advertisement(DeserializedV1Advertisement && other)569 DeserializedV1Advertisement::DeserializedV1Advertisement(
570 DeserializedV1Advertisement&& other) noexcept
571 : v1_advertisement_(std::move(other.v1_advertisement_)) {}
572
operator =(DeserializedV1Advertisement && other)573 DeserializedV1Advertisement& DeserializedV1Advertisement::operator=(
574 DeserializedV1Advertisement&& other) noexcept {
575 if (this != &other) {
576 this->v1_advertisement_ = std::move(other.v1_advertisement_);
577 }
578 return *this;
579 }
580
581 // V1 Stuff
GetNumLegibleSections() const582 uint8_t DeserializedV1Advertisement::GetNumLegibleSections() const {
583 assert_panic(this->v1_advertisement_ != nullptr);
584 return np_ffi::internal::
585 np_ffi_DeserializedV1Advertisement_get_num_legible_sections(
586 *v1_advertisement_);
587 }
588
GetNumUndecryptableSections() const589 uint8_t DeserializedV1Advertisement::GetNumUndecryptableSections() const {
590 assert_panic(this->v1_advertisement_ != nullptr);
591 return np_ffi::internal::
592 np_ffi_DeserializedV1Advertisement_get_num_undecryptable_sections(
593 *v1_advertisement_);
594 }
595
596 absl::StatusOr<DeserializedV1Section>
TryGetSection(const uint8_t section_index) const597 DeserializedV1Advertisement::TryGetSection(const uint8_t section_index) const {
598 assert_panic(this->v1_advertisement_ != nullptr);
599 auto result =
600 np_ffi::internal::np_ffi_DeserializedV1Advertisement_get_section(
601 *v1_advertisement_, section_index);
602 auto kind = np_ffi::internal::np_ffi_GetV1SectionResult_kind(result);
603 switch (kind) {
604 case np_ffi::internal::GetV1SectionResultKind::Error: {
605 return absl::OutOfRangeError("Invalid section index");
606 }
607 case np_ffi::internal::GetV1SectionResultKind::Success: {
608 auto section =
609 np_ffi::internal::np_ffi_GetV1SectionResult_into_SUCCESS(result);
610 return DeserializedV1Section(section, v1_advertisement_);
611 }
612 }
613 }
614
NumberOfDataElements() const615 uint8_t DeserializedV1Section::NumberOfDataElements() const {
616 return np_ffi::internal::np_ffi_DeserializedV1Section_get_num_des(section_);
617 }
618
GetIdentityKind() const619 DeserializedV1IdentityKind DeserializedV1Section::GetIdentityKind() const {
620 return np_ffi::internal::np_ffi_DeserializedV1Section_get_identity_kind(
621 section_);
622 }
623
TryGetDataElement(const uint8_t index) const624 absl::StatusOr<V1DataElement> DeserializedV1Section::TryGetDataElement(
625 const uint8_t index) const {
626 auto result =
627 np_ffi::internal::np_ffi_DeserializedV1Section_get_de(section_, index);
628 auto kind = np_ffi::internal::np_ffi_GetV1DEResult_kind(result);
629 switch (kind) {
630 case np_ffi::internal::GetV1DEResultKind::Error: {
631 return absl::OutOfRangeError(
632 "Invalid data element index for this section");
633 }
634 case np_ffi::internal::GetV1DEResultKind::Success: {
635 return V1DataElement(
636 np_ffi::internal::np_ffi_GetV1DEResult_into_SUCCESS(result));
637 }
638 }
639 }
640
TryDecryptMetadata() const641 absl::StatusOr<std::vector<uint8_t>> DeserializedV1Section::TryDecryptMetadata()
642 const {
643 assert_panic(this->owning_v1_advertisement_ != nullptr);
644 auto decrypt_result =
645 np_ffi::internal::np_ffi_DeserializedV1Section_decrypt_metadata(
646 this->section_);
647 return MetadataResultToVec(decrypt_result);
648 }
649
650 absl::StatusOr<DeserializedV1IdentityDetails>
GetIdentityDetails() const651 DeserializedV1Section::GetIdentityDetails() const {
652 assert_panic(this->owning_v1_advertisement_ != nullptr);
653 auto result =
654 np_ffi::internal::np_ffi_DeserializedV1Section_get_identity_details(
655 this->section_);
656 auto kind = np_ffi::internal::np_ffi_GetV1IdentityDetailsResult_kind(result);
657 switch (kind) {
658 case np_ffi::internal::GetV1IdentityDetailsResultKind::Error: {
659 return absl::InvalidArgumentError(
660 "Identity details are not available for public advertisements");
661 }
662 case np_ffi::internal::GetV1IdentityDetailsResultKind::Success: {
663 return np_ffi::internal::np_ffi_GetV1IdentityDetailsResult_into_SUCCESS(
664 result);
665 }
666 }
667 }
668
669 absl::StatusOr<std::array<uint8_t, DERIVED_SALT_SIZE>>
DeriveSaltForOffset(const uint8_t offset) const670 DeserializedV1Section::DeriveSaltForOffset(const uint8_t offset) const {
671 auto result = np_ffi::internal::
672 np_ffi_DeserializedV1Section_derive_16_byte_salt_for_offset(
673 this->section_, offset);
674 auto kind = np_ffi::internal::np_ffi_GetV1DE16ByteSaltResult_kind(result);
675 switch (kind) {
676 case np_ffi::internal::GetV1DE16ByteSaltResultKind::Error: {
677 return absl::InvalidArgumentError("Failed to derive salt for offset");
678 }
679 case np_ffi::internal::GetV1DE16ByteSaltResultKind::Success: {
680 auto buffer =
681 np_ffi::internal::np_ffi_GetV1DE16ByteSaltResult_into_SUCCESS(result);
682 return std::to_array(buffer._0);
683 }
684 }
685 }
686
GetDataElementTypeCode() const687 uint32_t V1DataElement::GetDataElementTypeCode() const {
688 return np_ffi::internal::np_ffi_V1DEType_to_uint32_t(
689 np_ffi::internal::np_ffi_V1DataElement_to_generic(this->v1_data_element_)
690 .de_type);
691 }
692
GetPayload() const693 ByteBuffer<MAX_V1_DE_PAYLOAD_SIZE> V1DataElement::GetPayload() const {
694 return ByteBuffer(
695 np_ffi::internal::np_ffi_V1DataElement_to_generic(this->v1_data_element_)
696 .payload);
697 }
698
GetOffset() const699 uint8_t V1DataElement::GetOffset() const {
700 return np_ffi::internal::np_ffi_V1DataElement_to_generic(
701 this->v1_data_element_)
702 .offset;
703 }
704
MatchedCredentialData(const uint32_t cred_id,std::span<const uint8_t> metadata_bytes)705 MatchedCredentialData::MatchedCredentialData(
706 const uint32_t cred_id, std::span<const uint8_t> metadata_bytes) {
707 this->data_ = {cred_id, metadata_bytes.data(), metadata_bytes.size()};
708 }
709
710 template <typename T, size_t N>
CopyToRawArray(T (& dest)[N],const std::array<T,N> & src)711 void CopyToRawArray(T (&dest)[N], const std::array<T, N>& src) {
712 memcpy(dest, src.data(), sizeof(T) * N);
713 }
714
V0MatchableCredential(const std::array<uint8_t,32> key_seed,const std::array<uint8_t,32> legacy_metadata_key_hmac,const MatchedCredentialData matched_credential_data)715 V0MatchableCredential::V0MatchableCredential(
716 const std::array<uint8_t, 32> key_seed,
717 const std::array<uint8_t, 32> legacy_metadata_key_hmac,
718 const MatchedCredentialData matched_credential_data) {
719 np_ffi::internal::V0DiscoveryCredential discovery_cred{};
720 CopyToRawArray(discovery_cred.key_seed, key_seed);
721 CopyToRawArray(discovery_cred.identity_token_hmac, legacy_metadata_key_hmac);
722 this->internal_ = {discovery_cred, matched_credential_data.data_};
723 }
724
V1MatchableCredential(const std::array<uint8_t,32> key_seed,const std::array<uint8_t,32> expected_mic_extended_salt_identity_token_hmac,const std::array<uint8_t,32> expected_signature_identity_token_hmac,const std::array<uint8_t,32> pub_key,const MatchedCredentialData matched_credential_data)725 V1MatchableCredential::V1MatchableCredential(
726 const std::array<uint8_t, 32> key_seed,
727 const std::array<uint8_t, 32>
728 expected_mic_extended_salt_identity_token_hmac,
729 const std::array<uint8_t, 32> expected_signature_identity_token_hmac,
730 const std::array<uint8_t, 32> pub_key,
731 const MatchedCredentialData matched_credential_data) {
732 np_ffi::internal::V1DiscoveryCredential discovery_cred{};
733 CopyToRawArray(discovery_cred.key_seed, key_seed);
734 CopyToRawArray(discovery_cred.expected_mic_extended_salt_identity_token_hmac,
735 expected_mic_extended_salt_identity_token_hmac);
736 CopyToRawArray(discovery_cred.expected_signature_identity_token_hmac,
737 expected_signature_identity_token_hmac);
738 CopyToRawArray(discovery_cred.pub_key, pub_key);
739 this->internal_ = {discovery_cred, matched_credential_data.data_};
740 }
741
V0BroadcastCredential(std::array<uint8_t,32> key_seed,std::array<uint8_t,14> identity_token)742 V0BroadcastCredential::V0BroadcastCredential(
743 std::array<uint8_t, 32> key_seed, std::array<uint8_t, 14> identity_token) {
744 CopyToRawArray(internal_.key_seed, key_seed);
745 CopyToRawArray(internal_.identity_token, identity_token);
746 }
747
~V0AdvertisementBuilder()748 V0AdvertisementBuilder::~V0AdvertisementBuilder() {
749 if (!this->moved_) {
750 auto result = np_ffi::internal::np_ffi_deallocate_v0_advertisement_builder(
751 adv_builder_);
752 assert_panic(result == np_ffi::internal::DeallocateResult::Success);
753 }
754 }
755
V0AdvertisementBuilder(V0AdvertisementBuilder && other)756 V0AdvertisementBuilder::V0AdvertisementBuilder(
757 V0AdvertisementBuilder&& other) noexcept
758 : adv_builder_(other.adv_builder_), moved_(other.moved_) {
759 other.adv_builder_ = {};
760 other.moved_ = true;
761 }
762
operator =(V0AdvertisementBuilder && other)763 V0AdvertisementBuilder& V0AdvertisementBuilder::operator=(
764 V0AdvertisementBuilder&& other) noexcept {
765 if (this != &other) {
766 if (!this->moved_) {
767 auto result =
768 np_ffi::internal::np_ffi_deallocate_v0_advertisement_builder(
769 this->adv_builder_);
770 assert_panic(result == np_ffi::internal::DeallocateResult::Success);
771 }
772
773 this->adv_builder_ = other.adv_builder_;
774 this->moved_ = other.moved_;
775
776 other.adv_builder_ = {};
777 other.moved_ = true;
778 }
779 return *this;
780 }
781
TryAddDE(V0DataElement de)782 absl::Status V0AdvertisementBuilder::TryAddDE(V0DataElement de) {
783 assert_panic(!this->moved_);
784 auto result = np_ffi::internal::np_ffi_V0AdvertisementBuilder_add_de(
785 this->adv_builder_, de.v0_data_element_);
786 switch (result) {
787 case AddV0DEResult::Success: {
788 return absl::OkStatus();
789 }
790 case AddV0DEResult::InvalidAdvertisementBuilderHandle: {
791 return absl::InvalidArgumentError(
792 "invalid v0 advertisement builder handle provided");
793 }
794 case AddV0DEResult::InsufficientAdvertisementSpace: {
795 return absl::ResourceExhaustedError(
796 "insufficient advertisement space to add DE");
797 }
798 case AddV0DEResult::InvalidIdentityTypeForDataElement: {
799 return absl::InvalidArgumentError(
800 "the DE may not be added to this advertisement builder due to an "
801 "identity type mismatch");
802 }
803 }
804 }
805
806 [[nodiscard]] absl::StatusOr<ByteBuffer<24>>
TrySerialize()807 V0AdvertisementBuilder::TrySerialize() {
808 assert_panic(!this->moved_);
809 auto result =
810 np_ffi::internal::np_ffi_V0AdvertisementBuilder_into_advertisement(
811 this->adv_builder_);
812 auto kind =
813 np_ffi::internal::np_ffi_SerializeV0AdvertisementResult_kind(result);
814 // Regardless of what happens, we've invalidated the original adv builder.
815 this->moved_ = true;
816
817 switch (kind) {
818 case SerializeV0AdvertisementResultKind::Success: {
819 auto bytes =
820 np_ffi::internal::np_ffi_SerializeV0AdvertisementResult_into_SUCCESS(
821 result);
822 return ByteBuffer(bytes);
823 }
824 case SerializeV0AdvertisementResultKind::LdtError: {
825 return absl::OutOfRangeError(
826 "The advertisement contents were not of a proper size for LDT "
827 "encryption");
828 }
829 case SerializeV0AdvertisementResultKind::UnencryptedError: {
830 return absl::OutOfRangeError(
831 "The advertisement contents did not meet the length requirements");
832 }
833 case SerializeV0AdvertisementResultKind::
834 InvalidAdvertisementBuilderHandle: {
835 return absl::InvalidArgumentError(
836 "The advertisement builder handle was invalid");
837 }
838 }
839 }
840
CreatePublic()841 [[nodiscard]] V0AdvertisementBuilder V0AdvertisementBuilder::CreatePublic() {
842 auto result =
843 np_ffi::internal::np_ffi_create_v0_public_advertisement_builder();
844 return V0AdvertisementBuilder(result);
845 }
846
847 template <uintptr_t N>
ToFFIArray(std::array<uint8_t,N> value)848 [[nodiscard]] np_ffi::internal::FixedSizeArray<N> ToFFIArray(
849 std::array<uint8_t, N> value) {
850 np_ffi::internal::FixedSizeArray<N> result;
851 std::copy(std::begin(value), std::end(value), result._0);
852 return result;
853 }
854
CreateEncrypted(V0BroadcastCredential broadcast_cred,std::array<uint8_t,2> salt)855 [[nodiscard]] V0AdvertisementBuilder V0AdvertisementBuilder::CreateEncrypted(
856 V0BroadcastCredential broadcast_cred, std::array<uint8_t, 2> salt) {
857 auto result =
858 np_ffi::internal::np_ffi_create_v0_encrypted_advertisement_builder(
859 broadcast_cred.internal_, ToFFIArray(salt));
860 return V0AdvertisementBuilder(result);
861 }
862
863 } // namespace nearby_protocol
864