/* SPDX-License-Identifier: GPL-3.0-only */ /* Bounded persistent role-based user and SSH authorized-key database. */ #pragma once #include #include #include #include "esp_err.h" #ifdef __cplusplus extern "C" { #endif #define USER_DATABASE_NVS_NAMESPACE "user_db" #define USER_DATABASE_NVS_BLOB_KEY "database" #define USER_DATABASE_MAX_USERS 8U #define USER_DATABASE_MAX_SSH_KEYS_PER_USER 3U #define USER_DATABASE_USERNAME_CAPACITY 16U #define USER_DATABASE_PASSWORD_CAPACITY 64U #define USER_DATABASE_PASSWORD_MIN_LENGTH 12U #define USER_DATABASE_GENERATED_PASSWORD_LENGTH 24U #define USER_DATABASE_SSH_KEY_TYPE_CAPACITY 32U #define USER_DATABASE_SSH_KEY_BLOB_CAPACITY 128U #define USER_DATABASE_SHA256_LENGTH 32U #define USER_DATABASE_ERR_DUPLICATE_SSH_KEY ESP_ERR_NOT_ALLOWED typedef enum { USER_ROLE_USER = 1, USER_ROLE_ADMIN = 2, } user_role_t; typedef enum { USER_AUTH_METHOD_PASSWORD = 1, USER_AUTH_METHOD_SSH_PUBLIC_KEY = 2, } user_auth_method_t; typedef enum { USER_DATABASE_LOAD_STORED = 0, USER_DATABASE_LOAD_MIGRATED_LEGACY, USER_DATABASE_LOAD_EMPTY, } user_database_load_result_t; typedef struct { const uint8_t *username; size_t username_length; const uint8_t *password; size_t password_length; } user_database_legacy_credentials_t; typedef struct { uint32_t user_id; uint32_t auth_generation; user_role_t role; user_auth_method_t method; size_t username_length; char username[USER_DATABASE_USERNAME_CAPACITY + 1U]; } user_principal_t; typedef struct { size_t password_length; uint8_t password[USER_DATABASE_PASSWORD_CAPACITY + 1U]; } user_database_generated_password_t; typedef struct { bool active; uint8_t index; size_t key_type_length; char key_type[USER_DATABASE_SSH_KEY_TYPE_CAPACITY + 1U]; uint8_t sha256_fingerprint[USER_DATABASE_SHA256_LENGTH]; } user_database_key_snapshot_t; typedef struct { bool active; uint32_t user_id; uint32_t auth_generation; user_role_t role; size_t username_length; char username[USER_DATABASE_USERNAME_CAPACITY + 1U]; uint8_t public_key_count; user_database_key_snapshot_t public_keys[USER_DATABASE_MAX_SSH_KEYS_PER_USER]; } user_database_user_snapshot_t; typedef struct { bool initialized; bool admin_bootstrapped; uint32_t generation; uint8_t user_count; uint8_t admin_count; user_database_user_snapshot_t users[USER_DATABASE_MAX_USERS]; } user_database_snapshot_t; esp_err_t user_database_init(const user_database_legacy_credentials_t *legacy, user_database_load_result_t *load_result); /* * Before the first administrator is established, keep the migrated account in * sync with the legacy recovery credential. Once bootstrapped, that credential * remains independent and no longer authenticates Phase 8B network services. */ esp_err_t user_database_sync_legacy_credentials( const user_database_legacy_credentials_t *legacy, bool *synchronized); /* Explicit UART0 recovery: replace unavailable user storage with one legacy user. */ esp_err_t user_database_recover_from_legacy( const user_database_legacy_credentials_t *legacy); esp_err_t user_database_get_snapshot(user_database_snapshot_t *snapshot); esp_err_t user_database_authenticate_password( const uint8_t *username, size_t username_length, const uint8_t *password, size_t password_length, user_principal_t *principal, bool *authenticated); esp_err_t user_database_authorize_ssh_public_key( const uint8_t *username, size_t username_length, const uint8_t *key_type, size_t key_type_length, const uint8_t *key_blob, size_t key_blob_length, user_principal_t *principal, bool *authorized); esp_err_t user_database_principal_is_current(const user_principal_t *principal, bool *current); esp_err_t user_database_bootstrap_admin(const uint8_t *password, size_t password_length); esp_err_t user_database_bootstrap_admin_generated( user_database_generated_password_t *generated_password); esp_err_t user_database_create(const uint8_t *username, size_t username_length, user_role_t role, const uint8_t *password, size_t password_length); esp_err_t user_database_create_generated( const uint8_t *username, size_t username_length, user_role_t role, user_database_generated_password_t *generated_password); esp_err_t user_database_delete(const uint8_t *username, size_t username_length); esp_err_t user_database_set_role(const uint8_t *username, size_t username_length, user_role_t role); esp_err_t user_database_set_password(const uint8_t *username, size_t username_length, const uint8_t *password, size_t password_length); esp_err_t user_database_generate_password( const uint8_t *username, size_t username_length, user_database_generated_password_t *generated_password); esp_err_t user_database_add_ssh_key( const uint8_t *username, size_t username_length, const uint8_t *key_type, size_t key_type_length, const uint8_t *key_blob, size_t key_blob_length, uint8_t *key_index); esp_err_t user_database_remove_ssh_key(const uint8_t *username, size_t username_length, uint8_t key_index); esp_err_t user_database_clear_ssh_keys(const uint8_t *username, size_t username_length); bool user_database_username_valid(const uint8_t *username, size_t length); bool user_database_password_valid(const uint8_t *password, size_t length); bool user_database_key_valid(const uint8_t *key_type, size_t key_type_length, const uint8_t *key_blob, size_t key_blob_length); const char *user_role_to_string(user_role_t role); bool user_role_parse(const char *text, user_role_t *role); #ifdef __cplusplus } #endif