split identity away from meta data and csrk, moved privacy feature from db to security manager

pull/6188/head
paul-szczepanek-arm 2018-01-29 21:10:08 +00:00
parent f4d29bbef6
commit eb899bc798
4 changed files with 161 additions and 71 deletions

View File

@ -201,6 +201,16 @@ struct octet_type_t {
memcpy(value, input_value, sizeof(value)); 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. * Equal operator between two octet types.
*/ */
@ -218,14 +228,14 @@ struct octet_type_t {
/** /**
* Subscript operator to access data content * Subscript operator to access data content
*/ */
uint8_t operator[](uint8_t i) const { uint8_t& operator[](uint8_t i) {
return value[i]; return value[i];
} }
/** /**
* Return the pointer to the buffer holding data. * Return the pointer to the buffer holding data.
*/ */
const uint8_t* data() const { uint8_t* data() {
return value; return value;
} }

View File

@ -72,7 +72,6 @@ struct SecurityEntry_t {
} }
connection_handle_t handle; connection_handle_t handle;
address_t peer_identity_address;
uint8_t encryption_key_size; uint8_t encryption_key_size;
uint8_t peer_address_public:1; uint8_t peer_address_public:1;
@ -100,14 +99,19 @@ struct SecurityEntry_t {
}; };
struct SecurityEntryKeys_t { struct SecurityEntryKeys_t {
ltk_t ltk; ltk_t ltk;
ediv_t ediv; ediv_t ediv;
rand_t rand; rand_t rand;
}; };
struct SecurityEntryIdentity_t { struct SecurityEntryIdentity_t {
irk_t irk; address_t peer_identity_address;
csrk_t csrk; irk_t irk;
};
struct IdentytList_t {
SecurityEntryIdentity_t* identities;
size_t size;
}; };
/** Return value for callbacks to indicate to the security db /** 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&)> SecurityEntryDbCb_t;
typedef mbed::Callback<DbCbAction_t(SecurityEntry_t&, SecurityEntryKeys_t&)> SecurityEntryKeysDbCb_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<void(connection_handle_t, const csrk_t*)> SecurityEntryCsrkDbCb_t;
typedef mbed::Callback<DbCbAction_t(SecurityEntry_t&, SecurityEntryIdentity_t&)> SecurityEntryIdentityDbCb_t; typedef mbed::Callback<void(const SecurityEntryIdentity_t*)> SecurityEntryIdentityDbCb_t;
typedef mbed::Callback<DbCbAction_t(Gap::Whitelist_t&)> WhitelistDbCb_t; typedef mbed::Callback<DbCbAction_t(Gap::Whitelist_t&)> WhitelistDbCb_t;
/** /**
@ -201,8 +205,8 @@ public:
) = 0; ) = 0;
/** /**
* Return asynchronously the local signing key through a callback * Return asynchronously the peer encryption key through a callback
* so that packets being sent can be signed. * so that encryption can be enabled.
* @param cb callback which will receive the key * @param cb callback which will receive the key
* @param connection handle of the connection queried * @param connection handle of the connection queried
*/ */
@ -211,6 +215,18 @@ public:
connection_handle_t connection connection_handle_t connection
) = 0; ) = 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. * Update all values in one call.
* @param connection for which the values are being updated * @param connection for which the values are being updated
@ -306,6 +322,17 @@ public:
/* list management */ /* 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 * Create a new entry or retrieve existing stored entry
* and put it in the live connections store to be retrieved * 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. * @param reload if true values will be preserved across resets.
*/ */
virtual void set_restore(bool reload) = 0; 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 */ /* naive memory implementation for verification */
@ -446,12 +435,12 @@ private:
db_store_t() { }; db_store_t() { };
SecurityEntry_t entry; SecurityEntry_t entry;
SecurityEntryKeys_t key; SecurityEntryKeys_t key;
SecurityEntryIdentity_t identity; csrk_t csrk;
}; };
static const size_t MAX_ENTRIES = 5; static const size_t MAX_ENTRIES = 5;
public: public:
MemoryGenericSecurityDb() { }; MemoryGenericSecurityDb() : _irk_index(0) { };
virtual ~MemoryGenericSecurityDb() { }; virtual ~MemoryGenericSecurityDb() { };
virtual SecurityEntry_t* get_entry(connection_handle_t connection) { virtual SecurityEntry_t* get_entry(connection_handle_t connection) {
@ -505,13 +494,27 @@ public:
connection_handle_t connection connection_handle_t connection
) { ) {
SecurityEntry_t *entry = NULL; SecurityEntry_t *entry = NULL;
SecurityEntryIdentity_t *identity = NULL; csrk_t csrk;
db_store_t *store = get_store(connection); db_store_t *store = get_store(connection);
if (store) { if (store) {
entry = &store->entry; 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( virtual void get_entry_peer_keys(
@ -541,13 +544,15 @@ public:
) { ) {
db_store_t *store = get_store(connection); db_store_t *store = get_store(connection);
if (store) { if (store) {
store->entry.peer_identity_address = peer_address;
store->key.ltk = *ltk; store->key.ltk = *ltk;
store->key.ediv = *ediv; store->key.ediv = *ediv;
store->key.rand = *rand; store->key.rand = *rand;
store->identity.irk = *irk; store->csrk = *csrk;
store->identity.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( virtual void set_entry_peer_ltk(
@ -578,8 +583,10 @@ public:
) { ) {
db_store_t *store = get_store(connection); db_store_t *store = get_store(connection);
if (store) { if (store) {
store->identity.irk = *irk; size_t index = store - _db;
_identities[index].irk = *irk;
} }
_irk_index = 0;
} }
virtual void set_entry_peer_bdaddr( virtual void set_entry_peer_bdaddr(
@ -589,7 +596,8 @@ public:
) { ) {
db_store_t *store = get_store(connection); db_store_t *store = get_store(connection);
if (store) { 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); db_store_t *store = get_store(connection);
if (store) { if (store) {
store->identity.csrk = *csrk; store->csrk = *csrk;
} }
} }
/* local csrk */ /* local csrk */
virtual const csrk_t* get_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) { virtual void set_local_csrk(const csrk_t *csrk) {
_local_identity.csrk = *csrk; _local_csrk = *csrk;
} }
/* list management */ /* 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) { virtual SecurityEntry_t* connect_entry(connection_handle_t connection, address_t peer_address) {
for (size_t i = 0; i < MAX_ENTRIES; i++) { for (size_t i = 0; i < MAX_ENTRIES; i++) {
if (_db[i].entry.connected) { if (_db[i].entry.connected) {
continue; continue;
} else if (peer_address == _db[i].entry.peer_identity_address) { } else if (peer_address == _identities[i].peer_identity_address) {
return &_db[i].entry;
} else if (check_against_identity_address(peer_address, &_db[i].identity.irk)) {
return &_db[i].entry; return &_db[i].entry;
} }
} }
@ -630,6 +640,8 @@ public:
for (size_t i = 0; i < MAX_ENTRIES; i++) { for (size_t i = 0; i < MAX_ENTRIES; i++) {
if (!_db[i].entry.connected) { if (!_db[i].entry.connected) {
_db[i] = db_store_t(); _db[i] = db_store_t();
_identities[i] = SecurityEntryIdentity_t();
_irk_index = 0;
return &_db[i].entry; return &_db[i].entry;
} }
} }
@ -646,6 +658,7 @@ public:
} }
_local_keys = SecurityEntryKeys_t(); _local_keys = SecurityEntryKeys_t();
_local_identity = SecurityEntryIdentity_t(); _local_identity = SecurityEntryIdentity_t();
_local_csrk = csrk_t();
} }
virtual void get_whitelist(WhitelistDbCb_t cb) { } virtual void get_whitelist(WhitelistDbCb_t cb) { }
@ -676,8 +689,12 @@ private:
} }
db_store_t _db[MAX_ENTRIES]; db_store_t _db[MAX_ENTRIES];
SecurityEntryIdentity_t _identities[MAX_ENTRIES];
SecurityEntryKeys_t _local_keys; SecurityEntryKeys_t _local_keys;
SecurityEntryIdentity_t _local_identity; SecurityEntryIdentity_t _local_identity;
csrk_t _local_csrk;
size_t _irk_index;
}; };
} /* namespace generic */ } /* namespace generic */

View File

@ -174,12 +174,27 @@ public:
virtual ble_error_t setPrivateAddressTimeout(uint16_t timeout_in_seconds); 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 // 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. * Returns the requested LTK to the PAL. Called by the security db.
* *
@ -193,10 +208,11 @@ public:
SecurityEntryKeys_t& entryKeys SecurityEntryKeys_t& entryKeys
); );
DbCbAction_t return_csrk_cb( void return_csrk_cb(
connection_handle_t connection, connection_handle_t connection,
const csrk_t *csrk const csrk_t *csrk
); );
public:
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Authentication // Authentication

View File

@ -428,6 +428,12 @@ ble_error_t GenericSecurityManager::setPrivateAddressTimeout(uint16_t timeout_in
return _pal.set_private_address_timeout(timeout_in_seconds); return _pal.set_private_address_timeout(timeout_in_seconds);
} }
void GenericSecurityManager::check_against_irk_cb(
const irk_t *irk
) {
}
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Keys // Keys
// //
@ -476,13 +482,13 @@ DbCbAction_t GenericSecurityManager::set_ltk_cb(
return DB_CB_ACTION_NO_UPDATE_REQUIRED; return DB_CB_ACTION_NO_UPDATE_REQUIRED;
} }
DbCbAction_t GenericSecurityManager::return_csrk_cb( void GenericSecurityManager::return_csrk_cb(
connection_handle_t connection, connection_handle_t connection,
const csrk_t *csrk const csrk_t *csrk
) { ) {
SecurityEntry_t *entry = _db.get_entry(connection); SecurityEntry_t *entry = _db.get_entry(connection);
if (!entry) { if (!entry) {
return DB_CB_ACTION_NO_UPDATE_REQUIRED; return;
} }
_app_event_handler->signingKey( _app_event_handler->signingKey(
@ -490,7 +496,6 @@ DbCbAction_t GenericSecurityManager::return_csrk_cb(
csrk, csrk,
entry->mitm_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) { 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 = NULL;
SecurityEntry_t *entry = _db.connect_entry(connection, peer_address);
/* 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) { if (!entry) {
return; return;
} }
entry->reset(); entry->reset();
entry->master = is_master; 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 */ } /* namespace generic */