Update embedded SDK

New features:
* Smart Audio Source Switching.
* Fast Pair for BLE-only devices.
* Two byte salt size for improved security.

Other changes:
* Added optional encryption modules based on mbedtls.
* Fixed build issues.
* More verbose and readable logs.
This commit is contained in:
Janusz Sobczak
2022-12-20 10:50:13 -08:00
parent 3dbf10eb66
commit f643f7a466
28 changed files with 4094 additions and 533 deletions
+32
View File
@@ -7,6 +7,8 @@
GTEST_DIR = ../third_party/gtest/googletest/
GMOCK_DIR = ../third_party/gtest/googlemock/
MBEDTLS_DIR = ../third_party/mbedtls/
ARCH ?= host
OUT_DIR_NAME ?= $(ARCH)
@@ -84,12 +86,42 @@ ifeq ($(wildcard common/target/$(ARCH_COMMON_NAME)),)
$(error need to create directory common/target/$(ARCH_COMMON_NAME) )
endif
ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
CFLAGS += -DNEARBY_FP_ENABLE_BATTERY_NOTIFICATION=$(NEARBY_FP_ENABLE_BATTERY_NOTIFICATION)
endif
ifdef NEARBY_FP_ENABLE_ADDITIONAL_DATA
CFLAGS += -DNEARBY_FP_ENABLE_ADDITIONAL_DATA=$(NEARBY_FP_ENABLE_ADDITIONAL_DATA)
endif
ifdef NEARBY_FP_MESSAGE_STREAM
CFLAGS += -DNEARBY_FP_MESSAGE_STREAM=$(NEARBY_FP_MESSAGE_STREAM)
endif
ifdef NEARBY_FP_RETROACTIVE_PAIRING
CFLAGS += -DNEARBY_FP_RETROACTIVE_PAIRING=$(NEARBY_FP_RETROACTIVE_PAIRING)
endif
ifdef NEARBY_FP_BLE_ONLY
CFLAGS += -DNEARBY_FP_BLE_ONLY=$(NEARBY_FP_BLE_ONLY)
endif
ifdef NEARBY_FP_PREFER_BLE_BONDING
CFLAGS += -DNEARBY_FP_PREFER_BLE_BONDING=$(NEARBY_FP_PREFER_BLE_BONDING)
endif
ifdef NEARBY_FP_PREFER_LE_TRANSPORT
CFLAGS += -DNEARBY_FP_PREFER_LE_TRANSPORT=$(NEARBY_FP_PREFER_LE_TRANSPORT)
endif
COMMON_INCLUDE_DIRS += \
-I. \
-I$(ARCH_COMMON_DIR) \
-Icommon/target \
-Icommon/target/$(ARCH_COMMON_NAME) \
-I$(COMMON_DIR)
ifeq ($(NEARBY_PLATFORM_USE_MBEDTLS),1)
COMMON_INCLUDE_DIRS += -I$(MBEDTLS_DIR)/include
endif
CLIENT_INCLUDES += \
-I$(CLIENT_DIR) \
+13 -1
View File
@@ -28,4 +28,16 @@ Nearby SDK assumes a single-threading model. All calls to the SDK must be on the
nearby_fp_client_Init(NULL);
nearby_fp_client_SetAdvertisement(NEARBY_FP_ADVERTISEMENT_DISCOVERABLE);
```
1. Use [Fast Pair Validator](https://play.google.com/store/apps/details?id=com.google.location.nearby.apps.fastpair.validator) to verify that your device is behaving correctly.
1. Use [Fast Pair Validator](https://play.google.com/store/apps/details?id=com.google.location.nearby.apps.fastpair.validator) to verify that your device is behaving correctly.
## Optional modules
The optional modules provide partial implementation of HAL interfaces using common libraries, mbedtls in particular.
1. *mbedtls* located in `common/source/mbedtls/mbedtls.c` implements `nearby_platform_Sha256Start()`, `nearby_platform_Sha256Update()`, `nearby_platform_Sha256Finish()`, `nearby_platform_Aes128Encrypt()`, `nearby_platform_Aes128Decrypt()`.
Nearby SDK can be configured to use the MBEDTLS package, commonly available on ARM
implementations, with the config.mk flag `NEARBY_PLATFORM_USE_MBEDTLS`.
2. *gen_secret* located in `common/source/mbedtls/gen_secret.c` implements `nearby_platform_GenSec256r1Secret()`.
*gen_secret* generates a shared secret based on a given private key on platforms that don't support hardware SE. *gen_secret* module is enabled `NEARBY_PLATFORM_USE_MBEDTLS` is set and `NEARBY_PLATFORM_HAS_SE` is *not* set. When `NEARBY_PLATFORM_HAS_SE` is set, the platform needs to provide their own `nearby_platform_GenSec256r1Secret()` routine.
File diff suppressed because it is too large Load Diff
+7 -2
View File
@@ -52,7 +52,7 @@ typedef struct {
// Ask the Seeker to show pairing UI indication. This flag can be combined with
// NEARBY_FP_ADVERTISEMENT_NON_DISCOVERABLE
#define NEARBY_FP_ADVERTISEMENT_PAIRING_UI_INDICATOR 0x04
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
#if NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
// Include battery and charging info in the advertisement. This flag can be
// combined with NEARBY_FP_ADVERTISEMENT_NON_DISCOVERABLE
#define NEARBY_FP_ADVERTISEMENT_INCLUDE_BATTERY_INFO 0x08
@@ -60,6 +60,11 @@ typedef struct {
// NEARBY_FP_ADVERTISEMENT_INCLUDE_BATTERY_INFO
#define NEARBY_FP_ADVERTISEMENT_BATTERY_UI_INDICATOR 0x10
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
#ifdef NEARBY_FP_ENABLE_SASS
// Include SASS advertisement. This flag can be
// combined with NEARBY_FP_ADVERTISEMENT_NON_DISCOVERABLE
#define NEARBY_FP_ADVERTISEMENT_SASS 0x20
#endif /* NEARBY_FP_ENABLE_SASS */
// Sets Fast Pair advertisement type
nearby_platform_status nearby_fp_client_SetAdvertisement(int mode);
@@ -68,7 +73,7 @@ nearby_platform_status nearby_fp_client_SetAdvertisement(int mode);
nearby_platform_status nearby_fp_client_Init(
const nearby_fp_client_Callbacks* callbacks);
#ifdef NEARBY_FP_MESSAGE_STREAM
#if NEARBY_FP_MESSAGE_STREAM
// Serializes and sends |message| over Message Stream
nearby_platform_status nearby_fp_client_SendMessage(
uint64_t peer_address, const nearby_message_stream_Message* message);
+166
View File
@@ -17,6 +17,172 @@
#include "nearby_fp_client.h"
#include "nearby_platform_audio.h"
static const nearby_platform_AudioCallbacks* callbacks;
static bool multipoint = false;
static uint8_t switching_preference_flags = 0;
static uint64_t switch_active_peer_address = 0;
static uint8_t switch_active_flags = 0;
static uint64_t switch_active_preferred_audio_source = 0;
static uint64_t switch_back_peer_address = 0;
static uint8_t switch_back_flags = 0;
static uint64_t notify_peer_address = 0;
static uint8_t notify_flags = 0;
static uint64_t drop_peer_address = 0;
static uint8_t drop_flags = 0;
static uint64_t active_peer_address = 0;
bool nearby_platform_GetEarbudRightStatus() { return false; }
bool nearby_platform_GetEarbudLeftStatus() { return false; }
unsigned int nearby_platform_GetAudioConnectionState() {
return NEARBY_PLATFORM_CONNECTION_STATE_A2DP_WITH_AVRCP;
}
bool nearby_platform_OnHead() { return true; }
// Returns true if the device can accept another audio connection without
// dropping any of the existing connections.
bool nearby_platform_CanAcceptConnection() { return false; }
// When the device is in focus mode, connection switching is not allowed
bool nearby_platform_InFocusMode() { return false; }
// Returns true if the current connection is auto-recconnected, meaning it is
// not connected by the user. For multi-point connections, returns true if any
// of the existing connections is auto-reconnected.
bool nearby_platform_AutoReconnected() { return false; }
// Sets a bit in the |bitmap| for every connected peer. The bit stays cleared
// for bonded but not connected peers. The order change is acceptable if it is
// unavoidable, e.g. when users factory reset the headset or when the bonded
// device count reaches the upper limit.
// |length| is the |bitmap| length on input in bytes and used space on output.
// For example, if there are 5 bonded devices, then |length| should be set to 1.
void nearby_platform_GetConnectionBitmap(uint8_t* bitmap, size_t* length) {
if (*length > 0) {
bitmap[0] = 0x09;
*length = 1;
}
}
bool nearby_platform_IsSassOn() { return true; }
bool nearby_platform_IsMultipointConfigurable() { return true; }
bool nearby_platform_IsMultipointOn() { return multipoint; }
bool nearby_platform_IsOnHeadDetectionSupported() { return true; }
bool nearby_platform_IsOnHeadDetectionEnabled() { return false; }
nearby_platform_status nearby_platform_SetMultipoint(uint64_t peer_address,
bool enable) {
multipoint = enable;
return kNearbyStatusOK;
}
nearby_platform_status nearby_platform_SetSwitchingPreference(uint8_t flags) {
switching_preference_flags = flags;
return kNearbyStatusOK;
}
// Gets switching preference flags
uint8_t nearby_platform_GetSwitchingPreference() {
return switching_preference_flags;
}
nearby_platform_status nearby_platform_SwitchActiveAudioSource(
uint64_t peer_address, uint8_t flags, uint64_t preferred_audio_source) {
switch_active_peer_address = peer_address;
switch_active_flags = flags;
switch_active_preferred_audio_source = preferred_audio_source;
return kNearbyStatusOK;
}
nearby_platform_status nearby_platform_SwitchBackAudioSource(
uint64_t peer_address, uint8_t flags) {
switch_back_peer_address = peer_address;
switch_back_flags = flags;
return kNearbyStatusOK;
}
nearby_platform_status nearby_platform_NotifySassInitiatedConnection(
uint64_t peer_address, uint8_t flags) {
notify_peer_address = peer_address;
notify_flags = flags;
return kNearbyStatusOK;
}
nearby_platform_status nearby_platform_SetDropConnectionTarget(
uint64_t peer_address, uint8_t flags) {
drop_peer_address = peer_address;
drop_flags = flags;
return kNearbyStatusOK;
}
uint64_t nearby_platform_GetActiveAudioSource() { return active_peer_address; }
void nearby_test_fakes_SetActiveAudioSource(uint64_t peer_address) {
active_peer_address = peer_address;
}
// Initializes Audio module
nearby_platform_status nearby_platform_AudioInit(
const nearby_platform_AudioCallbacks* audio_interface) {
multipoint = false;
switching_preference_flags = 0;
switch_active_peer_address = 0;
switch_active_flags = 0;
switch_active_preferred_audio_source = 0;
switch_back_peer_address = 0;
switch_back_flags = 0;
notify_peer_address = 0;
notify_flags = 0;
drop_peer_address = 0;
drop_flags = 0;
callbacks = audio_interface;
return kNearbyStatusOK;
}
uint64_t nearby_test_fakes_GetSassSwitchActiveSourcePeerAddress() {
return switch_active_peer_address;
}
uint8_t nearby_test_fakes_GetSassSwitchActiveSourceFlags() {
return switch_active_flags;
}
uint64_t nearby_test_fakes_GetSassSwitchActiveSourcePreferredAudioSource() {
return switch_active_preferred_audio_source;
}
uint64_t nearby_test_fakes_GetSassSwitchBackPeerAddress() {
return switch_back_peer_address;
}
uint8_t nearby_test_fakes_GetSassSwitchBackFlags() { return switch_back_flags; }
uint64_t nearby_test_fakes_GetSassNotifyInitiatedConnectionPeerAddress() {
return notify_peer_address;
}
uint8_t nearby_test_fakes_GetSassNotifyInitiatedConnectionFlags() {
return notify_flags;
}
uint64_t nearby_test_fakes_GetSassDropConnectionTargetPeerAddress() {
return drop_peer_address;
}
uint8_t nearby_test_fakes_GetSassDropConnectionTargetFlags() {
return drop_flags;
}
void nearby_test_fakes_SassMultipointSwitch(uint8_t reason,
uint64_t peer_address,
const char* name) {
callbacks->on_multipoint_switch_event(reason, peer_address, name);
}
+4 -1
View File
@@ -16,12 +16,13 @@
#include "nearby.h"
#include "nearby_platform_battery.h"
static nearby_platform_BatteryInfo test_battery_info = {
constexpr nearby_platform_BatteryInfo kDefaultBatteryInfo = {
.is_charging = true,
.right_bud_battery_level = 80,
.left_bud_battery_level = 85,
.charging_case_battery_level = 90,
.remaining_time_minutes = 100};
static nearby_platform_BatteryInfo test_battery_info = kDefaultBatteryInfo;
static nearby_platform_status get_battery_info_result = kNearbyStatusOK;
static const nearby_platform_BatteryInterface* battery_interface;
@@ -58,5 +59,7 @@ void nearby_test_fakes_SetGetBatteryInfoResult(nearby_platform_status status) {
nearby_platform_status nearby_platform_BatteryInit(
nearby_platform_BatteryInterface* callbacks) {
battery_interface = callbacks;
test_battery_info = kDefaultBatteryInfo;
get_battery_info_result = kNearbyStatusOK;
return kNearbyStatusOK;
}
+13
View File
@@ -30,6 +30,8 @@ static const nearby_platform_BleInterface* ble_interface;
static std::map<nearby_fp_Characteristic, std::vector<uint8_t>> notifications;
static std::vector<uint8_t> advertisement;
static nearby_fp_AvertisementInterval interval;
constexpr int32_t kDefaultPsm = -1;
static int32_t psm = kDefaultPsm;
std::map<nearby_fp_Characteristic, std::vector<uint8_t>>&
nearby_test_fakes_GetGattNotifications() {
@@ -83,6 +85,8 @@ nearby_platform_status nearby_platform_SetAdvertisement(
return kNearbyStatusOK;
}
int32_t nearby_platform_GetMessageStreamPsm() { return psm; }
// Initializes BLE
nearby_platform_status nearby_platform_BleInit(
const nearby_platform_BleInterface* callbacks) {
@@ -91,9 +95,12 @@ nearby_platform_status nearby_platform_BleInit(
advertisement.clear();
interval = kDisabled;
ble_address = kDefaultBleAddress;
psm = kDefaultPsm;
return kNearbyStatusOK;
}
void nearby_test_fakes_SetPsm(int32_t value) { psm = value; }
std::vector<uint8_t>& nearby_test_fakes_GetAdvertisement() {
return advertisement;
}
@@ -125,3 +132,9 @@ nearby_platform_status nearby_fp_fakes_ReceiveAdditionalData(
return ble_interface->on_gatt_write(peer_address, kAdditionalData, request,
length);
}
nearby_platform_status nearby_fp_fakes_GattReadMessageStreamPsm(
uint8_t* output, size_t* length) {
return ble_interface->on_gatt_read(peer_address, kMessageStreamPsm, output,
length);
}
+24 -4
View File
@@ -13,6 +13,7 @@
// limitations under the License.
#include <cstring>
#include <map>
#include <vector>
#include "nearby_platform_bt.h"
@@ -20,13 +21,16 @@
static const uint32_t kFastPairId = 0x101112;
static const int8_t kTxLevel = 33;
static const uint64_t kPublicAddress = 0xA0A1A2A3A4A5;
// No secondary identity address by default.
static const uint64_t kSecondaryPublicAddress = 0;
static const uint32_t kLocalPasskey = 123456;
static uint32_t remote_passkey;
static uint64_t remote_address;
static uint64_t paired_peer_address;
static std::vector<uint8_t> rfcomm_output;
static std::map<uint64_t, std::vector<uint8_t>> rfcomm_outputs;
static std::vector<char> device_name;
static bool pairing_mode = false;
static uint64_t secondary_public_address = kSecondaryPublicAddress;
static const nearby_platform_BtInterface* bt_interface;
// Returns Fast Pair Model Id.
@@ -37,6 +41,10 @@ int8_t nearby_platform_GetTxLevel() { return kTxLevel; }
// Returns public BR/EDR address
uint64_t nearby_platform_GetPublicAddress() { return kPublicAddress; }
uint64_t nearby_platform_GetSecondaryPublicAddress() {
return secondary_public_address;
}
// Returns passkey used during pairing
uint32_t nearby_platfrom_GetPairingPassKey() { return kLocalPasskey; }
@@ -71,6 +79,8 @@ nearby_platform_status nearby_platform_BtInit(
remote_passkey = 0;
paired_peer_address = 0;
pairing_mode = false;
secondary_public_address = kSecondaryPublicAddress;
rfcomm_outputs.clear();
return kNearbyStatusOK;
}
@@ -85,6 +95,10 @@ void nearby_test_fakes_SimulatePairing(uint64_t peer_address) {
}
}
void nearby_test_fakes_SetSecondaryPublicAddress(uint64_t address) {
secondary_public_address = address;
}
uint64_t nearby_test_fakes_GetPairedDevice() { return paired_peer_address; }
void nearby_test_fakes_DevicePaired(uint64_t peer_address) {
@@ -94,14 +108,20 @@ void nearby_test_fakes_DevicePaired(uint64_t peer_address) {
nearby_platform_status nearby_platform_SendMessageStream(uint64_t peer_address,
const uint8_t* message,
size_t length) {
rfcomm_outputs.emplace(peer_address, std::vector<uint8_t>());
auto rfcomm_output = rfcomm_outputs.find(peer_address);
for (int i = 0; i < length; i++) {
rfcomm_output.push_back(message[i]);
rfcomm_output->second.push_back(message[i]);
}
return kNearbyStatusOK;
}
std::vector<uint8_t>& nearby_test_fakes_GetRfcommOutput(uint64_t peer_address) {
return rfcomm_outputs[peer_address];
}
std::vector<uint8_t>& nearby_test_fakes_GetRfcommOutput() {
return rfcomm_output;
return nearby_test_fakes_GetRfcommOutput(paired_peer_address);
}
nearby_platform_status nearby_platform_SetDeviceName(const char* name) {
@@ -128,7 +148,7 @@ void nearby_test_fakes_SetInPairingMode(bool in_pairing_mode) {
pairing_mode = in_pairing_mode;
}
#ifdef NEARBY_FP_MESSAGE_STREAM
#if NEARBY_FP_MESSAGE_STREAM
void nearby_test_fakes_MessageStreamConnected(uint64_t peer_address) {
bt_interface->on_message_stream_connected(peer_address);
}
+79 -15
View File
@@ -25,14 +25,19 @@
void nearby_test_fakes_SetRandomNumber(unsigned int value);
void nearby_test_fakes_SetRandomNumberSequence(std::vector<uint8_t>& value);
void nearby_test_fakes_SetAccountKeys(const uint8_t* input, size_t length);
std::vector<uint8_t> nearby_test_fakes_GetRawAccountKeys();
nearby_platform_status nearby_test_fakes_GattReadModelId(uint8_t* output,
size_t* length);
#ifdef MBEDTLS_FOR_SSL
extern "C" {
#endif
nearby_platform_status nearby_test_fakes_SetAntiSpoofingKey(
const uint8_t private_key[32], const uint8_t public_key[64]);
#ifdef MBEDTLS_FOR_SSL
}
#endif
nearby_platform_status nearby_test_fakes_GenSec256r1Secret(
const uint8_t remote_party_public_key[64], uint8_t secret[32]);
@@ -60,41 +65,79 @@ nearby_platform_status nearby_fp_fakes_ReceiveAccountKeyWrite(
const uint8_t* request, size_t length);
nearby_platform_status nearby_fp_fakes_ReceiveAdditionalData(
const uint8_t* request, size_t length);
nearby_platform_status nearby_fp_fakes_GattReadMessageStreamPsm(uint8_t* output,
size_t* length);
uint64_t nearby_test_fakes_GetPairingRequestAddress();
uint32_t nearby_test_fakes_GetRemotePasskey();
uint64_t nearby_test_fakes_GetPairedDevice();
void nearby_test_fakes_DevicePaired(uint64_t peer_address);
class AccountKeyPair {
public:
AccountKeyPair(uint64_t bt_address, const std::vector<uint8_t>& account_key)
: address_(bt_address), account_key_(account_key) {}
AccountKeyPair(uint64_t bt_address, const uint8_t* account_key)
: address_(bt_address),
account_key_(account_key, account_key + ACCOUNT_KEY_SIZE_BYTES) {}
uint64_t address_;
std::vector<uint8_t> account_key_;
};
struct RawAccountKeyList {
uint8_t num_keys;
nearby_platform_AccountKeyInfo key[NEARBY_MAX_ACCOUNT_KEYS];
};
class AccountKeyList {
public:
explicit AccountKeyList(const std::vector<uint8_t>& raw_values) {
if (raw_values.size() == 0) return;
int key_count = raw_values[0];
for (int i = 0; i < key_count; i++) {
const uint8_t* p = raw_values.data() + 1 + (i * ACCOUNT_KEY_SIZE_BYTES);
keys_.emplace_back(std::vector<uint8_t>(p, p + ACCOUNT_KEY_SIZE_BYTES));
const RawAccountKeyList* list =
reinterpret_cast<const RawAccountKeyList*>(raw_values.data());
if (!list) return;
for (size_t i = 0; i < list->num_keys; i++) {
uint64_t address = 0;
#ifdef NEARBY_FP_ENABLE_SASS
address = list->key[i].peer_address;
#endif /* NEARBY_FP_ENABLE_SASS */
key_pairs_.emplace_back(
address, std::vector<uint8_t>(
list->key[i].account_key,
list->key[i].account_key + ACCOUNT_KEY_SIZE_BYTES));
}
}
explicit AccountKeyList(const std::vector<AccountKeyPair>& key_pairs)
: key_pairs_(key_pairs) {}
size_t size() { return keys_.size(); }
size_t size() { return key_pairs_.size(); }
std::vector<std::vector<uint8_t>>& GetKeys() { return keys_; }
std::vector<uint8_t> GetKey(int index) {
return key_pairs_[index].account_key_;
}
std::vector<uint8_t> GetRawFormat() {
std::vector<uint8_t> result;
result.push_back(size());
for (auto& key : keys_) {
for (auto& v : key) {
result.push_back(v);
RawAccountKeyList account_keys;
account_keys.num_keys = size();
for (size_t i = 0; i < size(); i++) {
#ifdef NEARBY_FP_ENABLE_SASS
account_keys.key[i].peer_address = key_pairs_[i].address_;
#endif /* NEARBY_FP_ENABLE_SASS */
for (size_t j = 0; j < key_pairs_[i].account_key_.size(); j++) {
account_keys.key[i].account_key[j] = key_pairs_[i].account_key_[j];
}
}
return result;
uint8_t* p = reinterpret_cast<uint8_t*>(&account_keys);
return std::vector<uint8_t>(p, p + sizeof(account_keys));
}
private:
std::vector<std::vector<uint8_t>> keys_;
std::vector<AccountKeyPair> key_pairs_;
};
void nearby_test_fakes_SetAccountKeys(const uint8_t* input, size_t length);
void nearby_test_fakes_SetAccountKeys(
std::vector<AccountKeyPair>& account_key_pairs);
void nearby_test_fakes_SetAccountKeys(AccountKeyList& keys);
AccountKeyList nearby_test_fakes_GetAccountKeys();
@@ -112,6 +155,7 @@ void nearby_test_fakes_BatteryTime(uint16_t battery_time);
void nearby_test_fakes_SetGetBatteryInfoResult(nearby_platform_status status);
std::vector<uint8_t>& nearby_test_fakes_GetRfcommOutput();
std::vector<uint8_t>& nearby_test_fakes_GetRfcommOutput(uint64_t peer_address);
void nearby_test_fakes_MessageStreamConnected(uint64_t peer_address);
@@ -130,5 +174,25 @@ void nearby_test_fakes_SetInPairingMode(bool in_pairing_mode);
uint8_t nearby_test_fakes_GetRingCommand(void);
uint16_t nearby_test_fakes_GetRingTimeout(void);
uint64_t nearby_test_fakes_GetSassSwitchActiveSourcePeerAddress();
uint8_t nearby_test_fakes_GetSassSwitchActiveSourceFlags();
uint64_t nearby_test_fakes_GetSassSwitchActiveSourcePreferredAudioSource();
uint64_t nearby_test_fakes_GetSassSwitchBackPeerAddress();
uint8_t nearby_test_fakes_GetSassSwitchBackFlags();
uint64_t nearby_test_fakes_GetSassNotifyInitiatedConnectionPeerAddress();
uint8_t nearby_test_fakes_GetSassNotifyInitiatedConnectionFlags();
uint64_t nearby_test_fakes_GetSassDropConnectionTargetPeerAddress();
uint8_t nearby_test_fakes_GetSassDropConnectionTargetFlags();
void nearby_test_fakes_SassMultipointSwitch(uint8_t reason,
uint64_t peer_address,
const char* name);
#ifdef NEARBY_FP_ENABLE_SASS
void nearby_test_fakes_SetActiveAudioSource(uint64_t peer_address);
#endif /* NEARBY_FP_ENABLE_SASS */
void nearby_test_fakes_SetPsm(int32_t value);
void nearby_test_fakes_SetSecondaryPublicAddress(uint64_t address);
#endif /* NEARBY_TEST_FAKES_H */
@@ -51,6 +51,12 @@ nearby_platform_status nearby_platform_PersistenceInit() {
return kNearbyStatusOK;
}
void nearby_test_fakes_SetAccountKeys(
std::vector<AccountKeyPair>& account_key_pairs) {
AccountKeyList list(account_key_pairs);
nearby_test_fakes_SetAccountKeys(list);
}
void nearby_test_fakes_SetAccountKeys(const uint8_t* input, size_t length) {
storage[kStoredKeyAccountKeyList].assign(input, input + length);
}
+32 -19
View File
@@ -28,9 +28,13 @@
#include "fakes.h"
#include "nearby_platform_se.h"
#pragma GCC diagnostic ignored "-Wunused-function"
static unsigned int random_value = 0;
static std::queue<uint8_t> random_sequence;
static uint8_t private_key_store[32];
static std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)> anti_spoofing_key(
NULL, EVP_PKEY_free);
@@ -54,7 +58,7 @@ uint8_t nearby_platform_Rand() {
}
}
#ifdef NEARBY_FP_ENABLE_ADDITIONAL_DATA
#ifndef NEARBY_PLATFORM_USE_MBEDTLS
static SHA256_CTX sha256_context;
nearby_platform_status nearby_platform_Sha256Start() {
@@ -72,7 +76,6 @@ nearby_platform_status nearby_platform_Sha256Finish(uint8_t out[32]) {
SHA256_Final(out, &sha256_context);
return kNearbyStatusOK;
}
#endif /* NEARBY_FP_ENABLE_ADDITIONAL_DATA */
// Encrypts a data block with AES128 in ECB mode.
nearby_platform_status nearby_platform_Aes128Encrypt(const uint8_t input[16],
@@ -102,6 +105,7 @@ nearby_platform_status nearby_platform_Aes128Decrypt(const uint8_t input[16],
int output_length = 16;
EVP_DecryptInit(ctx, EVP_aes_128_ecb(), key, NULL);
EVP_CIPHER_CTX_set_padding(ctx, 0);
if (1 !=
EVP_DecryptUpdate(ctx, output, &output_length, input, input_length)) {
@@ -111,6 +115,7 @@ nearby_platform_status nearby_platform_Aes128Decrypt(const uint8_t input[16],
EVP_CIPHER_CTX_free(ctx);
return kNearbyStatusOK;
}
#endif /* NEARBY_PLATFORM_USE_MBEDTLS */
static EC_POINT *load_public_key(const uint8_t public_key[64]) {
BN_CTX *bn_ctx;
@@ -132,6 +137,16 @@ static EC_POINT *load_public_key(const uint8_t public_key[64]) {
return point;
}
static BIGNUM *load_private_key(const uint8_t private_key[32]) {
uint8_t buffer[37];
buffer[0] = buffer[1] = buffer[2] = 0;
buffer[3] = 33;
buffer[4] = 0;
memcpy(buffer + 5, private_key, 32);
return BN_mpi2bn(buffer, sizeof(buffer), NULL);
}
#ifdef NEARBY_PLATFORM_HAS_SE
// Generates a shared sec256p1 secret using remote party public key and this
// device's private key.
nearby_platform_status nearby_platform_GenSec256r1Secret(
@@ -177,16 +192,9 @@ nearby_platform_status nearby_platform_GenSec256r1Secret(
EC_POINT_free(peer_point);
EVP_PKEY_free(peerkey);
std::cout << "Secret: " << ArrayToString(secret, 32) << std::endl;
return kNearbyStatusOK;
}
// Initializes secure element module
nearby_platform_status nearby_platform_SecureElementInit() {
random_value = 0;
random_sequence = std::queue<uint8_t>();
return kNearbyStatusOK;
}
#endif /* NEARBY_PLATFORM_HAS_SE */
void nearby_test_fakes_SetRandomNumber(unsigned int value) {
random_value = value;
@@ -196,15 +204,6 @@ void nearby_test_fakes_SetRandomNumberSequence(std::vector<uint8_t> &value) {
for (auto &v : value) random_sequence.push(v);
}
static BIGNUM *load_private_key(const uint8_t private_key[32]) {
uint8_t buffer[37];
buffer[0] = buffer[1] = buffer[2] = 0;
buffer[3] = 33;
buffer[4] = 0;
memcpy(buffer + 5, private_key, 32);
return BN_mpi2bn(buffer, sizeof(buffer), NULL);
}
nearby_platform_status nearby_test_fakes_SetAntiSpoofingKey(
const uint8_t private_key[32], const uint8_t public_key[64]) {
EC_KEY *key;
@@ -224,6 +223,9 @@ nearby_platform_status nearby_test_fakes_SetAntiSpoofingKey(
anti_spoofing_key.reset(EVP_PKEY_new());
if (1 != EVP_PKEY_assign_EC_KEY(anti_spoofing_key.get(), key))
return kNearbyStatusError;
memcpy(private_key_store, private_key, 32);
BN_free(prv);
EC_POINT_free(pub);
return kNearbyStatusOK;
@@ -247,3 +249,14 @@ nearby_platform_status nearby_test_fakes_Aes128Encrypt(
const uint8_t key[AES_MESSAGE_SIZE_BYTES]) {
return nearby_platform_Aes128Encrypt(input, output, key);
}
const uint8_t *nearby_platform_GetAntiSpoofingPrivateKey() {
return private_key_store;
}
// Initializes secure element module
nearby_platform_status nearby_platform_SecureElementInit() {
random_value = 0;
random_sequence = std::queue<uint8_t>();
return kNearbyStatusOK;
}
+15 -11
View File
@@ -22,7 +22,7 @@
#include "nearby.h"
#include "nearby_message_stream.h"
#ifdef NEARBY_FP_MESSAGE_STREAM
#if NEARBY_FP_MESSAGE_STREAM
constexpr uint64_t kPeerAddress = 0x101112;
constexpr size_t kBufferSize = 64;
constexpr size_t kMaxPayloadSize =
@@ -122,11 +122,11 @@ class MessageStreamTest : public ::testing::Test {
.peer_address = kPeerAddress,
.length = sizeof(buffer),
.buffer = buffer};
} * test_fixture;
}* test_fixture;
void MessageStreamTest::SetUp() {
test_fixture = this;
nearby_test_fakes_GetRfcommOutput().clear();
nearby_test_fakes_GetRfcommOutput(kPeerAddress).clear();
nearby_message_stream_Init(&stream_state_);
}
@@ -300,8 +300,9 @@ TEST_F(MessageStreamTest, SendMessageNoPayload) {
Send(&message);
ASSERT_THAT(kExpectedOutput,
ElementsAreArray(nearby_test_fakes_GetRfcommOutput()));
ASSERT_THAT(
kExpectedOutput,
ElementsAreArray(nearby_test_fakes_GetRfcommOutput(kPeerAddress)));
}
TEST_F(MessageStreamTest, SendMessageWithPayload) {
@@ -317,8 +318,9 @@ TEST_F(MessageStreamTest, SendMessageWithPayload) {
Send(&message);
ASSERT_THAT(kExpectedOutput,
ElementsAreArray(nearby_test_fakes_GetRfcommOutput()));
ASSERT_THAT(
kExpectedOutput,
ElementsAreArray(nearby_test_fakes_GetRfcommOutput(kPeerAddress)));
}
TEST_F(MessageStreamTest, SendAck) {
@@ -332,8 +334,9 @@ TEST_F(MessageStreamTest, SendAck) {
SendAck(&message);
ASSERT_THAT(kExpectedOutput,
ElementsAreArray(nearby_test_fakes_GetRfcommOutput()));
ASSERT_THAT(
kExpectedOutput,
ElementsAreArray(nearby_test_fakes_GetRfcommOutput(kPeerAddress)));
}
TEST_F(MessageStreamTest, SendNack) {
@@ -348,8 +351,9 @@ TEST_F(MessageStreamTest, SendNack) {
SendNack(&message, kFailReason);
ASSERT_THAT(kExpectedOutput,
ElementsAreArray(nearby_test_fakes_GetRfcommOutput()));
ASSERT_THAT(
kExpectedOutput,
ElementsAreArray(nearby_test_fakes_GetRfcommOutput(kPeerAddress)));
}
#endif /* NEARBY_FP_MESSAGE_STREAM */
File diff suppressed because it is too large Load Diff
+108
View File
@@ -0,0 +1,108 @@
// 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.
//
// gen_secret module
//
// Implements the nearby_platform_GenSec256r1Secret() function, using the
// MBEDTLS package developed by ARM.
//
// To use the package, uncomment the NEARBY_PLATFORM_USE_MBEDTLS define in
// config.mk.
//
// This routine was separated from the mbedtls.c code because an implementation
// may need to implement the shared secret function using a hardware embedded
// key. In such an implementation, the actual private key is not directly
// accessable, but rather the shared secret is generated using the public key
// and the sequestered private key without ever revealing the private key in
// code.
//
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
#include <mbedtls/aes.h>
#include <mbedtls/ecdh.h>
#include <mbedtls/ecp.h>
#include <mbedtls/error.h>
#include <mbedtls/md.h> /* generic interface */
#include <mbedtls/sha256.h>
#if (MBEDTLS_VERSION_NUMBER >= 0x03000000)
#include <mbedtls/compat-2.x.h>
#endif
#include <nearby_platform_se.h>
#include <stdlib.h>
#ifndef NEARBY_PLATFORM_HAS_SE
static int crypto_rand(void* const seed, uint8_t* const out,
size_t const size) {
(void)seed;
for (size_t i = 0; i < size; i++) {
out[i] = rand() % UINT8_MAX;
}
return 0;
}
/**
* Generates a shared sec256p1 secret using remote party public key and this
* device's private key.
*/
nearby_platform_status nearby_platform_GenSec256r1Secret(
const uint8_t remote_party_public_key[64], uint8_t shared_secret[32]) {
nearby_platform_status status = kNearbyStatusError;
mbedtls_ecp_group grp;
mbedtls_ecp_point pub;
mbedtls_mpi prv;
mbedtls_mpi secret;
mbedtls_ecp_group_init(&grp);
mbedtls_ecp_point_init(&pub);
mbedtls_mpi_init(&prv);
mbedtls_mpi_init(&secret);
mbedtls_mpi_uint p = 1;
pub.Z.p = &p;
pub.Z.n = 1;
const uint8_t* pkp;
pkp = nearby_platform_GetAntiSpoofingPrivateKey();
if (!pkp) {
goto exit;
}
if (mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1) != 0) goto exit;
if (mbedtls_mpi_read_binary(&pub.X, remote_party_public_key, 32) != 0)
goto exit;
if (mbedtls_mpi_read_binary(&pub.Y, remote_party_public_key + 32, 32) != 0)
goto exit;
if (mbedtls_mpi_read_binary(&prv, pkp, 32) != 0) goto exit;
if ((mbedtls_ecdh_compute_shared(&grp, &secret, &pub, &prv, crypto_rand,
NULL)) != 0)
goto exit;
if (mbedtls_mpi_write_binary(&secret, shared_secret, 32) != 0) goto exit;
status = kNearbyStatusOK;
exit:
pub.Z.p = NULL;
pub.Z.n = 0;
mbedtls_ecp_group_free(&grp);
mbedtls_ecp_point_free(&pub);
mbedtls_mpi_free(&prv);
mbedtls_mpi_free(&secret);
return status;
}
#endif /* NEARBY_PLATFORM_HAS_SE */
+121
View File
@@ -0,0 +1,121 @@
// 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.
//
// MBEDTLS module
//
// Purpose: Implements the cryptographic functions used by fp-provider using the
// MBEDTLS package developed by ARM.
//
// To use the package, uncomment the NEARBY_PLATFORM_USE_MBEDTLS define in
// config.mk.
//
// The following features are implemented here:
//
// nearby_platform_Sha256Start(), nearby_platform_Sha256Update(),
// nearby_platform_Sha256Finish()
//
// Implements the SHA function across and arbitrary length block. Partial
// blocks can be sent, and the resulting SHA will be over all blocks.
//
// nearby_platform_Aes128Encrypt(), nearby_platform_Aes128Decrypt()
//
// Encrypt and decrypt a block of data with a given key.
//
// Note that the required function nearby_platform_GenSec256r1Secret() is in a
// separate file, gen_secret.c.
//
#include <mbedtls/aes.h>
#include <mbedtls/ecdh.h>
#include <mbedtls/ecp.h>
#include <mbedtls/md.h> /* generic interface */
#include <mbedtls/sha256.h>
#if (MBEDTLS_VERSION_NUMBER >= 0x03000000)
#include <mbedtls/compat-2.x.h>
#endif
#include <nearby_platform_se.h>
static mbedtls_sha256_context sha256_ctx;
nearby_platform_status nearby_platform_Sha256Start() {
nearby_platform_status status = kNearbyStatusError;
mbedtls_sha256_init(&sha256_ctx);
if (mbedtls_sha256_starts_ret(&sha256_ctx, 0) == 0) {
status = kNearbyStatusOK;
} else {
mbedtls_sha256_free(&sha256_ctx);
}
return status;
}
nearby_platform_status nearby_platform_Sha256Update(const void* data,
size_t length) {
nearby_platform_status status = kNearbyStatusError;
if (mbedtls_sha256_update_ret(&sha256_ctx, (const unsigned char*)data,
length) == 0) {
status = kNearbyStatusOK;
} else {
mbedtls_sha256_free(&sha256_ctx);
}
return status;
}
nearby_platform_status nearby_platform_Sha256Finish(uint8_t out[32]) {
nearby_platform_status status = kNearbyStatusError;
if (mbedtls_sha256_finish_ret(&sha256_ctx, out) == 0) {
status = kNearbyStatusOK;
}
mbedtls_sha256_free(&sha256_ctx);
return status;
}
/**
* Encrypts a data block with AES128 in ECB mode.
*/
nearby_platform_status nearby_platform_Aes128Encrypt(
const uint8_t input[AES_MESSAGE_SIZE_BYTES],
uint8_t output[AES_MESSAGE_SIZE_BYTES],
const uint8_t key[AES_MESSAGE_SIZE_BYTES]) {
nearby_platform_status status = kNearbyStatusError;
mbedtls_aes_context ctx;
mbedtls_aes_init(&ctx);
if (mbedtls_aes_setkey_enc(&ctx, key, 128) != 0) goto exit;
if (mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, input, output) != 0)
goto exit;
status = kNearbyStatusOK;
exit:
mbedtls_aes_free(&ctx);
return status;
}
/**
* Decrypts a data block with AES128 in ECB mode.
*/
nearby_platform_status nearby_platform_Aes128Decrypt(
const uint8_t input[AES_MESSAGE_SIZE_BYTES],
uint8_t output[AES_MESSAGE_SIZE_BYTES],
const uint8_t key[AES_MESSAGE_SIZE_BYTES]) {
nearby_platform_status status = kNearbyStatusError;
mbedtls_aes_context ctx;
mbedtls_aes_init(&ctx);
if (mbedtls_aes_setkey_dec(&ctx, key, 128) != 0) goto exit;
if (mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_DECRYPT, input, output) != 0)
goto exit;
status = kNearbyStatusOK;
exit:
mbedtls_aes_free(&ctx);
return status;
}
+38 -5
View File
@@ -16,31 +16,64 @@
#define NEARBY_CONFIG_H
// Support FP Battery Notification extension
#define NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
#ifndef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
#define NEARBY_FP_ENABLE_BATTERY_NOTIFICATION 1
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
// Support FP Additional Data extension
#define NEARBY_FP_ENABLE_ADDITIONAL_DATA
#ifndef NEARBY_FP_ENABLE_ADDITIONAL_DATA
#define NEARBY_FP_ENABLE_ADDITIONAL_DATA 1
#endif /* NEARBY_FP_ENABLE_ADDITIONAL_DATA */
// Personalized name max size in bytes
#define PERSONALIZED_NAME_MAX_SIZE 64
// Support FP Message Stream extension
#define NEARBY_FP_MESSAGE_STREAM
#ifndef NEARBY_FP_MESSAGE_STREAM
#define NEARBY_FP_MESSAGE_STREAM 1
#endif /* NEARBY_FP_MESSAGE_STREAM */
// Number of salt bytes to use in advertisements
#define NEARBY_FP_SALT_SIZE 2
// Does the platform have a native BLE address rotation routine?
// #define NEARBY_FP_HAVE_BLE_ADDRESS_ROTATION
// Support Smart Audio Source Switching
// #define NEARBY_FP_ENABLE_SASS
// The maximum size in bytes of additional data in a message in Message Stream.
// Bigger payloads will be truncated.
#define MAX_MESSAGE_STREAM_PAYLOAD_SIZE 8
#define MAX_MESSAGE_STREAM_PAYLOAD_SIZE 22
// The maximum number of concurrent RFCOMM connections
#define NEARBY_MAX_RFCOMM_CONNECTIONS 2
// Support Retroactive pairing extension
#define NEARBY_FP_RETROACTIVE_PAIRING
#ifndef NEARBY_FP_RETROACTIVE_PAIRING
#define NEARBY_FP_RETROACTIVE_PAIRING 1
#endif /* NEARBY_FP_RETROACTIVE_PAIRING */
// The maximum number of concurrent retroactive pairing process
#define NEARBY_MAX_RETROACTIVE_PAIRING 2
// Is this platform a BLE-only device?
#ifndef NEARBY_FP_BLE_ONLY
#define NEARBY_FP_BLE_ONLY 0
#endif /* NEARBY_FP_BLE_ONLY */
// Does this device prefer BLE bonding?
#ifndef NEARBY_FP_PREFER_BLE_BONDING
#define NEARBY_FP_PREFER_BLE_BONDING 0
#endif /* NEARBY_FP_PREFER_BLE_BONDING */
// Does this device prefer LE transport for FP Message Stream?
// When this feature is On, the Seeker will try to connect to the provider over
// an L2CAP channel. When this feature is Off, the Seeker connects over RFCOMM.
#ifndef NEARBY_FP_PREFER_LE_TRANSPORT
#define NEARBY_FP_PREFER_LE_TRANSPORT 0
#endif /* NEARBY_FP_PREFER_LE_TRANSPORT */
// The maximum number of account keys that can be stored on the device.
#define NEARBY_MAX_ACCOUNT_KEYS 5
#endif /* NEARBY_CONFIG_H */
+112
View File
@@ -112,6 +112,10 @@ typedef struct {
// Handled by: Client app
#define MESSAGE_CODE_PLATFORM_TYPE 8
// Direction: Provider -> Seeker
// Handled by: Nearby Fast Pair library
#define MESSAGE_CODE_SESSION_NONCE 0x0A
// Message group Device Action Event
#define MESSAGE_GROUP_DEVICE_ACTION_EVENT 4
@@ -137,4 +141,112 @@ typedef struct {
uint8_t *data;
} nearby_event_MessageStreamReceived;
// Message group Smart Audio Source Switching
#define MESSAGE_GROUP_SASS 7
// Direction: Both
// Handled by: Nearby Fast Pair library
// Encrypted: No
// Signed: No
// ACK required: No
#define MESSAGE_CODE_SASS_GET_CAPABILITY 0x10
// Direction: Both
// Handled by: Nearby Fast Pair library
// Encrypted: No
// Signed: Yes
// ACK required: Yes
#define MESSAGE_CODE_SASS_NOTIFY_CAPABILITY 0x11
// Direction: Seeker -> Provider
// Handled by: Nearby Fast Pair library
// Encrypted: No
// Signed: Yes
// ACK required: Yes
#define MESSAGE_CODE_SASS_SET_MULTIPOINT_STATE 0x12
// Direction: Seeker -> Provider
// Handled by: Nearby Fast Pair library
// Encrypted: No
// Signed: Yes
// ACK required: Yes
#define MESSAGE_CODE_SASS_SET_SWITCHING_PREFERENCE 0x20
// Direction: Both
// Handled by: Nearby Fast Pair library
// Encrypted: No
// Signed: No
// ACK required: No
#define MESSAGE_CODE_SASS_GET_SWITCHING_PREFERENCE 0x21
// Direction: Both
// Handled by: Nearby Fast Pair library
// Encrypted: No
// Signed: Yes
// ACK required: Yes
#define MESSAGE_CODE_SASS_NOTIFY_SWITCHING_PREFERENCE 0x22
// Direction: Seeker -> Provider
// Handled by: Nearby Fast Pair library
// Encrypted: No
// Signed: Yes
// ACK required: Yes
#define MESSAGE_CODE_SASS_SWITCH_ACTIVE_AUDIO_SOURCE 0x30
// Direction: Seeker -> Provider
// Handled by: Nearby Fast Pair library
// Encrypted: No
// Signed: Yes
// ACK required: Yes
#define MESSAGE_CODE_SASS_SWITCH_BACK_AUDIO_SOURCE 0x31
// Direction: Provider -> Seeker
// Handled by: Nearby Fast Pair library
// Encrypted: No
// Signed: No
// ACK required: No
#define MESSAGE_CODE_SASS_NOTIFY_MULTIPOINT_SWITCH_EVENT 0x32
// Direction: Seeker -> Provider
// Handled by: Nearby Fast Pair library
// Encrypted: No
// Signed: No
// ACK required: No
#define MESSAGE_CODE_SASS_GET_CONNECTION_STATUS 0x33
// Direction: Provider -> Seeker
// Handled by: Nearby Fast Pair library
// Encrypted: Yes
// Signed: No
// ACK required: No
#define MESSAGE_CODE_SASS_NOTIFY_CONNECTION_STATUS 0x34
// Direction: Seeker -> Provider
// Handled by: Nearby Fast Pair library
// Encrypted: No
// Signed: Yes
// ACK required: Yes
#define MESSAGE_CODE_SASS_NOTIFY_SASS_INITIATED_CONNECTION 0x40
// Direction: Seeker -> Provider
// Handled by: Nearby Fast Pair library
// Encrypted: No
// Signed: Yes
// ACK required: Yes
#define MESSAGE_CODE_SASS_IN_USE_ACCOUNT_KEY 0x41
// Direction: Seeker -> Provider
// Handled by: Nearby Fast Pair library
// Encrypted: No
// Signed: Yes
// ACK required: Yes
#define MESSAGE_CODE_SASS_SEND_CUSTOM_DATA 0x42
// Direction: Seeker -> Provider
// Handled by: Nearby Fast Pair library
// Encrypted: No
// Signed: Yes
// ACK required: Yes
#define MESSAGE_CODE_SASS_SET_DROP_CONNECTION_TARGET 0x43
#endif /* NEARBY_EVENT_H */
+476 -95
View File
@@ -18,16 +18,34 @@
#include "nearby.h"
#include "nearby_assert.h"
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
#include "nearby_message_stream.h"
#if NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
#include "nearby_platform_battery.h"
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
#include "nearby_platform_audio.h"
#include "nearby_platform_bt.h"
#include "nearby_platform_persistence.h"
#include "nearby_platform_se.h"
#include "nearby_trace.h"
#include "nearby_utils.h"
#define ACCOUNT_KEY_LIST_SIZE_BYTES 81
typedef struct {
uint8_t num_keys;
nearby_platform_AccountKeyInfo key[NEARBY_MAX_ACCOUNT_KEYS];
} AccountKeyList;
// Advertisement header with SASS format (version 1)
#define SASS_HEADER 0x10
// Offset to the advertisement header
#define HEADER_OFFSET 4
// Offset to account data in a non-discoverable advertisement
#define ACCOUNT_KEY_DATA_OFFSET 5
#define LTV_HEADER_SIZE 1
#define ACCOUNT_KEY_LIST_SIZE_BYTES sizeof(AccountKeyList)
#define SHOW_PAIRING_INDICATION_BYTE 0
#define DONT_SHOW_PAIRING_INDICATION_BYTE 2
#define SHOW_BATTERY_INDICATION_BYTE 0x33
@@ -37,88 +55,175 @@
// In the battery values, the highest bit indicates charging, the lower 7 are
// the battery level
#define BATTERY_LEVEL_MASK 0x7F
#define SALT_FIELD_LENGTH_AND_TYPE_BYTE 0x11
#define SALT_SIZE_BYTES 1
#define SALT_FIELD_LENGTH_AND_TYPE_BYTE ((NEARBY_FP_SALT_SIZE << 4) | 1)
#define BATTERY_INFO_SIZE_BYTES 4
// Response for seekers that don't support BLE-only devices.
#define KEY_BASED_PAIRING_RESPONSE_FLAG 0x01
// Response for seekers that support BLE-only devices.
#define KEY_BASED_PAIRING_EXTENDED_RESPONSE_FLAG 0x02
#define GAP_DATA_TYPE_SERVICE_DATA_UUID 0x16
#define FP_SERVICE_UUID 0xFE2C
#define GAP_DATA_TYPE_TX_POWER_LEVEL_UUID 0x0A
#define TX_POWER_DATA_SIZE 2
#define SASS_HEADER_SIZE 1
static uint8_t account_key_list[ACCOUNT_KEY_LIST_SIZE_BYTES];
// (type + length) + state + custom_data
#define MIN_SASS_ADERTISEMENT_SIZE 3
#define SASS_CONN_STATE_ON_HEAD_OFFSET 7
#define SASS_CONN_STATE_AVAIL_OFFSET 6
#define SASS_CONN_STATE_FOCUS_OFFSET 5
#define SASS_CONN_STATE_AUTO_RECONNECTED_OFFSET 4
#define SASS_CONN_STATE_MASK 0x0F
#define MOST_RECENTLY_USED_ACCOUNT_KEY_BIT 0x01
#define IN_USE_ACCOUNT_KEY_BIT 0x02
// SASS Configuration Flags
#define SASS_CF_ON_OFFSET 15
#define SASS_CF_MULTIPOINT_CONFIGURABLE 14
#define SASS_CF_MULTIPOINT_ON 13
#define SASS_CF_OHD_SUPPORTED 12
#define SASS_CF_OHD_ENABLED 11
#define MESSAGE_AUTHENTICATION_CODE_SIZE 8
#define BOOL_TO_INT(x) ((x) ? 1 : 0)
static const uint8_t kSassRrdKey[] = {'S', 'A', 'S', 'S', '-', 'R',
'R', 'D', '-', 'K', 'E', 'Y'};
static AccountKeyList account_key_list;
static uint8_t sha_buffer[32];
static uint8_t key_and_salt[ACCOUNT_KEY_SIZE_BYTES + SALT_SIZE_BYTES +
BATTERY_INFO_SIZE_BYTES];
static size_t GetAccountKeyListUsedSize() {
return nearby_fp_GetAccountKeyOffset(nearby_fp_GetAccountKeyCount());
#define RETURN_IF_ERROR(X) \
do { \
nearby_platform_status status = X; \
if (kNearbyStatusOK != status) return status; \
} while (0)
// Returns the Length part of Length|Type field.
static int GetLtLength(uint8_t value) { return value >> 4; }
// Returns the Type part of Length|Type field.
static int GetLtType(uint8_t value) { return value & 0x0F; }
const uint8_t* nearby_fp_FindLtv(const uint8_t* advertisement, int type) {
size_t offset = ACCOUNT_KEY_DATA_OFFSET;
size_t advertisement_length = advertisement[0];
while (offset < advertisement_length) {
if (type == GetLtType(advertisement[offset])) {
return advertisement + offset;
}
offset += GetLtLength(advertisement[offset]) + LTV_HEADER_SIZE;
}
return NULL;
}
size_t nearby_fp_GetAccountKeyCount() { return account_key_list[0]; }
static size_t GetAccountKeyListUsedSize() { return sizeof(account_key_list); }
size_t nearby_fp_GetAccountKeyOffset(unsigned key_number) {
return 1 + key_number * ACCOUNT_KEY_SIZE_BYTES;
// Returns true if |account_key| is present in [0..end_offset) range in acount
// key list
static bool IsAccountKeyInRange(const uint8_t* account_key, size_t end_offset) {
for (size_t i = 0; i < end_offset; i++) {
if (!memcmp(account_key, nearby_fp_GetAccountKey(i)->account_key,
ACCOUNT_KEY_SIZE_BYTES)) {
return true;
}
}
return false;
}
const uint8_t* nearby_fp_GetAccountKey(unsigned key_number) {
size_t nearby_fp_GetAccountKeyCount() { return account_key_list.num_keys; }
size_t nearby_fp_GetUniqueAccountKeyCount() {
size_t count = 0;
int offset = 0;
while (true) {
offset = nearby_fp_GetNextUniqueAccountKeyIndex(offset);
if (offset == -1) break;
count++;
offset++;
}
return count;
}
int nearby_fp_GetNextUniqueAccountKeyIndex(int offset) {
while (offset < nearby_fp_GetAccountKeyCount()) {
const nearby_platform_AccountKeyInfo* account_key_info =
nearby_fp_GetAccountKey(offset);
if (IsAccountKeyInRange(account_key_info->account_key, offset)) {
offset++;
} else {
return offset;
}
}
return -1;
}
const nearby_platform_AccountKeyInfo* nearby_fp_GetAccountKey(
unsigned key_number) {
NEARBY_ASSERT(key_number < nearby_fp_GetAccountKeyCount());
return account_key_list + nearby_fp_GetAccountKeyOffset(key_number);
return &account_key_list.key[key_number];
}
void nearby_fp_MarkAccountKeyAsActive(unsigned key_number) {
NEARBY_ASSERT(key_number < nearby_fp_GetAccountKeyCount());
uint8_t tmp[ACCOUNT_KEY_SIZE_BYTES];
if (key_number == 0) return;
// Move the key to the top of the list
nearby_fp_CopyAccountKey(tmp, key_number);
memmove(account_key_list + nearby_fp_GetAccountKeyOffset(1),
account_key_list + nearby_fp_GetAccountKeyOffset(0),
key_number * ACCOUNT_KEY_SIZE_BYTES);
memcpy(account_key_list + nearby_fp_GetAccountKeyOffset(0), tmp,
ACCOUNT_KEY_SIZE_BYTES);
nearby_platform_AccountKeyInfo tmp = account_key_list.key[key_number];
for (unsigned i = key_number; i > 0; i--) {
account_key_list.key[i] = account_key_list.key[i - 1];
}
account_key_list.key[0] = tmp;
}
void nearby_fp_CopyAccountKey(uint8_t* dest, unsigned key_number) {
size_t offset = nearby_fp_GetAccountKeyOffset(key_number);
NEARBY_ASSERT(offset + ACCOUNT_KEY_SIZE_BYTES <= ACCOUNT_KEY_LIST_SIZE_BYTES);
memcpy(dest, account_key_list + offset, ACCOUNT_KEY_SIZE_BYTES);
void nearby_fp_CopyAccountKey(nearby_platform_AccountKeyInfo* dest,
unsigned key_number) {
NEARBY_ASSERT(key_number < nearby_fp_GetAccountKeyCount());
*dest = account_key_list.key[key_number];
}
static unsigned combineNibbles(unsigned high, unsigned low) {
return ((high << 4) & 0xF0) | (low & 0x0F);
}
void nearby_fp_AddAccountKey(const uint8_t key[ACCOUNT_KEY_SIZE_BYTES]) {
// Returns |a| == |b|
static bool AccountKeyInfoEquals(const nearby_platform_AccountKeyInfo* a,
const nearby_platform_AccountKeyInfo* b) {
return a == b ||
(
#ifdef NEARBY_FP_ENABLE_SASS
a->peer_address == b->peer_address &&
#endif /* NEARBY_FP_ENABLE_SASS */
!memcmp(a->account_key, b->account_key, ACCOUNT_KEY_SIZE_BYTES));
}
void nearby_fp_AddAccountKey(const nearby_platform_AccountKeyInfo* key) {
// Find if the key is already on the list
unsigned i;
size_t key_count;
size_t length;
size_t max_bytes;
key_count = nearby_fp_GetAccountKeyCount();
for (i = 0; i < key_count; i++) {
if (!memcmp(key, nearby_fp_GetAccountKey(i), ACCOUNT_KEY_SIZE_BYTES)) {
if (AccountKeyInfoEquals(key, nearby_fp_GetAccountKey(i))) {
nearby_fp_MarkAccountKeyAsActive(i);
return;
}
}
// Insert `key` at the list top
length = key_count * ACCOUNT_KEY_SIZE_BYTES;
max_bytes = ACCOUNT_KEY_LIST_SIZE_BYTES - nearby_fp_GetAccountKeyOffset(1);
if (length > max_bytes) {
// Buffer is full, the last key will fall off the edge
length = max_bytes;
} else {
// We have room for one more key
account_key_list[0]++;
// Insert `key` at the list top. If the list is full, the last key will fall
// off the edge
size_t keys_to_copy = key_count < NEARBY_MAX_ACCOUNT_KEYS
? key_count
: NEARBY_MAX_ACCOUNT_KEYS - 1;
for (i = keys_to_copy; i > 0; i--) {
account_key_list.key[i] = account_key_list.key[i - 1];
}
account_key_list.key[0] = *key;
if (key_count < NEARBY_MAX_ACCOUNT_KEYS) {
account_key_list.num_keys++;
}
memmove(account_key_list + nearby_fp_GetAccountKeyOffset(1),
account_key_list + nearby_fp_GetAccountKeyOffset(0), length);
memcpy(account_key_list + nearby_fp_GetAccountKeyOffset(0), key,
ACCOUNT_KEY_SIZE_BYTES);
}
size_t nearby_fp_CreateDiscoverableAdvertisement(uint8_t* output,
size_t length) {
NEARBY_ASSERT(length >= DISCOVERABLE_ADV_SIZE_BYTES);
@@ -144,7 +249,7 @@ size_t nearby_fp_CreateDiscoverableAdvertisement(uint8_t* output,
return i;
}
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
#if NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
void SerializeBatteryInfo(uint8_t* output,
const nearby_platform_BatteryInfo* battery_info) {
uint8_t charging = battery_info->is_charging ? BATTERY_INFO_CHARGING
@@ -169,7 +274,7 @@ static void AddBatteryInfo(uint8_t* output, size_t length,
static size_t CreateNondiscoverableAdvertisement(
uint8_t* output, size_t length, bool show_pairing_indicator
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
#if NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
,
bool show_battery_indicator, const nearby_platform_BatteryInfo* battery_info
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
@@ -179,6 +284,7 @@ static size_t CreateNondiscoverableAdvertisement(
NEARBY_ASSERT(length >= kHeaderSize);
unsigned i = 1;
uint8_t salt[NEARBY_FP_SALT_SIZE];
// service data UUID
output[i++] = GAP_DATA_TYPE_SERVICE_DATA_UUID;
@@ -189,29 +295,26 @@ static size_t CreateNondiscoverableAdvertisement(
i += FP_SERVICE_UUID_SIZE;
// service data
uint8_t salt = nearby_platform_Rand();
size_t n = nearby_fp_GetAccountKeyCount();
for (int si = 0; si < NEARBY_FP_SALT_SIZE; si++)
salt[si] = nearby_platform_Rand();
size_t n = nearby_fp_GetUniqueAccountKeyCount();
if (n == 0) {
const unsigned kMessageSize = 2;
NEARBY_ASSERT(length >= i + kMessageSize);
output[i++] = 0x00;
output[i++] = 0x00;
} else {
const unsigned kFlagFilterAndSaltSize = 4;
const unsigned kFlagFilterAndSaltSize = 3 + NEARBY_FP_SALT_SIZE;
const size_t s = (6 * n + 15) / 5;
const size_t kAccountKeyDataSize = s + kFlagFilterAndSaltSize;
NEARBY_ASSERT(length >= i + kAccountKeyDataSize);
unsigned used_key_and_salt_size = ACCOUNT_KEY_SIZE_BYTES + SALT_SIZE_BYTES;
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
#if NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
// We need to add the battery info first because it's used as salt
if (battery_info != NULL) {
uint8_t battery_chunk_offset = i + kAccountKeyDataSize;
uint8_t* battery_chunk = output + battery_chunk_offset;
used_key_and_salt_size += BATTERY_INFO_SIZE_BYTES;
AddBatteryInfo(battery_chunk, length - battery_chunk_offset,
show_battery_indicator, battery_info);
memcpy(key_and_salt + ACCOUNT_KEY_SIZE_BYTES + SALT_SIZE_BYTES,
battery_chunk, BATTERY_INFO_SIZE_BYTES);
}
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
// flags
@@ -221,21 +324,10 @@ static size_t CreateNondiscoverableAdvertisement(
? SHOW_PAIRING_INDICATION_BYTE
: DONT_SHOW_PAIRING_INDICATION_BYTE);
memset(output + i, 0, s);
key_and_salt[ACCOUNT_KEY_SIZE_BYTES] = salt;
unsigned k, j;
for (k = 0; k < n; k++) {
nearby_fp_CopyAccountKey(key_and_salt, k);
nearby_fp_Sha256(sha_buffer, key_and_salt, used_key_and_salt_size);
for (j = 0; j < 8; j++) {
uint32_t x = nearby_utils_GetBigEndian32(sha_buffer + 4 * j);
uint32_t m = x % (s * 8);
output[i + (m / 8)] |= (1 << (m % 8));
}
}
i += s;
output[i++] = SALT_FIELD_LENGTH_AND_TYPE_BYTE;
output[i++] = salt;
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
for (int si = 0; si < NEARBY_FP_SALT_SIZE; si++) output[i++] = salt[si];
#if NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
if (battery_info != NULL) {
i += BATTERY_INFO_SIZE_BYTES;
}
@@ -250,7 +342,7 @@ static size_t CreateNondiscoverableAdvertisement(
size_t nearby_fp_CreateNondiscoverableAdvertisement(
uint8_t* output, size_t length, bool show_pairing_indicator) {
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
#if NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
return CreateNondiscoverableAdvertisement(
output, length, show_pairing_indicator, false, NULL);
#else
@@ -259,10 +351,11 @@ size_t nearby_fp_CreateNondiscoverableAdvertisement(
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
}
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
#if NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
// |battery_info| can be NULL
size_t nearby_fp_CreateNondiscoverableAdvertisementWithBattery(
uint8_t* output, size_t length, bool show_pairing_indicator,
bool show_battery_indicator,
const nearby_platform_BatteryInfo* battery_info) {
return CreateNondiscoverableAdvertisement(
@@ -271,18 +364,94 @@ size_t nearby_fp_CreateNondiscoverableAdvertisementWithBattery(
}
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
static const uint8_t* FindBatteryInfoLt(const uint8_t* advertisement) {
const uint8_t* battery_info = nearby_fp_FindLtv(
advertisement, BATTERY_INFO_WITH_UI_INDICATION_FIELD_TYPE);
if (battery_info == NULL) {
battery_info = nearby_fp_FindLtv(
advertisement, BATTERY_INFO_WITHOUT_UI_INDICATION_FIELD_TYPE);
}
return battery_info;
}
size_t nearby_fp_SetBloomFilter(uint8_t* advertisement, bool use_sass_format,
const uint8_t* in_use_key) {
unsigned key_offset = 0;
if (advertisement[ACCOUNT_KEY_DATA_OFFSET] == 0) {
NEARBY_TRACE(INFO, "Empty account key filter");
return 0;
}
// Salt is mandatory and is included in the calculation without the LT header
const uint8_t* salt_field = nearby_fp_FindLtv(advertisement, SALT_FIELD_TYPE);
NEARBY_ASSERT(salt_field != NULL);
const uint8_t* salt = salt_field + LTV_HEADER_SIZE;
int salt_length = GetLtLength(*salt_field);
// Battery info is optional and is included in the calculation with the LT
// header
const uint8_t* battery_info_field = FindBatteryInfoLt(advertisement);
int battery_info_field_length =
battery_info_field != NULL
? GetLtLength(*battery_info_field) + LTV_HEADER_SIZE
: 0;
// Random resolvable field is optional and is included in the calculation with
// the LT header
const uint8_t* random_resolvable_field =
nearby_fp_FindLtv(advertisement, RANDOM_RESOLVABLE_FIELD_TYPE);
int random_resolvable_field_length =
random_resolvable_field != NULL
? GetLtLength(*random_resolvable_field) + LTV_HEADER_SIZE
: 0;
const size_t n = nearby_fp_GetUniqueAccountKeyCount();
const size_t s = (6 * n + 15) / 5;
NEARBY_ASSERT(s == GetLtLength(advertisement[ACCOUNT_KEY_DATA_OFFSET]));
uint8_t* output = advertisement + ACCOUNT_KEY_DATA_OFFSET + LTV_HEADER_SIZE;
memset(output, 0, s);
for (size_t k = 0; k < n; k++) {
key_offset = nearby_fp_GetNextUniqueAccountKeyIndex(key_offset);
NEARBY_ASSERT(key_offset >= 0);
const uint8_t* key = nearby_fp_GetAccountKey(key_offset)->account_key;
uint8_t flags = key[0];
if (use_sass_format) {
if (in_use_key != NULL) {
if (!memcmp(key, in_use_key, ACCOUNT_KEY_SIZE_BYTES)) {
flags |= IN_USE_ACCOUNT_KEY_BIT;
}
} else if (k == 0) {
// The first key is the most recently used one
flags |= MOST_RECENTLY_USED_ACCOUNT_KEY_BIT;
}
}
key_offset++;
nearby_platform_Sha256Start();
nearby_platform_Sha256Update(&flags, sizeof(flags));
nearby_platform_Sha256Update(key + sizeof(flags),
ACCOUNT_KEY_SIZE_BYTES - sizeof(flags));
nearby_platform_Sha256Update(salt, salt_length);
nearby_platform_Sha256Update(battery_info_field, battery_info_field_length);
nearby_platform_Sha256Update(random_resolvable_field,
random_resolvable_field_length);
nearby_platform_Sha256Finish(sha_buffer);
for (unsigned j = 0; j < 8; j++) {
uint32_t x = nearby_utils_GetBigEndian32(sha_buffer + 4 * j);
uint32_t m = x % (s * 8);
output[m / 8] |= (1 << (m % 8));
}
}
if (use_sass_format) {
advertisement[HEADER_OFFSET] = SASS_HEADER;
}
return s;
}
size_t nearby_fp_AppendTxPower(uint8_t* advertisement, size_t length,
int8_t tx_power) {
size_t offset = 0;
NEARBY_ASSERT(length >= 1 + TX_POWER_DATA_SIZE);
// tx power level data size
advertisement[offset++] = TX_POWER_DATA_SIZE;
// tx power level UUID
advertisement[offset++] = GAP_DATA_TYPE_TX_POWER_LEVEL_UUID;
// tx power level
advertisement[offset++] = tx_power;
@@ -291,13 +460,14 @@ size_t nearby_fp_AppendTxPower(uint8_t* advertisement, size_t length,
nearby_platform_status nearby_fp_LoadAccountKeys() {
size_t length = sizeof(account_key_list);
memset(account_key_list, 0, length);
return nearby_platform_LoadValue(kStoredKeyAccountKeyList, account_key_list,
&length);
memset(&account_key_list, 0, length);
return nearby_platform_LoadValue(kStoredKeyAccountKeyList,
(uint8_t*)&account_key_list, &length);
}
nearby_platform_status nearby_fp_SaveAccountKeys() {
return nearby_platform_SaveValue(kStoredKeyAccountKeyList, account_key_list,
return nearby_platform_SaveValue(kStoredKeyAccountKeyList,
(uint8_t*)&account_key_list,
GetAccountKeyListUsedSize());
}
@@ -327,51 +497,107 @@ nearby_platform_status nearby_fp_CreateSharedSecret(
}
nearby_platform_status nearby_fp_CreateRawKeybasedPairingResponse(
uint8_t output[AES_MESSAGE_SIZE_BYTES]) {
output[0] = KEY_BASED_PAIRING_RESPONSE_FLAG;
nearby_utils_CopyBigEndian(output + 1, nearby_platform_GetPublicAddress(),
uint8_t output[AES_MESSAGE_SIZE_BYTES], bool extended_response) {
int i = 0;
uint64_t secondary_address = 0;
if (extended_response) {
secondary_address = nearby_platform_GetSecondaryPublicAddress();
output[i++] = KEY_BASED_PAIRING_EXTENDED_RESPONSE_FLAG;
uint8_t flags = 0;
#if NEARBY_FP_BLE_ONLY
flags |= 0x80;
#endif /* NEARBY_FP_BLE_ONLY */
#if NEARBY_FP_PREFER_BLE_BONDING
flags |= 0x40;
#endif /* NEARBY_FP_PREFER_BLE_BONDING */
#if NEARBY_FP_PREFER_LE_TRANSPORT
flags |= 0x20;
#endif /* NEARBY_FP_PREFER_LE_TRANSPORT */
output[i++] = flags;
// Number of the Providers addresses. The number is either 1 or 2.
output[i++] = secondary_address != 0 ? 2 : 1;
} else {
output[i++] = KEY_BASED_PAIRING_RESPONSE_FLAG;
}
nearby_utils_CopyBigEndian(&output[i], nearby_platform_GetPublicAddress(),
BT_ADDRESS_LENGTH);
int i;
for (i = 1 + BT_ADDRESS_LENGTH; i < AES_MESSAGE_SIZE_BYTES; i++) {
i += BT_ADDRESS_LENGTH;
if (secondary_address != 0) {
nearby_utils_CopyBigEndian(&output[i], secondary_address,
BT_ADDRESS_LENGTH);
i += BT_ADDRESS_LENGTH;
}
for (; i < AES_MESSAGE_SIZE_BYTES; i++) {
output[i] = nearby_platform_Rand();
}
return kNearbyStatusOK;
}
#ifdef NEARBY_FP_ENABLE_ADDITIONAL_DATA
#define HMAC_SHA256_KEY_SIZE 64
#define OPAD 0x5C
#define IPAD 0x36
#define NONCE_SIZE 8
#define ADDITIONAL_DATA_SHA_SIZE 8
static void PadKey(uint8_t output[HMAC_SHA256_KEY_SIZE], const uint8_t* key,
size_t key_length, uint8_t pad) {
int i;
size_t i;
for (i = 0; i < key_length; i++) {
*output++ = *key++ ^ pad;
}
memset(output, pad, HMAC_SHA256_KEY_SIZE - key_length);
}
#define RETURN_IF_ERROR(X) \
do { \
nearby_platform_status status = X; \
if (kNearbyStatusOK != status) return status; \
} while (0)
static nearby_platform_status HmacSha256(uint8_t out[32],
static nearby_platform_status HmacSha256(uint8_t out[SHA256_KEY_SIZE],
uint8_t hmac_key[HMAC_SHA256_KEY_SIZE],
const uint8_t* data,
size_t data_length) {
RETURN_IF_ERROR(nearby_platform_Sha256Start());
RETURN_IF_ERROR(nearby_platform_Sha256Update(hmac_key, HMAC_SHA256_KEY_SIZE));
RETURN_IF_ERROR(nearby_platform_Sha256Update(data, data_length));
return nearby_platform_Sha256Finish(out);
}
static nearby_platform_status HmacSha256WithNonce(
uint8_t out[32], uint8_t hmac_key[HMAC_SHA256_KEY_SIZE],
const uint8_t session_nonce[SESSION_NONCE_SIZE],
const uint8_t message_nonce[SESSION_NONCE_SIZE], const uint8_t* data,
size_t data_length) {
RETURN_IF_ERROR(nearby_platform_Sha256Start());
RETURN_IF_ERROR(nearby_platform_Sha256Update(hmac_key, HMAC_SHA256_KEY_SIZE));
RETURN_IF_ERROR(
nearby_platform_Sha256Update(session_nonce, SESSION_NONCE_SIZE));
RETURN_IF_ERROR(
nearby_platform_Sha256Update(message_nonce, SESSION_NONCE_SIZE));
RETURN_IF_ERROR(nearby_platform_Sha256Update(data, data_length));
RETURN_IF_ERROR(nearby_platform_Sha256Finish(out));
return kNearbyStatusOK;
}
nearby_platform_status nearby_fp_HmacSha256(uint8_t out[32], const uint8_t* key,
nearby_platform_status nearby_fp_HmacSha256Start(
nearby_fp_HmacSha256Context* context, const uint8_t* key,
size_t key_length) {
// out = HASH(Key XOR ipad, data)
PadKey(context->hmac_key, key, key_length, IPAD);
RETURN_IF_ERROR(nearby_platform_Sha256Start());
return nearby_platform_Sha256Update(context->hmac_key, HMAC_SHA256_KEY_SIZE);
}
nearby_platform_status nearby_fp_HmacSha256Update(const uint8_t* data,
size_t data_length) {
return nearby_platform_Sha256Update(data, data_length);
}
nearby_platform_status nearby_fp_HmacSha256Finish(
nearby_fp_HmacSha256Context* context, const uint8_t* key,
size_t key_length) {
RETURN_IF_ERROR(nearby_platform_Sha256Finish(context->hash));
// out = HASH(Key XOR opad, out)
PadKey(context->hmac_key, key, key_length, OPAD);
return HmacSha256(context->hash, context->hmac_key, context->hash,
SHA256_KEY_SIZE);
}
nearby_platform_status nearby_fp_HmacSha256(uint8_t out[SHA256_KEY_SIZE],
const uint8_t* key,
size_t key_length,
const uint8_t* data,
size_t data_length) {
@@ -381,7 +607,52 @@ nearby_platform_status nearby_fp_HmacSha256(uint8_t out[32], const uint8_t* key,
RETURN_IF_ERROR(HmacSha256(out, hmac_key, data, data_length));
// out = HASH(Key XOR opad, out)
PadKey(hmac_key, key, key_length, OPAD);
return HmacSha256(out, hmac_key, out, 32);
return HmacSha256(out, hmac_key, out, SHA256_KEY_SIZE);
}
nearby_platform_status nearby_fp_HkdfExtractSha256(uint8_t out[SHA256_KEY_SIZE],
const uint8_t* salt,
size_t salt_length,
const uint8_t* ikm,
size_t ikm_length) {
return nearby_fp_HmacSha256(out, salt, salt_length, ikm, ikm_length);
}
nearby_platform_status nearby_fp_HkdfExpandSha256(
uint8_t* out, size_t out_length, const uint8_t* prk, size_t prk_length,
const uint8_t* info, size_t info_length) {
nearby_fp_HmacSha256Context context;
size_t offset = 0;
uint8_t chunk_number = 1;
while (offset < out_length) {
RETURN_IF_ERROR(nearby_fp_HmacSha256Start(&context, prk, prk_length));
if (offset != 0) {
RETURN_IF_ERROR(
nearby_fp_HmacSha256Update(context.hash, SHA256_KEY_SIZE));
}
RETURN_IF_ERROR(nearby_fp_HmacSha256Update(info, info_length));
RETURN_IF_ERROR(
nearby_fp_HmacSha256Update(&chunk_number, sizeof(chunk_number)));
RETURN_IF_ERROR(nearby_fp_HmacSha256Finish(&context, prk, prk_length));
size_t space_left = out_length - offset;
size_t bytes_to_copy =
space_left < SHA256_KEY_SIZE ? space_left : SHA256_KEY_SIZE;
memcpy(out + offset, context.hash, bytes_to_copy);
offset += bytes_to_copy;
++chunk_number;
}
return kNearbyStatusOK;
}
static nearby_platform_status GetRrdKey(
uint8_t out[AES_MESSAGE_SIZE_BYTES],
const uint8_t account_key[ACCOUNT_KEY_SIZE_BYTES]) {
uint8_t prk[SHA256_KEY_SIZE];
RETURN_IF_ERROR(nearby_fp_HkdfExtractSha256(prk, NULL, 0, account_key,
ACCOUNT_KEY_SIZE_BYTES));
return nearby_fp_HkdfExpandSha256(out, AES_MESSAGE_SIZE_BYTES, prk,
sizeof(prk), kSassRrdKey,
sizeof(kSassRrdKey));
}
nearby_platform_status nearby_fp_AesCtr(
@@ -394,8 +665,8 @@ nearby_platform_status nearby_fp_AesCtr(
memcpy(iv + AES_MESSAGE_SIZE_BYTES - NONCE_SIZE, message, NONCE_SIZE);
while (offset < message_length) {
int i;
int bytes_left = message_length - offset;
unsigned i;
unsigned bytes_left = message_length - offset;
RETURN_IF_ERROR(nearby_platform_Aes128Encrypt(iv, key_stream, key));
for (i = 0; i < bytes_left && i < sizeof(key_stream); i++) {
message[offset++] ^= key_stream[i];
@@ -405,6 +676,8 @@ nearby_platform_status nearby_fp_AesCtr(
return kNearbyStatusOK;
}
#if NEARBY_FP_ENABLE_ADDITIONAL_DATA
#define ADDITIONAL_DATA_SHA_SIZE 8
nearby_platform_status nearby_fp_DecodeAdditionalData(
uint8_t* data, size_t length, const uint8_t key[ACCOUNT_KEY_SIZE_BYTES]) {
NEARBY_ASSERT(length > ADDITIONAL_DATA_SHA_SIZE + NONCE_SIZE);
@@ -414,6 +687,12 @@ nearby_platform_status nearby_fp_DecodeAdditionalData(
length - ADDITIONAL_DATA_SHA_SIZE));
if (memcmp(sha, data, ADDITIONAL_DATA_SHA_SIZE) != 0) {
NEARBY_TRACE(WARNING, "Additional Data SHA check failed");
NEARBY_TRACE(WARNING, "SHA: %s",
nearby_utils_ArrayToString(sha, sizeof(sha)));
NEARBY_TRACE(
WARNING, "key: %s",
nearby_utils_ArrayToString(key, sizeof(ACCOUNT_KEY_SIZE_BYTES)));
NEARBY_TRACE(WARNING, "data: %s", nearby_utils_ArrayToString(data, length));
return kNearbyStatusError;
}
return nearby_fp_AesCtr(data + ADDITIONAL_DATA_SHA_SIZE,
@@ -450,3 +729,105 @@ nearby_platform_status nearby_fp_Sha256(uint8_t out[32], const void* data,
}
return status;
}
uint8_t nearby_fp_GetSassConnectionState() {
return (BOOL_TO_INT(nearby_platform_OnHead())
<< SASS_CONN_STATE_ON_HEAD_OFFSET) |
(BOOL_TO_INT(nearby_platform_CanAcceptConnection())
<< SASS_CONN_STATE_AVAIL_OFFSET) |
(BOOL_TO_INT(nearby_platform_InFocusMode())
<< SASS_CONN_STATE_FOCUS_OFFSET) |
(BOOL_TO_INT(nearby_platform_AutoReconnected())
<< SASS_CONN_STATE_AUTO_RECONNECTED_OFFSET) |
(nearby_platform_GetAudioConnectionState() & SASS_CONN_STATE_MASK);
}
size_t nearby_fp_GenerateSassAdvertisement(
uint8_t* advertisement, size_t length, uint8_t connection_state,
uint8_t custom_data, const uint8_t* devices_bitmap, size_t bitmap_length) {
size_t advert_size = MIN_SASS_ADERTISEMENT_SIZE + bitmap_length;
NEARBY_ASSERT(length >= advert_size);
size_t offset = 0;
advertisement[offset++] = combineNibbles(advert_size - SASS_HEADER_SIZE,
SASS_ADVERTISEMENT_FIELD_TYPE);
advertisement[offset++] = connection_state;
advertisement[offset++] = custom_data;
memcpy(advertisement + offset, devices_bitmap, bitmap_length);
return advert_size;
}
nearby_platform_status nearby_fp_EncryptRandomResolvableField(
uint8_t* data, size_t length, const uint8_t key[AES_MESSAGE_SIZE_BYTES],
const uint8_t* salt_field) {
NEARBY_ASSERT(length <= AES_MESSAGE_SIZE_BYTES + RRF_HEADER_SIZE);
uint8_t iv[AES_MESSAGE_SIZE_BYTES];
uint8_t rrd_key[AES_MESSAGE_SIZE_BYTES];
memset(iv, 0, sizeof(iv));
if (salt_field != NULL) {
int salt_length = GetLtLength(salt_field[0]);
memcpy(iv, salt_field + LTV_HEADER_SIZE, salt_length);
}
RETURN_IF_ERROR(GetRrdKey(rrd_key, key));
RETURN_IF_ERROR(nearby_platform_Aes128Encrypt(iv, iv, rrd_key));
data[0] =
combineNibbles(length - RRF_HEADER_SIZE, RANDOM_RESOLVABLE_FIELD_TYPE);
for (size_t i = RRF_HEADER_SIZE; i < length; i++) {
data[i] ^= iv[i - RRF_HEADER_SIZE];
}
return kNearbyStatusOK;
}
nearby_platform_status nearby_fp_AesEncryptIv(
uint8_t* data, size_t length, uint8_t iv[AES_MESSAGE_SIZE_BYTES],
const uint8_t key[AES_MESSAGE_SIZE_BYTES]) {
NEARBY_ASSERT(length <= AES_MESSAGE_SIZE_BYTES);
RETURN_IF_ERROR(nearby_platform_Aes128Encrypt(iv, iv, key));
for (size_t i = 0; i < length; i++) {
data[i] ^= iv[i];
}
return kNearbyStatusOK;
}
uint16_t nearby_fp_GetSassCapabilityFlags() {
return (BOOL_TO_INT(nearby_platform_IsSassOn()) << SASS_CF_ON_OFFSET) |
(BOOL_TO_INT(nearby_platform_IsMultipointConfigurable())
<< SASS_CF_MULTIPOINT_CONFIGURABLE) |
(BOOL_TO_INT(nearby_platform_IsMultipointOn())
<< SASS_CF_MULTIPOINT_ON) |
(BOOL_TO_INT(nearby_platform_IsOnHeadDetectionSupported())
<< SASS_CF_OHD_SUPPORTED) |
(BOOL_TO_INT(nearby_platform_IsOnHeadDetectionEnabled())
<< SASS_CF_OHD_ENABLED);
}
nearby_platform_status nearby_fp_VerifyMessageAuthenticationCode(
const uint8_t* message, size_t length,
const uint8_t key[ACCOUNT_KEY_SIZE_BYTES],
const uint8_t session_nonce[SESSION_NONCE_SIZE]) {
if (length <= SESSION_NONCE_SIZE - MESSAGE_AUTHENTICATION_CODE_SIZE) {
return kNearbyStatusInvalidInput;
}
size_t data_length =
length - SESSION_NONCE_SIZE - MESSAGE_AUTHENTICATION_CODE_SIZE;
const uint8_t* message_nonce = message + data_length;
uint8_t hmac_key[HMAC_SHA256_KEY_SIZE];
uint8_t out[32];
// out = HASH(Key XOR ipad, session nonce + message nonce + data)
PadKey(hmac_key, key, ACCOUNT_KEY_SIZE_BYTES, IPAD);
RETURN_IF_ERROR(HmacSha256WithNonce(out, hmac_key, session_nonce,
message_nonce, message, data_length));
// out = HASH(Key XOR opad, out)
PadKey(hmac_key, key, ACCOUNT_KEY_SIZE_BYTES, OPAD);
RETURN_IF_ERROR(HmacSha256(out, hmac_key, out, 32));
const uint8_t* mac = message + data_length + SESSION_NONCE_SIZE;
if (memcmp(out, mac, MESSAGE_AUTHENTICATION_CODE_SIZE)) {
NEARBY_TRACE(INFO,
"Expected MAC "
"0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x",
out[0], out[1], out[2], out[3], out[4], out[5], out[6],
out[7]);
return kNearbyStatusInvalidInput;
} else {
return kNearbyStatusOK;
}
}
+146 -16
View File
@@ -20,7 +20,8 @@
// clang-format on
#include "nearby.h"
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
#include "nearby_message_stream.h"
#if NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
#include "nearby_platform_battery.h"
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
@@ -30,13 +31,30 @@ extern "C" {
#define BT_ADDRESS_LENGTH 6
#define DISCOVERABLE_ADV_SIZE_BYTES 10
// Sufficient for 5 account keys and battery notification
#define NON_DISCOVERABLE_ADV_SIZE_BYTES 24
#ifdef NEARBY_FP_ENABLE_SASS
// Sufficent for 5 account keys, battery notification and SASS info
#define NON_DISCOVERABLE_ADV_SIZE_BYTES 32
#else
// Sufficent for 5 account keys and battery notification
#define NON_DISCOVERABLE_ADV_SIZE_BYTES 25
#endif /* NEARBY_FP_ENABLE_SASS */
#define ADDITIONAL_DATA_HEADER_SIZE 16
#define HMAC_SHA256_KEY_SIZE 64
#define SHA256_KEY_SIZE 32
// Creates advertisement with the Model Id. Returns the number of bytes written
// to |output|.
// Random Resolvable Field header size
#define RRF_HEADER_SIZE 1
// Field types in the non-discoverable advertisement
#define SALT_FIELD_TYPE 1
#define BATTERY_INFO_WITH_UI_INDICATION_FIELD_TYPE 3
#define BATTERY_INFO_WITHOUT_UI_INDICATION_FIELD_TYPE 4
#define SASS_ADVERTISEMENT_FIELD_TYPE 5
#define RANDOM_RESOLVABLE_FIELD_TYPE 6
// Creates advertisement with the Model Id. Returns the number of bytes
// written to |output|.
//
// output - Advertisement data output buffer.
// length - Length of output buffer.
@@ -52,7 +70,7 @@ size_t nearby_fp_CreateDiscoverableAdvertisement(uint8_t* output,
size_t nearby_fp_CreateNondiscoverableAdvertisement(
uint8_t* output, size_t length, bool show_pairing_indicator);
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
#if NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
void SerializeBatteryInfo(uint8_t* output,
const nearby_platform_BatteryInfo* battery_info);
@@ -62,6 +80,7 @@ void SerializeBatteryInfo(uint8_t* output,
// output - Advertisement data output buffer.
// length - Length of output buffer.
// show_ui_indicator - Ask seeker to show UI indication.
// show_battery_indicator - Ask seeker to show battery indicator
// battery_info - Battery status data structure.
size_t nearby_fp_CreateNondiscoverableAdvertisementWithBattery(
uint8_t* output, size_t length, bool show_pairing_indicator,
@@ -87,26 +106,49 @@ nearby_platform_status nearby_fp_SaveAccountKeys();
//
// dest - Buffer for key fetched.
// key_number - Number of key to fetch.
void nearby_fp_CopyAccountKey(uint8_t* dest, unsigned key_number);
void nearby_fp_CopyAccountKey(nearby_platform_AccountKeyInfo* dest,
unsigned key_number);
// Gets number of active keys.
size_t nearby_fp_GetAccountKeyCount();
// Gets offset of key by key number. Returns the offset.
//
// key_number - ordinal number of key to return.
size_t nearby_fp_GetAccountKeyOffset(unsigned key_number);
// Gets the number of unique account keys. Account keys are duplicated when two
// or more seekers share an account key
size_t nearby_fp_GetUniqueAccountKeyCount();
// Returns the index of the next unique account key in [offset..number of keys]
// range, that is a key that wasn't already seen in [0..offset -1] range.
// Returns -1 if not found.
int nearby_fp_GetNextUniqueAccountKeyIndex(int offset);
// Gets pointer to given key by ordinal number.
//
// key_number - Ordinal number of key to return.
const uint8_t* nearby_fp_GetAccountKey(unsigned key_number);
const nearby_platform_AccountKeyInfo* nearby_fp_GetAccountKey(
unsigned key_number);
// Marks account key as active by moving it to the top of the key list.
//
// key_number - Original number of key to mark active.
void nearby_fp_MarkAccountKeyAsActive(unsigned key_number);
// Adds key to key list. Inserts key to top of list.
//
// key - Buffer containing key to insert.
void nearby_fp_AddAccountKey(const uint8_t key[ACCOUNT_KEY_SIZE_BYTES]);
void nearby_fp_AddAccountKey(const nearby_platform_AccountKeyInfo* key);
// Computes the account bloom filter and stores it in the Account Key Filter
// field in the advertisement. Returns the bloom filter size.
//
// `advertisement` is a non-discoverable FP advertisement. It must contain an
// LTV field for Account Key Filter, where the LT header is already set but the
// contents may be unitialized. It must contain an LTV with salt. Battery Info
// field and Random Resolvable field are used in the bloom filter calculation if
// present in the advertisement, When `use_sass_format` is set, the bloom filter
// defined by SASS will be used.
size_t nearby_fp_SetBloomFilter(uint8_t* advertisement, bool use_sass_format,
const uint8_t* in_use_key);
// Gets fast pair model ID
//
// output - Buffer to hold model ID.
@@ -126,16 +168,77 @@ nearby_platform_status nearby_fp_CreateSharedSecret(
//
// output - Buffer returning the pairing response.
nearby_platform_status nearby_fp_CreateRawKeybasedPairingResponse(
uint8_t output[AES_MESSAGE_SIZE_BYTES]);
uint8_t output[AES_MESSAGE_SIZE_BYTES], bool extended_response);
// Context for calculating HMAC-SHA256 hash
typedef struct {
// The resulting hash after calling `nearby_fp_HmacSha256Finish`
uint8_t hash[SHA256_KEY_SIZE];
uint8_t hmac_key[HMAC_SHA256_KEY_SIZE];
} nearby_fp_HmacSha256Context;
// Starts calculating HMAC-SHA156 hash incrementally. Only one hash calculation
// can be running at a time.
//
// context - Uninitalized context.
// key - Input key to calculate hash.
// key_length - Length of key.
nearby_platform_status nearby_fp_HmacSha256Start(
nearby_fp_HmacSha256Context* context, const uint8_t* key,
size_t key_length);
// Adds data to the hash.
//
// data - Data to calculate hash.
// data_length - Data length.
nearby_platform_status nearby_fp_HmacSha256Update(const uint8_t* data,
size_t data_length);
// Finishes hash calculation. The parameters must match those passed to
// `nearby_fp_HmacSha256Start`.
//
// context - Context initalized with `nearby_fp_HmacSha256Start`.
// key - Input key to calculate hash.
// key_length - Length of key.
nearby_platform_status nearby_fp_HmacSha256Finish(
nearby_fp_HmacSha256Context* context, const uint8_t* key,
size_t key_length);
// Implements HKDF-Extract method with SHA256 hash per
// https://www.rfc-editor.org/rfc/rfc5869#section-2.2
//
// out - output Pseudo Random Key material
// salt - optional salt
// salt_length - salt length in bytes. Set to 0 when `salt is NULL
// ikm - Input Key Material
// ikm_length - IKM length in bytes
nearby_platform_status nearby_fp_HkdfExtractSha256(uint8_t out[SHA256_KEY_SIZE],
const uint8_t* salt,
size_t salt_length,
const uint8_t* ikm,
size_t ikm_length);
// Implements HKDF-Expand with SHA256 hash per
// https://www.rfc-editor.org/rfc/rfc5869#section-2.3
//
// out - Output Key Material
// out_length - OKM length in bytes
// prk - Pseudo Random Key
// prk_length - PRK length in bytes
// info - optional context information
// info_length - info length in bytes. Set to 0 when `info` is NULL.
nearby_platform_status nearby_fp_HkdfExpandSha256(
uint8_t* out, size_t out_length, const uint8_t* prk, size_t prk_length,
const uint8_t* info, size_t info_length);
const uint8_t* nearby_fp_FindLtv(const uint8_t* advertisement, int type);
#ifdef NEARBY_FP_ENABLE_ADDITIONAL_DATA
// Calculates HMAC SHA256 hash.
//
// out - Output buffer for hash.
// key - Input key to calculate hash.
// key_length - Length of key.
// data - Data to calculate hash.
// data_lenth - Data length.
// data_length - Data length.
nearby_platform_status nearby_fp_HmacSha256(uint8_t out[32], const uint8_t* key,
size_t key_length,
const uint8_t* data,
@@ -151,6 +254,14 @@ nearby_platform_status nearby_fp_AesCtr(
uint8_t* message, size_t message_length,
const uint8_t key[AES_MESSAGE_SIZE_BYTES]);
// Encrypts |data| in place: data = data ^ AES(key, iv). The data must
// be shorter than AES_MESSAGE_SIZE_BYTES.
// Side effect: |iv| contents are destroyed.
nearby_platform_status nearby_fp_AesEncryptIv(
uint8_t* data, size_t length, uint8_t iv[AES_MESSAGE_SIZE_BYTES],
const uint8_t key[AES_MESSAGE_SIZE_BYTES]);
#if NEARBY_FP_ENABLE_ADDITIONAL_DATA
// Decodes data package read from Additional Data characteristics. The decoding
// happens in-place. Returns an error if HMAC-SHA checksum is invalid.
//
@@ -180,6 +291,25 @@ nearby_platform_status nearby_fp_EncodeAdditionalData(
nearby_platform_status nearby_fp_Sha256(uint8_t out[32], const void* data,
size_t length);
uint8_t nearby_fp_GetSassConnectionState();
size_t nearby_fp_GenerateSassAdvertisement(
uint8_t* advertisement, size_t length, uint8_t connection_state,
uint8_t custom_data, const uint8_t* devices_bitmap, size_t bitmap_length);
uint16_t nearby_fp_GetSassCapabilityFlags();
nearby_platform_status nearby_fp_VerifyMessageAuthenticationCode(
const uint8_t* message, size_t length,
const uint8_t key[ACCOUNT_KEY_SIZE_BYTES],
const uint8_t session_nonce[SESSION_NONCE_SIZE]);
// Encrypts Random Resolvable Field in place. The payload must start
// at RRF_HEADER_SIZE offset in the |data| buffer. |salt_field| is a
// part of non-discoverable advertisement
nearby_platform_status nearby_fp_EncryptRandomResolvableField(
uint8_t* data, size_t length, const uint8_t key[AES_MESSAGE_SIZE_BYTES],
const uint8_t* salt_field);
#ifdef __cplusplus
}
#endif
@@ -23,7 +23,7 @@
#define ACK_CODE 1
#define NACK_CODE 2
#ifdef NEARBY_FP_MESSAGE_STREAM
#if NEARBY_FP_MESSAGE_STREAM
void nearby_message_stream_Init(const nearby_message_stream_State* state) {
nearby_message_stream_Metadata* metadata =
(nearby_message_stream_Metadata*)state->buffer;
@@ -30,6 +30,7 @@ extern "C" {
#define ACK_CODE 1
#define NACK_CODE 2
#define FAIL_REASON_INVALID_MAC 3
#define FAIL_REASON_REDUNDANT_DEVICE_ACTION 4
// Message sent and received over the message stream
@@ -63,7 +64,7 @@ typedef struct {
uint16_t bytes_read;
} nearby_message_stream_Metadata;
#ifdef NEARBY_FP_MESSAGE_STREAM
#if NEARBY_FP_MESSAGE_STREAM
// Initializes the parser
void nearby_message_stream_Init(const nearby_message_stream_State* state);
+16
View File
@@ -17,6 +17,10 @@
#include "nearby_types.h"
// clang-format off
#include "nearby_config.h"
// clang-format on
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
@@ -24,7 +28,19 @@ extern "C" {
#define AES_MESSAGE_SIZE_BYTES 16
#define FP_SERVICE_UUID_SIZE 2
#define FP_MODEL_ID_SIZE 3
// Nearby stores BT addresses as uint64_t values
#define FP_BT_ADDRESS_SIZE sizeof(uint64_t)
#define ACCOUNT_KEY_SIZE_BYTES AES_MESSAGE_SIZE_BYTES
#define SESSION_NONCE_SIZE 8
typedef struct {
uint8_t account_key[ACCOUNT_KEY_SIZE_BYTES];
#ifdef NEARBY_FP_ENABLE_SASS
uint64_t peer_address;
#endif /* NEARBY_FP_ENABLE_SASS */
} nearby_platform_AccountKeyInfo;
#define NEARBY_FP_ACCOUNT_KEY_INFO_SIZE sizeof(nearby_platform_AccountKeyInfo)
typedef enum {
kNearbyStatusOK = 0,
@@ -8,12 +8,187 @@
extern "C" {
#endif /* __cplusplus */
#define NEARBY_PLATFORM_CONNECTION_STATE_NO_CONNECTION 0
#define NEARBY_PLATFORM_CONNECTION_STATE_PAGING 1
#define NEARBY_PLATFORM_CONNECTION_STATE_NO_DATA_TRANFER 2
#define NEARBY_PLATFORM_CONNECTION_STATE_NON_AUDIO_DATA_TRANSFER 3
#define NEARBY_PLATFORM_CONNECTION_STATE_A2DP 4
#define NEARBY_PLATFORM_CONNECTION_STATE_A2DP_WITH_AVRCP 5
#define NEARBY_PLATFORM_CONNECTION_STATE_HFP 6
#define NEARBY_PLATFORM_CONNECTION_STATE_LE_MEDIA_WITHOUT_CONTROL 7
#define NEARBY_PLATFORM_CONNECTION_STATE_LE_MEDIA_WITH_CONTROL 8
#define NEARBY_PLATFORM_CONNECTION_STATE_LE_CALL 9
#define NEARBY_PLATFORM_CONNECTION_STATE_LE_BIS 10
#define NEARBY_PLATFORM_CONNECTION_STATE_DISABLED_CONNECTION_SWITCH 15
// Multipoint switching preference flag bits. Every bit represents "new profile
// request" vs "current profile request". 1 - preference for switching, 0 -
// preference for not switching
#define NEARBY_SASS_SWITCHING_PREFERENCE_A2DP_VS_A2DP_MASK (1 << 7)
#define NEARBY_SASS_SWITCHING_PREFERENCE_HFP_VS_HFP_MASK (1 << 6)
#define NEARBY_SASS_SWITCHING_PREFERENCE_A2DP_VS_HFP_MASK (1 << 5)
#define NEARBY_SASS_SWITCHING_PREFERENCE_HFP_VS_A2DP_MASK (1 << 4)
// Flags for SwitchActiveAudioSource
// 1 switch to this device, 0 switch to second connected device
#define NEARBY_SASS_SWITCH_ACTIVE_AUDIO_SOURCE_THIS_DEVICE_MASK (1 << 7)
// Resume playing on switch to device after switching, 0 otherwise. Resuming
// playing means the Provider sends a PLAY notification to the Seeker through
// AVRCP profile. If the previous state (before switched away) was not PLAY, the
// Provider should ignore this flag.
#define NEARBY_SASS_SWITCH_ACTIVE_AUDIO_SOURCE_RESUME_MASK (1 << 6)
// 1 reject SCO on switched away device, 0 otherwise
#define NEARBY_SASS_SWITCH_ACTIVE_AUDIO_SOURCE_REJECT_SCO_MASK (1 << 5)
// 1 disconnect Bluetooth on switch away device, 0 otherwise.
#define NEARBY_SASS_SWITCH_ACTIVE_AUDIO_SOURCE_DISCONNECT_PREVIOUS_MASK (1 << 4)
// Flags for SwitchBackAudioSource. Note that the flags below are not bitmasks.
#define NEARBY_SASS_SWITCH_BACK 0x01
#define NEARBY_SASS_SWITCH_BACK_AND_RESUME 0x02
// Flags for NotifySassInitiatedConnection. Note that the flags below are not
// bitmasks.
#define NEARBY_SASS_CONNECTION_INITIATED_BY_SASS 0
#define NEARBY_SASS_CONNECTION_NOT_INITIATED_BY_SASS 1
// Reasons for |on_multipoint_switch_event|
#define NEARBY_SASS_MULTIPOINT_SWITCH_REASON_UNKNOWN 0
#define NEARBY_SASS_MULTIPOINT_SWITCH_REASON_A2DP 1
#define NEARBY_SASS_MULTIPOINT_SWITCH_REASON_HFP 2
// Returns true if right earbud is active
bool nearby_platform_GetEarbudRightStatus();
// Returns true if left earbud is active
bool nearby_platform_GetEarbudLeftStatus();
typedef struct {
// The platform HAL should call this callback on audio state changes. That is,
// when any of the query methods would return a new value.
void (*on_state_change)();
// This event should be called after a multipoint audio source switch. To make
// users aware of a multipoint-switch event taking place, SASS Seekers may
// show a notification to users. So the Provider should notify connected SASS
// Seekers about the switching event.
// |reason| is one of NEARBY_SASS_MULTIPOINT_SWITCH_REASON_* constants
// |peer_address| is the address the new audio source
// |name| is utf-8 encoded name of the new audio source or NULL if name is not
// known.
void (*on_multipoint_switch_event)(uint8_t reason, uint64_t peer_address,
const char* name);
} nearby_platform_AudioCallbacks;
// Returns one of NEARBY_PLATFORM_CONNECTION_STATE_* values
// Call |nearby_platform_AudioCallbacks::on_state_change| when this state
// changes.
unsigned int nearby_platform_GetAudioConnectionState();
// Returns true if the device is on head (or in ear).
// Call |nearby_platform_AudioCallbacks::on_state_change| when on-head state
// changes.
bool nearby_platform_OnHead();
// Returns true if the device can accept another audio connection without
// dropping any of the existing connections.
// Call |nearby_platform_AudioCallbacks::on_state_change| when this state
// changes.
bool nearby_platform_CanAcceptConnection();
// When the device is in focus mode, connection switching is not allowed
// Call |nearby_platform_AudioCallbacks::on_state_change| when this state
// changes.
bool nearby_platform_InFocusMode();
// Returns true if the current connection is auto-recconnected, meaning it is
// not connected by the user. For multi-point connections, returns true if any
// of the existing connections is auto-reconnected.
// Call |nearby_platform_AudioCallbacks::on_state_change| when this state
// changes.
bool nearby_platform_AutoReconnected();
// Sets a bit in the |bitmap| for every connected peer. The bit stays cleared
// for bonded but not connected peers. The order change is acceptable if it is
// unavoidable, e.g. when users factory reset the headset or when the bonded
// device count reaches the upper limit.
// |length| is the |bitmap| length on input in bytes and used space on output.
// For example, if there are 5 bonded devices, then |length| should be set to 1.
// Call |nearby_platform_AudioCallbacks::on_state_change| when this state
// changes.
void nearby_platform_GetConnectionBitmap(uint8_t* bitmap, size_t* length);
// Returns true is SASS state in On
bool nearby_platform_IsSassOn();
// Returns true if the device supports multipoint and it can be switched between
// on and off
bool nearby_platform_IsMultipointConfigurable();
// Returns true is multipoint in On
bool nearby_platform_IsMultipointOn();
// Returns true if the device supports OHD (even if it's turned off at the
// moment)
bool nearby_platform_IsOnHeadDetectionSupported();
// Returns true if OHD is supported and enabled
bool nearby_platform_IsOnHeadDetectionEnabled();
// Enables or disables multipoint
// When |enable| is false, the device should keep the connection with
// |peer_address| and disconnect other connections (if any)
nearby_platform_status nearby_platform_SetMultipoint(uint64_t peer_address,
bool enable);
// Sets multipoint switching preference flags
nearby_platform_status nearby_platform_SetSwitchingPreference(uint8_t flags);
// Gets switching preference flags
uint8_t nearby_platform_GetSwitchingPreference();
// Switches active audio source (to a connected device). If the flags indicate a
// switch to |peer_address| device but |peer_address| is already the active
// device, then return kNearbyStatusRedundantAction.
// If the flags indicate a switch to another device, then
// |preferred_audio_source| is the address of the next, preferred audio source.
// |preferred_audio_source| is 0 if Nearby SDK cannot figure out what the next
// audio source should be. It may happen if they are no other connected Seekers.
nearby_platform_status nearby_platform_SwitchActiveAudioSource(
uint64_t peer_address, uint8_t flags, uint64_t preferred_audio_source);
// Switches back to a disconnected audio source.
// |peer_address| is the address of the seeker who sent this command, *not* the
// address of the audio source that the device should switch to. The device
// should connect to the previous, currently disconnected, audio source and
// disconnect from |peer_address|. Ideally, the device should disconnect from
// |peer_address| after returning from this function. This would give Nearby SDK
// a chance to send an ACK message to the seeker.
nearby_platform_status nearby_platform_SwitchBackAudioSource(
uint64_t peer_address, uint8_t flags);
// Notifies the platform if the connection was initiated by SASS. SASS Providers
// may need to know if the connection switching is triggered by SASS to have
// different reactions, e.g. disable earcons for SASS events. The Seeker sends a
// message to notify the Provider that this connection was a SASS initiated
// connection.
nearby_platform_status nearby_platform_NotifySassInitiatedConnection(
uint64_t peer_address, uint8_t flags);
// Sets drop connection target
// On multipoint headphones, if the preferred connection to be dropped is not
// the least recently used one, SASS Seekers can tell the Provider which device
// to be dropped by using the message below.
nearby_platform_status nearby_platform_SetDropConnectionTarget(
uint64_t peer_address, uint8_t flags);
// Returns the BT address of the active audio source
// Call |nearby_platform_AudioCallbacks::on_state_change| when active audio
// source changes.
uint64_t nearby_platform_GetActiveAudioSource();
// Initializes Audio module
nearby_platform_status nearby_platform_AudioInit(
const nearby_platform_AudioCallbacks* audio_interface);
#ifdef __cplusplus
}
#endif
@@ -28,6 +28,11 @@ typedef enum {
kAccountKey, // FE2C1236-8366-4814-8EB0-01DE32100BEA (write)
kFirmwareRevision, // 0x2A26
kAdditionalData, // FE2C1237-8366-4814-8EB0-01DE32100BEA
// Message Stream PSM characteristic is required to establish an L2CAP
// communication channel between Seeker and Provider. Platforms that don't
// support L2CAP channel may choose not to implement this characteristic.
// By default, the Seeker connect to the Provider using RFCOMM.
kMessageStreamPsm, // FE2C1239-8366-4814-8EB0-01DE32100BEA (read, encrypted)
} nearby_fp_Characteristic;
typedef enum {
@@ -61,6 +66,15 @@ uint64_t nearby_platform_SetBleAddress(uint64_t address);
uint64_t nearby_platform_RotateBleAddress();
#endif /* NEARBY_FP_HAVE_BLE_ADDRESS_ROTATION */
// Gets the PSM - Protocol and Service Mulitplexor - assigned to Fast Pair's
// Message Stream.
// To support Message Stream for BLE devices, Fast Pair will build and maintain
// a BLE L2CAP channel for sending and receiving messages. The PSM can be
// dynamic or fixed.
// Returns a 16 bit PSM number or a negative value on error. When a valid PSM
// number is returned, the device must be ready to accept L2CAP connections.
int32_t nearby_platform_GetMessageStreamPsm();
// Sends a notification to the connected GATT client.
//
// peer_address - Address of peer.
+15 -4
View File
@@ -29,7 +29,9 @@ typedef struct {
void (*on_pairing_request)(uint64_t peer_address);
void (*on_paired)(uint64_t peer_address);
void (*on_pairing_failed)(uint64_t peer_address);
#ifdef NEARBY_FP_MESSAGE_STREAM
#if NEARBY_FP_MESSAGE_STREAM
// Message Stream messages are sent over RFCOMM on BT devices or L2CAP on BLE
// devices.
void (*on_message_stream_connected)(uint64_t peer_address);
void (*on_message_stream_disconnected)(uint64_t peer_address);
void (*on_message_stream_received)(uint64_t peer_address,
@@ -43,9 +45,17 @@ uint32_t nearby_platform_GetModelId();
// Returns tx power level.
int8_t nearby_platform_GetTxLevel();
// Returns public BR/EDR address
// Returns public BR/EDR address.
// On a BLE-only device, return the public identity address.
uint64_t nearby_platform_GetPublicAddress();
// Returns the secondary identity address.
// Some devices, such as ear-buds, can advertise two identity addresses. In this
// case, the Seeker pairs with each address separately but treats them as a
// single logical device set.
// Return 0 if this device does not have a secondary identity address.
uint64_t nearby_platform_GetSecondaryPublicAddress();
// Returns passkey used during pairing
uint32_t nearby_platfrom_GetPairingPassKey();
@@ -87,8 +97,9 @@ nearby_platform_status nearby_platform_GetDeviceName(char* name,
// Returns true if the device is in pairing mode (either fast-pair or manual).
bool nearby_platform_IsInPairingMode();
#ifdef NEARBY_FP_MESSAGE_STREAM
// Sends message stream through RFCOMM channel initiated by Seeker
#if NEARBY_FP_MESSAGE_STREAM
// Sends message stream through RFCOMM or L2CAP channel initiated by Seeker.
// BT devices should use RFCOMM channel. BLE-only devices should use L2CAP.
//
// peer_address - Peer address.
// message - Contents of message.
@@ -70,6 +70,12 @@ nearby_platform_status nearby_platform_Aes128Decrypt(
nearby_platform_status nearby_platform_GenSec256r1Secret(
const uint8_t remote_party_public_key[64], uint8_t secret[32]);
// Returns anti-spoofing 128 bit private key.
// Only used if the implementation also uses the
// nearby_platform_GenSec256r1Secret() routine defined in gen_secret.c.
// Return NULL if not implemented.
const uint8_t* nearby_platform_GetAntiSpoofingPrivateKey();
// Initializes secure element module
nearby_platform_status nearby_platform_SecureElementInit();
+20 -1
View File
@@ -1,3 +1,10 @@
# Use MBEDTLS for test SSL implementation instead of OPENSSL
# Compiles in mbedtls implementation of a number of functions in se.h
NEARBY_PLATFORM_USE_MBEDTLS ?= 0
# Use the hardware SE to generate the secp256r1 secret. Alternatively, generate
# the secret in software.
NEARBY_PLATFORM_HAS_SE ?= 1
CFLAGS_EXTRA ?=
CFLAGS += -g \
-MD \
@@ -19,10 +26,22 @@ else
CFLAGS += -O0
endif
ifeq ($(NEARBY_PLATFORM_USE_MBEDTLS),1)
CFLAGS += -DNEARBY_PLATFORM_USE_MBEDTLS
endif
ifeq ($(NEARBY_PLATFORM_HAS_SE),1)
CFLAGS += -DNEARBY_PLATFORM_HAS_SE
endif
TEST_INCLUDES = \
-I$(GTEST_DIR)/include -I$(GTEST_DIR) \
-I$(GMOCK_DIR)/include -I$(GMOCK_DIR) \
$(CLIENT_INCLUDES) \
-Iclient/tests/ \
-Iclient/tests/$(ARCH)/ \
-Iclient/tests/mocks/
-Iclient/tests/mocks/
ifeq ($(NEARBY_PLATFORM_USE_MBEDTLS),1)
TEST_INCLUDES += -I$(MBEDTLS_DIR)/include
endif
+38 -3
View File
@@ -8,22 +8,34 @@ TEST_SRCS := $(filter-out $(TEST_SRCS_EXCLUDE), $(TEST_SRCS))
TEST_OBJS += $(patsubst %.cc,$(OUT_DIR)/%.o,$(TEST_SRCS))
GLINUX_TARGET_SRCS := $(wildcard client/tests/glinux/*.cc)
MBEDTLS_TARGET_SRCS := $(wildcard common/source/mbedtls/*.c)
GTEST_SRCS = $(GTEST_DIR)/src/gtest-all.cc $(GMOCK_DIR)/src/gmock-all.cc
EMPTY_TARGET_SRCS := $(wildcard client/tests/empty_target/*.c)
GTEST_OBJS := $(patsubst %.cc,$(OUT_DIR)/%.o,$(GTEST_SRCS))
GLINUX_TARGET_OBJS := $(patsubst %.cc,$(OUT_DIR)/%.o,$(GLINUX_TARGET_SRCS))
MBEDTLS_TARGET_OBJS := $(patsubst %.c,$(OUT_DIR)/%.o,$(MBEDTLS_TARGET_SRCS))
EMPTY_TARGET_OBJS := $(patsubst %.c,$(OUT_DIR)/%.o,$(EMPTY_TARGET_SRCS))
ALL_TEST_OBJS += $(GLINUX_TARGET_OBJS)
ALL_TEST_OBJS += $(TEST_OBJS)
ALL_TEST_OBJS += $(GTEST_OBJS)
ALL_TEST_OBJS += $(EMPTY_TARGET_OBJS)
ifeq ($(NEARBY_PLATFORM_USE_MBEDTLS),1)
ALL_TEST_OBJS += $(MBEDTLS_TARGET_OBJS)
endif
# The glinux target layer
$(GLINUX_TARGET_OBJS) : $(OUT_DIR)/%.o: %.cc
$(call compile_c,$(TEST_INCLUDES) -I. -std=c++14 -c $(CFLAGS))
ifeq ($(NEARBY_PLATFORM_USE_MBEDTLS),1)
# Mbed TLS interface files
$(MBEDTLS_TARGET_OBJS) : $(OUT_DIR)/%.o: %.c
$(call compile_c,$(TEST_INCLUDES) -I. -std=c99 -c $(CFLAGS))
endif
$(EMPTY_TARGET_OBJS) : $(OUT_DIR)/%.o: %.c
$(info "compiling empty target object $<")
@@ -45,16 +57,25 @@ $(TESTS_TO_RUN) : %_run : %
./$< --gtest_output=xml:$(OUT_DIR)/test_logs/$(notdir $<)_logs/sponge_log.xml
TARGET_OBJS ?= $(GLINUX_TARGET_OBJS)
ifeq ($(NEARBY_PLATFORM_USE_MBEDTLS),1)
TARGET_OBJS += $(MBEDTLS_TARGET_OBJS)
endif
TARGET_OS_SRCS := $(wildcard client/tests/gLinux/*.cc)
TARGET_MBEDTLS_SRCS += $(wildcard common/source/mbedtls/*.c)
TARGET_OS_OBJS ?= $(patsubst %.cc,$(OUT_DIR)/%.o,$(TARGET_OS_SRCS))
TARGET_MBEDTLS_OBJS ?= $(patsubst %.c,$(OUT_DIR)/%.o,$(TARGET_MBEDTLS_SRCS))
$(TARGET_OS_OBJS) : $(OUT_DIR)/%.o: %.cc
$(call compile_c,$(TEST_INCLUDES) -I. -std=c++14 $(CFLAGS))
$(TARGET_MBED_OBJS) : $(OUT_DIR)/%.o: %.c
$(call compile_c,$(TEST_INCLUDES) -I. -std=c++14 $(CFLAGS))
ALL_TEST_OBJS += $(TEST_OBJS)
ALL_TEST_OBJS += $(TARGET_OS_OBJS)
ifeq ($(NEARBY_PLATFORM_USE_MBEDTLS),1)
ALL_TEST_OBJS += $(TARGET_MBEDTLS_OBJS)
endif
ALL_TEST_OBJS += $(TARGET_OS_OBJS)
ALL_OBJS += $(ALL_TEST_OBJS)
# Must include the .d files for test sources here since gLinux.rules is included
# by makefile after after it includes $(DEPFILES).
@@ -64,9 +85,23 @@ EMPTY_TARGET_TEST = $(OUT_DIR)/client/tests/empty_target_test
$(EMPTY_TARGET_TEST) : TARGET_OBJS = $(EMPTY_TARGET_OBJS)
$(EMPTY_TARGET_TEST) : $(EMPTY_TARGET_OBJS)
TEST_BINARIES = $(patsubst %.cc,$(OUT_DIR)/%,$(TEST_SRCS))
ifeq ($(NEARBY_PLATFORM_USE_MBEDTLS),1)
$(TEST_BINARIES) : $(NAME) $(GTEST_OBJS) $(GLINUX_TARGET_OBJS) $(MBEDTLS_TARGET_OBJS)
else
$(TEST_BINARIES) : $(NAME) $(GTEST_OBJS) $(GLINUX_TARGET_OBJS)
endif
$(TEST_BINARIES) : $(TARGET_OS_OBJS)
LIBS ?= -L $(OUT_DIR) -lnearby -lcrypto -lssl -lpthread -lstdc++
ifeq ($(NEARBY_PLATFORM_USE_MBEDTLS),1)
MBEDTLS_LIBS = $(MBEDTLS_DIR)/library/libmbedtls.a $(MBEDTLS_DIR)/library/libmbedcrypto.a
$(MBEDTLS_LIBS) &:
cd $(MBEDTLS_DIR) && $(MAKE)
$(TEST_BINARIES) : $(MBEDTLS_LIBS)
LIBS += -L $(MBEDTLS_DIR)/library -lmbedtls -lmbedcrypto
endif
$(TEST_BINARIES) : % : %.o
mkdir -p $(dir $@)
$(CC) -o $@ $< \
@@ -74,10 +109,10 @@ $(TEST_BINARIES) : % : %.o
$(TEST_INCLUDES) \
$(GTEST_OBJS) \
$(TARGET_OBJS) \
$(TARGET_OS_OBJS) \
$(TARGET_OS_OBJS) \
-L /usr/local/lib \
-std=c++14 \
-L $(OUT_DIR) -lnearby -lcrypto -lssl -lpthread -lstdc++
$(LIBS)
run_tests: $(TESTS_TO_RUN)