diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml
index 41b34476174..422d849ea79 100644
--- a/MAINTAINERS.yml
+++ b/MAINTAINERS.yml
@@ -4113,6 +4113,7 @@ Secure storage:
- tomi-font
files:
- subsys/secure_storage/
+ - samples/psa/
- tests/subsys/secure_storage/
labels:
- "area: Secure storage"
diff --git a/samples/psa/index.rst b/samples/psa/index.rst
new file mode 100644
index 00000000000..fd5f3386eb3
--- /dev/null
+++ b/samples/psa/index.rst
@@ -0,0 +1,6 @@
+.. zephyr:code-sample-category:: psa
+ :name: PSA
+ :show-listing:
+
+ The following samples demonstrate various uses of several of the
+ `Platform Security Architecture (PSA) Certified APIs `_.
diff --git a/samples/psa/its/CMakeLists.txt b/samples/psa/its/CMakeLists.txt
new file mode 100644
index 00000000000..fbac539c4f0
--- /dev/null
+++ b/samples/psa/its/CMakeLists.txt
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: Apache-2.0
+cmake_minimum_required(VERSION 3.20.0)
+find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
+
+project(psa_its)
+
+target_sources(app PRIVATE src/main.c)
diff --git a/samples/psa/its/README.rst b/samples/psa/its/README.rst
new file mode 100644
index 00000000000..94bff4f47d9
--- /dev/null
+++ b/samples/psa/its/README.rst
@@ -0,0 +1,67 @@
+.. zephyr:code-sample:: psa_its
+ :name: PSA Internal Trusted Storage API
+ :relevant-api: psa_secure_storage
+
+ Use the PSA ITS API.
+
+Overview
+********
+
+This sample demonstrates usage of the
+`PSA Internal Trusted Storage (ITS) API `_,
+which is part of the `PSA Secure Storage API `_,
+for storing and retrieving persistent data.
+
+Requirements
+************
+
+An implementation of the PSA ITS API must be present for this sample to build.
+It can be provided by:
+
+* :ref:`tfm`, for ``*/ns`` :term:`board targets`.
+* The :ref:`secure storage subsystem `, for the other board targets.
+
+Building
+********
+
+This sample is located in :zephyr_file:`samples/psa/its`.
+
+Different configurations are defined in the :file:`sample.yaml` file.
+You can use them to build the sample, depending on the PSA ITS provider, as follows:
+
+.. tabs::
+
+ .. tab:: TF-M
+
+ For board targets with TF-M:
+
+ .. zephyr-app-commands::
+ :zephyr-app: samples/psa/its
+ :tool: west
+ :goals: build
+ :board:
+ :west-args: -T sample.psa.its.tfm
+
+ .. tab:: secure storage subsystem
+
+ For board targets without TF-M.
+
+ If the board target to compile for has an entropy driver (preferable):
+
+ .. zephyr-app-commands::
+ :zephyr-app: samples/psa/its
+ :tool: west
+ :goals: build
+ :board:
+ :west-args: -T sample.psa.its.secure_storage.entropy_driver
+
+ Or, to use an insecure entropy source (only for testing):
+
+ .. zephyr-app-commands::
+ :zephyr-app: samples/psa/its
+ :tool: west
+ :goals: build
+ :board:
+ :west-args: -T sample.psa.its.secure_storage.entropy_not_secure
+
+To flash it, see :ref:`west-flashing`.
diff --git a/samples/psa/its/overlay-entropy_driver.conf b/samples/psa/its/overlay-entropy_driver.conf
new file mode 100644
index 00000000000..b2fea61e044
--- /dev/null
+++ b/samples/psa/its/overlay-entropy_driver.conf
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: Apache-2.0
+
+CONFIG_ENTROPY_GENERATOR=y
+CONFIG_MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG=y
diff --git a/samples/psa/its/overlay-entropy_not_secure.conf b/samples/psa/its/overlay-entropy_not_secure.conf
new file mode 100644
index 00000000000..2aba3a2c7e2
--- /dev/null
+++ b/samples/psa/its/overlay-entropy_not_secure.conf
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: Apache-2.0
+
+CONFIG_TEST_RANDOM_GENERATOR=y
+CONFIG_TIMER_RANDOM_GENERATOR=y
+CONFIG_MBEDTLS_ENTROPY_POLL_ZEPHYR=y
diff --git a/samples/psa/its/overlay-secure_storage.conf b/samples/psa/its/overlay-secure_storage.conf
new file mode 100644
index 00000000000..3473ae38910
--- /dev/null
+++ b/samples/psa/its/overlay-secure_storage.conf
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: Apache-2.0
+
+CONFIG_MBEDTLS=y
+CONFIG_MBEDTLS_PSA_CRYPTO_C=y
+
+# The default stack size (1024) is not enough for the PSA Crypto core.
+# On top of that, the ITS implementation uses the stack for buffers.
+CONFIG_MAIN_STACK_SIZE=3072
+
+CONFIG_SECURE_STORAGE=y
diff --git a/samples/psa/its/prj.conf b/samples/psa/its/prj.conf
new file mode 100644
index 00000000000..4c214a79a52
--- /dev/null
+++ b/samples/psa/its/prj.conf
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: Apache-2.0
+
+CONFIG_LOG=y
+CONFIG_LOG_DEFAULT_LEVEL=3
diff --git a/samples/psa/its/sample.yaml b/samples/psa/its/sample.yaml
new file mode 100644
index 00000000000..c4ee3543696
--- /dev/null
+++ b/samples/psa/its/sample.yaml
@@ -0,0 +1,31 @@
+sample:
+ name: PSA ITS API sample
+ description: Demonstration of PSA Internal Trusted Storage (ITS) API usage.
+common:
+ tags:
+ - psa.secure_storage
+ timeout: 10
+ harness: console
+ harness_config:
+ type: one_line
+ regex:
+ - "Sample finished successfully."
+tests:
+ sample.psa.its.tfm:
+ filter: CONFIG_BUILD_WITH_TFM
+ tags:
+ - trusted-firmware-m
+ sample.psa.its.secure_storage.entropy_driver:
+ filter: CONFIG_SECURE_STORAGE and not CONFIG_SECURE_STORAGE_ITS_STORE_IMPLEMENTATION_NONE
+ and CONFIG_ENTROPY_HAS_DRIVER
+ extra_args: EXTRA_CONF_FILE=overlay-secure_storage.conf;overlay-entropy_driver.conf
+ tags:
+ - drivers.entropy
+ - settings
+ sample.psa.its.secure_storage.entropy_not_secure:
+ filter: CONFIG_SECURE_STORAGE and not CONFIG_SECURE_STORAGE_ITS_STORE_IMPLEMENTATION_NONE
+ and not CONFIG_ENTROPY_HAS_DRIVER
+ extra_args: EXTRA_CONF_FILE="overlay-secure_storage.conf;overlay-entropy_not_secure.conf"
+ tags:
+ - random
+ - settings
diff --git a/samples/psa/its/src/main.c b/samples/psa/its/src/main.c
new file mode 100644
index 00000000000..47752f05658
--- /dev/null
+++ b/samples/psa/its/src/main.c
@@ -0,0 +1,128 @@
+/* Copyright (c) 2024 Nordic Semiconductor
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include
+#include
+
+LOG_MODULE_REGISTER(psa_its);
+
+#define SAMPLE_DATA_UID (psa_storage_uid_t)1
+#define SAMPLE_DATA_FLAGS PSA_STORAGE_FLAG_NONE
+
+#ifdef CONFIG_SECURE_STORAGE
+#define SAMPLE_DATA_SIZE CONFIG_SECURE_STORAGE_ITS_MAX_DATA_SIZE
+#else
+#define SAMPLE_DATA_SIZE 128
+#endif
+
+static int write_and_read_data(void)
+{
+ LOG_INF("Writing to and reading back from ITS...");
+ psa_status_t ret;
+
+ /* Data to be written to ITS. */
+ uint8_t p_data_write[SAMPLE_DATA_SIZE];
+
+ for (unsigned int i = 0; i != sizeof(p_data_write); ++i) {
+ p_data_write[i] = i;
+ }
+
+ ret = psa_its_set(SAMPLE_DATA_UID, sizeof(p_data_write), p_data_write, SAMPLE_DATA_FLAGS);
+ if (ret != PSA_SUCCESS) {
+ LOG_ERR("Writing the data to ITS failed. (%d)", ret);
+ return -1;
+ }
+
+ /* Data to be read from ITS. */
+ uint8_t p_data_read[SAMPLE_DATA_SIZE];
+
+ /* Read back the data starting from an offset. */
+ const size_t data_offset = SAMPLE_DATA_SIZE / 2;
+
+ /* Number of bytes read. */
+ size_t p_data_length = 0;
+
+ ret = psa_its_get(SAMPLE_DATA_UID, data_offset, sizeof(p_data_read), p_data_read,
+ &p_data_length);
+ if (ret != PSA_SUCCESS) {
+ LOG_ERR("Reading back the data from ITS failed. (%d).", ret);
+ return -1;
+ }
+
+ if (p_data_length != SAMPLE_DATA_SIZE - data_offset) {
+ LOG_ERR("Unexpected amount of bytes read back. (%zu != %zu)",
+ p_data_length, SAMPLE_DATA_SIZE - data_offset);
+ return -1;
+ }
+
+ if (memcmp(p_data_write + data_offset, p_data_read, p_data_length)) {
+ LOG_HEXDUMP_INF(p_data_write + data_offset, p_data_length, "Data written:");
+ LOG_HEXDUMP_INF(p_data_read, p_data_length, "Data read back:");
+ LOG_ERR("The data read back doesn't match the data written.");
+ return -1;
+ }
+
+ LOG_INF("Successfully wrote to ITS and read back what was written.");
+ return 0;
+}
+
+static int read_info(void)
+{
+ LOG_INF("Reading the written entry's metadata...");
+ psa_status_t ret;
+
+ /* The entry's metadata. */
+ struct psa_storage_info_t p_info;
+
+ ret = psa_its_get_info(SAMPLE_DATA_UID, &p_info);
+ if (ret != PSA_SUCCESS) {
+ LOG_ERR("Failed to retrieve the entry's metadata. (%d)", ret);
+ return -1;
+ }
+
+ if (p_info.capacity != SAMPLE_DATA_SIZE
+ || p_info.size != SAMPLE_DATA_SIZE
+ || p_info.flags != SAMPLE_DATA_FLAGS) {
+ LOG_ERR("Entry metadata unexpected. (capacity:%zu size:%zu flags:0x%x)",
+ p_info.capacity, p_info.size, p_info.flags);
+ return -1;
+ }
+
+ LOG_INF("Successfully read the entry's metadata.");
+ return 0;
+}
+
+static int remove_entry(void)
+{
+ LOG_INF("Removing the entry from ITS...");
+ psa_status_t ret;
+
+ ret = psa_its_remove(SAMPLE_DATA_UID);
+ if (ret != PSA_SUCCESS) {
+ LOG_ERR("Failed to remove the entry. (%d)", ret);
+ return -1;
+ }
+
+ LOG_INF("Entry removed from ITS.");
+ return 0;
+}
+
+int main(void)
+{
+ LOG_INF("PSA ITS sample started.");
+
+ if (write_and_read_data()) {
+ return -1;
+ }
+
+ if (read_info()) {
+ return -1;
+ }
+
+ if (remove_entry()) {
+ return -1;
+ }
+
+ LOG_INF("Sample finished successfully.");
+ return 0;
+}