From abafeccc88c7193f2674c1d5bfa92d875d461b89 Mon Sep 17 00:00:00 2001 From: Tuomas Salokanto Date: Thu, 23 Apr 2026 12:49:57 +0300 Subject: [PATCH] xtest: Add Application Secrets TA testsuite The suite covers seal/unseal round-trips at 1-byte and maximum plaintext sizes, randomized sealing (IV uniqueness), rejection of malformed and tampered ciphertext, and binding to the caller's login identity. Sealing overhead is measured at runtime via a one-byte probe seal instead of being hardcoded, so the max-plaintext subcase remains valid across changes to TA-side overhead. The same probe also detects TA absence: TEEC_ERROR_ITEM_NOT_FOUND from the probe skips the suite, any other probe error fails it. A subcase that seals under one uid and unseals under another uid is not included because that would require external orchestration outside this test case. The analogous gid subcase is present, but skipped at runtime when the caller is member of only one group. The login-method-mismatch subcase still verifies that login type is part of the binding. Signed-off-by: Tuomas Salokanto --- host/xtest/CMakeLists.txt | 9 + host/xtest/Makefile | 11 + host/xtest/asteec_1000.c | 434 ++++++++++++++++++++++++++++++++++++++ host/xtest/xtest_main.c | 18 +- host/xtest/xtest_test.h | 3 + 5 files changed, 474 insertions(+), 1 deletion(-) create mode 100644 host/xtest/asteec_1000.c diff --git a/host/xtest/CMakeLists.txt b/host/xtest/CMakeLists.txt index da193bdc3..f8325c733 100644 --- a/host/xtest/CMakeLists.txt +++ b/host/xtest/CMakeLists.txt @@ -111,6 +111,11 @@ if (CFG_PKCS11_TA) list (APPEND SRC pkcs11_1000.c) endif() +if (CFG_APP_SECRETS_TA) + add_compile_options(-DCFG_APP_SECRETS_TA) + list (APPEND SRC asteec_1000.c) +endif() + if (CFG_CRYPTO_SE05X) add_compile_options(-DCFG_CRYPTO_SE05X) endif() @@ -141,6 +146,10 @@ target_link_libraries (${PROJECT_NAME} PRIVATE ckteec ) +if (CFG_APP_SECRETS_TA) + target_link_libraries (${PROJECT_NAME} PRIVATE asteec) +endif() + ################################################################################ # Install targets ################################################################################ diff --git a/host/xtest/Makefile b/host/xtest/Makefile index 18ffb7b85..414f2a7c8 100644 --- a/host/xtest/Makefile +++ b/host/xtest/Makefile @@ -92,12 +92,20 @@ ifeq ($(CFG_PKCS11_TA),y) srcs += pkcs11_1000.c endif +ifeq ($(CFG_APP_SECRETS_TA),y) +srcs += asteec_1000.c +endif + objs := $(patsubst %.c,$(out-dir)/xtest/%.o, $(srcs)) ifeq ($(CFG_PKCS11_TA),y) CFLAGS += -DCFG_PKCS11_TA endif +ifeq ($(CFG_APP_SECRETS_TA),y) +CFLAGS += -DCFG_APP_SECRETS_TA +endif + ifeq ($(CFG_CRYPTO_SE05X),y) CFLAGS += -DCFG_CRYPTO_SE05X endif @@ -161,6 +169,9 @@ LDFLAGS += -L$(OPTEE_CLIENT_EXPORT)/lib -lteec ifeq ($(CFG_PKCS11_TA),y) LDFLAGS += -lckteec endif +ifeq ($(CFG_APP_SECRETS_TA),y) +LDFLAGS += -lasteec +endif LDFLAGS += -lpthread -lm .PHONY: all diff --git a/host/xtest/asteec_1000.c b/host/xtest/asteec_1000.c new file mode 100644 index 000000000..ab9ff7a42 --- /dev/null +++ b/host/xtest/asteec_1000.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Vaisala Oyj. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xtest_helpers.h" +#include "xtest_test.h" + +/* + * Must match MAX_BUF_SIZE defined in optee-os + * ta/app_secrets/app_secrets_ta.c. + */ +#define ASTEEC_TA_MAX_BUF_SIZE 4096 + +static TEEC_Result seal(uint32_t login_method, gid_t login_gid, + const void *plain, size_t plain_len, + uint8_t **sealed, size_t *sealed_len) +{ + TEEC_Result res = TEEC_ERROR_GENERIC; + + *sealed = NULL; + *sealed_len = 0; + + res = asteec_seal(login_method, login_gid, plain, plain_len, NULL, + sealed_len); + if (res != TEEC_ERROR_SHORT_BUFFER) + return res; + + *sealed = malloc(*sealed_len); + if (!*sealed) + return TEEC_ERROR_OUT_OF_MEMORY; + + res = asteec_seal(login_method, login_gid, plain, plain_len, *sealed, + sealed_len); + if (res != TEEC_SUCCESS) { + free(*sealed); + *sealed = NULL; + } + return res; +} + +static TEEC_Result unseal(uint32_t login_method, gid_t login_gid, + const void *sealed, size_t sealed_len, + uint8_t **plain, size_t *plain_len) +{ + TEEC_Result res = TEEC_ERROR_GENERIC; + + *plain = NULL; + *plain_len = 0; + + res = asteec_unseal(login_method, login_gid, sealed, sealed_len, + NULL, plain_len); + if (res != TEEC_ERROR_SHORT_BUFFER) + return res; + + *plain = malloc(*plain_len); + if (!*plain) + return TEEC_ERROR_OUT_OF_MEMORY; + + res = asteec_unseal(login_method, login_gid, sealed, sealed_len, *plain, + plain_len); + if (res != TEEC_SUCCESS) { + free(*plain); + *plain = NULL; + } + return res; +} + +static TEEC_Result probe_sealing_overhead(ADBG_Case_t *c, size_t *overhead) +{ + uint8_t probe_byte = 0; + size_t probe_len = 0; + TEEC_Result res = TEEC_ERROR_GENERIC; + + res = asteec_seal(TEEC_LOGIN_PUBLIC, 0, &probe_byte, sizeof(probe_byte), + NULL, &probe_len); + if (res == TEEC_ERROR_ITEM_NOT_FOUND) + return res; + if (!ADBG_EXPECT_TEEC_RESULT(c, TEEC_ERROR_SHORT_BUFFER, res)) + return res; + if (!ADBG_EXPECT_COMPARE_UNSIGNED(c, probe_len, >, + sizeof(probe_byte)) || + !ADBG_EXPECT_COMPARE_UNSIGNED(c, probe_len, <=, + ASTEEC_TA_MAX_BUF_SIZE)) + return TEEC_ERROR_GENERIC; + + *overhead = probe_len - sizeof(probe_byte); + return TEEC_SUCCESS; +} + +static void test_unseal_with_different_group(ADBG_Case_t *c, uint8_t *plain, + size_t plain_len) +{ + gid_t cur_gid = getegid(); + gid_t other_gid = 0; + gid_t *groups = NULL; + uint8_t *sealed = NULL; + uint8_t *unsealed = NULL; + size_t sealed_len = 0; + size_t unsealed_len = 0; + int ngroups = 0; + int i = 0; + bool found_other = false; + TEEC_Result res = TEEC_ERROR_GENERIC; + + ngroups = getgroups(0, NULL); + if (!ADBG_EXPECT_COMPARE_SIGNED(c, ngroups, >=, 0)) + goto out; + if (ngroups > 0) { + groups = calloc(ngroups, sizeof(*groups)); + if (!ADBG_EXPECT_NOT_NULL(c, groups)) + goto out; + ngroups = getgroups(ngroups, groups); + if (!ADBG_EXPECT_COMPARE_SIGNED(c, ngroups, >=, 0)) + goto out; + } + for (i = 0; i < ngroups; i++) { + if (groups[i] != cur_gid) { + other_gid = groups[i]; + found_other = true; + break; + } + } + if (!found_other) { + Do_ADBG_Log("Skipping group-mismatch coverage: caller is in only one group"); + goto out; + } + + memset(plain, 0xCC, plain_len); + + res = seal(TEEC_LOGIN_GROUP, cur_gid, plain, plain_len, + &sealed, &sealed_len); + if (ADBG_EXPECT_TEEC_SUCCESS(c, res)) + ADBG_EXPECT_TEEC_RESULT(c, TEEC_ERROR_SECURITY, + unseal(TEEC_LOGIN_GROUP, other_gid, + sealed, sealed_len, + &unsealed, &unsealed_len)); +out: + free(groups); + free(sealed); + free(unsealed); +} + +static void xtest_asteec_test_1000(ADBG_Case_t *c) +{ + TEEC_Result res = TEEC_ERROR_GENERIC; + uint8_t plain[32] = { }; + size_t sealing_overhead = 0; + size_t max_plain_len = 0; + + res = probe_sealing_overhead(c, &sealing_overhead); + if (res == TEEC_ERROR_ITEM_NOT_FOUND) { + Do_ADBG_Log("skip test, Application Secrets TA not found"); + return; + } + if (res != TEEC_SUCCESS) + return; + + max_plain_len = ASTEEC_TA_MAX_BUF_SIZE - sealing_overhead; + + Do_ADBG_BeginSubCase(c, "Round-trip with 1-byte plaintext"); + { + uint8_t one_byte = 0xCC; + uint8_t *sealed = NULL; + uint8_t *unsealed = NULL; + size_t sealed_len = 0; + size_t unsealed_len = 0; + + res = seal(TEEC_LOGIN_PUBLIC, 0, &one_byte, 1, + &sealed, &sealed_len); + if (ADBG_EXPECT_TEEC_SUCCESS(c, res)) { + ADBG_EXPECT_COMPARE_UNSIGNED(c, sealed_len, ==, + 1 + sealing_overhead); + res = unseal(TEEC_LOGIN_PUBLIC, 0, sealed, sealed_len, + &unsealed, &unsealed_len); + if (ADBG_EXPECT_TEEC_SUCCESS(c, res)) + ADBG_EXPECT_BUFFER(c, &one_byte, 1, + unsealed, unsealed_len); + } + + free(sealed); + free(unsealed); + } + Do_ADBG_EndSubCase(c, "Round-trip with 1-byte plaintext"); + + Do_ADBG_BeginSubCase(c, "Round-trip with maximum-size plaintext"); + { + uint8_t *max_plain = malloc(max_plain_len); + uint8_t *sealed = NULL; + uint8_t *unsealed = NULL; + size_t sealed_len = 0; + size_t unsealed_len = 0; + + if (ADBG_EXPECT_NOT_NULL(c, max_plain)) { + memset(max_plain, 0xCC, max_plain_len); + + res = seal(TEEC_LOGIN_PUBLIC, 0, max_plain, + max_plain_len, &sealed, &sealed_len); + if (ADBG_EXPECT_TEEC_SUCCESS(c, res)) { + ADBG_EXPECT_COMPARE_UNSIGNED(c, sealed_len, ==, + ASTEEC_TA_MAX_BUF_SIZE); + res = unseal(TEEC_LOGIN_PUBLIC, 0, sealed, + sealed_len, &unsealed, + &unsealed_len); + if (ADBG_EXPECT_TEEC_SUCCESS(c, res)) + ADBG_EXPECT_BUFFER(c, max_plain, + max_plain_len, + unsealed, + unsealed_len); + } + } + + free(max_plain); + free(sealed); + free(unsealed); + } + Do_ADBG_EndSubCase(c, "Round-trip with maximum-size plaintext"); + + Do_ADBG_BeginSubCase(c, "Zero-length plaintext rejected"); + { + uint8_t dummy = 0; + uint8_t *sealed = NULL; + size_t sealed_len = 0; + + ADBG_EXPECT_TEEC_RESULT(c, TEEC_ERROR_BAD_PARAMETERS, + seal(TEEC_LOGIN_PUBLIC, 0, &dummy, 0, + &sealed, &sealed_len)); + + free(sealed); + } + Do_ADBG_EndSubCase(c, "Zero-length plaintext rejected"); + + Do_ADBG_BeginSubCase(c, "Plaintext over maximum size rejected"); + { + const size_t over_len = max_plain_len + 1; + uint8_t *over = malloc(over_len); + uint8_t *sealed = NULL; + size_t sealed_len = 0; + + if (ADBG_EXPECT_NOT_NULL(c, over)) { + memset(over, 0xCC, over_len); + ADBG_EXPECT_TEEC_RESULT(c, TEEC_ERROR_BAD_PARAMETERS, + seal(TEEC_LOGIN_PUBLIC, 0, + over, over_len, &sealed, + &sealed_len)); + } + + free(over); + free(sealed); + } + Do_ADBG_EndSubCase(c, "Plaintext over maximum size rejected"); + + Do_ADBG_BeginSubCase(c, + "Same plaintext sealed twice yields different sealed output"); + { + uint8_t *sealed1 = NULL; + uint8_t *sealed2 = NULL; + size_t sealed1_len = 0; + size_t sealed2_len = 0; + + memset(plain, 0xCC, sizeof(plain)); + + res = seal(TEEC_LOGIN_PUBLIC, 0, plain, sizeof(plain), + &sealed1, &sealed1_len); + if (ADBG_EXPECT_TEEC_SUCCESS(c, res)) { + res = seal(TEEC_LOGIN_PUBLIC, 0, plain, sizeof(plain), + &sealed2, &sealed2_len); + if (ADBG_EXPECT_TEEC_SUCCESS(c, res) && + ADBG_EXPECT_COMPARE_UNSIGNED(c, sealed1_len, ==, + sealed2_len)) { + ADBG_EXPECT_COMPARE_SIGNED(c, + memcmp(sealed1, sealed2, sealed1_len), + !=, 0); + } + } + + free(sealed1); + free(sealed2); + } + Do_ADBG_EndSubCase(c, + "Same plaintext sealed twice yields different sealed output"); + + Do_ADBG_BeginSubCase(c, "Byte flip in sealed blob rejected"); + { + uint8_t *sealed = NULL; + uint8_t *unsealed = NULL; + size_t sealed_len = 0; + size_t unsealed_len = 0; + + memset(plain, 0xCC, sizeof(plain)); + + res = seal(TEEC_LOGIN_PUBLIC, 0, plain, sizeof(plain), + &sealed, &sealed_len); + if (ADBG_EXPECT_TEEC_SUCCESS(c, res) && + ADBG_EXPECT_COMPARE_UNSIGNED(c, sealed_len, >, 0)) { + sealed[sealed_len / 2] ^= 0xff; + ADBG_EXPECT_TEEC_RESULT(c, TEEC_ERROR_SECURITY, + unseal(TEEC_LOGIN_PUBLIC, 0, + sealed, sealed_len, + &unsealed, + &unsealed_len)); + } + + free(sealed); + free(unsealed); + } + Do_ADBG_EndSubCase(c, "Byte flip in sealed blob rejected"); + + Do_ADBG_BeginSubCase(c, "Truncated sealed blob rejected"); + { + uint8_t *sealed = NULL; + uint8_t *unsealed = NULL; + size_t sealed_len = 0; + size_t unsealed_len = 0; + + memset(plain, 0xCC, sizeof(plain)); + + res = seal(TEEC_LOGIN_PUBLIC, 0, plain, sizeof(plain), + &sealed, &sealed_len); + if (ADBG_EXPECT_TEEC_SUCCESS(c, res) && + ADBG_EXPECT_COMPARE_UNSIGNED(c, sealed_len, >, 0)) + ADBG_EXPECT_TEEC_RESULT(c, TEEC_ERROR_SECURITY, + unseal(TEEC_LOGIN_PUBLIC, 0, + sealed, sealed_len - 1, + &unsealed, + &unsealed_len)); + + free(sealed); + free(unsealed); + } + Do_ADBG_EndSubCase(c, "Truncated sealed blob rejected"); + + Do_ADBG_BeginSubCase(c, "Undersized sealed blob rejected"); + { + uint8_t junk[16] = { }; + uint8_t *unsealed = NULL; + size_t unsealed_len = 0; + + ADBG_EXPECT_TEEC_RESULT(c, TEEC_ERROR_BAD_PARAMETERS, + unseal(TEEC_LOGIN_PUBLIC, 0, junk, + sizeof(junk), &unsealed, + &unsealed_len)); + free(unsealed); + } + Do_ADBG_EndSubCase(c, "Undersized sealed blob rejected"); + + Do_ADBG_BeginSubCase(c, "Round-trip with user login"); + { + uint8_t *sealed = NULL; + uint8_t *unsealed = NULL; + size_t sealed_len = 0; + size_t unsealed_len = 0; + + memset(plain, 0xCC, sizeof(plain)); + + res = seal(TEEC_LOGIN_USER, 0, plain, sizeof(plain), + &sealed, &sealed_len); + if (ADBG_EXPECT_TEEC_SUCCESS(c, res)) { + res = unseal(TEEC_LOGIN_USER, 0, sealed, sealed_len, + &unsealed, &unsealed_len); + if (ADBG_EXPECT_TEEC_SUCCESS(c, res)) + ADBG_EXPECT_BUFFER(c, plain, sizeof(plain), + unsealed, unsealed_len); + } + + free(sealed); + free(unsealed); + } + Do_ADBG_EndSubCase(c, "Round-trip with user login"); + + Do_ADBG_BeginSubCase(c, "Round-trip with group login"); + { + uint8_t *sealed = NULL; + uint8_t *unsealed = NULL; + size_t sealed_len = 0; + size_t unsealed_len = 0; + gid_t cur_gid = getegid(); + + memset(plain, 0xCC, sizeof(plain)); + + res = seal(TEEC_LOGIN_GROUP, cur_gid, plain, sizeof(plain), + &sealed, &sealed_len); + if (ADBG_EXPECT_TEEC_SUCCESS(c, res)) { + res = unseal(TEEC_LOGIN_GROUP, cur_gid, sealed, + sealed_len, &unsealed, &unsealed_len); + if (ADBG_EXPECT_TEEC_SUCCESS(c, res)) + ADBG_EXPECT_BUFFER(c, plain, sizeof(plain), + unsealed, unsealed_len); + } + + free(sealed); + free(unsealed); + } + Do_ADBG_EndSubCase(c, "Round-trip with group login"); + + Do_ADBG_BeginSubCase(c, "Unseal with different login type rejected"); + { + uint8_t *sealed = NULL; + uint8_t *unsealed = NULL; + size_t sealed_len = 0; + size_t unsealed_len = 0; + + memset(plain, 0xCC, sizeof(plain)); + + res = seal(TEEC_LOGIN_USER, 0, plain, sizeof(plain), + &sealed, &sealed_len); + if (ADBG_EXPECT_TEEC_SUCCESS(c, res)) + ADBG_EXPECT_TEEC_RESULT(c, TEEC_ERROR_SECURITY, + unseal(TEEC_LOGIN_PUBLIC, 0, + sealed, sealed_len, + &unsealed, + &unsealed_len)); + + free(sealed); + free(unsealed); + } + Do_ADBG_EndSubCase(c, "Unseal with different login type rejected"); + + Do_ADBG_BeginSubCase(c, "Unseal with different group rejected"); + test_unseal_with_different_group(c, plain, sizeof(plain)); + Do_ADBG_EndSubCase(c, "Unseal with different group rejected"); +} + +ADBG_CASE_DEFINE(asteec, 1000, xtest_asteec_test_1000, + "Test Application Secrets TA via libasteec"); diff --git a/host/xtest/xtest_main.c b/host/xtest/xtest_main.c index f0fdaacdc..68203e025 100644 --- a/host/xtest/xtest_main.c +++ b/host/xtest/xtest_main.c @@ -41,6 +41,9 @@ ADBG_SUITE_DEFINE(pkcs11); ADBG_SUITE_DEFINE(ffa_spmc); #endif ADBG_SUITE_DEFINE(regression); +#ifdef CFG_APP_SECRETS_TA +ADBG_SUITE_DEFINE(asteec); +#endif char *xtest_progname; char *xtest_tee_name = NULL; @@ -65,7 +68,13 @@ static const char glevel[] = "0"; #define FFA_SPMC_SUITE "" #endif -static char gsuitename[] = "regression" GP_SUITE PKCS11_SUITE FFA_SPMC_SUITE; +#ifdef CFG_APP_SECRETS_TA +#define ASTEEC_SUITE "+asteec" +#else +#define ASTEEC_SUITE "" +#endif + +static char gsuitename[] = "regression" GP_SUITE PKCS11_SUITE FFA_SPMC_SUITE ASTEEC_SUITE; void usage(char *program); @@ -86,6 +95,9 @@ void usage(char *program) #endif #ifdef CFG_SPMC_TESTS printf(" ffa_spmc"); +#endif +#ifdef CFG_APP_SECRETS_TA + printf(" asteec"); #endif printf("\n"); printf("\t To run several suites, use multiple names\n"); @@ -267,6 +279,10 @@ int main(int argc, char *argv[]) #ifdef CFG_SPMC_TESTS else if (!strcmp(token, "ffa_spmc")) ret = Do_ADBG_AppendToSuite(&all, &ADBG_Suite_ffa_spmc); +#endif +#ifdef CFG_APP_SECRETS_TA + else if (!strcmp(token, "asteec")) + ret = Do_ADBG_AppendToSuite(&all, &ADBG_Suite_asteec); #endif else { fprintf(stderr, "Unknown test suite: %s\n", token); diff --git a/host/xtest/xtest_test.h b/host/xtest/xtest_test.h index 0b21ef1cf..45fbd1c22 100644 --- a/host/xtest/xtest_test.h +++ b/host/xtest/xtest_test.h @@ -20,6 +20,9 @@ ADBG_SUITE_DECLARE(gp); #ifdef CFG_PKCS11_TA ADBG_SUITE_DECLARE(pkcs11); #endif +#ifdef CFG_APP_SECRETS_TA +ADBG_SUITE_DECLARE(asteec); +#endif #ifdef CFG_SPMC_TESTS ADBG_SUITE_DECLARE(ffa_spmc); #endif