Merge pull request #1778 from shwetathecodewarrior/main

Removing advetisement related files from SDK
This commit is contained in:
shwetathecodewarrior
2023-06-06 17:33:09 +00:00
committed by GitHub
5 changed files with 0 additions and 1471 deletions
-202
View File
@@ -1,202 +0,0 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// clang-format off
#include "nearby.h"
#include "nearby_assert.h"
#include "nearby_config.h"
// clang-format on
#include "nearby_advert.h"
#include "nearby_fp_library.h"
#include "nearby_platform_ble.h"
#include "nearby_platform_os.h"
#include "nearby_spot.h"
#include "nearby_trace.h"
#ifdef NEARBY_FP_HAVE_MULTIPLE_ADVERTISEMENTS
nearby_platform_status nearby_SetFpAdvertisement(
const uint8_t* payload, size_t length,
nearby_fp_AvertisementInterval interval) {
return nearby_platform_SetAdvertisement(payload, length, interval);
}
#if NEARBY_FP_ENABLE_SPOT
nearby_platform_status nearby_SetSpotAdvertisement(uint64_t address,
const uint8_t* payload, size_t length) {
return nearby_platform_SetSpotAdvertisement(address, payload, length);
}
#endif /* NEARBY_FP_ENABLE_SPOT */
#else
static nearby_platform_status SetSpotAdvertisement();
static nearby_platform_status SetFpAdvertisement();
static uint8_t fp_advertisement[NON_DISCOVERABLE_ADV_SIZE_BYTES];
static size_t fp_length;
static nearby_fp_AvertisementInterval fp_interval;
#if NEARBY_FP_ENABLE_SPOT
// Time needed for the BLE controller to emit one SPOT advertisement. As little
// as 50ms should be enough but it's safer to give the controller a little bit
// more time.
#define SPOT_EMIT_TIME_MS 100
// SPOT advertisements should be emitted every 2 seconds
#define SPOT_EMIT_INTERVAL_MS 2000
typedef enum {
kRotationNoAdvertisement,
kRotationFastPair,
kRotationSpot
} Rotation;
static Rotation rotation = kRotationNoAdvertisement;
static void* timer_task;
static uint8_t spot_advertisement[NEARBY_SPOT_ADVERTISEMENT_SIZE];
static size_t spot_length;
#endif /* NEARBY_FP_ENABLE_SPOT */
static bool HasFpAdvertisement() { return fp_length != 0; }
static bool HasSpotAdvertisement() {
#if NEARBY_FP_ENABLE_SPOT
return spot_length != 0;
#else
return false;
#endif /* NEARBY_FP_ENABLE_SPOT */
}
#if NEARBY_FP_ENABLE_SPOT
static void CancelTask() {
void* task = timer_task;
timer_task = NULL;
if (task != NULL) {
nearby_platform_status status = nearby_platform_CancelTimer(task);
if (kNearbyStatusOK != status) {
NEARBY_TRACE(ERROR,
"Error canceling advertisement rotation task, status %d",
status);
}
}
}
static uint32_t GetDelayMs() {
return rotation == kRotationSpot ? SPOT_EMIT_TIME_MS
: SPOT_EMIT_INTERVAL_MS - SPOT_EMIT_TIME_MS;
}
static void RotateAdvertisements();
static void ScheduleTask() {
timer_task = nearby_platform_StartTimer(RotateAdvertisements, GetDelayMs());
if (timer_task == NULL) {
NEARBY_TRACE(ERROR, "Failed to schedule advertisement rotation");
}
}
static void RotateAdvertisements() {
if (rotation == kRotationFastPair) {
SetSpotAdvertisement();
} else if (rotation == kRotationSpot) {
SetFpAdvertisement();
}
ScheduleTask();
}
#endif /* NEARBY_FP_ENABLE_SPOT */
static void RescheduleAdvertisementRotation() {
#if NEARBY_FP_ENABLE_SPOT
CancelTask();
if (fp_length == 0 || spot_length == 0) {
// Only one advertisement, no need to rotate
return;
}
ScheduleTask();
#endif /* NEARBY_FP_ENABLE_SPOT */
}
static nearby_platform_status SetSpotAdvertisement() {
#if NEARBY_FP_ENABLE_SPOT
const void* payload = HasSpotAdvertisement() ? spot_advertisement : NULL;
rotation = HasSpotAdvertisement() ? kRotationSpot : kRotationNoAdvertisement;
return nearby_platform_SetAdvertisement(payload, spot_length,
kNoLargerThan2Seconds);
#else
return kNearbyStatusUnsupported;
#endif /* NEARBY_FP_ENABLE_SPOT */
}
static nearby_platform_status SetFpAdvertisement() {
#if NEARBY_FP_ENABLE_SPOT
rotation =
HasFpAdvertisement() ? kRotationFastPair : kRotationNoAdvertisement;
#endif /* NEARBY_FP_ENABLE_SPOT */
const void* payload = HasFpAdvertisement() ? fp_advertisement : NULL;
return nearby_platform_SetAdvertisement(payload, fp_length, fp_interval);
}
static nearby_platform_status DisableAdvertisement() {
return nearby_platform_SetAdvertisement(NULL, 0, kDisabled);
}
nearby_platform_status nearby_SetFpAdvertisement(
const uint8_t* payload, size_t length,
nearby_fp_AvertisementInterval interval) {
nearby_platform_status status;
NEARBY_ASSERT(length <= sizeof(fp_advertisement));
if (payload != NULL) memcpy(fp_advertisement, payload, length);
fp_length = length;
fp_interval = interval;
if (HasFpAdvertisement()) {
status = SetFpAdvertisement();
} else if (HasSpotAdvertisement()) {
status = SetSpotAdvertisement();
} else {
status = DisableAdvertisement();
}
RescheduleAdvertisementRotation();
return status;
}
#if NEARBY_FP_ENABLE_SPOT
nearby_platform_status nearby_SetSpotAdvertisement(uint64_t address,
const uint8_t* payload, size_t length) {
nearby_platform_status status;
NEARBY_ASSERT(length <= sizeof(spot_advertisement));
if (payload != NULL) memcpy(spot_advertisement, payload, length);
spot_length = length;
if (HasSpotAdvertisement()) {
status = SetSpotAdvertisement();
} else if (HasFpAdvertisement()) {
status = SetFpAdvertisement();
} else {
status = DisableAdvertisement();
}
RescheduleAdvertisementRotation();
return status;
}
#endif /* NEARBY_FP_ENABLE_SPOT */
#endif /* NEARBY_FP_HAVE_MULTIPLE_ADVERTISEMENTS */
void nearby_AdvertInit() {
#ifndef NEARBY_FP_HAVE_MULTIPLE_ADVERTISEMENTS
fp_length = 0;
#if NEARBY_FP_ENABLE_SPOT
timer_task = NULL;
spot_length = 0;
#endif /* NEARBY_FP_ENABLE_SPOT */
#endif /* NEARBY_FP_HAVE_MULTIPLE_ADVERTISEMENTS */
}
-55
View File
@@ -1,55 +0,0 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This module provides a workaround for interleaving Fast Pair and SPOT
// advertisements on platforms that don't have native support.
// The flow when both advertisements are enabled is:
// 1. Start advertising SPOT
// 2. Set a small timer but long enough for one advertisement to be emitted.
// 3. Start advertising Fast Pair
// 4. Set a longer (2 seconds) timer and repeat the cycle.
//
// There is no cycling between advertisements when only one (or none) is
// enabled, of course.
//
// If the platform has native support for mulitiple advertisements, then this
// module is a no-op pass-through to native implementation.
#ifndef NEARBY_ADVERT_H
#define NEARBY_ADVERT_H
// clang-format off
#include "nearby_config.h"
// clang-format on
#include "nearby.h"
#include "nearby_platform_ble.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
nearby_platform_status nearby_SetFpAdvertisement(
const uint8_t* payload, size_t length,
nearby_fp_AvertisementInterval interval);
nearby_platform_status nearby_SetSpotAdvertisement(uint64_t address,
const uint8_t* payload, size_t length);
void nearby_AdvertInit();
#ifdef __cplusplus
}
#endif
#endif /* NEARBY_ADVERT_H */
-876
View File
@@ -1,876 +0,0 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// clang-format off
#include "nearby_config.h"
// clang-format on
#include "nearby_spot.h"
#include "nearby.h"
#include "nearby_advert.h"
#include "nearby_assert.h"
#include "nearby_fp_library.h"
#include "nearby_platform_ble.h"
#include "nearby_platform_bt.h"
#include "nearby_platform_os.h"
#include "nearby_platform_persistence.h"
#include "nearby_platform_se.h"
#include "nearby_trace.h"
#include "nearby_utils.h"
#ifdef NEARBY_SPOT_BATTERY_LEVEL_INDICATION
#include "nearby_platform_battery.h"
#endif
#if NEARBY_FP_ENABLE_SPOT
#define STATUS_UNAUTHENTICATED 0x80
#define STATUS_INVALID_VALUE 0x81
#define STATUS_NO_USER_CONSENT 0x82
#define BEACON_NONCE_SIZE 8
#define REQUEST_READ_BEACON_PARAMETERS 0x00
#define REQUEST_READ_PROVISIONING_STATE 0x01
#define REQUEST_SET_EPHEMERAL_KEY 0x02
#define REQUEST_CLEAR_EPHEMERAL_KEY 0x03
#define REQUEST_READ_EPHEMERAL_KEY 0x04
#define REQUEST_RING 0x05
#define REQUEST_READ_RINGING_STATE 0x06
#define REQUEST_ACTIVATE_UNWANTED_TRACKING_PROTECTION 0x07
#define REQUEST_DEACTIVATE_UNWANTED_TRACKING_PROTECTION 0x08
#define RESPONSE_READ_BEACON_PARAMETERS 0x00
#define RESPONSE_READ_PROVISIONING_STATE 0x01
#define RESPONSE_SET_EPHEMERAL_KEY 0x02
#define RESPONSE_CLEAR_EPHEMERAL_KEY 0x03
#define RESPONSE_READ_EPHEMERAL_KEY 0x04
#define RESPONSE_RING_STATE_CHANGE 0x05
#define RESPONSE_READ_RINGING_STATE 0x06
#define RESPONSE_ACTIVATE_UNWANTED_TRACKING_PROTECTION 0x07
#define RESPONSE_DEACTIVATE_UNWANTED_TRACKING_PROTECTION 0x08
#define FRAME_TYPE_SIZE 1
#define FRAME_TYPE 0x40
#define FRAME_TYPE_WITH_UNWANTED_TRACKING_PROTECTION 0x41
#define REQUEST_HEADER_SIZE 2
#define RESPONSE_HEADER_SIZE 2
// The size of authentication key in the write requests
#define AUTH_KEY_SIZE 8
#define EPHEMERAL_KEY_SIZE 32
#define RECOVERY_KEY_SUFFIX 0x01
#define RING_KEY_SUFFIX 0x02
#define UNWANTED_TRACKING_PROTECTION_KEY_SUFFIX 0x03
// The size of `ring` request data.
#define RING_STATE_SIZE 3
#define ROTATION_PERIOD_EXPONENT 10
#define CLOCK_MASK (~((1 << (ROTATION_PERIOD_EXPONENT)) - 1))
#define BEACON_PROTOCOL_VERSION_SIZE 1
#ifdef NEARBY_SPOT_BATTERY_LEVEL_INDICATION
#define BATTERY_LEVEL_UNSUPPORTED_MASK 0x00
#define BATTERY_LEVEL_NORMAL_MASK 0x02
#define BATTERY_LEVEL_LOW_MASK 0x04
#define BATTERY_LEVEL_CRITICALLY_LOW_MASK 0x06
#endif /* NEARBY_SPOT_BATTERY_LEVEL_INDICATION */
#define CONTROL_FLAG_SKIP_RINGING_AUTHENTICATION 0x01
static const uint8_t frame_header[] = {2, 1, 6, 24, 0x16, 0xAA, 0xFE};
static uint64_t remote_address;
static bool has_nonce;
static uint8_t nonce[BEACON_NONCE_SIZE];
static bool has_owner_key;
static uint8_t owner_key[ACCOUNT_KEY_SIZE_BYTES];
static bool has_ephemeral_key;
static uint8_t ephemeral_key[EPHEMERAL_KEY_SIZE];
static uint8_t ephemeral_id[EPHEMERAL_ID_SIZE];
static uint8_t hashed_field_lsb;
static uint8_t unwanted_tracking_protection_mode = 0;
uint8_t account_key_index = 0xFF;
static uint8_t control_flags = 0x00;
static uint64_t ringing_peer_address = 0x00;
#if NEARBY_SUPPORT_MULTIPLE_BLE_ADDRESSES
static uint64_t spot_randomized_address = 0;
static unsigned int spot_address_rotation_timestamp = 0;
#endif /* NEARBY_SUPPORT_MULTIPLE_BLE_ADDRESSES */
#define RETURN_IF_ERROR(X) \
do { \
nearby_platform_status status = X; \
if (kNearbyStatusOK != status) return status; \
} while (0)
#ifdef NEARBY_SPOT_BATTERY_LEVEL_INDICATION
static uint8_t nearby_spot_GetBatteryLevel(void) {
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
nearby_platform_status status;
nearby_platform_BatteryInfo battery_info;
// set to incorrect battery level
battery_info.battery_level[MAIN_BATTERY_LEVEL_INDEX] = 0xFF;
status = nearby_platform_GetBatteryInfo(&battery_info);
// Read the battery level (lower 7 bits)
uint8_t main_battery_lev = NEARBY_PLATFORM_GET_BATTERY_LEVEL(
battery_info.battery_level[MAIN_BATTERY_LEVEL_INDEX]);
if ((status == kNearbyStatusOK) && (main_battery_lev != 0xFF)) {
if (main_battery_lev <= NEARBY_SPOT_BATTERY_LEVEL_CRITICALLY_LOW_THRESHOLD)
return BATTERY_LEVEL_CRITICALLY_LOW_MASK;
else if (main_battery_lev <= NEARBY_SPOT_BATTERY_LEVEL_LOW_THRESHOLD)
return BATTERY_LEVEL_LOW_MASK;
else
return BATTERY_LEVEL_NORMAL_MASK;
}
#endif
return BATTERY_LEVEL_UNSUPPORTED_MASK;
}
#endif // NEARBY_SPOT_BATTERY_LEVEL_INDICATION
void nearby_spot_SetUnwantedTrackingProtectionMode(bool activate) {
if (activate)
unwanted_tracking_protection_mode = 0x01;
else
unwanted_tracking_protection_mode = 0x00;
}
bool nearby_spot_GetUnwantedTrackingProtectionModeState(void) {
return unwanted_tracking_protection_mode;
}
uint8_t nearby_spot_GetControlFlags(void) { return control_flags; }
nearby_platform_status nearby_spot_GetEid(uint8_t* eid_ptr) {
if (has_ephemeral_key && eid_ptr != NULL)
memcpy(eid_ptr, &ephemeral_id, EPHEMERAL_ID_SIZE);
else
return kNearbyStatusInvalidInput;
return kNearbyStatusOK;
}
// Returns true if the `key` matches.
static bool VerifyKey(const uint8_t* key, size_t key_size,
const uint8_t* auth_key) {
uint8_t hash[32];
RETURN_IF_ERROR(nearby_platform_Sha256Start());
RETURN_IF_ERROR(nearby_platform_Sha256Update(key, key_size));
RETURN_IF_ERROR(nearby_platform_Sha256Update(nonce, sizeof(nonce)));
RETURN_IF_ERROR(nearby_platform_Sha256Finish(hash));
bool result = memcmp(hash, auth_key, AUTH_KEY_SIZE) == 0;
if (!result) {
NEARBY_TRACE(DEBUG, "Auth key: %s",
nearby_utils_ArrayToString(auth_key, AUTH_KEY_SIZE));
NEARBY_TRACE(DEBUG, "Key: %s", nearby_utils_ArrayToString(key, key_size));
NEARBY_TRACE(DEBUG, "Nonce: %s",
nearby_utils_ArrayToString(nonce, sizeof(nonce)));
}
NEARBY_TRACE(DEBUG, "VerifyKey: %s", result ? "OK" : "failed");
return result;
}
// The function verifies or generate the auth key and returns ture
// auth_key = HMAC-SHA256(key, major protocol version || nonce || data ID
// || data lenght || additional data)
static bool VerifyOrGenerateAuthenticationKey(const uint8_t* key,
size_t key_size,
uint8_t* auth_key,
const uint8_t* payload,
bool generate) {
nearby_fp_HmacSha256Context context;
uint8_t protocol_major_version = SPOT_BEACON_PROTOCOL_MAJOR_VERSION;
uint8_t additional_data_length = (payload[1] - AUTH_KEY_SIZE);
// The first 8 bytes of HMAC-SHA256(Account Key,
// Protocol major version number || the last nonce read from the
// characteristic|| Data ID ||Data length || Additional Data).
RETURN_IF_ERROR(nearby_fp_HmacSha256Start(&context, key, key_size));
RETURN_IF_ERROR(
nearby_fp_HmacSha256Update(&protocol_major_version, sizeof(uint8_t)));
RETURN_IF_ERROR(nearby_fp_HmacSha256Update(nonce, sizeof(nonce)));
// Data ID which is first byte in payload & Data length : second byte in
// payload
RETURN_IF_ERROR(nearby_fp_HmacSha256Update(&payload[0], 2 * sizeof(uint8_t)));
// Check if addtioal data is present or not
if (additional_data_length) {
RETURN_IF_ERROR(nearby_fp_HmacSha256Update(
&payload[REQUEST_HEADER_SIZE + AUTH_KEY_SIZE], additional_data_length));
}
// For generating the auth key for response, also append 0x01 at the end
if (generate) {
uint8_t last_hmac_data = 0x01;
RETURN_IF_ERROR(
nearby_fp_HmacSha256Update(&last_hmac_data, sizeof(uint8_t)));
}
RETURN_IF_ERROR(nearby_fp_HmacSha256Finish(&context, key, key_size));
bool result = true;
if (generate) {
memcpy(auth_key, context.hash, AUTH_KEY_SIZE);
} else {
result = memcmp(context.hash, auth_key, AUTH_KEY_SIZE) == 0;
NEARBY_TRACE(DEBUG, "Hash: %s",
nearby_utils_ArrayToString(context.hash, AUTH_KEY_SIZE));
if (!result) {
NEARBY_TRACE(DEBUG, "Auth key: %s",
nearby_utils_ArrayToString(auth_key, AUTH_KEY_SIZE));
NEARBY_TRACE(DEBUG, "Key: %s", nearby_utils_ArrayToString(key, key_size));
NEARBY_TRACE(DEBUG, "Nonce: %s",
nearby_utils_ArrayToString(nonce, sizeof(nonce)));
NEARBY_TRACE(DEBUG, "VerifyOrGenerateAuthenticationKey: %s",
result ? "OK" : "failed");
}
}
return result;
}
static bool VerifyAnyAccountKey(uint8_t* auth_key, const uint8_t* request) {
for (size_t i = 0; i < nearby_fp_GetAccountKeyCount(); i++) {
if (VerifyOrGenerateAuthenticationKey(
nearby_fp_GetAccountKey(i)->account_key, ACCOUNT_KEY_SIZE_BYTES,
auth_key, request, false)) {
account_key_index = i;
return true;
}
}
return false;
}
#define VERIFY_KEY(key, auth_key) \
if (!VerifyKey(key, sizeof(key), auth_key)) { \
NEARBY_TRACE(ERROR, "Failed Key verification"); \
return STATUS_UNAUTHENTICATED; \
}
#define VERIFY_AUTH_KEY(key, auth_key, request) \
if (!VerifyOrGenerateAuthenticationKey(key, sizeof(key), auth_key, request, \
false)) { \
NEARBY_TRACE(ERROR, "Failed auth"); \
return STATUS_UNAUTHENTICATED; \
}
#define GENERATE_AUTH_KEY(key, key_size, auth_key, payload) \
if (!VerifyOrGenerateAuthenticationKey(key, key_size, auth_key, payload, \
true)) { \
NEARBY_TRACE(ERROR, "Failed auth"); \
return STATUS_UNAUTHENTICATED; \
}
#define VERIFY_AUTH_KEY_WITH_ANY_ACCOUNT_KEY(auth_key, request) \
if (!VerifyAnyAccountKey(auth_key, request)) { \
NEARBY_TRACE(ERROR, "Failed verify any account key"); \
return STATUS_UNAUTHENTICATED; \
}
#define VERIFY_COND(cond) \
if (!(cond)) { \
NEARBY_TRACE(ERROR, "Failed: " #cond); \
return STATUS_INVALID_VALUE; \
}
static bool HasUserConsentForReadingEik() {
return (nearby_platform_IsInPairingMode() ||
nearby_platform_HasUserConsentForReadingEik());
}
static nearby_platform_status ComputeKey(uint8_t output[AUTH_KEY_SIZE],
uint8_t suffix) {
uint8_t hash[32];
NEARBY_ASSERT(has_ephemeral_key);
RETURN_IF_ERROR(nearby_platform_Sha256Start());
RETURN_IF_ERROR(
nearby_platform_Sha256Update(ephemeral_key, sizeof(ephemeral_key)));
RETURN_IF_ERROR(nearby_platform_Sha256Update(&suffix, sizeof(suffix)));
RETURN_IF_ERROR(nearby_platform_Sha256Finish(hash));
memcpy(output, hash, AUTH_KEY_SIZE);
return kNearbyStatusOK;
}
static nearby_platform_status ComputeRecoveryKey(
uint8_t output[AUTH_KEY_SIZE]) {
return ComputeKey(output, RECOVERY_KEY_SUFFIX);
}
static nearby_platform_status ComputeRingKey(uint8_t output[AUTH_KEY_SIZE]) {
return ComputeKey(output, RING_KEY_SUFFIX);
}
static nearby_platform_status ComputeUnwantedTrackingProtectionKey(
uint8_t output[AUTH_KEY_SIZE]) {
return ComputeKey(output, UNWANTED_TRACKING_PROTECTION_KEY_SUFFIX);
}
static nearby_platform_status GattNotify(
uint64_t address, nearby_fp_Characteristic characteristic,
const uint8_t* message, size_t length) {
nearby_platform_status status =
nearby_platform_GattNotify(address, characteristic, message, length);
NEARBY_TRACE(DEBUG, "Notify(%s), result %d",
nearby_utils_ArrayToString(message, length), status);
return status;
}
// TODO(jsobczak): The Owner Account Key must not be removed when the Provider
// runs out of free Account Key slots.
nearby_platform_status nearby_spot_SetBeaconAccountKey(
const uint8_t key[ACCOUNT_KEY_SIZE_BYTES]) {
NEARBY_TRACE(VERBOSE, "SetBeaconAccountKey");
memcpy(owner_key, key, sizeof(owner_key));
has_owner_key = true;
return nearby_platform_SaveValue(kStoredKeyOwnerKey, owner_key,
sizeof(owner_key));
}
nearby_platform_status nearby_spot_ReadBeaconAction(uint64_t peer_address,
uint8_t* output,
size_t* length) {
NEARBY_ASSERT(*length >= (BEACON_NONCE_SIZE + BEACON_PROTOCOL_VERSION_SIZE));
*length = BEACON_NONCE_SIZE + BEACON_PROTOCOL_VERSION_SIZE;
remote_address = peer_address;
output[0] = SPOT_BEACON_PROTOCOL_MAJOR_VERSION;
for (int i = 0; i < BEACON_NONCE_SIZE; i++) {
nonce[i] = output[i + 1] = nearby_platform_Rand();
}
has_nonce = true;
return kNearbyStatusOK;
}
static nearby_platform_status GenerateEphemeralId() {
uint8_t buffer[EPHEMERAL_KEY_SIZE];
uint32_t timestamp = nearby_platform_GetPersistentTime() & CLOCK_MASK;
NEARBY_TRACE(INFO, "Timestamp 0x%x", timestamp);
// Bytes 0 - 10: padding 0xFF
memset(buffer, 0xFF, 11);
buffer[11] = ROTATION_PERIOD_EXPONENT;
nearby_utils_CopyBigEndian(buffer + 12, timestamp, sizeof(timestamp));
// Bytes 16 - 26: padding 0x00
memset(buffer + 16, 0, 11);
buffer[27] = ROTATION_PERIOD_EXPONENT;
memcpy(buffer + 28, buffer + 12, sizeof(timestamp));
NEARBY_TRACE(
INFO, "EIK %s",
nearby_utils_ArrayToString(ephemeral_key, sizeof(ephemeral_key)));
NEARBY_TRACE(INFO, "EID enc input %s",
nearby_utils_ArrayToString(buffer, sizeof(buffer)));
RETURN_IF_ERROR(nearby_platform_Aes256Encrypt(buffer, buffer, ephemeral_key));
RETURN_IF_ERROR(
nearby_platform_Aes256Encrypt(buffer + 16, buffer + 16, ephemeral_key));
NEARBY_TRACE(INFO, "EID enc output %s",
nearby_utils_ArrayToString(buffer, sizeof(buffer)));
nearby_platform_status status = nearby_platform_GetSecp160r1PublicKey(
buffer, ephemeral_id, &hashed_field_lsb);
NEARBY_TRACE(INFO, "EID %s",
nearby_utils_ArrayToString(ephemeral_id, sizeof(ephemeral_id)));
return status;
}
#if NEARBY_SUPPORT_MULTIPLE_BLE_ADDRESSES
unsigned int nearby_spot_GetAddressRotationTimestamp() {
return spot_address_rotation_timestamp;
}
void nearby_spot_GenerateRandomizedAddress() {
uint64_t address = 0;
for (int i = 0; i < BT_ADDRESS_LENGTH; i++) {
address = (address << 8) ^ nearby_platform_Rand();
}
address &= ~((uint64_t)3 << 46);
spot_randomized_address = address;
spot_address_rotation_timestamp = nearby_platform_GetCurrentTimeMs();
NEARBY_TRACE(VERBOSE, "address=0x%lx spot_address_rotation_timestamp=%u",
address, spot_address_rotation_timestamp);
}
#endif /* NEARBY_SUPPORT_MULTIPLE_BLE_ADDRESSES */
static uint64_t GetSpotAddress() {
#if NEARBY_SUPPORT_MULTIPLE_BLE_ADDRESSES
if (nearby_spot_GetUnwantedTrackingProtectionModeState()) {
return spot_randomized_address;
}
#endif /* NEARBY_SUPPORT_MULTIPLE_BLE_ADDRESSES*/
return nearby_platform_GetBleAddress();
}
static nearby_platform_status StartAdvertising() {
NEARBY_TRACE(VERBOSE, "Start SPOT advertisement");
uint8_t hashed_flag = 0;
uint64_t address = GetSpotAddress();
uint8_t advertisement[sizeof(frame_header) + FRAME_TYPE_SIZE +
sizeof(ephemeral_id) + sizeof(hashed_flag)];
uint8_t adv_length = sizeof(advertisement);
memcpy(advertisement, frame_header, sizeof(frame_header));
if (nearby_spot_GetUnwantedTrackingProtectionModeState())
advertisement[sizeof(frame_header)] =
FRAME_TYPE_WITH_UNWANTED_TRACKING_PROTECTION;
else
advertisement[sizeof(frame_header)] = FRAME_TYPE;
memcpy(advertisement + sizeof(frame_header) + FRAME_TYPE_SIZE, ephemeral_id,
sizeof(ephemeral_id));
hashed_flag |= unwanted_tracking_protection_mode;
#ifdef NEARBY_SPOT_BATTERY_LEVEL_INDICATION
hashed_flag |= nearby_spot_GetBatteryLevel();
#endif
if (hashed_flag) {
hashed_flag ^= hashed_field_lsb;
memcpy(advertisement + sizeof(frame_header) + FRAME_TYPE_SIZE +
sizeof(ephemeral_id),
&hashed_flag, sizeof(hashed_flag));
} else
adv_length -= 1;
NEARBY_TRACE(INFO, "SPOT advert %s",
nearby_utils_ArrayToString(advertisement, adv_length));
return nearby_SetSpotAdvertisement(address, advertisement, adv_length);
}
static nearby_platform_status StopAdvertising() {
NEARBY_TRACE(VERBOSE, "Stop SPOT advertisement");
return nearby_SetSpotAdvertisement(0, NULL, 0);
}
static nearby_platform_status UpdateAdvertisement() {
nearby_platform_status status = GenerateEphemeralId();
StopAdvertising();
if (kNearbyStatusOK == status) {
status = StartAdvertising();
}
return status;
}
static nearby_platform_status ReadBeaconParameters(uint64_t peer_address) {
NEARBY_TRACE(VERBOSE, "ReadBeaconParameters");
uint8_t payload[26] = {0};
uint8_t* additional_data = &payload[RESPONSE_HEADER_SIZE + AUTH_KEY_SIZE];
nearby_platform_RingingInfo info;
payload[0] = RESPONSE_READ_BEACON_PARAMETERS;
payload[1] = sizeof(payload) - RESPONSE_HEADER_SIZE;
additional_data[0] = nearby_platform_GetTxLevel();
nearby_utils_CopyBigEndian(&additional_data[1],
nearby_platform_GetPersistentTime(),
sizeof(uint32_t));
// curve type
#if (NEARBY_SPOT_SECP_CURVE_SIZE == 20)
additional_data[5] = 0x00;
#else
additional_data[5] = 0x01;
#endif
RETURN_IF_ERROR(nearby_platform_GetRingingInfo(&info));
// number of ringing component
additional_data[6] = info.num_components;
// ringing volume capability
additional_data[7] = info.volume;
// zero padded 8 bytes
memset(&additional_data[8], 0, 8);
if (account_key_index != 0xFF) {
// Encrypt the data
const uint8_t* key =
nearby_fp_GetAccountKey(account_key_index)->account_key;
RETURN_IF_ERROR(nearby_platform_Aes128Encrypt(
additional_data, &payload[RESPONSE_HEADER_SIZE + AUTH_KEY_SIZE], key));
// Generate and the set the authentication key
GENERATE_AUTH_KEY(key, ACCOUNT_KEY_SIZE_BYTES,
&payload[RESPONSE_HEADER_SIZE], payload);
} else {
return STATUS_UNAUTHENTICATED;
}
return GattNotify(peer_address, kBeaconActions, payload, sizeof(payload));
}
static nearby_platform_status ReadProvisioningState(uint64_t peer_address,
bool is_owner_key) {
NEARBY_TRACE(VERBOSE, "ReadProvisioningState");
uint8_t
payload[RESPONSE_HEADER_SIZE + AUTH_KEY_SIZE + 1 + sizeof(ephemeral_id)];
payload[0] = RESPONSE_READ_PROVISIONING_STATE;
payload[1] = AUTH_KEY_SIZE + 1;
payload[RESPONSE_HEADER_SIZE + AUTH_KEY_SIZE] =
(has_ephemeral_key ? 1 : 0) | (is_owner_key ? 2 : 0);
if (has_ephemeral_key) {
payload[1] += sizeof(ephemeral_id);
memcpy(&payload[RESPONSE_HEADER_SIZE + AUTH_KEY_SIZE + 1], ephemeral_id,
sizeof(ephemeral_id));
}
if (account_key_index != 0xFF) {
// Generate and set the authentication key
const uint8_t* key =
nearby_fp_GetAccountKey(account_key_index)->account_key;
GENERATE_AUTH_KEY(key, ACCOUNT_KEY_SIZE_BYTES,
&payload[RESPONSE_HEADER_SIZE], payload);
} else {
return STATUS_UNAUTHENTICATED;
}
return GattNotify(peer_address, kBeaconActions, payload,
RESPONSE_HEADER_SIZE + payload[1]);
}
static nearby_platform_status DecryptAndSaveEphemeralKey(
const uint8_t* encrypted_key) {
NEARBY_TRACE(VERBOSE, "Decrypt and save ephemeral key");
RETURN_IF_ERROR(
nearby_platform_Aes128Decrypt(encrypted_key, ephemeral_key, owner_key));
RETURN_IF_ERROR(nearby_platform_Aes128Decrypt(encrypted_key + 16,
ephemeral_key + 16, owner_key));
RETURN_IF_ERROR(nearby_platform_SaveValue(
kStoredKeyEphemeralKey, ephemeral_key, sizeof(ephemeral_key)));
has_ephemeral_key = true;
return UpdateAdvertisement();
}
static nearby_platform_status SetEphemeralIdentityKeyResponse(
uint64_t peer_address) {
NEARBY_TRACE(VERBOSE, "SetEphemeralIdentityKeyResponse");
uint8_t payload[RESPONSE_HEADER_SIZE + AUTH_KEY_SIZE];
payload[0] = RESPONSE_SET_EPHEMERAL_KEY;
payload[1] = AUTH_KEY_SIZE;
// Generate and set the authentication key
GENERATE_AUTH_KEY(owner_key, ACCOUNT_KEY_SIZE_BYTES,
&payload[RESPONSE_HEADER_SIZE], payload);
return GattNotify(peer_address, kBeaconActions, payload,
RESPONSE_HEADER_SIZE + payload[1]);
}
static nearby_platform_status ClearEphemeralKey() {
NEARBY_TRACE(VERBOSE, "Clear ephemeral key");
has_ephemeral_key = false;
memset(ephemeral_key, 0, sizeof(ephemeral_key));
memset(ephemeral_id, 0, sizeof(ephemeral_id));
nearby_platform_status status =
nearby_platform_ClearValue(kStoredKeyEphemeralKey);
if (kNearbyStatusOK != status) {
NEARBY_TRACE(ERROR, "Error clearing ephemeral key %d", status);
}
return StopAdvertising();
}
static nearby_platform_status ClearEphemeralIdentityKeyResponse(
uint64_t peer_address) {
NEARBY_TRACE(VERBOSE, "ClearEphemeralIdentityKeyResponse");
uint8_t payload[RESPONSE_HEADER_SIZE + AUTH_KEY_SIZE];
payload[0] = RESPONSE_CLEAR_EPHEMERAL_KEY;
payload[1] = AUTH_KEY_SIZE;
nearby_platform_status status = ClearEphemeralKey();
if (status != kNearbyStatusOK) return status;
// Generate and set the authentication key
GENERATE_AUTH_KEY(owner_key, ACCOUNT_KEY_SIZE_BYTES,
&payload[RESPONSE_HEADER_SIZE], payload);
status = GattNotify(peer_address, kBeaconActions, payload,
RESPONSE_HEADER_SIZE + payload[1]);
if (status != kNearbyStatusOK) return status;
#if NEARBY_SPOT_FACTORY_RESET_DEVICE_ON_CLEARING_EIK
status = nearby_platform_FactoryReset();
#endif
return status;
}
static nearby_platform_status ReadEphemeralKey(uint64_t peer_address,
uint8_t* recovery_key) {
NEARBY_TRACE(VERBOSE, "ReadEphemeralKey");
uint8_t payload[RESPONSE_HEADER_SIZE + AUTH_KEY_SIZE + EPHEMERAL_KEY_SIZE];
payload[0] = RESPONSE_READ_EPHEMERAL_KEY;
payload[1] = sizeof(payload) - RESPONSE_HEADER_SIZE;
// Encrypt the ephemeral key with the account key
RETURN_IF_ERROR(nearby_platform_Aes128Encrypt(
ephemeral_key, payload + AUTH_KEY_SIZE + RESPONSE_HEADER_SIZE,
owner_key));
RETURN_IF_ERROR(nearby_platform_Aes128Encrypt(
ephemeral_key + 16, payload + AUTH_KEY_SIZE + RESPONSE_HEADER_SIZE + 16,
owner_key));
// Generate and set the authentication key
GENERATE_AUTH_KEY(recovery_key, AUTH_KEY_SIZE, &payload[RESPONSE_HEADER_SIZE],
payload);
return GattNotify(peer_address, kBeaconActions, payload, sizeof(payload));
}
static nearby_platform_status GetPlaftormRingState(
nearby_platform_RingingInfo* info) {
memset(info, 0, sizeof(nearby_platform_RingingInfo));
RETURN_IF_ERROR(nearby_platform_GetRingingInfo(info));
if (info->ring_state != kRingStateStarted || info->num_components == 0 ||
info->components == 0) {
info->timeout = 0;
}
return kNearbyStatusOK;
}
static nearby_platform_status Ring(uint64_t peer_address, const uint8_t* data,
size_t length) {
NEARBY_TRACE(VERBOSE, "Ring");
if (length < 1) return STATUS_INVALID_VALUE;
uint8_t command = data[0];
uint16_t timeout = 0;
nearby_platform_RingingVolume volume = kRingingVolumeDefault;
if (command != 0) {
if (length != 4) return STATUS_INVALID_VALUE;
timeout = nearby_utils_GetBigEndian16(&data[1]);
volume = data[3];
}
ringing_peer_address = peer_address;
return nearby_platform_Ring(command, timeout, volume);
}
nearby_platform_status nearby_spot_OnRingStateChange() {
uint8_t payload[14];
nearby_platform_RingingInfo info;
uint8_t ring_key[AUTH_KEY_SIZE];
RETURN_IF_ERROR(GetPlaftormRingState(&info));
VERIFY_COND(has_ephemeral_key);
RETURN_IF_ERROR(ComputeRingKey(ring_key));
payload[0] = RESPONSE_RING_STATE_CHANGE;
payload[1] = sizeof(payload) - RESPONSE_HEADER_SIZE;
payload[RESPONSE_HEADER_SIZE + AUTH_KEY_SIZE] = info.ring_state;
payload[RESPONSE_HEADER_SIZE + AUTH_KEY_SIZE + 1] = info.components;
nearby_utils_CopyBigEndian(payload + RESPONSE_HEADER_SIZE + AUTH_KEY_SIZE + 2,
info.timeout, 2);
// Generate and set the authentication key
GENERATE_AUTH_KEY(ring_key, AUTH_KEY_SIZE, &payload[RESPONSE_HEADER_SIZE],
payload);
return GattNotify(ringing_peer_address, kBeaconActions, payload,
sizeof(payload));
}
static nearby_platform_status ReadRingingState(uint64_t peer_address,
uint8_t* ring_key) {
NEARBY_TRACE(VERBOSE, "ReadRingingState");
uint8_t payload[13];
nearby_platform_RingingInfo info;
RETURN_IF_ERROR(GetPlaftormRingState(&info));
payload[0] = RESPONSE_READ_RINGING_STATE;
payload[1] = sizeof(payload) - RESPONSE_HEADER_SIZE;
payload[RESPONSE_HEADER_SIZE + AUTH_KEY_SIZE] = info.components;
nearby_utils_CopyBigEndian(payload + RESPONSE_HEADER_SIZE + AUTH_KEY_SIZE + 1,
info.timeout, 2);
// Generate and set the authentication key
GENERATE_AUTH_KEY(ring_key, AUTH_KEY_SIZE, &payload[RESPONSE_HEADER_SIZE],
payload);
return GattNotify(peer_address, kBeaconActions, payload, sizeof(payload));
}
static nearby_platform_status ActivateUnwantedTrackingProtection(
uint64_t peer_address, uint8_t* unwanted_tracking_protection_key,
const uint8_t* data, size_t length) {
NEARBY_TRACE(VERBOSE, "ActivateUnwantedTrackingProtection");
// The first byte of the data section defines control flags for the operation.
if (length == 1) {
control_flags = data[AUTH_KEY_SIZE];
}
// Once Unwanted Tracking Protection mode was activated, the beacon should
// reduce address(RPA) rotation frequency to once per 24h.
nearby_spot_SetUnwantedTrackingProtectionMode(true);
if (UpdateAdvertisement() != kNearbyStatusOK) {
return kNearbyStatusError;
}
uint8_t payload[RESPONSE_HEADER_SIZE + AUTH_KEY_SIZE];
payload[0] = RESPONSE_ACTIVATE_UNWANTED_TRACKING_PROTECTION;
payload[1] = sizeof(payload) - RESPONSE_HEADER_SIZE;
// Generate and set the authentication key
GENERATE_AUTH_KEY(unwanted_tracking_protection_key, AUTH_KEY_SIZE,
&payload[RESPONSE_HEADER_SIZE], payload);
return GattNotify(peer_address, kBeaconActions, payload, sizeof(payload));
}
static nearby_platform_status DeactivateUnwantedTrackingProtection(
uint64_t peer_address, uint8_t* unwanted_tracking_protection_key) {
NEARBY_TRACE(VERBOSE, "DeactivateUnwantedTrackingProtection");
// Reset control flags
control_flags = 0;
// Once Unwanted Tracking Protection deactivated, the beacon should
// start rotating the MAC address(RPA) at a normal rate again
nearby_spot_SetUnwantedTrackingProtectionMode(false);
if (UpdateAdvertisement() != kNearbyStatusOK) {
return kNearbyStatusError;
}
uint8_t payload[RESPONSE_HEADER_SIZE + AUTH_KEY_SIZE];
payload[0] = RESPONSE_DEACTIVATE_UNWANTED_TRACKING_PROTECTION;
payload[1] = sizeof(payload) - RESPONSE_HEADER_SIZE;
// Generate and set the authentication key
GENERATE_AUTH_KEY(unwanted_tracking_protection_key, AUTH_KEY_SIZE,
&payload[RESPONSE_HEADER_SIZE], payload);
return GattNotify(peer_address, kBeaconActions, payload, sizeof(payload));
}
nearby_platform_status nearby_spot_WriteBeaconAction(uint64_t peer_address,
const uint8_t* request,
size_t length) {
uint8_t expected_auth_key[AUTH_KEY_SIZE];
nearby_platform_status status;
NEARBY_TRACE(INFO, "WriteBeaconAction request from(%s) %s",
nearby_utils_MacToString(peer_address),
nearby_utils_ArrayToString(request, length));
if (peer_address != remote_address || !has_nonce) {
NEARBY_TRACE(INFO, "Invalid remote address or nonce");
return STATUS_UNAUTHENTICATED;
}
// Mark the nonce as used. It is still valid during this write request.
has_nonce = false;
if (length < REQUEST_HEADER_SIZE) return STATUS_INVALID_VALUE;
uint8_t data_id = request[0];
uint8_t data_length = request[1];
uint8_t* auth_key = (uint8_t*)&request[REQUEST_HEADER_SIZE];
VERIFY_COND(data_length + REQUEST_HEADER_SIZE == length);
switch (data_id) {
case REQUEST_READ_BEACON_PARAMETERS:
account_key_index = 0xFF;
VERIFY_COND(data_length == AUTH_KEY_SIZE);
VERIFY_AUTH_KEY_WITH_ANY_ACCOUNT_KEY(auth_key, request);
return ReadBeaconParameters(peer_address);
case REQUEST_READ_PROVISIONING_STATE:
account_key_index = 0xFF;
VERIFY_COND(data_length == AUTH_KEY_SIZE);
VERIFY_AUTH_KEY_WITH_ANY_ACCOUNT_KEY(auth_key, request);
// In some cases, where the Provider didnt previously keep track of the
// order of AccountKeys as theyre being added - and now being updated to
// support Finder, it is allowed to define the Owner Account Key as the
// first Account Key used for reading the provisioning state from the
// Provider.
if (!has_owner_key) {
nearby_spot_SetBeaconAccountKey(
nearby_fp_GetAccountKey(account_key_index)->account_key);
}
return ReadProvisioningState(
peer_address,
VerifyOrGenerateAuthenticationKey(owner_key, sizeof(owner_key),
auth_key, request, false));
case REQUEST_SET_EPHEMERAL_KEY:
if (data_length == AUTH_KEY_SIZE + EPHEMERAL_KEY_SIZE) {
VERIFY_COND(!has_ephemeral_key);
} else if (data_length ==
AUTH_KEY_SIZE + EPHEMERAL_KEY_SIZE + AUTH_KEY_SIZE) {
VERIFY_COND(has_ephemeral_key);
VERIFY_KEY(
ephemeral_key,
&request[REQUEST_HEADER_SIZE + AUTH_KEY_SIZE + EPHEMERAL_KEY_SIZE]);
} else {
return STATUS_INVALID_VALUE;
}
VERIFY_COND(has_owner_key);
VERIFY_AUTH_KEY(owner_key, auth_key, request);
status = DecryptAndSaveEphemeralKey(
&request[REQUEST_HEADER_SIZE + AUTH_KEY_SIZE]);
if (status != kNearbyStatusOK) return status;
return SetEphemeralIdentityKeyResponse(peer_address);
case REQUEST_CLEAR_EPHEMERAL_KEY:
VERIFY_COND(data_length == 2 * AUTH_KEY_SIZE);
VERIFY_COND(has_owner_key);
VERIFY_COND(has_ephemeral_key);
VERIFY_AUTH_KEY(owner_key, auth_key, request);
VERIFY_KEY(ephemeral_key, &request[REQUEST_HEADER_SIZE + AUTH_KEY_SIZE]);
return ClearEphemeralIdentityKeyResponse(peer_address);
case REQUEST_READ_EPHEMERAL_KEY:
VERIFY_COND(data_length == AUTH_KEY_SIZE);
VERIFY_COND(has_ephemeral_key);
RETURN_IF_ERROR(ComputeRecoveryKey(expected_auth_key));
VERIFY_AUTH_KEY(expected_auth_key, auth_key, request);
if (!HasUserConsentForReadingEik()) return STATUS_NO_USER_CONSENT;
return ReadEphemeralKey(peer_address, expected_auth_key);
case REQUEST_RING:
VERIFY_COND(data_length > AUTH_KEY_SIZE);
VERIFY_COND(has_ephemeral_key);
RETURN_IF_ERROR(ComputeRingKey(expected_auth_key));
if (!(nearby_spot_GetControlFlags() ==
CONTROL_FLAG_SKIP_RINGING_AUTHENTICATION &&
nearby_spot_GetUnwantedTrackingProtectionModeState())) {
VERIFY_AUTH_KEY(expected_auth_key, auth_key, request);
}
return Ring(peer_address, &request[REQUEST_HEADER_SIZE + AUTH_KEY_SIZE],
(data_length - AUTH_KEY_SIZE));
case REQUEST_READ_RINGING_STATE:
VERIFY_COND(data_length == AUTH_KEY_SIZE);
VERIFY_COND(has_ephemeral_key);
RETURN_IF_ERROR(ComputeRingKey(expected_auth_key));
VERIFY_AUTH_KEY(expected_auth_key, auth_key, request);
return ReadRingingState(peer_address, expected_auth_key);
case REQUEST_ACTIVATE_UNWANTED_TRACKING_PROTECTION:
VERIFY_COND(data_length >= AUTH_KEY_SIZE);
VERIFY_COND(has_ephemeral_key);
RETURN_IF_ERROR(ComputeUnwantedTrackingProtectionKey(expected_auth_key));
VERIFY_AUTH_KEY(expected_auth_key, auth_key, request);
return ActivateUnwantedTrackingProtection(peer_address, expected_auth_key,
&request[REQUEST_HEADER_SIZE],
(data_length - AUTH_KEY_SIZE));
case REQUEST_DEACTIVATE_UNWANTED_TRACKING_PROTECTION:
VERIFY_COND(data_length == 2 * AUTH_KEY_SIZE);
VERIFY_COND(has_ephemeral_key);
RETURN_IF_ERROR(ComputeUnwantedTrackingProtectionKey(expected_auth_key));
VERIFY_AUTH_KEY(expected_auth_key, auth_key, request);
VERIFY_KEY(ephemeral_key, &request[REQUEST_HEADER_SIZE + AUTH_KEY_SIZE]);
return DeactivateUnwantedTrackingProtection(peer_address,
expected_auth_key);
}
return STATUS_INVALID_VALUE;
}
nearby_platform_status nearby_spot_SetAdvertisement(bool enable) {
if (!enable) {
return StopAdvertising();
}
NEARBY_TRACE(INFO, "Set spot advertisement");
if (!has_ephemeral_key) {
NEARBY_TRACE(WARNING, "Can't advertise SPOT without ephemeral key");
return kNearbyStatusError;
}
// Strictly speaking, the owner acoount key is not required to generate
// the advertisement but it indicates that the setup is incorrect.
if (!has_owner_key) {
NEARBY_TRACE(ERROR, "Can't advertise SPOT without owner account key");
return kNearbyStatusError;
}
return UpdateAdvertisement();
}
nearby_platform_status nearby_spot_Init() {
size_t key_length = sizeof(ephemeral_key);
nearby_platform_status status = nearby_platform_LoadValue(
kStoredKeyEphemeralKey, ephemeral_key, &key_length);
has_ephemeral_key =
kNearbyStatusOK == status && key_length == sizeof(ephemeral_key);
key_length = sizeof(owner_key);
status =
nearby_platform_LoadValue(kStoredKeyOwnerKey, owner_key, &key_length);
has_owner_key = kNearbyStatusOK == status && key_length == sizeof(owner_key);
if (has_owner_key) {
NEARBY_TRACE(DEBUG, "Owner key: %s",
nearby_utils_ArrayToString(owner_key, sizeof(owner_key)));
}
if (has_ephemeral_key) {
NEARBY_TRACE(
DEBUG, "EIK: %s",
nearby_utils_ArrayToString(ephemeral_key, sizeof(ephemeral_key)));
}
remote_address = 0;
has_nonce = false;
unwanted_tracking_protection_mode = 0;
#if NEARBY_SUPPORT_MULTIPLE_BLE_ADDRESSES
nearby_spot_GenerateRandomizedAddress();
#endif /* NEARBY_SUPPORT_MULTIPLE_BLE_ADDRESSES */
return kNearbyStatusOK;
}
bool nearby_spot_IsProvisioned() { return has_ephemeral_key; }
#endif /* NEARBY_FP_ENABLE_SPOT */
-108
View File
@@ -1,108 +0,0 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef NEARBY_SPOT_H
#define NEARBY_SPOT_H
// clang-format off
#include "nearby_config.h"
// clang-format on
#include "nearby.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#if NEARBY_FP_ENABLE_SPOT
#if (NEARBY_SPOT_SECP_CURVE_SIZE == 20)
#define NEARBY_SPOT_ADVERTISEMENT_SIZE 29
#define EPHEMERAL_ID_SIZE 20
#else
#define NEARBY_SPOT_ADVERTISEMENT_SIZE 41
#define EPHEMERAL_ID_SIZE 32
#endif /* NEARBY_SPOT_SECP_CURVE_SIZE */
// Define protocol version for SPOT protocol
#define SPOT_BEACON_PROTOCOL_MAJOR_VERSION 1
#ifdef NEARBY_SPOT_BATTERY_LEVEL_INDICATION
#define MAIN_BATTERY_LEVEL_INDEX 0
#endif /* NEARBY_SPOT_BATTERY_LEVEL_INDICATION */
// The BLE address should be rotated on average every 24 hours (86400 seconds)
#define ADDRESS_ROTATION_PERIOD_UNWANTED_TRACKING_PROTECTION_MS 86400000U
// Initializes SPOT library. Reads configuration from Flash, doesn't start
// advertising, though.
nearby_platform_status nearby_spot_Init();
// Sets the "owner" account key. The owner account key is the first account key
// added to the device
nearby_platform_status nearby_spot_SetBeaconAccountKey(
const uint8_t key[ACCOUNT_KEY_SIZE_BYTES]);
// Creates SPOT advertisement and starts advertising when `enable` is true. When
// `enable` is false, stops advertising. nearby_spot_SetAdvertisement should be
// called, on average, every 1024 seconds. It should also be called when BLE
// address changes.
nearby_platform_status nearby_spot_SetAdvertisement(bool enable);
// Activates or Deactivates the Unwanted TrackingProtection Mode
// Pass true to activate or false to deactivate
void nearby_spot_SetUnwantedTrackingProtectionMode(bool activate);
// Gets the current status of Unwanted Tracking Mode
// output is true if activated or false if unactivated
bool nearby_spot_GetUnwantedTrackingProtectionModeState(void);
// Returns the current value of control flags:
// 0x01 - Skip Ringing Authentication
// 0x00 - No flag set
uint8_t nearby_spot_GetControlFlags(void);
// A callback for reading from Beacon Action characteristic.
nearby_platform_status nearby_spot_ReadBeaconAction(uint64_t peer_address,
uint8_t* output,
size_t* length);
// A callback for writing to Beacon Action characteristic.
nearby_platform_status nearby_spot_WriteBeaconAction(uint64_t peer_address,
const uint8_t* request,
size_t length);
// A callback that should be triggered when the device starts or stops ringing.
nearby_platform_status nearby_spot_OnRingStateChange();
// Returns true if the device is provisionned by any account.
bool nearby_spot_IsProvisioned();
// Copies the Ephemeral ID into the input eid_ptr
nearby_platform_status nearby_spot_GetEid(uint8_t* eid_ptr);
#if NEARBY_SUPPORT_MULTIPLE_BLE_ADDRESSES
// Generates the SPOT randomized address
void nearby_spot_GenerateRandomizedAddress();
// Returns the timestamp when the address was rotated
unsigned int nearby_spot_GetAddressRotationTimestamp();
#endif /* NEARBY_SUPPORT_MULTIPLE_BLE_ADDRESSES */
#endif /* NEARBY_FP_ENABLE_SPOT */
#ifdef __cplusplus
}
#endif
#endif /* NEARBY_SPOT_H */
-230
View File
@@ -1,230 +0,0 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <deque>
#include <iostream>
#include <vector>
#include "fakes.h"
#include "gmock/gmock-matchers.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "nearby.h"
#include "nearby_advert.h"
#include "nearby_event.h"
#include "nearby_fp_library.h"
#include "nearby_platform_ble.h"
#include "nearby_platform_os.h"
#include "nearby_spot.h"
using ::testing::ElementsAreArray;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-const-variable"
#pragma GCC diagnostic pop
constexpr uint8_t kFpAdvertisement[NON_DISCOVERABLE_ADV_SIZE_BYTES] = {1};
#if NEARBY_FP_ENABLE_SPOT
constexpr uint8_t kSpotAdvertisement[NEARBY_SPOT_ADVERTISEMENT_SIZE] = {2};
#endif /* NEARBY_FP_ENABLE_SPOT */
class AdvertTest : public ::testing::Test {
protected:
void SetUp() override;
};
void AdvertTest::SetUp() {
nearby_platform_OsInit(NULL);
nearby_platform_BleInit(NULL);
nearby_AdvertInit();
}
TEST_F(AdvertTest, SetFpAdvertisement_AdvertisesTimerOff) {
ASSERT_EQ(kNearbyStatusOK, nearby_SetFpAdvertisement(kFpAdvertisement,
sizeof(kFpAdvertisement),
kNoLargerThan100ms));
ASSERT_THAT(kFpAdvertisement,
ElementsAreArray(nearby_test_fakes_GetAdvertisement()));
ASSERT_EQ(0, nearby_test_fakes_GetNextTimerMs());
}
TEST_F(AdvertTest, ChangeFpAdvertisement_AdvertisesTimerOff) {
constexpr uint8_t kAdvertisement[NON_DISCOVERABLE_ADV_SIZE_BYTES] = {3};
ASSERT_EQ(kNearbyStatusOK, nearby_SetFpAdvertisement(kFpAdvertisement,
sizeof(kFpAdvertisement),
kNoLargerThan100ms));
ASSERT_EQ(kNearbyStatusOK,
nearby_SetFpAdvertisement(kAdvertisement, sizeof(kAdvertisement),
kNoLargerThan100ms));
ASSERT_THAT(kAdvertisement,
ElementsAreArray(nearby_test_fakes_GetAdvertisement()));
ASSERT_EQ(0, nearby_test_fakes_GetNextTimerMs());
}
TEST_F(AdvertTest, RemoveFpAdvertisement_NoAdvertisementTimerOff) {
ASSERT_EQ(kNearbyStatusOK, nearby_SetFpAdvertisement(kFpAdvertisement,
sizeof(kFpAdvertisement),
kNoLargerThan100ms));
ASSERT_EQ(kNearbyStatusOK, nearby_SetFpAdvertisement(NULL, 0, kDisabled));
ASSERT_EQ(0, nearby_test_fakes_GetAdvertisement().size());
ASSERT_EQ(0, nearby_test_fakes_GetNextTimerMs());
}
#if NEARBY_FP_ENABLE_SPOT
TEST_F(AdvertTest, SetSpotAdvertisement_AdvertisesTimerOff) {
ASSERT_EQ(kNearbyStatusOK,
nearby_SetSpotAdvertisement(0, kSpotAdvertisement,
sizeof(kSpotAdvertisement)));
ASSERT_THAT(kSpotAdvertisement,
ElementsAreArray(nearby_test_fakes_GetSpotAdvertisement()));
ASSERT_EQ(0, nearby_test_fakes_GetNextTimerMs());
}
TEST_F(AdvertTest, ChangeSpotAdvertisement_AdvertisesTimerOff) {
constexpr uint8_t kAdvertisement[NON_DISCOVERABLE_ADV_SIZE_BYTES] = {3};
ASSERT_EQ(kNearbyStatusOK,
nearby_SetSpotAdvertisement(0, kSpotAdvertisement,
sizeof(kSpotAdvertisement)));
ASSERT_EQ(kNearbyStatusOK, nearby_SetSpotAdvertisement(0,
kAdvertisement, sizeof(kAdvertisement)));
ASSERT_THAT(kAdvertisement,
ElementsAreArray(nearby_test_fakes_GetSpotAdvertisement()));
ASSERT_EQ(0, nearby_test_fakes_GetNextTimerMs());
}
TEST_F(AdvertTest, RemoveSpotAdvertisement_NoAdvertisementTimerOff) {
ASSERT_EQ(kNearbyStatusOK,
nearby_SetSpotAdvertisement(0, kSpotAdvertisement,
sizeof(kSpotAdvertisement)));
ASSERT_EQ(kNearbyStatusOK, nearby_SetSpotAdvertisement(0, NULL, 0));
ASSERT_EQ(0, nearby_test_fakes_GetSpotAdvertisement().size());
ASSERT_EQ(0, nearby_test_fakes_GetNextTimerMs());
}
#ifndef NEARBY_FP_HAVE_MULTIPLE_ADVERTISEMENTS
TEST_F(AdvertTest, SetFpThenSpot_RotatesOnTimer) {
ASSERT_EQ(kNearbyStatusOK, nearby_SetFpAdvertisement(kFpAdvertisement,
sizeof(kFpAdvertisement),
kNoLargerThan100ms));
ASSERT_EQ(kNearbyStatusOK,
nearby_SetSpotAdvertisement(kSpotAdvertisement,
sizeof(kSpotAdvertisement)));
ASSERT_THAT(kSpotAdvertisement,
ElementsAreArray(nearby_test_fakes_GetAdvertisement()));
nearby_test_fakes_SetCurrentTimeMs(nearby_test_fakes_GetNextTimerMs());
ASSERT_THAT(kFpAdvertisement,
ElementsAreArray(nearby_test_fakes_GetAdvertisement()));
nearby_test_fakes_SetCurrentTimeMs(nearby_test_fakes_GetNextTimerMs());
ASSERT_THAT(kSpotAdvertisement,
ElementsAreArray(nearby_test_fakes_GetAdvertisement()));
}
TEST_F(AdvertTest, SetSpotThenFp_RotatesOnTimer) {
ASSERT_EQ(kNearbyStatusOK,
nearby_SetSpotAdvertisement(kSpotAdvertisement,
sizeof(kSpotAdvertisement)));
ASSERT_EQ(kNearbyStatusOK, nearby_SetFpAdvertisement(kFpAdvertisement,
sizeof(kFpAdvertisement),
kNoLargerThan100ms));
ASSERT_THAT(kFpAdvertisement,
ElementsAreArray(nearby_test_fakes_GetAdvertisement()));
nearby_test_fakes_SetCurrentTimeMs(nearby_test_fakes_GetNextTimerMs());
ASSERT_THAT(kSpotAdvertisement,
ElementsAreArray(nearby_test_fakes_GetAdvertisement()));
nearby_test_fakes_SetCurrentTimeMs(nearby_test_fakes_GetNextTimerMs());
ASSERT_THAT(kFpAdvertisement,
ElementsAreArray(nearby_test_fakes_GetAdvertisement()));
}
TEST_F(AdvertTest, RemoveFp_SpotActive_RemovesTimer) {
nearby_SetFpAdvertisement(kFpAdvertisement, sizeof(kFpAdvertisement),
kNoLargerThan100ms);
nearby_SetSpotAdvertisement(kSpotAdvertisement, sizeof(kSpotAdvertisement));
ASSERT_EQ(kNearbyStatusOK, nearby_SetFpAdvertisement(NULL, 0, kDisabled));
ASSERT_THAT(kSpotAdvertisement,
ElementsAreArray(nearby_test_fakes_GetAdvertisement()));
ASSERT_EQ(0, nearby_test_fakes_GetNextTimerMs());
}
TEST_F(AdvertTest, RemoveFp_FpActive_SwitchesToSpotRemovesTimer) {
nearby_SetSpotAdvertisement(kSpotAdvertisement, sizeof(kSpotAdvertisement));
nearby_SetFpAdvertisement(kFpAdvertisement, sizeof(kFpAdvertisement),
kNoLargerThan100ms);
ASSERT_EQ(kNearbyStatusOK, nearby_SetFpAdvertisement(NULL, 0, kDisabled));
ASSERT_THAT(kSpotAdvertisement,
ElementsAreArray(nearby_test_fakes_GetAdvertisement()));
ASSERT_EQ(0, nearby_test_fakes_GetNextTimerMs());
}
TEST_F(AdvertTest, RemoveSpot_FpActive_RemovesTimer) {
nearby_SetSpotAdvertisement(kSpotAdvertisement, sizeof(kSpotAdvertisement));
nearby_SetFpAdvertisement(kFpAdvertisement, sizeof(kFpAdvertisement),
kNoLargerThan100ms);
ASSERT_EQ(kNearbyStatusOK, nearby_SetSpotAdvertisement(NULL, 0));
ASSERT_THAT(kFpAdvertisement,
ElementsAreArray(nearby_test_fakes_GetAdvertisement()));
ASSERT_EQ(0, nearby_test_fakes_GetNextTimerMs());
}
TEST_F(AdvertTest, RemoveSpot_SpotActive_SwitchesToFpRemovesTimer) {
nearby_SetFpAdvertisement(kFpAdvertisement, sizeof(kFpAdvertisement),
kNoLargerThan100ms);
nearby_SetSpotAdvertisement(kSpotAdvertisement, sizeof(kSpotAdvertisement));
ASSERT_EQ(kNearbyStatusOK, nearby_SetSpotAdvertisement(NULL, 0));
ASSERT_THAT(kFpAdvertisement,
ElementsAreArray(nearby_test_fakes_GetAdvertisement()));
ASSERT_EQ(0, nearby_test_fakes_GetNextTimerMs());
}
TEST_F(AdvertTest, RemoveBoth_DisablesAdvertisementRemovesTimer) {
nearby_SetSpotAdvertisement(kSpotAdvertisement, sizeof(kSpotAdvertisement));
nearby_SetFpAdvertisement(kFpAdvertisement, sizeof(kFpAdvertisement),
kNoLargerThan100ms);
ASSERT_EQ(kNearbyStatusOK, nearby_SetSpotAdvertisement(NULL, 0));
ASSERT_EQ(kNearbyStatusOK, nearby_SetFpAdvertisement(NULL, 0, kDisabled));
ASSERT_EQ(0, nearby_test_fakes_GetAdvertisement().size());
ASSERT_EQ(0, nearby_test_fakes_GetNextTimerMs());
}
#endif /* NEARBY_FP_HAVE_MULTIPLE_ADVERTISEMENTS */
#endif /* NEARBY_FP_ENABLE_SPOT */
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}