mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge #18637
18637: Fido2 follow up r=benpicco a=Ollrogge Co-authored-by: Ollrogge <nils-ollrogge@outlook.de>
This commit is contained in:
commit
ca3b259add
@ -162,11 +162,6 @@ static int _find_matching_rks(ctap_resident_key_t *rks, size_t rks_len,
|
||||
ctap_cred_desc_alt_t *allow_list,
|
||||
size_t allow_list_len,
|
||||
uint8_t *rp_id, size_t rp_id_len);
|
||||
/**
|
||||
* @brief Check if any of the credentials in li belong to this authenticator
|
||||
*/
|
||||
static bool _rks_exist(ctap_cred_desc_alt_t *li, size_t len, uint8_t *rp_id,
|
||||
size_t rp_id_len);
|
||||
|
||||
/**
|
||||
* @brief Decrypt credential that is stored by relying party
|
||||
@ -178,21 +173,11 @@ static bool _rks_exist(ctap_cred_desc_alt_t *li, size_t len, uint8_t *rp_id,
|
||||
*/
|
||||
static int _ctap_decrypt_rk(ctap_resident_key_t *rk, ctap_cred_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief Write credential to flash
|
||||
*/
|
||||
static int _write_rk_to_flash(ctap_resident_key_t *rk);
|
||||
|
||||
/**
|
||||
* @brief Save PIN to authenticator state and write the updated state to flash
|
||||
*/
|
||||
static int _save_pin(uint8_t *pin, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Write authenticator state to flash.
|
||||
*/
|
||||
static int _write_state_to_flash(const ctap_state_t *state);
|
||||
|
||||
/**
|
||||
* @brief Check if PIN protocol version is supported
|
||||
*/
|
||||
@ -210,7 +195,7 @@ static int _decrement_pin_attempts(void);
|
||||
/**
|
||||
* @brief Reset PIN attempts to its starting values
|
||||
*/
|
||||
static void _reset_pin_attempts(void);
|
||||
static int _reset_pin_attempts(void);
|
||||
|
||||
/**
|
||||
* @brief Verify pinAuth sent by platform
|
||||
@ -273,13 +258,7 @@ int fido2_ctap_init(void)
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* CTAP state information is stored at flashpage 0 of the memory area
|
||||
* dedicated for storing CTAP data
|
||||
*/
|
||||
ret = fido2_ctap_mem_read(&_state, fido2_ctap_mem_flash_page(), 0,
|
||||
sizeof(_state));
|
||||
|
||||
ret = fido2_ctap_mem_read_state_from_flash(&_state);
|
||||
if (ret != CTAP2_OK) {
|
||||
return -EPROTO;
|
||||
}
|
||||
@ -357,6 +336,11 @@ size_t fido2_ctap_handle_request(ctap_req_t *req, ctap_resp_t *resp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctap_state_t *fido2_ctap_get_state(void)
|
||||
{
|
||||
return &_state;
|
||||
}
|
||||
|
||||
size_t fido2_ctap_get_info(ctap_resp_t *resp)
|
||||
{
|
||||
assert(resp);
|
||||
@ -422,6 +406,11 @@ size_t fido2_ctap_reset(ctap_resp_t *resp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t get_id(void)
|
||||
{
|
||||
return _state.id_cnt++;
|
||||
}
|
||||
|
||||
static int _reset(void)
|
||||
{
|
||||
fido2_ctap_mem_erase_flash();
|
||||
@ -430,6 +419,7 @@ static int _reset(void)
|
||||
_state.rem_pin_att = CTAP_PIN_MAX_ATTS;
|
||||
_state.pin_is_set = false;
|
||||
_state.rk_amount_stored = 0;
|
||||
_state.id_cnt = 0;
|
||||
|
||||
_rem_pin_att_boot = CTAP_PIN_MAX_ATTS_BOOT;
|
||||
|
||||
@ -448,7 +438,7 @@ static int _reset(void)
|
||||
|
||||
memcpy(_state.config.aaguid, aaguid, sizeof(_state.config.aaguid));
|
||||
|
||||
return _write_state_to_flash(&_state);
|
||||
return fido2_ctap_mem_write_state_to_flash(&_state);
|
||||
}
|
||||
|
||||
static int _make_credential(ctap_req_t *req_raw)
|
||||
@ -486,8 +476,9 @@ static int _make_credential(ctap_req_t *req_raw)
|
||||
rk = req.options.rk;
|
||||
|
||||
if (req.exclude_list_len > 0) {
|
||||
if (_rks_exist(req.exclude_list, req.exclude_list_len, req.rp.id,
|
||||
req.rp.id_len)) {
|
||||
if (_find_matching_rks(_assert_state.rks, CTAP_MAX_EXCLUDE_LIST_SIZE,
|
||||
req.exclude_list, req.exclude_list_len, req.rp.id,
|
||||
req.rp.id_len) > 0x0) {
|
||||
if (!IS_ACTIVE(CONFIG_FIDO2_CTAP_DISABLE_UP)) {
|
||||
fido2_ctap_utils_user_presence_test();
|
||||
}
|
||||
@ -580,7 +571,7 @@ static int _make_credential(ctap_req_t *req_raw)
|
||||
|
||||
/* if created credential is a resident credential, save it to flash */
|
||||
if (rk) {
|
||||
ret = _write_rk_to_flash(&k);
|
||||
ret = fido2_ctap_mem_write_rk_to_flash(&k);
|
||||
if (ret != CTAP2_OK) {
|
||||
goto done;
|
||||
}
|
||||
@ -734,7 +725,7 @@ static int _get_assertion(ctap_req_t *req_raw)
|
||||
* therefore needs to be saved on the device.
|
||||
*/
|
||||
if (!rk->cred_desc.has_nonce) {
|
||||
ret = _write_rk_to_flash(rk);
|
||||
ret = fido2_ctap_mem_write_rk_to_flash(rk);
|
||||
|
||||
if (ret != CTAP2_OK) {
|
||||
goto done;
|
||||
@ -820,7 +811,7 @@ static int _get_next_assertion(void)
|
||||
* therefore needs to be saved on the device.
|
||||
*/
|
||||
if (!rk->cred_desc.has_nonce) {
|
||||
ret = _write_rk_to_flash(rk);
|
||||
ret = fido2_ctap_mem_write_rk_to_flash(rk);
|
||||
|
||||
if (ret != CTAP2_OK) {
|
||||
goto done;
|
||||
@ -1011,9 +1002,15 @@ static int _set_pin(ctap_client_pin_req_t *req)
|
||||
goto done;
|
||||
}
|
||||
|
||||
_save_pin(new_pin_dec, sz);
|
||||
ret = _save_pin(new_pin_dec, sz);
|
||||
if (ret != CTAP2_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
_reset_pin_attempts();
|
||||
ret = _reset_pin_attempts();
|
||||
if (ret != CTAP2_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = CTAP2_OK;
|
||||
|
||||
@ -1123,7 +1120,10 @@ static int _change_pin(ctap_client_pin_req_t *req)
|
||||
goto done;
|
||||
}
|
||||
|
||||
_write_state_to_flash(&_state);
|
||||
ret = fido2_ctap_mem_write_state_to_flash(&_state);
|
||||
if (ret != CTAP2_OK) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = _decrement_pin_attempts();
|
||||
|
||||
@ -1135,7 +1135,10 @@ static int _change_pin(ctap_client_pin_req_t *req)
|
||||
goto done;
|
||||
}
|
||||
|
||||
_reset_pin_attempts();
|
||||
ret = _reset_pin_attempts();
|
||||
if (ret != CTAP2_OK) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
sz = sizeof(new_pin_dec);
|
||||
/* decrypt newPinEnc to obtain newPin */
|
||||
@ -1154,7 +1157,10 @@ static int _change_pin(ctap_client_pin_req_t *req)
|
||||
goto done;
|
||||
}
|
||||
|
||||
_save_pin(new_pin_dec, sz);
|
||||
ret = _save_pin(new_pin_dec, sz);
|
||||
if (ret != CTAP2_OK) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = CTAP2_OK;
|
||||
|
||||
@ -1238,7 +1244,10 @@ static int _get_pin_token(ctap_client_pin_req_t *req)
|
||||
goto done;
|
||||
}
|
||||
|
||||
_reset_pin_attempts();
|
||||
ret = _reset_pin_attempts();
|
||||
if (ret != CTAP2_OK) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
sz = sizeof(pin_token_enc);
|
||||
ret = fido2_ctap_crypto_aes_enc(pin_token_enc, &sz, _pin_token,
|
||||
@ -1274,7 +1283,7 @@ static int _save_pin(uint8_t *pin, size_t len)
|
||||
memcpy(_state.pin_hash, buf, sizeof(_state.pin_hash));
|
||||
_state.pin_is_set = true;
|
||||
|
||||
return _write_state_to_flash(&_state);
|
||||
return fido2_ctap_mem_write_state_to_flash(&_state);
|
||||
}
|
||||
|
||||
bool fido2_ctap_cred_params_supported(uint8_t cred_type, int32_t alg_type)
|
||||
@ -1310,6 +1319,8 @@ static inline bool _is_boot_locked(void)
|
||||
|
||||
static int _decrement_pin_attempts(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (_state.rem_pin_att > 0) {
|
||||
_state.rem_pin_att--;
|
||||
}
|
||||
@ -1318,7 +1329,10 @@ static int _decrement_pin_attempts(void)
|
||||
_rem_pin_att_boot--;
|
||||
}
|
||||
|
||||
_write_state_to_flash(&_state);
|
||||
ret = fido2_ctap_mem_write_state_to_flash(&_state);
|
||||
if (ret != CTAP2_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (_is_locked()) {
|
||||
return CTAP2_ERR_PIN_BLOCKED;
|
||||
@ -1331,12 +1345,12 @@ static int _decrement_pin_attempts(void)
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
static void _reset_pin_attempts(void)
|
||||
static int _reset_pin_attempts(void)
|
||||
{
|
||||
_state.rem_pin_att = CTAP_PIN_MAX_ATTS;
|
||||
_rem_pin_att_boot = CTAP_PIN_MAX_ATTS_BOOT;
|
||||
|
||||
_write_state_to_flash(&_state);
|
||||
return fido2_ctap_mem_write_state_to_flash(&_state);
|
||||
}
|
||||
|
||||
static int _verify_pin_auth(uint8_t *auth, uint8_t *hash, size_t len)
|
||||
@ -1357,73 +1371,6 @@ static int _verify_pin_auth(uint8_t *auth, uint8_t *hash, size_t len)
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
static bool _rks_exist(ctap_cred_desc_alt_t *li, size_t len, uint8_t *rp_id,
|
||||
size_t rp_id_len)
|
||||
{
|
||||
uint8_t rp_id_hash[SHA256_DIGEST_LENGTH] = { 0 };
|
||||
ctap_resident_key_t rk;
|
||||
int ret;
|
||||
|
||||
ret = fido2_ctap_crypto_sha256(rp_id, rp_id_len, rp_id_hash);
|
||||
|
||||
if (ret != CTAP2_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* no rks stored, try decrypt only */
|
||||
if (_state.rk_amount_stored == 0) {
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
ret = _ctap_decrypt_rk(&rk, &li[i].cred_id);
|
||||
if (ret == CTAP2_OK) {
|
||||
if (memcmp(rk.rp_id_hash, rp_id_hash, SHA256_DIGEST_LENGTH)
|
||||
== 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < _state.rk_amount_stored; i++) {
|
||||
int page_num = fido2_ctap_mem_get_flashpage_number_of_rk(i);
|
||||
|
||||
if (page_num < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int offset_into_page = fido2_ctap_mem_get_offset_of_rk_into_flashpage(i);
|
||||
|
||||
if (offset_into_page < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = fido2_ctap_mem_read(&rk, page_num, offset_into_page, sizeof(rk));
|
||||
|
||||
if (ret != CTAP2_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(rk.rp_id_hash, rp_id_hash, SHA256_DIGEST_LENGTH) == 0) {
|
||||
for (size_t j = 0; j < len; j++) {
|
||||
if (memcmp(li[j].cred_id.id, rk.cred_desc.cred_id,
|
||||
CTAP_CREDENTIAL_ID_SIZE) == 0) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
/* no match with stored key, try to decrypt */
|
||||
ret = _ctap_decrypt_rk(&rk, &li[i].cred_id);
|
||||
if (ret == CTAP2_OK) {
|
||||
if (memcmp(rk.rp_id_hash, rp_id_hash,
|
||||
SHA256_DIGEST_LENGTH) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int _find_matching_rks(ctap_resident_key_t *rks, size_t rks_len,
|
||||
ctap_cred_desc_alt_t *allow_list,
|
||||
size_t allow_list_len, uint8_t *rp_id,
|
||||
@ -1431,7 +1378,6 @@ static int _find_matching_rks(ctap_resident_key_t *rks, size_t rks_len,
|
||||
{
|
||||
uint8_t index = 0;
|
||||
uint8_t rp_id_hash[SHA256_DIGEST_LENGTH] = { 0 };
|
||||
ctap_resident_key_t rk;
|
||||
int ret;
|
||||
|
||||
ret = fido2_ctap_crypto_sha256(rp_id, rp_id_len, rp_id_hash);
|
||||
@ -1453,52 +1399,33 @@ static int _find_matching_rks(ctap_resident_key_t *rks, size_t rks_len,
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _state.rk_amount_stored; i++) {
|
||||
int page_num = fido2_ctap_mem_get_flashpage_number_of_rk(i);
|
||||
ctap_resident_key_t rk = { 0 };
|
||||
uint32_t addr = 0x0;
|
||||
|
||||
if (page_num < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
while (fido2_ctap_mem_read_rk_from_flash(&rk, rp_id_hash, &addr) == CTAP2_OK) {
|
||||
if (allow_list_len == 0) {
|
||||
memcpy(&rks[index], &rk, sizeof(rk));
|
||||
index++;
|
||||
}
|
||||
|
||||
int offset_into_page = fido2_ctap_mem_get_offset_of_rk_into_flashpage(i);
|
||||
|
||||
if (offset_into_page < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
ret = fido2_ctap_mem_read(&rk, page_num, offset_into_page, sizeof(rk));
|
||||
|
||||
if (ret != CTAP2_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* search for rk's matching rp_id_hash */
|
||||
if (memcmp(rk.rp_id_hash, rp_id_hash, SHA256_DIGEST_LENGTH) == 0) {
|
||||
if (allow_list_len == 0) {
|
||||
for (size_t i = 0; i < allow_list_len; i++) {
|
||||
/* if allow list is present, check that cred_id is in list */
|
||||
if (memcmp(allow_list[i].cred_id.id, rk.cred_desc.cred_id,
|
||||
sizeof(rk.cred_desc.cred_id)) == 0) {
|
||||
memcpy(&rks[index], &rk, sizeof(rk));
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
/* if allow list is present, also check that cred_id is in list */
|
||||
for (size_t j = 0; j < allow_list_len; j++) {
|
||||
if (memcmp(allow_list[j].cred_id.id, rk.cred_desc.cred_id,
|
||||
sizeof(rk.cred_desc.cred_id)) == 0) {
|
||||
memcpy(&rks[index], &rk, sizeof(rk));
|
||||
/* no match with stored key, try to decrypt */
|
||||
ret = _ctap_decrypt_rk(&rks[index],
|
||||
&allow_list[i].cred_id);
|
||||
if (ret == CTAP2_OK) {
|
||||
if (memcmp(rks[index].rp_id_hash, rk.rp_id_hash,
|
||||
SHA256_DIGEST_LENGTH) == 0) {
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
/* no match with stored key, try to decrypt */
|
||||
ret = _ctap_decrypt_rk(&rks[index],
|
||||
&allow_list[j].cred_id);
|
||||
if (ret == CTAP2_OK) {
|
||||
if (memcmp(rks[index].rp_id_hash, rk.rp_id_hash,
|
||||
SHA256_DIGEST_LENGTH) == 0) {
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1509,8 +1436,8 @@ static int _find_matching_rks(ctap_resident_key_t *rks, size_t rks_len,
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort in descending order based on creation time. Credential with the
|
||||
* most recent (highest) creation time will be first in list.
|
||||
* Sort in descending order based on id. Credential with the
|
||||
* highest (most recent) id will be first in list.
|
||||
*/
|
||||
if (index > 0) {
|
||||
qsort(rks, index, sizeof(ctap_resident_key_t), fido2_ctap_utils_cred_cmp);
|
||||
@ -1519,69 +1446,6 @@ static int _find_matching_rks(ctap_resident_key_t *rks, size_t rks_len,
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* overwrite existing key if equal, else find free space.
|
||||
*
|
||||
* The current official CTAP spec does not have credential management yet
|
||||
* so rk's can't be deleted, only overwritten => we can be sure that there are
|
||||
* no holes when reading keys from flash memory
|
||||
*/
|
||||
static int _write_rk_to_flash(ctap_resident_key_t *rk)
|
||||
{
|
||||
int ret;
|
||||
int page_num = fido2_ctap_mem_flash_page() + CTAP_FLASH_RK_OFF;
|
||||
int offset_into_page = 0;
|
||||
bool equal = false;
|
||||
ctap_resident_key_t rk_tmp = { 0 };
|
||||
|
||||
if (_state.rk_amount_stored > 0) {
|
||||
for (uint16_t i = 0; i <= _state.rk_amount_stored; i++) {
|
||||
page_num = fido2_ctap_mem_get_flashpage_number_of_rk(i);
|
||||
|
||||
if (page_num < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
offset_into_page = fido2_ctap_mem_get_offset_of_rk_into_flashpage(i);
|
||||
|
||||
if (offset_into_page < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
if (i == _state.rk_amount_stored) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = fido2_ctap_mem_read(&rk_tmp, page_num, offset_into_page, sizeof(rk_tmp));
|
||||
|
||||
if (ret != CTAP2_OK) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
/* if equal overwrite */
|
||||
if (fido2_ctap_utils_ks_equal(&rk_tmp, rk)) {
|
||||
equal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!equal) {
|
||||
if (_state.rk_amount_stored >= CTAP_FLASH_MAX_NUM_RKS) {
|
||||
return CTAP2_ERR_KEY_STORE_FULL;
|
||||
}
|
||||
|
||||
_state.rk_amount_stored++;
|
||||
ret = _write_state_to_flash(&_state);
|
||||
|
||||
if (ret != CTAP2_OK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return fido2_ctap_mem_write(rk, page_num, offset_into_page, CTAP_FLASH_RK_SZ);
|
||||
}
|
||||
|
||||
static int _make_auth_data_assert(uint8_t *rp_id, size_t rp_id_len,
|
||||
ctap_auth_data_header_t *auth_data, bool uv,
|
||||
bool up, uint32_t sign_count)
|
||||
@ -1680,7 +1544,7 @@ static int _make_auth_data_attest(ctap_make_credential_req_t *req,
|
||||
/* init key */
|
||||
k->cred_desc.cred_type = req->cred_type;
|
||||
k->user_id_len = user->id_len;
|
||||
k->creation_time = ztimer_now(ZTIMER_MSEC);
|
||||
k->id = get_id();
|
||||
|
||||
memcpy(k->user_id, user->id, user->id_len);
|
||||
memcpy(k->rp_id_hash, auth_header->rp_id_hash, SHA256_DIGEST_LENGTH);
|
||||
@ -1745,7 +1609,11 @@ int fido2_ctap_encrypt_rk(ctap_resident_key_t *rk, uint8_t *nonce,
|
||||
}
|
||||
|
||||
_state.cred_key_is_initialized = true;
|
||||
_write_state_to_flash(&_state);
|
||||
|
||||
ret = fido2_ctap_mem_write_state_to_flash(&_state);
|
||||
if (ret != CTAP2_OK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = fido2_ctap_crypto_aes_ccm_enc((uint8_t *)id, sizeof(id),
|
||||
@ -1790,17 +1658,6 @@ static int _ctap_decrypt_rk(ctap_resident_key_t *rk, ctap_cred_id_t *id)
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
static int _write_state_to_flash(const ctap_state_t *state)
|
||||
{
|
||||
/**
|
||||
* CTAP state information is stored at flashpage 0 of the memory area
|
||||
* dedicated for storing CTAP data
|
||||
*/
|
||||
return fido2_ctap_mem_write(state,
|
||||
fido2_ctap_mem_flash_page(), 0,
|
||||
CTAP_FLASH_STATE_SZ);
|
||||
}
|
||||
|
||||
int fido2_ctap_get_sig(const uint8_t *auth_data, size_t auth_data_len,
|
||||
const uint8_t *client_data_hash,
|
||||
const ctap_resident_key_t *rk,
|
||||
|
@ -17,10 +17,13 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "bitarithm.h"
|
||||
|
||||
#include "mtd.h"
|
||||
#include "mtd_flashpage.h"
|
||||
|
||||
#include "fido2/ctap/ctap_mem.h"
|
||||
#include "fido2/ctap/ctap_utils.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
@ -39,13 +42,23 @@ static mtd_dev_t *_mtd_dev = &_mtd_flash_dev.base;
|
||||
/**
|
||||
* @brief Check if flash region is erased
|
||||
*/
|
||||
static bool _flash_is_erased(int page, int offset, size_t len);
|
||||
static bool _flash_is_erased(uint32_t addr, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Get available amount of flashpages to store resident keys
|
||||
*/
|
||||
static unsigned _amount_flashpages_rk(void);
|
||||
|
||||
/**
|
||||
* @brief Write to flash memory
|
||||
*/
|
||||
static int _flash_write(const void *buf, uint32_t addr, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Get start address of reserved flash memory region
|
||||
*/
|
||||
static unsigned _flash_start(void);
|
||||
|
||||
int fido2_ctap_mem_init(void)
|
||||
{
|
||||
int ret = mtd_init(_mtd_dev);
|
||||
@ -77,21 +90,24 @@ int fido2_ctap_mem_read(void *buf, uint32_t page, uint32_t offset, uint32_t len)
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
int fido2_ctap_mem_write(const void *buf, uint32_t page, uint32_t offset, uint32_t len)
|
||||
static int _flash_write(const void *buf, uint32_t addr, size_t len)
|
||||
{
|
||||
assert(buf);
|
||||
|
||||
int ret;
|
||||
|
||||
if (!_flash_is_erased(page, offset, len)) {
|
||||
ret = mtd_write_page(_mtd_dev, buf, page, offset, len);
|
||||
if (!_flash_is_erased(addr, len)) {
|
||||
/* page size is always a power of two */
|
||||
const uint32_t page_shift = bitarithm_msb(_mtd_dev->page_size);
|
||||
const uint32_t page_mask = _mtd_dev->page_size - 1;
|
||||
|
||||
ret = mtd_write_page(_mtd_dev, buf, addr >> page_shift, addr & page_mask, len);
|
||||
|
||||
if (ret < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret = mtd_write_page_raw(_mtd_dev, buf, page, offset, len);
|
||||
ret = mtd_write(_mtd_dev, buf, addr, len);
|
||||
|
||||
if (ret < 0) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
@ -101,12 +117,10 @@ int fido2_ctap_mem_write(const void *buf, uint32_t page, uint32_t offset, uint32
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
static bool _flash_is_erased(int page, int offset, size_t len)
|
||||
static bool _flash_is_erased(uint32_t addr, size_t len)
|
||||
{
|
||||
uint8_t *addr = ((uint8_t *)flashpage_addr(page) + offset);
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (addr[i] != FLASHPAGE_ERASE_STATE) {
|
||||
if (*(uint32_t *)(addr + i) != FLASHPAGE_ERASE_STATE) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -114,49 +128,14 @@ static bool _flash_is_erased(int page, int offset, size_t len)
|
||||
return true;
|
||||
}
|
||||
|
||||
int fido2_ctap_mem_get_flashpage_number_of_rk(uint16_t rk_idx)
|
||||
{
|
||||
uint16_t idx = 0;
|
||||
unsigned start = fido2_ctap_mem_flash_page() + CTAP_FLASH_RK_OFF;
|
||||
unsigned amount = _amount_flashpages_rk();
|
||||
|
||||
for (unsigned i = start; i < start + amount; i++) {
|
||||
idx += flashpage_size(i) / CTAP_FLASH_RK_SZ;
|
||||
|
||||
if (idx >= rk_idx) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fido2_ctap_mem_get_offset_of_rk_into_flashpage(uint16_t rk_idx)
|
||||
{
|
||||
uint16_t idx = 0;
|
||||
unsigned start = fido2_ctap_mem_flash_page() + CTAP_FLASH_RK_OFF;
|
||||
unsigned amount = _amount_flashpages_rk();
|
||||
|
||||
for (unsigned i = start; i < start + amount; i++) {
|
||||
uint16_t old_idx = idx;
|
||||
idx += flashpage_size(i) / CTAP_FLASH_RK_SZ;
|
||||
|
||||
if (idx >= rk_idx) {
|
||||
return CTAP_FLASH_RK_SZ * (rk_idx - old_idx);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned fido2_ctap_mem_flash_page(void)
|
||||
static unsigned _flash_start(void)
|
||||
{
|
||||
return flashpage_page((void *)_backing_memory);
|
||||
}
|
||||
|
||||
int fido2_ctap_mem_erase_flash(void)
|
||||
{
|
||||
unsigned start = fido2_ctap_mem_flash_page();
|
||||
unsigned start = _flash_start();
|
||||
unsigned end = start + CONFIG_FIDO2_CTAP_NUM_FLASHPAGES;
|
||||
|
||||
for (unsigned page = start; page < end; page++) {
|
||||
@ -165,3 +144,109 @@ int fido2_ctap_mem_erase_flash(void)
|
||||
|
||||
return CTAP2_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* CTAP state information is stored at flashpage 0 of the memory area
|
||||
* dedicated for storing CTAP data
|
||||
*/
|
||||
int fido2_ctap_mem_read_state_from_flash(ctap_state_t *state)
|
||||
{
|
||||
uint32_t addr = (uint32_t)flashpage_addr(_flash_start());
|
||||
|
||||
return mtd_read(_mtd_dev, state, addr, sizeof(ctap_state_t));
|
||||
}
|
||||
|
||||
/**
|
||||
* overwrite existing key if equal, else find free space.
|
||||
*
|
||||
* The current official CTAP spec does not have credential management yet
|
||||
* so rk's can't be deleted, only overwritten => we can be sure that there are
|
||||
* no holes when reading keys from flash memory
|
||||
*/
|
||||
int fido2_ctap_mem_write_rk_to_flash(ctap_resident_key_t *rk)
|
||||
{
|
||||
int ret;
|
||||
uint32_t addr = (uint32_t)flashpage_addr(_flash_start() + CTAP_FLASH_RK_OFF);
|
||||
uint16_t amt_stored = fido2_ctap_get_state()->rk_amount_stored;
|
||||
ctap_resident_key_t tmp = { 0 };
|
||||
bool equal = false;
|
||||
|
||||
for (uint16_t i = 0; i < amt_stored; i++) {
|
||||
ret = mtd_read(_mtd_dev, &tmp, addr, sizeof(ctap_resident_key_t));
|
||||
|
||||
if (ret < 0) {
|
||||
DEBUG("%s, %d: mtd_read failed", RIOT_FILE_RELATIVE,
|
||||
__LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fido2_ctap_utils_ks_equal(&tmp, rk)) {
|
||||
equal = true;
|
||||
break;
|
||||
}
|
||||
|
||||
addr += CTAP_FLASH_RK_SZ;
|
||||
}
|
||||
|
||||
if (!equal) {
|
||||
if (amt_stored >= CTAP_FLASH_MAX_NUM_RKS) {
|
||||
return CTAP2_ERR_KEY_STORE_FULL;
|
||||
}
|
||||
|
||||
ctap_state_t *state = fido2_ctap_get_state();
|
||||
state->rk_amount_stored++;
|
||||
ret = fido2_ctap_mem_write_state_to_flash(state);
|
||||
|
||||
if (ret != CTAP2_OK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return _flash_write(rk, addr, CTAP_FLASH_RK_SZ);
|
||||
}
|
||||
|
||||
int fido2_ctap_mem_write_state_to_flash(ctap_state_t *state)
|
||||
{
|
||||
uint32_t addr = (uint32_t)flashpage_addr(_flash_start());
|
||||
|
||||
return _flash_write(state, addr, CTAP_FLASH_STATE_SZ);
|
||||
}
|
||||
|
||||
int fido2_ctap_mem_read_rk_from_flash(ctap_resident_key_t *key, uint8_t *rp_id_hash, uint32_t *addr)
|
||||
{
|
||||
uint16_t end;
|
||||
uint16_t amt_stored = fido2_ctap_get_state()->rk_amount_stored;
|
||||
|
||||
if (*addr == 0x0) {
|
||||
end = amt_stored;
|
||||
*addr = (uint32_t)flashpage_addr(_flash_start() + CTAP_FLASH_RK_OFF);
|
||||
}
|
||||
else {
|
||||
uint32_t start_addr = (uint32_t)flashpage_addr(_flash_start() + CTAP_FLASH_RK_OFF);
|
||||
uint16_t rks_read = (*addr - start_addr) / CTAP_FLASH_RK_SZ;
|
||||
|
||||
if (rks_read > amt_stored) {
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
end = amt_stored - rks_read;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < end; i++) {
|
||||
int ret = mtd_read(_mtd_dev, key, *addr, sizeof(ctap_resident_key_t));
|
||||
|
||||
if (ret < 0) {
|
||||
DEBUG("%s, %d: mtd_read failed", RIOT_FILE_RELATIVE,
|
||||
__LINE__);
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
||||
*addr += CTAP_FLASH_RK_SZ;
|
||||
|
||||
if (memcmp(key->rp_id_hash, rp_id_hash, SHA256_DIGEST_LENGTH) == 0) {
|
||||
return CTAP2_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return CTAP1_ERR_OTHER;
|
||||
}
|
||||
|
@ -55,12 +55,12 @@ extern "C" {
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_MAKE_CREDENTIAL 0x01 /**< authenticatorMakeCredential method */
|
||||
#define CTAP_GET_ASSERTION 0x02 /**< authenticatorGetAssertion method */
|
||||
#define CTAP_GET_INFO 0x04 /**< authenticatorGetInfo method */
|
||||
#define CTAP_CLIENT_PIN 0x06 /**< authenticatorClientPIN method */
|
||||
#define CTAP_RESET 0x07 /**< authenticatorReset method */
|
||||
#define CTAP_GET_NEXT_ASSERTION 0x08 /**< authenticatorGetNextAssertion method */
|
||||
#define CTAP_MAKE_CREDENTIAL 0x01 /**< authenticatorMakeCredential method */
|
||||
#define CTAP_GET_ASSERTION 0x02 /**< authenticatorGetAssertion method */
|
||||
#define CTAP_GET_INFO 0x04 /**< authenticatorGetInfo method */
|
||||
#define CTAP_CLIENT_PIN 0x06 /**< authenticatorClientPIN method */
|
||||
#define CTAP_RESET 0x07 /**< authenticatorReset method */
|
||||
#define CTAP_GET_NEXT_ASSERTION 0x08 /**< authenticatorGetNextAssertion method */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@ -79,9 +79,9 @@ extern "C" {
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_VERSION_FLAG_FIDO_PRE 0x01 /**< FIDO 2.1 flag */
|
||||
#define CTAP_VERSION_FLAG_FIDO 0x02 /**< FIDO 2 flag */
|
||||
#define CTAP_VERSION_FLAG_U2F_V2 0x04 /**< U2F V2 flag */
|
||||
#define CTAP_VERSION_FLAG_FIDO_PRE 0x01 /**< FIDO 2.1 flag */
|
||||
#define CTAP_VERSION_FLAG_FIDO 0x02 /**< FIDO 2 flag */
|
||||
#define CTAP_VERSION_FLAG_U2F_V2 0x04 /**< U2F V2 flag */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@ -103,11 +103,11 @@ extern "C" {
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_INFO_OPTIONS_FLAG_PLAT (1 << 0) /**< platform device flag */
|
||||
#define CTAP_INFO_OPTIONS_FLAG_RK (1 << 1) /**< resident key flag */
|
||||
#define CTAP_INFO_OPTIONS_FLAG_CLIENT_PIN (1 << 2) /**< clientPIN flag */
|
||||
#define CTAP_INFO_OPTIONS_FLAG_UP (1 << 3) /**< user presence flag */
|
||||
#define CTAP_INFO_OPTIONS_FLAG_UV (1 << 4) /**< user verification flag */
|
||||
#define CTAP_INFO_OPTIONS_FLAG_PLAT (1 << 0) /**< platform device flag */
|
||||
#define CTAP_INFO_OPTIONS_FLAG_RK (1 << 1) /**< resident key flag */
|
||||
#define CTAP_INFO_OPTIONS_FLAG_CLIENT_PIN (1 << 2) /**< clientPIN flag */
|
||||
#define CTAP_INFO_OPTIONS_FLAG_UP (1 << 3) /**< user presence flag */
|
||||
#define CTAP_INFO_OPTIONS_FLAG_UV (1 << 4) /**< user verification flag */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@ -115,11 +115,11 @@ extern "C" {
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define CTAP_CP_REQ_SUB_COMMAND_GET_RETRIES 0x01 /**< getRetries subCommand */
|
||||
#define CTAP_CP_REQ_SUB_COMMAND_GET_KEY_AGREEMENT 0x02 /**< getKeyAgreement subCommand */
|
||||
#define CTAP_CP_REQ_SUB_COMMAND_SET_PIN 0x03 /**< setPIN subCommand */
|
||||
#define CTAP_CP_REQ_SUB_COMMAND_CHANGE_PIN 0x04 /**< changePIN subCommand */
|
||||
#define CTAP_CP_REQ_SUB_COMMAND_GET_PIN_TOKEN 0x05 /**< getPinToken subCommand */
|
||||
#define CTAP_CP_REQ_SUB_COMMAND_GET_RETRIES 0x01 /**< getRetries subCommand */
|
||||
#define CTAP_CP_REQ_SUB_COMMAND_GET_KEY_AGREEMENT 0x02 /**< getKeyAgreement subCommand */
|
||||
#define CTAP_CP_REQ_SUB_COMMAND_SET_PIN 0x03 /**< setPIN subCommand */
|
||||
#define CTAP_CP_REQ_SUB_COMMAND_CHANGE_PIN 0x04 /**< changePIN subCommand */
|
||||
#define CTAP_CP_REQ_SUB_COMMAND_GET_PIN_TOKEN 0x05 /**< getPinToken subCommand */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@ -380,7 +380,7 @@ extern "C" {
|
||||
/**
|
||||
* @brief Max size of allow list
|
||||
*/
|
||||
#define CTAP_MAX_EXCLUDE_LIST_SIZE 0x10
|
||||
#define CTAP_MAX_EXCLUDE_LIST_SIZE 0x14
|
||||
|
||||
/**
|
||||
* @brief CTAP cred struct forward declaration
|
||||
@ -420,6 +420,7 @@ typedef struct {
|
||||
uint8_t cred_key[CTAP_CRED_KEY_LEN]; /**< AES CCM encryption key for cred */
|
||||
bool cred_key_is_initialized; /**< AES CCM key initialized flag */
|
||||
bool pin_is_set; /**< PIN is set or not */
|
||||
uint32_t id_cnt; /**< id counter for credential id */
|
||||
} ctap_state_t;
|
||||
|
||||
/**
|
||||
@ -498,12 +499,11 @@ struct __attribute__((packed)) ctap_resident_key {
|
||||
uint8_t user_id[CTAP_USER_ID_MAX_SIZE]; /**< id of user */
|
||||
uint8_t user_id_len; /**< length of the user id */
|
||||
uint8_t priv_key[CTAP_CRYPTO_KEY_SIZE]; /**< private key */
|
||||
uint16_t id; /**< internal id of key */
|
||||
uint32_t sign_count; /**< signature counter.
|
||||
See webauthn specification
|
||||
(version 20190304) section 6.1.1
|
||||
for details. */
|
||||
uint32_t creation_time; /**< timestamp for when credential
|
||||
was created */
|
||||
ctap_cred_desc_t cred_desc; /**< credential descriptor */
|
||||
};
|
||||
|
||||
@ -681,6 +681,13 @@ int fido2_ctap_encrypt_rk(ctap_resident_key_t *rk, uint8_t *nonce,
|
||||
*/
|
||||
bool fido2_ctap_pin_is_set(void);
|
||||
|
||||
/**
|
||||
* @brief Get a pointer to the authenticator state
|
||||
*
|
||||
* @return pointer to @ref ctap_state_t
|
||||
*/
|
||||
ctap_state_t *fido2_ctap_get_state(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -104,18 +104,6 @@ extern "C" {
|
||||
*/
|
||||
int fido2_ctap_mem_init(void);
|
||||
|
||||
/**
|
||||
* @brief Write to flash memory
|
||||
*
|
||||
* @param[in] buf buffer to write
|
||||
* @param[in] page page to write to
|
||||
* @param[in] offset offset from the start of the page (in bytes)
|
||||
* @param[in] len number of bytes to write
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_mem_write(const void *buf, uint32_t page, uint32_t offset, uint32_t len);
|
||||
|
||||
/**
|
||||
* @brief Read from flash memory
|
||||
*
|
||||
@ -128,41 +116,6 @@ int fido2_ctap_mem_write(const void *buf, uint32_t page, uint32_t offset, uint32
|
||||
*/
|
||||
int fido2_ctap_mem_read(void *buf, uint32_t page, uint32_t offset, uint32_t len);
|
||||
|
||||
/**
|
||||
* @brief Get flashpage number resident key with index @p rk_idx.
|
||||
*
|
||||
* @param[in] rk_idx index of resident key
|
||||
*
|
||||
* @return page number if no error
|
||||
* @return -1 if @p rk_idx is invalid
|
||||
*/
|
||||
int fido2_ctap_mem_get_flashpage_number_of_rk(uint16_t rk_idx);
|
||||
|
||||
/**
|
||||
* @brief Get offset of resident key into flashpage where flashpage =
|
||||
* fido2_ctap_mem_get_flashpage_number_of_r(i)
|
||||
*
|
||||
* @param[in] rk_idx index of resident key
|
||||
*
|
||||
* @return page number if no error
|
||||
* @return -1 if @p rk_idx is invalid
|
||||
*/
|
||||
int fido2_ctap_mem_get_offset_of_rk_into_flashpage(uint16_t rk_idx);
|
||||
|
||||
/**
|
||||
* @brief Get page number for storing authenticator state information
|
||||
*
|
||||
* @return page number
|
||||
*/
|
||||
unsigned fido2_ctap_mem_flash_page(void);
|
||||
|
||||
/**
|
||||
* @brief Get start page for storing resident keys
|
||||
*
|
||||
* @return page number
|
||||
*/
|
||||
unsigned fido2_ctap_mem_get_rk_start_page(void);
|
||||
|
||||
/**
|
||||
* @brief Erase all flashpages containing CTAP data
|
||||
*
|
||||
@ -170,6 +123,50 @@ unsigned fido2_ctap_mem_get_rk_start_page(void);
|
||||
*/
|
||||
int fido2_ctap_mem_erase_flash(void);
|
||||
|
||||
/**
|
||||
* @brief Read authenticator state from flash
|
||||
*
|
||||
* @param[in] state pointer to authenticator state
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_mem_read_state_from_flash(ctap_state_t *state);
|
||||
|
||||
/**
|
||||
* @brief Write authenticator state to flash
|
||||
*
|
||||
* @param[in] state pointer to authenticator state
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_mem_write_state_to_flash(ctap_state_t *state);
|
||||
|
||||
/**
|
||||
* @brief Find resident credential for @p rp_id_hash in flash
|
||||
*
|
||||
* The function stores the flash address of the next credential in @p addr.
|
||||
* This allows for consecutive calls of the function in order to find all
|
||||
* stored credentials stored for the relying party identified by
|
||||
* @p rp_id_hash.
|
||||
*
|
||||
* @param[in] key pointer to authenticator state
|
||||
* @param[in] rp_id_hash pointer to hash of rp domain string
|
||||
* @param[in] addr pointer to address where to read from
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_mem_read_rk_from_flash(ctap_resident_key_t *key, uint8_t *rp_id_hash,
|
||||
uint32_t *addr);
|
||||
|
||||
/**
|
||||
* @brief Write resident credential to flash
|
||||
*
|
||||
* @param[in] rk pointer to resident credential
|
||||
*
|
||||
* @return @ref ctap_status_codes_t
|
||||
*/
|
||||
int fido2_ctap_mem_write_rk_to_flash(ctap_resident_key_t *rk);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -52,21 +52,24 @@ int fido2_ctap_utils_init_gpio_pin(gpio_t pin, gpio_mode_t mode, gpio_flank_t fl
|
||||
int fido2_ctap_utils_user_presence_test(void);
|
||||
|
||||
/**
|
||||
* @brief Compare fido2 credentials based on creation time
|
||||
* @brief Compare fido2 credentials based on id to find more recent one
|
||||
*
|
||||
* The more recent credential has a higher id. Therefore we sort in
|
||||
* descending order.
|
||||
*
|
||||
* @param[in] k1 first resident key
|
||||
* @param[in] k2 second resident key
|
||||
*
|
||||
* @return <0 if k2 has a bigger sign_count
|
||||
* @return 0 if equal k1 and k2 have equal sign_count
|
||||
* @return >0 if k1 has a bigger sign_count
|
||||
* @return <0 if k1 > k2
|
||||
* @return >0 if k1 < k2
|
||||
*/
|
||||
static inline int fido2_ctap_utils_cred_cmp(const void *k1, const void *k2)
|
||||
{
|
||||
ctap_resident_key_t *_k1 = (ctap_resident_key_t *)k1;
|
||||
ctap_resident_key_t *_k2 = (ctap_resident_key_t *)k2;
|
||||
|
||||
return _k2->creation_time - _k1->creation_time;
|
||||
/* multiply by -1 because we want descending order. */
|
||||
return (_k1->id - _k2->id) * -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user