mirror of https://github.com/ARMmbed/mbed-os.git
split identity away from meta data and csrk, moved privacy feature from db to security manager
parent
f4d29bbef6
commit
eb899bc798
|
@ -201,6 +201,16 @@ struct octet_type_t {
|
|||
memcpy(value, input_value, sizeof(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a data from an buffer of bytes.
|
||||
*
|
||||
* @param input_value pointer to buffer.
|
||||
* @param size buffer size
|
||||
*/
|
||||
octet_type_t(const uint8_t* input_value, size_t size) {
|
||||
memcpy(value, input_value, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equal operator between two octet types.
|
||||
*/
|
||||
|
@ -218,14 +228,14 @@ struct octet_type_t {
|
|||
/**
|
||||
* Subscript operator to access data content
|
||||
*/
|
||||
uint8_t operator[](uint8_t i) const {
|
||||
uint8_t& operator[](uint8_t i) {
|
||||
return value[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pointer to the buffer holding data.
|
||||
*/
|
||||
const uint8_t* data() const {
|
||||
uint8_t* data() {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,6 @@ struct SecurityEntry_t {
|
|||
}
|
||||
|
||||
connection_handle_t handle;
|
||||
address_t peer_identity_address;
|
||||
uint8_t encryption_key_size;
|
||||
uint8_t peer_address_public:1;
|
||||
|
||||
|
@ -100,14 +99,19 @@ struct SecurityEntry_t {
|
|||
};
|
||||
|
||||
struct SecurityEntryKeys_t {
|
||||
ltk_t ltk;
|
||||
ltk_t ltk;
|
||||
ediv_t ediv;
|
||||
rand_t rand;
|
||||
};
|
||||
|
||||
struct SecurityEntryIdentity_t {
|
||||
irk_t irk;
|
||||
csrk_t csrk;
|
||||
address_t peer_identity_address;
|
||||
irk_t irk;
|
||||
};
|
||||
|
||||
struct IdentytList_t {
|
||||
SecurityEntryIdentity_t* identities;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/** Return value for callbacks to indicate to the security db
|
||||
|
@ -122,8 +126,8 @@ enum DbCbAction_t {
|
|||
|
||||
typedef mbed::Callback<DbCbAction_t(SecurityEntry_t&)> SecurityEntryDbCb_t;
|
||||
typedef mbed::Callback<DbCbAction_t(SecurityEntry_t&, SecurityEntryKeys_t&)> SecurityEntryKeysDbCb_t;
|
||||
typedef mbed::Callback<DbCbAction_t(connection_handle_t, const csrk_t*)> SecurityEntryCsrkDbCb_t;
|
||||
typedef mbed::Callback<DbCbAction_t(SecurityEntry_t&, SecurityEntryIdentity_t&)> SecurityEntryIdentityDbCb_t;
|
||||
typedef mbed::Callback<void(connection_handle_t, const csrk_t*)> SecurityEntryCsrkDbCb_t;
|
||||
typedef mbed::Callback<void(const SecurityEntryIdentity_t*)> SecurityEntryIdentityDbCb_t;
|
||||
typedef mbed::Callback<DbCbAction_t(Gap::Whitelist_t&)> WhitelistDbCb_t;
|
||||
|
||||
/**
|
||||
|
@ -201,8 +205,8 @@ public:
|
|||
) = 0;
|
||||
|
||||
/**
|
||||
* Return asynchronously the local signing key through a callback
|
||||
* so that packets being sent can be signed.
|
||||
* Return asynchronously the peer encryption key through a callback
|
||||
* so that encryption can be enabled.
|
||||
* @param cb callback which will receive the key
|
||||
* @param connection handle of the connection queried
|
||||
*/
|
||||
|
@ -211,6 +215,18 @@ public:
|
|||
connection_handle_t connection
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Return asynchronously one identity entry, call until you get NULL
|
||||
* to get all Iidentity entries containing IRK and identity address
|
||||
* @param cb callback which will receive the entry
|
||||
* @note query is stateful and will return NULL when all
|
||||
* entries have been returned, it may return the same entry multiple
|
||||
* times if list is changed in between queries
|
||||
*/
|
||||
virtual void get_next_entry_peer_identity(
|
||||
SecurityEntryIdentityDbCb_t cb
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Update all values in one call.
|
||||
* @param connection for which the values are being updated
|
||||
|
@ -306,6 +322,17 @@ public:
|
|||
|
||||
/* list management */
|
||||
|
||||
/**
|
||||
* If implementation has enough memory it can return the
|
||||
* irk list synchronously, otherwise asynchronously iteration
|
||||
* shall be used through get_next_entry_peer_identity
|
||||
*
|
||||
* @param list the list of entires, NULL if empty
|
||||
*
|
||||
* @return BLE_ERROR_NONE if the function is implemented.
|
||||
*/
|
||||
virtual ble_error_t get_identity_list(IdentytList_t* list) = 0;
|
||||
|
||||
/**
|
||||
* Create a new entry or retrieve existing stored entry
|
||||
* and put it in the live connections store to be retrieved
|
||||
|
@ -399,44 +426,6 @@ public:
|
|||
* @param reload if true values will be preserved across resets.
|
||||
*/
|
||||
virtual void set_restore(bool reload) = 0;
|
||||
|
||||
protected:
|
||||
virtual bool check_against_identity_address(
|
||||
const address_t peer_address,
|
||||
const irk_t *irk
|
||||
) {
|
||||
if ((peer_address[0] & 0x3) == 0x2) {
|
||||
/* we need to verify the identity by encrypting the
|
||||
* PRAND part with the IRK key and checking the result
|
||||
* @see BLUETOOTH SPECIFICATION Version 5.0 | Vol 3, Part H - 2.2.2 */
|
||||
address_t prand_hash = peer_address;
|
||||
|
||||
/* remove the hash and leave only prand */
|
||||
prand_hash[3] = 0;
|
||||
prand_hash[4] = 0;
|
||||
prand_hash[5] = 0;
|
||||
|
||||
/* TODO:
|
||||
GenericSecurityManager *sm = GenericSecurityManager::instance();
|
||||
if (!sm) {
|
||||
return BLE_ERROR_INITIALIZATION_INCOMPLETE;
|
||||
}
|
||||
|
||||
sm->encrypt_data(irk, address_checked.data());
|
||||
*/
|
||||
|
||||
/* prand_hash now contains the hash result in the first 3 octects
|
||||
* compare it with the hash in the peer identity address */
|
||||
|
||||
/* can't use memcmp because of address_t constness */
|
||||
if ((prand_hash[0] == peer_address[3])
|
||||
|| (prand_hash[1] == peer_address[4])
|
||||
|| (prand_hash[2] == peer_address[5])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/* naive memory implementation for verification */
|
||||
|
@ -446,12 +435,12 @@ private:
|
|||
db_store_t() { };
|
||||
SecurityEntry_t entry;
|
||||
SecurityEntryKeys_t key;
|
||||
SecurityEntryIdentity_t identity;
|
||||
csrk_t csrk;
|
||||
};
|
||||
static const size_t MAX_ENTRIES = 5;
|
||||
|
||||
public:
|
||||
MemoryGenericSecurityDb() { };
|
||||
MemoryGenericSecurityDb() : _irk_index(0) { };
|
||||
virtual ~MemoryGenericSecurityDb() { };
|
||||
|
||||
virtual SecurityEntry_t* get_entry(connection_handle_t connection) {
|
||||
|
@ -505,13 +494,27 @@ public:
|
|||
connection_handle_t connection
|
||||
) {
|
||||
SecurityEntry_t *entry = NULL;
|
||||
SecurityEntryIdentity_t *identity = NULL;
|
||||
csrk_t csrk;
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
entry = &store->entry;
|
||||
identity = &store->identity;
|
||||
csrk = store->csrk;
|
||||
}
|
||||
cb(entry->handle, &csrk);
|
||||
}
|
||||
|
||||
virtual void get_next_entry_peer_identity(
|
||||
SecurityEntryIdentityDbCb_t cb
|
||||
) {
|
||||
SecurityEntryIdentity_t identity;
|
||||
|
||||
if (_irk_index < MAX_ENTRIES) {
|
||||
_irk_index++;
|
||||
cb(&_identities[_irk_index - 1]);
|
||||
} else {
|
||||
_irk_index = 0;
|
||||
cb(NULL);
|
||||
}
|
||||
cb(entry->handle, &identity->csrk);
|
||||
}
|
||||
|
||||
virtual void get_entry_peer_keys(
|
||||
|
@ -541,13 +544,15 @@ public:
|
|||
) {
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
store->entry.peer_identity_address = peer_address;
|
||||
store->key.ltk = *ltk;
|
||||
store->key.ediv = *ediv;
|
||||
store->key.rand = *rand;
|
||||
store->identity.irk = *irk;
|
||||
store->identity.csrk = *csrk;
|
||||
store->csrk = *csrk;
|
||||
size_t index = store - _db;
|
||||
_identities[index].irk = *irk;
|
||||
_identities[index].peer_identity_address = peer_address;
|
||||
}
|
||||
_irk_index = 0;
|
||||
}
|
||||
|
||||
virtual void set_entry_peer_ltk(
|
||||
|
@ -578,8 +583,10 @@ public:
|
|||
) {
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
store->identity.irk = *irk;
|
||||
size_t index = store - _db;
|
||||
_identities[index].irk = *irk;
|
||||
}
|
||||
_irk_index = 0;
|
||||
}
|
||||
|
||||
virtual void set_entry_peer_bdaddr(
|
||||
|
@ -589,7 +596,8 @@ public:
|
|||
) {
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
store->entry.peer_identity_address = peer_address;
|
||||
size_t index = store - _db;
|
||||
_identities[index].peer_identity_address = peer_address;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -599,29 +607,31 @@ public:
|
|||
) {
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
store->identity.csrk = *csrk;
|
||||
store->csrk = *csrk;
|
||||
}
|
||||
}
|
||||
|
||||
/* local csrk */
|
||||
|
||||
virtual const csrk_t* get_local_csrk() {
|
||||
return &_local_identity.csrk;
|
||||
return &_local_csrk;
|
||||
}
|
||||
|
||||
virtual void set_local_csrk(const csrk_t *csrk) {
|
||||
_local_identity.csrk = *csrk;
|
||||
_local_csrk = *csrk;
|
||||
}
|
||||
|
||||
/* list management */
|
||||
|
||||
virtual ble_error_t get_identity_list(IdentytList_t* list) {
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
virtual SecurityEntry_t* connect_entry(connection_handle_t connection, address_t peer_address) {
|
||||
for (size_t i = 0; i < MAX_ENTRIES; i++) {
|
||||
if (_db[i].entry.connected) {
|
||||
continue;
|
||||
} else if (peer_address == _db[i].entry.peer_identity_address) {
|
||||
return &_db[i].entry;
|
||||
} else if (check_against_identity_address(peer_address, &_db[i].identity.irk)) {
|
||||
} else if (peer_address == _identities[i].peer_identity_address) {
|
||||
return &_db[i].entry;
|
||||
}
|
||||
}
|
||||
|
@ -630,6 +640,8 @@ public:
|
|||
for (size_t i = 0; i < MAX_ENTRIES; i++) {
|
||||
if (!_db[i].entry.connected) {
|
||||
_db[i] = db_store_t();
|
||||
_identities[i] = SecurityEntryIdentity_t();
|
||||
_irk_index = 0;
|
||||
return &_db[i].entry;
|
||||
}
|
||||
}
|
||||
|
@ -646,6 +658,7 @@ public:
|
|||
}
|
||||
_local_keys = SecurityEntryKeys_t();
|
||||
_local_identity = SecurityEntryIdentity_t();
|
||||
_local_csrk = csrk_t();
|
||||
}
|
||||
|
||||
virtual void get_whitelist(WhitelistDbCb_t cb) { }
|
||||
|
@ -676,8 +689,12 @@ private:
|
|||
}
|
||||
|
||||
db_store_t _db[MAX_ENTRIES];
|
||||
SecurityEntryIdentity_t _identities[MAX_ENTRIES];
|
||||
SecurityEntryKeys_t _local_keys;
|
||||
SecurityEntryIdentity_t _local_identity;
|
||||
csrk_t _local_csrk;
|
||||
|
||||
size_t _irk_index;
|
||||
};
|
||||
|
||||
} /* namespace generic */
|
||||
|
|
|
@ -174,12 +174,27 @@ public:
|
|||
|
||||
virtual ble_error_t setPrivateAddressTimeout(uint16_t timeout_in_seconds);
|
||||
|
||||
private:
|
||||
bool check_against_identity_address(
|
||||
const address_t peer_address,
|
||||
const irk_t *irk
|
||||
);
|
||||
|
||||
void check_against_irk_cb(
|
||||
const irk_t *irk
|
||||
);
|
||||
public:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Keys
|
||||
//
|
||||
|
||||
virtual ble_error_t getSigningKey(connection_handle_t connection, bool authenticated);
|
||||
virtual ble_error_t getSigningKey(
|
||||
connection_handle_t connection,
|
||||
bool authenticated
|
||||
);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns the requested LTK to the PAL. Called by the security db.
|
||||
*
|
||||
|
@ -193,10 +208,11 @@ public:
|
|||
SecurityEntryKeys_t& entryKeys
|
||||
);
|
||||
|
||||
DbCbAction_t return_csrk_cb(
|
||||
void return_csrk_cb(
|
||||
connection_handle_t connection,
|
||||
const csrk_t *csrk
|
||||
);
|
||||
public:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Authentication
|
||||
|
|
|
@ -428,6 +428,12 @@ ble_error_t GenericSecurityManager::setPrivateAddressTimeout(uint16_t timeout_in
|
|||
return _pal.set_private_address_timeout(timeout_in_seconds);
|
||||
}
|
||||
|
||||
void GenericSecurityManager::check_against_irk_cb(
|
||||
const irk_t *irk
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Keys
|
||||
//
|
||||
|
@ -476,13 +482,13 @@ DbCbAction_t GenericSecurityManager::set_ltk_cb(
|
|||
return DB_CB_ACTION_NO_UPDATE_REQUIRED;
|
||||
}
|
||||
|
||||
DbCbAction_t GenericSecurityManager::return_csrk_cb(
|
||||
void GenericSecurityManager::return_csrk_cb(
|
||||
connection_handle_t connection,
|
||||
const csrk_t *csrk
|
||||
) {
|
||||
SecurityEntry_t *entry = _db.get_entry(connection);
|
||||
if (!entry) {
|
||||
return DB_CB_ACTION_NO_UPDATE_REQUIRED;
|
||||
return;
|
||||
}
|
||||
|
||||
_app_event_handler->signingKey(
|
||||
|
@ -490,7 +496,6 @@ DbCbAction_t GenericSecurityManager::return_csrk_cb(
|
|||
csrk,
|
||||
entry->mitm_csrk
|
||||
);
|
||||
return DB_CB_ACTION_NO_UPDATE_REQUIRED;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -893,15 +898,57 @@ void GenericSecurityManager::on_disconnected(connection_handle_t connection) {
|
|||
}
|
||||
|
||||
void GenericSecurityManager::on_connected(connection_handle_t connection, address_t peer_address, bool is_master) {
|
||||
/* TODO: if resolvable peer address, find identity address */
|
||||
SecurityEntry_t *entry = _db.connect_entry(connection, peer_address);
|
||||
SecurityEntry_t *entry = NULL;
|
||||
|
||||
/* if it's a resolvable address, check against IRKs */
|
||||
if ((peer_address[0] & 0x3) == 0x2) {
|
||||
IdentytList_t *list = NULL;
|
||||
if (_db.get_identity_list(list) == BLE_ERROR_NONE && list) {
|
||||
for (size_t i; i < list->size; ++i) {
|
||||
/* if the address resolves connect to an existing entry based on the identity address */
|
||||
if (check_against_identity_address(peer_address, &list->identities[i].irk)) {
|
||||
entry = _db.connect_entry(connection, list->identities[i].peer_identity_address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entry = _db.connect_entry(connection, peer_address);
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
entry->reset();
|
||||
entry->master = is_master;
|
||||
}
|
||||
|
||||
bool GenericSecurityManager::check_against_identity_address(
|
||||
const address_t peer_address,
|
||||
const irk_t *irk
|
||||
) {
|
||||
/* we need to verify the identity by encrypting the
|
||||
* PRAND part with the IRK key and checking the result
|
||||
* @see BLUETOOTH SPECIFICATION Version 5.0 | Vol 3, Part H - 2.2.2 */
|
||||
octet_type_t<6> prand_hash(peer_address.data(), 6);
|
||||
|
||||
/* remove the hash and leave only prand */
|
||||
prand_hash[3] = 0;
|
||||
prand_hash[4] = 0;
|
||||
prand_hash[5] = 0;
|
||||
|
||||
_pal.encrypt_data(irk, prand_hash.data());
|
||||
|
||||
/* prand_hash now contains the hash result in the first 3 octects
|
||||
* compare it with the hash in the peer identity address */
|
||||
|
||||
/* can't use memcmp because of address_t constness */
|
||||
if ((prand_hash[0] == peer_address[3])
|
||||
|| (prand_hash[1] == peer_address[4])
|
||||
|| (prand_hash[2] == peer_address[5])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
} /* namespace generic */
|
||||
|
|
Loading…
Reference in New Issue