diff --git a/embedded/client/source/nearby_advert.c b/embedded/client/source/nearby_advert.c deleted file mode 100644 index 3ef8d2a6..00000000 --- a/embedded/client/source/nearby_advert.c +++ /dev/null @@ -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 */ -} diff --git a/embedded/client/source/nearby_advert.h b/embedded/client/source/nearby_advert.h deleted file mode 100644 index f664b0f5..00000000 --- a/embedded/client/source/nearby_advert.h +++ /dev/null @@ -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 */ diff --git a/embedded/client/source/nearby_spot.c b/embedded/client/source/nearby_spot.c deleted file mode 100644 index 4e34183f..00000000 --- a/embedded/client/source/nearby_spot.c +++ /dev/null @@ -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 didn’t previously keep track of the - // order of AccountKeys as they’re 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 */ diff --git a/embedded/client/source/nearby_spot.h b/embedded/client/source/nearby_spot.h deleted file mode 100644 index 5a822bc9..00000000 --- a/embedded/client/source/nearby_spot.h +++ /dev/null @@ -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 */ diff --git a/embedded/client/tests/advert_test.cc b/embedded/client/tests/advert_test.cc deleted file mode 100644 index 6084cd72..00000000 --- a/embedded/client/tests/advert_test.cc +++ /dev/null @@ -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 -#include -#include - -#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(); -}