From 5f4032fdfadd84e2516e1af0f5f2773a59f6cdf5 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Fri, 17 Jul 2026 22:14:38 +0200 Subject: [PATCH] Add bounded object-policy evaluation to the SDK Validate operations, authorization facts, namespaces and session epochs with default-deny tests Signed-off-by: Pol Henarejos --- picokeys_sdk_import.cmake | 1 + src/fs/object_policy.c | 114 ++++++++++++++++++++++++++++++++++++++ src/fs/object_policy.h | 71 ++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 src/fs/object_policy.c create mode 100644 src/fs/object_policy.h diff --git a/picokeys_sdk_import.cmake b/picokeys_sdk_import.cmake index ac47299..f94e774 100644 --- a/picokeys_sdk_import.cmake +++ b/picokeys_sdk_import.cmake @@ -318,6 +318,7 @@ list(APPEND PICOKEYS_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/fs/object_store.c ${CMAKE_CURRENT_LIST_DIR}/src/fs/object_store_txn.c ${CMAKE_CURRENT_LIST_DIR}/src/fs/object_container.c + ${CMAKE_CURRENT_LIST_DIR}/src/fs/object_policy.c ${CMAKE_CURRENT_LIST_DIR}/src/fs/object_crypto_provider.c ${CMAKE_CURRENT_LIST_DIR}/src/fs/flash.c ${CMAKE_CURRENT_LIST_DIR}/src/fs/low_flash.c diff --git a/src/fs/object_policy.c b/src/fs/object_policy.c new file mode 100644 index 0000000..e1aa7b8 --- /dev/null +++ b/src/fs/object_policy.c @@ -0,0 +1,114 @@ +/* + * This file is part of the Pico Keys SDK distribution (https://github.com/polhenarejos/pico-keys-sdk). + * 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_policy.h" + +#include + +#define FILE_OBJECT_POLICY_RULE_OPERATION_OFFSET 0u +#define FILE_OBJECT_POLICY_RULE_REQUIRED_FACTS_OFFSET 2u +#define FILE_OBJECT_POLICY_RULE_FORBIDDEN_FACTS_OFFSET 6u +#define FILE_OBJECT_POLICY_RULE_CALLER_NAMESPACE_OFFSET 10u +#define FILE_OBJECT_POLICY_RULE_FLAGS_OFFSET 12u + +static bool file_object_policy_rule_valid(const uint8_t *rule) { + uint16_t operations = get_uint16_be(rule + FILE_OBJECT_POLICY_RULE_OPERATION_OFFSET); + uint32_t required_facts = get_uint32_be(rule + FILE_OBJECT_POLICY_RULE_REQUIRED_FACTS_OFFSET); + uint32_t forbidden_facts = get_uint32_be(rule + FILE_OBJECT_POLICY_RULE_FORBIDDEN_FACTS_OFFSET); + uint16_t caller_namespace = get_uint16_be(rule + FILE_OBJECT_POLICY_RULE_CALLER_NAMESPACE_OFFSET); + uint16_t flags = get_uint16_be(rule + FILE_OBJECT_POLICY_RULE_FLAGS_OFFSET); + + if (operations == 0 || (operations & ~FILE_OBJECT_OPERATION_MASK) != 0) { + return false; + } + if (((required_facts | forbidden_facts) & ~FILE_OBJECT_FACT_MASK) != 0 || (required_facts & forbidden_facts) != 0) { + return false; + } + if (caller_namespace == 0 || (flags & ~FILE_OBJECT_POLICY_RULE_FLAG_MASK) != 0) { + return false; + } + return true; +} + +int file_object_policy_validate(const uint8_t *policy, size_t policy_size) { + if (!policy) { + return PICOKEYS_ERR_NULL_PARAM; + } + if (policy_size < FILE_OBJECT_POLICY_HEADER_SIZE || policy[0] != FILE_OBJECT_POLICY_FORMAT_VERSION || policy[1] > FILE_OBJECT_POLICY_MAX_RULES) { + return PICOKEYS_WRONG_DATA; + } + + size_t expected_size = FILE_OBJECT_POLICY_HEADER_SIZE + (size_t)policy[1] * FILE_OBJECT_POLICY_RULE_SIZE; + if (policy_size != expected_size) { + return PICOKEYS_WRONG_LENGTH; + } + + for (uint8_t i = 0; i < policy[1]; i++) { + const uint8_t *rule = policy + FILE_OBJECT_POLICY_HEADER_SIZE + (size_t)i * FILE_OBJECT_POLICY_RULE_SIZE; + if (!file_object_policy_rule_valid(rule)) { + return PICOKEYS_WRONG_DATA; + } + } + return PICOKEYS_OK; +} + +int file_object_policy_hash(const uint8_t *policy, size_t policy_size, uint8_t hash[FILE_OBJECT_POLICY_HASH_SIZE]) { + if (!hash) { + return PICOKEYS_ERR_NULL_PARAM; + } + memset(hash, 0, FILE_OBJECT_POLICY_HASH_SIZE); + + int r = file_object_policy_validate(policy, policy_size); + if (r != PICOKEYS_OK) { + return r; + } + + uint8_t full_hash[32]; + if (mbedtls_sha256(policy, policy_size, full_hash, 0) != 0) { + memset(full_hash, 0, sizeof(full_hash)); + return PICOKEYS_EXEC_ERROR; + } + memcpy(hash, full_hash, FILE_OBJECT_POLICY_HASH_SIZE); + memset(full_hash, 0, sizeof(full_hash)); + return PICOKEYS_OK; +} + +bool file_object_policy_authorize(const uint8_t *policy, size_t policy_size, uint16_t operation, const file_object_authorization_context_t *context) { + if (file_object_policy_validate(policy, policy_size) != PICOKEYS_OK || !context) { + return false; + } + if (operation == 0 || (operation & ~FILE_OBJECT_OPERATION_MASK) != 0 || (operation & (operation - 1u)) != 0) { + return false; + } + if (context->caller_namespace == 0 || context->caller_namespace == FILE_OBJECT_POLICY_NAMESPACE_ANY || context->session_epoch == 0 || context->session_epoch != context->facts_epoch || (context->facts & ~FILE_OBJECT_FACT_MASK) != 0) { + return false; + } + + for (uint8_t i = 0; i < policy[1]; i++) { + const uint8_t *rule = policy + FILE_OBJECT_POLICY_HEADER_SIZE + (size_t)i * FILE_OBJECT_POLICY_RULE_SIZE; + uint16_t operations = get_uint16_be(rule + FILE_OBJECT_POLICY_RULE_OPERATION_OFFSET); + uint32_t required_facts = get_uint32_be(rule + FILE_OBJECT_POLICY_RULE_REQUIRED_FACTS_OFFSET); + uint32_t forbidden_facts = get_uint32_be(rule + FILE_OBJECT_POLICY_RULE_FORBIDDEN_FACTS_OFFSET); + uint16_t caller_namespace = get_uint16_be(rule + FILE_OBJECT_POLICY_RULE_CALLER_NAMESPACE_OFFSET); + + if ((operations & operation) != 0 && (caller_namespace == FILE_OBJECT_POLICY_NAMESPACE_ANY || caller_namespace == context->caller_namespace) && (context->facts & required_facts) == required_facts && (context->facts & forbidden_facts) == 0) { + return true; + } + } + return false; +} diff --git a/src/fs/object_policy.h b/src/fs/object_policy.h new file mode 100644 index 0000000..8bf8fc3 --- /dev/null +++ b/src/fs/object_policy.h @@ -0,0 +1,71 @@ +/* + * This file is part of the Pico Keys SDK distribution (https://github.com/polhenarejos/pico-keys-sdk). + * 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_POLICY_H_ +#define _OBJECT_POLICY_H_ + +#include "object_container.h" + +#define FILE_OBJECT_POLICY_FORMAT_VERSION 1u +#define FILE_OBJECT_POLICY_HEADER_SIZE 2u +#define FILE_OBJECT_POLICY_RULE_SIZE 14u +#define FILE_OBJECT_POLICY_MAX_RULES 8u +#define FILE_OBJECT_POLICY_MAX_SIZE (FILE_OBJECT_POLICY_HEADER_SIZE + FILE_OBJECT_POLICY_MAX_RULES * FILE_OBJECT_POLICY_RULE_SIZE) +#define FILE_OBJECT_POLICY_NAMESPACE_ANY 0xffffu + +#define FILE_OBJECT_OPERATION_READ 0x0001u +#define FILE_OBJECT_OPERATION_ENUMERATE 0x0002u +#define FILE_OBJECT_OPERATION_USE 0x0004u +#define FILE_OBJECT_OPERATION_SIGN 0x0008u +#define FILE_OBJECT_OPERATION_DECRYPT 0x0010u +#define FILE_OBJECT_OPERATION_DERIVE 0x0020u +#define FILE_OBJECT_OPERATION_WRAP 0x0040u +#define FILE_OBJECT_OPERATION_UNWRAP 0x0080u +#define FILE_OBJECT_OPERATION_EXPORT 0x0100u +#define FILE_OBJECT_OPERATION_UPDATE 0x0200u +#define FILE_OBJECT_OPERATION_DELETE 0x0400u +#define FILE_OBJECT_OPERATION_CHANGE_POLICY 0x0800u +#define FILE_OBJECT_OPERATION_ADMIN_RECOVERY 0x1000u +#define FILE_OBJECT_OPERATION_MASK 0x1fffu + +#define FILE_OBJECT_FACT_USER_PRESENCE 0x00000001u +#define FILE_OBJECT_FACT_USER_VERIFICATION 0x00000002u +#define FILE_OBJECT_FACT_APP_PIN 0x00000004u +#define FILE_OBJECT_FACT_ADMIN 0x00000008u +#define FILE_OBJECT_FACT_SECURE_MESSAGING 0x00000010u +#define FILE_OBJECT_FACT_INTERNAL_FIRMWARE 0x00000020u +#define FILE_OBJECT_FACT_OWNING_APPLICATION 0x00000040u +#define FILE_OBJECT_FACT_PHYSICAL_CONFIRMATION 0x00000080u +#define FILE_OBJECT_FACT_DEVICE_STATE 0x00000100u +#define FILE_OBJECT_FACT_ONE_TIME_AUTHORIZATION 0x00000200u +#define FILE_OBJECT_FACT_SESSION_BOUND 0x00000400u +#define FILE_OBJECT_FACT_MASK 0x000007ffu + +#define FILE_OBJECT_POLICY_RULE_FLAG_MASK 0x0000u + +typedef struct file_object_authorization_context { + uint32_t facts; + uint32_t session_epoch; + uint32_t facts_epoch; + uint16_t caller_namespace; +} file_object_authorization_context_t; + +int file_object_policy_validate(const uint8_t *policy, size_t policy_size); +int file_object_policy_hash(const uint8_t *policy, size_t policy_size, uint8_t hash[FILE_OBJECT_POLICY_HASH_SIZE]); +bool file_object_policy_authorize(const uint8_t *policy, size_t policy_size, uint16_t operation, const file_object_authorization_context_t *context); + +#endif // _OBJECT_POLICY_H_