feat(neg-boot): tftf invalid rotpk test
Test to improve code coverage in scenario with corrupt rotpk in fip certificate
Change-Id: I995ab6d9f1488fda44e10ae9ef70c9056d780a89
Signed-off-by: Thaddeus Gonzalez-Serna <Thaddeus.Gonzalez-Serna@arm.com>
diff --git a/tftf/tests/neg_scenario_tests/neg_scenario_test_infra.c b/tftf/tests/neg_scenario_tests/neg_scenario_test_infra.c
new file mode 100644
index 0000000..e45a0b0
--- /dev/null
+++ b/tftf/tests/neg_scenario_tests/neg_scenario_test_infra.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "neg_scenario_test_infra.h"
+
+/*
+ Temporary variables to speed up the authentication parameters search. These
+ variables are assigned once during the integrity check and used any time an
+ authentication parameter is requested, so we do not have to parse the image
+ again
+ */
+
+
+int get_pubKey_from_cert(void *cert, size_t cert_len, void **returnPtr) {
+
+ int ret;
+ size_t len;
+ unsigned char *p, *end, *crt_end, *pk_end;
+ mbedtls_asn1_buf pk;
+
+ /*
+ * The unique ASN.1 DER encoding of [0] EXPLICIT INTEGER { v3(2} }.
+ */
+ const char v3[] = {
+ /* The outer CONTEXT SPECIFIC 0 tag */
+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC | 0,
+ /* The number bytes used to encode the inner INTEGER */
+ 3,
+ /* The tag of the inner INTEGER */
+ MBEDTLS_ASN1_INTEGER,
+ /* The number of bytes needed to represent 2 */
+ 1,
+ /* The actual value 2 */
+ 2,
+ };
+
+ p = (unsigned char *)cert;
+ len = cert_len;
+ crt_end = p + len;
+ end = crt_end;
+
+ /*
+ * Certificate ::= SEQUENCE {
+ * tbsCertificate TBSCertificate,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signatureValue BIT STRING }
+ */
+ ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
+ MBEDTLS_ASN1_SEQUENCE);
+ if ((ret != 0) || ((p + len) != end))
+ return -1;
+
+ /*
+ * TBSCertificate ::= SEQUENCE {
+ */
+ ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
+ MBEDTLS_ASN1_SEQUENCE);
+ if (ret != 0)
+ return -1;
+
+ end = p + len;
+
+ /*
+ * Version ::= [0] EXPLICIT INTEGER { v1(0), v2(1), v3(2) }
+ * -- only v3 accepted
+ */
+ if (((end - p) <= (ptrdiff_t)sizeof(v3)) ||
+ (memcmp(p, v3, sizeof(v3)) != 0)) {
+ return -1;
+ }
+ p += sizeof(v3);
+
+ /*
+ * CertificateSerialNumber ::= INTEGER
+ */
+ ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER);
+ if (ret != 0)
+ return -1;
+
+ p += len;
+
+ /*
+ * signature AlgorithmIdentifier
+ */
+
+ ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
+ MBEDTLS_ASN1_SEQUENCE);
+ if (ret != 0)
+ return -1;
+
+ p += len;
+
+ /*
+ * issuer Name
+ */
+ ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
+ MBEDTLS_ASN1_SEQUENCE);
+ if (ret != 0)
+ return -1;
+
+ p += len;
+
+ /*
+ * Validity ::= SEQUENCE {
+ * notBefore Time,
+ * notAfter Time }
+ *
+ */
+ ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
+ MBEDTLS_ASN1_SEQUENCE);
+ if (ret != 0)
+ return -1;
+
+ p += len;
+
+ /*
+ * subject Name
+ */
+ ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
+ MBEDTLS_ASN1_SEQUENCE);
+ if (ret != 0)
+ return -1;
+
+ p += len;
+
+ /*
+ * SubjectPublicKeyInfo
+ */
+ pk.p = p;
+ ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
+ MBEDTLS_ASN1_SEQUENCE);
+ if (ret != 0)
+ return -1;
+
+ pk_end = p + len;
+ pk.len = pk_end - pk.p;
+
+ *returnPtr = p;
+
+ return 0;
+}
diff --git a/tftf/tests/neg_scenario_tests/neg_scenario_test_infra.h b/tftf/tests/neg_scenario_tests/neg_scenario_test_infra.h
new file mode 100644
index 0000000..428f86b
--- /dev/null
+++ b/tftf/tests/neg_scenario_tests/neg_scenario_test_infra.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <string.h>
+#include <mbedtls/oid.h>
+#include <mbedtls/platform.h>
+#include <firmware_image_package.h>
+
+/*
+ cert:
+ cert_len:
+ returnPtr: ptr to pubKey in cert
+
+ return 0 upon success, -1 on fail
+*/
+int get_pubKey_from_cert(void *cert, size_t cert_len, void **returnPtr);
diff --git a/tftf/tests/neg_scenario_tests/test_invalid_rotpk.c b/tftf/tests/neg_scenario_tests/test_invalid_rotpk.c
new file mode 100644
index 0000000..9f465a0
--- /dev/null
+++ b/tftf/tests/neg_scenario_tests/test_invalid_rotpk.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <uuid.h>
+#include <io_storage.h>
+#include <platform.h>
+#include <platform_def.h>
+#include <psci.h>
+#include <smccc.h>
+#include <status.h>
+#include <tftf_lib.h>
+#include <uuid_utils.h>
+#include "neg_scenario_test_infra.h"
+
+#define CRYPTO_SUPPORT 1
+
+static fip_toc_entry_t *
+find_fiptoc_entry_t(const int fip_base, const uuid_t *uuid)
+{
+ fip_toc_entry_t *current_file =
+ (fip_toc_entry_t *) (fip_base + sizeof(fip_toc_header_t));
+
+ while (!is_uuid_null(&(current_file->uuid))) {
+ if (uuid_equal(&(current_file->uuid), uuid)){
+ return current_file;
+ }
+
+ current_file += 1;
+ };
+
+ return NULL;
+}
+
+test_result_t test_invalid_rotpk(void)
+{
+ smc_args args = { SMC_PSCI_SYSTEM_RESET };
+ smc_ret_values ret = (smc_ret_values){0};
+ const uuid_t trusted_cert = UUID_TRUSTED_KEY_CERT;
+
+ uintptr_t handle;
+ fip_toc_entry_t * cert;
+ size_t exp_len, len;
+ int address, rc;
+ void * paramOut = NULL;
+
+ if(tftf_is_rebooted() ){
+ /* ROTPK is tampered with and upon reboot tfa should not reach this point */
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Locate Trusted Key certificate memory address by using UUID */
+ cert = find_fiptoc_entry_t(PLAT_ARM_FIP_BASE, &trusted_cert);
+ if (cert == NULL){
+ return TEST_RESULT_FAIL;
+ }
+
+ address = (uintptr_t)cert->offset_address;
+ exp_len = cert->size;
+ if (exp_len == 0U){
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Runtime-sized buffer on stack */
+ uint8_t cert_buffer[exp_len];
+
+ /* Open NVM and Read certicate */
+ plat_get_nvm_handle(&handle);
+ if(handle < 0) {
+ return TEST_RESULT_FAIL;
+ }
+
+ rc = io_seek(handle, IO_SEEK_SET, address);
+ if (rc < 0){
+ return TEST_RESULT_FAIL;
+ }
+
+ rc = io_read(handle, (uintptr_t) &cert_buffer, exp_len, &len);
+ if (rc < 0 || len != exp_len){
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Parse certifacte to retrieve public key */
+ rc = get_pubKey_from_cert(&cert_buffer, len, ¶mOut);
+ if ( rc != 0){
+ return TEST_RESULT_FAIL;
+ }
+
+ /*
+ * Corrupt part of the certificate in storage.
+ * Simple overwrite: just clobber the first 32 bytes so parsing/verification fails.
+ */
+ {
+ uint8_t junk[32] = {0};
+
+ rc = io_seek(handle, IO_SEEK_SET, address);
+ if (rc < 0){
+ return TEST_RESULT_FAIL;
+ }
+
+ rc = io_write(handle, (uintptr_t)junk, sizeof(junk), &len);
+ if (rc < 0 || len != sizeof(junk)){
+ return TEST_RESULT_FAIL;
+ }
+ }
+
+ /* Reboot */
+ tftf_notify_reboot();
+ ret = tftf_smc(&args);
+
+ /* The PSCI SYSTEM_RESET call is not supposed to return */
+ tftf_testcase_printf("System didn't reboot properly (%d)\n",
+ (unsigned int)ret.ret0);
+
+ /* If this point is reached, reboot failed to trigger*/
+ return TEST_RESULT_FAIL;
+}
diff --git a/tftf/tests/tests-corrupt-rotpk.mk b/tftf/tests/tests-corrupt-rotpk.mk
new file mode 100644
index 0000000..bf25158
--- /dev/null
+++ b/tftf/tests/tests-corrupt-rotpk.mk
@@ -0,0 +1,14 @@
+#
+# Copyright (c) 2025, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+TESTS_SOURCES += $(addprefix tftf/tests/neg_scenario_tests/, \
+ test_invalid_rotpk.c \
+ neg_scenario_test_infra.c \
+)
+
+include lib/ext_mbedtls/mbedtls.mk
+
+
diff --git a/tftf/tests/tests-corrupt-rotpk.xml b/tftf/tests/tests-corrupt-rotpk.xml
new file mode 100644
index 0000000..354752b
--- /dev/null
+++ b/tftf/tests/tests-corrupt-rotpk.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (c) 2025, Arm Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-3-Clause
+-->
+
+<testsuites>
+ <testsuite name="neg scenario test-rotpk" description="Stress test for when ROTPK in fip cert is corrupted" >
+ <testcase name="invalid rotpk" function="test_invalid_rotpk" />
+ </testsuite>
+
+</testsuites>
+