Add FIDO object authorization context with operation-scoped evidence

Invalidate authorization epochs on PIN/UV token and session changes, with host tests

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
Pol Henarejos
2026-07-17 22:49:30 +02:00
parent 16f9379e70
commit 43cf570ac3
7 changed files with 225 additions and 4 deletions
+14
View File
@@ -112,6 +112,7 @@ endif()
list(APPEND SOURCES
${CMAKE_CURRENT_LIST_DIR}/src/fido/fido.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/object_authorization.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/object_provider.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/files.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/cmd_register.c
@@ -226,6 +227,19 @@ if(NOT ESP_PLATFORM)
endif()
target_link_libraries(fido_object_provider_test PRIVATE mbedtls)
add_test(NAME fido_object_provider_test COMMAND fido_object_provider_test)
add_executable(fido_object_authorization_test
${CMAKE_CURRENT_LIST_DIR}/src/fido/object_authorization.c
${CMAKE_CURRENT_LIST_DIR}/tests/fido_object_authorization_test.c
)
target_include_directories(fido_object_authorization_test PRIVATE ${INCLUDES})
if(MSVC)
target_compile_options(fido_object_authorization_test PRIVATE /W4 /WX)
else()
target_compile_options(fido_object_authorization_test PRIVATE -Wall -Wextra -Werror)
endif()
target_link_libraries(fido_object_authorization_test PRIVATE mbedtls)
add_test(NAME fido_object_authorization_test COMMAND fido_object_authorization_test)
else()
target_link_libraries(
pico_fido
+15 -3
View File
@@ -33,6 +33,7 @@
#include "random.h"
#include "crypto_utils.h"
#include "apdu.h"
#include "object_authorization.h"
uint32_t usage_timer = 0, initial_usage_time_limit = 0;
uint32_t max_usage_time_period = 600 * 1000;
@@ -74,28 +75,34 @@ static int beginUsingPinUvAuthToken(bool userIsPresent) {
initial_usage_time_limit = board_millis();
usage_timer = board_millis();
paut.in_use = true;
fido_object_authorization_session_invalidate();
return 0;
}
void clearUserPresentFlag(void) {
if (paut.in_use == true) {
if (paut.in_use == true && paut.user_present) {
paut.user_present = false;
fido_object_authorization_session_invalidate();
}
}
void clearUserVerifiedFlag(void) {
if (paut.in_use == true) {
if (paut.in_use == true && paut.user_verified) {
paut.user_verified = false;
fido_object_authorization_session_invalidate();
}
}
void clearPinUvAuthTokenPermissionsExceptLbw(void) {
if (paut.in_use == true) {
if (paut.in_use == true && paut.permissions != CTAP_PERMISSION_LBW) {
paut.permissions = CTAP_PERMISSION_LBW;
fido_object_authorization_session_invalidate();
}
}
static void stopUsingPinUvAuthToken(void) {
bool token_active = paut.in_use || paut.permissions != 0 || paut.has_rp_id || paut.user_present || paut.user_verified;
paut.permissions = 0;
usage_timer = 0;
paut.in_use = false;
@@ -104,6 +111,9 @@ static void stopUsingPinUvAuthToken(void) {
initial_usage_time_limit = 0;
paut.user_present = paut.user_verified = false;
user_present_time_limit = 0;
if (token_active) {
fido_object_authorization_session_invalidate();
}
}
bool getUserPresentFlagValue(void) {
@@ -195,6 +205,7 @@ int resetPinUvAuthToken(void) {
paut.permissions = 0;
paut.data = file_get_data(ef_authtoken);
paut.len = file_get_size(ef_authtoken);
fido_object_authorization_session_invalidate();
return 0;
}
@@ -204,6 +215,7 @@ int resetPersistentPinUvAuthToken(void) {
ppaut.permissions = 0;
ppaut.data = file_get_data(ef_pauthtoken);
ppaut.len = file_get_size(ef_pauthtoken);
fido_object_authorization_session_invalidate();
return 0;
}
+3
View File
@@ -37,6 +37,7 @@
#endif
#include <math.h>
#include "management.h"
#include "object_authorization.h"
#include "hid/ctap_hid.h"
#include "ctap2_cbor.h"
#include "credential.h"
@@ -102,6 +103,7 @@ INITIALIZER ( fido_ctor ) {
}
static int fido_unload(void) {
fido_object_authorization_session_invalidate();
return PICOKEYS_OK;
}
@@ -505,6 +507,7 @@ void scan_all(void) {
extern bool needs_power_cycle;
void init_fido(void) {
fido_object_authorization_session_invalidate();
scan_all();
credential_migrate_rp_secure();
#ifdef ENABLE_OTP_APP
+61
View File
@@ -0,0 +1,61 @@
/*
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
* Copyright (c) 2022 Pol Henarejos.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "picokeys.h"
#include "object_authorization.h"
#include "object_provider.h"
static uint32_t fido_object_session_epoch = 1;
void fido_object_authorization_session_invalidate(void) {
fido_object_session_epoch++;
if (fido_object_session_epoch == 0) {
fido_object_session_epoch = 1;
}
}
uint32_t fido_object_authorization_session_epoch(void) {
return fido_object_session_epoch;
}
int fido_object_authorization_context_build(const fido_object_authorization_evidence_t *evidence, bool internal_firmware, file_object_authorization_context_t *context) {
if (!context) {
return PICOKEYS_ERR_NULL_PARAM;
}
uint32_t facts = FILE_OBJECT_FACT_OWNING_APPLICATION | FILE_OBJECT_FACT_SESSION_BOUND;
if (internal_firmware) {
facts |= FILE_OBJECT_FACT_INTERNAL_FIRMWARE;
}
else if (evidence) {
if (evidence->user_presence) {
facts |= FILE_OBJECT_FACT_USER_PRESENCE;
}
if (evidence->user_verification) {
facts |= FILE_OBJECT_FACT_USER_VERIFICATION;
}
if (evidence->pin_uv_auth) {
facts |= FILE_OBJECT_FACT_APP_PIN | FILE_OBJECT_FACT_USER_VERIFICATION;
}
}
context->facts = facts;
context->session_epoch = fido_object_session_epoch;
context->facts_epoch = fido_object_session_epoch;
context->caller_namespace = FIDO_OBJECT_NAMESPACE;
return PICOKEYS_OK;
}
+34
View File
@@ -0,0 +1,34 @@
/*
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
* Copyright (c) 2022 Pol Henarejos.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _OBJECT_AUTHORIZATION_H_
#define _OBJECT_AUTHORIZATION_H_
#include "object_policy.h"
typedef struct fido_object_authorization_evidence {
bool user_presence;
bool user_verification;
bool pin_uv_auth;
} fido_object_authorization_evidence_t;
// Evidence must represent checks completed for the current CTAP operation, including token permission and RP binding.
int fido_object_authorization_context_build(const fido_object_authorization_evidence_t *evidence, bool internal_firmware, file_object_authorization_context_t *context);
void fido_object_authorization_session_invalidate(void);
uint32_t fido_object_authorization_session_epoch(void);
#endif // _OBJECT_AUTHORIZATION_H_
+97
View File
@@ -0,0 +1,97 @@
/*
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
* Copyright (c) 2022 Pol Henarejos.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "picokeys.h"
#include "object_authorization.h"
#include "object_provider.h"
#include <assert.h>
#include <stdio.h>
static void test_unauthenticated_context(void) {
file_object_authorization_context_t context;
assert(fido_object_authorization_context_build(NULL, false, &context) == PICOKEYS_OK);
assert(context.caller_namespace == FIDO_OBJECT_NAMESPACE);
assert(context.session_epoch != 0);
assert(context.facts_epoch == context.session_epoch);
assert(context.facts == (FILE_OBJECT_FACT_OWNING_APPLICATION | FILE_OBJECT_FACT_SESSION_BOUND));
}
static void test_operation_evidence(void) {
fido_object_authorization_evidence_t evidence = {
.user_presence = true,
.user_verification = true,
.pin_uv_auth = false
};
file_object_authorization_context_t context;
assert(fido_object_authorization_context_build(&evidence, false, &context) == PICOKEYS_OK);
assert((context.facts & FILE_OBJECT_FACT_USER_PRESENCE) != 0);
assert((context.facts & FILE_OBJECT_FACT_USER_VERIFICATION) != 0);
assert((context.facts & FILE_OBJECT_FACT_APP_PIN) == 0);
}
static void test_pin_uv_auth_context(void) {
fido_object_authorization_evidence_t evidence = {
.user_presence = false,
.user_verification = false,
.pin_uv_auth = true
};
file_object_authorization_context_t context;
assert(fido_object_authorization_context_build(&evidence, false, &context) == PICOKEYS_OK);
assert((context.facts & FILE_OBJECT_FACT_APP_PIN) != 0);
assert((context.facts & FILE_OBJECT_FACT_USER_VERIFICATION) != 0);
assert((context.facts & FILE_OBJECT_FACT_USER_PRESENCE) == 0);
}
static void test_internal_context(void) {
fido_object_authorization_evidence_t evidence = {
.user_presence = true,
.user_verification = true,
.pin_uv_auth = true
};
file_object_authorization_context_t context;
assert(fido_object_authorization_context_build(&evidence, true, &context) == PICOKEYS_OK);
assert(context.facts == (FILE_OBJECT_FACT_OWNING_APPLICATION | FILE_OBJECT_FACT_SESSION_BOUND | FILE_OBJECT_FACT_INTERNAL_FIRMWARE));
}
static void test_epoch_invalidation(void) {
file_object_authorization_context_t before;
file_object_authorization_context_t after;
assert(fido_object_authorization_context_build(NULL, false, &before) == PICOKEYS_OK);
assert(fido_object_authorization_session_epoch() == before.session_epoch);
fido_object_authorization_session_invalidate();
assert(fido_object_authorization_context_build(NULL, false, &after) == PICOKEYS_OK);
assert(after.session_epoch != before.session_epoch);
assert(fido_object_authorization_session_epoch() == after.session_epoch);
assert(after.facts_epoch == after.session_epoch);
}
int main(void) {
test_unauthenticated_context();
test_operation_evidence();
test_pin_uv_auth_context();
test_internal_context();
test_epoch_invalidation();
assert(fido_object_authorization_context_build(NULL, false, NULL) == PICOKEYS_ERR_NULL_PARAM);
puts("fido_object_authorization_test: OK");
return 0;
}