diff --git a/CMakeLists.txt b/CMakeLists.txt index 37002e7..1bd5ac3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/pico-keys-sdk b/pico-keys-sdk index 5e2c033..5f4032f 160000 --- a/pico-keys-sdk +++ b/pico-keys-sdk @@ -1 +1 @@ -Subproject commit 5e2c0336d1e9d6a5897dd2f15b7ca08c273a1eda +Subproject commit 5f4032fdfadd84e2516e1af0f5f2773a59f6cdf5 diff --git a/src/fido/cbor_client_pin.c b/src/fido/cbor_client_pin.c index 1459487..9a91a6c 100644 --- a/src/fido/cbor_client_pin.c +++ b/src/fido/cbor_client_pin.c @@ -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; } diff --git a/src/fido/fido.c b/src/fido/fido.c index 41808b4..d911b6d 100644 --- a/src/fido/fido.c +++ b/src/fido/fido.c @@ -37,6 +37,7 @@ #endif #include #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 diff --git a/src/fido/object_authorization.c b/src/fido/object_authorization.c new file mode 100644 index 0000000..8ac9e4f --- /dev/null +++ b/src/fido/object_authorization.c @@ -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 . + */ + +#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; +} diff --git a/src/fido/object_authorization.h b/src/fido/object_authorization.h new file mode 100644 index 0000000..54722f7 --- /dev/null +++ b/src/fido/object_authorization.h @@ -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 . + */ + +#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_ diff --git a/tests/fido_object_authorization_test.c b/tests/fido_object_authorization_test.c new file mode 100644 index 0000000..f8b40c2 --- /dev/null +++ b/tests/fido_object_authorization_test.c @@ -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 . + */ + +#include "picokeys.h" +#include "object_authorization.h" +#include "object_provider.h" + +#include +#include + +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; +}