Move crypto modules

Move all the modules that constitute
the crypto library as well as the
associated headers to tf-psa-crypto/core
for the PSA core modules and to
tf-psa-crypto/drivers/builtin/src for
the others.

The common.h file is copied instead of
being just moved as eventually they
will be different in mbedtls and
TF-PSA-Crypto. Some parts of it can be
shared though but this will be done later,
probably when adding the CMake build
system in tf-psa-crypto.

Signed-off-by: Ronald Cron <ronald.cron@arm.com>
diff --git a/tf-psa-crypto/core/.gitignore b/tf-psa-crypto/core/.gitignore
new file mode 100644
index 0000000..70d9d04
--- /dev/null
+++ b/tf-psa-crypto/core/.gitignore
@@ -0,0 +1,4 @@
+###START_GENERATED_FILES###
+/psa_crypto_driver_wrappers.h
+/psa_crypto_driver_wrappers_no_static.c
+###END_GENERATED_FILES###
diff --git a/tf-psa-crypto/core/alignment.h b/tf-psa-crypto/core/alignment.h
new file mode 100644
index 0000000..a17001d
--- /dev/null
+++ b/tf-psa-crypto/core/alignment.h
@@ -0,0 +1,684 @@
+/**
+ * \file alignment.h
+ *
+ * \brief Utility code for dealing with unaligned memory accesses
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef MBEDTLS_LIBRARY_ALIGNMENT_H
+#define MBEDTLS_LIBRARY_ALIGNMENT_H
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ * Define MBEDTLS_EFFICIENT_UNALIGNED_ACCESS for architectures where unaligned memory
+ * accesses are known to be efficient.
+ *
+ * All functions defined here will behave correctly regardless, but might be less
+ * efficient when this is not defined.
+ */
+#if defined(__ARM_FEATURE_UNALIGNED) \
+    || defined(MBEDTLS_ARCH_IS_X86) || defined(MBEDTLS_ARCH_IS_X64) \
+    || defined(MBEDTLS_PLATFORM_IS_WINDOWS_ON_ARM64)
+/*
+ * __ARM_FEATURE_UNALIGNED is defined where appropriate by armcc, gcc 7, clang 9
+ * (and later versions) for Arm v7 and later; all x86 platforms should have
+ * efficient unaligned access.
+ *
+ * https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170#alignment
+ * specifies that on Windows-on-Arm64, unaligned access is safe (except for uncached
+ * device memory).
+ */
+#define MBEDTLS_EFFICIENT_UNALIGNED_ACCESS
+#endif
+
+#if defined(__IAR_SYSTEMS_ICC__) && \
+    (defined(MBEDTLS_ARCH_IS_ARM64) || defined(MBEDTLS_ARCH_IS_ARM32) \
+    || defined(__ICCRX__) || defined(__ICCRL78__) || defined(__ICCRISCV__))
+#pragma language=save
+#pragma language=extended
+#define MBEDTLS_POP_IAR_LANGUAGE_PRAGMA
+/* IAR recommend this technique for accessing unaligned data in
+ * https://www.iar.com/knowledge/support/technical-notes/compiler/accessing-unaligned-data
+ * This results in a single load / store instruction (if unaligned access is supported).
+ * According to that document, this is only supported on certain architectures.
+ */
+    #define UINT_UNALIGNED
+typedef uint16_t __packed mbedtls_uint16_unaligned_t;
+typedef uint32_t __packed mbedtls_uint32_unaligned_t;
+typedef uint64_t __packed mbedtls_uint64_unaligned_t;
+#elif defined(MBEDTLS_COMPILER_IS_GCC) && (MBEDTLS_GCC_VERSION >= 40504) && \
+    ((MBEDTLS_GCC_VERSION < 60300) || (!defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS)))
+/*
+ * gcc may generate a branch to memcpy for calls like `memcpy(dest, src, 4)` rather than
+ * generating some LDR or LDRB instructions (similar for stores).
+ *
+ * This is architecture dependent: x86-64 seems fine even with old gcc; 32-bit Arm
+ * is affected. To keep it simple, we enable for all architectures.
+ *
+ * For versions of gcc < 5.4.0 this issue always happens.
+ * For gcc < 6.3.0, this issue happens at -O0
+ * For all versions, this issue happens iff unaligned access is not supported.
+ *
+ * For gcc 4.x, this implementation will generate byte-by-byte loads even if unaligned access is
+ * supported, which is correct but not optimal.
+ *
+ * For performance (and code size, in some cases), we want to avoid the branch and just generate
+ * some inline load/store instructions since the access is small and constant-size.
+ *
+ * The manual states:
+ * "The packed attribute specifies that a variable or structure field should have the smallest
+ *  possible alignment—one byte for a variable"
+ * https://gcc.gnu.org/onlinedocs/gcc-4.5.4/gcc/Variable-Attributes.html
+ *
+ * Previous implementations used __attribute__((__aligned__(1)), but had issues with a gcc bug:
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94662
+ *
+ * Tested with several versions of GCC from 4.5.0 up to 13.2.0
+ * We don't enable for older than 4.5.0 as this has not been tested.
+ */
+ #define UINT_UNALIGNED_STRUCT
+typedef struct {
+    uint16_t x;
+} __attribute__((packed)) mbedtls_uint16_unaligned_t;
+typedef struct {
+    uint32_t x;
+} __attribute__((packed)) mbedtls_uint32_unaligned_t;
+typedef struct {
+    uint64_t x;
+} __attribute__((packed)) mbedtls_uint64_unaligned_t;
+ #endif
+
+/*
+ * We try to force mbedtls_(get|put)_unaligned_uintXX to be always inline, because this results
+ * in code that is both smaller and faster. IAR and gcc both benefit from this when optimising
+ * for size.
+ */
+
+/**
+ * Read the unsigned 16 bits integer from the given address, which need not
+ * be aligned.
+ *
+ * \param   p pointer to 2 bytes of data
+ * \return  Data at the given address
+ */
+#if defined(__IAR_SYSTEMS_ICC__)
+#pragma inline = forced
+#elif defined(__GNUC__)
+__attribute__((always_inline))
+#endif
+static inline uint16_t mbedtls_get_unaligned_uint16(const void *p)
+{
+    uint16_t r;
+#if defined(UINT_UNALIGNED)
+    mbedtls_uint16_unaligned_t *p16 = (mbedtls_uint16_unaligned_t *) p;
+    r = *p16;
+#elif defined(UINT_UNALIGNED_STRUCT)
+    mbedtls_uint16_unaligned_t *p16 = (mbedtls_uint16_unaligned_t *) p;
+    r = p16->x;
+#else
+    memcpy(&r, p, sizeof(r));
+#endif
+    return r;
+}
+
+/**
+ * Write the unsigned 16 bits integer to the given address, which need not
+ * be aligned.
+ *
+ * \param   p pointer to 2 bytes of data
+ * \param   x data to write
+ */
+#if defined(__IAR_SYSTEMS_ICC__)
+#pragma inline = forced
+#elif defined(__GNUC__)
+__attribute__((always_inline))
+#endif
+static inline void mbedtls_put_unaligned_uint16(void *p, uint16_t x)
+{
+#if defined(UINT_UNALIGNED)
+    mbedtls_uint16_unaligned_t *p16 = (mbedtls_uint16_unaligned_t *) p;
+    *p16 = x;
+#elif defined(UINT_UNALIGNED_STRUCT)
+    mbedtls_uint16_unaligned_t *p16 = (mbedtls_uint16_unaligned_t *) p;
+    p16->x = x;
+#else
+    memcpy(p, &x, sizeof(x));
+#endif
+}
+
+/**
+ * Read the unsigned 32 bits integer from the given address, which need not
+ * be aligned.
+ *
+ * \param   p pointer to 4 bytes of data
+ * \return  Data at the given address
+ */
+#if defined(__IAR_SYSTEMS_ICC__)
+#pragma inline = forced
+#elif defined(__GNUC__)
+__attribute__((always_inline))
+#endif
+static inline uint32_t mbedtls_get_unaligned_uint32(const void *p)
+{
+    uint32_t r;
+#if defined(UINT_UNALIGNED)
+    mbedtls_uint32_unaligned_t *p32 = (mbedtls_uint32_unaligned_t *) p;
+    r = *p32;
+#elif defined(UINT_UNALIGNED_STRUCT)
+    mbedtls_uint32_unaligned_t *p32 = (mbedtls_uint32_unaligned_t *) p;
+    r = p32->x;
+#else
+    memcpy(&r, p, sizeof(r));
+#endif
+    return r;
+}
+
+/**
+ * Write the unsigned 32 bits integer to the given address, which need not
+ * be aligned.
+ *
+ * \param   p pointer to 4 bytes of data
+ * \param   x data to write
+ */
+#if defined(__IAR_SYSTEMS_ICC__)
+#pragma inline = forced
+#elif defined(__GNUC__)
+__attribute__((always_inline))
+#endif
+static inline void mbedtls_put_unaligned_uint32(void *p, uint32_t x)
+{
+#if defined(UINT_UNALIGNED)
+    mbedtls_uint32_unaligned_t *p32 = (mbedtls_uint32_unaligned_t *) p;
+    *p32 = x;
+#elif defined(UINT_UNALIGNED_STRUCT)
+    mbedtls_uint32_unaligned_t *p32 = (mbedtls_uint32_unaligned_t *) p;
+    p32->x = x;
+#else
+    memcpy(p, &x, sizeof(x));
+#endif
+}
+
+/**
+ * Read the unsigned 64 bits integer from the given address, which need not
+ * be aligned.
+ *
+ * \param   p pointer to 8 bytes of data
+ * \return  Data at the given address
+ */
+#if defined(__IAR_SYSTEMS_ICC__)
+#pragma inline = forced
+#elif defined(__GNUC__)
+__attribute__((always_inline))
+#endif
+static inline uint64_t mbedtls_get_unaligned_uint64(const void *p)
+{
+    uint64_t r;
+#if defined(UINT_UNALIGNED)
+    mbedtls_uint64_unaligned_t *p64 = (mbedtls_uint64_unaligned_t *) p;
+    r = *p64;
+#elif defined(UINT_UNALIGNED_STRUCT)
+    mbedtls_uint64_unaligned_t *p64 = (mbedtls_uint64_unaligned_t *) p;
+    r = p64->x;
+#else
+    memcpy(&r, p, sizeof(r));
+#endif
+    return r;
+}
+
+/**
+ * Write the unsigned 64 bits integer to the given address, which need not
+ * be aligned.
+ *
+ * \param   p pointer to 8 bytes of data
+ * \param   x data to write
+ */
+#if defined(__IAR_SYSTEMS_ICC__)
+#pragma inline = forced
+#elif defined(__GNUC__)
+__attribute__((always_inline))
+#endif
+static inline void mbedtls_put_unaligned_uint64(void *p, uint64_t x)
+{
+#if defined(UINT_UNALIGNED)
+    mbedtls_uint64_unaligned_t *p64 = (mbedtls_uint64_unaligned_t *) p;
+    *p64 = x;
+#elif defined(UINT_UNALIGNED_STRUCT)
+    mbedtls_uint64_unaligned_t *p64 = (mbedtls_uint64_unaligned_t *) p;
+    p64->x = x;
+#else
+    memcpy(p, &x, sizeof(x));
+#endif
+}
+
+#if defined(MBEDTLS_POP_IAR_LANGUAGE_PRAGMA)
+#pragma language=restore
+#endif
+
+/** Byte Reading Macros
+ *
+ * Given a multi-byte integer \p x, MBEDTLS_BYTE_n retrieves the n-th
+ * byte from x, where byte 0 is the least significant byte.
+ */
+#define MBEDTLS_BYTE_0(x) ((uint8_t) ((x)         & 0xff))
+#define MBEDTLS_BYTE_1(x) ((uint8_t) (((x) >>  8) & 0xff))
+#define MBEDTLS_BYTE_2(x) ((uint8_t) (((x) >> 16) & 0xff))
+#define MBEDTLS_BYTE_3(x) ((uint8_t) (((x) >> 24) & 0xff))
+#define MBEDTLS_BYTE_4(x) ((uint8_t) (((x) >> 32) & 0xff))
+#define MBEDTLS_BYTE_5(x) ((uint8_t) (((x) >> 40) & 0xff))
+#define MBEDTLS_BYTE_6(x) ((uint8_t) (((x) >> 48) & 0xff))
+#define MBEDTLS_BYTE_7(x) ((uint8_t) (((x) >> 56) & 0xff))
+
+/*
+ * Detect GCC built-in byteswap routines
+ */
+#if defined(__GNUC__) && defined(__GNUC_PREREQ)
+#if __GNUC_PREREQ(4, 8)
+#define MBEDTLS_BSWAP16 __builtin_bswap16
+#endif /* __GNUC_PREREQ(4,8) */
+#if __GNUC_PREREQ(4, 3)
+#define MBEDTLS_BSWAP32 __builtin_bswap32
+#define MBEDTLS_BSWAP64 __builtin_bswap64
+#endif /* __GNUC_PREREQ(4,3) */
+#endif /* defined(__GNUC__) && defined(__GNUC_PREREQ) */
+
+/*
+ * Detect Clang built-in byteswap routines
+ */
+#if defined(__clang__) && defined(__has_builtin)
+#if __has_builtin(__builtin_bswap16) && !defined(MBEDTLS_BSWAP16)
+#define MBEDTLS_BSWAP16 __builtin_bswap16
+#endif /* __has_builtin(__builtin_bswap16) */
+#if __has_builtin(__builtin_bswap32) && !defined(MBEDTLS_BSWAP32)
+#define MBEDTLS_BSWAP32 __builtin_bswap32
+#endif /* __has_builtin(__builtin_bswap32) */
+#if __has_builtin(__builtin_bswap64) && !defined(MBEDTLS_BSWAP64)
+#define MBEDTLS_BSWAP64 __builtin_bswap64
+#endif /* __has_builtin(__builtin_bswap64) */
+#endif /* defined(__clang__) && defined(__has_builtin) */
+
+/*
+ * Detect MSVC built-in byteswap routines
+ */
+#if defined(_MSC_VER)
+#if !defined(MBEDTLS_BSWAP16)
+#define MBEDTLS_BSWAP16 _byteswap_ushort
+#endif
+#if !defined(MBEDTLS_BSWAP32)
+#define MBEDTLS_BSWAP32 _byteswap_ulong
+#endif
+#if !defined(MBEDTLS_BSWAP64)
+#define MBEDTLS_BSWAP64 _byteswap_uint64
+#endif
+#endif /* defined(_MSC_VER) */
+
+/* Detect armcc built-in byteswap routine */
+#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 410000) && !defined(MBEDTLS_BSWAP32)
+#if defined(__ARM_ACLE)  /* ARM Compiler 6 - earlier versions don't need a header */
+#include <arm_acle.h>
+#endif
+#define MBEDTLS_BSWAP32 __rev
+#endif
+
+/* Detect IAR built-in byteswap routine */
+#if defined(__IAR_SYSTEMS_ICC__)
+#if defined(__ARM_ACLE)
+#include <arm_acle.h>
+#define MBEDTLS_BSWAP16(x) ((uint16_t) __rev16((uint32_t) (x)))
+#define MBEDTLS_BSWAP32 __rev
+#define MBEDTLS_BSWAP64 __revll
+#endif
+#endif
+
+/*
+ * Where compiler built-ins are not present, fall back to C code that the
+ * compiler may be able to detect and transform into the relevant bswap or
+ * similar instruction.
+ */
+#if !defined(MBEDTLS_BSWAP16)
+static inline uint16_t mbedtls_bswap16(uint16_t x)
+{
+    return
+        (x & 0x00ff) << 8 |
+        (x & 0xff00) >> 8;
+}
+#define MBEDTLS_BSWAP16 mbedtls_bswap16
+#endif /* !defined(MBEDTLS_BSWAP16) */
+
+#if !defined(MBEDTLS_BSWAP32)
+static inline uint32_t mbedtls_bswap32(uint32_t x)
+{
+    return
+        (x & 0x000000ff) << 24 |
+        (x & 0x0000ff00) <<  8 |
+        (x & 0x00ff0000) >>  8 |
+        (x & 0xff000000) >> 24;
+}
+#define MBEDTLS_BSWAP32 mbedtls_bswap32
+#endif /* !defined(MBEDTLS_BSWAP32) */
+
+#if !defined(MBEDTLS_BSWAP64)
+static inline uint64_t mbedtls_bswap64(uint64_t x)
+{
+    return
+        (x & 0x00000000000000ffULL) << 56 |
+        (x & 0x000000000000ff00ULL) << 40 |
+        (x & 0x0000000000ff0000ULL) << 24 |
+        (x & 0x00000000ff000000ULL) <<  8 |
+        (x & 0x000000ff00000000ULL) >>  8 |
+        (x & 0x0000ff0000000000ULL) >> 24 |
+        (x & 0x00ff000000000000ULL) >> 40 |
+        (x & 0xff00000000000000ULL) >> 56;
+}
+#define MBEDTLS_BSWAP64 mbedtls_bswap64
+#endif /* !defined(MBEDTLS_BSWAP64) */
+
+#if !defined(__BYTE_ORDER__)
+
+#if defined(__LITTLE_ENDIAN__)
+/* IAR defines __xxx_ENDIAN__, but not __BYTE_ORDER__ */
+#define MBEDTLS_IS_BIG_ENDIAN 0
+#elif defined(__BIG_ENDIAN__)
+#define MBEDTLS_IS_BIG_ENDIAN 1
+#else
+static const uint16_t mbedtls_byte_order_detector = { 0x100 };
+#define MBEDTLS_IS_BIG_ENDIAN (*((unsigned char *) (&mbedtls_byte_order_detector)) == 0x01)
+#endif
+
+#else
+
+#if (__BYTE_ORDER__) == (__ORDER_BIG_ENDIAN__)
+#define MBEDTLS_IS_BIG_ENDIAN 1
+#else
+#define MBEDTLS_IS_BIG_ENDIAN 0
+#endif
+
+#endif /* !defined(__BYTE_ORDER__) */
+
+/**
+ * Get the unsigned 32 bits integer corresponding to four bytes in
+ * big-endian order (MSB first).
+ *
+ * \param   data    Base address of the memory to get the four bytes from.
+ * \param   offset  Offset from \p data of the first and most significant
+ *                  byte of the four bytes to build the 32 bits unsigned
+ *                  integer from.
+ */
+#define MBEDTLS_GET_UINT32_BE(data, offset)                                \
+    ((MBEDTLS_IS_BIG_ENDIAN)                                               \
+        ? mbedtls_get_unaligned_uint32((data) + (offset))                  \
+        : MBEDTLS_BSWAP32(mbedtls_get_unaligned_uint32((data) + (offset))) \
+    )
+
+/**
+ * Put in memory a 32 bits unsigned integer in big-endian order.
+ *
+ * \param   n       32 bits unsigned integer to put in memory.
+ * \param   data    Base address of the memory where to put the 32
+ *                  bits unsigned integer in.
+ * \param   offset  Offset from \p data where to put the most significant
+ *                  byte of the 32 bits unsigned integer \p n.
+ */
+#define MBEDTLS_PUT_UINT32_BE(n, data, offset)                                   \
+    {                                                                            \
+        if (MBEDTLS_IS_BIG_ENDIAN)                                               \
+        {                                                                        \
+            mbedtls_put_unaligned_uint32((data) + (offset), (uint32_t) (n));     \
+        }                                                                        \
+        else                                                                     \
+        {                                                                        \
+            mbedtls_put_unaligned_uint32((data) + (offset), MBEDTLS_BSWAP32((uint32_t) (n))); \
+        }                                                                        \
+    }
+
+/**
+ * Get the unsigned 32 bits integer corresponding to four bytes in
+ * little-endian order (LSB first).
+ *
+ * \param   data    Base address of the memory to get the four bytes from.
+ * \param   offset  Offset from \p data of the first and least significant
+ *                  byte of the four bytes to build the 32 bits unsigned
+ *                  integer from.
+ */
+#define MBEDTLS_GET_UINT32_LE(data, offset)                                \
+    ((MBEDTLS_IS_BIG_ENDIAN)                                               \
+        ? MBEDTLS_BSWAP32(mbedtls_get_unaligned_uint32((data) + (offset))) \
+        : mbedtls_get_unaligned_uint32((data) + (offset))                  \
+    )
+
+
+/**
+ * Put in memory a 32 bits unsigned integer in little-endian order.
+ *
+ * \param   n       32 bits unsigned integer to put in memory.
+ * \param   data    Base address of the memory where to put the 32
+ *                  bits unsigned integer in.
+ * \param   offset  Offset from \p data where to put the least significant
+ *                  byte of the 32 bits unsigned integer \p n.
+ */
+#define MBEDTLS_PUT_UINT32_LE(n, data, offset)                                   \
+    {                                                                            \
+        if (MBEDTLS_IS_BIG_ENDIAN)                                               \
+        {                                                                        \
+            mbedtls_put_unaligned_uint32((data) + (offset), MBEDTLS_BSWAP32((uint32_t) (n))); \
+        }                                                                        \
+        else                                                                     \
+        {                                                                        \
+            mbedtls_put_unaligned_uint32((data) + (offset), ((uint32_t) (n)));   \
+        }                                                                        \
+    }
+
+/**
+ * Get the unsigned 16 bits integer corresponding to two bytes in
+ * little-endian order (LSB first).
+ *
+ * \param   data    Base address of the memory to get the two bytes from.
+ * \param   offset  Offset from \p data of the first and least significant
+ *                  byte of the two bytes to build the 16 bits unsigned
+ *                  integer from.
+ */
+#define MBEDTLS_GET_UINT16_LE(data, offset)                                \
+    ((MBEDTLS_IS_BIG_ENDIAN)                                               \
+        ? MBEDTLS_BSWAP16(mbedtls_get_unaligned_uint16((data) + (offset))) \
+        : mbedtls_get_unaligned_uint16((data) + (offset))                  \
+    )
+
+/**
+ * Put in memory a 16 bits unsigned integer in little-endian order.
+ *
+ * \param   n       16 bits unsigned integer to put in memory.
+ * \param   data    Base address of the memory where to put the 16
+ *                  bits unsigned integer in.
+ * \param   offset  Offset from \p data where to put the least significant
+ *                  byte of the 16 bits unsigned integer \p n.
+ */
+#define MBEDTLS_PUT_UINT16_LE(n, data, offset)                                   \
+    {                                                                            \
+        if (MBEDTLS_IS_BIG_ENDIAN)                                               \
+        {                                                                        \
+            mbedtls_put_unaligned_uint16((data) + (offset), MBEDTLS_BSWAP16((uint16_t) (n))); \
+        }                                                                        \
+        else                                                                     \
+        {                                                                        \
+            mbedtls_put_unaligned_uint16((data) + (offset), (uint16_t) (n));     \
+        }                                                                        \
+    }
+
+/**
+ * Get the unsigned 16 bits integer corresponding to two bytes in
+ * big-endian order (MSB first).
+ *
+ * \param   data    Base address of the memory to get the two bytes from.
+ * \param   offset  Offset from \p data of the first and most significant
+ *                  byte of the two bytes to build the 16 bits unsigned
+ *                  integer from.
+ */
+#define MBEDTLS_GET_UINT16_BE(data, offset)                                \
+    ((MBEDTLS_IS_BIG_ENDIAN)                                               \
+        ? mbedtls_get_unaligned_uint16((data) + (offset))                  \
+        : MBEDTLS_BSWAP16(mbedtls_get_unaligned_uint16((data) + (offset))) \
+    )
+
+/**
+ * Put in memory a 16 bits unsigned integer in big-endian order.
+ *
+ * \param   n       16 bits unsigned integer to put in memory.
+ * \param   data    Base address of the memory where to put the 16
+ *                  bits unsigned integer in.
+ * \param   offset  Offset from \p data where to put the most significant
+ *                  byte of the 16 bits unsigned integer \p n.
+ */
+#define MBEDTLS_PUT_UINT16_BE(n, data, offset)                                   \
+    {                                                                            \
+        if (MBEDTLS_IS_BIG_ENDIAN)                                               \
+        {                                                                        \
+            mbedtls_put_unaligned_uint16((data) + (offset), (uint16_t) (n));     \
+        }                                                                        \
+        else                                                                     \
+        {                                                                        \
+            mbedtls_put_unaligned_uint16((data) + (offset), MBEDTLS_BSWAP16((uint16_t) (n))); \
+        }                                                                        \
+    }
+
+/**
+ * Get the unsigned 24 bits integer corresponding to three bytes in
+ * big-endian order (MSB first).
+ *
+ * \param   data    Base address of the memory to get the three bytes from.
+ * \param   offset  Offset from \p data of the first and most significant
+ *                  byte of the three bytes to build the 24 bits unsigned
+ *                  integer from.
+ */
+#define MBEDTLS_GET_UINT24_BE(data, offset)        \
+    (                                              \
+        ((uint32_t) (data)[(offset)] << 16)        \
+        | ((uint32_t) (data)[(offset) + 1] << 8)   \
+        | ((uint32_t) (data)[(offset) + 2])        \
+    )
+
+/**
+ * Put in memory a 24 bits unsigned integer in big-endian order.
+ *
+ * \param   n       24 bits unsigned integer to put in memory.
+ * \param   data    Base address of the memory where to put the 24
+ *                  bits unsigned integer in.
+ * \param   offset  Offset from \p data where to put the most significant
+ *                  byte of the 24 bits unsigned integer \p n.
+ */
+#define MBEDTLS_PUT_UINT24_BE(n, data, offset)                \
+    {                                                         \
+        (data)[(offset)] = MBEDTLS_BYTE_2(n);                 \
+        (data)[(offset) + 1] = MBEDTLS_BYTE_1(n);             \
+        (data)[(offset) + 2] = MBEDTLS_BYTE_0(n);             \
+    }
+
+/**
+ * Get the unsigned 24 bits integer corresponding to three bytes in
+ * little-endian order (LSB first).
+ *
+ * \param   data    Base address of the memory to get the three bytes from.
+ * \param   offset  Offset from \p data of the first and least significant
+ *                  byte of the three bytes to build the 24 bits unsigned
+ *                  integer from.
+ */
+#define MBEDTLS_GET_UINT24_LE(data, offset)               \
+    (                                                     \
+        ((uint32_t) (data)[(offset)])                     \
+        | ((uint32_t) (data)[(offset) + 1] <<  8)         \
+        | ((uint32_t) (data)[(offset) + 2] << 16)         \
+    )
+
+/**
+ * Put in memory a 24 bits unsigned integer in little-endian order.
+ *
+ * \param   n       24 bits unsigned integer to put in memory.
+ * \param   data    Base address of the memory where to put the 24
+ *                  bits unsigned integer in.
+ * \param   offset  Offset from \p data where to put the least significant
+ *                  byte of the 24 bits unsigned integer \p n.
+ */
+#define MBEDTLS_PUT_UINT24_LE(n, data, offset)                \
+    {                                                         \
+        (data)[(offset)] = MBEDTLS_BYTE_0(n);                 \
+        (data)[(offset) + 1] = MBEDTLS_BYTE_1(n);             \
+        (data)[(offset) + 2] = MBEDTLS_BYTE_2(n);             \
+    }
+
+/**
+ * Get the unsigned 64 bits integer corresponding to eight bytes in
+ * big-endian order (MSB first).
+ *
+ * \param   data    Base address of the memory to get the eight bytes from.
+ * \param   offset  Offset from \p data of the first and most significant
+ *                  byte of the eight bytes to build the 64 bits unsigned
+ *                  integer from.
+ */
+#define MBEDTLS_GET_UINT64_BE(data, offset)                                \
+    ((MBEDTLS_IS_BIG_ENDIAN)                                               \
+        ? mbedtls_get_unaligned_uint64((data) + (offset))                  \
+        : MBEDTLS_BSWAP64(mbedtls_get_unaligned_uint64((data) + (offset))) \
+    )
+
+/**
+ * Put in memory a 64 bits unsigned integer in big-endian order.
+ *
+ * \param   n       64 bits unsigned integer to put in memory.
+ * \param   data    Base address of the memory where to put the 64
+ *                  bits unsigned integer in.
+ * \param   offset  Offset from \p data where to put the most significant
+ *                  byte of the 64 bits unsigned integer \p n.
+ */
+#define MBEDTLS_PUT_UINT64_BE(n, data, offset)                                   \
+    {                                                                            \
+        if (MBEDTLS_IS_BIG_ENDIAN)                                               \
+        {                                                                        \
+            mbedtls_put_unaligned_uint64((data) + (offset), (uint64_t) (n));     \
+        }                                                                        \
+        else                                                                     \
+        {                                                                        \
+            mbedtls_put_unaligned_uint64((data) + (offset), MBEDTLS_BSWAP64((uint64_t) (n))); \
+        }                                                                        \
+    }
+
+/**
+ * Get the unsigned 64 bits integer corresponding to eight bytes in
+ * little-endian order (LSB first).
+ *
+ * \param   data    Base address of the memory to get the eight bytes from.
+ * \param   offset  Offset from \p data of the first and least significant
+ *                  byte of the eight bytes to build the 64 bits unsigned
+ *                  integer from.
+ */
+#define MBEDTLS_GET_UINT64_LE(data, offset)                                \
+    ((MBEDTLS_IS_BIG_ENDIAN)                                               \
+        ? MBEDTLS_BSWAP64(mbedtls_get_unaligned_uint64((data) + (offset))) \
+        : mbedtls_get_unaligned_uint64((data) + (offset))                  \
+    )
+
+/**
+ * Put in memory a 64 bits unsigned integer in little-endian order.
+ *
+ * \param   n       64 bits unsigned integer to put in memory.
+ * \param   data    Base address of the memory where to put the 64
+ *                  bits unsigned integer in.
+ * \param   offset  Offset from \p data where to put the least significant
+ *                  byte of the 64 bits unsigned integer \p n.
+ */
+#define MBEDTLS_PUT_UINT64_LE(n, data, offset)                                   \
+    {                                                                            \
+        if (MBEDTLS_IS_BIG_ENDIAN)                                               \
+        {                                                                        \
+            mbedtls_put_unaligned_uint64((data) + (offset), MBEDTLS_BSWAP64((uint64_t) (n))); \
+        }                                                                        \
+        else                                                                     \
+        {                                                                        \
+            mbedtls_put_unaligned_uint64((data) + (offset), (uint64_t) (n));     \
+        }                                                                        \
+    }
+
+#endif /* MBEDTLS_LIBRARY_ALIGNMENT_H */
diff --git a/tf-psa-crypto/core/common.h b/tf-psa-crypto/core/common.h
new file mode 100644
index 0000000..3936ffd
--- /dev/null
+++ b/tf-psa-crypto/core/common.h
@@ -0,0 +1,435 @@
+/**
+ * \file common.h
+ *
+ * \brief Utility macros for internal use in the library
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef MBEDTLS_LIBRARY_COMMON_H
+#define MBEDTLS_LIBRARY_COMMON_H
+
+#include "mbedtls/build_info.h"
+#include "alignment.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#if defined(__ARM_NEON)
+#include <arm_neon.h>
+#define MBEDTLS_HAVE_NEON_INTRINSICS
+#elif defined(MBEDTLS_PLATFORM_IS_WINDOWS_ON_ARM64)
+#include <arm64_neon.h>
+#define MBEDTLS_HAVE_NEON_INTRINSICS
+#endif
+
+/** Helper to define a function as static except when building invasive tests.
+ *
+ * If a function is only used inside its own source file and should be
+ * declared `static` to allow the compiler to optimize for code size,
+ * but that function has unit tests, define it with
+ * ```
+ * MBEDTLS_STATIC_TESTABLE int mbedtls_foo(...) { ... }
+ * ```
+ * and declare it in a header in the `library/` directory with
+ * ```
+ * #if defined(MBEDTLS_TEST_HOOKS)
+ * int mbedtls_foo(...);
+ * #endif
+ * ```
+ */
+#if defined(MBEDTLS_TEST_HOOKS)
+#define MBEDTLS_STATIC_TESTABLE
+#else
+#define MBEDTLS_STATIC_TESTABLE static
+#endif
+
+#if defined(MBEDTLS_TEST_HOOKS)
+extern void (*mbedtls_test_hook_test_fail)(const char *test, int line, const char *file);
+#define MBEDTLS_TEST_HOOK_TEST_ASSERT(TEST) \
+    do { \
+        if ((!(TEST)) && ((*mbedtls_test_hook_test_fail) != NULL)) \
+        { \
+            (*mbedtls_test_hook_test_fail)( #TEST, __LINE__, __FILE__); \
+        } \
+    } while (0)
+#else
+#define MBEDTLS_TEST_HOOK_TEST_ASSERT(TEST)
+#endif /* defined(MBEDTLS_TEST_HOOKS) */
+
+/** \def ARRAY_LENGTH
+ * Return the number of elements of a static or stack array.
+ *
+ * \param array         A value of array (not pointer) type.
+ *
+ * \return The number of elements of the array.
+ */
+/* A correct implementation of ARRAY_LENGTH, but which silently gives
+ * a nonsensical result if called with a pointer rather than an array. */
+#define ARRAY_LENGTH_UNSAFE(array)            \
+    (sizeof(array) / sizeof(*(array)))
+
+#if defined(__GNUC__)
+/* Test if arg and &(arg)[0] have the same type. This is true if arg is
+ * an array but not if it's a pointer. */
+#define IS_ARRAY_NOT_POINTER(arg)                                     \
+    (!__builtin_types_compatible_p(__typeof__(arg),                \
+                                   __typeof__(&(arg)[0])))
+/* A compile-time constant with the value 0. If `const_expr` is not a
+ * compile-time constant with a nonzero value, cause a compile-time error. */
+#define STATIC_ASSERT_EXPR(const_expr)                                \
+    (0 && sizeof(struct { unsigned int STATIC_ASSERT : 1 - 2 * !(const_expr); }))
+
+/* Return the scalar value `value` (possibly promoted). This is a compile-time
+ * constant if `value` is. `condition` must be a compile-time constant.
+ * If `condition` is false, arrange to cause a compile-time error. */
+#define STATIC_ASSERT_THEN_RETURN(condition, value)   \
+    (STATIC_ASSERT_EXPR(condition) ? 0 : (value))
+
+#define ARRAY_LENGTH(array)                                           \
+    (STATIC_ASSERT_THEN_RETURN(IS_ARRAY_NOT_POINTER(array),         \
+                               ARRAY_LENGTH_UNSAFE(array)))
+
+#else
+/* If we aren't sure the compiler supports our non-standard tricks,
+ * fall back to the unsafe implementation. */
+#define ARRAY_LENGTH(array) ARRAY_LENGTH_UNSAFE(array)
+#endif
+/** Allow library to access its structs' private members.
+ *
+ * Although structs defined in header files are publicly available,
+ * their members are private and should not be accessed by the user.
+ */
+#define MBEDTLS_ALLOW_PRIVATE_ACCESS
+
+/**
+ * \brief       Securely zeroize a buffer then free it.
+ *
+ *              Similar to making consecutive calls to
+ *              \c mbedtls_platform_zeroize() and \c mbedtls_free(), but has
+ *              code size savings, and potential for optimisation in the future.
+ *
+ *              Guaranteed to be a no-op if \p buf is \c NULL and \p len is 0.
+ *
+ * \param buf   Buffer to be zeroized then freed.
+ * \param len   Length of the buffer in bytes
+ */
+void mbedtls_zeroize_and_free(void *buf, size_t len);
+
+/** Return an offset into a buffer.
+ *
+ * This is just the addition of an offset to a pointer, except that this
+ * function also accepts an offset of 0 into a buffer whose pointer is null.
+ * (`p + n` has undefined behavior when `p` is null, even when `n == 0`.
+ * A null pointer is a valid buffer pointer when the size is 0, for example
+ * as the result of `malloc(0)` on some platforms.)
+ *
+ * \param p     Pointer to a buffer of at least n bytes.
+ *              This may be \p NULL if \p n is zero.
+ * \param n     An offset in bytes.
+ * \return      Pointer to offset \p n in the buffer \p p.
+ *              Note that this is only a valid pointer if the size of the
+ *              buffer is at least \p n + 1.
+ */
+static inline unsigned char *mbedtls_buffer_offset(
+    unsigned char *p, size_t n)
+{
+    return p == NULL ? NULL : p + n;
+}
+
+/** Return an offset into a read-only buffer.
+ *
+ * Similar to mbedtls_buffer_offset(), but for const pointers.
+ *
+ * \param p     Pointer to a buffer of at least n bytes.
+ *              This may be \p NULL if \p n is zero.
+ * \param n     An offset in bytes.
+ * \return      Pointer to offset \p n in the buffer \p p.
+ *              Note that this is only a valid pointer if the size of the
+ *              buffer is at least \p n + 1.
+ */
+static inline const unsigned char *mbedtls_buffer_offset_const(
+    const unsigned char *p, size_t n)
+{
+    return p == NULL ? NULL : p + n;
+}
+
+/* Always inline mbedtls_xor() for similar reasons as mbedtls_xor_no_simd(). */
+#if defined(__IAR_SYSTEMS_ICC__)
+#pragma inline = forced
+#elif defined(__GNUC__)
+__attribute__((always_inline))
+#endif
+/**
+ * Perform a fast block XOR operation, such that
+ * r[i] = a[i] ^ b[i] where 0 <= i < n
+ *
+ * \param   r Pointer to result (buffer of at least \p n bytes). \p r
+ *            may be equal to either \p a or \p b, but behaviour when
+ *            it overlaps in other ways is undefined.
+ * \param   a Pointer to input (buffer of at least \p n bytes)
+ * \param   b Pointer to input (buffer of at least \p n bytes)
+ * \param   n Number of bytes to process.
+ *
+ * \note      Depending on the situation, it may be faster to use either mbedtls_xor() or
+ *            mbedtls_xor_no_simd() (these are functionally equivalent).
+ *            If the result is used immediately after the xor operation in non-SIMD code (e.g, in
+ *            AES-CBC), there may be additional latency to transfer the data from SIMD to scalar
+ *            registers, and in this case, mbedtls_xor_no_simd() may be faster. In other cases where
+ *            the result is not used immediately (e.g., in AES-CTR), mbedtls_xor() may be faster.
+ *            For targets without SIMD support, they will behave the same.
+ */
+static inline void mbedtls_xor(unsigned char *r,
+                               const unsigned char *a,
+                               const unsigned char *b,
+                               size_t n)
+{
+    size_t i = 0;
+#if defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS)
+#if defined(MBEDTLS_HAVE_NEON_INTRINSICS) && \
+    (!(defined(MBEDTLS_COMPILER_IS_GCC) && MBEDTLS_GCC_VERSION < 70300))
+    /* Old GCC versions generate a warning here, so disable the NEON path for these compilers */
+    for (; (i + 16) <= n; i += 16) {
+        uint8x16_t v1 = vld1q_u8(a + i);
+        uint8x16_t v2 = vld1q_u8(b + i);
+        uint8x16_t x = veorq_u8(v1, v2);
+        vst1q_u8(r + i, x);
+    }
+#if defined(__IAR_SYSTEMS_ICC__)
+    /* This if statement helps some compilers (e.g., IAR) optimise out the byte-by-byte tail case
+     * where n is a constant multiple of 16.
+     * For other compilers (e.g. recent gcc and clang) it makes no difference if n is a compile-time
+     * constant, and is a very small perf regression if n is not a compile-time constant. */
+    if (n % 16 == 0) {
+        return;
+    }
+#endif
+#elif defined(MBEDTLS_ARCH_IS_X64) || defined(MBEDTLS_ARCH_IS_ARM64)
+    /* This codepath probably only makes sense on architectures with 64-bit registers */
+    for (; (i + 8) <= n; i += 8) {
+        uint64_t x = mbedtls_get_unaligned_uint64(a + i) ^ mbedtls_get_unaligned_uint64(b + i);
+        mbedtls_put_unaligned_uint64(r + i, x);
+    }
+#if defined(__IAR_SYSTEMS_ICC__)
+    if (n % 8 == 0) {
+        return;
+    }
+#endif
+#else
+    for (; (i + 4) <= n; i += 4) {
+        uint32_t x = mbedtls_get_unaligned_uint32(a + i) ^ mbedtls_get_unaligned_uint32(b + i);
+        mbedtls_put_unaligned_uint32(r + i, x);
+    }
+#if defined(__IAR_SYSTEMS_ICC__)
+    if (n % 4 == 0) {
+        return;
+    }
+#endif
+#endif
+#endif
+    for (; i < n; i++) {
+        r[i] = a[i] ^ b[i];
+    }
+}
+
+/* Always inline mbedtls_xor_no_simd() as we see significant perf regressions when it does not get
+ * inlined (e.g., observed about 3x perf difference in gcm_mult_largetable with gcc 7 - 12) */
+#if defined(__IAR_SYSTEMS_ICC__)
+#pragma inline = forced
+#elif defined(__GNUC__)
+__attribute__((always_inline))
+#endif
+/**
+ * Perform a fast block XOR operation, such that
+ * r[i] = a[i] ^ b[i] where 0 <= i < n
+ *
+ * In some situations, this can perform better than mbedtls_xor() (e.g., it's about 5%
+ * better in AES-CBC).
+ *
+ * \param   r Pointer to result (buffer of at least \p n bytes). \p r
+ *            may be equal to either \p a or \p b, but behaviour when
+ *            it overlaps in other ways is undefined.
+ * \param   a Pointer to input (buffer of at least \p n bytes)
+ * \param   b Pointer to input (buffer of at least \p n bytes)
+ * \param   n Number of bytes to process.
+ *
+ * \note      Depending on the situation, it may be faster to use either mbedtls_xor() or
+ *            mbedtls_xor_no_simd() (these are functionally equivalent).
+ *            If the result is used immediately after the xor operation in non-SIMD code (e.g, in
+ *            AES-CBC), there may be additional latency to transfer the data from SIMD to scalar
+ *            registers, and in this case, mbedtls_xor_no_simd() may be faster. In other cases where
+ *            the result is not used immediately (e.g., in AES-CTR), mbedtls_xor() may be faster.
+ *            For targets without SIMD support, they will behave the same.
+ */
+static inline void mbedtls_xor_no_simd(unsigned char *r,
+                                       const unsigned char *a,
+                                       const unsigned char *b,
+                                       size_t n)
+{
+    size_t i = 0;
+#if defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS)
+#if defined(MBEDTLS_ARCH_IS_X64) || defined(MBEDTLS_ARCH_IS_ARM64)
+    /* This codepath probably only makes sense on architectures with 64-bit registers */
+    for (; (i + 8) <= n; i += 8) {
+        uint64_t x = mbedtls_get_unaligned_uint64(a + i) ^ mbedtls_get_unaligned_uint64(b + i);
+        mbedtls_put_unaligned_uint64(r + i, x);
+    }
+#if defined(__IAR_SYSTEMS_ICC__)
+    /* This if statement helps some compilers (e.g., IAR) optimise out the byte-by-byte tail case
+     * where n is a constant multiple of 8.
+     * For other compilers (e.g. recent gcc and clang) it makes no difference if n is a compile-time
+     * constant, and is a very small perf regression if n is not a compile-time constant. */
+    if (n % 8 == 0) {
+        return;
+    }
+#endif
+#else
+    for (; (i + 4) <= n; i += 4) {
+        uint32_t x = mbedtls_get_unaligned_uint32(a + i) ^ mbedtls_get_unaligned_uint32(b + i);
+        mbedtls_put_unaligned_uint32(r + i, x);
+    }
+#if defined(__IAR_SYSTEMS_ICC__)
+    if (n % 4 == 0) {
+        return;
+    }
+#endif
+#endif
+#endif
+    for (; i < n; i++) {
+        r[i] = a[i] ^ b[i];
+    }
+}
+
+/* Fix MSVC C99 compatible issue
+ *      MSVC support __func__ from visual studio 2015( 1900 )
+ *      Use MSVC predefine macro to avoid name check fail.
+ */
+#if (defined(_MSC_VER) && (_MSC_VER <= 1900))
+#define /*no-check-names*/ __func__ __FUNCTION__
+#endif
+
+/* Define `asm` for compilers which don't define it. */
+/* *INDENT-OFF* */
+#ifndef asm
+#if defined(__IAR_SYSTEMS_ICC__)
+#define asm __asm
+#else
+#define asm __asm__
+#endif
+#endif
+/* *INDENT-ON* */
+
+/*
+ * Define the constraint used for read-only pointer operands to aarch64 asm.
+ *
+ * This is normally the usual "r", but for aarch64_32 (aka ILP32,
+ * as found in watchos), "p" is required to avoid warnings from clang.
+ *
+ * Note that clang does not recognise '+p' or '=p', and armclang
+ * does not recognise 'p' at all. Therefore, to update a pointer from
+ * aarch64 assembly, it is necessary to use something like:
+ *
+ * uintptr_t uptr = (uintptr_t) ptr;
+ * asm( "ldr x4, [%x0], #8" ... : "+r" (uptr) : : )
+ * ptr = (void*) uptr;
+ *
+ * Note that the "x" in "%x0" is neccessary; writing "%0" will cause warnings.
+ */
+#if defined(__aarch64__) && defined(MBEDTLS_HAVE_ASM)
+#if UINTPTR_MAX == 0xfffffffful
+/* ILP32: Specify the pointer operand slightly differently, as per #7787. */
+#define MBEDTLS_ASM_AARCH64_PTR_CONSTRAINT "p"
+#elif UINTPTR_MAX == 0xfffffffffffffffful
+/* Normal case (64-bit pointers): use "r" as the constraint for pointer operands to asm */
+#define MBEDTLS_ASM_AARCH64_PTR_CONSTRAINT "r"
+#else
+#error "Unrecognised pointer size for aarch64"
+#endif
+#endif
+
+/* Always provide a static assert macro, so it can be used unconditionally.
+ * It will expand to nothing on some systems.
+ * Can be used outside functions (but don't add a trailing ';' in that case:
+ * the semicolon is included here to avoid triggering -Wextra-semi when
+ * MBEDTLS_STATIC_ASSERT() expands to nothing).
+ * Can't use the C11-style `defined(static_assert)` on FreeBSD, since it
+ * defines static_assert even with -std=c99, but then complains about it.
+ */
+#if defined(static_assert) && !defined(__FreeBSD__)
+#define MBEDTLS_STATIC_ASSERT(expr, msg)    static_assert(expr, msg);
+#else
+#define MBEDTLS_STATIC_ASSERT(expr, msg)
+#endif
+
+#if defined(__has_builtin)
+#define MBEDTLS_HAS_BUILTIN(x) __has_builtin(x)
+#else
+#define MBEDTLS_HAS_BUILTIN(x) 0
+#endif
+
+/* Define compiler branch hints */
+#if MBEDTLS_HAS_BUILTIN(__builtin_expect)
+#define MBEDTLS_LIKELY(x)       __builtin_expect(!!(x), 1)
+#define MBEDTLS_UNLIKELY(x)     __builtin_expect(!!(x), 0)
+#else
+#define MBEDTLS_LIKELY(x)       x
+#define MBEDTLS_UNLIKELY(x)     x
+#endif
+
+/* MBEDTLS_ASSUME may be used to provide additional information to the compiler
+ * which can result in smaller code-size. */
+#if MBEDTLS_HAS_BUILTIN(__builtin_assume)
+/* clang provides __builtin_assume */
+#define MBEDTLS_ASSUME(x)       __builtin_assume(x)
+#elif MBEDTLS_HAS_BUILTIN(__builtin_unreachable)
+/* gcc and IAR can use __builtin_unreachable */
+#define MBEDTLS_ASSUME(x)       do { if (!(x)) __builtin_unreachable(); } while (0)
+#elif defined(_MSC_VER)
+/* Supported by MSVC since VS 2005 */
+#define MBEDTLS_ASSUME(x)       __assume(x)
+#else
+#define MBEDTLS_ASSUME(x)       do { } while (0)
+#endif
+
+/* For gcc -Os, override with -O2 for a given function.
+ *
+ * This will not affect behaviour for other optimisation settings, e.g. -O0.
+ */
+#if defined(MBEDTLS_COMPILER_IS_GCC) && defined(__OPTIMIZE_SIZE__)
+#define MBEDTLS_OPTIMIZE_FOR_PERFORMANCE __attribute__((optimize("-O2")))
+#else
+#define MBEDTLS_OPTIMIZE_FOR_PERFORMANCE
+#endif
+
+/* Suppress compiler warnings for unused functions and variables. */
+#if !defined(MBEDTLS_MAYBE_UNUSED) && defined(__has_attribute)
+#    if __has_attribute(unused)
+#        define MBEDTLS_MAYBE_UNUSED __attribute__((unused))
+#    endif
+#endif
+#if !defined(MBEDTLS_MAYBE_UNUSED) && defined(__GNUC__)
+#    define MBEDTLS_MAYBE_UNUSED __attribute__((unused))
+#endif
+#if !defined(MBEDTLS_MAYBE_UNUSED) && defined(__IAR_SYSTEMS_ICC__) && defined(__VER__)
+/* IAR does support __attribute__((unused)), but only if the -e flag (extended language support)
+ * is given; the pragma always works.
+ * Unfortunately the pragma affects the rest of the file where it is used, but this is harmless.
+ * Check for version 5.2 or later - this pragma may be supported by earlier versions, but I wasn't
+ * able to find documentation).
+ */
+#    if (__VER__ >= 5020000)
+#        define MBEDTLS_MAYBE_UNUSED _Pragma("diag_suppress=Pe177")
+#    endif
+#endif
+#if !defined(MBEDTLS_MAYBE_UNUSED) && defined(_MSC_VER)
+#    define MBEDTLS_MAYBE_UNUSED __pragma(warning(suppress:4189))
+#endif
+#if !defined(MBEDTLS_MAYBE_UNUSED)
+#    define MBEDTLS_MAYBE_UNUSED
+#endif
+
+#endif /* MBEDTLS_LIBRARY_COMMON_H */
diff --git a/tf-psa-crypto/core/psa_crypto.c b/tf-psa-crypto/core/psa_crypto.c
new file mode 100644
index 0000000..8100afc
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto.c
@@ -0,0 +1,9171 @@
+/*
+ *  PSA crypto layer on top of Mbed TLS crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+#include "psa_crypto_core_common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#if defined(MBEDTLS_PSA_CRYPTO_CONFIG)
+#include "check_crypto_config.h"
+#endif
+
+#include "psa/crypto.h"
+#include "psa/crypto_values.h"
+
+#include "psa_crypto_cipher.h"
+#include "psa_crypto_core.h"
+#include "psa_crypto_invasive.h"
+#include "psa_crypto_driver_wrappers.h"
+#include "psa_crypto_driver_wrappers_no_static.h"
+#include "psa_crypto_ecp.h"
+#include "psa_crypto_ffdh.h"
+#include "psa_crypto_hash.h"
+#include "psa_crypto_mac.h"
+#include "psa_crypto_rsa.h"
+#include "psa_crypto_ecp.h"
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+#include "psa_crypto_se.h"
+#endif
+#include "psa_crypto_slot_management.h"
+/* Include internal declarations that are useful for implementing persistently
+ * stored keys. */
+#include "psa_crypto_storage.h"
+
+#include "psa_crypto_random_impl.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "mbedtls/platform.h"
+
+#include "mbedtls/aes.h"
+#include "mbedtls/asn1.h"
+#include "mbedtls/asn1write.h"
+#include "mbedtls/bignum.h"
+#include "mbedtls/camellia.h"
+#include "mbedtls/chacha20.h"
+#include "mbedtls/chachapoly.h"
+#include "mbedtls/cipher.h"
+#include "mbedtls/ccm.h"
+#include "mbedtls/cmac.h"
+#include "mbedtls/constant_time.h"
+#include "mbedtls/des.h"
+#include "mbedtls/ecdh.h"
+#include "mbedtls/ecp.h"
+#include "mbedtls/entropy.h"
+#include "mbedtls/error.h"
+#include "mbedtls/gcm.h"
+#include "mbedtls/md5.h"
+#include "mbedtls/pk.h"
+#include "pk_wrap.h"
+#include "mbedtls/platform_util.h"
+#include "mbedtls/error.h"
+#include "mbedtls/ripemd160.h"
+#include "mbedtls/rsa.h"
+#include "mbedtls/sha1.h"
+#include "mbedtls/sha256.h"
+#include "mbedtls/sha512.h"
+#include "mbedtls/psa_util.h"
+#include "mbedtls/threading.h"
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF) ||          \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) ||  \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND)
+#define BUILTIN_ALG_ANY_HKDF 1
+#endif
+
+/****************************************************************/
+/* Global data, support functions and library management */
+/****************************************************************/
+
+static int key_type_is_raw_bytes(psa_key_type_t type)
+{
+    return PSA_KEY_TYPE_IS_UNSTRUCTURED(type);
+}
+
+/* Values for psa_global_data_t::rng_state */
+#define RNG_NOT_INITIALIZED 0
+#define RNG_INITIALIZED 1
+#define RNG_SEEDED 2
+
+/* IDs for PSA crypto subsystems. Starts at 1 to catch potential uninitialized
+ * variables as arguments. */
+typedef enum {
+    PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS = 1,
+    PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS,
+    PSA_CRYPTO_SUBSYSTEM_RNG,
+    PSA_CRYPTO_SUBSYSTEM_TRANSACTION,
+} mbedtls_psa_crypto_subsystem;
+
+/* Initialization flags for global_data::initialized */
+#define PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED    0x01
+#define PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED          0x02
+#define PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED        0x04
+
+#define PSA_CRYPTO_SUBSYSTEM_ALL_INITIALISED                ( \
+        PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED | \
+        PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED | \
+        PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED)
+
+typedef struct {
+    uint8_t initialized;
+    uint8_t rng_state;
+    mbedtls_psa_random_context_t rng;
+} psa_global_data_t;
+
+static psa_global_data_t global_data;
+
+static uint8_t psa_get_initialized(void)
+{
+    uint8_t initialized;
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    initialized = global_data.rng_state == RNG_SEEDED;
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    initialized =
+        (initialized && (global_data.initialized == PSA_CRYPTO_SUBSYSTEM_ALL_INITIALISED));
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    return initialized;
+}
+
+static uint8_t psa_get_drivers_initialized(void)
+{
+    uint8_t initialized;
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    initialized = (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED) != 0;
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    return initialized;
+}
+
+#define GUARD_MODULE_INITIALIZED        \
+    if (psa_get_initialized() == 0)     \
+    return PSA_ERROR_BAD_STATE;
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+
+/* Declare a local copy of an input buffer and a variable that will be used
+ * to store a pointer to the start of the buffer.
+ *
+ * Note: This macro must be called before any operations which may jump to
+ * the exit label, so that the local input copy object is safe to be freed.
+ *
+ * Assumptions:
+ * - input is the name of a pointer to the buffer to be copied
+ * - The name LOCAL_INPUT_COPY_OF_input is unused in the current scope
+ * - input_copy_name is a name that is unused in the current scope
+ */
+#define LOCAL_INPUT_DECLARE(input, input_copy_name) \
+    psa_crypto_local_input_t LOCAL_INPUT_COPY_OF_##input = PSA_CRYPTO_LOCAL_INPUT_INIT; \
+    const uint8_t *input_copy_name = NULL;
+
+/* Allocate a copy of the buffer input and set the pointer input_copy to
+ * point to the start of the copy.
+ *
+ * Assumptions:
+ * - psa_status_t status exists
+ * - An exit label is declared
+ * - input is the name of a pointer to the buffer to be copied
+ * - LOCAL_INPUT_DECLARE(input, input_copy) has previously been called
+ */
+#define LOCAL_INPUT_ALLOC(input, length, input_copy) \
+    status = psa_crypto_local_input_alloc(input, length, \
+                                          &LOCAL_INPUT_COPY_OF_##input); \
+    if (status != PSA_SUCCESS) { \
+        goto exit; \
+    } \
+    input_copy = LOCAL_INPUT_COPY_OF_##input.buffer;
+
+/* Free the local input copy allocated previously by LOCAL_INPUT_ALLOC()
+ *
+ * Assumptions:
+ * - input_copy is the name of the input copy pointer set by LOCAL_INPUT_ALLOC()
+ * - input is the name of the original buffer that was copied
+ */
+#define LOCAL_INPUT_FREE(input, input_copy) \
+    input_copy = NULL; \
+    psa_crypto_local_input_free(&LOCAL_INPUT_COPY_OF_##input);
+
+/* Declare a local copy of an output buffer and a variable that will be used
+ * to store a pointer to the start of the buffer.
+ *
+ * Note: This macro must be called before any operations which may jump to
+ * the exit label, so that the local output copy object is safe to be freed.
+ *
+ * Assumptions:
+ * - output is the name of a pointer to the buffer to be copied
+ * - The name LOCAL_OUTPUT_COPY_OF_output is unused in the current scope
+ * - output_copy_name is a name that is unused in the current scope
+ */
+#define LOCAL_OUTPUT_DECLARE(output, output_copy_name) \
+    psa_crypto_local_output_t LOCAL_OUTPUT_COPY_OF_##output = PSA_CRYPTO_LOCAL_OUTPUT_INIT; \
+    uint8_t *output_copy_name = NULL;
+
+/* Allocate a copy of the buffer output and set the pointer output_copy to
+ * point to the start of the copy.
+ *
+ * Assumptions:
+ * - psa_status_t status exists
+ * - An exit label is declared
+ * - output is the name of a pointer to the buffer to be copied
+ * - LOCAL_OUTPUT_DECLARE(output, output_copy) has previously been called
+ */
+#define LOCAL_OUTPUT_ALLOC(output, length, output_copy) \
+    status = psa_crypto_local_output_alloc(output, length, \
+                                           &LOCAL_OUTPUT_COPY_OF_##output); \
+    if (status != PSA_SUCCESS) { \
+        goto exit; \
+    } \
+    output_copy = LOCAL_OUTPUT_COPY_OF_##output.buffer;
+
+/* Free the local output copy allocated previously by LOCAL_OUTPUT_ALLOC()
+ * after first copying back its contents to the original buffer.
+ *
+ * Assumptions:
+ * - psa_status_t status exists
+ * - output_copy is the name of the output copy pointer set by LOCAL_OUTPUT_ALLOC()
+ * - output is the name of the original buffer that was copied
+ */
+#define LOCAL_OUTPUT_FREE(output, output_copy) \
+    output_copy = NULL; \
+    do { \
+        psa_status_t local_output_status; \
+        local_output_status = psa_crypto_local_output_free(&LOCAL_OUTPUT_COPY_OF_##output); \
+        if (local_output_status != PSA_SUCCESS) { \
+            /* Since this error case is an internal error, it's more serious than \
+             * any existing error code and so it's fine to overwrite the existing \
+             * status. */ \
+            status = local_output_status; \
+        } \
+    } while (0)
+#else /* !MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS */
+#define LOCAL_INPUT_DECLARE(input, input_copy_name) \
+    const uint8_t *input_copy_name = NULL;
+#define LOCAL_INPUT_ALLOC(input, length, input_copy) \
+    input_copy = input;
+#define LOCAL_INPUT_FREE(input, input_copy) \
+    input_copy = NULL;
+#define LOCAL_OUTPUT_DECLARE(output, output_copy_name) \
+    uint8_t *output_copy_name = NULL;
+#define LOCAL_OUTPUT_ALLOC(output, length, output_copy) \
+    output_copy = output;
+#define LOCAL_OUTPUT_FREE(output, output_copy) \
+    output_copy = NULL;
+#endif /* !MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS */
+
+
+int psa_can_do_hash(psa_algorithm_t hash_alg)
+{
+    (void) hash_alg;
+    return psa_get_drivers_initialized();
+}
+
+int psa_can_do_cipher(psa_key_type_t key_type, psa_algorithm_t cipher_alg)
+{
+    (void) key_type;
+    (void) cipher_alg;
+    return psa_get_drivers_initialized();
+}
+
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_IMPORT) ||       \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY) ||     \
+    defined(PSA_WANT_KEY_TYPE_DH_KEY_PAIR_GENERATE)
+static int psa_is_dh_key_size_valid(size_t bits)
+{
+    switch (bits) {
+#if defined(PSA_WANT_DH_RFC7919_2048)
+        case 2048:
+            return 1;
+#endif /* PSA_WANT_DH_RFC7919_2048 */
+#if defined(PSA_WANT_DH_RFC7919_3072)
+        case 3072:
+            return 1;
+#endif /* PSA_WANT_DH_RFC7919_3072 */
+#if defined(PSA_WANT_DH_RFC7919_4096)
+        case 4096:
+            return 1;
+#endif /* PSA_WANT_DH_RFC7919_4096 */
+#if defined(PSA_WANT_DH_RFC7919_6144)
+        case 6144:
+            return 1;
+#endif /* PSA_WANT_DH_RFC7919_6144 */
+#if defined(PSA_WANT_DH_RFC7919_8192)
+        case 8192:
+            return 1;
+#endif /* PSA_WANT_DH_RFC7919_8192 */
+        default:
+            return 0;
+    }
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_IMPORT ||
+          MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY ||
+          PSA_WANT_KEY_TYPE_DH_KEY_PAIR_GENERATE */
+
+psa_status_t mbedtls_to_psa_error(int ret)
+{
+    /* Mbed TLS error codes can combine a high-level error code and a
+     * low-level error code. The low-level error usually reflects the
+     * root cause better, so dispatch on that preferably. */
+    int low_level_ret = -(-ret & 0x007f);
+    switch (low_level_ret != 0 ? low_level_ret : ret) {
+        case 0:
+            return PSA_SUCCESS;
+
+#if defined(MBEDTLS_AES_C)
+        case MBEDTLS_ERR_AES_INVALID_KEY_LENGTH:
+        case MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH:
+            return PSA_ERROR_NOT_SUPPORTED;
+        case MBEDTLS_ERR_AES_BAD_INPUT_DATA:
+            return PSA_ERROR_INVALID_ARGUMENT;
+#endif
+
+#if defined(MBEDTLS_ASN1_PARSE_C) || defined(MBEDTLS_ASN1_WRITE_C)
+        case MBEDTLS_ERR_ASN1_OUT_OF_DATA:
+        case MBEDTLS_ERR_ASN1_UNEXPECTED_TAG:
+        case MBEDTLS_ERR_ASN1_INVALID_LENGTH:
+        case MBEDTLS_ERR_ASN1_LENGTH_MISMATCH:
+        case MBEDTLS_ERR_ASN1_INVALID_DATA:
+            return PSA_ERROR_INVALID_ARGUMENT;
+        case MBEDTLS_ERR_ASN1_ALLOC_FAILED:
+            return PSA_ERROR_INSUFFICIENT_MEMORY;
+        case MBEDTLS_ERR_ASN1_BUF_TOO_SMALL:
+            return PSA_ERROR_BUFFER_TOO_SMALL;
+#endif
+
+#if defined(MBEDTLS_CAMELLIA_C)
+        case MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA:
+        case MBEDTLS_ERR_CAMELLIA_INVALID_INPUT_LENGTH:
+            return PSA_ERROR_NOT_SUPPORTED;
+#endif
+
+#if defined(MBEDTLS_CCM_C)
+        case MBEDTLS_ERR_CCM_BAD_INPUT:
+            return PSA_ERROR_INVALID_ARGUMENT;
+        case MBEDTLS_ERR_CCM_AUTH_FAILED:
+            return PSA_ERROR_INVALID_SIGNATURE;
+#endif
+
+#if defined(MBEDTLS_CHACHA20_C)
+        case MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA:
+            return PSA_ERROR_INVALID_ARGUMENT;
+#endif
+
+#if defined(MBEDTLS_CHACHAPOLY_C)
+        case MBEDTLS_ERR_CHACHAPOLY_BAD_STATE:
+            return PSA_ERROR_BAD_STATE;
+        case MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED:
+            return PSA_ERROR_INVALID_SIGNATURE;
+#endif
+
+#if defined(MBEDTLS_CIPHER_C)
+        case MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE:
+            return PSA_ERROR_NOT_SUPPORTED;
+        case MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA:
+            return PSA_ERROR_INVALID_ARGUMENT;
+        case MBEDTLS_ERR_CIPHER_ALLOC_FAILED:
+            return PSA_ERROR_INSUFFICIENT_MEMORY;
+        case MBEDTLS_ERR_CIPHER_INVALID_PADDING:
+            return PSA_ERROR_INVALID_PADDING;
+        case MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED:
+            return PSA_ERROR_INVALID_ARGUMENT;
+        case MBEDTLS_ERR_CIPHER_AUTH_FAILED:
+            return PSA_ERROR_INVALID_SIGNATURE;
+        case MBEDTLS_ERR_CIPHER_INVALID_CONTEXT:
+            return PSA_ERROR_CORRUPTION_DETECTED;
+#endif
+
+#if !(defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) ||      \
+            defined(MBEDTLS_PSA_HMAC_DRBG_MD_TYPE))
+        /* Only check CTR_DRBG error codes if underlying mbedtls_xxx
+         * functions are passed a CTR_DRBG instance. */
+        case MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED:
+            return PSA_ERROR_INSUFFICIENT_ENTROPY;
+        case MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG:
+        case MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG:
+            return PSA_ERROR_NOT_SUPPORTED;
+        case MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR:
+            return PSA_ERROR_INSUFFICIENT_ENTROPY;
+#endif
+
+#if defined(MBEDTLS_DES_C)
+        case MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH:
+            return PSA_ERROR_NOT_SUPPORTED;
+#endif
+
+        case MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED:
+        case MBEDTLS_ERR_ENTROPY_NO_STRONG_SOURCE:
+        case MBEDTLS_ERR_ENTROPY_SOURCE_FAILED:
+            return PSA_ERROR_INSUFFICIENT_ENTROPY;
+
+#if defined(MBEDTLS_GCM_C)
+        case MBEDTLS_ERR_GCM_AUTH_FAILED:
+            return PSA_ERROR_INVALID_SIGNATURE;
+        case MBEDTLS_ERR_GCM_BUFFER_TOO_SMALL:
+            return PSA_ERROR_BUFFER_TOO_SMALL;
+        case MBEDTLS_ERR_GCM_BAD_INPUT:
+            return PSA_ERROR_INVALID_ARGUMENT;
+#endif
+
+#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) &&        \
+            defined(MBEDTLS_PSA_HMAC_DRBG_MD_TYPE)
+        /* Only check HMAC_DRBG error codes if underlying mbedtls_xxx
+         * functions are passed a HMAC_DRBG instance. */
+        case MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED:
+            return PSA_ERROR_INSUFFICIENT_ENTROPY;
+        case MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG:
+        case MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG:
+            return PSA_ERROR_NOT_SUPPORTED;
+        case MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR:
+            return PSA_ERROR_INSUFFICIENT_ENTROPY;
+#endif
+
+#if defined(MBEDTLS_MD_LIGHT)
+        case MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE:
+            return PSA_ERROR_NOT_SUPPORTED;
+        case MBEDTLS_ERR_MD_BAD_INPUT_DATA:
+            return PSA_ERROR_INVALID_ARGUMENT;
+        case MBEDTLS_ERR_MD_ALLOC_FAILED:
+            return PSA_ERROR_INSUFFICIENT_MEMORY;
+#if defined(MBEDTLS_FS_IO)
+        case MBEDTLS_ERR_MD_FILE_IO_ERROR:
+            return PSA_ERROR_STORAGE_FAILURE;
+#endif
+#endif
+
+#if defined(MBEDTLS_BIGNUM_C)
+#if defined(MBEDTLS_FS_IO)
+        case MBEDTLS_ERR_MPI_FILE_IO_ERROR:
+            return PSA_ERROR_STORAGE_FAILURE;
+#endif
+        case MBEDTLS_ERR_MPI_BAD_INPUT_DATA:
+            return PSA_ERROR_INVALID_ARGUMENT;
+        case MBEDTLS_ERR_MPI_INVALID_CHARACTER:
+            return PSA_ERROR_INVALID_ARGUMENT;
+        case MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL:
+            return PSA_ERROR_BUFFER_TOO_SMALL;
+        case MBEDTLS_ERR_MPI_NEGATIVE_VALUE:
+            return PSA_ERROR_INVALID_ARGUMENT;
+        case MBEDTLS_ERR_MPI_DIVISION_BY_ZERO:
+            return PSA_ERROR_INVALID_ARGUMENT;
+        case MBEDTLS_ERR_MPI_NOT_ACCEPTABLE:
+            return PSA_ERROR_INVALID_ARGUMENT;
+        case MBEDTLS_ERR_MPI_ALLOC_FAILED:
+            return PSA_ERROR_INSUFFICIENT_MEMORY;
+#endif
+
+#if defined(MBEDTLS_PK_C)
+        case MBEDTLS_ERR_PK_ALLOC_FAILED:
+            return PSA_ERROR_INSUFFICIENT_MEMORY;
+        case MBEDTLS_ERR_PK_TYPE_MISMATCH:
+        case MBEDTLS_ERR_PK_BAD_INPUT_DATA:
+            return PSA_ERROR_INVALID_ARGUMENT;
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) || defined(MBEDTLS_FS_IO) || \
+            defined(MBEDTLS_PSA_ITS_FILE_C)
+        case MBEDTLS_ERR_PK_FILE_IO_ERROR:
+            return PSA_ERROR_STORAGE_FAILURE;
+#endif
+        case MBEDTLS_ERR_PK_KEY_INVALID_VERSION:
+        case MBEDTLS_ERR_PK_KEY_INVALID_FORMAT:
+            return PSA_ERROR_INVALID_ARGUMENT;
+        case MBEDTLS_ERR_PK_UNKNOWN_PK_ALG:
+            return PSA_ERROR_NOT_SUPPORTED;
+        case MBEDTLS_ERR_PK_PASSWORD_REQUIRED:
+        case MBEDTLS_ERR_PK_PASSWORD_MISMATCH:
+            return PSA_ERROR_NOT_PERMITTED;
+        case MBEDTLS_ERR_PK_INVALID_PUBKEY:
+            return PSA_ERROR_INVALID_ARGUMENT;
+        case MBEDTLS_ERR_PK_INVALID_ALG:
+        case MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE:
+        case MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE:
+            return PSA_ERROR_NOT_SUPPORTED;
+        case MBEDTLS_ERR_PK_SIG_LEN_MISMATCH:
+            return PSA_ERROR_INVALID_SIGNATURE;
+        case MBEDTLS_ERR_PK_BUFFER_TOO_SMALL:
+            return PSA_ERROR_BUFFER_TOO_SMALL;
+#endif
+
+        case MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED:
+            return PSA_ERROR_HARDWARE_FAILURE;
+        case MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED:
+            return PSA_ERROR_NOT_SUPPORTED;
+
+#if defined(MBEDTLS_RSA_C)
+        case MBEDTLS_ERR_RSA_BAD_INPUT_DATA:
+            return PSA_ERROR_INVALID_ARGUMENT;
+        case MBEDTLS_ERR_RSA_INVALID_PADDING:
+            return PSA_ERROR_INVALID_PADDING;
+        case MBEDTLS_ERR_RSA_KEY_GEN_FAILED:
+            return PSA_ERROR_HARDWARE_FAILURE;
+        case MBEDTLS_ERR_RSA_KEY_CHECK_FAILED:
+            return PSA_ERROR_INVALID_ARGUMENT;
+        case MBEDTLS_ERR_RSA_PUBLIC_FAILED:
+        case MBEDTLS_ERR_RSA_PRIVATE_FAILED:
+            return PSA_ERROR_CORRUPTION_DETECTED;
+        case MBEDTLS_ERR_RSA_VERIFY_FAILED:
+            return PSA_ERROR_INVALID_SIGNATURE;
+        case MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE:
+            return PSA_ERROR_BUFFER_TOO_SMALL;
+        case MBEDTLS_ERR_RSA_RNG_FAILED:
+            return PSA_ERROR_INSUFFICIENT_ENTROPY;
+#endif
+
+#if defined(MBEDTLS_ECP_LIGHT)
+        case MBEDTLS_ERR_ECP_BAD_INPUT_DATA:
+        case MBEDTLS_ERR_ECP_INVALID_KEY:
+            return PSA_ERROR_INVALID_ARGUMENT;
+        case MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL:
+            return PSA_ERROR_BUFFER_TOO_SMALL;
+        case MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE:
+            return PSA_ERROR_NOT_SUPPORTED;
+        case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:
+        case MBEDTLS_ERR_ECP_VERIFY_FAILED:
+            return PSA_ERROR_INVALID_SIGNATURE;
+        case MBEDTLS_ERR_ECP_ALLOC_FAILED:
+            return PSA_ERROR_INSUFFICIENT_MEMORY;
+        case MBEDTLS_ERR_ECP_RANDOM_FAILED:
+            return PSA_ERROR_INSUFFICIENT_ENTROPY;
+
+#if defined(MBEDTLS_ECP_RESTARTABLE)
+        case MBEDTLS_ERR_ECP_IN_PROGRESS:
+            return PSA_OPERATION_INCOMPLETE;
+#endif
+#endif
+
+        case MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED:
+            return PSA_ERROR_CORRUPTION_DETECTED;
+
+        default:
+            return PSA_ERROR_GENERIC_ERROR;
+    }
+}
+
+/**
+ * \brief                       For output buffers which contain "tags"
+ *                              (outputs that may be checked for validity like
+ *                              hashes, MACs and signatures), fill the unused
+ *                              part of the output buffer (the whole buffer on
+ *                              error, the trailing part on success) with
+ *                              something that isn't a valid tag (barring an
+ *                              attack on the tag and deliberately-crafted
+ *                              input), in case the caller doesn't check the
+ *                              return status properly.
+ *
+ * \param output_buffer         Pointer to buffer to wipe. May not be NULL
+ *                              unless \p output_buffer_size is zero.
+ * \param status                Status of function called to generate
+ *                              output_buffer originally
+ * \param output_buffer_size    Size of output buffer. If zero, \p output_buffer
+ *                              could be NULL.
+ * \param output_buffer_length  Length of data written to output_buffer, must be
+ *                              less than \p output_buffer_size
+ */
+static void psa_wipe_tag_output_buffer(uint8_t *output_buffer, psa_status_t status,
+                                       size_t output_buffer_size, size_t output_buffer_length)
+{
+    size_t offset = 0;
+
+    if (output_buffer_size == 0) {
+        /* If output_buffer_size is 0 then we have nothing to do. We must not
+           call memset because output_buffer may be NULL in this case */
+        return;
+    }
+
+    if (status == PSA_SUCCESS) {
+        offset = output_buffer_length;
+    }
+
+    memset(output_buffer + offset, '!', output_buffer_size - offset);
+}
+
+
+psa_status_t psa_validate_unstructured_key_bit_size(psa_key_type_t type,
+                                                    size_t bits)
+{
+    /* Check that the bit size is acceptable for the key type */
+    switch (type) {
+        case PSA_KEY_TYPE_RAW_DATA:
+        case PSA_KEY_TYPE_HMAC:
+        case PSA_KEY_TYPE_DERIVE:
+        case PSA_KEY_TYPE_PASSWORD:
+        case PSA_KEY_TYPE_PASSWORD_HASH:
+            break;
+#if defined(PSA_WANT_KEY_TYPE_AES)
+        case PSA_KEY_TYPE_AES:
+            if (bits != 128 && bits != 192 && bits != 256) {
+                return PSA_ERROR_INVALID_ARGUMENT;
+            }
+            break;
+#endif
+#if defined(PSA_WANT_KEY_TYPE_ARIA)
+        case PSA_KEY_TYPE_ARIA:
+            if (bits != 128 && bits != 192 && bits != 256) {
+                return PSA_ERROR_INVALID_ARGUMENT;
+            }
+            break;
+#endif
+#if defined(PSA_WANT_KEY_TYPE_CAMELLIA)
+        case PSA_KEY_TYPE_CAMELLIA:
+            if (bits != 128 && bits != 192 && bits != 256) {
+                return PSA_ERROR_INVALID_ARGUMENT;
+            }
+            break;
+#endif
+#if defined(PSA_WANT_KEY_TYPE_DES)
+        case PSA_KEY_TYPE_DES:
+            if (bits != 64 && bits != 128 && bits != 192) {
+                return PSA_ERROR_INVALID_ARGUMENT;
+            }
+            break;
+#endif
+#if defined(PSA_WANT_KEY_TYPE_CHACHA20)
+        case PSA_KEY_TYPE_CHACHA20:
+            if (bits != 256) {
+                return PSA_ERROR_INVALID_ARGUMENT;
+            }
+            break;
+#endif
+        default:
+            return PSA_ERROR_NOT_SUPPORTED;
+    }
+    if (bits % 8 != 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    return PSA_SUCCESS;
+}
+
+/** Check whether a given key type is valid for use with a given MAC algorithm
+ *
+ * Upon successful return of this function, the behavior of #PSA_MAC_LENGTH
+ * when called with the validated \p algorithm and \p key_type is well-defined.
+ *
+ * \param[in] algorithm     The specific MAC algorithm (can be wildcard).
+ * \param[in] key_type      The key type of the key to be used with the
+ *                          \p algorithm.
+ *
+ * \retval #PSA_SUCCESS
+ *         The \p key_type is valid for use with the \p algorithm
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         The \p key_type is not valid for use with the \p algorithm
+ */
+MBEDTLS_STATIC_TESTABLE psa_status_t psa_mac_key_can_do(
+    psa_algorithm_t algorithm,
+    psa_key_type_t key_type)
+{
+    if (PSA_ALG_IS_HMAC(algorithm)) {
+        if (key_type == PSA_KEY_TYPE_HMAC) {
+            return PSA_SUCCESS;
+        }
+    }
+
+    if (PSA_ALG_IS_BLOCK_CIPHER_MAC(algorithm)) {
+        /* Check that we're calling PSA_BLOCK_CIPHER_BLOCK_LENGTH with a cipher
+         * key. */
+        if ((key_type & PSA_KEY_TYPE_CATEGORY_MASK) ==
+            PSA_KEY_TYPE_CATEGORY_SYMMETRIC) {
+            /* PSA_BLOCK_CIPHER_BLOCK_LENGTH returns 1 for stream ciphers and
+             * the block length (larger than 1) for block ciphers. */
+            if (PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) > 1) {
+                return PSA_SUCCESS;
+            }
+        }
+    }
+
+    return PSA_ERROR_INVALID_ARGUMENT;
+}
+
+psa_status_t psa_allocate_buffer_to_slot(psa_key_slot_t *slot,
+                                         size_t buffer_length)
+{
+    if (slot->key.data != NULL) {
+        return PSA_ERROR_ALREADY_EXISTS;
+    }
+
+    slot->key.data = mbedtls_calloc(1, buffer_length);
+    if (slot->key.data == NULL) {
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+
+    slot->key.bytes = buffer_length;
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_copy_key_material_into_slot(psa_key_slot_t *slot,
+                                             const uint8_t *data,
+                                             size_t data_length)
+{
+    psa_status_t status = psa_allocate_buffer_to_slot(slot,
+                                                      data_length);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    memcpy(slot->key.data, data, data_length);
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_import_key_into_slot(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *data, size_t data_length,
+    uint8_t *key_buffer, size_t key_buffer_size,
+    size_t *key_buffer_length, size_t *bits)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_type_t type = attributes->type;
+
+    /* zero-length keys are never supported. */
+    if (data_length == 0) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    if (key_type_is_raw_bytes(type)) {
+        *bits = PSA_BYTES_TO_BITS(data_length);
+
+        status = psa_validate_unstructured_key_bit_size(attributes->type,
+                                                        *bits);
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+
+        /* Copy the key material. */
+        memcpy(key_buffer, data, data_length);
+        *key_buffer_length = data_length;
+        (void) key_buffer_size;
+
+        return PSA_SUCCESS;
+    } else if (PSA_KEY_TYPE_IS_ASYMMETRIC(type)) {
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_IMPORT) || \
+        defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY)
+        if (PSA_KEY_TYPE_IS_DH(type)) {
+            if (psa_is_dh_key_size_valid(PSA_BYTES_TO_BITS(data_length)) == 0) {
+                return PSA_ERROR_NOT_SUPPORTED;
+            }
+            return mbedtls_psa_ffdh_import_key(attributes,
+                                               data, data_length,
+                                               key_buffer, key_buffer_size,
+                                               key_buffer_length,
+                                               bits);
+        }
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_IMPORT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY) */
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_IMPORT) || \
+        defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY)
+        if (PSA_KEY_TYPE_IS_ECC(type)) {
+            return mbedtls_psa_ecp_import_key(attributes,
+                                              data, data_length,
+                                              key_buffer, key_buffer_size,
+                                              key_buffer_length,
+                                              bits);
+        }
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_IMPORT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) */
+#if (defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) && \
+        defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT)) || \
+        defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY)
+        if (PSA_KEY_TYPE_IS_RSA(type)) {
+            return mbedtls_psa_rsa_import_key(attributes,
+                                              data, data_length,
+                                              key_buffer, key_buffer_size,
+                                              key_buffer_length,
+                                              bits);
+        }
+#endif /* (defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) &&
+           defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT)) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */
+    }
+
+    return PSA_ERROR_NOT_SUPPORTED;
+}
+
+/** Calculate the intersection of two algorithm usage policies.
+ *
+ * Return 0 (which allows no operation) on incompatibility.
+ */
+static psa_algorithm_t psa_key_policy_algorithm_intersection(
+    psa_key_type_t key_type,
+    psa_algorithm_t alg1,
+    psa_algorithm_t alg2)
+{
+    /* Common case: both sides actually specify the same policy. */
+    if (alg1 == alg2) {
+        return alg1;
+    }
+    /* If the policies are from the same hash-and-sign family, check
+     * if one is a wildcard. If so the other has the specific algorithm. */
+    if (PSA_ALG_IS_SIGN_HASH(alg1) &&
+        PSA_ALG_IS_SIGN_HASH(alg2) &&
+        (alg1 & ~PSA_ALG_HASH_MASK) == (alg2 & ~PSA_ALG_HASH_MASK)) {
+        if (PSA_ALG_SIGN_GET_HASH(alg1) == PSA_ALG_ANY_HASH) {
+            return alg2;
+        }
+        if (PSA_ALG_SIGN_GET_HASH(alg2) == PSA_ALG_ANY_HASH) {
+            return alg1;
+        }
+    }
+    /* If the policies are from the same AEAD family, check whether
+     * one of them is a minimum-tag-length wildcard. Calculate the most
+     * restrictive tag length. */
+    if (PSA_ALG_IS_AEAD(alg1) && PSA_ALG_IS_AEAD(alg2) &&
+        (PSA_ALG_AEAD_WITH_SHORTENED_TAG(alg1, 0) ==
+         PSA_ALG_AEAD_WITH_SHORTENED_TAG(alg2, 0))) {
+        size_t alg1_len = PSA_ALG_AEAD_GET_TAG_LENGTH(alg1);
+        size_t alg2_len = PSA_ALG_AEAD_GET_TAG_LENGTH(alg2);
+        size_t restricted_len = alg1_len > alg2_len ? alg1_len : alg2_len;
+
+        /* If both are wildcards, return most restrictive wildcard */
+        if (((alg1 & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0) &&
+            ((alg2 & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0)) {
+            return PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(
+                alg1, restricted_len);
+        }
+        /* If only one is a wildcard, return specific algorithm if compatible. */
+        if (((alg1 & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0) &&
+            (alg1_len <= alg2_len)) {
+            return alg2;
+        }
+        if (((alg2 & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0) &&
+            (alg2_len <= alg1_len)) {
+            return alg1;
+        }
+    }
+    /* If the policies are from the same MAC family, check whether one
+     * of them is a minimum-MAC-length policy. Calculate the most
+     * restrictive tag length. */
+    if (PSA_ALG_IS_MAC(alg1) && PSA_ALG_IS_MAC(alg2) &&
+        (PSA_ALG_FULL_LENGTH_MAC(alg1) ==
+         PSA_ALG_FULL_LENGTH_MAC(alg2))) {
+        /* Validate the combination of key type and algorithm. Since the base
+         * algorithm of alg1 and alg2 are the same, we only need this once. */
+        if (PSA_SUCCESS != psa_mac_key_can_do(alg1, key_type)) {
+            return 0;
+        }
+
+        /* Get the (exact or at-least) output lengths for both sides of the
+         * requested intersection. None of the currently supported algorithms
+         * have an output length dependent on the actual key size, so setting it
+         * to a bogus value of 0 is currently OK.
+         *
+         * Note that for at-least-this-length wildcard algorithms, the output
+         * length is set to the shortest allowed length, which allows us to
+         * calculate the most restrictive tag length for the intersection. */
+        size_t alg1_len = PSA_MAC_LENGTH(key_type, 0, alg1);
+        size_t alg2_len = PSA_MAC_LENGTH(key_type, 0, alg2);
+        size_t restricted_len = alg1_len > alg2_len ? alg1_len : alg2_len;
+
+        /* If both are wildcards, return most restrictive wildcard */
+        if (((alg1 & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) &&
+            ((alg2 & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0)) {
+            return PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(alg1, restricted_len);
+        }
+
+        /* If only one is an at-least-this-length policy, the intersection would
+         * be the other (fixed-length) policy as long as said fixed length is
+         * equal to or larger than the shortest allowed length. */
+        if ((alg1 & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) {
+            return (alg1_len <= alg2_len) ? alg2 : 0;
+        }
+        if ((alg2 & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) {
+            return (alg2_len <= alg1_len) ? alg1 : 0;
+        }
+
+        /* If none of them are wildcards, check whether they define the same tag
+         * length. This is still possible here when one is default-length and
+         * the other specific-length. Ensure to always return the
+         * specific-length version for the intersection. */
+        if (alg1_len == alg2_len) {
+            return PSA_ALG_TRUNCATED_MAC(alg1, alg1_len);
+        }
+    }
+    /* If the policies are incompatible, allow nothing. */
+    return 0;
+}
+
+static int psa_key_algorithm_permits(psa_key_type_t key_type,
+                                     psa_algorithm_t policy_alg,
+                                     psa_algorithm_t requested_alg)
+{
+    /* Common case: the policy only allows requested_alg. */
+    if (requested_alg == policy_alg) {
+        return 1;
+    }
+    /* If policy_alg is a hash-and-sign with a wildcard for the hash,
+     * and requested_alg is the same hash-and-sign family with any hash,
+     * then requested_alg is compliant with policy_alg. */
+    if (PSA_ALG_IS_SIGN_HASH(requested_alg) &&
+        PSA_ALG_SIGN_GET_HASH(policy_alg) == PSA_ALG_ANY_HASH) {
+        return (policy_alg & ~PSA_ALG_HASH_MASK) ==
+               (requested_alg & ~PSA_ALG_HASH_MASK);
+    }
+    /* If policy_alg is a wildcard AEAD algorithm of the same base as
+     * the requested algorithm, check the requested tag length to be
+     * equal-length or longer than the wildcard-specified length. */
+    if (PSA_ALG_IS_AEAD(policy_alg) &&
+        PSA_ALG_IS_AEAD(requested_alg) &&
+        (PSA_ALG_AEAD_WITH_SHORTENED_TAG(policy_alg, 0) ==
+         PSA_ALG_AEAD_WITH_SHORTENED_TAG(requested_alg, 0)) &&
+        ((policy_alg & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0)) {
+        return PSA_ALG_AEAD_GET_TAG_LENGTH(policy_alg) <=
+               PSA_ALG_AEAD_GET_TAG_LENGTH(requested_alg);
+    }
+    /* If policy_alg is a MAC algorithm of the same base as the requested
+     * algorithm, check whether their MAC lengths are compatible. */
+    if (PSA_ALG_IS_MAC(policy_alg) &&
+        PSA_ALG_IS_MAC(requested_alg) &&
+        (PSA_ALG_FULL_LENGTH_MAC(policy_alg) ==
+         PSA_ALG_FULL_LENGTH_MAC(requested_alg))) {
+        /* Validate the combination of key type and algorithm. Since the policy
+         * and requested algorithms are the same, we only need this once. */
+        if (PSA_SUCCESS != psa_mac_key_can_do(policy_alg, key_type)) {
+            return 0;
+        }
+
+        /* Get both the requested output length for the algorithm which is to be
+         * verified, and the default output length for the base algorithm.
+         * Note that none of the currently supported algorithms have an output
+         * length dependent on actual key size, so setting it to a bogus value
+         * of 0 is currently OK. */
+        size_t requested_output_length = PSA_MAC_LENGTH(
+            key_type, 0, requested_alg);
+        size_t default_output_length = PSA_MAC_LENGTH(
+            key_type, 0,
+            PSA_ALG_FULL_LENGTH_MAC(requested_alg));
+
+        /* If the policy is default-length, only allow an algorithm with
+         * a declared exact-length matching the default. */
+        if (PSA_MAC_TRUNCATED_LENGTH(policy_alg) == 0) {
+            return requested_output_length == default_output_length;
+        }
+
+        /* If the requested algorithm is default-length, allow it if the policy
+         * length exactly matches the default length. */
+        if (PSA_MAC_TRUNCATED_LENGTH(requested_alg) == 0 &&
+            PSA_MAC_TRUNCATED_LENGTH(policy_alg) == default_output_length) {
+            return 1;
+        }
+
+        /* If policy_alg is an at-least-this-length wildcard MAC algorithm,
+         * check for the requested MAC length to be equal to or longer than the
+         * minimum allowed length. */
+        if ((policy_alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) {
+            return PSA_MAC_TRUNCATED_LENGTH(policy_alg) <=
+                   requested_output_length;
+        }
+    }
+    /* If policy_alg is a generic key agreement operation, then using it for
+     * a key derivation with that key agreement should also be allowed. This
+     * behaviour is expected to be defined in a future specification version. */
+    if (PSA_ALG_IS_RAW_KEY_AGREEMENT(policy_alg) &&
+        PSA_ALG_IS_KEY_AGREEMENT(requested_alg)) {
+        return PSA_ALG_KEY_AGREEMENT_GET_BASE(requested_alg) ==
+               policy_alg;
+    }
+    /* If it isn't explicitly permitted, it's forbidden. */
+    return 0;
+}
+
+/** Test whether a policy permits an algorithm.
+ *
+ * The caller must test usage flags separately.
+ *
+ * \note This function requires providing the key type for which the policy is
+ *       being validated, since some algorithm policy definitions (e.g. MAC)
+ *       have different properties depending on what kind of cipher it is
+ *       combined with.
+ *
+ * \retval PSA_SUCCESS                  When \p alg is a specific algorithm
+ *                                      allowed by the \p policy.
+ * \retval PSA_ERROR_INVALID_ARGUMENT   When \p alg is not a specific algorithm
+ * \retval PSA_ERROR_NOT_PERMITTED      When \p alg is a specific algorithm, but
+ *                                      the \p policy does not allow it.
+ */
+static psa_status_t psa_key_policy_permits(const psa_key_policy_t *policy,
+                                           psa_key_type_t key_type,
+                                           psa_algorithm_t alg)
+{
+    /* '0' is not a valid algorithm */
+    if (alg == 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* A requested algorithm cannot be a wildcard. */
+    if (PSA_ALG_IS_WILDCARD(alg)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (psa_key_algorithm_permits(key_type, policy->alg, alg) ||
+        psa_key_algorithm_permits(key_type, policy->alg2, alg)) {
+        return PSA_SUCCESS;
+    } else {
+        return PSA_ERROR_NOT_PERMITTED;
+    }
+}
+
+/** Restrict a key policy based on a constraint.
+ *
+ * \note This function requires providing the key type for which the policy is
+ *       being restricted, since some algorithm policy definitions (e.g. MAC)
+ *       have different properties depending on what kind of cipher it is
+ *       combined with.
+ *
+ * \param[in] key_type      The key type for which to restrict the policy
+ * \param[in,out] policy    The policy to restrict.
+ * \param[in] constraint    The policy constraint to apply.
+ *
+ * \retval #PSA_SUCCESS
+ *         \c *policy contains the intersection of the original value of
+ *         \c *policy and \c *constraint.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         \c key_type, \c *policy and \c *constraint are incompatible.
+ *         \c *policy is unchanged.
+ */
+static psa_status_t psa_restrict_key_policy(
+    psa_key_type_t key_type,
+    psa_key_policy_t *policy,
+    const psa_key_policy_t *constraint)
+{
+    psa_algorithm_t intersection_alg =
+        psa_key_policy_algorithm_intersection(key_type, policy->alg,
+                                              constraint->alg);
+    psa_algorithm_t intersection_alg2 =
+        psa_key_policy_algorithm_intersection(key_type, policy->alg2,
+                                              constraint->alg2);
+    if (intersection_alg == 0 && policy->alg != 0 && constraint->alg != 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+    if (intersection_alg2 == 0 && policy->alg2 != 0 && constraint->alg2 != 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+    policy->usage &= constraint->usage;
+    policy->alg = intersection_alg;
+    policy->alg2 = intersection_alg2;
+    return PSA_SUCCESS;
+}
+
+/** Get the description of a key given its identifier and policy constraints
+ *  and lock it.
+ *
+ * The key must have allow all the usage flags set in \p usage. If \p alg is
+ * nonzero, the key must allow operations with this algorithm. If \p alg is
+ * zero, the algorithm is not checked.
+ *
+ * In case of a persistent key, the function loads the description of the key
+ * into a key slot if not already done.
+ *
+ * On success, the returned key slot has been registered for reading.
+ * It is the responsibility of the caller to then unregister
+ * once they have finished reading the contents of the slot.
+ * The caller unregisters by calling psa_unregister_read() or
+ * psa_unregister_read_under_mutex(). psa_unregister_read() must be called
+ * if and only if the caller already holds the global key slot mutex
+ * (when mutexes are enabled). psa_unregister_read_under_mutex() encapsulates
+ * the unregister with mutex lock and unlock operations.
+ */
+static psa_status_t psa_get_and_lock_key_slot_with_policy(
+    mbedtls_svc_key_id_t key,
+    psa_key_slot_t **p_slot,
+    psa_key_usage_t usage,
+    psa_algorithm_t alg)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot = NULL;
+
+    status = psa_get_and_lock_key_slot(key, p_slot);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+    slot = *p_slot;
+
+    /* Enforce that usage policy for the key slot contains all the flags
+     * required by the usage parameter. There is one exception: public
+     * keys can always be exported, so we treat public key objects as
+     * if they had the export flag. */
+    if (PSA_KEY_TYPE_IS_PUBLIC_KEY(slot->attr.type)) {
+        usage &= ~PSA_KEY_USAGE_EXPORT;
+    }
+
+    if ((slot->attr.policy.usage & usage) != usage) {
+        status = PSA_ERROR_NOT_PERMITTED;
+        goto error;
+    }
+
+    /* Enforce that the usage policy permits the requested algorithm. */
+    if (alg != 0) {
+        status = psa_key_policy_permits(&slot->attr.policy,
+                                        slot->attr.type,
+                                        alg);
+        if (status != PSA_SUCCESS) {
+            goto error;
+        }
+    }
+
+    return PSA_SUCCESS;
+
+error:
+    *p_slot = NULL;
+    psa_unregister_read_under_mutex(slot);
+
+    return status;
+}
+
+/** Get a key slot containing a transparent key and lock it.
+ *
+ * A transparent key is a key for which the key material is directly
+ * available, as opposed to a key in a secure element and/or to be used
+ * by a secure element.
+ *
+ * This is a temporary function that may be used instead of
+ * psa_get_and_lock_key_slot_with_policy() when there is no opaque key support
+ * for a cryptographic operation.
+ *
+ * On success, the returned key slot has been registered for reading.
+ * It is the responsibility of the caller to then unregister
+ * once they have finished reading the contents of the slot.
+ * The caller unregisters by calling psa_unregister_read() or
+ * psa_unregister_read_under_mutex(). psa_unregister_read() must be called
+ * if and only if the caller already holds the global key slot mutex
+ * (when mutexes are enabled). psa_unregister_read_under_mutex() encapsulates
+ * psa_unregister_read() with mutex lock and unlock operations.
+ */
+static psa_status_t psa_get_and_lock_transparent_key_slot_with_policy(
+    mbedtls_svc_key_id_t key,
+    psa_key_slot_t **p_slot,
+    psa_key_usage_t usage,
+    psa_algorithm_t alg)
+{
+    psa_status_t status = psa_get_and_lock_key_slot_with_policy(key, p_slot,
+                                                                usage, alg);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    if (psa_key_lifetime_is_external((*p_slot)->attr.lifetime)) {
+        psa_unregister_read_under_mutex(*p_slot);
+        *p_slot = NULL;
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_remove_key_data_from_memory(psa_key_slot_t *slot)
+{
+    if (slot->key.data != NULL) {
+        mbedtls_zeroize_and_free(slot->key.data, slot->key.bytes);
+    }
+
+    slot->key.data = NULL;
+    slot->key.bytes = 0;
+
+    return PSA_SUCCESS;
+}
+
+/** Completely wipe a slot in memory, including its policy.
+ * Persistent storage is not affected. */
+psa_status_t psa_wipe_key_slot(psa_key_slot_t *slot)
+{
+    psa_status_t status = psa_remove_key_data_from_memory(slot);
+
+    /*
+     * As the return error code may not be handled in case of multiple errors,
+     * do our best to report an unexpected amount of registered readers or
+     * an unexpected state.
+     * Assert with MBEDTLS_TEST_HOOK_TEST_ASSERT that the slot is valid for
+     * wiping.
+     * if the MBEDTLS_TEST_HOOKS configuration option is enabled and the
+     * function is called as part of the execution of a test suite, the
+     * execution of the test suite is stopped in error if the assertion fails.
+     */
+    switch (slot->state) {
+        case PSA_SLOT_FULL:
+        /* In this state psa_wipe_key_slot() must only be called if the
+         * caller is the last reader. */
+        case PSA_SLOT_PENDING_DELETION:
+            /* In this state psa_wipe_key_slot() must only be called if the
+             * caller is the last reader. */
+            if (slot->registered_readers != 1) {
+                MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->registered_readers == 1);
+                status = PSA_ERROR_CORRUPTION_DETECTED;
+            }
+            break;
+        case PSA_SLOT_FILLING:
+            /* In this state registered_readers must be 0. */
+            if (slot->registered_readers != 0) {
+                MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->registered_readers == 0);
+                status = PSA_ERROR_CORRUPTION_DETECTED;
+            }
+            break;
+        case PSA_SLOT_EMPTY:
+            /* The slot is already empty, it cannot be wiped. */
+            MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->state != PSA_SLOT_EMPTY);
+            status = PSA_ERROR_CORRUPTION_DETECTED;
+            break;
+        default:
+            /* The slot's state is invalid. */
+            status = PSA_ERROR_CORRUPTION_DETECTED;
+    }
+
+    /* Multipart operations may still be using the key. This is safe
+     * because all multipart operation objects are independent from
+     * the key slot: if they need to access the key after the setup
+     * phase, they have a copy of the key. Note that this means that
+     * key material can linger until all operations are completed. */
+    /* At this point, key material and other type-specific content has
+     * been wiped. Clear remaining metadata. We can call memset and not
+     * zeroize because the metadata is not particularly sensitive.
+     * This memset also sets the slot's state to PSA_SLOT_EMPTY. */
+    memset(slot, 0, sizeof(*slot));
+    return status;
+}
+
+psa_status_t psa_destroy_key(mbedtls_svc_key_id_t key)
+{
+    psa_key_slot_t *slot;
+    psa_status_t status; /* status of the last operation */
+    psa_status_t overall_status = PSA_SUCCESS;
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+    psa_se_drv_table_entry_t *driver;
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+    if (mbedtls_svc_key_id_is_null(key)) {
+        return PSA_SUCCESS;
+    }
+
+    /*
+     * Get the description of the key in a key slot, and register to read it.
+     * In the case of a persistent key, this will load the key description
+     * from persistent memory if not done yet.
+     * We cannot avoid this loading as without it we don't know if
+     * the key is operated by an SE or not and this information is needed by
+     * the current implementation. */
+    status = psa_get_and_lock_key_slot(key, &slot);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+#if defined(MBEDTLS_THREADING_C)
+    /* We cannot unlock between setting the state to PENDING_DELETION
+     * and destroying the key in storage, as otherwise another thread
+     * could load the key into a new slot and the key will not be
+     * fully destroyed. */
+    PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(
+                                    &mbedtls_threading_key_slot_mutex));
+
+    if (slot->state == PSA_SLOT_PENDING_DELETION) {
+        /* Another thread has destroyed the key between us locking the slot
+         * and us gaining the mutex. Unregister from the slot,
+         * and report that the key does not exist. */
+        status = psa_unregister_read(slot);
+
+        PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                                  &mbedtls_threading_key_slot_mutex));
+        return (status == PSA_SUCCESS) ? PSA_ERROR_INVALID_HANDLE : status;
+    }
+#endif
+    /* Set the key slot containing the key description's state to
+     * PENDING_DELETION. This stops new operations from registering
+     * to read the slot. Current readers can safely continue to access
+     * the key within the slot; the last registered reader will
+     * automatically wipe the slot when they call psa_unregister_read().
+     * If the key is persistent, we can now delete the copy of the key
+     * from memory. If the key is opaque, we require the driver to
+     * deal with the deletion. */
+    overall_status = psa_key_slot_state_transition(slot, PSA_SLOT_FULL,
+                                                   PSA_SLOT_PENDING_DELETION);
+
+    if (overall_status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (PSA_KEY_LIFETIME_IS_READ_ONLY(slot->attr.lifetime)) {
+        /* Refuse the destruction of a read-only key (which may or may not work
+         * if we attempt it, depending on whether the key is merely read-only
+         * by policy or actually physically read-only).
+         * Just do the best we can, which is to wipe the copy in memory
+         * (done in this function's cleanup code). */
+        overall_status = PSA_ERROR_NOT_PERMITTED;
+        goto exit;
+    }
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+    driver = psa_get_se_driver_entry(slot->attr.lifetime);
+    if (driver != NULL) {
+        /* For a key in a secure element, we need to do three things:
+         * remove the key file in internal storage, destroy the
+         * key inside the secure element, and update the driver's
+         * persistent data. Start a transaction that will encompass these
+         * three actions. */
+        psa_crypto_prepare_transaction(PSA_CRYPTO_TRANSACTION_DESTROY_KEY);
+        psa_crypto_transaction.key.lifetime = slot->attr.lifetime;
+        psa_crypto_transaction.key.slot = psa_key_slot_get_slot_number(slot);
+        psa_crypto_transaction.key.id = slot->attr.id;
+        status = psa_crypto_save_transaction();
+        if (status != PSA_SUCCESS) {
+            (void) psa_crypto_stop_transaction();
+            /* We should still try to destroy the key in the secure
+             * element and the key metadata in storage. This is especially
+             * important if the error is that the storage is full.
+             * But how to do it exactly without risking an inconsistent
+             * state after a reset?
+             * https://github.com/ARMmbed/mbed-crypto/issues/215
+             */
+            overall_status = status;
+            goto exit;
+        }
+
+        status = psa_destroy_se_key(driver,
+                                    psa_key_slot_get_slot_number(slot));
+        if (overall_status == PSA_SUCCESS) {
+            overall_status = status;
+        }
+    }
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
+    if (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
+        /* Destroy the copy of the persistent key from storage.
+         * The slot will still hold a copy of the key until the last reader
+         * unregisters. */
+        status = psa_destroy_persistent_key(slot->attr.id);
+        if (overall_status == PSA_SUCCESS) {
+            overall_status = status;
+        }
+    }
+#endif /* defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) */
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+    if (driver != NULL) {
+        status = psa_save_se_persistent_data(driver);
+        if (overall_status == PSA_SUCCESS) {
+            overall_status = status;
+        }
+        status = psa_crypto_stop_transaction();
+        if (overall_status == PSA_SUCCESS) {
+            overall_status = status;
+        }
+    }
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+exit:
+    /* Unregister from reading the slot. If we are the last active reader
+     * then this will wipe the slot. */
+    status = psa_unregister_read(slot);
+    /* Prioritize CORRUPTION_DETECTED from unregistering over
+     * a storage error. */
+    if (status != PSA_SUCCESS) {
+        overall_status = status;
+    }
+
+#if defined(MBEDTLS_THREADING_C)
+    /* Don't overwrite existing errors if the unlock fails. */
+    status = overall_status;
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+
+    return overall_status;
+}
+
+/** Retrieve all the publicly-accessible attributes of a key.
+ */
+psa_status_t psa_get_key_attributes(mbedtls_svc_key_id_t key,
+                                    psa_key_attributes_t *attributes)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+
+    psa_reset_key_attributes(attributes);
+
+    status = psa_get_and_lock_key_slot_with_policy(key, &slot, 0, 0);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    *attributes = slot->attr;
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+    if (psa_get_se_driver_entry(slot->attr.lifetime) != NULL) {
+        psa_set_key_slot_number(attributes,
+                                psa_key_slot_get_slot_number(slot));
+    }
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+    return psa_unregister_read_under_mutex(slot);
+}
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+psa_status_t psa_get_key_slot_number(
+    const psa_key_attributes_t *attributes,
+    psa_key_slot_number_t *slot_number)
+{
+    if (attributes->has_slot_number) {
+        *slot_number = attributes->slot_number;
+        return PSA_SUCCESS;
+    } else {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+}
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+static psa_status_t psa_export_key_buffer_internal(const uint8_t *key_buffer,
+                                                   size_t key_buffer_size,
+                                                   uint8_t *data,
+                                                   size_t data_size,
+                                                   size_t *data_length)
+{
+    if (key_buffer_size > data_size) {
+        return PSA_ERROR_BUFFER_TOO_SMALL;
+    }
+    memcpy(data, key_buffer, key_buffer_size);
+    memset(data + key_buffer_size, 0,
+           data_size - key_buffer_size);
+    *data_length = key_buffer_size;
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_export_key_internal(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    uint8_t *data, size_t data_size, size_t *data_length)
+{
+    psa_key_type_t type = attributes->type;
+
+    if (key_type_is_raw_bytes(type) ||
+        PSA_KEY_TYPE_IS_RSA(type)   ||
+        PSA_KEY_TYPE_IS_ECC(type)   ||
+        PSA_KEY_TYPE_IS_DH(type)) {
+        return psa_export_key_buffer_internal(
+            key_buffer, key_buffer_size,
+            data, data_size, data_length);
+    } else {
+        /* This shouldn't happen in the reference implementation, but
+           it is valid for a special-purpose implementation to omit
+           support for exporting certain key types. */
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+}
+
+psa_status_t psa_export_key(mbedtls_svc_key_id_t key,
+                            uint8_t *data_external,
+                            size_t data_size,
+                            size_t *data_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+    LOCAL_OUTPUT_DECLARE(data_external, data);
+
+    /* Reject a zero-length output buffer now, since this can never be a
+     * valid key representation. This way we know that data must be a valid
+     * pointer and we can do things like memset(data, ..., data_size). */
+    if (data_size == 0) {
+        return PSA_ERROR_BUFFER_TOO_SMALL;
+    }
+
+    /* Set the key to empty now, so that even when there are errors, we always
+     * set data_length to a value between 0 and data_size. On error, setting
+     * the key to empty is a good choice because an empty key representation is
+     * unlikely to be accepted anywhere. */
+    *data_length = 0;
+
+    /* Export requires the EXPORT flag. There is an exception for public keys,
+     * which don't require any flag, but
+     * psa_get_and_lock_key_slot_with_policy() takes care of this.
+     */
+    status = psa_get_and_lock_key_slot_with_policy(key, &slot,
+                                                   PSA_KEY_USAGE_EXPORT, 0);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    LOCAL_OUTPUT_ALLOC(data_external, data_size, data);
+
+    status = psa_driver_wrapper_export_key(&slot->attr,
+                                           slot->key.data, slot->key.bytes,
+                                           data, data_size, data_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+    unlock_status = psa_unregister_read_under_mutex(slot);
+
+    LOCAL_OUTPUT_FREE(data_external, data);
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_export_public_key_internal(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    uint8_t *data,
+    size_t data_size,
+    size_t *data_length)
+{
+    psa_key_type_t type = attributes->type;
+
+    if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type) &&
+        (PSA_KEY_TYPE_IS_RSA(type) || PSA_KEY_TYPE_IS_ECC(type) ||
+         PSA_KEY_TYPE_IS_DH(type))) {
+        /* Exporting public -> public */
+        return psa_export_key_buffer_internal(
+            key_buffer, key_buffer_size,
+            data, data_size, data_length);
+    } else if (PSA_KEY_TYPE_IS_RSA(type)) {
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT) || \
+        defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY)
+        return mbedtls_psa_rsa_export_public_key(attributes,
+                                                 key_buffer,
+                                                 key_buffer_size,
+                                                 data,
+                                                 data_size,
+                                                 data_length);
+#else
+        /* We don't know how to convert a private RSA key to public. */
+        return PSA_ERROR_NOT_SUPPORTED;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */
+    } else if (PSA_KEY_TYPE_IS_ECC(type)) {
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_EXPORT) || \
+        defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY)
+        return mbedtls_psa_ecp_export_public_key(attributes,
+                                                 key_buffer,
+                                                 key_buffer_size,
+                                                 data,
+                                                 data_size,
+                                                 data_length);
+#else
+        /* We don't know how to convert a private ECC key to public */
+        return PSA_ERROR_NOT_SUPPORTED;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_EXPORT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) */
+    } else if (PSA_KEY_TYPE_IS_DH(type)) {
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_EXPORT) || \
+        defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY)
+        return mbedtls_psa_ffdh_export_public_key(attributes,
+                                                  key_buffer,
+                                                  key_buffer_size,
+                                                  data, data_size,
+                                                  data_length);
+#else
+        return PSA_ERROR_NOT_SUPPORTED;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_EXPORT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY) */
+    } else {
+        (void) key_buffer;
+        (void) key_buffer_size;
+        (void) data;
+        (void) data_size;
+        (void) data_length;
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+}
+
+psa_status_t psa_export_public_key(mbedtls_svc_key_id_t key,
+                                   uint8_t *data_external,
+                                   size_t data_size,
+                                   size_t *data_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+
+    LOCAL_OUTPUT_DECLARE(data_external, data);
+
+    /* Reject a zero-length output buffer now, since this can never be a
+     * valid key representation. This way we know that data must be a valid
+     * pointer and we can do things like memset(data, ..., data_size). */
+    if (data_size == 0) {
+        return PSA_ERROR_BUFFER_TOO_SMALL;
+    }
+
+    /* Set the key to empty now, so that even when there are errors, we always
+     * set data_length to a value between 0 and data_size. On error, setting
+     * the key to empty is a good choice because an empty key representation is
+     * unlikely to be accepted anywhere. */
+    *data_length = 0;
+
+    /* Exporting a public key doesn't require a usage flag. */
+    status = psa_get_and_lock_key_slot_with_policy(key, &slot, 0, 0);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    LOCAL_OUTPUT_ALLOC(data_external, data_size, data);
+
+    if (!PSA_KEY_TYPE_IS_ASYMMETRIC(slot->attr.type)) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    status = psa_driver_wrapper_export_public_key(
+        &slot->attr, slot->key.data, slot->key.bytes,
+        data, data_size, data_length);
+
+exit:
+    unlock_status = psa_unregister_read_under_mutex(slot);
+
+    LOCAL_OUTPUT_FREE(data_external, data);
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+/** Validate that a key policy is internally well-formed.
+ *
+ * This function only rejects invalid policies. It does not validate the
+ * consistency of the policy with respect to other attributes of the key
+ * such as the key type.
+ */
+static psa_status_t psa_validate_key_policy(const psa_key_policy_t *policy)
+{
+    if ((policy->usage & ~(PSA_KEY_USAGE_EXPORT |
+                           PSA_KEY_USAGE_COPY |
+                           PSA_KEY_USAGE_ENCRYPT |
+                           PSA_KEY_USAGE_DECRYPT |
+                           PSA_KEY_USAGE_SIGN_MESSAGE |
+                           PSA_KEY_USAGE_VERIFY_MESSAGE |
+                           PSA_KEY_USAGE_SIGN_HASH |
+                           PSA_KEY_USAGE_VERIFY_HASH |
+                           PSA_KEY_USAGE_VERIFY_DERIVATION |
+                           PSA_KEY_USAGE_DERIVE)) != 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    return PSA_SUCCESS;
+}
+
+/** Validate the internal consistency of key attributes.
+ *
+ * This function only rejects invalid attribute values. If does not
+ * validate the consistency of the attributes with any key data that may
+ * be involved in the creation of the key.
+ *
+ * Call this function early in the key creation process.
+ *
+ * \param[in] attributes    Key attributes for the new key.
+ * \param[out] p_drv        On any return, the driver for the key, if any.
+ *                          NULL for a transparent key.
+ *
+ */
+static psa_status_t psa_validate_key_attributes(
+    const psa_key_attributes_t *attributes,
+    psa_se_drv_table_entry_t **p_drv)
+{
+    psa_status_t status = PSA_ERROR_INVALID_ARGUMENT;
+    psa_key_lifetime_t lifetime = psa_get_key_lifetime(attributes);
+    mbedtls_svc_key_id_t key = psa_get_key_id(attributes);
+
+    status = psa_validate_key_location(lifetime, p_drv);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = psa_validate_key_persistence(lifetime);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    if (PSA_KEY_LIFETIME_IS_VOLATILE(lifetime)) {
+        if (MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key) != 0) {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+    } else {
+        if (!psa_is_valid_key_id(psa_get_key_id(attributes), 0)) {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+    }
+
+    status = psa_validate_key_policy(&attributes->policy);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    /* Refuse to create overly large keys.
+     * Note that this doesn't trigger on import if the attributes don't
+     * explicitly specify a size (so psa_get_key_bits returns 0), so
+     * psa_import_key() needs its own checks. */
+    if (psa_get_key_bits(attributes) > PSA_MAX_KEY_BITS) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    return PSA_SUCCESS;
+}
+
+/** Prepare a key slot to receive key material.
+ *
+ * This function allocates a key slot and sets its metadata.
+ *
+ * If this function fails, call psa_fail_key_creation().
+ *
+ * This function is intended to be used as follows:
+ * -# Call psa_start_key_creation() to allocate a key slot, prepare
+ *    it with the specified attributes, and in case of a volatile key assign it
+ *    a volatile key identifier.
+ * -# Populate the slot with the key material.
+ * -# Call psa_finish_key_creation() to finalize the creation of the slot.
+ * In case of failure at any step, stop the sequence and call
+ * psa_fail_key_creation().
+ *
+ * On success, the key slot's state is PSA_SLOT_FILLING.
+ * It is the responsibility of the caller to change the slot's state to
+ * PSA_SLOT_EMPTY/FULL once key creation has finished.
+ *
+ * \param method            An identification of the calling function.
+ * \param[in] attributes    Key attributes for the new key.
+ * \param[out] p_slot       On success, a pointer to the prepared slot.
+ * \param[out] p_drv        On any return, the driver for the key, if any.
+ *                          NULL for a transparent key.
+ *
+ * \retval #PSA_SUCCESS
+ *         The key slot is ready to receive key material.
+ * \return If this function fails, the key slot is an invalid state.
+ *         You must call psa_fail_key_creation() to wipe and free the slot.
+ */
+static psa_status_t psa_start_key_creation(
+    psa_key_creation_method_t method,
+    const psa_key_attributes_t *attributes,
+    psa_key_slot_t **p_slot,
+    psa_se_drv_table_entry_t **p_drv)
+{
+    psa_status_t status;
+    psa_key_id_t volatile_key_id;
+    psa_key_slot_t *slot;
+
+    (void) method;
+    *p_drv = NULL;
+
+    status = psa_validate_key_attributes(attributes, p_drv);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+    status = psa_reserve_free_key_slot(&volatile_key_id, p_slot);
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+    slot = *p_slot;
+
+    /* We're storing the declared bit-size of the key. It's up to each
+     * creation mechanism to verify that this information is correct.
+     * It's automatically correct for mechanisms that use the bit-size as
+     * an input (generate, device) but not for those where the bit-size
+     * is optional (import, copy). In case of a volatile key, assign it the
+     * volatile key identifier associated to the slot returned to contain its
+     * definition. */
+
+    slot->attr = *attributes;
+    if (PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
+#if !defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
+        slot->attr.id = volatile_key_id;
+#else
+        slot->attr.id.key_id = volatile_key_id;
+#endif
+    }
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+    /* For a key in a secure element, we need to do three things
+     * when creating or registering a persistent key:
+     * create the key file in internal storage, create the
+     * key inside the secure element, and update the driver's
+     * persistent data. This is done by starting a transaction that will
+     * encompass these three actions.
+     * For registering a volatile key, we just need to find an appropriate
+     * slot number inside the SE. Since the key is designated volatile, creating
+     * a transaction is not required. */
+    /* The first thing to do is to find a slot number for the new key.
+     * We save the slot number in persistent storage as part of the
+     * transaction data. It will be needed to recover if the power
+     * fails during the key creation process, to clean up on the secure
+     * element side after restarting. Obtaining a slot number from the
+     * secure element driver updates its persistent state, but we do not yet
+     * save the driver's persistent state, so that if the power fails,
+     * we can roll back to a state where the key doesn't exist. */
+    if (*p_drv != NULL) {
+        psa_key_slot_number_t slot_number;
+        status = psa_find_se_slot_for_key(attributes, method, *p_drv,
+                                          &slot_number);
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+
+        if (!PSA_KEY_LIFETIME_IS_VOLATILE(attributes->lifetime)) {
+            psa_crypto_prepare_transaction(PSA_CRYPTO_TRANSACTION_CREATE_KEY);
+            psa_crypto_transaction.key.lifetime = slot->attr.lifetime;
+            psa_crypto_transaction.key.slot = slot_number;
+            psa_crypto_transaction.key.id = slot->attr.id;
+            status = psa_crypto_save_transaction();
+            if (status != PSA_SUCCESS) {
+                (void) psa_crypto_stop_transaction();
+                return status;
+            }
+        }
+
+        status = psa_copy_key_material_into_slot(
+            slot, (uint8_t *) (&slot_number), sizeof(slot_number));
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+    }
+
+    if (*p_drv == NULL && method == PSA_KEY_CREATION_REGISTER) {
+        /* Key registration only makes sense with a secure element. */
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+    return PSA_SUCCESS;
+}
+
+/** Finalize the creation of a key once its key material has been set.
+ *
+ * This entails writing the key to persistent storage.
+ *
+ * If this function fails, call psa_fail_key_creation().
+ * See the documentation of psa_start_key_creation() for the intended use
+ * of this function.
+ *
+ * If the finalization succeeds, the function sets the key slot's state to
+ * PSA_SLOT_FULL, and the key slot can no longer be accessed as part of the
+ * key creation process.
+ *
+ * \param[in,out] slot  Pointer to the slot with key material.
+ * \param[in] driver    The secure element driver for the key,
+ *                      or NULL for a transparent key.
+ * \param[out] key      On success, identifier of the key. Note that the
+ *                      key identifier is also stored in the key slot.
+ *
+ * \retval #PSA_SUCCESS
+ *         The key was successfully created.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription
+ * \retval #PSA_ERROR_ALREADY_EXISTS \emptydescription
+ * \retval #PSA_ERROR_DATA_INVALID \emptydescription
+ * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ *
+ * \return If this function fails, the key slot is an invalid state.
+ *         You must call psa_fail_key_creation() to wipe and free the slot.
+ */
+static psa_status_t psa_finish_key_creation(
+    psa_key_slot_t *slot,
+    psa_se_drv_table_entry_t *driver,
+    mbedtls_svc_key_id_t *key)
+{
+    psa_status_t status = PSA_SUCCESS;
+    (void) slot;
+    (void) driver;
+
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
+    if (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+        if (driver != NULL) {
+            psa_se_key_data_storage_t data;
+            psa_key_slot_number_t slot_number =
+                psa_key_slot_get_slot_number(slot);
+
+            MBEDTLS_STATIC_ASSERT(sizeof(slot_number) ==
+                                  sizeof(data.slot_number),
+                                  "Slot number size does not match psa_se_key_data_storage_t");
+
+            memcpy(&data.slot_number, &slot_number, sizeof(slot_number));
+            status = psa_save_persistent_key(&slot->attr,
+                                             (uint8_t *) &data,
+                                             sizeof(data));
+        } else
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+        {
+            /* Key material is saved in export representation in the slot, so
+             * just pass the slot buffer for storage. */
+            status = psa_save_persistent_key(&slot->attr,
+                                             slot->key.data,
+                                             slot->key.bytes);
+        }
+    }
+#endif /* defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) */
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+    /* Finish the transaction for a key creation. This does not
+     * happen when registering an existing key. Detect this case
+     * by checking whether a transaction is in progress (actual
+     * creation of a persistent key in a secure element requires a transaction,
+     * but registration or volatile key creation doesn't use one). */
+    if (driver != NULL &&
+        psa_crypto_transaction.unknown.type == PSA_CRYPTO_TRANSACTION_CREATE_KEY) {
+        status = psa_save_se_persistent_data(driver);
+        if (status != PSA_SUCCESS) {
+            psa_destroy_persistent_key(slot->attr.id);
+
+#if defined(MBEDTLS_THREADING_C)
+            PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                                      &mbedtls_threading_key_slot_mutex));
+#endif
+            return status;
+        }
+        status = psa_crypto_stop_transaction();
+    }
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+    if (status == PSA_SUCCESS) {
+        *key = slot->attr.id;
+        status = psa_key_slot_state_transition(slot, PSA_SLOT_FILLING,
+                                               PSA_SLOT_FULL);
+        if (status != PSA_SUCCESS) {
+            *key = MBEDTLS_SVC_KEY_ID_INIT;
+        }
+    }
+
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+    return status;
+}
+
+/** Abort the creation of a key.
+ *
+ * You may call this function after calling psa_start_key_creation(),
+ * or after psa_finish_key_creation() fails. In other circumstances, this
+ * function may not clean up persistent storage.
+ * See the documentation of psa_start_key_creation() for the intended use
+ * of this function. Sets the slot's state to PSA_SLOT_EMPTY.
+ *
+ * \param[in,out] slot  Pointer to the slot with key material.
+ * \param[in] driver    The secure element driver for the key,
+ *                      or NULL for a transparent key.
+ */
+static void psa_fail_key_creation(psa_key_slot_t *slot,
+                                  psa_se_drv_table_entry_t *driver)
+{
+    (void) driver;
+
+    if (slot == NULL) {
+        return;
+    }
+
+#if defined(MBEDTLS_THREADING_C)
+    /* If the lock operation fails we still wipe the slot.
+     * Operations will no longer work after a failed lock,
+     * but we still need to wipe the slot of confidential data. */
+    mbedtls_mutex_lock(&mbedtls_threading_key_slot_mutex);
+#endif
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+    /* TODO: If the key has already been created in the secure
+     * element, and the failure happened later (when saving metadata
+     * to internal storage), we need to destroy the key in the secure
+     * element.
+     * https://github.com/ARMmbed/mbed-crypto/issues/217
+     */
+
+    /* Abort the ongoing transaction if any (there may not be one if
+     * the creation process failed before starting one, or if the
+     * key creation is a registration of a key in a secure element).
+     * Earlier functions must already have done what it takes to undo any
+     * partial creation. All that's left is to update the transaction data
+     * itself. */
+    (void) psa_crypto_stop_transaction();
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+    psa_wipe_key_slot(slot);
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_unlock(&mbedtls_threading_key_slot_mutex);
+#endif
+}
+
+/** Validate optional attributes during key creation.
+ *
+ * Some key attributes are optional during key creation. If they are
+ * specified in the attributes structure, check that they are consistent
+ * with the data in the slot.
+ *
+ * This function should be called near the end of key creation, after
+ * the slot in memory is fully populated but before saving persistent data.
+ */
+static psa_status_t psa_validate_optional_attributes(
+    const psa_key_slot_t *slot,
+    const psa_key_attributes_t *attributes)
+{
+    if (attributes->type != 0) {
+        if (attributes->type != slot->attr.type) {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+    }
+
+    if (attributes->bits != 0) {
+        if (attributes->bits != slot->attr.bits) {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+    }
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_import_key(const psa_key_attributes_t *attributes,
+                            const uint8_t *data_external,
+                            size_t data_length,
+                            mbedtls_svc_key_id_t *key)
+{
+    psa_status_t status;
+    LOCAL_INPUT_DECLARE(data_external, data);
+    psa_key_slot_t *slot = NULL;
+    psa_se_drv_table_entry_t *driver = NULL;
+    size_t bits;
+    size_t storage_size = data_length;
+
+    *key = MBEDTLS_SVC_KEY_ID_INIT;
+
+    /* Reject zero-length symmetric keys (including raw data key objects).
+     * This also rejects any key which might be encoded as an empty string,
+     * which is never valid. */
+    if (data_length == 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Ensure that the bytes-to-bits conversion cannot overflow. */
+    if (data_length > SIZE_MAX / 8) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    LOCAL_INPUT_ALLOC(data_external, data_length, data);
+
+    status = psa_start_key_creation(PSA_KEY_CREATION_IMPORT, attributes,
+                                    &slot, &driver);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    /* In the case of a transparent key or an opaque key stored in local
+     * storage ( thus not in the case of importing a key in a secure element
+     * with storage ( MBEDTLS_PSA_CRYPTO_SE_C ) ),we have to allocate a
+     * buffer to hold the imported key material. */
+    if (slot->key.data == NULL) {
+        if (psa_key_lifetime_is_external(attributes->lifetime)) {
+            status = psa_driver_wrapper_get_key_buffer_size_from_key_data(
+                attributes, data, data_length, &storage_size);
+            if (status != PSA_SUCCESS) {
+                goto exit;
+            }
+        }
+        status = psa_allocate_buffer_to_slot(slot, storage_size);
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+    }
+
+    bits = slot->attr.bits;
+    status = psa_driver_wrapper_import_key(attributes,
+                                           data, data_length,
+                                           slot->key.data,
+                                           slot->key.bytes,
+                                           &slot->key.bytes, &bits);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (slot->attr.bits == 0) {
+        slot->attr.bits = (psa_key_bits_t) bits;
+    } else if (bits != slot->attr.bits) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    /* Enforce a size limit, and in particular ensure that the bit
+     * size fits in its representation type.*/
+    if (bits > PSA_MAX_KEY_BITS) {
+        status = PSA_ERROR_NOT_SUPPORTED;
+        goto exit;
+    }
+    status = psa_validate_optional_attributes(slot, attributes);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_finish_key_creation(slot, driver, key);
+exit:
+    LOCAL_INPUT_FREE(data_external, data);
+    if (status != PSA_SUCCESS) {
+        psa_fail_key_creation(slot, driver);
+    }
+
+    return status;
+}
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+psa_status_t mbedtls_psa_register_se_key(
+    const psa_key_attributes_t *attributes)
+{
+    psa_status_t status;
+    psa_key_slot_t *slot = NULL;
+    psa_se_drv_table_entry_t *driver = NULL;
+    mbedtls_svc_key_id_t key = MBEDTLS_SVC_KEY_ID_INIT;
+
+    /* Leaving attributes unspecified is not currently supported.
+     * It could make sense to query the key type and size from the
+     * secure element, but not all secure elements support this
+     * and the driver HAL doesn't currently support it. */
+    if (psa_get_key_type(attributes) == PSA_KEY_TYPE_NONE) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+    if (psa_get_key_bits(attributes) == 0) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    status = psa_start_key_creation(PSA_KEY_CREATION_REGISTER, attributes,
+                                    &slot, &driver);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_finish_key_creation(slot, driver, &key);
+
+exit:
+    if (status != PSA_SUCCESS) {
+        psa_fail_key_creation(slot, driver);
+    }
+
+    /* Registration doesn't keep the key in RAM. */
+    psa_close_key(key);
+    return status;
+}
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+psa_status_t psa_copy_key(mbedtls_svc_key_id_t source_key,
+                          const psa_key_attributes_t *specified_attributes,
+                          mbedtls_svc_key_id_t *target_key)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *source_slot = NULL;
+    psa_key_slot_t *target_slot = NULL;
+    psa_key_attributes_t actual_attributes = *specified_attributes;
+    psa_se_drv_table_entry_t *driver = NULL;
+    size_t storage_size = 0;
+
+    *target_key = MBEDTLS_SVC_KEY_ID_INIT;
+
+    status = psa_get_and_lock_key_slot_with_policy(
+        source_key, &source_slot, PSA_KEY_USAGE_COPY, 0);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_validate_optional_attributes(source_slot,
+                                              specified_attributes);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    /* The target key type and number of bits have been validated by
+     * psa_validate_optional_attributes() to be either equal to zero or
+     * equal to the ones of the source key. So it is safe to inherit
+     * them from the source key now."
+     * */
+    actual_attributes.bits = source_slot->attr.bits;
+    actual_attributes.type = source_slot->attr.type;
+
+
+    status = psa_restrict_key_policy(source_slot->attr.type,
+                                     &actual_attributes.policy,
+                                     &source_slot->attr.policy);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_start_key_creation(PSA_KEY_CREATION_COPY, &actual_attributes,
+                                    &target_slot, &driver);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+    if (PSA_KEY_LIFETIME_GET_LOCATION(target_slot->attr.lifetime) !=
+        PSA_KEY_LIFETIME_GET_LOCATION(source_slot->attr.lifetime)) {
+        /*
+         * If the source and target keys are stored in different locations,
+         * the source key would need to be exported as plaintext and re-imported
+         * in the other location. This has security implications which have not
+         * been fully mapped. For now, this can be achieved through
+         * appropriate API invocations from the application, if needed.
+         * */
+        status = PSA_ERROR_NOT_SUPPORTED;
+        goto exit;
+    }
+    /*
+     * When the source and target keys are within the same location,
+     * - For transparent keys it is a blind copy without any driver invocation,
+     * - For opaque keys this translates to an invocation of the drivers'
+     *   copy_key entry point through the dispatch layer.
+     * */
+    if (psa_key_lifetime_is_external(actual_attributes.lifetime)) {
+        status = psa_driver_wrapper_get_key_buffer_size(&actual_attributes,
+                                                        &storage_size);
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+
+        status = psa_allocate_buffer_to_slot(target_slot, storage_size);
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+
+        status = psa_driver_wrapper_copy_key(&actual_attributes,
+                                             source_slot->key.data,
+                                             source_slot->key.bytes,
+                                             target_slot->key.data,
+                                             target_slot->key.bytes,
+                                             &target_slot->key.bytes);
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+    } else {
+        status = psa_copy_key_material_into_slot(target_slot,
+                                                 source_slot->key.data,
+                                                 source_slot->key.bytes);
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+    }
+    status = psa_finish_key_creation(target_slot, driver, target_key);
+exit:
+    if (status != PSA_SUCCESS) {
+        psa_fail_key_creation(target_slot, driver);
+    }
+
+    unlock_status = psa_unregister_read_under_mutex(source_slot);
+
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+
+
+/****************************************************************/
+/* Message digests */
+/****************************************************************/
+
+psa_status_t psa_hash_abort(psa_hash_operation_t *operation)
+{
+    /* Aborting a non-active operation is allowed */
+    if (operation->id == 0) {
+        return PSA_SUCCESS;
+    }
+
+    psa_status_t status = psa_driver_wrapper_hash_abort(operation);
+    operation->id = 0;
+
+    return status;
+}
+
+psa_status_t psa_hash_setup(psa_hash_operation_t *operation,
+                            psa_algorithm_t alg)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    /* A context must be freshly initialized before it can be set up. */
+    if (operation->id != 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (!PSA_ALG_IS_HASH(alg)) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    /* Ensure all of the context is zeroized, since PSA_HASH_OPERATION_INIT only
+     * directly zeroes the int-sized dummy member of the context union. */
+    memset(&operation->ctx, 0, sizeof(operation->ctx));
+
+    status = psa_driver_wrapper_hash_setup(operation, alg);
+
+exit:
+    if (status != PSA_SUCCESS) {
+        psa_hash_abort(operation);
+    }
+
+    return status;
+}
+
+psa_status_t psa_hash_update(psa_hash_operation_t *operation,
+                             const uint8_t *input_external,
+                             size_t input_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    LOCAL_INPUT_DECLARE(input_external, input);
+
+    if (operation->id == 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    /* Don't require hash implementations to behave correctly on a
+     * zero-length input, which may have an invalid pointer. */
+    if (input_length == 0) {
+        return PSA_SUCCESS;
+    }
+
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+    status = psa_driver_wrapper_hash_update(operation, input, input_length);
+
+exit:
+    if (status != PSA_SUCCESS) {
+        psa_hash_abort(operation);
+    }
+
+    LOCAL_INPUT_FREE(input_external, input);
+    return status;
+}
+
+static psa_status_t psa_hash_finish_internal(psa_hash_operation_t *operation,
+                                             uint8_t *hash,
+                                             size_t hash_size,
+                                             size_t *hash_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    *hash_length = 0;
+    if (operation->id == 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    status = psa_driver_wrapper_hash_finish(
+        operation, hash, hash_size, hash_length);
+    psa_hash_abort(operation);
+
+    return status;
+}
+
+psa_status_t psa_hash_finish(psa_hash_operation_t *operation,
+                             uint8_t *hash_external,
+                             size_t hash_size,
+                             size_t *hash_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    LOCAL_OUTPUT_DECLARE(hash_external, hash);
+
+    LOCAL_OUTPUT_ALLOC(hash_external, hash_size, hash);
+    status = psa_hash_finish_internal(operation, hash, hash_size, hash_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+    LOCAL_OUTPUT_FREE(hash_external, hash);
+    return status;
+}
+
+psa_status_t psa_hash_verify(psa_hash_operation_t *operation,
+                             const uint8_t *hash_external,
+                             size_t hash_length)
+{
+    uint8_t actual_hash[PSA_HASH_MAX_SIZE];
+    size_t actual_hash_length;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    LOCAL_INPUT_DECLARE(hash_external, hash);
+
+    status = psa_hash_finish_internal(
+        operation,
+        actual_hash, sizeof(actual_hash),
+        &actual_hash_length);
+
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (actual_hash_length != hash_length) {
+        status = PSA_ERROR_INVALID_SIGNATURE;
+        goto exit;
+    }
+
+    LOCAL_INPUT_ALLOC(hash_external, hash_length, hash);
+    if (mbedtls_ct_memcmp(hash, actual_hash, actual_hash_length) != 0) {
+        status = PSA_ERROR_INVALID_SIGNATURE;
+    }
+
+exit:
+    mbedtls_platform_zeroize(actual_hash, sizeof(actual_hash));
+    if (status != PSA_SUCCESS) {
+        psa_hash_abort(operation);
+    }
+    LOCAL_INPUT_FREE(hash_external, hash);
+    return status;
+}
+
+psa_status_t psa_hash_compute(psa_algorithm_t alg,
+                              const uint8_t *input_external, size_t input_length,
+                              uint8_t *hash_external, size_t hash_size,
+                              size_t *hash_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    LOCAL_INPUT_DECLARE(input_external, input);
+    LOCAL_OUTPUT_DECLARE(hash_external, hash);
+
+    *hash_length = 0;
+    if (!PSA_ALG_IS_HASH(alg)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+    LOCAL_OUTPUT_ALLOC(hash_external, hash_size, hash);
+    status = psa_driver_wrapper_hash_compute(alg, input, input_length,
+                                             hash, hash_size, hash_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+    LOCAL_INPUT_FREE(input_external, input);
+    LOCAL_OUTPUT_FREE(hash_external, hash);
+    return status;
+}
+
+psa_status_t psa_hash_compare(psa_algorithm_t alg,
+                              const uint8_t *input_external, size_t input_length,
+                              const uint8_t *hash_external, size_t hash_length)
+{
+    uint8_t actual_hash[PSA_HASH_MAX_SIZE];
+    size_t actual_hash_length;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    LOCAL_INPUT_DECLARE(input_external, input);
+    LOCAL_INPUT_DECLARE(hash_external, hash);
+
+    if (!PSA_ALG_IS_HASH(alg)) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        return status;
+    }
+
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+    status = psa_driver_wrapper_hash_compute(
+        alg, input, input_length,
+        actual_hash, sizeof(actual_hash),
+        &actual_hash_length);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+    if (actual_hash_length != hash_length) {
+        status = PSA_ERROR_INVALID_SIGNATURE;
+        goto exit;
+    }
+
+    LOCAL_INPUT_ALLOC(hash_external, hash_length, hash);
+    if (mbedtls_ct_memcmp(hash, actual_hash, actual_hash_length) != 0) {
+        status = PSA_ERROR_INVALID_SIGNATURE;
+    }
+
+exit:
+    mbedtls_platform_zeroize(actual_hash, sizeof(actual_hash));
+
+    LOCAL_INPUT_FREE(input_external, input);
+    LOCAL_INPUT_FREE(hash_external, hash);
+
+    return status;
+}
+
+psa_status_t psa_hash_clone(const psa_hash_operation_t *source_operation,
+                            psa_hash_operation_t *target_operation)
+{
+    if (source_operation->id == 0 ||
+        target_operation->id != 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    psa_status_t status = psa_driver_wrapper_hash_clone(source_operation,
+                                                        target_operation);
+    if (status != PSA_SUCCESS) {
+        psa_hash_abort(target_operation);
+    }
+
+    return status;
+}
+
+
+/****************************************************************/
+/* MAC */
+/****************************************************************/
+
+psa_status_t psa_mac_abort(psa_mac_operation_t *operation)
+{
+    /* Aborting a non-active operation is allowed */
+    if (operation->id == 0) {
+        return PSA_SUCCESS;
+    }
+
+    psa_status_t status = psa_driver_wrapper_mac_abort(operation);
+    operation->mac_size = 0;
+    operation->is_sign = 0;
+    operation->id = 0;
+
+    return status;
+}
+
+static psa_status_t psa_mac_finalize_alg_and_key_validation(
+    psa_algorithm_t alg,
+    const psa_key_attributes_t *attributes,
+    uint8_t *mac_size)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_type_t key_type = psa_get_key_type(attributes);
+    size_t key_bits = psa_get_key_bits(attributes);
+
+    if (!PSA_ALG_IS_MAC(alg)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Validate the combination of key type and algorithm */
+    status = psa_mac_key_can_do(alg, key_type);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    /* Get the output length for the algorithm and key combination */
+    *mac_size = PSA_MAC_LENGTH(key_type, key_bits, alg);
+
+    if (*mac_size < 4) {
+        /* A very short MAC is too short for security since it can be
+         * brute-forced. Ancient protocols with 32-bit MACs do exist,
+         * so we make this our minimum, even though 32 bits is still
+         * too small for security. */
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    if (*mac_size > PSA_MAC_LENGTH(key_type, key_bits,
+                                   PSA_ALG_FULL_LENGTH_MAC(alg))) {
+        /* It's impossible to "truncate" to a larger length than the full length
+         * of the algorithm. */
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (*mac_size > PSA_MAC_MAX_SIZE) {
+        /* PSA_MAC_LENGTH returns the correct length even for a MAC algorithm
+         * that is disabled in the compile-time configuration. The result can
+         * therefore be larger than PSA_MAC_MAX_SIZE, which does take the
+         * configuration into account. In this case, force a return of
+         * PSA_ERROR_NOT_SUPPORTED here. Otherwise psa_mac_verify(), or
+         * psa_mac_compute(mac_size=PSA_MAC_MAX_SIZE), would return
+         * PSA_ERROR_BUFFER_TOO_SMALL for an unsupported algorithm whose MAC size
+         * is larger than PSA_MAC_MAX_SIZE, which is misleading and which breaks
+         * systematically generated tests. */
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    return PSA_SUCCESS;
+}
+
+static psa_status_t psa_mac_setup(psa_mac_operation_t *operation,
+                                  mbedtls_svc_key_id_t key,
+                                  psa_algorithm_t alg,
+                                  int is_sign)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot = NULL;
+
+    /* A context must be freshly initialized before it can be set up. */
+    if (operation->id != 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    status = psa_get_and_lock_key_slot_with_policy(
+        key,
+        &slot,
+        is_sign ? PSA_KEY_USAGE_SIGN_MESSAGE : PSA_KEY_USAGE_VERIFY_MESSAGE,
+        alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_mac_finalize_alg_and_key_validation(alg, &slot->attr,
+                                                     &operation->mac_size);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    operation->is_sign = is_sign;
+    /* Dispatch the MAC setup call with validated input */
+    if (is_sign) {
+        status = psa_driver_wrapper_mac_sign_setup(operation,
+                                                   &slot->attr,
+                                                   slot->key.data,
+                                                   slot->key.bytes,
+                                                   alg);
+    } else {
+        status = psa_driver_wrapper_mac_verify_setup(operation,
+                                                     &slot->attr,
+                                                     slot->key.data,
+                                                     slot->key.bytes,
+                                                     alg);
+    }
+
+exit:
+    if (status != PSA_SUCCESS) {
+        psa_mac_abort(operation);
+    }
+
+    unlock_status = psa_unregister_read_under_mutex(slot);
+
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_mac_sign_setup(psa_mac_operation_t *operation,
+                                mbedtls_svc_key_id_t key,
+                                psa_algorithm_t alg)
+{
+    return psa_mac_setup(operation, key, alg, 1);
+}
+
+psa_status_t psa_mac_verify_setup(psa_mac_operation_t *operation,
+                                  mbedtls_svc_key_id_t key,
+                                  psa_algorithm_t alg)
+{
+    return psa_mac_setup(operation, key, alg, 0);
+}
+
+psa_status_t psa_mac_update(psa_mac_operation_t *operation,
+                            const uint8_t *input_external,
+                            size_t input_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    LOCAL_INPUT_DECLARE(input_external, input);
+
+    if (operation->id == 0) {
+        status = PSA_ERROR_BAD_STATE;
+        return status;
+    }
+
+    /* Don't require hash implementations to behave correctly on a
+     * zero-length input, which may have an invalid pointer. */
+    if (input_length == 0) {
+        status = PSA_SUCCESS;
+        return status;
+    }
+
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+    status = psa_driver_wrapper_mac_update(operation, input, input_length);
+
+    if (status != PSA_SUCCESS) {
+        psa_mac_abort(operation);
+    }
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+    LOCAL_INPUT_FREE(input_external, input);
+
+    return status;
+}
+
+psa_status_t psa_mac_sign_finish(psa_mac_operation_t *operation,
+                                 uint8_t *mac_external,
+                                 size_t mac_size,
+                                 size_t *mac_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t abort_status = PSA_ERROR_CORRUPTION_DETECTED;
+    LOCAL_OUTPUT_DECLARE(mac_external, mac);
+    LOCAL_OUTPUT_ALLOC(mac_external, mac_size, mac);
+
+    if (operation->id == 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (!operation->is_sign) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    /* Sanity check. This will guarantee that mac_size != 0 (and so mac != NULL)
+     * once all the error checks are done. */
+    if (operation->mac_size == 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (mac_size < operation->mac_size) {
+        status = PSA_ERROR_BUFFER_TOO_SMALL;
+        goto exit;
+    }
+
+
+    status = psa_driver_wrapper_mac_sign_finish(operation,
+                                                mac, operation->mac_size,
+                                                mac_length);
+
+exit:
+    /* In case of success, set the potential excess room in the output buffer
+     * to an invalid value, to avoid potentially leaking a longer MAC.
+     * In case of error, set the output length and content to a safe default,
+     * such that in case the caller misses an error check, the output would be
+     * an unachievable MAC.
+     */
+    if (status != PSA_SUCCESS) {
+        *mac_length = mac_size;
+        operation->mac_size = 0;
+    }
+
+    if (mac != NULL) {
+        psa_wipe_tag_output_buffer(mac, status, mac_size, *mac_length);
+    }
+
+    abort_status = psa_mac_abort(operation);
+    LOCAL_OUTPUT_FREE(mac_external, mac);
+
+    return status == PSA_SUCCESS ? abort_status : status;
+}
+
+psa_status_t psa_mac_verify_finish(psa_mac_operation_t *operation,
+                                   const uint8_t *mac_external,
+                                   size_t mac_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t abort_status = PSA_ERROR_CORRUPTION_DETECTED;
+    LOCAL_INPUT_DECLARE(mac_external, mac);
+
+    if (operation->id == 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (operation->is_sign) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (operation->mac_size != mac_length) {
+        status = PSA_ERROR_INVALID_SIGNATURE;
+        goto exit;
+    }
+
+    LOCAL_INPUT_ALLOC(mac_external, mac_length, mac);
+    status = psa_driver_wrapper_mac_verify_finish(operation,
+                                                  mac, mac_length);
+
+exit:
+    abort_status = psa_mac_abort(operation);
+    LOCAL_INPUT_FREE(mac_external, mac);
+
+    return status == PSA_SUCCESS ? abort_status : status;
+}
+
+static psa_status_t psa_mac_compute_internal(mbedtls_svc_key_id_t key,
+                                             psa_algorithm_t alg,
+                                             const uint8_t *input,
+                                             size_t input_length,
+                                             uint8_t *mac,
+                                             size_t mac_size,
+                                             size_t *mac_length,
+                                             int is_sign)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+    uint8_t operation_mac_size = 0;
+
+    status = psa_get_and_lock_key_slot_with_policy(
+        key,
+        &slot,
+        is_sign ? PSA_KEY_USAGE_SIGN_MESSAGE : PSA_KEY_USAGE_VERIFY_MESSAGE,
+        alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_mac_finalize_alg_and_key_validation(alg, &slot->attr,
+                                                     &operation_mac_size);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (mac_size < operation_mac_size) {
+        status = PSA_ERROR_BUFFER_TOO_SMALL;
+        goto exit;
+    }
+
+    status = psa_driver_wrapper_mac_compute(
+        &slot->attr,
+        slot->key.data, slot->key.bytes,
+        alg,
+        input, input_length,
+        mac, operation_mac_size, mac_length);
+
+exit:
+    /* In case of success, set the potential excess room in the output buffer
+     * to an invalid value, to avoid potentially leaking a longer MAC.
+     * In case of error, set the output length and content to a safe default,
+     * such that in case the caller misses an error check, the output would be
+     * an unachievable MAC.
+     */
+    if (status != PSA_SUCCESS) {
+        *mac_length = mac_size;
+        operation_mac_size = 0;
+    }
+
+    psa_wipe_tag_output_buffer(mac, status, mac_size, *mac_length);
+
+    unlock_status = psa_unregister_read_under_mutex(slot);
+
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_mac_compute(mbedtls_svc_key_id_t key,
+                             psa_algorithm_t alg,
+                             const uint8_t *input_external,
+                             size_t input_length,
+                             uint8_t *mac_external,
+                             size_t mac_size,
+                             size_t *mac_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    LOCAL_INPUT_DECLARE(input_external, input);
+    LOCAL_OUTPUT_DECLARE(mac_external, mac);
+
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+    LOCAL_OUTPUT_ALLOC(mac_external, mac_size, mac);
+    status = psa_mac_compute_internal(key, alg,
+                                      input, input_length,
+                                      mac, mac_size, mac_length, 1);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+    LOCAL_INPUT_FREE(input_external, input);
+    LOCAL_OUTPUT_FREE(mac_external, mac);
+
+    return status;
+}
+
+psa_status_t psa_mac_verify(mbedtls_svc_key_id_t key,
+                            psa_algorithm_t alg,
+                            const uint8_t *input_external,
+                            size_t input_length,
+                            const uint8_t *mac_external,
+                            size_t mac_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    uint8_t actual_mac[PSA_MAC_MAX_SIZE];
+    size_t actual_mac_length;
+    LOCAL_INPUT_DECLARE(input_external, input);
+    LOCAL_INPUT_DECLARE(mac_external, mac);
+
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+    status = psa_mac_compute_internal(key, alg,
+                                      input, input_length,
+                                      actual_mac, sizeof(actual_mac),
+                                      &actual_mac_length, 0);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (mac_length != actual_mac_length) {
+        status = PSA_ERROR_INVALID_SIGNATURE;
+        goto exit;
+    }
+
+    LOCAL_INPUT_ALLOC(mac_external, mac_length, mac);
+    if (mbedtls_ct_memcmp(mac, actual_mac, actual_mac_length) != 0) {
+        status = PSA_ERROR_INVALID_SIGNATURE;
+        goto exit;
+    }
+
+exit:
+    mbedtls_platform_zeroize(actual_mac, sizeof(actual_mac));
+    LOCAL_INPUT_FREE(input_external, input);
+    LOCAL_INPUT_FREE(mac_external, mac);
+
+    return status;
+}
+
+/****************************************************************/
+/* Asymmetric cryptography */
+/****************************************************************/
+
+static psa_status_t psa_sign_verify_check_alg(int input_is_message,
+                                              psa_algorithm_t alg)
+{
+    if (input_is_message) {
+        if (!PSA_ALG_IS_SIGN_MESSAGE(alg)) {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+
+        if (PSA_ALG_IS_SIGN_HASH(alg)) {
+            if (!PSA_ALG_IS_HASH(PSA_ALG_SIGN_GET_HASH(alg))) {
+                return PSA_ERROR_INVALID_ARGUMENT;
+            }
+        }
+    } else {
+        if (!PSA_ALG_IS_SIGN_HASH(alg)) {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+    }
+
+    return PSA_SUCCESS;
+}
+
+static psa_status_t psa_sign_internal(mbedtls_svc_key_id_t key,
+                                      int input_is_message,
+                                      psa_algorithm_t alg,
+                                      const uint8_t *input,
+                                      size_t input_length,
+                                      uint8_t *signature,
+                                      size_t signature_size,
+                                      size_t *signature_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+
+    *signature_length = 0;
+
+    status = psa_sign_verify_check_alg(input_is_message, alg);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    /* Immediately reject a zero-length signature buffer. This guarantees
+     * that signature must be a valid pointer. (On the other hand, the input
+     * buffer can in principle be empty since it doesn't actually have
+     * to be a hash.) */
+    if (signature_size == 0) {
+        return PSA_ERROR_BUFFER_TOO_SMALL;
+    }
+
+    status = psa_get_and_lock_key_slot_with_policy(
+        key, &slot,
+        input_is_message ? PSA_KEY_USAGE_SIGN_MESSAGE :
+        PSA_KEY_USAGE_SIGN_HASH,
+        alg);
+
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (!PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    if (input_is_message) {
+        status = psa_driver_wrapper_sign_message(
+            &slot->attr, slot->key.data, slot->key.bytes,
+            alg, input, input_length,
+            signature, signature_size, signature_length);
+    } else {
+
+        status = psa_driver_wrapper_sign_hash(
+            &slot->attr, slot->key.data, slot->key.bytes,
+            alg, input, input_length,
+            signature, signature_size, signature_length);
+    }
+
+
+exit:
+    psa_wipe_tag_output_buffer(signature, status, signature_size,
+                               *signature_length);
+
+    unlock_status = psa_unregister_read_under_mutex(slot);
+
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+static psa_status_t psa_verify_internal(mbedtls_svc_key_id_t key,
+                                        int input_is_message,
+                                        psa_algorithm_t alg,
+                                        const uint8_t *input,
+                                        size_t input_length,
+                                        const uint8_t *signature,
+                                        size_t signature_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+
+    status = psa_sign_verify_check_alg(input_is_message, alg);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = psa_get_and_lock_key_slot_with_policy(
+        key, &slot,
+        input_is_message ? PSA_KEY_USAGE_VERIFY_MESSAGE :
+        PSA_KEY_USAGE_VERIFY_HASH,
+        alg);
+
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    if (input_is_message) {
+        status = psa_driver_wrapper_verify_message(
+            &slot->attr, slot->key.data, slot->key.bytes,
+            alg, input, input_length,
+            signature, signature_length);
+    } else {
+        status = psa_driver_wrapper_verify_hash(
+            &slot->attr, slot->key.data, slot->key.bytes,
+            alg, input, input_length,
+            signature, signature_length);
+    }
+
+    unlock_status = psa_unregister_read_under_mutex(slot);
+
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+
+}
+
+psa_status_t psa_sign_message_builtin(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg,
+    const uint8_t *input,
+    size_t input_length,
+    uint8_t *signature,
+    size_t signature_size,
+    size_t *signature_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    if (PSA_ALG_IS_SIGN_HASH(alg)) {
+        size_t hash_length;
+        uint8_t hash[PSA_HASH_MAX_SIZE];
+
+        status = psa_driver_wrapper_hash_compute(
+            PSA_ALG_SIGN_GET_HASH(alg),
+            input, input_length,
+            hash, sizeof(hash), &hash_length);
+
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+
+        return psa_driver_wrapper_sign_hash(
+            attributes, key_buffer, key_buffer_size,
+            alg, hash, hash_length,
+            signature, signature_size, signature_length);
+    }
+
+    return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t psa_sign_message(mbedtls_svc_key_id_t key,
+                              psa_algorithm_t alg,
+                              const uint8_t *input_external,
+                              size_t input_length,
+                              uint8_t *signature_external,
+                              size_t signature_size,
+                              size_t *signature_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    LOCAL_INPUT_DECLARE(input_external, input);
+    LOCAL_OUTPUT_DECLARE(signature_external, signature);
+
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+    LOCAL_OUTPUT_ALLOC(signature_external, signature_size, signature);
+    status = psa_sign_internal(key, 1, alg, input, input_length, signature,
+                               signature_size, signature_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+    LOCAL_INPUT_FREE(input_external, input);
+    LOCAL_OUTPUT_FREE(signature_external, signature);
+    return status;
+}
+
+psa_status_t psa_verify_message_builtin(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg,
+    const uint8_t *input,
+    size_t input_length,
+    const uint8_t *signature,
+    size_t signature_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    if (PSA_ALG_IS_SIGN_HASH(alg)) {
+        size_t hash_length;
+        uint8_t hash[PSA_HASH_MAX_SIZE];
+
+        status = psa_driver_wrapper_hash_compute(
+            PSA_ALG_SIGN_GET_HASH(alg),
+            input, input_length,
+            hash, sizeof(hash), &hash_length);
+
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+
+        return psa_driver_wrapper_verify_hash(
+            attributes, key_buffer, key_buffer_size,
+            alg, hash, hash_length,
+            signature, signature_length);
+    }
+
+    return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t psa_verify_message(mbedtls_svc_key_id_t key,
+                                psa_algorithm_t alg,
+                                const uint8_t *input_external,
+                                size_t input_length,
+                                const uint8_t *signature_external,
+                                size_t signature_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    LOCAL_INPUT_DECLARE(input_external, input);
+    LOCAL_INPUT_DECLARE(signature_external, signature);
+
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+    LOCAL_INPUT_ALLOC(signature_external, signature_length, signature);
+    status = psa_verify_internal(key, 1, alg, input, input_length, signature,
+                                 signature_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+    LOCAL_INPUT_FREE(input_external, input);
+    LOCAL_INPUT_FREE(signature_external, signature);
+
+    return status;
+}
+
+psa_status_t psa_sign_hash_builtin(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+    uint8_t *signature, size_t signature_size, size_t *signature_length)
+{
+    if (attributes->type == PSA_KEY_TYPE_RSA_KEY_PAIR) {
+        if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) ||
+            PSA_ALG_IS_RSA_PSS(alg)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || \
+            defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS)
+            return mbedtls_psa_rsa_sign_hash(
+                attributes,
+                key_buffer, key_buffer_size,
+                alg, hash, hash_length,
+                signature, signature_size, signature_length);
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) */
+        } else {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+    } else if (PSA_KEY_TYPE_IS_ECC(attributes->type)) {
+        if (PSA_ALG_IS_ECDSA(alg)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+            defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)
+            return mbedtls_psa_ecdsa_sign_hash(
+                attributes,
+                key_buffer, key_buffer_size,
+                alg, hash, hash_length,
+                signature, signature_size, signature_length);
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */
+        } else {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+    }
+
+    (void) key_buffer;
+    (void) key_buffer_size;
+    (void) hash;
+    (void) hash_length;
+    (void) signature;
+    (void) signature_size;
+    (void) signature_length;
+
+    return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t psa_sign_hash(mbedtls_svc_key_id_t key,
+                           psa_algorithm_t alg,
+                           const uint8_t *hash_external,
+                           size_t hash_length,
+                           uint8_t *signature_external,
+                           size_t signature_size,
+                           size_t *signature_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    LOCAL_INPUT_DECLARE(hash_external, hash);
+    LOCAL_OUTPUT_DECLARE(signature_external, signature);
+
+    LOCAL_INPUT_ALLOC(hash_external, hash_length, hash);
+    LOCAL_OUTPUT_ALLOC(signature_external, signature_size, signature);
+    status = psa_sign_internal(key, 0, alg, hash, hash_length, signature,
+                               signature_size, signature_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+    LOCAL_INPUT_FREE(hash_external, hash);
+    LOCAL_OUTPUT_FREE(signature_external, signature);
+
+    return status;
+}
+
+psa_status_t psa_verify_hash_builtin(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+    const uint8_t *signature, size_t signature_length)
+{
+    if (PSA_KEY_TYPE_IS_RSA(attributes->type)) {
+        if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) ||
+            PSA_ALG_IS_RSA_PSS(alg)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || \
+            defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS)
+            return mbedtls_psa_rsa_verify_hash(
+                attributes,
+                key_buffer, key_buffer_size,
+                alg, hash, hash_length,
+                signature, signature_length);
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) */
+        } else {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+    } else if (PSA_KEY_TYPE_IS_ECC(attributes->type)) {
+        if (PSA_ALG_IS_ECDSA(alg)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+            defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)
+            return mbedtls_psa_ecdsa_verify_hash(
+                attributes,
+                key_buffer, key_buffer_size,
+                alg, hash, hash_length,
+                signature, signature_length);
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */
+        } else {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+    }
+
+    (void) key_buffer;
+    (void) key_buffer_size;
+    (void) hash;
+    (void) hash_length;
+    (void) signature;
+    (void) signature_length;
+
+    return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t psa_verify_hash(mbedtls_svc_key_id_t key,
+                             psa_algorithm_t alg,
+                             const uint8_t *hash_external,
+                             size_t hash_length,
+                             const uint8_t *signature_external,
+                             size_t signature_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    LOCAL_INPUT_DECLARE(hash_external, hash);
+    LOCAL_INPUT_DECLARE(signature_external, signature);
+
+    LOCAL_INPUT_ALLOC(hash_external, hash_length, hash);
+    LOCAL_INPUT_ALLOC(signature_external, signature_length, signature);
+    status = psa_verify_internal(key, 0, alg, hash, hash_length, signature,
+                                 signature_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+    LOCAL_INPUT_FREE(hash_external, hash);
+    LOCAL_INPUT_FREE(signature_external, signature);
+
+    return status;
+}
+
+psa_status_t psa_asymmetric_encrypt(mbedtls_svc_key_id_t key,
+                                    psa_algorithm_t alg,
+                                    const uint8_t *input_external,
+                                    size_t input_length,
+                                    const uint8_t *salt_external,
+                                    size_t salt_length,
+                                    uint8_t *output_external,
+                                    size_t output_size,
+                                    size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+
+    LOCAL_INPUT_DECLARE(input_external, input);
+    LOCAL_INPUT_DECLARE(salt_external, salt);
+    LOCAL_OUTPUT_DECLARE(output_external, output);
+
+    (void) input;
+    (void) input_length;
+    (void) salt;
+    (void) output;
+    (void) output_size;
+
+    *output_length = 0;
+
+    if (!PSA_ALG_IS_RSA_OAEP(alg) && salt_length != 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    status = psa_get_and_lock_key_slot_with_policy(
+        key, &slot, PSA_KEY_USAGE_ENCRYPT, alg);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+    if (!(PSA_KEY_TYPE_IS_PUBLIC_KEY(slot->attr.type) ||
+          PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type))) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+    LOCAL_INPUT_ALLOC(salt_external, salt_length, salt);
+    LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+    status = psa_driver_wrapper_asymmetric_encrypt(
+        &slot->attr, slot->key.data, slot->key.bytes,
+        alg, input, input_length, salt, salt_length,
+        output, output_size, output_length);
+exit:
+    unlock_status = psa_unregister_read_under_mutex(slot);
+
+    LOCAL_INPUT_FREE(input_external, input);
+    LOCAL_INPUT_FREE(salt_external, salt);
+    LOCAL_OUTPUT_FREE(output_external, output);
+
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_asymmetric_decrypt(mbedtls_svc_key_id_t key,
+                                    psa_algorithm_t alg,
+                                    const uint8_t *input_external,
+                                    size_t input_length,
+                                    const uint8_t *salt_external,
+                                    size_t salt_length,
+                                    uint8_t *output_external,
+                                    size_t output_size,
+                                    size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+
+    LOCAL_INPUT_DECLARE(input_external, input);
+    LOCAL_INPUT_DECLARE(salt_external, salt);
+    LOCAL_OUTPUT_DECLARE(output_external, output);
+
+    (void) input;
+    (void) input_length;
+    (void) salt;
+    (void) output;
+    (void) output_size;
+
+    *output_length = 0;
+
+    if (!PSA_ALG_IS_RSA_OAEP(alg) && salt_length != 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    status = psa_get_and_lock_key_slot_with_policy(
+        key, &slot, PSA_KEY_USAGE_DECRYPT, alg);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+    if (!PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+    LOCAL_INPUT_ALLOC(salt_external, salt_length, salt);
+    LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+    status = psa_driver_wrapper_asymmetric_decrypt(
+        &slot->attr, slot->key.data, slot->key.bytes,
+        alg, input, input_length, salt, salt_length,
+        output, output_size, output_length);
+
+exit:
+    unlock_status = psa_unregister_read_under_mutex(slot);
+
+    LOCAL_INPUT_FREE(input_external, input);
+    LOCAL_INPUT_FREE(salt_external, salt);
+    LOCAL_OUTPUT_FREE(output_external, output);
+
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+/****************************************************************/
+/* Asymmetric interruptible cryptography                        */
+/****************************************************************/
+
+static uint32_t psa_interruptible_max_ops = PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED;
+
+void psa_interruptible_set_max_ops(uint32_t max_ops)
+{
+    psa_interruptible_max_ops = max_ops;
+}
+
+uint32_t psa_interruptible_get_max_ops(void)
+{
+    return psa_interruptible_max_ops;
+}
+
+uint32_t psa_sign_hash_get_num_ops(
+    const psa_sign_hash_interruptible_operation_t *operation)
+{
+    return operation->num_ops;
+}
+
+uint32_t psa_verify_hash_get_num_ops(
+    const psa_verify_hash_interruptible_operation_t *operation)
+{
+    return operation->num_ops;
+}
+
+static psa_status_t psa_sign_hash_abort_internal(
+    psa_sign_hash_interruptible_operation_t *operation)
+{
+    if (operation->id == 0) {
+        /* The object has (apparently) been initialized but it is not (yet)
+         * in use. It's ok to call abort on such an object, and there's
+         * nothing to do. */
+        return PSA_SUCCESS;
+    }
+
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    status = psa_driver_wrapper_sign_hash_abort(operation);
+
+    operation->id = 0;
+
+    /* Do not clear either the error_occurred or num_ops elements here as they
+     * only want to be cleared by the application calling abort, not by abort
+     * being called at completion of an operation. */
+
+    return status;
+}
+
+psa_status_t psa_sign_hash_start(
+    psa_sign_hash_interruptible_operation_t *operation,
+    mbedtls_svc_key_id_t key, psa_algorithm_t alg,
+    const uint8_t *hash_external, size_t hash_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+
+    LOCAL_INPUT_DECLARE(hash_external, hash);
+
+    /* Check that start has not been previously called, or operation has not
+     * previously errored. */
+    if (operation->id != 0 || operation->error_occurred) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    status = psa_sign_verify_check_alg(0, alg);
+    if (status != PSA_SUCCESS) {
+        operation->error_occurred = 1;
+        return status;
+    }
+
+    status = psa_get_and_lock_key_slot_with_policy(key, &slot,
+                                                   PSA_KEY_USAGE_SIGN_HASH,
+                                                   alg);
+
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (!PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    LOCAL_INPUT_ALLOC(hash_external, hash_length, hash);
+
+    /* Ensure ops count gets reset, in case of operation re-use. */
+    operation->num_ops = 0;
+
+    status = psa_driver_wrapper_sign_hash_start(operation, &slot->attr,
+                                                slot->key.data,
+                                                slot->key.bytes, alg,
+                                                hash, hash_length);
+exit:
+
+    if (status != PSA_SUCCESS) {
+        operation->error_occurred = 1;
+        psa_sign_hash_abort_internal(operation);
+    }
+
+    unlock_status = psa_unregister_read_under_mutex(slot);
+
+    if (unlock_status != PSA_SUCCESS) {
+        operation->error_occurred = 1;
+    }
+
+    LOCAL_INPUT_FREE(hash_external, hash);
+
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+
+psa_status_t psa_sign_hash_complete(
+    psa_sign_hash_interruptible_operation_t *operation,
+    uint8_t *signature_external, size_t signature_size,
+    size_t *signature_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    LOCAL_OUTPUT_DECLARE(signature_external, signature);
+
+    *signature_length = 0;
+
+    /* Check that start has been called first, and that operation has not
+     * previously errored. */
+    if (operation->id == 0 || operation->error_occurred) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    /* Immediately reject a zero-length signature buffer. This guarantees that
+     * signature must be a valid pointer. */
+    if (signature_size == 0) {
+        status = PSA_ERROR_BUFFER_TOO_SMALL;
+        goto exit;
+    }
+
+    LOCAL_OUTPUT_ALLOC(signature_external, signature_size, signature);
+
+    status = psa_driver_wrapper_sign_hash_complete(operation, signature,
+                                                   signature_size,
+                                                   signature_length);
+
+    /* Update ops count with work done. */
+    operation->num_ops = psa_driver_wrapper_sign_hash_get_num_ops(operation);
+
+exit:
+
+    if (signature != NULL) {
+        psa_wipe_tag_output_buffer(signature, status, signature_size,
+                                   *signature_length);
+    }
+
+    if (status != PSA_OPERATION_INCOMPLETE) {
+        if (status != PSA_SUCCESS) {
+            operation->error_occurred = 1;
+        }
+
+        psa_sign_hash_abort_internal(operation);
+    }
+
+    LOCAL_OUTPUT_FREE(signature_external, signature);
+
+    return status;
+}
+
+psa_status_t psa_sign_hash_abort(
+    psa_sign_hash_interruptible_operation_t *operation)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    status = psa_sign_hash_abort_internal(operation);
+
+    /* We clear the number of ops done here, so that it is not cleared when
+     * the operation fails or succeeds, only on manual abort. */
+    operation->num_ops = 0;
+
+    /* Likewise, failure state. */
+    operation->error_occurred = 0;
+
+    return status;
+}
+
+static psa_status_t psa_verify_hash_abort_internal(
+    psa_verify_hash_interruptible_operation_t *operation)
+{
+    if (operation->id == 0) {
+        /* The object has (apparently) been initialized but it is not (yet)
+         * in use. It's ok to call abort on such an object, and there's
+         * nothing to do. */
+        return PSA_SUCCESS;
+    }
+
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    status = psa_driver_wrapper_verify_hash_abort(operation);
+
+    operation->id = 0;
+
+    /* Do not clear either the error_occurred or num_ops elements here as they
+     * only want to be cleared by the application calling abort, not by abort
+     * being called at completion of an operation. */
+
+    return status;
+}
+
+psa_status_t psa_verify_hash_start(
+    psa_verify_hash_interruptible_operation_t *operation,
+    mbedtls_svc_key_id_t key, psa_algorithm_t alg,
+    const uint8_t *hash_external, size_t hash_length,
+    const uint8_t *signature_external, size_t signature_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+
+    LOCAL_INPUT_DECLARE(hash_external, hash);
+    LOCAL_INPUT_DECLARE(signature_external, signature);
+
+    /* Check that start has not been previously called, or operation has not
+     * previously errored. */
+    if (operation->id != 0 || operation->error_occurred) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    status = psa_sign_verify_check_alg(0, alg);
+    if (status != PSA_SUCCESS) {
+        operation->error_occurred = 1;
+        return status;
+    }
+
+    status = psa_get_and_lock_key_slot_with_policy(key, &slot,
+                                                   PSA_KEY_USAGE_VERIFY_HASH,
+                                                   alg);
+
+    if (status != PSA_SUCCESS) {
+        operation->error_occurred = 1;
+        return status;
+    }
+
+    LOCAL_INPUT_ALLOC(hash_external, hash_length, hash);
+    LOCAL_INPUT_ALLOC(signature_external, signature_length, signature);
+
+    /* Ensure ops count gets reset, in case of operation re-use. */
+    operation->num_ops = 0;
+
+    status = psa_driver_wrapper_verify_hash_start(operation, &slot->attr,
+                                                  slot->key.data,
+                                                  slot->key.bytes,
+                                                  alg, hash, hash_length,
+                                                  signature, signature_length);
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+
+    if (status != PSA_SUCCESS) {
+        operation->error_occurred = 1;
+        psa_verify_hash_abort_internal(operation);
+    }
+
+    unlock_status = psa_unregister_read_under_mutex(slot);
+
+    if (unlock_status != PSA_SUCCESS) {
+        operation->error_occurred = 1;
+    }
+
+    LOCAL_INPUT_FREE(hash_external, hash);
+    LOCAL_INPUT_FREE(signature_external, signature);
+
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_verify_hash_complete(
+    psa_verify_hash_interruptible_operation_t *operation)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    /* Check that start has been called first, and that operation has not
+     * previously errored. */
+    if (operation->id == 0 || operation->error_occurred) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    status = psa_driver_wrapper_verify_hash_complete(operation);
+
+    /* Update ops count with work done. */
+    operation->num_ops = psa_driver_wrapper_verify_hash_get_num_ops(
+        operation);
+
+exit:
+
+    if (status != PSA_OPERATION_INCOMPLETE) {
+        if (status != PSA_SUCCESS) {
+            operation->error_occurred = 1;
+        }
+
+        psa_verify_hash_abort_internal(operation);
+    }
+
+    return status;
+}
+
+psa_status_t psa_verify_hash_abort(
+    psa_verify_hash_interruptible_operation_t *operation)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    status = psa_verify_hash_abort_internal(operation);
+
+    /* We clear the number of ops done here, so that it is not cleared when
+     * the operation fails or succeeds, only on manual abort. */
+    operation->num_ops = 0;
+
+    /* Likewise, failure state. */
+    operation->error_occurred = 0;
+
+    return status;
+}
+
+/****************************************************************/
+/* Asymmetric interruptible cryptography internal               */
+/* implementations                                              */
+/****************************************************************/
+
+void mbedtls_psa_interruptible_set_max_ops(uint32_t max_ops)
+{
+
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+    defined(MBEDTLS_ECP_RESTARTABLE)
+
+    /* Internal implementation uses zero to indicate infinite number max ops,
+     * therefore avoid this value, and set to minimum possible. */
+    if (max_ops == 0) {
+        max_ops = 1;
+    }
+
+    mbedtls_ecp_set_max_ops(max_ops);
+#else
+    (void) max_ops;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+        * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+uint32_t mbedtls_psa_sign_hash_get_num_ops(
+    const mbedtls_psa_sign_hash_interruptible_operation_t *operation)
+{
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+    defined(MBEDTLS_ECP_RESTARTABLE)
+
+    return operation->num_ops;
+#else
+    (void) operation;
+    return 0;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+        * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+uint32_t mbedtls_psa_verify_hash_get_num_ops(
+    const mbedtls_psa_verify_hash_interruptible_operation_t *operation)
+{
+    #if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+    defined(MBEDTLS_ECP_RESTARTABLE)
+
+    return operation->num_ops;
+#else
+    (void) operation;
+    return 0;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+        * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+psa_status_t mbedtls_psa_sign_hash_start(
+    mbedtls_psa_sign_hash_interruptible_operation_t *operation,
+    const psa_key_attributes_t *attributes, const uint8_t *key_buffer,
+    size_t key_buffer_size, psa_algorithm_t alg,
+    const uint8_t *hash, size_t hash_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    size_t required_hash_length;
+
+    if (!PSA_KEY_TYPE_IS_ECC(attributes->type)) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    if (!PSA_ALG_IS_ECDSA(alg)) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+    defined(MBEDTLS_ECP_RESTARTABLE)
+
+    mbedtls_ecdsa_restart_init(&operation->restart_ctx);
+
+    /* Ensure num_ops is zero'ed in case of context re-use. */
+    operation->num_ops = 0;
+
+    status = mbedtls_psa_ecp_load_representation(attributes->type,
+                                                 attributes->bits,
+                                                 key_buffer,
+                                                 key_buffer_size,
+                                                 &operation->ctx);
+
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    operation->coordinate_bytes = PSA_BITS_TO_BYTES(
+        operation->ctx->grp.nbits);
+
+    psa_algorithm_t hash_alg = PSA_ALG_SIGN_GET_HASH(alg);
+    operation->md_alg = mbedtls_md_type_from_psa_alg(hash_alg);
+    operation->alg = alg;
+
+    /* We only need to store the same length of hash as the private key size
+     * here, it would be truncated by the internal implementation anyway. */
+    required_hash_length = (hash_length < operation->coordinate_bytes ?
+                            hash_length : operation->coordinate_bytes);
+
+    if (required_hash_length > sizeof(operation->hash)) {
+        /* Shouldn't happen, but better safe than sorry. */
+        return PSA_ERROR_CORRUPTION_DETECTED;
+    }
+
+    memcpy(operation->hash, hash, required_hash_length);
+    operation->hash_length = required_hash_length;
+
+    return PSA_SUCCESS;
+
+#else
+    (void) operation;
+    (void) key_buffer;
+    (void) key_buffer_size;
+    (void) alg;
+    (void) hash;
+    (void) hash_length;
+    (void) status;
+    (void) required_hash_length;
+
+    return PSA_ERROR_NOT_SUPPORTED;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+        * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+psa_status_t mbedtls_psa_sign_hash_complete(
+    mbedtls_psa_sign_hash_interruptible_operation_t *operation,
+    uint8_t *signature, size_t signature_size,
+    size_t *signature_length)
+{
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+    defined(MBEDTLS_ECP_RESTARTABLE)
+
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_mpi r;
+    mbedtls_mpi s;
+
+    mbedtls_mpi_init(&r);
+    mbedtls_mpi_init(&s);
+
+    /* Ensure max_ops is set to the current value (or default). */
+    mbedtls_psa_interruptible_set_max_ops(psa_interruptible_get_max_ops());
+
+    if (signature_size < 2 * operation->coordinate_bytes) {
+        status = PSA_ERROR_BUFFER_TOO_SMALL;
+        goto exit;
+    }
+
+    if (PSA_ALG_ECDSA_IS_DETERMINISTIC(operation->alg)) {
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)
+        status = mbedtls_to_psa_error(
+            mbedtls_ecdsa_sign_det_restartable(&operation->ctx->grp,
+                                               &r,
+                                               &s,
+                                               &operation->ctx->d,
+                                               operation->hash,
+                                               operation->hash_length,
+                                               operation->md_alg,
+                                               mbedtls_psa_get_random,
+                                               MBEDTLS_PSA_RANDOM_STATE,
+                                               &operation->restart_ctx));
+#else /* defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */
+        status = PSA_ERROR_NOT_SUPPORTED;
+        goto exit;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */
+    } else {
+        status = mbedtls_to_psa_error(
+            mbedtls_ecdsa_sign_restartable(&operation->ctx->grp,
+                                           &r,
+                                           &s,
+                                           &operation->ctx->d,
+                                           operation->hash,
+                                           operation->hash_length,
+                                           mbedtls_psa_get_random,
+                                           MBEDTLS_PSA_RANDOM_STATE,
+                                           mbedtls_psa_get_random,
+                                           MBEDTLS_PSA_RANDOM_STATE,
+                                           &operation->restart_ctx));
+    }
+
+    /* Hide the fact that the restart context only holds a delta of number of
+     * ops done during the last operation, not an absolute value. */
+    operation->num_ops += operation->restart_ctx.ecp.ops_done;
+
+    if (status == PSA_SUCCESS) {
+        status =  mbedtls_to_psa_error(
+            mbedtls_mpi_write_binary(&r,
+                                     signature,
+                                     operation->coordinate_bytes)
+            );
+
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+
+        status =  mbedtls_to_psa_error(
+            mbedtls_mpi_write_binary(&s,
+                                     signature +
+                                     operation->coordinate_bytes,
+                                     operation->coordinate_bytes)
+            );
+
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+
+        *signature_length = operation->coordinate_bytes * 2;
+
+        status = PSA_SUCCESS;
+    }
+
+exit:
+
+    mbedtls_mpi_free(&r);
+    mbedtls_mpi_free(&s);
+    return status;
+
+ #else
+
+    (void) operation;
+    (void) signature;
+    (void) signature_size;
+    (void) signature_length;
+
+    return PSA_ERROR_NOT_SUPPORTED;
+
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+        * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+psa_status_t mbedtls_psa_sign_hash_abort(
+    mbedtls_psa_sign_hash_interruptible_operation_t *operation)
+{
+
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+    defined(MBEDTLS_ECP_RESTARTABLE)
+
+    if (operation->ctx) {
+        mbedtls_ecdsa_free(operation->ctx);
+        mbedtls_free(operation->ctx);
+        operation->ctx = NULL;
+    }
+
+    mbedtls_ecdsa_restart_free(&operation->restart_ctx);
+
+    operation->num_ops = 0;
+
+    return PSA_SUCCESS;
+
+#else
+
+    (void) operation;
+
+    return PSA_ERROR_NOT_SUPPORTED;
+
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+        * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+psa_status_t mbedtls_psa_verify_hash_start(
+    mbedtls_psa_verify_hash_interruptible_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg,
+    const uint8_t *hash, size_t hash_length,
+    const uint8_t *signature, size_t signature_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    size_t coordinate_bytes = 0;
+    size_t required_hash_length = 0;
+
+    if (!PSA_KEY_TYPE_IS_ECC(attributes->type)) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    if (!PSA_ALG_IS_ECDSA(alg)) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+    defined(MBEDTLS_ECP_RESTARTABLE)
+
+    mbedtls_ecdsa_restart_init(&operation->restart_ctx);
+    mbedtls_mpi_init(&operation->r);
+    mbedtls_mpi_init(&operation->s);
+
+    /* Ensure num_ops is zero'ed in case of context re-use. */
+    operation->num_ops = 0;
+
+    status = mbedtls_psa_ecp_load_representation(attributes->type,
+                                                 attributes->bits,
+                                                 key_buffer,
+                                                 key_buffer_size,
+                                                 &operation->ctx);
+
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    coordinate_bytes = PSA_BITS_TO_BYTES(operation->ctx->grp.nbits);
+
+    if (signature_length != 2 * coordinate_bytes) {
+        return PSA_ERROR_INVALID_SIGNATURE;
+    }
+
+    status = mbedtls_to_psa_error(
+        mbedtls_mpi_read_binary(&operation->r,
+                                signature,
+                                coordinate_bytes));
+
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = mbedtls_to_psa_error(
+        mbedtls_mpi_read_binary(&operation->s,
+                                signature +
+                                coordinate_bytes,
+                                coordinate_bytes));
+
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = mbedtls_psa_ecp_load_public_part(operation->ctx);
+
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    /* We only need to store the same length of hash as the private key size
+     * here, it would be truncated by the internal implementation anyway. */
+    required_hash_length = (hash_length < coordinate_bytes ? hash_length :
+                            coordinate_bytes);
+
+    if (required_hash_length > sizeof(operation->hash)) {
+        /* Shouldn't happen, but better safe than sorry. */
+        return PSA_ERROR_CORRUPTION_DETECTED;
+    }
+
+    memcpy(operation->hash, hash, required_hash_length);
+    operation->hash_length = required_hash_length;
+
+    return PSA_SUCCESS;
+#else
+    (void) operation;
+    (void) key_buffer;
+    (void) key_buffer_size;
+    (void) alg;
+    (void) hash;
+    (void) hash_length;
+    (void) signature;
+    (void) signature_length;
+    (void) status;
+    (void) coordinate_bytes;
+    (void) required_hash_length;
+
+    return PSA_ERROR_NOT_SUPPORTED;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+        * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+psa_status_t mbedtls_psa_verify_hash_complete(
+    mbedtls_psa_verify_hash_interruptible_operation_t *operation)
+{
+
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+    defined(MBEDTLS_ECP_RESTARTABLE)
+
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    /* Ensure max_ops is set to the current value (or default). */
+    mbedtls_psa_interruptible_set_max_ops(psa_interruptible_get_max_ops());
+
+    status = mbedtls_to_psa_error(
+        mbedtls_ecdsa_verify_restartable(&operation->ctx->grp,
+                                         operation->hash,
+                                         operation->hash_length,
+                                         &operation->ctx->Q,
+                                         &operation->r,
+                                         &operation->s,
+                                         &operation->restart_ctx));
+
+    /* Hide the fact that the restart context only holds a delta of number of
+     * ops done during the last operation, not an absolute value. */
+    operation->num_ops += operation->restart_ctx.ecp.ops_done;
+
+    return status;
+#else
+    (void) operation;
+
+    return PSA_ERROR_NOT_SUPPORTED;
+
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+        * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+psa_status_t mbedtls_psa_verify_hash_abort(
+    mbedtls_psa_verify_hash_interruptible_operation_t *operation)
+{
+
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+    defined(MBEDTLS_ECP_RESTARTABLE)
+
+    if (operation->ctx) {
+        mbedtls_ecdsa_free(operation->ctx);
+        mbedtls_free(operation->ctx);
+        operation->ctx = NULL;
+    }
+
+    mbedtls_ecdsa_restart_free(&operation->restart_ctx);
+
+    operation->num_ops = 0;
+
+    mbedtls_mpi_free(&operation->r);
+    mbedtls_mpi_free(&operation->s);
+
+    return PSA_SUCCESS;
+
+#else
+    (void) operation;
+
+    return PSA_ERROR_NOT_SUPPORTED;
+
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+        * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+static psa_status_t psa_generate_random_internal(uint8_t *output,
+                                                 size_t output_size)
+{
+    GUARD_MODULE_INITIALIZED;
+
+#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+
+    psa_status_t status;
+    size_t output_length = 0;
+    status = mbedtls_psa_external_get_random(&global_data.rng,
+                                             output, output_size,
+                                             &output_length);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+    /* Breaking up a request into smaller chunks is currently not supported
+     * for the external RNG interface. */
+    if (output_length != output_size) {
+        return PSA_ERROR_INSUFFICIENT_ENTROPY;
+    }
+    return PSA_SUCCESS;
+
+#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+
+    while (output_size > 0) {
+        int ret = MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED;
+        size_t request_size =
+            (output_size > MBEDTLS_PSA_RANDOM_MAX_REQUEST ?
+             MBEDTLS_PSA_RANDOM_MAX_REQUEST :
+             output_size);
+#if defined(MBEDTLS_CTR_DRBG_C)
+        ret = mbedtls_ctr_drbg_random(&global_data.rng.drbg, output, request_size);
+#elif defined(MBEDTLS_HMAC_DRBG_C)
+        ret = mbedtls_hmac_drbg_random(&global_data.rng.drbg, output, request_size);
+#endif /* !MBEDTLS_CTR_DRBG_C && !MBEDTLS_HMAC_DRBG_C */
+        if (ret != 0) {
+            return mbedtls_to_psa_error(ret);
+        }
+        output_size -= request_size;
+        output += request_size;
+    }
+    return PSA_SUCCESS;
+#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+}
+
+
+/****************************************************************/
+/* Symmetric cryptography */
+/****************************************************************/
+
+static psa_status_t psa_cipher_setup(psa_cipher_operation_t *operation,
+                                     mbedtls_svc_key_id_t key,
+                                     psa_algorithm_t alg,
+                                     mbedtls_operation_t cipher_operation)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot = NULL;
+    psa_key_usage_t usage = (cipher_operation == MBEDTLS_ENCRYPT ?
+                             PSA_KEY_USAGE_ENCRYPT :
+                             PSA_KEY_USAGE_DECRYPT);
+
+    /* A context must be freshly initialized before it can be set up. */
+    if (operation->id != 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (!PSA_ALG_IS_CIPHER(alg)) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    status = psa_get_and_lock_key_slot_with_policy(key, &slot, usage, alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    /* Initialize the operation struct members, except for id. The id member
+     * is used to indicate to psa_cipher_abort that there are resources to free,
+     * so we only set it (in the driver wrapper) after resources have been
+     * allocated/initialized. */
+    operation->iv_set = 0;
+    if (alg == PSA_ALG_ECB_NO_PADDING) {
+        operation->iv_required = 0;
+    } else {
+        operation->iv_required = 1;
+    }
+    operation->default_iv_length = PSA_CIPHER_IV_LENGTH(slot->attr.type, alg);
+
+    /* Try doing the operation through a driver before using software fallback. */
+    if (cipher_operation == MBEDTLS_ENCRYPT) {
+        status = psa_driver_wrapper_cipher_encrypt_setup(operation,
+                                                         &slot->attr,
+                                                         slot->key.data,
+                                                         slot->key.bytes,
+                                                         alg);
+    } else {
+        status = psa_driver_wrapper_cipher_decrypt_setup(operation,
+                                                         &slot->attr,
+                                                         slot->key.data,
+                                                         slot->key.bytes,
+                                                         alg);
+    }
+
+exit:
+    if (status != PSA_SUCCESS) {
+        psa_cipher_abort(operation);
+    }
+
+    unlock_status = psa_unregister_read_under_mutex(slot);
+
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_cipher_encrypt_setup(psa_cipher_operation_t *operation,
+                                      mbedtls_svc_key_id_t key,
+                                      psa_algorithm_t alg)
+{
+    return psa_cipher_setup(operation, key, alg, MBEDTLS_ENCRYPT);
+}
+
+psa_status_t psa_cipher_decrypt_setup(psa_cipher_operation_t *operation,
+                                      mbedtls_svc_key_id_t key,
+                                      psa_algorithm_t alg)
+{
+    return psa_cipher_setup(operation, key, alg, MBEDTLS_DECRYPT);
+}
+
+psa_status_t psa_cipher_generate_iv(psa_cipher_operation_t *operation,
+                                    uint8_t *iv_external,
+                                    size_t iv_size,
+                                    size_t *iv_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    size_t default_iv_length = 0;
+
+    LOCAL_OUTPUT_DECLARE(iv_external, iv);
+
+    if (operation->id == 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (operation->iv_set || !operation->iv_required) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    default_iv_length = operation->default_iv_length;
+    if (iv_size < default_iv_length) {
+        status = PSA_ERROR_BUFFER_TOO_SMALL;
+        goto exit;
+    }
+
+    if (default_iv_length > PSA_CIPHER_IV_MAX_SIZE) {
+        status = PSA_ERROR_GENERIC_ERROR;
+        goto exit;
+    }
+
+    LOCAL_OUTPUT_ALLOC(iv_external, default_iv_length, iv);
+
+    status = psa_generate_random_internal(iv, default_iv_length);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_driver_wrapper_cipher_set_iv(operation,
+                                              iv, default_iv_length);
+
+exit:
+    if (status == PSA_SUCCESS) {
+        *iv_length = default_iv_length;
+        operation->iv_set = 1;
+    } else {
+        *iv_length = 0;
+        psa_cipher_abort(operation);
+        if (iv != NULL) {
+            mbedtls_platform_zeroize(iv, default_iv_length);
+        }
+    }
+
+    LOCAL_OUTPUT_FREE(iv_external, iv);
+    return status;
+}
+
+psa_status_t psa_cipher_set_iv(psa_cipher_operation_t *operation,
+                               const uint8_t *iv_external,
+                               size_t iv_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    LOCAL_INPUT_DECLARE(iv_external, iv);
+
+    if (operation->id == 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (operation->iv_set || !operation->iv_required) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (iv_length > PSA_CIPHER_IV_MAX_SIZE) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    LOCAL_INPUT_ALLOC(iv_external, iv_length, iv);
+
+    status = psa_driver_wrapper_cipher_set_iv(operation,
+                                              iv,
+                                              iv_length);
+
+exit:
+    if (status == PSA_SUCCESS) {
+        operation->iv_set = 1;
+    } else {
+        psa_cipher_abort(operation);
+    }
+
+    LOCAL_INPUT_FREE(iv_external, iv);
+
+    return status;
+}
+
+psa_status_t psa_cipher_update(psa_cipher_operation_t *operation,
+                               const uint8_t *input_external,
+                               size_t input_length,
+                               uint8_t *output_external,
+                               size_t output_size,
+                               size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    LOCAL_INPUT_DECLARE(input_external, input);
+    LOCAL_OUTPUT_DECLARE(output_external, output);
+
+    if (operation->id == 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (operation->iv_required && !operation->iv_set) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+    LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+    status = psa_driver_wrapper_cipher_update(operation,
+                                              input,
+                                              input_length,
+                                              output,
+                                              output_size,
+                                              output_length);
+
+exit:
+    if (status != PSA_SUCCESS) {
+        psa_cipher_abort(operation);
+    }
+
+    LOCAL_INPUT_FREE(input_external, input);
+    LOCAL_OUTPUT_FREE(output_external, output);
+
+    return status;
+}
+
+psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation,
+                               uint8_t *output_external,
+                               size_t output_size,
+                               size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_GENERIC_ERROR;
+
+    LOCAL_OUTPUT_DECLARE(output_external, output);
+
+    if (operation->id == 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (operation->iv_required && !operation->iv_set) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+    status = psa_driver_wrapper_cipher_finish(operation,
+                                              output,
+                                              output_size,
+                                              output_length);
+
+exit:
+    if (status == PSA_SUCCESS) {
+        status = psa_cipher_abort(operation);
+    } else {
+        *output_length = 0;
+        (void) psa_cipher_abort(operation);
+    }
+
+    LOCAL_OUTPUT_FREE(output_external, output);
+
+    return status;
+}
+
+psa_status_t psa_cipher_abort(psa_cipher_operation_t *operation)
+{
+    if (operation->id == 0) {
+        /* The object has (apparently) been initialized but it is not (yet)
+         * in use. It's ok to call abort on such an object, and there's
+         * nothing to do. */
+        return PSA_SUCCESS;
+    }
+
+    psa_driver_wrapper_cipher_abort(operation);
+
+    operation->id = 0;
+    operation->iv_set = 0;
+    operation->iv_required = 0;
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_cipher_encrypt(mbedtls_svc_key_id_t key,
+                                psa_algorithm_t alg,
+                                const uint8_t *input_external,
+                                size_t input_length,
+                                uint8_t *output_external,
+                                size_t output_size,
+                                size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot = NULL;
+    uint8_t local_iv[PSA_CIPHER_IV_MAX_SIZE];
+    size_t default_iv_length = 0;
+
+    LOCAL_INPUT_DECLARE(input_external, input);
+    LOCAL_OUTPUT_DECLARE(output_external, output);
+
+    if (!PSA_ALG_IS_CIPHER(alg)) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    status = psa_get_and_lock_key_slot_with_policy(key, &slot,
+                                                   PSA_KEY_USAGE_ENCRYPT,
+                                                   alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    default_iv_length = PSA_CIPHER_IV_LENGTH(slot->attr.type, alg);
+    if (default_iv_length > PSA_CIPHER_IV_MAX_SIZE) {
+        status = PSA_ERROR_GENERIC_ERROR;
+        goto exit;
+    }
+
+    if (default_iv_length > 0) {
+        if (output_size < default_iv_length) {
+            status = PSA_ERROR_BUFFER_TOO_SMALL;
+            goto exit;
+        }
+
+        status = psa_generate_random_internal(local_iv, default_iv_length);
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+    }
+
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+    LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+    status = psa_driver_wrapper_cipher_encrypt(
+        &slot->attr, slot->key.data, slot->key.bytes,
+        alg, local_iv, default_iv_length, input, input_length,
+        psa_crypto_buffer_offset(output, default_iv_length),
+        output_size - default_iv_length, output_length);
+
+exit:
+    unlock_status = psa_unregister_read_under_mutex(slot);
+    if (status == PSA_SUCCESS) {
+        status = unlock_status;
+    }
+
+    if (status == PSA_SUCCESS) {
+        if (default_iv_length > 0) {
+            memcpy(output, local_iv, default_iv_length);
+        }
+        *output_length += default_iv_length;
+    } else {
+        *output_length = 0;
+    }
+
+    LOCAL_INPUT_FREE(input_external, input);
+    LOCAL_OUTPUT_FREE(output_external, output);
+
+    return status;
+}
+
+psa_status_t psa_cipher_decrypt(mbedtls_svc_key_id_t key,
+                                psa_algorithm_t alg,
+                                const uint8_t *input_external,
+                                size_t input_length,
+                                uint8_t *output_external,
+                                size_t output_size,
+                                size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot = NULL;
+
+    LOCAL_INPUT_DECLARE(input_external, input);
+    LOCAL_OUTPUT_DECLARE(output_external, output);
+
+    if (!PSA_ALG_IS_CIPHER(alg)) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    status = psa_get_and_lock_key_slot_with_policy(key, &slot,
+                                                   PSA_KEY_USAGE_DECRYPT,
+                                                   alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (input_length < PSA_CIPHER_IV_LENGTH(slot->attr.type, alg)) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+    LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+    status = psa_driver_wrapper_cipher_decrypt(
+        &slot->attr, slot->key.data, slot->key.bytes,
+        alg, input, input_length,
+        output, output_size, output_length);
+
+exit:
+    unlock_status = psa_unregister_read_under_mutex(slot);
+    if (status == PSA_SUCCESS) {
+        status = unlock_status;
+    }
+
+    if (status != PSA_SUCCESS) {
+        *output_length = 0;
+    }
+
+    LOCAL_INPUT_FREE(input_external, input);
+    LOCAL_OUTPUT_FREE(output_external, output);
+
+    return status;
+}
+
+
+/****************************************************************/
+/* AEAD */
+/****************************************************************/
+
+/* Helper function to get the base algorithm from its variants. */
+static psa_algorithm_t psa_aead_get_base_algorithm(psa_algorithm_t alg)
+{
+    return PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(alg);
+}
+
+/* Helper function to perform common nonce length checks. */
+static psa_status_t psa_aead_check_nonce_length(psa_algorithm_t alg,
+                                                size_t nonce_length)
+{
+    psa_algorithm_t base_alg = psa_aead_get_base_algorithm(alg);
+
+    switch (base_alg) {
+#if defined(PSA_WANT_ALG_GCM)
+        case PSA_ALG_GCM:
+            /* Not checking max nonce size here as GCM spec allows almost
+             * arbitrarily large nonces. Please note that we do not generally
+             * recommend the usage of nonces of greater length than
+             * PSA_AEAD_NONCE_MAX_SIZE, as large nonces are hashed to a shorter
+             * size, which can then lead to collisions if you encrypt a very
+             * large number of messages.*/
+            if (nonce_length != 0) {
+                return PSA_SUCCESS;
+            }
+            break;
+#endif /* PSA_WANT_ALG_GCM */
+#if defined(PSA_WANT_ALG_CCM)
+        case PSA_ALG_CCM:
+            if (nonce_length >= 7 && nonce_length <= 13) {
+                return PSA_SUCCESS;
+            }
+            break;
+#endif /* PSA_WANT_ALG_CCM */
+#if defined(PSA_WANT_ALG_CHACHA20_POLY1305)
+        case PSA_ALG_CHACHA20_POLY1305:
+            if (nonce_length == 12) {
+                return PSA_SUCCESS;
+            } else if (nonce_length == 8) {
+                return PSA_ERROR_NOT_SUPPORTED;
+            }
+            break;
+#endif /* PSA_WANT_ALG_CHACHA20_POLY1305 */
+        default:
+            (void) nonce_length;
+            return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    return PSA_ERROR_INVALID_ARGUMENT;
+}
+
+static psa_status_t psa_aead_check_algorithm(psa_algorithm_t alg)
+{
+    if (!PSA_ALG_IS_AEAD(alg) || PSA_ALG_IS_WILDCARD(alg)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_aead_encrypt(mbedtls_svc_key_id_t key,
+                              psa_algorithm_t alg,
+                              const uint8_t *nonce_external,
+                              size_t nonce_length,
+                              const uint8_t *additional_data_external,
+                              size_t additional_data_length,
+                              const uint8_t *plaintext_external,
+                              size_t plaintext_length,
+                              uint8_t *ciphertext_external,
+                              size_t ciphertext_size,
+                              size_t *ciphertext_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+
+    LOCAL_INPUT_DECLARE(nonce_external, nonce);
+    LOCAL_INPUT_DECLARE(additional_data_external, additional_data);
+    LOCAL_INPUT_DECLARE(plaintext_external, plaintext);
+    LOCAL_OUTPUT_DECLARE(ciphertext_external, ciphertext);
+
+    *ciphertext_length = 0;
+
+    status = psa_aead_check_algorithm(alg);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = psa_get_and_lock_key_slot_with_policy(
+        key, &slot, PSA_KEY_USAGE_ENCRYPT, alg);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    LOCAL_INPUT_ALLOC(nonce_external, nonce_length, nonce);
+    LOCAL_INPUT_ALLOC(additional_data_external, additional_data_length, additional_data);
+    LOCAL_INPUT_ALLOC(plaintext_external, plaintext_length, plaintext);
+    LOCAL_OUTPUT_ALLOC(ciphertext_external, ciphertext_size, ciphertext);
+
+    status = psa_aead_check_nonce_length(alg, nonce_length);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_driver_wrapper_aead_encrypt(
+        &slot->attr, slot->key.data, slot->key.bytes,
+        alg,
+        nonce, nonce_length,
+        additional_data, additional_data_length,
+        plaintext, plaintext_length,
+        ciphertext, ciphertext_size, ciphertext_length);
+
+    if (status != PSA_SUCCESS && ciphertext_size != 0) {
+        memset(ciphertext, 0, ciphertext_size);
+    }
+
+exit:
+    LOCAL_INPUT_FREE(nonce_external, nonce);
+    LOCAL_INPUT_FREE(additional_data_external, additional_data);
+    LOCAL_INPUT_FREE(plaintext_external, plaintext);
+    LOCAL_OUTPUT_FREE(ciphertext_external, ciphertext);
+
+    psa_unregister_read_under_mutex(slot);
+
+    return status;
+}
+
+psa_status_t psa_aead_decrypt(mbedtls_svc_key_id_t key,
+                              psa_algorithm_t alg,
+                              const uint8_t *nonce_external,
+                              size_t nonce_length,
+                              const uint8_t *additional_data_external,
+                              size_t additional_data_length,
+                              const uint8_t *ciphertext_external,
+                              size_t ciphertext_length,
+                              uint8_t *plaintext_external,
+                              size_t plaintext_size,
+                              size_t *plaintext_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+
+    LOCAL_INPUT_DECLARE(nonce_external, nonce);
+    LOCAL_INPUT_DECLARE(additional_data_external, additional_data);
+    LOCAL_INPUT_DECLARE(ciphertext_external, ciphertext);
+    LOCAL_OUTPUT_DECLARE(plaintext_external, plaintext);
+
+    *plaintext_length = 0;
+
+    status = psa_aead_check_algorithm(alg);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = psa_get_and_lock_key_slot_with_policy(
+        key, &slot, PSA_KEY_USAGE_DECRYPT, alg);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    LOCAL_INPUT_ALLOC(nonce_external, nonce_length, nonce);
+    LOCAL_INPUT_ALLOC(additional_data_external, additional_data_length,
+                      additional_data);
+    LOCAL_INPUT_ALLOC(ciphertext_external, ciphertext_length, ciphertext);
+    LOCAL_OUTPUT_ALLOC(plaintext_external, plaintext_size, plaintext);
+
+    status = psa_aead_check_nonce_length(alg, nonce_length);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_driver_wrapper_aead_decrypt(
+        &slot->attr, slot->key.data, slot->key.bytes,
+        alg,
+        nonce, nonce_length,
+        additional_data, additional_data_length,
+        ciphertext, ciphertext_length,
+        plaintext, plaintext_size, plaintext_length);
+
+    if (status != PSA_SUCCESS && plaintext_size != 0) {
+        memset(plaintext, 0, plaintext_size);
+    }
+
+exit:
+    LOCAL_INPUT_FREE(nonce_external, nonce);
+    LOCAL_INPUT_FREE(additional_data_external, additional_data);
+    LOCAL_INPUT_FREE(ciphertext_external, ciphertext);
+    LOCAL_OUTPUT_FREE(plaintext_external, plaintext);
+
+    psa_unregister_read_under_mutex(slot);
+
+    return status;
+}
+
+static psa_status_t psa_validate_tag_length(psa_algorithm_t alg)
+{
+    const uint8_t tag_len = PSA_ALG_AEAD_GET_TAG_LENGTH(alg);
+
+    switch (PSA_ALG_AEAD_WITH_SHORTENED_TAG(alg, 0)) {
+#if defined(PSA_WANT_ALG_CCM)
+        case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, 0):
+            /* CCM allows the following tag lengths: 4, 6, 8, 10, 12, 14, 16.*/
+            if (tag_len < 4 || tag_len > 16 || tag_len % 2) {
+                return PSA_ERROR_INVALID_ARGUMENT;
+            }
+            break;
+#endif /* PSA_WANT_ALG_CCM */
+
+#if defined(PSA_WANT_ALG_GCM)
+        case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 0):
+            /* GCM allows the following tag lengths: 4, 8, 12, 13, 14, 15, 16. */
+            if (tag_len != 4 && tag_len != 8 && (tag_len < 12 || tag_len > 16)) {
+                return PSA_ERROR_INVALID_ARGUMENT;
+            }
+            break;
+#endif /* PSA_WANT_ALG_GCM */
+
+#if defined(PSA_WANT_ALG_CHACHA20_POLY1305)
+        case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305, 0):
+            /* We only support the default tag length. */
+            if (tag_len != 16) {
+                return PSA_ERROR_INVALID_ARGUMENT;
+            }
+            break;
+#endif /* PSA_WANT_ALG_CHACHA20_POLY1305 */
+
+        default:
+            (void) tag_len;
+            return PSA_ERROR_NOT_SUPPORTED;
+    }
+    return PSA_SUCCESS;
+}
+
+/* Set the key for a multipart authenticated operation. */
+static psa_status_t psa_aead_setup(psa_aead_operation_t *operation,
+                                   int is_encrypt,
+                                   mbedtls_svc_key_id_t key,
+                                   psa_algorithm_t alg)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot = NULL;
+    psa_key_usage_t key_usage = 0;
+
+    status = psa_aead_check_algorithm(alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (operation->id != 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (operation->nonce_set || operation->lengths_set ||
+        operation->ad_started || operation->body_started) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (is_encrypt) {
+        key_usage = PSA_KEY_USAGE_ENCRYPT;
+    } else {
+        key_usage = PSA_KEY_USAGE_DECRYPT;
+    }
+
+    status = psa_get_and_lock_key_slot_with_policy(key, &slot, key_usage,
+                                                   alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if ((status = psa_validate_tag_length(alg)) != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (is_encrypt) {
+        status = psa_driver_wrapper_aead_encrypt_setup(operation,
+                                                       &slot->attr,
+                                                       slot->key.data,
+                                                       slot->key.bytes,
+                                                       alg);
+    } else {
+        status = psa_driver_wrapper_aead_decrypt_setup(operation,
+                                                       &slot->attr,
+                                                       slot->key.data,
+                                                       slot->key.bytes,
+                                                       alg);
+    }
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    operation->key_type = psa_get_key_type(&slot->attr);
+
+exit:
+    unlock_status = psa_unregister_read_under_mutex(slot);
+
+    if (status == PSA_SUCCESS) {
+        status = unlock_status;
+        operation->alg = psa_aead_get_base_algorithm(alg);
+        operation->is_encrypt = is_encrypt;
+    } else {
+        psa_aead_abort(operation);
+    }
+
+    return status;
+}
+
+/* Set the key for a multipart authenticated encryption operation. */
+psa_status_t psa_aead_encrypt_setup(psa_aead_operation_t *operation,
+                                    mbedtls_svc_key_id_t key,
+                                    psa_algorithm_t alg)
+{
+    return psa_aead_setup(operation, 1, key, alg);
+}
+
+/* Set the key for a multipart authenticated decryption operation. */
+psa_status_t psa_aead_decrypt_setup(psa_aead_operation_t *operation,
+                                    mbedtls_svc_key_id_t key,
+                                    psa_algorithm_t alg)
+{
+    return psa_aead_setup(operation, 0, key, alg);
+}
+
+static psa_status_t psa_aead_set_nonce_internal(psa_aead_operation_t *operation,
+                                                const uint8_t *nonce,
+                                                size_t nonce_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    if (operation->id == 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (operation->nonce_set) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    status = psa_aead_check_nonce_length(operation->alg, nonce_length);
+    if (status != PSA_SUCCESS) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    status = psa_driver_wrapper_aead_set_nonce(operation, nonce,
+                                               nonce_length);
+
+exit:
+    if (status == PSA_SUCCESS) {
+        operation->nonce_set = 1;
+    } else {
+        psa_aead_abort(operation);
+    }
+
+    return status;
+}
+
+/* Generate a random nonce / IV for multipart AEAD operation */
+psa_status_t psa_aead_generate_nonce(psa_aead_operation_t *operation,
+                                     uint8_t *nonce_external,
+                                     size_t nonce_size,
+                                     size_t *nonce_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    uint8_t local_nonce[PSA_AEAD_NONCE_MAX_SIZE];
+    size_t required_nonce_size = 0;
+
+    LOCAL_OUTPUT_DECLARE(nonce_external, nonce);
+    LOCAL_OUTPUT_ALLOC(nonce_external, nonce_size, nonce);
+
+    *nonce_length = 0;
+
+    if (operation->id == 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (operation->nonce_set || !operation->is_encrypt) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    /* For CCM, this size may not be correct according to the PSA
+     * specification. The PSA Crypto 1.0.1 specification states:
+     *
+     * CCM encodes the plaintext length pLen in L octets, with L the smallest
+     * integer >= 2 where pLen < 2^(8L). The nonce length is then 15 - L bytes.
+     *
+     * However this restriction that L has to be the smallest integer is not
+     * applied in practice, and it is not implementable here since the
+     * plaintext length may or may not be known at this time. */
+    required_nonce_size = PSA_AEAD_NONCE_LENGTH(operation->key_type,
+                                                operation->alg);
+    if (nonce_size < required_nonce_size) {
+        status = PSA_ERROR_BUFFER_TOO_SMALL;
+        goto exit;
+    }
+
+    status = psa_generate_random_internal(local_nonce, required_nonce_size);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_aead_set_nonce_internal(operation, local_nonce,
+                                         required_nonce_size);
+
+exit:
+    if (status == PSA_SUCCESS) {
+        memcpy(nonce, local_nonce, required_nonce_size);
+        *nonce_length = required_nonce_size;
+    } else {
+        psa_aead_abort(operation);
+    }
+
+    LOCAL_OUTPUT_FREE(nonce_external, nonce);
+
+    return status;
+}
+
+/* Set the nonce for a multipart authenticated encryption or decryption
+   operation.*/
+psa_status_t psa_aead_set_nonce(psa_aead_operation_t *operation,
+                                const uint8_t *nonce_external,
+                                size_t nonce_length)
+{
+    psa_status_t status;
+
+    LOCAL_INPUT_DECLARE(nonce_external, nonce);
+    LOCAL_INPUT_ALLOC(nonce_external, nonce_length, nonce);
+
+    status = psa_aead_set_nonce_internal(operation, nonce, nonce_length);
+
+/* Exit label is only needed for buffer copying, prevent unused warnings. */
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+
+    LOCAL_INPUT_FREE(nonce_external, nonce);
+
+    return status;
+}
+
+/* Declare the lengths of the message and additional data for multipart AEAD. */
+psa_status_t psa_aead_set_lengths(psa_aead_operation_t *operation,
+                                  size_t ad_length,
+                                  size_t plaintext_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    if (operation->id == 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (operation->lengths_set || operation->ad_started ||
+        operation->body_started) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    switch (operation->alg) {
+#if defined(PSA_WANT_ALG_GCM)
+        case PSA_ALG_GCM:
+            /* Lengths can only be too large for GCM if size_t is bigger than 32
+             * bits. Without the guard this code will generate warnings on 32bit
+             * builds. */
+#if SIZE_MAX > UINT32_MAX
+            if (((uint64_t) ad_length) >> 61 != 0 ||
+                ((uint64_t) plaintext_length) > 0xFFFFFFFE0ull) {
+                status = PSA_ERROR_INVALID_ARGUMENT;
+                goto exit;
+            }
+#endif
+            break;
+#endif /* PSA_WANT_ALG_GCM */
+#if defined(PSA_WANT_ALG_CCM)
+        case PSA_ALG_CCM:
+            if (ad_length > 0xFF00) {
+                status = PSA_ERROR_INVALID_ARGUMENT;
+                goto exit;
+            }
+            break;
+#endif /* PSA_WANT_ALG_CCM */
+#if defined(PSA_WANT_ALG_CHACHA20_POLY1305)
+        case PSA_ALG_CHACHA20_POLY1305:
+            /* No length restrictions for ChaChaPoly. */
+            break;
+#endif /* PSA_WANT_ALG_CHACHA20_POLY1305 */
+        default:
+            break;
+    }
+
+    status = psa_driver_wrapper_aead_set_lengths(operation, ad_length,
+                                                 plaintext_length);
+
+exit:
+    if (status == PSA_SUCCESS) {
+        operation->ad_remaining = ad_length;
+        operation->body_remaining = plaintext_length;
+        operation->lengths_set = 1;
+    } else {
+        psa_aead_abort(operation);
+    }
+
+    return status;
+}
+
+/* Pass additional data to an active multipart AEAD operation. */
+psa_status_t psa_aead_update_ad(psa_aead_operation_t *operation,
+                                const uint8_t *input_external,
+                                size_t input_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    LOCAL_INPUT_DECLARE(input_external, input);
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+
+    if (operation->id == 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (!operation->nonce_set || operation->body_started) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    /* No input to add (zero length), nothing to do. */
+    if (input_length == 0) {
+        status = PSA_SUCCESS;
+        goto exit;
+    }
+
+    if (operation->lengths_set) {
+        if (operation->ad_remaining < input_length) {
+            status = PSA_ERROR_INVALID_ARGUMENT;
+            goto exit;
+        }
+
+        operation->ad_remaining -= input_length;
+    }
+#if defined(PSA_WANT_ALG_CCM)
+    else if (operation->alg == PSA_ALG_CCM) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+#endif /* PSA_WANT_ALG_CCM */
+
+    status = psa_driver_wrapper_aead_update_ad(operation, input,
+                                               input_length);
+
+exit:
+    if (status == PSA_SUCCESS) {
+        operation->ad_started = 1;
+    } else {
+        psa_aead_abort(operation);
+    }
+
+    LOCAL_INPUT_FREE(input_external, input);
+
+    return status;
+}
+
+/* Encrypt or decrypt a message fragment in an active multipart AEAD
+   operation.*/
+psa_status_t psa_aead_update(psa_aead_operation_t *operation,
+                             const uint8_t *input_external,
+                             size_t input_length,
+                             uint8_t *output_external,
+                             size_t output_size,
+                             size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+
+    LOCAL_INPUT_DECLARE(input_external, input);
+    LOCAL_OUTPUT_DECLARE(output_external, output);
+
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+    LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+    *output_length = 0;
+
+    if (operation->id == 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (!operation->nonce_set) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (operation->lengths_set) {
+        /* Additional data length was supplied, but not all the additional
+           data was supplied.*/
+        if (operation->ad_remaining != 0) {
+            status = PSA_ERROR_INVALID_ARGUMENT;
+            goto exit;
+        }
+
+        /* Too much data provided. */
+        if (operation->body_remaining < input_length) {
+            status = PSA_ERROR_INVALID_ARGUMENT;
+            goto exit;
+        }
+
+        operation->body_remaining -= input_length;
+    }
+#if defined(PSA_WANT_ALG_CCM)
+    else if (operation->alg == PSA_ALG_CCM) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+#endif /* PSA_WANT_ALG_CCM */
+
+    status = psa_driver_wrapper_aead_update(operation, input, input_length,
+                                            output, output_size,
+                                            output_length);
+
+exit:
+    if (status == PSA_SUCCESS) {
+        operation->body_started = 1;
+    } else {
+        psa_aead_abort(operation);
+    }
+
+    LOCAL_INPUT_FREE(input_external, input);
+    LOCAL_OUTPUT_FREE(output_external, output);
+
+    return status;
+}
+
+static psa_status_t psa_aead_final_checks(const psa_aead_operation_t *operation)
+{
+    if (operation->id == 0 || !operation->nonce_set) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    if (operation->lengths_set && (operation->ad_remaining != 0 ||
+                                   operation->body_remaining != 0)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    return PSA_SUCCESS;
+}
+
+/* Finish encrypting a message in a multipart AEAD operation. */
+psa_status_t psa_aead_finish(psa_aead_operation_t *operation,
+                             uint8_t *ciphertext_external,
+                             size_t ciphertext_size,
+                             size_t *ciphertext_length,
+                             uint8_t *tag_external,
+                             size_t tag_size,
+                             size_t *tag_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    LOCAL_OUTPUT_DECLARE(ciphertext_external, ciphertext);
+    LOCAL_OUTPUT_DECLARE(tag_external, tag);
+
+    LOCAL_OUTPUT_ALLOC(ciphertext_external, ciphertext_size, ciphertext);
+    LOCAL_OUTPUT_ALLOC(tag_external, tag_size, tag);
+
+    *ciphertext_length = 0;
+    *tag_length = tag_size;
+
+    status = psa_aead_final_checks(operation);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (!operation->is_encrypt) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    status = psa_driver_wrapper_aead_finish(operation, ciphertext,
+                                            ciphertext_size,
+                                            ciphertext_length,
+                                            tag, tag_size, tag_length);
+
+exit:
+
+
+    /* In case the operation fails and the user fails to check for failure or
+     * the zero tag size, make sure the tag is set to something implausible.
+     * Even if the operation succeeds, make sure we clear the rest of the
+     * buffer to prevent potential leakage of anything previously placed in
+     * the same buffer.*/
+    psa_wipe_tag_output_buffer(tag, status, tag_size, *tag_length);
+
+    psa_aead_abort(operation);
+
+    LOCAL_OUTPUT_FREE(ciphertext_external, ciphertext);
+    LOCAL_OUTPUT_FREE(tag_external, tag);
+
+    return status;
+}
+
+/* Finish authenticating and decrypting a message in a multipart AEAD
+   operation.*/
+psa_status_t psa_aead_verify(psa_aead_operation_t *operation,
+                             uint8_t *plaintext_external,
+                             size_t plaintext_size,
+                             size_t *plaintext_length,
+                             const uint8_t *tag_external,
+                             size_t tag_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    LOCAL_OUTPUT_DECLARE(plaintext_external, plaintext);
+    LOCAL_INPUT_DECLARE(tag_external, tag);
+
+    LOCAL_OUTPUT_ALLOC(plaintext_external, plaintext_size, plaintext);
+    LOCAL_INPUT_ALLOC(tag_external, tag_length, tag);
+
+    *plaintext_length = 0;
+
+    status = psa_aead_final_checks(operation);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (operation->is_encrypt) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    status = psa_driver_wrapper_aead_verify(operation, plaintext,
+                                            plaintext_size,
+                                            plaintext_length,
+                                            tag, tag_length);
+
+exit:
+    psa_aead_abort(operation);
+
+    LOCAL_OUTPUT_FREE(plaintext_external, plaintext);
+    LOCAL_INPUT_FREE(tag_external, tag);
+
+    return status;
+}
+
+/* Abort an AEAD operation. */
+psa_status_t psa_aead_abort(psa_aead_operation_t *operation)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    if (operation->id == 0) {
+        /* The object has (apparently) been initialized but it is not (yet)
+         * in use. It's ok to call abort on such an object, and there's
+         * nothing to do. */
+        return PSA_SUCCESS;
+    }
+
+    status = psa_driver_wrapper_aead_abort(operation);
+
+    memset(operation, 0, sizeof(*operation));
+
+    return status;
+}
+
+/****************************************************************/
+/* Generators */
+/****************************************************************/
+
+#if defined(BUILTIN_ALG_ANY_HKDF) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS) || \
+    defined(PSA_HAVE_SOFT_PBKDF2)
+#define AT_LEAST_ONE_BUILTIN_KDF
+#endif /* At least one builtin KDF */
+
+#if defined(BUILTIN_ALG_ANY_HKDF) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+static psa_status_t psa_key_derivation_start_hmac(
+    psa_mac_operation_t *operation,
+    psa_algorithm_t hash_alg,
+    const uint8_t *hmac_key,
+    size_t hmac_key_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+    psa_set_key_type(&attributes, PSA_KEY_TYPE_HMAC);
+    psa_set_key_bits(&attributes, PSA_BYTES_TO_BITS(hmac_key_length));
+    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_HASH);
+
+    operation->is_sign = 1;
+    operation->mac_size = PSA_HASH_LENGTH(hash_alg);
+
+    status = psa_driver_wrapper_mac_sign_setup(operation,
+                                               &attributes,
+                                               hmac_key, hmac_key_length,
+                                               PSA_ALG_HMAC(hash_alg));
+
+    psa_reset_key_attributes(&attributes);
+    return status;
+}
+#endif /* KDF algorithms reliant on HMAC */
+
+#define HKDF_STATE_INIT 0 /* no input yet */
+#define HKDF_STATE_STARTED 1 /* got salt */
+#define HKDF_STATE_KEYED 2 /* got key */
+#define HKDF_STATE_OUTPUT 3 /* output started */
+
+static psa_algorithm_t psa_key_derivation_get_kdf_alg(
+    const psa_key_derivation_operation_t *operation)
+{
+    if (PSA_ALG_IS_KEY_AGREEMENT(operation->alg)) {
+        return PSA_ALG_KEY_AGREEMENT_GET_KDF(operation->alg);
+    } else {
+        return operation->alg;
+    }
+}
+
+psa_status_t psa_key_derivation_abort(psa_key_derivation_operation_t *operation)
+{
+    psa_status_t status = PSA_SUCCESS;
+    psa_algorithm_t kdf_alg = psa_key_derivation_get_kdf_alg(operation);
+    if (kdf_alg == 0) {
+        /* The object has (apparently) been initialized but it is not
+         * in use. It's ok to call abort on such an object, and there's
+         * nothing to do. */
+    } else
+#if defined(BUILTIN_ALG_ANY_HKDF)
+    if (PSA_ALG_IS_ANY_HKDF(kdf_alg)) {
+        mbedtls_free(operation->ctx.hkdf.info);
+        status = psa_mac_abort(&operation->ctx.hkdf.hmac);
+    } else
+#endif /* BUILTIN_ALG_ANY_HKDF */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+    if (PSA_ALG_IS_TLS12_PRF(kdf_alg) ||
+        /* TLS-1.2 PSK-to-MS KDF uses the same core as TLS-1.2 PRF */
+        PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg)) {
+        if (operation->ctx.tls12_prf.secret != NULL) {
+            mbedtls_zeroize_and_free(operation->ctx.tls12_prf.secret,
+                                     operation->ctx.tls12_prf.secret_length);
+        }
+
+        if (operation->ctx.tls12_prf.seed != NULL) {
+            mbedtls_zeroize_and_free(operation->ctx.tls12_prf.seed,
+                                     operation->ctx.tls12_prf.seed_length);
+        }
+
+        if (operation->ctx.tls12_prf.label != NULL) {
+            mbedtls_zeroize_and_free(operation->ctx.tls12_prf.label,
+                                     operation->ctx.tls12_prf.label_length);
+        }
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+        if (operation->ctx.tls12_prf.other_secret != NULL) {
+            mbedtls_zeroize_and_free(operation->ctx.tls12_prf.other_secret,
+                                     operation->ctx.tls12_prf.other_secret_length);
+        }
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */
+        status = PSA_SUCCESS;
+
+        /* We leave the fields Ai and output_block to be erased safely by the
+         * mbedtls_platform_zeroize() in the end of this function. */
+    } else
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS)
+    if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
+        mbedtls_platform_zeroize(operation->ctx.tls12_ecjpake_to_pms.data,
+                                 sizeof(operation->ctx.tls12_ecjpake_to_pms.data));
+    } else
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS) */
+#if defined(PSA_HAVE_SOFT_PBKDF2)
+    if (PSA_ALG_IS_PBKDF2(kdf_alg)) {
+        if (operation->ctx.pbkdf2.salt != NULL) {
+            mbedtls_zeroize_and_free(operation->ctx.pbkdf2.salt,
+                                     operation->ctx.pbkdf2.salt_length);
+        }
+
+        status = PSA_SUCCESS;
+    } else
+#endif /* defined(PSA_HAVE_SOFT_PBKDF2) */
+    {
+        status = PSA_ERROR_BAD_STATE;
+    }
+    mbedtls_platform_zeroize(operation, sizeof(*operation));
+    return status;
+}
+
+psa_status_t psa_key_derivation_get_capacity(const psa_key_derivation_operation_t *operation,
+                                             size_t *capacity)
+{
+    if (operation->alg == 0) {
+        /* This is a blank key derivation operation. */
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    *capacity = operation->capacity;
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_key_derivation_set_capacity(psa_key_derivation_operation_t *operation,
+                                             size_t capacity)
+{
+    if (operation->alg == 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+    if (capacity > operation->capacity) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+    operation->capacity = capacity;
+    return PSA_SUCCESS;
+}
+
+#if defined(BUILTIN_ALG_ANY_HKDF)
+/* Read some bytes from an HKDF-based operation. */
+static psa_status_t psa_key_derivation_hkdf_read(psa_hkdf_key_derivation_t *hkdf,
+                                                 psa_algorithm_t kdf_alg,
+                                                 uint8_t *output,
+                                                 size_t output_length)
+{
+    psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH(kdf_alg);
+    uint8_t hash_length = PSA_HASH_LENGTH(hash_alg);
+    size_t hmac_output_length;
+    psa_status_t status;
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT)
+    const uint8_t last_block = PSA_ALG_IS_HKDF_EXTRACT(kdf_alg) ? 0 : 0xff;
+#else
+    const uint8_t last_block = 0xff;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */
+
+    if (hkdf->state < HKDF_STATE_KEYED ||
+        (!hkdf->info_set
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT)
+         && !PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */
+        )) {
+        return PSA_ERROR_BAD_STATE;
+    }
+    hkdf->state = HKDF_STATE_OUTPUT;
+
+    while (output_length != 0) {
+        /* Copy what remains of the current block */
+        uint8_t n = hash_length - hkdf->offset_in_block;
+        if (n > output_length) {
+            n = (uint8_t) output_length;
+        }
+        memcpy(output, hkdf->output_block + hkdf->offset_in_block, n);
+        output += n;
+        output_length -= n;
+        hkdf->offset_in_block += n;
+        if (output_length == 0) {
+            break;
+        }
+        /* We can't be wanting more output after the last block, otherwise
+         * the capacity check in psa_key_derivation_output_bytes() would have
+         * prevented this call. It could happen only if the operation
+         * object was corrupted or if this function is called directly
+         * inside the library. */
+        if (hkdf->block_number == last_block) {
+            return PSA_ERROR_BAD_STATE;
+        }
+
+        /* We need a new block */
+        ++hkdf->block_number;
+        hkdf->offset_in_block = 0;
+
+        status = psa_key_derivation_start_hmac(&hkdf->hmac,
+                                               hash_alg,
+                                               hkdf->prk,
+                                               hash_length);
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+
+        if (hkdf->block_number != 1) {
+            status = psa_mac_update(&hkdf->hmac,
+                                    hkdf->output_block,
+                                    hash_length);
+            if (status != PSA_SUCCESS) {
+                return status;
+            }
+        }
+        status = psa_mac_update(&hkdf->hmac,
+                                hkdf->info,
+                                hkdf->info_length);
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+        status = psa_mac_update(&hkdf->hmac,
+                                &hkdf->block_number, 1);
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+        status = psa_mac_sign_finish(&hkdf->hmac,
+                                     hkdf->output_block,
+                                     sizeof(hkdf->output_block),
+                                     &hmac_output_length);
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+    }
+
+    return PSA_SUCCESS;
+}
+#endif /* BUILTIN_ALG_ANY_HKDF */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+static psa_status_t psa_key_derivation_tls12_prf_generate_next_block(
+    psa_tls12_prf_key_derivation_t *tls12_prf,
+    psa_algorithm_t alg)
+{
+    psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH(alg);
+    uint8_t hash_length = PSA_HASH_LENGTH(hash_alg);
+    psa_mac_operation_t hmac = PSA_MAC_OPERATION_INIT;
+    size_t hmac_output_length;
+    psa_status_t status, cleanup_status;
+
+    /* We can't be wanting more output after block 0xff, otherwise
+     * the capacity check in psa_key_derivation_output_bytes() would have
+     * prevented this call. It could happen only if the operation
+     * object was corrupted or if this function is called directly
+     * inside the library. */
+    if (tls12_prf->block_number == 0xff) {
+        return PSA_ERROR_CORRUPTION_DETECTED;
+    }
+
+    /* We need a new block */
+    ++tls12_prf->block_number;
+    tls12_prf->left_in_block = hash_length;
+
+    /* Recall the definition of the TLS-1.2-PRF from RFC 5246:
+     *
+     * PRF(secret, label, seed) = P_<hash>(secret, label + seed)
+     *
+     * P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
+     *                        HMAC_hash(secret, A(2) + seed) +
+     *                        HMAC_hash(secret, A(3) + seed) + ...
+     *
+     * A(0) = seed
+     * A(i) = HMAC_hash(secret, A(i-1))
+     *
+     * The `psa_tls12_prf_key_derivation` structure saves the block
+     * `HMAC_hash(secret, A(i) + seed)` from which the output
+     * is currently extracted as `output_block` and where i is
+     * `block_number`.
+     */
+
+    status = psa_key_derivation_start_hmac(&hmac,
+                                           hash_alg,
+                                           tls12_prf->secret,
+                                           tls12_prf->secret_length);
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+
+    /* Calculate A(i) where i = tls12_prf->block_number. */
+    if (tls12_prf->block_number == 1) {
+        /* A(1) = HMAC_hash(secret, A(0)), where A(0) = seed. (The RFC overloads
+         * the variable seed and in this instance means it in the context of the
+         * P_hash function, where seed = label + seed.) */
+        status = psa_mac_update(&hmac,
+                                tls12_prf->label,
+                                tls12_prf->label_length);
+        if (status != PSA_SUCCESS) {
+            goto cleanup;
+        }
+        status = psa_mac_update(&hmac,
+                                tls12_prf->seed,
+                                tls12_prf->seed_length);
+        if (status != PSA_SUCCESS) {
+            goto cleanup;
+        }
+    } else {
+        /* A(i) = HMAC_hash(secret, A(i-1)) */
+        status = psa_mac_update(&hmac, tls12_prf->Ai, hash_length);
+        if (status != PSA_SUCCESS) {
+            goto cleanup;
+        }
+    }
+
+    status = psa_mac_sign_finish(&hmac,
+                                 tls12_prf->Ai, hash_length,
+                                 &hmac_output_length);
+    if (hmac_output_length != hash_length) {
+        status = PSA_ERROR_CORRUPTION_DETECTED;
+    }
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+
+    /* Calculate HMAC_hash(secret, A(i) + label + seed). */
+    status = psa_key_derivation_start_hmac(&hmac,
+                                           hash_alg,
+                                           tls12_prf->secret,
+                                           tls12_prf->secret_length);
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+    status = psa_mac_update(&hmac, tls12_prf->Ai, hash_length);
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+    status = psa_mac_update(&hmac, tls12_prf->label, tls12_prf->label_length);
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+    status = psa_mac_update(&hmac, tls12_prf->seed, tls12_prf->seed_length);
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+    status = psa_mac_sign_finish(&hmac,
+                                 tls12_prf->output_block, hash_length,
+                                 &hmac_output_length);
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+
+
+cleanup:
+    cleanup_status = psa_mac_abort(&hmac);
+    if (status == PSA_SUCCESS && cleanup_status != PSA_SUCCESS) {
+        status = cleanup_status;
+    }
+
+    return status;
+}
+
+static psa_status_t psa_key_derivation_tls12_prf_read(
+    psa_tls12_prf_key_derivation_t *tls12_prf,
+    psa_algorithm_t alg,
+    uint8_t *output,
+    size_t output_length)
+{
+    psa_algorithm_t hash_alg = PSA_ALG_TLS12_PRF_GET_HASH(alg);
+    uint8_t hash_length = PSA_HASH_LENGTH(hash_alg);
+    psa_status_t status;
+    uint8_t offset, length;
+
+    switch (tls12_prf->state) {
+        case PSA_TLS12_PRF_STATE_LABEL_SET:
+            tls12_prf->state = PSA_TLS12_PRF_STATE_OUTPUT;
+            break;
+        case PSA_TLS12_PRF_STATE_OUTPUT:
+            break;
+        default:
+            return PSA_ERROR_BAD_STATE;
+    }
+
+    while (output_length != 0) {
+        /* Check if we have fully processed the current block. */
+        if (tls12_prf->left_in_block == 0) {
+            status = psa_key_derivation_tls12_prf_generate_next_block(tls12_prf,
+                                                                      alg);
+            if (status != PSA_SUCCESS) {
+                return status;
+            }
+
+            continue;
+        }
+
+        if (tls12_prf->left_in_block > output_length) {
+            length = (uint8_t) output_length;
+        } else {
+            length = tls12_prf->left_in_block;
+        }
+
+        offset = hash_length - tls12_prf->left_in_block;
+        memcpy(output, tls12_prf->output_block + offset, length);
+        output += length;
+        output_length -= length;
+        tls12_prf->left_in_block -= length;
+    }
+
+    return PSA_SUCCESS;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF ||
+        * MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS)
+static psa_status_t psa_key_derivation_tls12_ecjpake_to_pms_read(
+    psa_tls12_ecjpake_to_pms_t *ecjpake,
+    uint8_t *output,
+    size_t output_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    size_t output_size = 0;
+
+    if (output_length != 32) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    status = psa_hash_compute(PSA_ALG_SHA_256, ecjpake->data,
+                              PSA_TLS12_ECJPAKE_TO_PMS_DATA_SIZE, output, output_length,
+                              &output_size);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    if (output_size != output_length) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    return PSA_SUCCESS;
+}
+#endif
+
+#if defined(PSA_HAVE_SOFT_PBKDF2)
+static psa_status_t psa_key_derivation_pbkdf2_generate_block(
+    psa_pbkdf2_key_derivation_t *pbkdf2,
+    psa_algorithm_t prf_alg,
+    uint8_t prf_output_length,
+    psa_key_attributes_t *attributes)
+{
+    psa_status_t status;
+    psa_mac_operation_t mac_operation = PSA_MAC_OPERATION_INIT;
+    size_t mac_output_length;
+    uint8_t U_i[PSA_MAC_MAX_SIZE];
+    uint8_t *U_accumulator = pbkdf2->output_block;
+    uint64_t i;
+    uint8_t block_counter[4];
+
+    mac_operation.is_sign = 1;
+    mac_operation.mac_size = prf_output_length;
+    MBEDTLS_PUT_UINT32_BE(pbkdf2->block_number, block_counter, 0);
+
+    status = psa_driver_wrapper_mac_sign_setup(&mac_operation,
+                                               attributes,
+                                               pbkdf2->password,
+                                               pbkdf2->password_length,
+                                               prf_alg);
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+    status = psa_mac_update(&mac_operation, pbkdf2->salt, pbkdf2->salt_length);
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+    status = psa_mac_update(&mac_operation, block_counter, sizeof(block_counter));
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+    status = psa_mac_sign_finish(&mac_operation, U_i, sizeof(U_i),
+                                 &mac_output_length);
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+
+    if (mac_output_length != prf_output_length) {
+        status = PSA_ERROR_CORRUPTION_DETECTED;
+        goto cleanup;
+    }
+
+    memcpy(U_accumulator, U_i, prf_output_length);
+
+    for (i = 1; i < pbkdf2->input_cost; i++) {
+        /* We are passing prf_output_length as mac_size because the driver
+         * function directly sets mac_output_length as mac_size upon success.
+         * See https://github.com/Mbed-TLS/mbedtls/issues/7801 */
+        status = psa_driver_wrapper_mac_compute(attributes,
+                                                pbkdf2->password,
+                                                pbkdf2->password_length,
+                                                prf_alg, U_i, prf_output_length,
+                                                U_i, prf_output_length,
+                                                &mac_output_length);
+        if (status != PSA_SUCCESS) {
+            goto cleanup;
+        }
+
+        mbedtls_xor(U_accumulator, U_accumulator, U_i, prf_output_length);
+    }
+
+cleanup:
+    /* Zeroise buffers to clear sensitive data from memory. */
+    mbedtls_platform_zeroize(U_i, PSA_MAC_MAX_SIZE);
+    return status;
+}
+
+static psa_status_t psa_key_derivation_pbkdf2_read(
+    psa_pbkdf2_key_derivation_t *pbkdf2,
+    psa_algorithm_t kdf_alg,
+    uint8_t *output,
+    size_t output_length)
+{
+    psa_status_t status;
+    psa_algorithm_t prf_alg;
+    uint8_t prf_output_length;
+    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+    psa_set_key_bits(&attributes, PSA_BYTES_TO_BITS(pbkdf2->password_length));
+    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE);
+
+    if (PSA_ALG_IS_PBKDF2_HMAC(kdf_alg)) {
+        prf_alg = PSA_ALG_HMAC(PSA_ALG_PBKDF2_HMAC_GET_HASH(kdf_alg));
+        prf_output_length = PSA_HASH_LENGTH(prf_alg);
+        psa_set_key_type(&attributes, PSA_KEY_TYPE_HMAC);
+    } else if (kdf_alg == PSA_ALG_PBKDF2_AES_CMAC_PRF_128) {
+        prf_alg = PSA_ALG_CMAC;
+        prf_output_length = PSA_MAC_LENGTH(PSA_KEY_TYPE_AES, 128U, PSA_ALG_CMAC);
+        psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
+    } else {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    switch (pbkdf2->state) {
+        case PSA_PBKDF2_STATE_PASSWORD_SET:
+            /* Initially we need a new block so bytes_used is equal to block size*/
+            pbkdf2->bytes_used = prf_output_length;
+            pbkdf2->state = PSA_PBKDF2_STATE_OUTPUT;
+            break;
+        case PSA_PBKDF2_STATE_OUTPUT:
+            break;
+        default:
+            return PSA_ERROR_BAD_STATE;
+    }
+
+    while (output_length != 0) {
+        uint8_t n = prf_output_length - pbkdf2->bytes_used;
+        if (n > output_length) {
+            n = (uint8_t) output_length;
+        }
+        memcpy(output, pbkdf2->output_block + pbkdf2->bytes_used, n);
+        output += n;
+        output_length -= n;
+        pbkdf2->bytes_used += n;
+
+        if (output_length == 0) {
+            break;
+        }
+
+        /* We need a new block */
+        pbkdf2->bytes_used = 0;
+        pbkdf2->block_number++;
+
+        status = psa_key_derivation_pbkdf2_generate_block(pbkdf2, prf_alg,
+                                                          prf_output_length,
+                                                          &attributes);
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+    }
+
+    return PSA_SUCCESS;
+}
+#endif /* PSA_HAVE_SOFT_PBKDF2 */
+
+psa_status_t psa_key_derivation_output_bytes(
+    psa_key_derivation_operation_t *operation,
+    uint8_t *output_external,
+    size_t output_length)
+{
+    psa_status_t status;
+    LOCAL_OUTPUT_DECLARE(output_external, output);
+
+    psa_algorithm_t kdf_alg = psa_key_derivation_get_kdf_alg(operation);
+
+    if (operation->alg == 0) {
+        /* This is a blank operation. */
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    if (output_length == 0 && operation->capacity == 0) {
+        /* Edge case: this is a finished operation, and 0 bytes
+         * were requested. The right error in this case could
+         * be either INSUFFICIENT_CAPACITY or BAD_STATE. Return
+         * INSUFFICIENT_CAPACITY, which is right for a finished
+         * operation, for consistency with the case when
+         * output_length > 0. */
+        return PSA_ERROR_INSUFFICIENT_DATA;
+    }
+
+    LOCAL_OUTPUT_ALLOC(output_external, output_length, output);
+    if (output_length > operation->capacity) {
+        operation->capacity = 0;
+        /* Go through the error path to wipe all confidential data now
+         * that the operation object is useless. */
+        status = PSA_ERROR_INSUFFICIENT_DATA;
+        goto exit;
+    }
+
+    operation->capacity -= output_length;
+
+#if defined(BUILTIN_ALG_ANY_HKDF)
+    if (PSA_ALG_IS_ANY_HKDF(kdf_alg)) {
+        status = psa_key_derivation_hkdf_read(&operation->ctx.hkdf, kdf_alg,
+                                              output, output_length);
+    } else
+#endif /* BUILTIN_ALG_ANY_HKDF */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+    if (PSA_ALG_IS_TLS12_PRF(kdf_alg) ||
+        PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg)) {
+        status = psa_key_derivation_tls12_prf_read(&operation->ctx.tls12_prf,
+                                                   kdf_alg, output,
+                                                   output_length);
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF ||
+        * MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS)
+    if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
+        status = psa_key_derivation_tls12_ecjpake_to_pms_read(
+            &operation->ctx.tls12_ecjpake_to_pms, output, output_length);
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS */
+#if defined(PSA_HAVE_SOFT_PBKDF2)
+    if (PSA_ALG_IS_PBKDF2(kdf_alg)) {
+        status = psa_key_derivation_pbkdf2_read(&operation->ctx.pbkdf2, kdf_alg,
+                                                output, output_length);
+    } else
+#endif /* PSA_HAVE_SOFT_PBKDF2 */
+
+    {
+        (void) kdf_alg;
+        status = PSA_ERROR_BAD_STATE;
+        LOCAL_OUTPUT_FREE(output_external, output);
+
+        return status;
+    }
+
+exit:
+    if (status != PSA_SUCCESS) {
+        /* Preserve the algorithm upon errors, but clear all sensitive state.
+         * This allows us to differentiate between exhausted operations and
+         * blank operations, so we can return PSA_ERROR_BAD_STATE on blank
+         * operations. */
+        psa_algorithm_t alg = operation->alg;
+        psa_key_derivation_abort(operation);
+        operation->alg = alg;
+        if (output != NULL) {
+            memset(output, '!', output_length);
+        }
+    }
+
+    LOCAL_OUTPUT_FREE(output_external, output);
+    return status;
+}
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES)
+static void psa_des_set_key_parity(uint8_t *data, size_t data_size)
+{
+    if (data_size >= 8) {
+        mbedtls_des_key_set_parity(data);
+    }
+    if (data_size >= 16) {
+        mbedtls_des_key_set_parity(data + 8);
+    }
+    if (data_size >= 24) {
+        mbedtls_des_key_set_parity(data + 16);
+    }
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES */
+
+/*
+ * ECC keys on a Weierstrass elliptic curve require the generation
+ * of a private key which is an integer
+ * in the range [1, N - 1], where N is the boundary of the private key domain:
+ * N is the prime p for Diffie-Hellman, or the order of the
+ * curve’s base point for ECC.
+ *
+ * Let m be the bit size of N, such that 2^m > N >= 2^(m-1).
+ * This function generates the private key using the following process:
+ *
+ * 1. Draw a byte string of length ceiling(m/8) bytes.
+ * 2. If m is not a multiple of 8, set the most significant
+ *    (8 * ceiling(m/8) - m) bits of the first byte in the string to zero.
+ * 3. Convert the string to integer k by decoding it as a big-endian byte string.
+ * 4. If k > N - 2, discard the result and return to step 1.
+ * 5. Output k + 1 as the private key.
+ *
+ * This method allows compliance to NIST standards, specifically the methods titled
+ * Key-Pair Generation by Testing Candidates in the following publications:
+ * - NIST Special Publication 800-56A: Recommendation for Pair-Wise Key-Establishment
+ *   Schemes Using Discrete Logarithm Cryptography [SP800-56A] §5.6.1.1.4 for
+ *   Diffie-Hellman keys.
+ *
+ * - [SP800-56A] §5.6.1.2.2 or FIPS Publication 186-4: Digital Signature
+ *   Standard (DSS) [FIPS186-4] §B.4.2 for elliptic curve keys.
+ *
+ * Note: Function allocates memory for *data buffer, so given *data should be
+ *       always NULL.
+ */
+#if defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE)
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_DERIVE)
+static psa_status_t psa_generate_derived_ecc_key_weierstrass_helper(
+    psa_key_slot_t *slot,
+    size_t bits,
+    psa_key_derivation_operation_t *operation,
+    uint8_t **data
+    )
+{
+    unsigned key_out_of_range = 1;
+    mbedtls_mpi k;
+    mbedtls_mpi diff_N_2;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    size_t m;
+    size_t m_bytes;
+
+    mbedtls_mpi_init(&k);
+    mbedtls_mpi_init(&diff_N_2);
+
+    psa_ecc_family_t curve = PSA_KEY_TYPE_ECC_GET_FAMILY(
+        slot->attr.type);
+    mbedtls_ecp_group_id grp_id =
+        mbedtls_ecc_group_from_psa(curve, bits);
+
+    if (grp_id == MBEDTLS_ECP_DP_NONE) {
+        ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
+        goto cleanup;
+    }
+
+    mbedtls_ecp_group ecp_group;
+    mbedtls_ecp_group_init(&ecp_group);
+
+    MBEDTLS_MPI_CHK(mbedtls_ecp_group_load(&ecp_group, grp_id));
+
+    /* N is the boundary of the private key domain (ecp_group.N). */
+    /* Let m be the bit size of N. */
+    m = ecp_group.nbits;
+
+    m_bytes = PSA_BITS_TO_BYTES(m);
+
+    /* Calculate N - 2 - it will be needed later. */
+    MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&diff_N_2, &ecp_group.N, 2));
+
+    /* Note: This function is always called with *data == NULL and it
+     * allocates memory for the data buffer. */
+    *data = mbedtls_calloc(1, m_bytes);
+    if (*data == NULL) {
+        ret = MBEDTLS_ERR_ASN1_ALLOC_FAILED;
+        goto cleanup;
+    }
+
+    while (key_out_of_range) {
+        /* 1. Draw a byte string of length ceiling(m/8) bytes. */
+        if ((status = psa_key_derivation_output_bytes(operation, *data, m_bytes)) != 0) {
+            goto cleanup;
+        }
+
+        /* 2. If m is not a multiple of 8 */
+        if (m % 8 != 0) {
+            /* Set the most significant
+             * (8 * ceiling(m/8) - m) bits of the first byte in
+             * the string to zero.
+             */
+            uint8_t clear_bit_mask = (1 << (m % 8)) - 1;
+            (*data)[0] &= clear_bit_mask;
+        }
+
+        /* 3. Convert the string to integer k by decoding it as a
+         *    big-endian byte string.
+         */
+        MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&k, *data, m_bytes));
+
+        /* 4. If k > N - 2, discard the result and return to step 1.
+         *    Result of comparison is returned. When it indicates error
+         *    then this function is called again.
+         */
+        MBEDTLS_MPI_CHK(mbedtls_mpi_lt_mpi_ct(&diff_N_2, &k, &key_out_of_range));
+    }
+
+    /* 5. Output k + 1 as the private key. */
+    MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&k, &k, 1));
+    MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&k, *data, m_bytes));
+cleanup:
+    if (ret != 0) {
+        status = mbedtls_to_psa_error(ret);
+    }
+    if (status != PSA_SUCCESS) {
+        mbedtls_free(*data);
+        *data = NULL;
+    }
+    mbedtls_mpi_free(&k);
+    mbedtls_mpi_free(&diff_N_2);
+    return status;
+}
+
+/* ECC keys on a Montgomery elliptic curve draws a byte string whose length
+ * is determined by the curve, and sets the mandatory bits accordingly. That is:
+ *
+ * - Curve25519 (PSA_ECC_FAMILY_MONTGOMERY, 255 bits):
+ *   draw a 32-byte string and process it as specified in
+ *   Elliptic Curves for Security [RFC7748] §5.
+ *
+ * - Curve448 (PSA_ECC_FAMILY_MONTGOMERY, 448 bits):
+ *   draw a 56-byte string and process it as specified in [RFC7748] §5.
+ *
+ * Note: Function allocates memory for *data buffer, so given *data should be
+ *       always NULL.
+ */
+
+static psa_status_t psa_generate_derived_ecc_key_montgomery_helper(
+    size_t bits,
+    psa_key_derivation_operation_t *operation,
+    uint8_t **data
+    )
+{
+    size_t output_length;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    switch (bits) {
+        case 255:
+            output_length = 32;
+            break;
+        case 448:
+            output_length = 56;
+            break;
+        default:
+            return PSA_ERROR_INVALID_ARGUMENT;
+            break;
+    }
+
+    *data = mbedtls_calloc(1, output_length);
+
+    if (*data == NULL) {
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+
+    status = psa_key_derivation_output_bytes(operation, *data, output_length);
+
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    switch (bits) {
+        case 255:
+            (*data)[0] &= 248;
+            (*data)[31] &= 127;
+            (*data)[31] |= 64;
+            break;
+        case 448:
+            (*data)[0] &= 252;
+            (*data)[55] |= 128;
+            break;
+        default:
+            return PSA_ERROR_CORRUPTION_DETECTED;
+            break;
+    }
+
+    return status;
+}
+#else /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_DERIVE */
+static psa_status_t psa_generate_derived_ecc_key_weierstrass_helper(
+    psa_key_slot_t *slot, size_t bits,
+    psa_key_derivation_operation_t *operation, uint8_t **data)
+{
+    (void) slot;
+    (void) bits;
+    (void) operation;
+    (void) data;
+    return PSA_ERROR_NOT_SUPPORTED;
+}
+
+static psa_status_t psa_generate_derived_ecc_key_montgomery_helper(
+    size_t bits, psa_key_derivation_operation_t *operation, uint8_t **data)
+{
+    (void) bits;
+    (void) operation;
+    (void) data;
+    return PSA_ERROR_NOT_SUPPORTED;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_DERIVE */
+#endif /* PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE */
+
+static psa_status_t psa_generate_derived_key_internal(
+    psa_key_slot_t *slot,
+    size_t bits,
+    psa_key_derivation_operation_t *operation)
+{
+    uint8_t *data = NULL;
+    size_t bytes = PSA_BITS_TO_BYTES(bits);
+    size_t storage_size = bytes;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    if (PSA_KEY_TYPE_IS_PUBLIC_KEY(slot->attr.type)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+#if defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE) || \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_DERIVE)
+    if (PSA_KEY_TYPE_IS_ECC(slot->attr.type)) {
+        psa_ecc_family_t curve = PSA_KEY_TYPE_ECC_GET_FAMILY(slot->attr.type);
+        if (PSA_ECC_FAMILY_IS_WEIERSTRASS(curve)) {
+            /* Weierstrass elliptic curve */
+            status = psa_generate_derived_ecc_key_weierstrass_helper(slot, bits, operation, &data);
+            if (status != PSA_SUCCESS) {
+                goto exit;
+            }
+        } else {
+            /* Montgomery elliptic curve */
+            status = psa_generate_derived_ecc_key_montgomery_helper(bits, operation, &data);
+            if (status != PSA_SUCCESS) {
+                goto exit;
+            }
+        }
+    } else
+#endif /* defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE) ||
+          defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_DERIVE) */
+    if (key_type_is_raw_bytes(slot->attr.type)) {
+        if (bits % 8 != 0) {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+        data = mbedtls_calloc(1, bytes);
+        if (data == NULL) {
+            return PSA_ERROR_INSUFFICIENT_MEMORY;
+        }
+
+        status = psa_key_derivation_output_bytes(operation, data, bytes);
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES)
+        if (slot->attr.type == PSA_KEY_TYPE_DES) {
+            psa_des_set_key_parity(data, bytes);
+        }
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES) */
+    } else {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    slot->attr.bits = (psa_key_bits_t) bits;
+
+    if (psa_key_lifetime_is_external(slot->attr.lifetime)) {
+        status = psa_driver_wrapper_get_key_buffer_size(&slot->attr,
+                                                        &storage_size);
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+    }
+    status = psa_allocate_buffer_to_slot(slot, storage_size);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_driver_wrapper_import_key(&slot->attr,
+                                           data, bytes,
+                                           slot->key.data,
+                                           slot->key.bytes,
+                                           &slot->key.bytes, &bits);
+    if (bits != slot->attr.bits) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+exit:
+    mbedtls_free(data);
+    return status;
+}
+
+static const psa_key_production_parameters_t default_production_parameters =
+    PSA_KEY_PRODUCTION_PARAMETERS_INIT;
+
+int psa_key_production_parameters_are_default(
+    const psa_key_production_parameters_t *params,
+    size_t params_data_length)
+{
+    if (params->flags != 0) {
+        return 0;
+    }
+    if (params_data_length != 0) {
+        return 0;
+    }
+    return 1;
+}
+
+psa_status_t psa_key_derivation_output_key_ext(
+    const psa_key_attributes_t *attributes,
+    psa_key_derivation_operation_t *operation,
+    const psa_key_production_parameters_t *params,
+    size_t params_data_length,
+    mbedtls_svc_key_id_t *key)
+{
+    psa_status_t status;
+    psa_key_slot_t *slot = NULL;
+    psa_se_drv_table_entry_t *driver = NULL;
+
+    *key = MBEDTLS_SVC_KEY_ID_INIT;
+
+    /* Reject any attempt to create a zero-length key so that we don't
+     * risk tripping up later, e.g. on a malloc(0) that returns NULL. */
+    if (psa_get_key_bits(attributes) == 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (!psa_key_production_parameters_are_default(params, params_data_length)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (operation->alg == PSA_ALG_NONE) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    if (!operation->can_output_key) {
+        return PSA_ERROR_NOT_PERMITTED;
+    }
+
+    status = psa_start_key_creation(PSA_KEY_CREATION_DERIVE, attributes,
+                                    &slot, &driver);
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+    if (driver != NULL) {
+        /* Deriving a key in a secure element is not implemented yet. */
+        status = PSA_ERROR_NOT_SUPPORTED;
+    }
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+    if (status == PSA_SUCCESS) {
+        status = psa_generate_derived_key_internal(slot,
+                                                   attributes->bits,
+                                                   operation);
+    }
+    if (status == PSA_SUCCESS) {
+        status = psa_finish_key_creation(slot, driver, key);
+    }
+    if (status != PSA_SUCCESS) {
+        psa_fail_key_creation(slot, driver);
+    }
+
+    return status;
+}
+
+psa_status_t psa_key_derivation_output_key(
+    const psa_key_attributes_t *attributes,
+    psa_key_derivation_operation_t *operation,
+    mbedtls_svc_key_id_t *key)
+{
+    return psa_key_derivation_output_key_ext(attributes, operation,
+                                             &default_production_parameters, 0,
+                                             key);
+}
+
+
+/****************************************************************/
+/* Key derivation */
+/****************************************************************/
+
+#if defined(AT_LEAST_ONE_BUILTIN_KDF)
+static int is_kdf_alg_supported(psa_algorithm_t kdf_alg)
+{
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF)
+    if (PSA_ALG_IS_HKDF(kdf_alg)) {
+        return 1;
+    }
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT)
+    if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) {
+        return 1;
+    }
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND)
+    if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg)) {
+        return 1;
+    }
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF)
+    if (PSA_ALG_IS_TLS12_PRF(kdf_alg)) {
+        return 1;
+    }
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+    if (PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg)) {
+        return 1;
+    }
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS)
+    if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
+        return 1;
+    }
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_HMAC)
+    if (PSA_ALG_IS_PBKDF2_HMAC(kdf_alg)) {
+        return 1;
+    }
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_AES_CMAC_PRF_128)
+    if (kdf_alg == PSA_ALG_PBKDF2_AES_CMAC_PRF_128) {
+        return 1;
+    }
+#endif
+    return 0;
+}
+
+static psa_status_t psa_hash_try_support(psa_algorithm_t alg)
+{
+    psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT;
+    psa_status_t status = psa_hash_setup(&operation, alg);
+    psa_hash_abort(&operation);
+    return status;
+}
+
+static psa_status_t psa_key_derivation_set_maximum_capacity(
+    psa_key_derivation_operation_t *operation,
+    psa_algorithm_t kdf_alg)
+{
+#if defined(PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS)
+    if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
+        operation->capacity = PSA_HASH_LENGTH(PSA_ALG_SHA_256);
+        return PSA_SUCCESS;
+    }
+#endif
+#if defined(PSA_WANT_ALG_PBKDF2_AES_CMAC_PRF_128)
+    if (kdf_alg == PSA_ALG_PBKDF2_AES_CMAC_PRF_128) {
+#if (SIZE_MAX > UINT32_MAX)
+        operation->capacity = UINT32_MAX * (size_t) PSA_MAC_LENGTH(
+            PSA_KEY_TYPE_AES,
+            128U,
+            PSA_ALG_CMAC);
+#else
+        operation->capacity = SIZE_MAX;
+#endif
+        return PSA_SUCCESS;
+    }
+#endif /* PSA_WANT_ALG_PBKDF2_AES_CMAC_PRF_128 */
+
+    /* After this point, if kdf_alg is not valid then value of hash_alg may be
+     * invalid or meaningless but it does not affect this function */
+    psa_algorithm_t hash_alg = PSA_ALG_GET_HASH(kdf_alg);
+    size_t hash_size = PSA_HASH_LENGTH(hash_alg);
+    if (hash_size == 0) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    /* Make sure that hash_alg is a supported hash algorithm. Otherwise
+     * we might fail later, which is somewhat unfriendly and potentially
+     * risk-prone. */
+    psa_status_t status = psa_hash_try_support(hash_alg);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+#if defined(PSA_WANT_ALG_HKDF)
+    if (PSA_ALG_IS_HKDF(kdf_alg)) {
+        operation->capacity = 255 * hash_size;
+    } else
+#endif
+#if defined(PSA_WANT_ALG_HKDF_EXTRACT)
+    if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) {
+        operation->capacity = hash_size;
+    } else
+#endif
+#if defined(PSA_WANT_ALG_HKDF_EXPAND)
+    if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg)) {
+        operation->capacity = 255 * hash_size;
+    } else
+#endif
+#if defined(PSA_WANT_ALG_TLS12_PRF)
+    if (PSA_ALG_IS_TLS12_PRF(kdf_alg) &&
+        (hash_alg == PSA_ALG_SHA_256 || hash_alg == PSA_ALG_SHA_384)) {
+        operation->capacity = SIZE_MAX;
+    } else
+#endif
+#if defined(PSA_WANT_ALG_TLS12_PSK_TO_MS)
+    if (PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg) &&
+        (hash_alg == PSA_ALG_SHA_256 || hash_alg == PSA_ALG_SHA_384)) {
+        /* Master Secret is always 48 bytes
+         * https://datatracker.ietf.org/doc/html/rfc5246.html#section-8.1 */
+        operation->capacity = 48U;
+    } else
+#endif
+#if defined(PSA_WANT_ALG_PBKDF2_HMAC)
+    if (PSA_ALG_IS_PBKDF2_HMAC(kdf_alg)) {
+#if (SIZE_MAX > UINT32_MAX)
+        operation->capacity = UINT32_MAX * hash_size;
+#else
+        operation->capacity = SIZE_MAX;
+#endif
+    } else
+#endif /* PSA_WANT_ALG_PBKDF2_HMAC */
+    {
+        (void) hash_size;
+        status = PSA_ERROR_NOT_SUPPORTED;
+    }
+    return status;
+}
+
+static psa_status_t psa_key_derivation_setup_kdf(
+    psa_key_derivation_operation_t *operation,
+    psa_algorithm_t kdf_alg)
+{
+    /* Make sure that operation->ctx is properly zero-initialised. (Macro
+     * initialisers for this union leave some bytes unspecified.) */
+    memset(&operation->ctx, 0, sizeof(operation->ctx));
+
+    /* Make sure that kdf_alg is a supported key derivation algorithm. */
+    if (!is_kdf_alg_supported(kdf_alg)) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    psa_status_t status = psa_key_derivation_set_maximum_capacity(operation,
+                                                                  kdf_alg);
+    return status;
+}
+
+static psa_status_t psa_key_agreement_try_support(psa_algorithm_t alg)
+{
+#if defined(PSA_WANT_ALG_ECDH)
+    if (alg == PSA_ALG_ECDH) {
+        return PSA_SUCCESS;
+    }
+#endif
+#if defined(PSA_WANT_ALG_FFDH)
+    if (alg == PSA_ALG_FFDH) {
+        return PSA_SUCCESS;
+    }
+#endif
+    (void) alg;
+    return PSA_ERROR_NOT_SUPPORTED;
+}
+
+static int psa_key_derivation_allows_free_form_secret_input(
+    psa_algorithm_t kdf_alg)
+{
+#if defined(PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS)
+    if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
+        return 0;
+    }
+#endif
+    (void) kdf_alg;
+    return 1;
+}
+#endif /* AT_LEAST_ONE_BUILTIN_KDF */
+
+psa_status_t psa_key_derivation_setup(psa_key_derivation_operation_t *operation,
+                                      psa_algorithm_t alg)
+{
+    psa_status_t status;
+
+    if (operation->alg != 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    if (PSA_ALG_IS_RAW_KEY_AGREEMENT(alg)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    } else if (PSA_ALG_IS_KEY_AGREEMENT(alg)) {
+#if defined(AT_LEAST_ONE_BUILTIN_KDF)
+        psa_algorithm_t kdf_alg = PSA_ALG_KEY_AGREEMENT_GET_KDF(alg);
+        psa_algorithm_t ka_alg = PSA_ALG_KEY_AGREEMENT_GET_BASE(alg);
+        status = psa_key_agreement_try_support(ka_alg);
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+        if (!psa_key_derivation_allows_free_form_secret_input(kdf_alg)) {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+        status = psa_key_derivation_setup_kdf(operation, kdf_alg);
+#else
+        return PSA_ERROR_NOT_SUPPORTED;
+#endif /* AT_LEAST_ONE_BUILTIN_KDF */
+    } else if (PSA_ALG_IS_KEY_DERIVATION(alg)) {
+#if defined(AT_LEAST_ONE_BUILTIN_KDF)
+        status = psa_key_derivation_setup_kdf(operation, alg);
+#else
+        return PSA_ERROR_NOT_SUPPORTED;
+#endif /* AT_LEAST_ONE_BUILTIN_KDF */
+    } else {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (status == PSA_SUCCESS) {
+        operation->alg = alg;
+    }
+    return status;
+}
+
+#if defined(BUILTIN_ALG_ANY_HKDF)
+static psa_status_t psa_hkdf_input(psa_hkdf_key_derivation_t *hkdf,
+                                   psa_algorithm_t kdf_alg,
+                                   psa_key_derivation_step_t step,
+                                   const uint8_t *data,
+                                   size_t data_length)
+{
+    psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH(kdf_alg);
+    psa_status_t status;
+    switch (step) {
+        case PSA_KEY_DERIVATION_INPUT_SALT:
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND)
+            if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg)) {
+                return PSA_ERROR_INVALID_ARGUMENT;
+            }
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND */
+            if (hkdf->state != HKDF_STATE_INIT) {
+                return PSA_ERROR_BAD_STATE;
+            } else {
+                status = psa_key_derivation_start_hmac(&hkdf->hmac,
+                                                       hash_alg,
+                                                       data, data_length);
+                if (status != PSA_SUCCESS) {
+                    return status;
+                }
+                hkdf->state = HKDF_STATE_STARTED;
+                return PSA_SUCCESS;
+            }
+        case PSA_KEY_DERIVATION_INPUT_SECRET:
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND)
+            if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg)) {
+                /* We shouldn't be in different state as HKDF_EXPAND only allows
+                 * two inputs: SECRET (this case) and INFO which does not modify
+                 * the state. It could happen only if the hkdf
+                 * object was corrupted. */
+                if (hkdf->state != HKDF_STATE_INIT) {
+                    return PSA_ERROR_BAD_STATE;
+                }
+
+                /* Allow only input that fits expected prk size */
+                if (data_length != PSA_HASH_LENGTH(hash_alg)) {
+                    return PSA_ERROR_INVALID_ARGUMENT;
+                }
+
+                memcpy(hkdf->prk, data, data_length);
+            } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND */
+            {
+                /* HKDF: If no salt was provided, use an empty salt.
+                 * HKDF-EXTRACT: salt is mandatory. */
+                if (hkdf->state == HKDF_STATE_INIT) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT)
+                    if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) {
+                        return PSA_ERROR_BAD_STATE;
+                    }
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */
+                    status = psa_key_derivation_start_hmac(&hkdf->hmac,
+                                                           hash_alg,
+                                                           NULL, 0);
+                    if (status != PSA_SUCCESS) {
+                        return status;
+                    }
+                    hkdf->state = HKDF_STATE_STARTED;
+                }
+                if (hkdf->state != HKDF_STATE_STARTED) {
+                    return PSA_ERROR_BAD_STATE;
+                }
+                status = psa_mac_update(&hkdf->hmac,
+                                        data, data_length);
+                if (status != PSA_SUCCESS) {
+                    return status;
+                }
+                status = psa_mac_sign_finish(&hkdf->hmac,
+                                             hkdf->prk,
+                                             sizeof(hkdf->prk),
+                                             &data_length);
+                if (status != PSA_SUCCESS) {
+                    return status;
+                }
+            }
+
+            hkdf->state = HKDF_STATE_KEYED;
+            hkdf->block_number = 0;
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT)
+            if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) {
+                /* The only block of output is the PRK. */
+                memcpy(hkdf->output_block, hkdf->prk, PSA_HASH_LENGTH(hash_alg));
+                hkdf->offset_in_block = 0;
+            } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */
+            {
+                /* Block 0 is empty, and the next block will be
+                 * generated by psa_key_derivation_hkdf_read(). */
+                hkdf->offset_in_block = PSA_HASH_LENGTH(hash_alg);
+            }
+
+            return PSA_SUCCESS;
+        case PSA_KEY_DERIVATION_INPUT_INFO:
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT)
+            if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) {
+                return PSA_ERROR_INVALID_ARGUMENT;
+            }
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND)
+            if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg) &&
+                hkdf->state == HKDF_STATE_INIT) {
+                return PSA_ERROR_BAD_STATE;
+            }
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */
+            if (hkdf->state == HKDF_STATE_OUTPUT) {
+                return PSA_ERROR_BAD_STATE;
+            }
+            if (hkdf->info_set) {
+                return PSA_ERROR_BAD_STATE;
+            }
+            hkdf->info_length = data_length;
+            if (data_length != 0) {
+                hkdf->info = mbedtls_calloc(1, data_length);
+                if (hkdf->info == NULL) {
+                    return PSA_ERROR_INSUFFICIENT_MEMORY;
+                }
+                memcpy(hkdf->info, data, data_length);
+            }
+            hkdf->info_set = 1;
+            return PSA_SUCCESS;
+        default:
+            return PSA_ERROR_INVALID_ARGUMENT;
+    }
+}
+#endif /* BUILTIN_ALG_ANY_HKDF */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+static psa_status_t psa_tls12_prf_set_seed(psa_tls12_prf_key_derivation_t *prf,
+                                           const uint8_t *data,
+                                           size_t data_length)
+{
+    if (prf->state != PSA_TLS12_PRF_STATE_INIT) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    if (data_length != 0) {
+        prf->seed = mbedtls_calloc(1, data_length);
+        if (prf->seed == NULL) {
+            return PSA_ERROR_INSUFFICIENT_MEMORY;
+        }
+
+        memcpy(prf->seed, data, data_length);
+        prf->seed_length = data_length;
+    }
+
+    prf->state = PSA_TLS12_PRF_STATE_SEED_SET;
+
+    return PSA_SUCCESS;
+}
+
+static psa_status_t psa_tls12_prf_set_key(psa_tls12_prf_key_derivation_t *prf,
+                                          const uint8_t *data,
+                                          size_t data_length)
+{
+    if (prf->state != PSA_TLS12_PRF_STATE_SEED_SET &&
+        prf->state != PSA_TLS12_PRF_STATE_OTHER_KEY_SET) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    if (data_length != 0) {
+        prf->secret = mbedtls_calloc(1, data_length);
+        if (prf->secret == NULL) {
+            return PSA_ERROR_INSUFFICIENT_MEMORY;
+        }
+
+        memcpy(prf->secret, data, data_length);
+        prf->secret_length = data_length;
+    }
+
+    prf->state = PSA_TLS12_PRF_STATE_KEY_SET;
+
+    return PSA_SUCCESS;
+}
+
+static psa_status_t psa_tls12_prf_set_label(psa_tls12_prf_key_derivation_t *prf,
+                                            const uint8_t *data,
+                                            size_t data_length)
+{
+    if (prf->state != PSA_TLS12_PRF_STATE_KEY_SET) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    if (data_length != 0) {
+        prf->label = mbedtls_calloc(1, data_length);
+        if (prf->label == NULL) {
+            return PSA_ERROR_INSUFFICIENT_MEMORY;
+        }
+
+        memcpy(prf->label, data, data_length);
+        prf->label_length = data_length;
+    }
+
+    prf->state = PSA_TLS12_PRF_STATE_LABEL_SET;
+
+    return PSA_SUCCESS;
+}
+
+static psa_status_t psa_tls12_prf_input(psa_tls12_prf_key_derivation_t *prf,
+                                        psa_key_derivation_step_t step,
+                                        const uint8_t *data,
+                                        size_t data_length)
+{
+    switch (step) {
+        case PSA_KEY_DERIVATION_INPUT_SEED:
+            return psa_tls12_prf_set_seed(prf, data, data_length);
+        case PSA_KEY_DERIVATION_INPUT_SECRET:
+            return psa_tls12_prf_set_key(prf, data, data_length);
+        case PSA_KEY_DERIVATION_INPUT_LABEL:
+            return psa_tls12_prf_set_label(prf, data, data_length);
+        default:
+            return PSA_ERROR_INVALID_ARGUMENT;
+    }
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) ||
+        * MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+static psa_status_t psa_tls12_prf_psk_to_ms_set_key(
+    psa_tls12_prf_key_derivation_t *prf,
+    const uint8_t *data,
+    size_t data_length)
+{
+    psa_status_t status;
+    const size_t pms_len = (prf->state == PSA_TLS12_PRF_STATE_OTHER_KEY_SET ?
+                            4 + data_length + prf->other_secret_length :
+                            4 + 2 * data_length);
+
+    if (data_length > PSA_TLS12_PSK_TO_MS_PSK_MAX_SIZE) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    uint8_t *pms = mbedtls_calloc(1, pms_len);
+    if (pms == NULL) {
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+    uint8_t *cur = pms;
+
+    /* pure-PSK:
+     * Quoting RFC 4279, Section 2:
+     *
+     * The premaster secret is formed as follows: if the PSK is N octets
+     * long, concatenate a uint16 with the value N, N zero octets, a second
+     * uint16 with the value N, and the PSK itself.
+     *
+     * mixed-PSK:
+     * In a DHE-PSK, RSA-PSK, ECDHE-PSK the premaster secret is formed as
+     * follows: concatenate a uint16 with the length of the other secret,
+     * the other secret itself, uint16 with the length of PSK, and the
+     * PSK itself.
+     * For details please check:
+     * - RFC 4279, Section 4 for the definition of RSA-PSK,
+     * - RFC 4279, Section 3 for the definition of DHE-PSK,
+     * - RFC 5489 for the definition of ECDHE-PSK.
+     */
+
+    if (prf->state == PSA_TLS12_PRF_STATE_OTHER_KEY_SET) {
+        *cur++ = MBEDTLS_BYTE_1(prf->other_secret_length);
+        *cur++ = MBEDTLS_BYTE_0(prf->other_secret_length);
+        if (prf->other_secret_length != 0) {
+            memcpy(cur, prf->other_secret, prf->other_secret_length);
+            mbedtls_platform_zeroize(prf->other_secret, prf->other_secret_length);
+            cur += prf->other_secret_length;
+        }
+    } else {
+        *cur++ = MBEDTLS_BYTE_1(data_length);
+        *cur++ = MBEDTLS_BYTE_0(data_length);
+        memset(cur, 0, data_length);
+        cur += data_length;
+    }
+
+    *cur++ = MBEDTLS_BYTE_1(data_length);
+    *cur++ = MBEDTLS_BYTE_0(data_length);
+    memcpy(cur, data, data_length);
+    cur += data_length;
+
+    status = psa_tls12_prf_set_key(prf, pms, (size_t) (cur - pms));
+
+    mbedtls_zeroize_and_free(pms, pms_len);
+    return status;
+}
+
+static psa_status_t psa_tls12_prf_psk_to_ms_set_other_key(
+    psa_tls12_prf_key_derivation_t *prf,
+    const uint8_t *data,
+    size_t data_length)
+{
+    if (prf->state != PSA_TLS12_PRF_STATE_SEED_SET) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    if (data_length != 0) {
+        prf->other_secret = mbedtls_calloc(1, data_length);
+        if (prf->other_secret == NULL) {
+            return PSA_ERROR_INSUFFICIENT_MEMORY;
+        }
+
+        memcpy(prf->other_secret, data, data_length);
+        prf->other_secret_length = data_length;
+    } else {
+        prf->other_secret_length = 0;
+    }
+
+    prf->state = PSA_TLS12_PRF_STATE_OTHER_KEY_SET;
+
+    return PSA_SUCCESS;
+}
+
+static psa_status_t psa_tls12_prf_psk_to_ms_input(
+    psa_tls12_prf_key_derivation_t *prf,
+    psa_key_derivation_step_t step,
+    const uint8_t *data,
+    size_t data_length)
+{
+    switch (step) {
+        case PSA_KEY_DERIVATION_INPUT_SECRET:
+            return psa_tls12_prf_psk_to_ms_set_key(prf,
+                                                   data, data_length);
+            break;
+        case PSA_KEY_DERIVATION_INPUT_OTHER_SECRET:
+            return psa_tls12_prf_psk_to_ms_set_other_key(prf,
+                                                         data,
+                                                         data_length);
+            break;
+        default:
+            return psa_tls12_prf_input(prf, step, data, data_length);
+            break;
+
+    }
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS)
+static psa_status_t psa_tls12_ecjpake_to_pms_input(
+    psa_tls12_ecjpake_to_pms_t *ecjpake,
+    psa_key_derivation_step_t step,
+    const uint8_t *data,
+    size_t data_length)
+{
+    if (data_length != PSA_TLS12_ECJPAKE_TO_PMS_INPUT_SIZE ||
+        step != PSA_KEY_DERIVATION_INPUT_SECRET) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Check if the passed point is in an uncompressed form */
+    if (data[0] != 0x04) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Only K.X has to be extracted - bytes 1 to 32 inclusive. */
+    memcpy(ecjpake->data, data + 1, PSA_TLS12_ECJPAKE_TO_PMS_DATA_SIZE);
+
+    return PSA_SUCCESS;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS */
+
+#if defined(PSA_HAVE_SOFT_PBKDF2)
+static psa_status_t psa_pbkdf2_set_input_cost(
+    psa_pbkdf2_key_derivation_t *pbkdf2,
+    psa_key_derivation_step_t step,
+    uint64_t data)
+{
+    if (step != PSA_KEY_DERIVATION_INPUT_COST) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (pbkdf2->state != PSA_PBKDF2_STATE_INIT) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    if (data > PSA_VENDOR_PBKDF2_MAX_ITERATIONS) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    if (data == 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    pbkdf2->input_cost = data;
+    pbkdf2->state = PSA_PBKDF2_STATE_INPUT_COST_SET;
+
+    return PSA_SUCCESS;
+}
+
+static psa_status_t psa_pbkdf2_set_salt(psa_pbkdf2_key_derivation_t *pbkdf2,
+                                        const uint8_t *data,
+                                        size_t data_length)
+{
+    if (pbkdf2->state == PSA_PBKDF2_STATE_INPUT_COST_SET) {
+        pbkdf2->state = PSA_PBKDF2_STATE_SALT_SET;
+    } else if (pbkdf2->state == PSA_PBKDF2_STATE_SALT_SET) {
+        /* Appending to existing salt. No state change. */
+    } else {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    if (data_length == 0) {
+        /* Appending an empty string, nothing to do. */
+    } else {
+        uint8_t *next_salt;
+
+        next_salt = mbedtls_calloc(1, data_length + pbkdf2->salt_length);
+        if (next_salt == NULL) {
+            return PSA_ERROR_INSUFFICIENT_MEMORY;
+        }
+
+        if (pbkdf2->salt_length != 0) {
+            memcpy(next_salt, pbkdf2->salt, pbkdf2->salt_length);
+        }
+        memcpy(next_salt + pbkdf2->salt_length, data, data_length);
+        pbkdf2->salt_length += data_length;
+        mbedtls_free(pbkdf2->salt);
+        pbkdf2->salt = next_salt;
+    }
+    return PSA_SUCCESS;
+}
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_HMAC)
+static psa_status_t psa_pbkdf2_hmac_set_password(psa_algorithm_t hash_alg,
+                                                 const uint8_t *input,
+                                                 size_t input_len,
+                                                 uint8_t *output,
+                                                 size_t *output_len)
+{
+    psa_status_t status = PSA_SUCCESS;
+    if (input_len > PSA_HASH_BLOCK_LENGTH(hash_alg)) {
+        return psa_hash_compute(hash_alg, input, input_len, output,
+                                PSA_HMAC_MAX_HASH_BLOCK_SIZE, output_len);
+    } else if (input_len > 0) {
+        memcpy(output, input, input_len);
+    }
+    *output_len = PSA_HASH_BLOCK_LENGTH(hash_alg);
+    return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_HMAC */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_AES_CMAC_PRF_128)
+static psa_status_t psa_pbkdf2_cmac_set_password(const uint8_t *input,
+                                                 size_t input_len,
+                                                 uint8_t *output,
+                                                 size_t *output_len)
+{
+    psa_status_t status = PSA_SUCCESS;
+    if (input_len != PSA_MAC_LENGTH(PSA_KEY_TYPE_AES, 128U, PSA_ALG_CMAC)) {
+        psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+        uint8_t zeros[16] = { 0 };
+        psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
+        psa_set_key_bits(&attributes, PSA_BYTES_TO_BITS(sizeof(zeros)));
+        psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE);
+        /* Passing PSA_MAC_LENGTH(PSA_KEY_TYPE_AES, 128U, PSA_ALG_CMAC) as
+         * mac_size as the driver function sets mac_output_length = mac_size
+         * on success. See https://github.com/Mbed-TLS/mbedtls/issues/7801 */
+        status = psa_driver_wrapper_mac_compute(&attributes,
+                                                zeros, sizeof(zeros),
+                                                PSA_ALG_CMAC, input, input_len,
+                                                output,
+                                                PSA_MAC_LENGTH(PSA_KEY_TYPE_AES,
+                                                               128U,
+                                                               PSA_ALG_CMAC),
+                                                output_len);
+    } else {
+        memcpy(output, input, input_len);
+        *output_len = PSA_MAC_LENGTH(PSA_KEY_TYPE_AES, 128U, PSA_ALG_CMAC);
+    }
+    return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_AES_CMAC_PRF_128 */
+
+static psa_status_t psa_pbkdf2_set_password(psa_pbkdf2_key_derivation_t *pbkdf2,
+                                            psa_algorithm_t kdf_alg,
+                                            const uint8_t *data,
+                                            size_t data_length)
+{
+    psa_status_t status = PSA_SUCCESS;
+    if (pbkdf2->state != PSA_PBKDF2_STATE_SALT_SET) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_HMAC)
+    if (PSA_ALG_IS_PBKDF2_HMAC(kdf_alg)) {
+        psa_algorithm_t hash_alg = PSA_ALG_PBKDF2_HMAC_GET_HASH(kdf_alg);
+        status = psa_pbkdf2_hmac_set_password(hash_alg, data, data_length,
+                                              pbkdf2->password,
+                                              &pbkdf2->password_length);
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_HMAC */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_AES_CMAC_PRF_128)
+    if (kdf_alg == PSA_ALG_PBKDF2_AES_CMAC_PRF_128) {
+        status = psa_pbkdf2_cmac_set_password(data, data_length,
+                                              pbkdf2->password,
+                                              &pbkdf2->password_length);
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_AES_CMAC_PRF_128 */
+    {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    pbkdf2->state = PSA_PBKDF2_STATE_PASSWORD_SET;
+
+    return status;
+}
+
+static psa_status_t psa_pbkdf2_input(psa_pbkdf2_key_derivation_t *pbkdf2,
+                                     psa_algorithm_t kdf_alg,
+                                     psa_key_derivation_step_t step,
+                                     const uint8_t *data,
+                                     size_t data_length)
+{
+    switch (step) {
+        case PSA_KEY_DERIVATION_INPUT_SALT:
+            return psa_pbkdf2_set_salt(pbkdf2, data, data_length);
+        case PSA_KEY_DERIVATION_INPUT_PASSWORD:
+            return psa_pbkdf2_set_password(pbkdf2, kdf_alg, data, data_length);
+        default:
+            return PSA_ERROR_INVALID_ARGUMENT;
+    }
+}
+#endif /* PSA_HAVE_SOFT_PBKDF2 */
+
+/** Check whether the given key type is acceptable for the given
+ * input step of a key derivation.
+ *
+ * Secret inputs must have the type #PSA_KEY_TYPE_DERIVE.
+ * Non-secret inputs must have the type #PSA_KEY_TYPE_RAW_DATA.
+ * Both secret and non-secret inputs can alternatively have the type
+ * #PSA_KEY_TYPE_NONE, which is never the type of a key object, meaning
+ * that the input was passed as a buffer rather than via a key object.
+ */
+static int psa_key_derivation_check_input_type(
+    psa_key_derivation_step_t step,
+    psa_key_type_t key_type)
+{
+    switch (step) {
+        case PSA_KEY_DERIVATION_INPUT_SECRET:
+            if (key_type == PSA_KEY_TYPE_DERIVE) {
+                return PSA_SUCCESS;
+            }
+            if (key_type == PSA_KEY_TYPE_NONE) {
+                return PSA_SUCCESS;
+            }
+            break;
+        case PSA_KEY_DERIVATION_INPUT_OTHER_SECRET:
+            if (key_type == PSA_KEY_TYPE_DERIVE) {
+                return PSA_SUCCESS;
+            }
+            if (key_type == PSA_KEY_TYPE_NONE) {
+                return PSA_SUCCESS;
+            }
+            break;
+        case PSA_KEY_DERIVATION_INPUT_LABEL:
+        case PSA_KEY_DERIVATION_INPUT_SALT:
+        case PSA_KEY_DERIVATION_INPUT_INFO:
+        case PSA_KEY_DERIVATION_INPUT_SEED:
+            if (key_type == PSA_KEY_TYPE_RAW_DATA) {
+                return PSA_SUCCESS;
+            }
+            if (key_type == PSA_KEY_TYPE_NONE) {
+                return PSA_SUCCESS;
+            }
+            break;
+        case PSA_KEY_DERIVATION_INPUT_PASSWORD:
+            if (key_type == PSA_KEY_TYPE_PASSWORD) {
+                return PSA_SUCCESS;
+            }
+            if (key_type == PSA_KEY_TYPE_DERIVE) {
+                return PSA_SUCCESS;
+            }
+            if (key_type == PSA_KEY_TYPE_NONE) {
+                return PSA_SUCCESS;
+            }
+            break;
+    }
+    return PSA_ERROR_INVALID_ARGUMENT;
+}
+
+static psa_status_t psa_key_derivation_input_internal(
+    psa_key_derivation_operation_t *operation,
+    psa_key_derivation_step_t step,
+    psa_key_type_t key_type,
+    const uint8_t *data,
+    size_t data_length)
+{
+    psa_status_t status;
+    psa_algorithm_t kdf_alg = psa_key_derivation_get_kdf_alg(operation);
+
+    status = psa_key_derivation_check_input_type(step, key_type);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+#if defined(BUILTIN_ALG_ANY_HKDF)
+    if (PSA_ALG_IS_ANY_HKDF(kdf_alg)) {
+        status = psa_hkdf_input(&operation->ctx.hkdf, kdf_alg,
+                                step, data, data_length);
+    } else
+#endif /* BUILTIN_ALG_ANY_HKDF */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF)
+    if (PSA_ALG_IS_TLS12_PRF(kdf_alg)) {
+        status = psa_tls12_prf_input(&operation->ctx.tls12_prf,
+                                     step, data, data_length);
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+    if (PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg)) {
+        status = psa_tls12_prf_psk_to_ms_input(&operation->ctx.tls12_prf,
+                                               step, data, data_length);
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS)
+    if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
+        status = psa_tls12_ecjpake_to_pms_input(
+            &operation->ctx.tls12_ecjpake_to_pms, step, data, data_length);
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS */
+#if defined(PSA_HAVE_SOFT_PBKDF2)
+    if (PSA_ALG_IS_PBKDF2(kdf_alg)) {
+        status = psa_pbkdf2_input(&operation->ctx.pbkdf2, kdf_alg,
+                                  step, data, data_length);
+    } else
+#endif /* PSA_HAVE_SOFT_PBKDF2 */
+    {
+        /* This can't happen unless the operation object was not initialized */
+        (void) data;
+        (void) data_length;
+        (void) kdf_alg;
+        return PSA_ERROR_BAD_STATE;
+    }
+
+exit:
+    if (status != PSA_SUCCESS) {
+        psa_key_derivation_abort(operation);
+    }
+    return status;
+}
+
+static psa_status_t psa_key_derivation_input_integer_internal(
+    psa_key_derivation_operation_t *operation,
+    psa_key_derivation_step_t step,
+    uint64_t value)
+{
+    psa_status_t status;
+    psa_algorithm_t kdf_alg = psa_key_derivation_get_kdf_alg(operation);
+
+#if defined(PSA_HAVE_SOFT_PBKDF2)
+    if (PSA_ALG_IS_PBKDF2(kdf_alg)) {
+        status = psa_pbkdf2_set_input_cost(
+            &operation->ctx.pbkdf2, step, value);
+    } else
+#endif /* PSA_HAVE_SOFT_PBKDF2 */
+    {
+        (void) step;
+        (void) value;
+        (void) kdf_alg;
+        status = PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (status != PSA_SUCCESS) {
+        psa_key_derivation_abort(operation);
+    }
+    return status;
+}
+
+psa_status_t psa_key_derivation_input_bytes(
+    psa_key_derivation_operation_t *operation,
+    psa_key_derivation_step_t step,
+    const uint8_t *data_external,
+    size_t data_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    LOCAL_INPUT_DECLARE(data_external, data);
+
+    LOCAL_INPUT_ALLOC(data_external, data_length, data);
+
+    status = psa_key_derivation_input_internal(operation, step,
+                                               PSA_KEY_TYPE_NONE,
+                                               data, data_length);
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+    LOCAL_INPUT_FREE(data_external, data);
+    return status;
+}
+
+psa_status_t psa_key_derivation_input_integer(
+    psa_key_derivation_operation_t *operation,
+    psa_key_derivation_step_t step,
+    uint64_t value)
+{
+    return psa_key_derivation_input_integer_internal(operation, step, value);
+}
+
+psa_status_t psa_key_derivation_input_key(
+    psa_key_derivation_operation_t *operation,
+    psa_key_derivation_step_t step,
+    mbedtls_svc_key_id_t key)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+
+    status = psa_get_and_lock_transparent_key_slot_with_policy(
+        key, &slot, PSA_KEY_USAGE_DERIVE, operation->alg);
+    if (status != PSA_SUCCESS) {
+        psa_key_derivation_abort(operation);
+        return status;
+    }
+
+    /* Passing a key object as a SECRET or PASSWORD input unlocks the
+     * permission to output to a key object. */
+    if (step == PSA_KEY_DERIVATION_INPUT_SECRET ||
+        step == PSA_KEY_DERIVATION_INPUT_PASSWORD) {
+        operation->can_output_key = 1;
+    }
+
+    status = psa_key_derivation_input_internal(operation,
+                                               step, slot->attr.type,
+                                               slot->key.data,
+                                               slot->key.bytes);
+
+    unlock_status = psa_unregister_read_under_mutex(slot);
+
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+
+
+/****************************************************************/
+/* Key agreement */
+/****************************************************************/
+
+psa_status_t psa_key_agreement_raw_builtin(const psa_key_attributes_t *attributes,
+                                           const uint8_t *key_buffer,
+                                           size_t key_buffer_size,
+                                           psa_algorithm_t alg,
+                                           const uint8_t *peer_key,
+                                           size_t peer_key_length,
+                                           uint8_t *shared_secret,
+                                           size_t shared_secret_size,
+                                           size_t *shared_secret_length)
+{
+    switch (alg) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH)
+        case PSA_ALG_ECDH:
+            return mbedtls_psa_key_agreement_ecdh(attributes, key_buffer,
+                                                  key_buffer_size, alg,
+                                                  peer_key, peer_key_length,
+                                                  shared_secret,
+                                                  shared_secret_size,
+                                                  shared_secret_length);
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_ECDH */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_FFDH)
+        case PSA_ALG_FFDH:
+            return mbedtls_psa_ffdh_key_agreement(attributes,
+                                                  peer_key,
+                                                  peer_key_length,
+                                                  key_buffer,
+                                                  key_buffer_size,
+                                                  shared_secret,
+                                                  shared_secret_size,
+                                                  shared_secret_length);
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_FFDH */
+
+        default:
+            (void) attributes;
+            (void) key_buffer;
+            (void) key_buffer_size;
+            (void) peer_key;
+            (void) peer_key_length;
+            (void) shared_secret;
+            (void) shared_secret_size;
+            (void) shared_secret_length;
+            return PSA_ERROR_NOT_SUPPORTED;
+    }
+}
+
+/** Internal function for raw key agreement
+ *  Calls the driver wrapper which will hand off key agreement task
+ *  to the driver's implementation if a driver is present.
+ *  Fallback specified in the driver wrapper is built-in raw key agreement
+ *  (psa_key_agreement_raw_builtin).
+ */
+static psa_status_t psa_key_agreement_raw_internal(psa_algorithm_t alg,
+                                                   psa_key_slot_t *private_key,
+                                                   const uint8_t *peer_key,
+                                                   size_t peer_key_length,
+                                                   uint8_t *shared_secret,
+                                                   size_t shared_secret_size,
+                                                   size_t *shared_secret_length)
+{
+    if (!PSA_ALG_IS_RAW_KEY_AGREEMENT(alg)) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    return psa_driver_wrapper_key_agreement(&private_key->attr,
+                                            private_key->key.data,
+                                            private_key->key.bytes, alg,
+                                            peer_key, peer_key_length,
+                                            shared_secret,
+                                            shared_secret_size,
+                                            shared_secret_length);
+}
+
+/* Note that if this function fails, you must call psa_key_derivation_abort()
+ * to potentially free embedded data structures and wipe confidential data.
+ */
+static psa_status_t psa_key_agreement_internal(psa_key_derivation_operation_t *operation,
+                                               psa_key_derivation_step_t step,
+                                               psa_key_slot_t *private_key,
+                                               const uint8_t *peer_key,
+                                               size_t peer_key_length)
+{
+    psa_status_t status;
+    uint8_t shared_secret[PSA_RAW_KEY_AGREEMENT_OUTPUT_MAX_SIZE] = { 0 };
+    size_t shared_secret_length = 0;
+    psa_algorithm_t ka_alg = PSA_ALG_KEY_AGREEMENT_GET_BASE(operation->alg);
+
+    /* Step 1: run the secret agreement algorithm to generate the shared
+     * secret. */
+    status = psa_key_agreement_raw_internal(ka_alg,
+                                            private_key,
+                                            peer_key, peer_key_length,
+                                            shared_secret,
+                                            sizeof(shared_secret),
+                                            &shared_secret_length);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    /* Step 2: set up the key derivation to generate key material from
+     * the shared secret. A shared secret is permitted wherever a key
+     * of type DERIVE is permitted. */
+    status = psa_key_derivation_input_internal(operation, step,
+                                               PSA_KEY_TYPE_DERIVE,
+                                               shared_secret,
+                                               shared_secret_length);
+exit:
+    mbedtls_platform_zeroize(shared_secret, shared_secret_length);
+    return status;
+}
+
+psa_status_t psa_key_derivation_key_agreement(psa_key_derivation_operation_t *operation,
+                                              psa_key_derivation_step_t step,
+                                              mbedtls_svc_key_id_t private_key,
+                                              const uint8_t *peer_key_external,
+                                              size_t peer_key_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+    LOCAL_INPUT_DECLARE(peer_key_external, peer_key);
+
+    if (!PSA_ALG_IS_KEY_AGREEMENT(operation->alg)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+    status = psa_get_and_lock_transparent_key_slot_with_policy(
+        private_key, &slot, PSA_KEY_USAGE_DERIVE, operation->alg);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    LOCAL_INPUT_ALLOC(peer_key_external, peer_key_length, peer_key);
+    status = psa_key_agreement_internal(operation, step,
+                                        slot,
+                                        peer_key, peer_key_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+    if (status != PSA_SUCCESS) {
+        psa_key_derivation_abort(operation);
+    } else {
+        /* If a private key has been added as SECRET, we allow the derived
+         * key material to be used as a key in PSA Crypto. */
+        if (step == PSA_KEY_DERIVATION_INPUT_SECRET) {
+            operation->can_output_key = 1;
+        }
+    }
+
+    unlock_status = psa_unregister_read_under_mutex(slot);
+    LOCAL_INPUT_FREE(peer_key_external, peer_key);
+
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_raw_key_agreement(psa_algorithm_t alg,
+                                   mbedtls_svc_key_id_t private_key,
+                                   const uint8_t *peer_key_external,
+                                   size_t peer_key_length,
+                                   uint8_t *output_external,
+                                   size_t output_size,
+                                   size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot = NULL;
+    size_t expected_length;
+    LOCAL_INPUT_DECLARE(peer_key_external, peer_key);
+    LOCAL_OUTPUT_DECLARE(output_external, output);
+    LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+    if (!PSA_ALG_IS_KEY_AGREEMENT(alg)) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+    status = psa_get_and_lock_transparent_key_slot_with_policy(
+        private_key, &slot, PSA_KEY_USAGE_DERIVE, alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    /* PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE() is in general an upper bound
+     * for the output size. The PSA specification only guarantees that this
+     * function works if output_size >= PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(...),
+     * but it might be nice to allow smaller buffers if the output fits.
+     * At the time of writing this comment, with only ECDH implemented,
+     * PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE() is exact so the point is moot.
+     * If FFDH is implemented, PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE() can easily
+     * be exact for it as well. */
+    expected_length =
+        PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(slot->attr.type, slot->attr.bits);
+    if (output_size < expected_length) {
+        status = PSA_ERROR_BUFFER_TOO_SMALL;
+        goto exit;
+    }
+
+    LOCAL_INPUT_ALLOC(peer_key_external, peer_key_length, peer_key);
+    status = psa_key_agreement_raw_internal(alg, slot,
+                                            peer_key, peer_key_length,
+                                            output, output_size,
+                                            output_length);
+
+exit:
+    /* Check for successful allocation of output,
+     * with an unsuccessful status. */
+    if (output != NULL && status != PSA_SUCCESS) {
+        /* If an error happens and is not handled properly, the output
+         * may be used as a key to protect sensitive data. Arrange for such
+         * a key to be random, which is likely to result in decryption or
+         * verification errors. This is better than filling the buffer with
+         * some constant data such as zeros, which would result in the data
+         * being protected with a reproducible, easily knowable key.
+         */
+        psa_generate_random_internal(output, output_size);
+        *output_length = output_size;
+    }
+
+    if (output == NULL) {
+        /* output allocation failed. */
+        *output_length = 0;
+    }
+
+    unlock_status = psa_unregister_read_under_mutex(slot);
+
+    LOCAL_INPUT_FREE(peer_key_external, peer_key);
+    LOCAL_OUTPUT_FREE(output_external, output);
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+
+/****************************************************************/
+/* Random generation */
+/****************************************************************/
+
+#if defined(MBEDTLS_PSA_INJECT_ENTROPY)
+#include "entropy_poll.h"
+#endif
+
+/** Initialize the PSA random generator.
+ *
+ *  Note: the mbedtls_threading_psa_rngdata_mutex should be held when calling
+ *  this function if mutexes are enabled.
+ */
+static void mbedtls_psa_random_init(mbedtls_psa_random_context_t *rng)
+{
+#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+    memset(rng, 0, sizeof(*rng));
+#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+
+    /* Set default configuration if
+     * mbedtls_psa_crypto_configure_entropy_sources() hasn't been called. */
+    if (rng->entropy_init == NULL) {
+        rng->entropy_init = mbedtls_entropy_init;
+    }
+    if (rng->entropy_free == NULL) {
+        rng->entropy_free = mbedtls_entropy_free;
+    }
+
+    rng->entropy_init(&rng->entropy);
+#if defined(MBEDTLS_PSA_INJECT_ENTROPY) && \
+    defined(MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES)
+    /* The PSA entropy injection feature depends on using NV seed as an entropy
+     * source. Add NV seed as an entropy source for PSA entropy injection. */
+    mbedtls_entropy_add_source(&rng->entropy,
+                               mbedtls_nv_seed_poll, NULL,
+                               MBEDTLS_ENTROPY_BLOCK_SIZE,
+                               MBEDTLS_ENTROPY_SOURCE_STRONG);
+#endif
+
+    mbedtls_psa_drbg_init(&rng->drbg);
+#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+}
+
+/** Deinitialize the PSA random generator.
+ *
+ *  Note: the mbedtls_threading_psa_rngdata_mutex should be held when calling
+ *  this function if mutexes are enabled.
+ */
+static void mbedtls_psa_random_free(mbedtls_psa_random_context_t *rng)
+{
+#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+    memset(rng, 0, sizeof(*rng));
+#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+    mbedtls_psa_drbg_free(&rng->drbg);
+    rng->entropy_free(&rng->entropy);
+#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+}
+
+/** Seed the PSA random generator.
+ */
+static psa_status_t mbedtls_psa_random_seed(mbedtls_psa_random_context_t *rng)
+{
+#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+    /* Do nothing: the external RNG seeds itself. */
+    (void) rng;
+    return PSA_SUCCESS;
+#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+    const unsigned char drbg_seed[] = "PSA";
+    int ret = mbedtls_psa_drbg_seed(&rng->drbg, &rng->entropy,
+                                    drbg_seed, sizeof(drbg_seed) - 1);
+    return mbedtls_to_psa_error(ret);
+#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+}
+
+psa_status_t psa_generate_random(uint8_t *output_external,
+                                 size_t output_size)
+{
+    psa_status_t status;
+
+    LOCAL_OUTPUT_DECLARE(output_external, output);
+    LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+    status = psa_generate_random_internal(output, output_size);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+    LOCAL_OUTPUT_FREE(output_external, output);
+    return status;
+}
+
+#if defined(MBEDTLS_PSA_INJECT_ENTROPY)
+psa_status_t mbedtls_psa_inject_entropy(const uint8_t *seed,
+                                        size_t seed_size)
+{
+    if (psa_get_initialized()) {
+        return PSA_ERROR_NOT_PERMITTED;
+    }
+
+    if (((seed_size < MBEDTLS_ENTROPY_MIN_PLATFORM) ||
+         (seed_size < MBEDTLS_ENTROPY_BLOCK_SIZE)) ||
+        (seed_size > MBEDTLS_ENTROPY_MAX_SEED_SIZE)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    return mbedtls_psa_storage_inject_entropy(seed, seed_size);
+}
+#endif /* MBEDTLS_PSA_INJECT_ENTROPY */
+
+/** Validate the key type and size for key generation
+ *
+ * \param  type  The key type
+ * \param  bits  The number of bits of the key
+ *
+ * \retval #PSA_SUCCESS
+ *         The key type and size are valid.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         The size in bits of the key is not valid.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         The type and/or the size in bits of the key or the combination of
+ *         the two is not supported.
+ */
+static psa_status_t psa_validate_key_type_and_size_for_key_generation(
+    psa_key_type_t type, size_t bits)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    if (key_type_is_raw_bytes(type)) {
+        status = psa_validate_unstructured_key_bit_size(type, bits);
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+    } else
+#if defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_GENERATE)
+    if (PSA_KEY_TYPE_IS_RSA(type) && PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
+        if (bits > PSA_VENDOR_RSA_MAX_KEY_BITS) {
+            return PSA_ERROR_NOT_SUPPORTED;
+        }
+        if (bits < PSA_VENDOR_RSA_GENERATE_MIN_KEY_BITS) {
+            return PSA_ERROR_NOT_SUPPORTED;
+        }
+
+        /* Accept only byte-aligned keys, for the same reasons as
+         * in psa_import_rsa_key(). */
+        if (bits % 8 != 0) {
+            return PSA_ERROR_NOT_SUPPORTED;
+        }
+    } else
+#endif /* defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_GENERATE) */
+
+#if defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_GENERATE)
+    if (PSA_KEY_TYPE_IS_ECC(type) && PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
+        /* To avoid empty block, return successfully here. */
+        return PSA_SUCCESS;
+    } else
+#endif /* defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_GENERATE) */
+
+#if defined(PSA_WANT_KEY_TYPE_DH_KEY_PAIR_GENERATE)
+    if (PSA_KEY_TYPE_IS_DH(type) && PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
+        if (psa_is_dh_key_size_valid(bits) == 0) {
+            return PSA_ERROR_NOT_SUPPORTED;
+        }
+    } else
+#endif /* defined(PSA_WANT_KEY_TYPE_DH_KEY_PAIR_GENERATE) */
+    {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_generate_key_internal(
+    const psa_key_attributes_t *attributes,
+    const psa_key_production_parameters_t *params, size_t params_data_length,
+    uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_type_t type = attributes->type;
+
+    /* Only used for RSA */
+    (void) params;
+    (void) params_data_length;
+
+    if (key_type_is_raw_bytes(type)) {
+        status = psa_generate_random_internal(key_buffer, key_buffer_size);
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES)
+        if (type == PSA_KEY_TYPE_DES) {
+            psa_des_set_key_parity(key_buffer, key_buffer_size);
+        }
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES */
+    } else
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_GENERATE)
+    if (type == PSA_KEY_TYPE_RSA_KEY_PAIR) {
+        return mbedtls_psa_rsa_generate_key(attributes,
+                                            params, params_data_length,
+                                            key_buffer,
+                                            key_buffer_size,
+                                            key_buffer_length);
+    } else
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_GENERATE) */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_GENERATE)
+    if (PSA_KEY_TYPE_IS_ECC(type) && PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
+        return mbedtls_psa_ecp_generate_key(attributes,
+                                            key_buffer,
+                                            key_buffer_size,
+                                            key_buffer_length);
+    } else
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_GENERATE) */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_GENERATE)
+    if (PSA_KEY_TYPE_IS_DH(type) && PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
+        return mbedtls_psa_ffdh_generate_key(attributes,
+                                             key_buffer,
+                                             key_buffer_size,
+                                             key_buffer_length);
+    } else
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_GENERATE) */
+    {
+        (void) key_buffer_length;
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_generate_key_ext(const psa_key_attributes_t *attributes,
+                                  const psa_key_production_parameters_t *params,
+                                  size_t params_data_length,
+                                  mbedtls_svc_key_id_t *key)
+{
+    psa_status_t status;
+    psa_key_slot_t *slot = NULL;
+    psa_se_drv_table_entry_t *driver = NULL;
+    size_t key_buffer_size;
+
+    *key = MBEDTLS_SVC_KEY_ID_INIT;
+
+    /* Reject any attempt to create a zero-length key so that we don't
+     * risk tripping up later, e.g. on a malloc(0) that returns NULL. */
+    if (psa_get_key_bits(attributes) == 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Reject any attempt to create a public key. */
+    if (PSA_KEY_TYPE_IS_PUBLIC_KEY(attributes->type)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+#if defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_GENERATE)
+    if (attributes->type == PSA_KEY_TYPE_RSA_KEY_PAIR) {
+        if (params->flags != 0) {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+    } else
+#endif
+    if (!psa_key_production_parameters_are_default(params, params_data_length)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    status = psa_start_key_creation(PSA_KEY_CREATION_GENERATE, attributes,
+                                    &slot, &driver);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    /* In the case of a transparent key or an opaque key stored in local
+     * storage ( thus not in the case of generating a key in a secure element
+     * with storage ( MBEDTLS_PSA_CRYPTO_SE_C ) ),we have to allocate a
+     * buffer to hold the generated key material. */
+    if (slot->key.data == NULL) {
+        if (PSA_KEY_LIFETIME_GET_LOCATION(attributes->lifetime) ==
+            PSA_KEY_LOCATION_LOCAL_STORAGE) {
+            status = psa_validate_key_type_and_size_for_key_generation(
+                attributes->type, attributes->bits);
+            if (status != PSA_SUCCESS) {
+                goto exit;
+            }
+
+            key_buffer_size = PSA_EXPORT_KEY_OUTPUT_SIZE(
+                attributes->type,
+                attributes->bits);
+        } else {
+            status = psa_driver_wrapper_get_key_buffer_size(
+                attributes, &key_buffer_size);
+            if (status != PSA_SUCCESS) {
+                goto exit;
+            }
+        }
+
+        status = psa_allocate_buffer_to_slot(slot, key_buffer_size);
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+    }
+
+    status = psa_driver_wrapper_generate_key(attributes,
+                                             params, params_data_length,
+                                             slot->key.data, slot->key.bytes,
+                                             &slot->key.bytes);
+    if (status != PSA_SUCCESS) {
+        psa_remove_key_data_from_memory(slot);
+    }
+
+exit:
+    if (status == PSA_SUCCESS) {
+        status = psa_finish_key_creation(slot, driver, key);
+    }
+    if (status != PSA_SUCCESS) {
+        psa_fail_key_creation(slot, driver);
+    }
+
+    return status;
+}
+
+psa_status_t psa_generate_key(const psa_key_attributes_t *attributes,
+                              mbedtls_svc_key_id_t *key)
+{
+    return psa_generate_key_ext(attributes,
+                                &default_production_parameters, 0,
+                                key);
+}
+
+/****************************************************************/
+/* Module setup */
+/****************************************************************/
+
+#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+psa_status_t mbedtls_psa_crypto_configure_entropy_sources(
+    void (* entropy_init)(mbedtls_entropy_context *ctx),
+    void (* entropy_free)(mbedtls_entropy_context *ctx))
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    if (global_data.rng_state != RNG_NOT_INITIALIZED) {
+        status = PSA_ERROR_BAD_STATE;
+    } else {
+        global_data.rng.entropy_init = entropy_init;
+        global_data.rng.entropy_free = entropy_free;
+        status = PSA_SUCCESS;
+    }
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    return status;
+}
+#endif /* !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) */
+
+void mbedtls_psa_crypto_free(void)
+{
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    /* Nothing to do to free transaction. */
+    if (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED) {
+        global_data.initialized &= ~PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
+    }
+
+    if (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED) {
+        psa_wipe_all_key_slots();
+        global_data.initialized &= ~PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED;
+    }
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    if (global_data.rng_state != RNG_NOT_INITIALIZED) {
+        mbedtls_psa_random_free(&global_data.rng);
+    }
+    global_data.rng_state = RNG_NOT_INITIALIZED;
+    mbedtls_platform_zeroize(&global_data.rng, sizeof(global_data.rng));
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    /* Terminate drivers */
+    if (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED) {
+        psa_driver_wrapper_free();
+        global_data.initialized &= ~PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED;
+    }
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+}
+
+#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS)
+/** Recover a transaction that was interrupted by a power failure.
+ *
+ * This function is called during initialization, before psa_crypto_init()
+ * returns. If this function returns a failure status, the initialization
+ * fails.
+ */
+static psa_status_t psa_crypto_recover_transaction(
+    const psa_crypto_transaction_t *transaction)
+{
+    switch (transaction->unknown.type) {
+        case PSA_CRYPTO_TRANSACTION_CREATE_KEY:
+        case PSA_CRYPTO_TRANSACTION_DESTROY_KEY:
+        /* TODO - fall through to the failure case until this
+         * is implemented.
+         * https://github.com/ARMmbed/mbed-crypto/issues/218
+         */
+        default:
+            /* We found an unsupported transaction in the storage.
+             * We don't know what state the storage is in. Give up. */
+            return PSA_ERROR_DATA_INVALID;
+    }
+}
+#endif /* PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS */
+
+static psa_status_t mbedtls_psa_crypto_init_subsystem(mbedtls_psa_crypto_subsystem subsystem)
+{
+    psa_status_t status = PSA_SUCCESS;
+    uint8_t driver_wrappers_initialized = 0;
+
+    switch (subsystem) {
+        case PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS:
+
+#if defined(MBEDTLS_THREADING_C)
+            PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+            if (!(global_data.initialized & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED)) {
+                /* Init drivers */
+                status = psa_driver_wrapper_init();
+
+                /* Drivers need shutdown regardless of startup errors. */
+                global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED;
+
+
+            }
+#if defined(MBEDTLS_THREADING_C)
+            PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
+                                            &mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+            break;
+
+        case PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS:
+
+#if defined(MBEDTLS_THREADING_C)
+            PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+            if (!(global_data.initialized & PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED)) {
+                status = psa_initialize_key_slots();
+
+                /* Need to wipe keys even if initialization fails. */
+                global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED;
+
+            }
+#if defined(MBEDTLS_THREADING_C)
+            PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
+                                            &mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+            break;
+
+        case PSA_CRYPTO_SUBSYSTEM_RNG:
+
+#if defined(MBEDTLS_THREADING_C)
+            PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+            driver_wrappers_initialized =
+                (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED);
+
+#if defined(MBEDTLS_THREADING_C)
+            PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
+                                            &mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+            /* Need to use separate mutex here, as initialisation can require
+             * testing of init flags, which requires locking the global data
+             * mutex. */
+#if defined(MBEDTLS_THREADING_C)
+            PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+            /* Initialize and seed the random generator. */
+            if (global_data.rng_state == RNG_NOT_INITIALIZED && driver_wrappers_initialized) {
+                mbedtls_psa_random_init(&global_data.rng);
+                global_data.rng_state = RNG_INITIALIZED;
+
+                status = mbedtls_psa_random_seed(&global_data.rng);
+                if (status == PSA_SUCCESS) {
+                    global_data.rng_state = RNG_SEEDED;
+                }
+            }
+
+#if defined(MBEDTLS_THREADING_C)
+            PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
+                                            &mbedtls_threading_psa_rngdata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+            break;
+
+        case PSA_CRYPTO_SUBSYSTEM_TRANSACTION:
+
+#if defined(MBEDTLS_THREADING_C)
+            PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+            if (!(global_data.initialized & PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED)) {
+#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS)
+                status = psa_crypto_load_transaction();
+                if (status == PSA_SUCCESS) {
+                    status = psa_crypto_recover_transaction(&psa_crypto_transaction);
+                    if (status == PSA_SUCCESS) {
+                        global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
+                    }
+                    status = psa_crypto_stop_transaction();
+                } else if (status == PSA_ERROR_DOES_NOT_EXIST) {
+                    /* There's no transaction to complete. It's all good. */
+                    global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
+                    status = PSA_SUCCESS;
+                }
+#else /* defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS) */
+                global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
+                status = PSA_SUCCESS;
+#endif /* defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS) */
+            }
+
+#if defined(MBEDTLS_THREADING_C)
+            PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
+                                            &mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+            break;
+
+        default:
+            status = PSA_ERROR_CORRUPTION_DETECTED;
+    }
+
+    /* Exit label only required when using threading macros. */
+#if defined(MBEDTLS_THREADING_C)
+exit:
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    return status;
+}
+
+psa_status_t psa_crypto_init(void)
+{
+    psa_status_t status;
+
+    /* Double initialization is explicitly allowed. Early out if everything is
+     * done. */
+    if (psa_get_initialized()) {
+        return PSA_SUCCESS;
+    }
+
+    status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_RNG);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_TRANSACTION);
+
+exit:
+
+    if (status != PSA_SUCCESS) {
+        mbedtls_psa_crypto_free();
+    }
+
+    return status;
+}
+
+#if defined(PSA_WANT_ALG_SOME_PAKE)
+psa_status_t psa_crypto_driver_pake_get_password_len(
+    const psa_crypto_driver_pake_inputs_t *inputs,
+    size_t *password_len)
+{
+    if (inputs->password_len == 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    *password_len = inputs->password_len;
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_driver_pake_get_password(
+    const psa_crypto_driver_pake_inputs_t *inputs,
+    uint8_t *buffer, size_t buffer_size, size_t *buffer_length)
+{
+    if (inputs->password_len == 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    if (buffer_size < inputs->password_len) {
+        return PSA_ERROR_BUFFER_TOO_SMALL;
+    }
+
+    memcpy(buffer, inputs->password, inputs->password_len);
+    *buffer_length = inputs->password_len;
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_driver_pake_get_user_len(
+    const psa_crypto_driver_pake_inputs_t *inputs,
+    size_t *user_len)
+{
+    if (inputs->user_len == 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    *user_len = inputs->user_len;
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_driver_pake_get_user(
+    const psa_crypto_driver_pake_inputs_t *inputs,
+    uint8_t *user_id, size_t user_id_size, size_t *user_id_len)
+{
+    if (inputs->user_len == 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    if (user_id_size < inputs->user_len) {
+        return PSA_ERROR_BUFFER_TOO_SMALL;
+    }
+
+    memcpy(user_id, inputs->user, inputs->user_len);
+    *user_id_len = inputs->user_len;
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_driver_pake_get_peer_len(
+    const psa_crypto_driver_pake_inputs_t *inputs,
+    size_t *peer_len)
+{
+    if (inputs->peer_len == 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    *peer_len = inputs->peer_len;
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_driver_pake_get_peer(
+    const psa_crypto_driver_pake_inputs_t *inputs,
+    uint8_t *peer_id, size_t peer_id_size, size_t *peer_id_length)
+{
+    if (inputs->peer_len == 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    if (peer_id_size < inputs->peer_len) {
+        return PSA_ERROR_BUFFER_TOO_SMALL;
+    }
+
+    memcpy(peer_id, inputs->peer, inputs->peer_len);
+    *peer_id_length = inputs->peer_len;
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_driver_pake_get_cipher_suite(
+    const psa_crypto_driver_pake_inputs_t *inputs,
+    psa_pake_cipher_suite_t *cipher_suite)
+{
+    if (inputs->cipher_suite.algorithm == PSA_ALG_NONE) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    *cipher_suite = inputs->cipher_suite;
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_pake_setup(
+    psa_pake_operation_t *operation,
+    const psa_pake_cipher_suite_t *cipher_suite)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    if (operation->stage != PSA_PAKE_OPERATION_STAGE_SETUP) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (PSA_ALG_IS_PAKE(cipher_suite->algorithm) == 0 ||
+        PSA_ALG_IS_HASH(cipher_suite->hash) == 0) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    memset(&operation->data.inputs, 0, sizeof(operation->data.inputs));
+
+    operation->alg = cipher_suite->algorithm;
+    operation->primitive = PSA_PAKE_PRIMITIVE(cipher_suite->type,
+                                              cipher_suite->family, cipher_suite->bits);
+    operation->data.inputs.cipher_suite = *cipher_suite;
+
+#if defined(PSA_WANT_ALG_JPAKE)
+    if (operation->alg == PSA_ALG_JPAKE) {
+        psa_jpake_computation_stage_t *computation_stage =
+            &operation->computation_stage.jpake;
+
+        memset(computation_stage, 0, sizeof(*computation_stage));
+        computation_stage->step = PSA_PAKE_STEP_KEY_SHARE;
+    } else
+#endif /* PSA_WANT_ALG_JPAKE */
+    {
+        status = PSA_ERROR_NOT_SUPPORTED;
+        goto exit;
+    }
+
+    operation->stage = PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS;
+
+    return PSA_SUCCESS;
+exit:
+    psa_pake_abort(operation);
+    return status;
+}
+
+psa_status_t psa_pake_set_password_key(
+    psa_pake_operation_t *operation,
+    mbedtls_svc_key_id_t password)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot = NULL;
+    psa_key_type_t type;
+
+    if (operation->stage != PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    status = psa_get_and_lock_key_slot_with_policy(password, &slot,
+                                                   PSA_KEY_USAGE_DERIVE,
+                                                   operation->alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    type = psa_get_key_type(&slot->attr);
+
+    if (type != PSA_KEY_TYPE_PASSWORD &&
+        type != PSA_KEY_TYPE_PASSWORD_HASH) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    operation->data.inputs.password = mbedtls_calloc(1, slot->key.bytes);
+    if (operation->data.inputs.password == NULL) {
+        status = PSA_ERROR_INSUFFICIENT_MEMORY;
+        goto exit;
+    }
+
+    memcpy(operation->data.inputs.password, slot->key.data, slot->key.bytes);
+    operation->data.inputs.password_len = slot->key.bytes;
+    operation->data.inputs.attributes = slot->attr;
+
+exit:
+    if (status != PSA_SUCCESS) {
+        psa_pake_abort(operation);
+    }
+    unlock_status = psa_unregister_read_under_mutex(slot);
+    return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_pake_set_user(
+    psa_pake_operation_t *operation,
+    const uint8_t *user_id_external,
+    size_t user_id_len)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    LOCAL_INPUT_DECLARE(user_id_external, user_id);
+
+    if (operation->stage != PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (user_id_len == 0) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    if (operation->data.inputs.user_len != 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    operation->data.inputs.user = mbedtls_calloc(1, user_id_len);
+    if (operation->data.inputs.user == NULL) {
+        status = PSA_ERROR_INSUFFICIENT_MEMORY;
+        goto exit;
+    }
+
+    LOCAL_INPUT_ALLOC(user_id_external, user_id_len, user_id);
+
+    memcpy(operation->data.inputs.user, user_id, user_id_len);
+    operation->data.inputs.user_len = user_id_len;
+
+    status = PSA_SUCCESS;
+
+exit:
+    LOCAL_INPUT_FREE(user_id_external, user_id);
+    if (status != PSA_SUCCESS) {
+        psa_pake_abort(operation);
+    }
+    return status;
+}
+
+psa_status_t psa_pake_set_peer(
+    psa_pake_operation_t *operation,
+    const uint8_t *peer_id_external,
+    size_t peer_id_len)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    LOCAL_INPUT_DECLARE(peer_id_external, peer_id);
+
+    if (operation->stage != PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (peer_id_len == 0) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    if (operation->data.inputs.peer_len != 0) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    operation->data.inputs.peer = mbedtls_calloc(1, peer_id_len);
+    if (operation->data.inputs.peer == NULL) {
+        status = PSA_ERROR_INSUFFICIENT_MEMORY;
+        goto exit;
+    }
+
+    LOCAL_INPUT_ALLOC(peer_id_external, peer_id_len, peer_id);
+
+    memcpy(operation->data.inputs.peer, peer_id, peer_id_len);
+    operation->data.inputs.peer_len = peer_id_len;
+
+    status = PSA_SUCCESS;
+
+exit:
+    LOCAL_INPUT_FREE(peer_id_external, peer_id);
+    if (status != PSA_SUCCESS) {
+        psa_pake_abort(operation);
+    }
+    return status;
+}
+
+psa_status_t psa_pake_set_role(
+    psa_pake_operation_t *operation,
+    psa_pake_role_t role)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    if (operation->stage != PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    switch (operation->alg) {
+#if defined(PSA_WANT_ALG_JPAKE)
+        case PSA_ALG_JPAKE:
+            if (role == PSA_PAKE_ROLE_NONE) {
+                return PSA_SUCCESS;
+            }
+            status = PSA_ERROR_INVALID_ARGUMENT;
+            break;
+#endif
+        default:
+            (void) role;
+            status = PSA_ERROR_NOT_SUPPORTED;
+            goto exit;
+    }
+exit:
+    psa_pake_abort(operation);
+    return status;
+}
+
+/* Auxiliary function to convert core computation stage to single driver step. */
+#if defined(PSA_WANT_ALG_JPAKE)
+static psa_crypto_driver_pake_step_t convert_jpake_computation_stage_to_driver_step(
+    psa_jpake_computation_stage_t *stage)
+{
+    psa_crypto_driver_pake_step_t key_share_step;
+    if (stage->round == PSA_JPAKE_FIRST) {
+        int is_x1;
+
+        if (stage->io_mode == PSA_JPAKE_OUTPUT) {
+            is_x1 = (stage->outputs < 1);
+        } else {
+            is_x1 = (stage->inputs < 1);
+        }
+
+        key_share_step = is_x1 ?
+                         PSA_JPAKE_X1_STEP_KEY_SHARE :
+                         PSA_JPAKE_X2_STEP_KEY_SHARE;
+    } else if (stage->round == PSA_JPAKE_SECOND) {
+        key_share_step = (stage->io_mode == PSA_JPAKE_OUTPUT) ?
+                         PSA_JPAKE_X2S_STEP_KEY_SHARE :
+                         PSA_JPAKE_X4S_STEP_KEY_SHARE;
+    } else {
+        return PSA_JPAKE_STEP_INVALID;
+    }
+    return (psa_crypto_driver_pake_step_t) (key_share_step + stage->step - PSA_PAKE_STEP_KEY_SHARE);
+}
+#endif /* PSA_WANT_ALG_JPAKE */
+
+static psa_status_t psa_pake_complete_inputs(
+    psa_pake_operation_t *operation)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    /* Create copy of the inputs on stack as inputs share memory
+       with the driver context which will be setup by the driver. */
+    psa_crypto_driver_pake_inputs_t inputs = operation->data.inputs;
+
+    if (inputs.password_len == 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    if (operation->alg == PSA_ALG_JPAKE) {
+        if (inputs.user_len == 0 || inputs.peer_len == 0) {
+            return PSA_ERROR_BAD_STATE;
+        }
+    }
+
+    /* Clear driver context */
+    mbedtls_platform_zeroize(&operation->data, sizeof(operation->data));
+
+    status = psa_driver_wrapper_pake_setup(operation, &inputs);
+
+    /* Driver is responsible for creating its own copy of the password. */
+    mbedtls_zeroize_and_free(inputs.password, inputs.password_len);
+
+    /* User and peer are translated to role. */
+    mbedtls_free(inputs.user);
+    mbedtls_free(inputs.peer);
+
+    if (status == PSA_SUCCESS) {
+#if defined(PSA_WANT_ALG_JPAKE)
+        if (operation->alg == PSA_ALG_JPAKE) {
+            operation->stage = PSA_PAKE_OPERATION_STAGE_COMPUTATION;
+        } else
+#endif /* PSA_WANT_ALG_JPAKE */
+        {
+            status = PSA_ERROR_NOT_SUPPORTED;
+        }
+    }
+    return status;
+}
+
+#if defined(PSA_WANT_ALG_JPAKE)
+static psa_status_t psa_jpake_prologue(
+    psa_pake_operation_t *operation,
+    psa_pake_step_t step,
+    psa_jpake_io_mode_t io_mode)
+{
+    if (step != PSA_PAKE_STEP_KEY_SHARE &&
+        step != PSA_PAKE_STEP_ZK_PUBLIC &&
+        step != PSA_PAKE_STEP_ZK_PROOF) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    psa_jpake_computation_stage_t *computation_stage =
+        &operation->computation_stage.jpake;
+
+    if (computation_stage->round != PSA_JPAKE_FIRST &&
+        computation_stage->round != PSA_JPAKE_SECOND) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    /* Check that the step we are given is the one we were expecting */
+    if (step != computation_stage->step) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    if (step == PSA_PAKE_STEP_KEY_SHARE &&
+        computation_stage->inputs == 0 &&
+        computation_stage->outputs == 0) {
+        /* Start of the round, so function decides whether we are inputting
+         * or outputting */
+        computation_stage->io_mode = io_mode;
+    } else if (computation_stage->io_mode != io_mode) {
+        /* Middle of the round so the mode we are in must match the function
+         * called by the user */
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    return PSA_SUCCESS;
+}
+
+static psa_status_t psa_jpake_epilogue(
+    psa_pake_operation_t *operation,
+    psa_jpake_io_mode_t io_mode)
+{
+    psa_jpake_computation_stage_t *stage =
+        &operation->computation_stage.jpake;
+
+    if (stage->step == PSA_PAKE_STEP_ZK_PROOF) {
+        /* End of an input/output */
+        if (io_mode == PSA_JPAKE_INPUT) {
+            stage->inputs++;
+            if (stage->inputs == PSA_JPAKE_EXPECTED_INPUTS(stage->round)) {
+                stage->io_mode = PSA_JPAKE_OUTPUT;
+            }
+        }
+        if (io_mode == PSA_JPAKE_OUTPUT) {
+            stage->outputs++;
+            if (stage->outputs == PSA_JPAKE_EXPECTED_OUTPUTS(stage->round)) {
+                stage->io_mode = PSA_JPAKE_INPUT;
+            }
+        }
+        if (stage->inputs == PSA_JPAKE_EXPECTED_INPUTS(stage->round) &&
+            stage->outputs == PSA_JPAKE_EXPECTED_OUTPUTS(stage->round)) {
+            /* End of a round, move to the next round */
+            stage->inputs = 0;
+            stage->outputs = 0;
+            stage->round++;
+        }
+        stage->step = PSA_PAKE_STEP_KEY_SHARE;
+    } else {
+        stage->step++;
+    }
+    return PSA_SUCCESS;
+}
+
+#endif /* PSA_WANT_ALG_JPAKE */
+
+psa_status_t psa_pake_output(
+    psa_pake_operation_t *operation,
+    psa_pake_step_t step,
+    uint8_t *output_external,
+    size_t output_size,
+    size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_crypto_driver_pake_step_t driver_step = PSA_JPAKE_STEP_INVALID;
+    LOCAL_OUTPUT_DECLARE(output_external, output);
+    *output_length = 0;
+
+    if (operation->stage == PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) {
+        status = psa_pake_complete_inputs(operation);
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+    }
+
+    if (operation->stage != PSA_PAKE_OPERATION_STAGE_COMPUTATION) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (output_size == 0) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    switch (operation->alg) {
+#if defined(PSA_WANT_ALG_JPAKE)
+        case PSA_ALG_JPAKE:
+            status = psa_jpake_prologue(operation, step, PSA_JPAKE_OUTPUT);
+            if (status != PSA_SUCCESS) {
+                goto exit;
+            }
+            driver_step = convert_jpake_computation_stage_to_driver_step(
+                &operation->computation_stage.jpake);
+            break;
+#endif /* PSA_WANT_ALG_JPAKE */
+        default:
+            (void) step;
+            status = PSA_ERROR_NOT_SUPPORTED;
+            goto exit;
+    }
+
+    LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+    status = psa_driver_wrapper_pake_output(operation, driver_step,
+                                            output, output_size, output_length);
+
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    switch (operation->alg) {
+#if defined(PSA_WANT_ALG_JPAKE)
+        case PSA_ALG_JPAKE:
+            status = psa_jpake_epilogue(operation, PSA_JPAKE_OUTPUT);
+            if (status != PSA_SUCCESS) {
+                goto exit;
+            }
+            break;
+#endif /* PSA_WANT_ALG_JPAKE */
+        default:
+            status = PSA_ERROR_NOT_SUPPORTED;
+            goto exit;
+    }
+
+exit:
+    LOCAL_OUTPUT_FREE(output_external, output);
+    if (status != PSA_SUCCESS) {
+        psa_pake_abort(operation);
+    }
+    return status;
+}
+
+psa_status_t psa_pake_input(
+    psa_pake_operation_t *operation,
+    psa_pake_step_t step,
+    const uint8_t *input_external,
+    size_t input_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_crypto_driver_pake_step_t driver_step = PSA_JPAKE_STEP_INVALID;
+    const size_t max_input_length = (size_t) PSA_PAKE_INPUT_SIZE(operation->alg,
+                                                                 operation->primitive,
+                                                                 step);
+    LOCAL_INPUT_DECLARE(input_external, input);
+
+    if (operation->stage == PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) {
+        status = psa_pake_complete_inputs(operation);
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+    }
+
+    if (operation->stage != PSA_PAKE_OPERATION_STAGE_COMPUTATION) {
+        status =  PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+    if (input_length == 0 || input_length > max_input_length) {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    switch (operation->alg) {
+#if defined(PSA_WANT_ALG_JPAKE)
+        case PSA_ALG_JPAKE:
+            status = psa_jpake_prologue(operation, step, PSA_JPAKE_INPUT);
+            if (status != PSA_SUCCESS) {
+                goto exit;
+            }
+            driver_step = convert_jpake_computation_stage_to_driver_step(
+                &operation->computation_stage.jpake);
+            break;
+#endif /* PSA_WANT_ALG_JPAKE */
+        default:
+            (void) step;
+            status = PSA_ERROR_NOT_SUPPORTED;
+            goto exit;
+    }
+
+    LOCAL_INPUT_ALLOC(input_external, input_length, input);
+    status = psa_driver_wrapper_pake_input(operation, driver_step,
+                                           input, input_length);
+
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    switch (operation->alg) {
+#if defined(PSA_WANT_ALG_JPAKE)
+        case PSA_ALG_JPAKE:
+            status = psa_jpake_epilogue(operation, PSA_JPAKE_INPUT);
+            if (status != PSA_SUCCESS) {
+                goto exit;
+            }
+            break;
+#endif /* PSA_WANT_ALG_JPAKE */
+        default:
+            status = PSA_ERROR_NOT_SUPPORTED;
+            goto exit;
+    }
+
+exit:
+    LOCAL_INPUT_FREE(input_external, input);
+    if (status != PSA_SUCCESS) {
+        psa_pake_abort(operation);
+    }
+    return status;
+}
+
+psa_status_t psa_pake_get_implicit_key(
+    psa_pake_operation_t *operation,
+    psa_key_derivation_operation_t *output)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t abort_status = PSA_ERROR_CORRUPTION_DETECTED;
+    uint8_t shared_key[MBEDTLS_PSA_JPAKE_BUFFER_SIZE];
+    size_t shared_key_len = 0;
+
+    if (operation->stage != PSA_PAKE_OPERATION_STAGE_COMPUTATION) {
+        status = PSA_ERROR_BAD_STATE;
+        goto exit;
+    }
+
+#if defined(PSA_WANT_ALG_JPAKE)
+    if (operation->alg == PSA_ALG_JPAKE) {
+        psa_jpake_computation_stage_t *computation_stage =
+            &operation->computation_stage.jpake;
+        if (computation_stage->round != PSA_JPAKE_FINISHED) {
+            status = PSA_ERROR_BAD_STATE;
+            goto exit;
+        }
+    } else
+#endif /* PSA_WANT_ALG_JPAKE */
+    {
+        status = PSA_ERROR_NOT_SUPPORTED;
+        goto exit;
+    }
+
+    status = psa_driver_wrapper_pake_get_implicit_key(operation,
+                                                      shared_key,
+                                                      sizeof(shared_key),
+                                                      &shared_key_len);
+
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_key_derivation_input_bytes(output,
+                                            PSA_KEY_DERIVATION_INPUT_SECRET,
+                                            shared_key,
+                                            shared_key_len);
+
+    mbedtls_platform_zeroize(shared_key, sizeof(shared_key));
+exit:
+    abort_status = psa_pake_abort(operation);
+    return status == PSA_SUCCESS ? abort_status : status;
+}
+
+psa_status_t psa_pake_abort(
+    psa_pake_operation_t *operation)
+{
+    psa_status_t status = PSA_SUCCESS;
+
+    if (operation->stage == PSA_PAKE_OPERATION_STAGE_COMPUTATION) {
+        status = psa_driver_wrapper_pake_abort(operation);
+    }
+
+    if (operation->stage == PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) {
+        if (operation->data.inputs.password != NULL) {
+            mbedtls_zeroize_and_free(operation->data.inputs.password,
+                                     operation->data.inputs.password_len);
+        }
+        if (operation->data.inputs.user != NULL) {
+            mbedtls_free(operation->data.inputs.user);
+        }
+        if (operation->data.inputs.peer != NULL) {
+            mbedtls_free(operation->data.inputs.peer);
+        }
+    }
+    memset(operation, 0, sizeof(psa_pake_operation_t));
+
+    return status;
+}
+#endif /* PSA_WANT_ALG_SOME_PAKE */
+
+/* Memory copying test hooks. These are called before input copy, after input
+ * copy, before output copy and after output copy, respectively.
+ * They are used by memory-poisoning tests to temporarily unpoison buffers
+ * while they are copied. */
+#if defined(MBEDTLS_TEST_HOOKS)
+void (*psa_input_pre_copy_hook)(const uint8_t *input, size_t input_len) = NULL;
+void (*psa_input_post_copy_hook)(const uint8_t *input, size_t input_len) = NULL;
+void (*psa_output_pre_copy_hook)(const uint8_t *output, size_t output_len) = NULL;
+void (*psa_output_post_copy_hook)(const uint8_t *output, size_t output_len) = NULL;
+#endif
+
+/** Copy from an input buffer to a local copy.
+ *
+ * \param[in] input             Pointer to input buffer.
+ * \param[in] input_len         Length of the input buffer.
+ * \param[out] input_copy       Pointer to a local copy in which to store the input data.
+ * \param[out] input_copy_len   Length of the local copy buffer.
+ * \return                      #PSA_SUCCESS, if the buffer was successfully
+ *                              copied.
+ * \return                      #PSA_ERROR_CORRUPTION_DETECTED, if the local
+ *                              copy is too small to hold contents of the
+ *                              input buffer.
+ */
+MBEDTLS_STATIC_TESTABLE
+psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len,
+                                   uint8_t *input_copy, size_t input_copy_len)
+{
+    if (input_len > input_copy_len) {
+        return PSA_ERROR_CORRUPTION_DETECTED;
+    }
+
+#if defined(MBEDTLS_TEST_HOOKS)
+    if (psa_input_pre_copy_hook != NULL) {
+        psa_input_pre_copy_hook(input, input_len);
+    }
+#endif
+
+    if (input_len > 0) {
+        memcpy(input_copy, input, input_len);
+    }
+
+#if defined(MBEDTLS_TEST_HOOKS)
+    if (psa_input_post_copy_hook != NULL) {
+        psa_input_post_copy_hook(input, input_len);
+    }
+#endif
+
+    return PSA_SUCCESS;
+}
+
+/** Copy from a local output buffer into a user-supplied one.
+ *
+ * \param[in] output_copy       Pointer to a local buffer containing the output.
+ * \param[in] output_copy_len   Length of the local buffer.
+ * \param[out] output           Pointer to user-supplied output buffer.
+ * \param[out] output_len       Length of the user-supplied output buffer.
+ * \return                      #PSA_SUCCESS, if the buffer was successfully
+ *                              copied.
+ * \return                      #PSA_ERROR_BUFFER_TOO_SMALL, if the
+ *                              user-supplied output buffer is too small to
+ *                              hold the contents of the local buffer.
+ */
+MBEDTLS_STATIC_TESTABLE
+psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_copy_len,
+                                    uint8_t *output, size_t output_len)
+{
+    if (output_len < output_copy_len) {
+        return PSA_ERROR_BUFFER_TOO_SMALL;
+    }
+
+#if defined(MBEDTLS_TEST_HOOKS)
+    if (psa_output_pre_copy_hook != NULL) {
+        psa_output_pre_copy_hook(output, output_len);
+    }
+#endif
+
+    if (output_copy_len > 0) {
+        memcpy(output, output_copy, output_copy_len);
+    }
+
+#if defined(MBEDTLS_TEST_HOOKS)
+    if (psa_output_post_copy_hook != NULL) {
+        psa_output_post_copy_hook(output, output_len);
+    }
+#endif
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_local_input_alloc(const uint8_t *input, size_t input_len,
+                                          psa_crypto_local_input_t *local_input)
+{
+    psa_status_t status;
+
+    *local_input = PSA_CRYPTO_LOCAL_INPUT_INIT;
+
+    if (input_len == 0) {
+        return PSA_SUCCESS;
+    }
+
+    local_input->buffer = mbedtls_calloc(input_len, 1);
+    if (local_input->buffer == NULL) {
+        /* Since we dealt with the zero-length case above, we know that
+         * a NULL return value means a failure of allocation. */
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+    /* From now on, we must free local_input->buffer on error. */
+
+    local_input->length = input_len;
+
+    status = psa_crypto_copy_input(input, input_len,
+                                   local_input->buffer, local_input->length);
+    if (status != PSA_SUCCESS) {
+        goto error;
+    }
+
+    return PSA_SUCCESS;
+
+error:
+    mbedtls_free(local_input->buffer);
+    local_input->buffer = NULL;
+    local_input->length = 0;
+    return status;
+}
+
+void psa_crypto_local_input_free(psa_crypto_local_input_t *local_input)
+{
+    mbedtls_free(local_input->buffer);
+    local_input->buffer = NULL;
+    local_input->length = 0;
+}
+
+psa_status_t psa_crypto_local_output_alloc(uint8_t *output, size_t output_len,
+                                           psa_crypto_local_output_t *local_output)
+{
+    *local_output = PSA_CRYPTO_LOCAL_OUTPUT_INIT;
+
+    if (output_len == 0) {
+        return PSA_SUCCESS;
+    }
+    local_output->buffer = mbedtls_calloc(output_len, 1);
+    if (local_output->buffer == NULL) {
+        /* Since we dealt with the zero-length case above, we know that
+         * a NULL return value means a failure of allocation. */
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+    local_output->length = output_len;
+    local_output->original = output;
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_local_output_free(psa_crypto_local_output_t *local_output)
+{
+    psa_status_t status;
+
+    if (local_output->buffer == NULL) {
+        local_output->length = 0;
+        return PSA_SUCCESS;
+    }
+    if (local_output->original == NULL) {
+        /* We have an internal copy but nothing to copy back to. */
+        return PSA_ERROR_CORRUPTION_DETECTED;
+    }
+
+    status = psa_crypto_copy_output(local_output->buffer, local_output->length,
+                                    local_output->original, local_output->length);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    mbedtls_free(local_output->buffer);
+    local_output->buffer = NULL;
+    local_output->length = 0;
+
+    return PSA_SUCCESS;
+}
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/tf-psa-crypto/core/psa_crypto_aead.c b/tf-psa-crypto/core/psa_crypto_aead.c
new file mode 100644
index 0000000..a201985
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_aead.c
@@ -0,0 +1,649 @@
+/*
+ *  PSA AEAD entry points
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include "psa_crypto_aead.h"
+#include "psa_crypto_core.h"
+#include "psa_crypto_cipher.h"
+
+#include <string.h>
+#include "mbedtls/platform.h"
+
+#include "mbedtls/ccm.h"
+#include "mbedtls/chachapoly.h"
+#include "mbedtls/cipher.h"
+#include "mbedtls/gcm.h"
+#include "mbedtls/error.h"
+
+static psa_status_t psa_aead_setup(
+    mbedtls_psa_aead_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_cipher_id_t cipher_id;
+    mbedtls_cipher_mode_t mode;
+    size_t key_bits = attributes->bits;
+    (void) key_buffer_size;
+
+    status = mbedtls_cipher_values_from_psa(alg, attributes->type,
+                                            &key_bits, &mode, &cipher_id);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    switch (PSA_ALG_AEAD_WITH_SHORTENED_TAG(alg, 0)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+        case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, 0):
+            operation->alg = PSA_ALG_CCM;
+            /* CCM allows the following tag lengths: 4, 6, 8, 10, 12, 14, 16.
+             * The call to mbedtls_ccm_encrypt_and_tag or
+             * mbedtls_ccm_auth_decrypt will validate the tag length. */
+            if (PSA_BLOCK_CIPHER_BLOCK_LENGTH(attributes->type) != 16) {
+                return PSA_ERROR_INVALID_ARGUMENT;
+            }
+
+            mbedtls_ccm_init(&operation->ctx.ccm);
+            status = mbedtls_to_psa_error(
+                mbedtls_ccm_setkey(&operation->ctx.ccm, cipher_id,
+                                   key_buffer, (unsigned int) key_bits));
+            if (status != PSA_SUCCESS) {
+                return status;
+            }
+            break;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+        case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 0):
+            operation->alg = PSA_ALG_GCM;
+            /* GCM allows the following tag lengths: 4, 8, 12, 13, 14, 15, 16.
+             * The call to mbedtls_gcm_crypt_and_tag or
+             * mbedtls_gcm_auth_decrypt will validate the tag length. */
+            if (PSA_BLOCK_CIPHER_BLOCK_LENGTH(attributes->type) != 16) {
+                return PSA_ERROR_INVALID_ARGUMENT;
+            }
+
+            mbedtls_gcm_init(&operation->ctx.gcm);
+            status = mbedtls_to_psa_error(
+                mbedtls_gcm_setkey(&operation->ctx.gcm, cipher_id,
+                                   key_buffer, (unsigned int) key_bits));
+            if (status != PSA_SUCCESS) {
+                return status;
+            }
+            break;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+        case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305, 0):
+            operation->alg = PSA_ALG_CHACHA20_POLY1305;
+            /* We only support the default tag length. */
+            if (alg != PSA_ALG_CHACHA20_POLY1305) {
+                return PSA_ERROR_NOT_SUPPORTED;
+            }
+
+            mbedtls_chachapoly_init(&operation->ctx.chachapoly);
+            status = mbedtls_to_psa_error(
+                mbedtls_chachapoly_setkey(&operation->ctx.chachapoly,
+                                          key_buffer));
+            if (status != PSA_SUCCESS) {
+                return status;
+            }
+            break;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+
+        default:
+            (void) status;
+            (void) key_buffer;
+            return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    operation->key_type = psa_get_key_type(attributes);
+
+    operation->tag_length = PSA_ALG_AEAD_GET_TAG_LENGTH(alg);
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_aead_encrypt(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg,
+    const uint8_t *nonce, size_t nonce_length,
+    const uint8_t *additional_data, size_t additional_data_length,
+    const uint8_t *plaintext, size_t plaintext_length,
+    uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_psa_aead_operation_t operation = MBEDTLS_PSA_AEAD_OPERATION_INIT;
+    uint8_t *tag;
+
+    status = psa_aead_setup(&operation, attributes, key_buffer,
+                            key_buffer_size, alg);
+
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    /* For all currently supported modes, the tag is at the end of the
+     * ciphertext. */
+    if (ciphertext_size < (plaintext_length + operation.tag_length)) {
+        status = PSA_ERROR_BUFFER_TOO_SMALL;
+        goto exit;
+    }
+    tag = ciphertext + plaintext_length;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+    if (operation.alg == PSA_ALG_CCM) {
+        status = mbedtls_to_psa_error(
+            mbedtls_ccm_encrypt_and_tag(&operation.ctx.ccm,
+                                        plaintext_length,
+                                        nonce, nonce_length,
+                                        additional_data,
+                                        additional_data_length,
+                                        plaintext, ciphertext,
+                                        tag, operation.tag_length));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+    if (operation.alg == PSA_ALG_GCM) {
+        status = mbedtls_to_psa_error(
+            mbedtls_gcm_crypt_and_tag(&operation.ctx.gcm,
+                                      MBEDTLS_GCM_ENCRYPT,
+                                      plaintext_length,
+                                      nonce, nonce_length,
+                                      additional_data, additional_data_length,
+                                      plaintext, ciphertext,
+                                      operation.tag_length, tag));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+    if (operation.alg == PSA_ALG_CHACHA20_POLY1305) {
+        if (operation.tag_length != 16) {
+            status = PSA_ERROR_NOT_SUPPORTED;
+            goto exit;
+        }
+        status = mbedtls_to_psa_error(
+            mbedtls_chachapoly_encrypt_and_tag(&operation.ctx.chachapoly,
+                                               plaintext_length,
+                                               nonce,
+                                               additional_data,
+                                               additional_data_length,
+                                               plaintext,
+                                               ciphertext,
+                                               tag));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+    {
+        (void) tag;
+        (void) nonce;
+        (void) nonce_length;
+        (void) additional_data;
+        (void) additional_data_length;
+        (void) plaintext;
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    if (status == PSA_SUCCESS) {
+        *ciphertext_length = plaintext_length + operation.tag_length;
+    }
+
+exit:
+    mbedtls_psa_aead_abort(&operation);
+
+    return status;
+}
+
+/* Locate the tag in a ciphertext buffer containing the encrypted data
+ * followed by the tag. Return the length of the part preceding the tag in
+ * *plaintext_length. This is the size of the plaintext in modes where
+ * the encrypted data has the same size as the plaintext, such as
+ * CCM and GCM. */
+static psa_status_t psa_aead_unpadded_locate_tag(size_t tag_length,
+                                                 const uint8_t *ciphertext,
+                                                 size_t ciphertext_length,
+                                                 size_t plaintext_size,
+                                                 const uint8_t **p_tag)
+{
+    size_t payload_length;
+    if (tag_length > ciphertext_length) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+    payload_length = ciphertext_length - tag_length;
+    if (payload_length > plaintext_size) {
+        return PSA_ERROR_BUFFER_TOO_SMALL;
+    }
+    *p_tag = ciphertext + payload_length;
+    return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_aead_decrypt(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg,
+    const uint8_t *nonce, size_t nonce_length,
+    const uint8_t *additional_data, size_t additional_data_length,
+    const uint8_t *ciphertext, size_t ciphertext_length,
+    uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_psa_aead_operation_t operation = MBEDTLS_PSA_AEAD_OPERATION_INIT;
+    const uint8_t *tag = NULL;
+
+    status = psa_aead_setup(&operation, attributes, key_buffer,
+                            key_buffer_size, alg);
+
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_aead_unpadded_locate_tag(operation.tag_length,
+                                          ciphertext, ciphertext_length,
+                                          plaintext_size, &tag);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+    if (operation.alg == PSA_ALG_CCM) {
+        status = mbedtls_to_psa_error(
+            mbedtls_ccm_auth_decrypt(&operation.ctx.ccm,
+                                     ciphertext_length - operation.tag_length,
+                                     nonce, nonce_length,
+                                     additional_data,
+                                     additional_data_length,
+                                     ciphertext, plaintext,
+                                     tag, operation.tag_length));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+    if (operation.alg == PSA_ALG_GCM) {
+        status = mbedtls_to_psa_error(
+            mbedtls_gcm_auth_decrypt(&operation.ctx.gcm,
+                                     ciphertext_length - operation.tag_length,
+                                     nonce, nonce_length,
+                                     additional_data,
+                                     additional_data_length,
+                                     tag, operation.tag_length,
+                                     ciphertext, plaintext));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+    if (operation.alg == PSA_ALG_CHACHA20_POLY1305) {
+        if (operation.tag_length != 16) {
+            status = PSA_ERROR_NOT_SUPPORTED;
+            goto exit;
+        }
+        status = mbedtls_to_psa_error(
+            mbedtls_chachapoly_auth_decrypt(&operation.ctx.chachapoly,
+                                            ciphertext_length - operation.tag_length,
+                                            nonce,
+                                            additional_data,
+                                            additional_data_length,
+                                            tag,
+                                            ciphertext,
+                                            plaintext));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+    {
+        (void) nonce;
+        (void) nonce_length;
+        (void) additional_data;
+        (void) additional_data_length;
+        (void) plaintext;
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    if (status == PSA_SUCCESS) {
+        *plaintext_length = ciphertext_length - operation.tag_length;
+    }
+
+exit:
+    mbedtls_psa_aead_abort(&operation);
+
+    if (status == PSA_SUCCESS) {
+        *plaintext_length = ciphertext_length - operation.tag_length;
+    }
+    return status;
+}
+
+/* Set the key and algorithm for a multipart authenticated encryption
+ * operation. */
+psa_status_t mbedtls_psa_aead_encrypt_setup(
+    mbedtls_psa_aead_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    status = psa_aead_setup(operation, attributes, key_buffer,
+                            key_buffer_size, alg);
+
+    if (status == PSA_SUCCESS) {
+        operation->is_encrypt = 1;
+    }
+
+    return status;
+}
+
+/* Set the key and algorithm for a multipart authenticated decryption
+ * operation. */
+psa_status_t mbedtls_psa_aead_decrypt_setup(
+    mbedtls_psa_aead_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    status = psa_aead_setup(operation, attributes, key_buffer,
+                            key_buffer_size, alg);
+
+    if (status == PSA_SUCCESS) {
+        operation->is_encrypt = 0;
+    }
+
+    return status;
+}
+
+/* Set a nonce for the multipart AEAD operation*/
+psa_status_t mbedtls_psa_aead_set_nonce(
+    mbedtls_psa_aead_operation_t *operation,
+    const uint8_t *nonce,
+    size_t nonce_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+    if (operation->alg == PSA_ALG_GCM) {
+        status = mbedtls_to_psa_error(
+            mbedtls_gcm_starts(&operation->ctx.gcm,
+                               operation->is_encrypt ?
+                               MBEDTLS_GCM_ENCRYPT : MBEDTLS_GCM_DECRYPT,
+                               nonce,
+                               nonce_length));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+    if (operation->alg == PSA_ALG_CCM) {
+        status = mbedtls_to_psa_error(
+            mbedtls_ccm_starts(&operation->ctx.ccm,
+                               operation->is_encrypt ?
+                               MBEDTLS_CCM_ENCRYPT : MBEDTLS_CCM_DECRYPT,
+                               nonce,
+                               nonce_length));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+    if (operation->alg == PSA_ALG_CHACHA20_POLY1305) {
+        /* Note - ChaChaPoly allows an 8 byte nonce, but we would have to
+         * allocate a buffer in the operation, copy the nonce to it and pad
+         * it, so for now check the nonce is 12 bytes, as
+         * mbedtls_chachapoly_starts() assumes it can read 12 bytes from the
+         * passed in buffer. */
+        if (nonce_length != 12) {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+
+        status = mbedtls_to_psa_error(
+            mbedtls_chachapoly_starts(&operation->ctx.chachapoly,
+                                      nonce,
+                                      operation->is_encrypt ?
+                                      MBEDTLS_CHACHAPOLY_ENCRYPT :
+                                      MBEDTLS_CHACHAPOLY_DECRYPT));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+    {
+        (void) operation;
+        (void) nonce;
+        (void) nonce_length;
+
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    return status;
+}
+
+/* Declare the lengths of the message and additional data for AEAD. */
+psa_status_t mbedtls_psa_aead_set_lengths(
+    mbedtls_psa_aead_operation_t *operation,
+    size_t ad_length,
+    size_t plaintext_length)
+{
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+    if (operation->alg == PSA_ALG_CCM) {
+        return mbedtls_to_psa_error(
+            mbedtls_ccm_set_lengths(&operation->ctx.ccm,
+                                    ad_length,
+                                    plaintext_length,
+                                    operation->tag_length));
+
+    }
+#else /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+    (void) operation;
+    (void) ad_length;
+    (void) plaintext_length;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+
+    return PSA_SUCCESS;
+}
+
+/* Pass additional data to an active multipart AEAD operation. */
+psa_status_t mbedtls_psa_aead_update_ad(
+    mbedtls_psa_aead_operation_t *operation,
+    const uint8_t *input,
+    size_t input_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+    if (operation->alg == PSA_ALG_GCM) {
+        status = mbedtls_to_psa_error(
+            mbedtls_gcm_update_ad(&operation->ctx.gcm, input, input_length));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+    if (operation->alg == PSA_ALG_CCM) {
+        status = mbedtls_to_psa_error(
+            mbedtls_ccm_update_ad(&operation->ctx.ccm, input, input_length));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+    if (operation->alg == PSA_ALG_CHACHA20_POLY1305) {
+        status = mbedtls_to_psa_error(
+            mbedtls_chachapoly_update_aad(&operation->ctx.chachapoly,
+                                          input,
+                                          input_length));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+    {
+        (void) operation;
+        (void) input;
+        (void) input_length;
+
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    return status;
+}
+
+/* Encrypt or decrypt a message fragment in an active multipart AEAD
+ * operation.*/
+psa_status_t mbedtls_psa_aead_update(
+    mbedtls_psa_aead_operation_t *operation,
+    const uint8_t *input,
+    size_t input_length,
+    uint8_t *output,
+    size_t output_size,
+    size_t *output_length)
+{
+    size_t update_output_length;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    update_output_length = input_length;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+    if (operation->alg == PSA_ALG_GCM) {
+        status =  mbedtls_to_psa_error(
+            mbedtls_gcm_update(&operation->ctx.gcm,
+                               input, input_length,
+                               output, output_size,
+                               &update_output_length));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+    if (operation->alg == PSA_ALG_CCM) {
+        if (output_size < input_length) {
+            return PSA_ERROR_BUFFER_TOO_SMALL;
+        }
+
+        status = mbedtls_to_psa_error(
+            mbedtls_ccm_update(&operation->ctx.ccm,
+                               input, input_length,
+                               output, output_size,
+                               &update_output_length));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+    if (operation->alg == PSA_ALG_CHACHA20_POLY1305) {
+        if (output_size < input_length) {
+            return PSA_ERROR_BUFFER_TOO_SMALL;
+        }
+
+        status = mbedtls_to_psa_error(
+            mbedtls_chachapoly_update(&operation->ctx.chachapoly,
+                                      input_length,
+                                      input,
+                                      output));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+    {
+        (void) operation;
+        (void) input;
+        (void) output;
+        (void) output_size;
+
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    if (status == PSA_SUCCESS) {
+        *output_length = update_output_length;
+    }
+
+    return status;
+}
+
+/* Finish encrypting a message in a multipart AEAD operation. */
+psa_status_t mbedtls_psa_aead_finish(
+    mbedtls_psa_aead_operation_t *operation,
+    uint8_t *ciphertext,
+    size_t ciphertext_size,
+    size_t *ciphertext_length,
+    uint8_t *tag,
+    size_t tag_size,
+    size_t *tag_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    size_t finish_output_size = 0;
+
+    if (tag_size < operation->tag_length) {
+        return PSA_ERROR_BUFFER_TOO_SMALL;
+    }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+    if (operation->alg == PSA_ALG_GCM) {
+        status =  mbedtls_to_psa_error(
+            mbedtls_gcm_finish(&operation->ctx.gcm,
+                               ciphertext, ciphertext_size, ciphertext_length,
+                               tag, operation->tag_length));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+    if (operation->alg == PSA_ALG_CCM) {
+        /* tag must be big enough to store a tag of size passed into set
+         * lengths. */
+        if (tag_size < operation->tag_length) {
+            return PSA_ERROR_BUFFER_TOO_SMALL;
+        }
+
+        status = mbedtls_to_psa_error(
+            mbedtls_ccm_finish(&operation->ctx.ccm,
+                               tag, operation->tag_length));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+    if (operation->alg == PSA_ALG_CHACHA20_POLY1305) {
+        /* Belt and braces. Although the above tag_size check should have
+         * already done this, if we later start supporting smaller tag sizes
+         * for chachapoly, then passing a tag buffer smaller than 16 into here
+         * could cause a buffer overflow, so better safe than sorry. */
+        if (tag_size < 16) {
+            return PSA_ERROR_BUFFER_TOO_SMALL;
+        }
+
+        status = mbedtls_to_psa_error(
+            mbedtls_chachapoly_finish(&operation->ctx.chachapoly,
+                                      tag));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+    {
+        (void) ciphertext;
+        (void) ciphertext_size;
+        (void) ciphertext_length;
+        (void) tag;
+        (void) tag_size;
+        (void) tag_length;
+
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    if (status == PSA_SUCCESS) {
+        /* This will be zero for all supported algorithms currently, but left
+         * here for future support. */
+        *ciphertext_length = finish_output_size;
+        *tag_length = operation->tag_length;
+    }
+
+    return status;
+}
+
+/* Abort an AEAD operation */
+psa_status_t mbedtls_psa_aead_abort(
+    mbedtls_psa_aead_operation_t *operation)
+{
+    switch (operation->alg) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+        case PSA_ALG_CCM:
+            mbedtls_ccm_free(&operation->ctx.ccm);
+            break;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+        case PSA_ALG_GCM:
+            mbedtls_gcm_free(&operation->ctx.gcm);
+            break;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+        case PSA_ALG_CHACHA20_POLY1305:
+            mbedtls_chachapoly_free(&operation->ctx.chachapoly);
+            break;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+    }
+
+    operation->is_encrypt = 0;
+
+    return PSA_SUCCESS;
+}
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/tf-psa-crypto/core/psa_crypto_aead.h b/tf-psa-crypto/core/psa_crypto_aead.h
new file mode 100644
index 0000000..a339219
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_aead.h
@@ -0,0 +1,499 @@
+/*
+ *  PSA AEAD driver entry points
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_AEAD_H
+#define PSA_CRYPTO_AEAD_H
+
+#include <psa/crypto.h>
+
+/**
+ * \brief Process an authenticated encryption operation.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       aead_encrypt entry point. This function behaves as an aead_encrypt
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in]  attributes         The attributes of the key to use for the
+ *                                operation.
+ * \param[in]  key_buffer         The buffer containing the key context.
+ * \param      key_buffer_size    Size of the \p key_buffer buffer in bytes.
+ * \param      alg                The AEAD algorithm to compute.
+ * \param[in]  nonce              Nonce or IV to use.
+ * \param      nonce_length       Size of the nonce buffer in bytes. This must
+ *                                be appropriate for the selected algorithm.
+ *                                The default nonce size is
+ *                                PSA_AEAD_NONCE_LENGTH(key_type, alg) where
+ *                                key_type is the type of key.
+ * \param[in]  additional_data    Additional data that will be authenticated
+ *                                but not encrypted.
+ * \param      additional_data_length  Size of additional_data in bytes.
+ * \param[in]  plaintext          Data that will be authenticated and encrypted.
+ * \param      plaintext_length   Size of plaintext in bytes.
+ * \param[out] ciphertext         Output buffer for the authenticated and
+ *                                encrypted data. The additional data is not
+ *                                part of this output. For algorithms where the
+ *                                encrypted data and the authentication tag are
+ *                                defined as separate outputs, the
+ *                                authentication tag is appended to the
+ *                                encrypted data.
+ * \param      ciphertext_size    Size of the ciphertext buffer in bytes. This
+ *                                must be appropriate for the selected algorithm
+ *                                and key:
+ *                                - A sufficient output size is
+ *                                  PSA_AEAD_ENCRYPT_OUTPUT_SIZE(key_type, alg,
+ *                                  plaintext_length) where key_type is the type
+ *                                  of key.
+ *                                - PSA_AEAD_ENCRYPT_OUTPUT_MAX_SIZE(
+ *                                  plaintext_length) evaluates to the maximum
+ *                                  ciphertext size of any supported AEAD
+ *                                  encryption.
+ * \param[out] ciphertext_length  On success, the size of the output in the
+ *                                ciphertext buffer.
+ *
+ * \retval #PSA_SUCCESS Success.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         \p alg is not supported.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         ciphertext_size is too small.
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_aead_encrypt(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg,
+    const uint8_t *nonce, size_t nonce_length,
+    const uint8_t *additional_data, size_t additional_data_length,
+    const uint8_t *plaintext, size_t plaintext_length,
+    uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_length);
+
+/**
+ * \brief Process an authenticated decryption operation.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       aead_decrypt entry point. This function behaves as an aead_decrypt
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in]  attributes         The attributes of the key to use for the
+ *                                operation.
+ * \param[in]  key_buffer         The buffer containing the key context.
+ * \param      key_buffer_size    Size of the \p key_buffer buffer in bytes.
+ * \param      alg                The AEAD algorithm to compute.
+ * \param[in]  nonce              Nonce or IV to use.
+ * \param      nonce_length       Size of the nonce buffer in bytes. This must
+ *                                be appropriate for the selected algorithm.
+ *                                The default nonce size is
+ *                                PSA_AEAD_NONCE_LENGTH(key_type, alg) where
+ *                                key_type is the type of key.
+ * \param[in]  additional_data    Additional data that has been authenticated
+ *                                but not encrypted.
+ * \param      additional_data_length  Size of additional_data in bytes.
+ * \param[in]  ciphertext         Data that has been authenticated and
+ *                                encrypted. For algorithms where the encrypted
+ *                                data and the authentication tag are defined
+ *                                as separate inputs, the buffer contains
+ *                                encrypted data followed by the authentication
+ *                                tag.
+ * \param      ciphertext_length  Size of ciphertext in bytes.
+ * \param[out] plaintext          Output buffer for the decrypted data.
+ * \param      plaintext_size     Size of the plaintext buffer in bytes. This
+ *                                must be appropriate for the selected algorithm
+ *                                and key:
+ *                                - A sufficient output size is
+ *                                  PSA_AEAD_DECRYPT_OUTPUT_SIZE(key_type, alg,
+ *                                  ciphertext_length) where key_type is the
+ *                                  type of key.
+ *                                - PSA_AEAD_DECRYPT_OUTPUT_MAX_SIZE(
+ *                                  ciphertext_length) evaluates to the maximum
+ *                                  plaintext size of any supported AEAD
+ *                                  decryption.
+ * \param[out] plaintext_length   On success, the size of the output in the
+ *                                plaintext buffer.
+ *
+ * \retval #PSA_SUCCESS Success.
+ * \retval #PSA_ERROR_INVALID_SIGNATURE
+ *         The cipher is not authentic.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         \p alg is not supported.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         plaintext_size is too small.
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_aead_decrypt(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg,
+    const uint8_t *nonce, size_t nonce_length,
+    const uint8_t *additional_data, size_t additional_data_length,
+    const uint8_t *ciphertext, size_t ciphertext_length,
+    uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_length);
+
+/** Set the key for a multipart authenticated encryption operation.
+ *
+ *  \note The signature of this function is that of a PSA driver
+ *       aead_encrypt_setup entry point. This function behaves as an
+ *       aead_encrypt_setup entry point as defined in the PSA driver interface
+ *       specification for transparent drivers.
+ *
+ * If an error occurs at any step after a call to
+ * mbedtls_psa_aead_encrypt_setup(), the operation is reset by the PSA core by a
+ * call to mbedtls_psa_aead_abort(). The PSA core may call
+ * mbedtls_psa_aead_abort() at any time after the operation has been
+ * initialized, and is required to when the operation is no longer needed.
+ *
+ * \param[in,out] operation     The operation object to set up. It must have
+ *                              been initialized as per the documentation for
+ *                              #mbedtls_psa_aead_operation_t and not yet in
+ *                              use.
+ * \param[in]  attributes       The attributes of the key to use for the
+ *                              operation.
+ * \param[in]  key_buffer       The buffer containing the key context.
+ * \param      key_buffer_size  Size of the \p key_buffer buffer in bytes.
+                                It must be consistent with the size in bits
+                                recorded in \p attributes.
+ * \param alg                   The AEAD algorithm to compute
+ *                              (\c PSA_ALG_XXX value such that
+ *                              #PSA_ALG_IS_AEAD(\p alg) is true).
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         An invalid block length was supplied.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         \p alg is not supported.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
+ *         Failed to allocate memory for key material
+ */
+psa_status_t mbedtls_psa_aead_encrypt_setup(
+    mbedtls_psa_aead_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg);
+
+/** Set the key for a multipart authenticated decryption operation.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       aead_decrypt_setup entry point. This function behaves as an
+ *       aead_decrypt_setup entry point as defined in the PSA driver interface
+ *       specification for transparent drivers.
+ *
+ * If an error occurs at any step after a call to
+ * mbedtls_psa_aead_decrypt_setup(), the PSA core resets the operation by a
+ * call to mbedtls_psa_aead_abort(). The PSA core may call
+ * mbedtls_psa_aead_abort() at any time after the operation has been
+ * initialized, and is required to when the operation is no longer needed.
+ *
+ * \param[in,out] operation     The operation object to set up. It must have
+ *                              been initialized as per the documentation for
+ *                              #mbedtls_psa_aead_operation_t and not yet in
+ *                              use.
+ * \param[in]  attributes       The attributes of the key to use for the
+ *                              operation.
+ * \param[in]  key_buffer       The buffer containing the key context.
+ * \param      key_buffer_size  Size of the \p key_buffer buffer in bytes.
+                                It must be consistent with the size in bits
+                                recorded in \p attributes.
+ * \param alg                   The AEAD algorithm to compute
+ *                              (\c PSA_ALG_XXX value such that
+ *                              #PSA_ALG_IS_AEAD(\p alg) is true).
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         An invalid block length was supplied.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         \p alg is not supported.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
+ *         Failed to allocate memory for key material
+ */
+psa_status_t mbedtls_psa_aead_decrypt_setup(
+    mbedtls_psa_aead_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg);
+
+/** Set the nonce for an authenticated encryption or decryption operation.
+ *
+ * \note The signature of this function is that of a PSA driver aead_set_nonce
+ *       entry point. This function behaves as an aead_set_nonce entry point as
+ *       defined in the PSA driver interface specification for transparent
+ *       drivers.
+ *
+ * This function sets the nonce for the authenticated
+ * encryption or decryption operation.
+ *
+ * The PSA core calls mbedtls_psa_aead_encrypt_setup() or
+ * mbedtls_psa_aead_decrypt_setup() before calling this function.
+ *
+ * If this function returns an error status, the PSA core will call
+ * mbedtls_psa_aead_abort().
+ *
+ * \param[in,out] operation     Active AEAD operation.
+ * \param[in] nonce             Buffer containing the nonce to use.
+ * \param nonce_length          Size of the nonce in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         The size of \p nonce is not acceptable for the chosen algorithm.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         Algorithm previously set is not supported in this configuration of
+ *         the library.
+ */
+psa_status_t mbedtls_psa_aead_set_nonce(
+    mbedtls_psa_aead_operation_t *operation,
+    const uint8_t *nonce,
+    size_t nonce_length);
+
+/** Declare the lengths of the message and additional data for AEAD.
+ *
+ * \note The signature of this function is that of a PSA driver aead_set_lengths
+ *       entry point. This function behaves as an aead_set_lengths entry point
+ *       as defined in the PSA driver interface specification for transparent
+ *       drivers.
+ *
+ * The PSA core calls this function before calling mbedtls_psa_aead_update_ad()
+ * or mbedtls_psa_aead_update() if the algorithm for the operation requires it.
+ * If the algorithm does not require it, calling this function is optional, but
+ * if this function is called then the implementation must enforce the lengths.
+ *
+ * The PSA core may call this function before or after setting the nonce with
+ * mbedtls_psa_aead_set_nonce().
+ *
+ * - For #PSA_ALG_CCM, calling this function is required.
+ * - For the other AEAD algorithms defined in this specification, calling
+ *   this function is not required.
+ *
+ * If this function returns an error status, the PSA core calls
+ * mbedtls_psa_aead_abort().
+ *
+ * \param[in,out] operation     Active AEAD operation.
+ * \param ad_length             Size of the non-encrypted additional
+ *                              authenticated data in bytes.
+ * \param plaintext_length      Size of the plaintext to encrypt in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         At least one of the lengths is not acceptable for the chosen
+ *         algorithm.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         Algorithm previously set is not supported in this configuration of
+ *         the library.
+ */
+psa_status_t mbedtls_psa_aead_set_lengths(
+    mbedtls_psa_aead_operation_t *operation,
+    size_t ad_length,
+    size_t plaintext_length);
+
+/** Pass additional data to an active AEAD operation.
+ *
+ *  \note The signature of this function is that of a PSA driver
+ *       aead_update_ad entry point. This function behaves as an aead_update_ad
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * Additional data is authenticated, but not encrypted.
+ *
+ * The PSA core can call this function multiple times to pass successive
+ * fragments of the additional data. It will not call this function after
+ * passing data to encrypt or decrypt with mbedtls_psa_aead_update().
+ *
+ * Before calling this function, the PSA core will:
+ *    1. Call either mbedtls_psa_aead_encrypt_setup() or
+ *       mbedtls_psa_aead_decrypt_setup().
+ *    2. Set the nonce with mbedtls_psa_aead_set_nonce().
+ *
+ * If this function returns an error status, the PSA core will call
+ * mbedtls_psa_aead_abort().
+ *
+ * \param[in,out] operation     Active AEAD operation.
+ * \param[in] input             Buffer containing the fragment of
+ *                              additional data.
+ * \param input_length          Size of the \p input buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         Algorithm previously set is not supported in this configuration of
+ *         the library.
+ */
+psa_status_t mbedtls_psa_aead_update_ad(
+    mbedtls_psa_aead_operation_t *operation,
+    const uint8_t *input,
+    size_t input_length);
+
+/** Encrypt or decrypt a message fragment in an active AEAD operation.
+ *
+ *  \note The signature of this function is that of a PSA driver
+ *       aead_update entry point. This function behaves as an aead_update entry
+ *       point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * Before calling this function, the PSA core will:
+ *    1. Call either mbedtls_psa_aead_encrypt_setup() or
+ *       mbedtls_psa_aead_decrypt_setup(). The choice of setup function
+ *       determines whether this function encrypts or decrypts its input.
+ *    2. Set the nonce with mbedtls_psa_aead_set_nonce().
+ *    3. Call mbedtls_psa_aead_update_ad() to pass all the additional data.
+ *
+ * If this function returns an error status, the PSA core will call
+ * mbedtls_psa_aead_abort().
+ *
+ * This function does not require the input to be aligned to any
+ * particular block boundary. If the implementation can only process
+ * a whole block at a time, it must consume all the input provided, but
+ * it may delay the end of the corresponding output until a subsequent
+ * call to mbedtls_psa_aead_update(), mbedtls_psa_aead_finish() provides
+ * sufficient input. The amount of data that can be delayed in this way is
+ * bounded by #PSA_AEAD_UPDATE_OUTPUT_SIZE.
+ *
+ * \param[in,out] operation     Active AEAD operation.
+ * \param[in] input             Buffer containing the message fragment to
+ *                              encrypt or decrypt.
+ * \param input_length          Size of the \p input buffer in bytes.
+ * \param[out] output           Buffer where the output is to be written.
+ * \param output_size           Size of the \p output buffer in bytes.
+ *                              This must be appropriate for the selected
+ *                                algorithm and key:
+ *                                - A sufficient output size is
+ *                                  #PSA_AEAD_UPDATE_OUTPUT_SIZE(\c key_type,
+ *                                  \c alg, \p input_length) where
+ *                                  \c key_type is the type of key and \c alg is
+ *                                  the algorithm that were used to set up the
+ *                                  operation.
+ *                                - #PSA_AEAD_UPDATE_OUTPUT_MAX_SIZE(\p
+ *                                  input_length) evaluates to the maximum
+ *                                  output size of any supported AEAD
+ *                                  algorithm.
+ * \param[out] output_length    On success, the number of bytes
+ *                              that make up the returned output.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ *
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p output buffer is too small.
+ *         #PSA_AEAD_UPDATE_OUTPUT_SIZE(\c key_type, \c alg, \p input_length) or
+ *         #PSA_AEAD_UPDATE_OUTPUT_MAX_SIZE(\p input_length) can be used to
+ *         determine the required buffer size.
+ */
+psa_status_t mbedtls_psa_aead_update(
+    mbedtls_psa_aead_operation_t *operation,
+    const uint8_t *input,
+    size_t input_length,
+    uint8_t *output,
+    size_t output_size,
+    size_t *output_length);
+
+/** Finish encrypting a message in an AEAD operation.
+ *
+ *  \note The signature of this function is that of a PSA driver
+ *       aead_finish entry point. This function behaves as an aead_finish entry
+ *       point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * The operation must have been set up by the PSA core with
+ * mbedtls_psa_aead_encrypt_setup().
+ *
+ * This function finishes the authentication of the additional data
+ * formed by concatenating the inputs passed to preceding calls to
+ * mbedtls_psa_aead_update_ad() with the plaintext formed by concatenating the
+ * inputs passed to preceding calls to mbedtls_psa_aead_update().
+ *
+ * This function has two output buffers:
+ * - \p ciphertext contains trailing ciphertext that was buffered from
+ *   preceding calls to mbedtls_psa_aead_update().
+ * - \p tag contains the authentication tag.
+ *
+ * Whether or not this function returns successfully, the PSA core subsequently
+ * calls mbedtls_psa_aead_abort() to deactivate the operation.
+ *
+ * \param[in,out] operation     Active AEAD operation.
+ * \param[out] ciphertext       Buffer where the last part of the ciphertext
+ *                              is to be written.
+ * \param ciphertext_size       Size of the \p ciphertext buffer in bytes.
+ *                              This must be appropriate for the selected
+ *                              algorithm and key:
+ *                              - A sufficient output size is
+ *                                #PSA_AEAD_FINISH_OUTPUT_SIZE(\c key_type,
+ *                                \c alg) where \c key_type is the type of key
+ *                                and \c alg is the algorithm that were used to
+ *                                set up the operation.
+ *                              - #PSA_AEAD_FINISH_OUTPUT_MAX_SIZE evaluates to
+ *                                the maximum output size of any supported AEAD
+ *                                algorithm.
+ * \param[out] ciphertext_length On success, the number of bytes of
+ *                              returned ciphertext.
+ * \param[out] tag              Buffer where the authentication tag is
+ *                              to be written.
+ * \param tag_size              Size of the \p tag buffer in bytes.
+ *                              This must be appropriate for the selected
+ *                              algorithm and key:
+ *                              - The exact tag size is #PSA_AEAD_TAG_LENGTH(\c
+ *                                key_type, \c key_bits, \c alg) where
+ *                                \c key_type and \c key_bits are the type and
+ *                                bit-size of the key, and \c alg are the
+ *                                algorithm that were used in the call to
+ *                                mbedtls_psa_aead_encrypt_setup().
+ *                              - #PSA_AEAD_TAG_MAX_SIZE evaluates to the
+ *                                maximum tag size of any supported AEAD
+ *                                algorithm.
+ * \param[out] tag_length       On success, the number of bytes
+ *                              that make up the returned tag.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p tag buffer is too small.
+ *         #PSA_AEAD_TAG_LENGTH(\c key_type, key_bits, \c alg) or
+ *         #PSA_AEAD_TAG_MAX_SIZE can be used to determine the required \p tag
+ *         buffer size.
+ */
+psa_status_t mbedtls_psa_aead_finish(
+    mbedtls_psa_aead_operation_t *operation,
+    uint8_t *ciphertext,
+    size_t ciphertext_size,
+    size_t *ciphertext_length,
+    uint8_t *tag,
+    size_t tag_size,
+    size_t *tag_length);
+
+/** Abort an AEAD operation.
+ *
+ *  \note The signature of this function is that of a PSA driver
+ *       aead_abort entry point. This function behaves as an aead_abort entry
+ *       point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * Aborting an operation frees all associated resources except for the
+ * \p operation structure itself. Once aborted, the operation object
+ * can be reused for another operation by the PSA core by it calling
+ * mbedtls_psa_aead_encrypt_setup() or mbedtls_psa_aead_decrypt_setup() again.
+ *
+ * The PSA core may call this function any time after the operation object has
+ * been initialized as described in #mbedtls_psa_aead_operation_t.
+ *
+ * In particular, calling mbedtls_psa_aead_abort() after the operation has been
+ * terminated by a call to mbedtls_psa_aead_abort() or
+ * mbedtls_psa_aead_finish() is safe and has no effect.
+ *
+ * \param[in,out] operation     Initialized AEAD operation.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ */
+psa_status_t mbedtls_psa_aead_abort(
+    mbedtls_psa_aead_operation_t *operation);
+
+#endif /* PSA_CRYPTO_AEAD_H */
diff --git a/tf-psa-crypto/core/psa_crypto_cipher.c b/tf-psa-crypto/core/psa_crypto_cipher.c
new file mode 100644
index 0000000..3216c94
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_cipher.c
@@ -0,0 +1,724 @@
+/*
+ *  PSA cipher driver entry points
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include "psa_crypto_cipher.h"
+#include "psa_crypto_core.h"
+#include "psa_crypto_random_impl.h"
+
+#include "mbedtls/cipher.h"
+#include "mbedtls/error.h"
+
+#include <string.h>
+
+/* mbedtls_cipher_values_from_psa() below only checks if the proper build symbols
+ * are enabled, but it does not provide any compatibility check between them
+ * (i.e. if the specified key works with the specified algorithm). This helper
+ * function is meant to provide this support.
+ * mbedtls_cipher_info_from_psa() might be used for the same purpose, but it
+ * requires CIPHER_C to be enabled.
+ */
+static psa_status_t mbedtls_cipher_validate_values(
+    psa_algorithm_t alg,
+    psa_key_type_t key_type)
+{
+    /* Reduce code size - hinting to the compiler about what it can assume allows the compiler to
+       eliminate bits of the logic below. */
+#if !defined(PSA_WANT_KEY_TYPE_AES)
+    MBEDTLS_ASSUME(key_type != PSA_KEY_TYPE_AES);
+#endif
+#if !defined(PSA_WANT_KEY_TYPE_ARIA)
+    MBEDTLS_ASSUME(key_type != PSA_KEY_TYPE_ARIA);
+#endif
+#if !defined(PSA_WANT_KEY_TYPE_CAMELLIA)
+    MBEDTLS_ASSUME(key_type != PSA_KEY_TYPE_CAMELLIA);
+#endif
+#if !defined(PSA_WANT_KEY_TYPE_CHACHA20)
+    MBEDTLS_ASSUME(key_type != PSA_KEY_TYPE_CHACHA20);
+#endif
+#if !defined(PSA_WANT_KEY_TYPE_DES)
+    MBEDTLS_ASSUME(key_type != PSA_KEY_TYPE_DES);
+#endif
+#if !defined(PSA_WANT_ALG_CCM)
+    MBEDTLS_ASSUME(alg != PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, 0));
+#endif
+#if !defined(PSA_WANT_ALG_GCM)
+    MBEDTLS_ASSUME(alg != PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 0));
+#endif
+#if !defined(PSA_WANT_ALG_STREAM_CIPHER)
+    MBEDTLS_ASSUME(alg != PSA_ALG_STREAM_CIPHER);
+#endif
+#if !defined(PSA_WANT_ALG_CHACHA20_POLY1305)
+    MBEDTLS_ASSUME(alg != PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305, 0));
+#endif
+#if !defined(PSA_WANT_ALG_CCM_STAR_NO_TAG)
+    MBEDTLS_ASSUME(alg != PSA_ALG_CCM_STAR_NO_TAG);
+#endif
+#if !defined(PSA_WANT_ALG_CTR)
+    MBEDTLS_ASSUME(alg != PSA_ALG_CTR);
+#endif
+#if !defined(PSA_WANT_ALG_CFB)
+    MBEDTLS_ASSUME(alg != PSA_ALG_CFB);
+#endif
+#if !defined(PSA_WANT_ALG_OFB)
+    MBEDTLS_ASSUME(alg != PSA_ALG_OFB);
+#endif
+#if !defined(PSA_WANT_ALG_XTS)
+    MBEDTLS_ASSUME(alg != PSA_ALG_XTS);
+#endif
+#if !defined(PSA_WANT_ALG_ECB_NO_PADDING)
+    MBEDTLS_ASSUME(alg != PSA_ALG_ECB_NO_PADDING);
+#endif
+#if !defined(PSA_WANT_ALG_CBC_NO_PADDING)
+    MBEDTLS_ASSUME(alg != PSA_ALG_CBC_NO_PADDING);
+#endif
+#if !defined(PSA_WANT_ALG_CBC_PKCS7)
+    MBEDTLS_ASSUME(alg != PSA_ALG_CBC_PKCS7);
+#endif
+#if !defined(PSA_WANT_ALG_CMAC)
+    MBEDTLS_ASSUME(alg != PSA_ALG_CMAC);
+#endif
+
+    if (alg == PSA_ALG_STREAM_CIPHER ||
+        alg == PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305, 0)) {
+        if (key_type == PSA_KEY_TYPE_CHACHA20) {
+            return PSA_SUCCESS;
+        }
+    }
+
+    if (alg == PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, 0) ||
+        alg == PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 0) ||
+        alg == PSA_ALG_CCM_STAR_NO_TAG) {
+        if (key_type == PSA_KEY_TYPE_AES ||
+            key_type == PSA_KEY_TYPE_ARIA ||
+            key_type == PSA_KEY_TYPE_CAMELLIA) {
+            return PSA_SUCCESS;
+        }
+    }
+
+    if (alg == PSA_ALG_CTR ||
+        alg == PSA_ALG_CFB ||
+        alg == PSA_ALG_OFB ||
+        alg == PSA_ALG_XTS ||
+        alg == PSA_ALG_ECB_NO_PADDING ||
+        alg == PSA_ALG_CBC_NO_PADDING ||
+        alg == PSA_ALG_CBC_PKCS7 ||
+        alg == PSA_ALG_CMAC) {
+        if (key_type == PSA_KEY_TYPE_AES ||
+            key_type == PSA_KEY_TYPE_ARIA ||
+            key_type == PSA_KEY_TYPE_DES ||
+            key_type == PSA_KEY_TYPE_CAMELLIA) {
+            return PSA_SUCCESS;
+        }
+    }
+
+    return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t mbedtls_cipher_values_from_psa(
+    psa_algorithm_t alg,
+    psa_key_type_t key_type,
+    size_t *key_bits,
+    mbedtls_cipher_mode_t *mode,
+    mbedtls_cipher_id_t *cipher_id)
+{
+    mbedtls_cipher_id_t cipher_id_tmp;
+    /* Only DES modifies key_bits */
+#if !defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES)
+    (void) key_bits;
+#endif
+
+    if (PSA_ALG_IS_AEAD(alg)) {
+        alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(alg, 0);
+    }
+
+    if (PSA_ALG_IS_CIPHER(alg) || PSA_ALG_IS_AEAD(alg)) {
+        switch (alg) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_STREAM_CIPHER)
+            case PSA_ALG_STREAM_CIPHER:
+                *mode = MBEDTLS_MODE_STREAM;
+                break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CTR)
+            case PSA_ALG_CTR:
+                *mode = MBEDTLS_MODE_CTR;
+                break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CFB)
+            case PSA_ALG_CFB:
+                *mode = MBEDTLS_MODE_CFB;
+                break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_OFB)
+            case PSA_ALG_OFB:
+                *mode = MBEDTLS_MODE_OFB;
+                break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING)
+            case PSA_ALG_ECB_NO_PADDING:
+                *mode = MBEDTLS_MODE_ECB;
+                break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING)
+            case PSA_ALG_CBC_NO_PADDING:
+                *mode = MBEDTLS_MODE_CBC;
+                break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7)
+            case PSA_ALG_CBC_PKCS7:
+                *mode = MBEDTLS_MODE_CBC;
+                break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM_STAR_NO_TAG)
+            case PSA_ALG_CCM_STAR_NO_TAG:
+                *mode = MBEDTLS_MODE_CCM_STAR_NO_TAG;
+                break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+            case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, 0):
+                *mode = MBEDTLS_MODE_CCM;
+                break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+            case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 0):
+                *mode = MBEDTLS_MODE_GCM;
+                break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+            case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305, 0):
+                *mode = MBEDTLS_MODE_CHACHAPOLY;
+                break;
+#endif
+            default:
+                return PSA_ERROR_NOT_SUPPORTED;
+        }
+    } else if (alg == PSA_ALG_CMAC) {
+        *mode = MBEDTLS_MODE_ECB;
+    } else {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    switch (key_type) {
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_AES)
+        case PSA_KEY_TYPE_AES:
+            cipher_id_tmp = MBEDTLS_CIPHER_ID_AES;
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ARIA)
+        case PSA_KEY_TYPE_ARIA:
+            cipher_id_tmp = MBEDTLS_CIPHER_ID_ARIA;
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES)
+        case PSA_KEY_TYPE_DES:
+            /* key_bits is 64 for Single-DES, 128 for two-key Triple-DES,
+             * and 192 for three-key Triple-DES. */
+            if (*key_bits == 64) {
+                cipher_id_tmp = MBEDTLS_CIPHER_ID_DES;
+            } else {
+                cipher_id_tmp = MBEDTLS_CIPHER_ID_3DES;
+            }
+            /* mbedtls doesn't recognize two-key Triple-DES as an algorithm,
+             * but two-key Triple-DES is functionally three-key Triple-DES
+             * with K1=K3, so that's how we present it to mbedtls. */
+            if (*key_bits == 128) {
+                *key_bits = 192;
+            }
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_CAMELLIA)
+        case PSA_KEY_TYPE_CAMELLIA:
+            cipher_id_tmp = MBEDTLS_CIPHER_ID_CAMELLIA;
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_CHACHA20)
+        case PSA_KEY_TYPE_CHACHA20:
+            cipher_id_tmp = MBEDTLS_CIPHER_ID_CHACHA20;
+            break;
+#endif
+        default:
+            return PSA_ERROR_NOT_SUPPORTED;
+    }
+    if (cipher_id != NULL) {
+        *cipher_id = cipher_id_tmp;
+    }
+
+    return mbedtls_cipher_validate_values(alg, key_type);
+}
+
+#if defined(MBEDTLS_CIPHER_C)
+const mbedtls_cipher_info_t *mbedtls_cipher_info_from_psa(
+    psa_algorithm_t alg,
+    psa_key_type_t key_type,
+    size_t key_bits,
+    mbedtls_cipher_id_t *cipher_id)
+{
+    mbedtls_cipher_mode_t mode;
+    psa_status_t status;
+    mbedtls_cipher_id_t cipher_id_tmp = MBEDTLS_CIPHER_ID_NONE;
+
+    status = mbedtls_cipher_values_from_psa(alg, key_type, &key_bits, &mode, &cipher_id_tmp);
+    if (status != PSA_SUCCESS) {
+        return NULL;
+    }
+    if (cipher_id != NULL) {
+        *cipher_id = cipher_id_tmp;
+    }
+
+    return mbedtls_cipher_info_from_values(cipher_id_tmp, (int) key_bits, mode);
+}
+#endif /* MBEDTLS_CIPHER_C */
+
+#if defined(MBEDTLS_PSA_BUILTIN_CIPHER)
+
+static psa_status_t psa_cipher_setup(
+    mbedtls_psa_cipher_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg,
+    mbedtls_operation_t cipher_operation)
+{
+    int ret = 0;
+    size_t key_bits;
+    const mbedtls_cipher_info_t *cipher_info = NULL;
+    psa_key_type_t key_type = attributes->type;
+
+    (void) key_buffer_size;
+
+    mbedtls_cipher_init(&operation->ctx.cipher);
+
+    operation->alg = alg;
+    key_bits = attributes->bits;
+    cipher_info = mbedtls_cipher_info_from_psa(alg, key_type,
+                                               key_bits, NULL);
+    if (cipher_info == NULL) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    ret = mbedtls_cipher_setup(&operation->ctx.cipher, cipher_info);
+    if (ret != 0) {
+        goto exit;
+    }
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES)
+    if (key_type == PSA_KEY_TYPE_DES && key_bits == 128) {
+        /* Two-key Triple-DES is 3-key Triple-DES with K1=K3 */
+        uint8_t keys[24];
+        memcpy(keys, key_buffer, 16);
+        memcpy(keys + 16, key_buffer, 8);
+        ret = mbedtls_cipher_setkey(&operation->ctx.cipher,
+                                    keys,
+                                    192, cipher_operation);
+    } else
+#endif
+    {
+        ret = mbedtls_cipher_setkey(&operation->ctx.cipher, key_buffer,
+                                    (int) key_bits, cipher_operation);
+    }
+    if (ret != 0) {
+        goto exit;
+    }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7)
+    switch (alg) {
+        case PSA_ALG_CBC_NO_PADDING:
+            ret = mbedtls_cipher_set_padding_mode(&operation->ctx.cipher,
+                                                  MBEDTLS_PADDING_NONE);
+            break;
+        case PSA_ALG_CBC_PKCS7:
+            ret = mbedtls_cipher_set_padding_mode(&operation->ctx.cipher,
+                                                  MBEDTLS_PADDING_PKCS7);
+            break;
+        default:
+            /* The algorithm doesn't involve padding. */
+            ret = 0;
+            break;
+    }
+    if (ret != 0) {
+        goto exit;
+    }
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING ||
+          MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7 */
+
+    operation->block_length = (PSA_ALG_IS_STREAM_CIPHER(alg) ? 1 :
+                               PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type));
+    operation->iv_length = PSA_CIPHER_IV_LENGTH(key_type, alg);
+
+exit:
+    return mbedtls_to_psa_error(ret);
+}
+
+psa_status_t mbedtls_psa_cipher_encrypt_setup(
+    mbedtls_psa_cipher_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg)
+{
+    return psa_cipher_setup(operation, attributes,
+                            key_buffer, key_buffer_size,
+                            alg, MBEDTLS_ENCRYPT);
+}
+
+psa_status_t mbedtls_psa_cipher_decrypt_setup(
+    mbedtls_psa_cipher_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg)
+{
+    return psa_cipher_setup(operation, attributes,
+                            key_buffer, key_buffer_size,
+                            alg, MBEDTLS_DECRYPT);
+}
+
+psa_status_t mbedtls_psa_cipher_set_iv(
+    mbedtls_psa_cipher_operation_t *operation,
+    const uint8_t *iv, size_t iv_length)
+{
+    if (iv_length != operation->iv_length) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    return mbedtls_to_psa_error(
+        mbedtls_cipher_set_iv(&operation->ctx.cipher,
+                              iv, iv_length));
+}
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING)
+/** Process input for which the algorithm is set to ECB mode.
+ *
+ * This requires manual processing, since the PSA API is defined as being
+ * able to process arbitrary-length calls to psa_cipher_update() with ECB mode,
+ * but the underlying mbedtls_cipher_update only takes full blocks.
+ *
+ * \param ctx           The mbedtls cipher context to use. It must have been
+ *                      set up for ECB.
+ * \param[in] input     The input plaintext or ciphertext to process.
+ * \param input_length  The number of bytes to process from \p input.
+ *                      This does not need to be aligned to a block boundary.
+ *                      If there is a partial block at the end of the input,
+ *                      it is stored in \p ctx for future processing.
+ * \param output        The buffer where the output is written. It must be
+ *                      at least `BS * floor((p + input_length) / BS)` bytes
+ *                      long, where `p` is the number of bytes in the
+ *                      unprocessed partial block in \p ctx (with
+ *                      `0 <= p <= BS - 1`) and `BS` is the block size.
+ * \param output_length On success, the number of bytes written to \p output.
+ *                      \c 0 on error.
+ *
+ * \return #PSA_SUCCESS or an error from a hardware accelerator
+ */
+static psa_status_t psa_cipher_update_ecb(
+    mbedtls_cipher_context_t *ctx,
+    const uint8_t *input,
+    size_t input_length,
+    uint8_t *output,
+    size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    size_t block_size = mbedtls_cipher_info_get_block_size(ctx->cipher_info);
+    size_t internal_output_length = 0;
+    *output_length = 0;
+
+    if (input_length == 0) {
+        status = PSA_SUCCESS;
+        goto exit;
+    }
+
+    if (ctx->unprocessed_len > 0) {
+        /* Fill up to block size, and run the block if there's a full one. */
+        size_t bytes_to_copy = block_size - ctx->unprocessed_len;
+
+        if (input_length < bytes_to_copy) {
+            bytes_to_copy = input_length;
+        }
+
+        memcpy(&(ctx->unprocessed_data[ctx->unprocessed_len]),
+               input, bytes_to_copy);
+        input_length -= bytes_to_copy;
+        input += bytes_to_copy;
+        ctx->unprocessed_len += bytes_to_copy;
+
+        if (ctx->unprocessed_len == block_size) {
+            status = mbedtls_to_psa_error(
+                mbedtls_cipher_update(ctx,
+                                      ctx->unprocessed_data,
+                                      block_size,
+                                      output, &internal_output_length));
+
+            if (status != PSA_SUCCESS) {
+                goto exit;
+            }
+
+            output += internal_output_length;
+            *output_length += internal_output_length;
+            ctx->unprocessed_len = 0;
+        }
+    }
+
+    while (input_length >= block_size) {
+        /* Run all full blocks we have, one by one */
+        status = mbedtls_to_psa_error(
+            mbedtls_cipher_update(ctx, input,
+                                  block_size,
+                                  output, &internal_output_length));
+
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+
+        input_length -= block_size;
+        input += block_size;
+
+        output += internal_output_length;
+        *output_length += internal_output_length;
+    }
+
+    if (input_length > 0) {
+        /* Save unprocessed bytes for later processing */
+        memcpy(&(ctx->unprocessed_data[ctx->unprocessed_len]),
+               input, input_length);
+        ctx->unprocessed_len += input_length;
+    }
+
+    status = PSA_SUCCESS;
+
+exit:
+    return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING */
+
+psa_status_t mbedtls_psa_cipher_update(
+    mbedtls_psa_cipher_operation_t *operation,
+    const uint8_t *input, size_t input_length,
+    uint8_t *output, size_t output_size, size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    size_t expected_output_size;
+
+    if (!PSA_ALG_IS_STREAM_CIPHER(operation->alg)) {
+        /* Take the unprocessed partial block left over from previous
+         * update calls, if any, plus the input to this call. Remove
+         * the last partial block, if any. You get the data that will be
+         * output in this call. */
+        expected_output_size =
+            (operation->ctx.cipher.unprocessed_len + input_length)
+            / operation->block_length * operation->block_length;
+    } else {
+        expected_output_size = input_length;
+    }
+
+    if (output_size < expected_output_size) {
+        return PSA_ERROR_BUFFER_TOO_SMALL;
+    }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING)
+    if (operation->alg == PSA_ALG_ECB_NO_PADDING) {
+        /* mbedtls_cipher_update has an API inconsistency: it will only
+         * process a single block at a time in ECB mode. Abstract away that
+         * inconsistency here to match the PSA API behaviour. */
+        status = psa_cipher_update_ecb(&operation->ctx.cipher,
+                                       input,
+                                       input_length,
+                                       output,
+                                       output_length);
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING */
+    if (input_length == 0) {
+        /* There is no input, nothing to be done */
+        *output_length = 0;
+        status = PSA_SUCCESS;
+    } else {
+        status = mbedtls_to_psa_error(
+            mbedtls_cipher_update(&operation->ctx.cipher, input,
+                                  input_length, output, output_length));
+
+        if (*output_length > output_size) {
+            return PSA_ERROR_CORRUPTION_DETECTED;
+        }
+    }
+
+    return status;
+}
+
+psa_status_t mbedtls_psa_cipher_finish(
+    mbedtls_psa_cipher_operation_t *operation,
+    uint8_t *output, size_t output_size, size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_GENERIC_ERROR;
+    uint8_t temp_output_buffer[MBEDTLS_MAX_BLOCK_LENGTH];
+
+    if (operation->ctx.cipher.unprocessed_len != 0) {
+        if (operation->alg == PSA_ALG_ECB_NO_PADDING ||
+            operation->alg == PSA_ALG_CBC_NO_PADDING) {
+            status = PSA_ERROR_INVALID_ARGUMENT;
+            goto exit;
+        }
+    }
+
+    status = mbedtls_to_psa_error(
+        mbedtls_cipher_finish(&operation->ctx.cipher,
+                              temp_output_buffer,
+                              output_length));
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (*output_length == 0) {
+        ; /* Nothing to copy. Note that output may be NULL in this case. */
+    } else if (output_size >= *output_length) {
+        memcpy(output, temp_output_buffer, *output_length);
+    } else {
+        status = PSA_ERROR_BUFFER_TOO_SMALL;
+    }
+
+exit:
+    mbedtls_platform_zeroize(temp_output_buffer,
+                             sizeof(temp_output_buffer));
+
+    return status;
+}
+
+psa_status_t mbedtls_psa_cipher_abort(
+    mbedtls_psa_cipher_operation_t *operation)
+{
+    /* Sanity check (shouldn't happen: operation->alg should
+     * always have been initialized to a valid value). */
+    if (!PSA_ALG_IS_CIPHER(operation->alg)) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    mbedtls_cipher_free(&operation->ctx.cipher);
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_cipher_encrypt(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg,
+    const uint8_t *iv,
+    size_t iv_length,
+    const uint8_t *input,
+    size_t input_length,
+    uint8_t *output,
+    size_t output_size,
+    size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_psa_cipher_operation_t operation = MBEDTLS_PSA_CIPHER_OPERATION_INIT;
+    size_t update_output_length, finish_output_length;
+
+    status = mbedtls_psa_cipher_encrypt_setup(&operation, attributes,
+                                              key_buffer, key_buffer_size,
+                                              alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (iv_length > 0) {
+        status = mbedtls_psa_cipher_set_iv(&operation, iv, iv_length);
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+    }
+
+    status = mbedtls_psa_cipher_update(&operation, input, input_length,
+                                       output, output_size,
+                                       &update_output_length);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = mbedtls_psa_cipher_finish(
+        &operation,
+        mbedtls_buffer_offset(output, update_output_length),
+        output_size - update_output_length, &finish_output_length);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    *output_length = update_output_length + finish_output_length;
+
+exit:
+    if (status == PSA_SUCCESS) {
+        status = mbedtls_psa_cipher_abort(&operation);
+    } else {
+        mbedtls_psa_cipher_abort(&operation);
+    }
+
+    return status;
+}
+
+psa_status_t mbedtls_psa_cipher_decrypt(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg,
+    const uint8_t *input,
+    size_t input_length,
+    uint8_t *output,
+    size_t output_size,
+    size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_psa_cipher_operation_t operation = MBEDTLS_PSA_CIPHER_OPERATION_INIT;
+    size_t olength, accumulated_length;
+
+    status = mbedtls_psa_cipher_decrypt_setup(&operation, attributes,
+                                              key_buffer, key_buffer_size,
+                                              alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (operation.iv_length > 0) {
+        status = mbedtls_psa_cipher_set_iv(&operation,
+                                           input, operation.iv_length);
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+    }
+
+    status = mbedtls_psa_cipher_update(
+        &operation,
+        mbedtls_buffer_offset_const(input, operation.iv_length),
+        input_length - operation.iv_length,
+        output, output_size, &olength);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    accumulated_length = olength;
+
+    status = mbedtls_psa_cipher_finish(
+        &operation,
+        mbedtls_buffer_offset(output, accumulated_length),
+        output_size - accumulated_length, &olength);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    *output_length = accumulated_length + olength;
+
+exit:
+    if (status == PSA_SUCCESS) {
+        status = mbedtls_psa_cipher_abort(&operation);
+    } else {
+        mbedtls_psa_cipher_abort(&operation);
+    }
+
+    return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_CIPHER */
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/tf-psa-crypto/core/psa_crypto_cipher.h b/tf-psa-crypto/core/psa_crypto_cipher.h
new file mode 100644
index 0000000..cc56585
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_cipher.h
@@ -0,0 +1,316 @@
+/*
+ *  PSA cipher driver entry points and associated auxiliary functions
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_CIPHER_H
+#define PSA_CRYPTO_CIPHER_H
+
+#include <mbedtls/cipher.h>
+#include <psa/crypto.h>
+
+/** Get Mbed TLS cipher information given the cipher algorithm PSA identifier
+ *  as well as the PSA type and size of the key to be used with the cipher
+ *  algorithm.
+ *
+ * \param[in]      alg          PSA cipher algorithm identifier
+ * \param[in]      key_type     PSA key type
+ * \param[in,out]  key_bits     Size of the key in bits. The value provided in input
+ *                              might be updated if necessary.
+ * \param[out]     mode         Mbed TLS cipher mode
+ * \param[out]     cipher_id    Mbed TLS cipher algorithm identifier
+ *
+ * \return  On success \c PSA_SUCCESS is returned and key_bits, mode and cipher_id
+ *          are properly updated.
+ *          \c PSA_ERROR_NOT_SUPPORTED is returned if the cipher algorithm is not
+ *          supported.
+ */
+
+psa_status_t mbedtls_cipher_values_from_psa(psa_algorithm_t alg, psa_key_type_t key_type,
+                                            size_t *key_bits, mbedtls_cipher_mode_t *mode,
+                                            mbedtls_cipher_id_t *cipher_id);
+
+#if defined(MBEDTLS_CIPHER_C)
+/** Get Mbed TLS cipher information given the cipher algorithm PSA identifier
+ *  as well as the PSA type and size of the key to be used with the cipher
+ *  algorithm.
+ *
+ * \param       alg        PSA cipher algorithm identifier
+ * \param       key_type   PSA key type
+ * \param       key_bits   Size of the key in bits
+ * \param[out]  cipher_id  Mbed TLS cipher algorithm identifier
+ *
+ * \return  The Mbed TLS cipher information of the cipher algorithm.
+ *          \c NULL if the PSA cipher algorithm is not supported.
+ */
+const mbedtls_cipher_info_t *mbedtls_cipher_info_from_psa(
+    psa_algorithm_t alg, psa_key_type_t key_type, size_t key_bits,
+    mbedtls_cipher_id_t *cipher_id);
+#endif /* MBEDTLS_CIPHER_C */
+
+/**
+ * \brief Set the key for a multipart symmetric encryption operation.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       cipher_encrypt_setup entry point. This function behaves as a
+ *       cipher_encrypt_setup entry point as defined in the PSA driver
+ *       interface specification for transparent drivers.
+ *
+ * \param[in,out] operation     The operation object to set up. It has been
+ *                              initialized as per the documentation for
+ *                              #psa_cipher_operation_t and not yet in use.
+ * \param[in] attributes        The attributes of the key to use for the
+ *                              operation.
+ * \param[in] key_buffer        The buffer containing the key context.
+ * \param[in] key_buffer_size   Size of the \p key_buffer buffer in bytes.
+ * \param[in] alg               The cipher algorithm to compute
+ *                              (\c PSA_ALG_XXX value such that
+ *                              #PSA_ALG_IS_CIPHER(\p alg) is true).
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_cipher_encrypt_setup(
+    mbedtls_psa_cipher_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg);
+
+/**
+ * \brief Set the key for a multipart symmetric decryption operation.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       cipher_decrypt_setup entry point. This function behaves as a
+ *       cipher_decrypt_setup entry point as defined in the PSA driver
+ *       interface specification for transparent drivers.
+ *
+ * \param[in,out] operation     The operation object to set up. It has been
+ *                              initialized as per the documentation for
+ *                              #psa_cipher_operation_t and not yet in use.
+ * \param[in] attributes        The attributes of the key to use for the
+ *                              operation.
+ * \param[in] key_buffer        The buffer containing the key context.
+ * \param[in] key_buffer_size   Size of the \p key_buffer buffer in bytes.
+ * \param[in] alg               The cipher algorithm to compute
+ *                              (\c PSA_ALG_XXX value such that
+ *                              #PSA_ALG_IS_CIPHER(\p alg) is true).
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_cipher_decrypt_setup(
+    mbedtls_psa_cipher_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg);
+
+/** Set the IV for a symmetric encryption or decryption operation.
+ *
+ * This function sets the IV (initialization vector), nonce
+ * or initial counter value for the encryption or decryption operation.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       cipher_set_iv entry point. This function behaves as a
+ *       cipher_set_iv entry point as defined in the PSA driver
+ *       interface specification for transparent drivers.
+ *
+ * \param[in,out] operation     Active cipher operation.
+ * \param[in] iv                Buffer containing the IV to use.
+ * \param[in] iv_length         Size of the IV in bytes. It is guaranteed by
+ *                              the core to be less or equal to
+ *                              PSA_CIPHER_IV_MAX_SIZE.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         The size of \p iv is not acceptable for the chosen algorithm,
+ *         or the chosen algorithm does not use an IV.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ */
+psa_status_t mbedtls_psa_cipher_set_iv(
+    mbedtls_psa_cipher_operation_t *operation,
+    const uint8_t *iv, size_t iv_length);
+
+/** Encrypt or decrypt a message fragment in an active cipher operation.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       cipher_update entry point. This function behaves as a
+ *       cipher_update entry point as defined in the PSA driver
+ *       interface specification for transparent drivers.
+ *
+ * \param[in,out] operation     Active cipher operation.
+ * \param[in] input             Buffer containing the message fragment to
+ *                              encrypt or decrypt.
+ * \param[in] input_length      Size of the \p input buffer in bytes.
+ * \param[out] output           Buffer where the output is to be written.
+ * \param[in]  output_size      Size of the \p output buffer in bytes.
+ * \param[out] output_length    On success, the number of bytes
+ *                              that make up the returned output.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p output buffer is too small.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ */
+psa_status_t mbedtls_psa_cipher_update(
+    mbedtls_psa_cipher_operation_t *operation,
+    const uint8_t *input, size_t input_length,
+    uint8_t *output, size_t output_size, size_t *output_length);
+
+/** Finish encrypting or decrypting a message in a cipher operation.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       cipher_finish entry point. This function behaves as a
+ *       cipher_finish entry point as defined in the PSA driver
+ *       interface specification for transparent drivers.
+ *
+ * \param[in,out] operation     Active cipher operation.
+ * \param[out] output           Buffer where the output is to be written.
+ * \param[in]  output_size      Size of the \p output buffer in bytes.
+ * \param[out] output_length    On success, the number of bytes
+ *                              that make up the returned output.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         The total input size passed to this operation is not valid for
+ *         this particular algorithm. For example, the algorithm is a based
+ *         on block cipher and requires a whole number of blocks, but the
+ *         total input size is not a multiple of the block size.
+ * \retval #PSA_ERROR_INVALID_PADDING
+ *         This is a decryption operation for an algorithm that includes
+ *         padding, and the ciphertext does not contain valid padding.
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p output buffer is too small.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ */
+psa_status_t mbedtls_psa_cipher_finish(
+    mbedtls_psa_cipher_operation_t *operation,
+    uint8_t *output, size_t output_size, size_t *output_length);
+
+/** Abort a cipher operation.
+ *
+ * Aborting an operation frees all associated resources except for the
+ * \p operation structure itself. Once aborted, the operation object
+ * can be reused for another operation.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       cipher_abort entry point. This function behaves as a
+ *       cipher_abort entry point as defined in the PSA driver
+ *       interface specification for transparent drivers.
+ *
+ * \param[in,out] operation     Initialized cipher operation.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ */
+psa_status_t mbedtls_psa_cipher_abort(mbedtls_psa_cipher_operation_t *operation);
+
+/** Encrypt a message using a symmetric cipher.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       cipher_encrypt entry point. This function behaves as a
+ *       cipher_encrypt entry point as defined in the PSA driver
+ *       interface specification for transparent drivers.
+ *
+ * \param[in] attributes        The attributes of the key to use for the
+ *                              operation.
+ * \param[in] key_buffer        The buffer containing the key context.
+ * \param[in] key_buffer_size   Size of the \p key_buffer buffer in bytes.
+ * \param[in] alg               The cipher algorithm to compute
+ *                              (\c PSA_ALG_XXX value such that
+ *                              #PSA_ALG_IS_CIPHER(\p alg) is true).
+ * \param[in] iv                Buffer containing the IV for encryption. The
+ *                              IV has been generated by the core.
+ * \param[in] iv_length         Size of the \p iv in bytes.
+ * \param[in] input             Buffer containing the message to encrypt.
+ * \param[in] input_length      Size of the \p input buffer in bytes.
+ * \param[in,out] output        Buffer where the output is to be written.
+ * \param[in]  output_size      Size of the \p output buffer in bytes.
+ * \param[out] output_length    On success, the number of bytes that make up
+ *                              the returned output. Initialized to zero
+ *                              by the core.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p output buffer is too small.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         The size \p iv_length is not acceptable for the chosen algorithm,
+ *         or the chosen algorithm does not use an IV.
+ *         The total input size passed to this operation is not valid for
+ *         this particular algorithm. For example, the algorithm is a based
+ *         on block cipher and requires a whole number of blocks, but the
+ *         total input size is not a multiple of the block size.
+ * \retval #PSA_ERROR_INVALID_PADDING
+ *         This is a decryption operation for an algorithm that includes
+ *         padding, and the ciphertext does not contain valid padding.
+ */
+psa_status_t mbedtls_psa_cipher_encrypt(const psa_key_attributes_t *attributes,
+                                        const uint8_t *key_buffer,
+                                        size_t key_buffer_size,
+                                        psa_algorithm_t alg,
+                                        const uint8_t *iv,
+                                        size_t iv_length,
+                                        const uint8_t *input,
+                                        size_t input_length,
+                                        uint8_t *output,
+                                        size_t output_size,
+                                        size_t *output_length);
+
+/** Decrypt a message using a symmetric cipher.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       cipher_decrypt entry point. This function behaves as a
+ *       cipher_decrypt entry point as defined in the PSA driver
+ *       interface specification for transparent drivers.
+ *
+ * \param[in]  attributes       The attributes of the key to use for the
+ *                              operation.
+ * \param[in]  key_buffer       The buffer containing the key context.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[in]  alg              The cipher algorithm to compute
+ *                              (\c PSA_ALG_XXX value such that
+ *                              #PSA_ALG_IS_CIPHER(\p alg) is true).
+ * \param[in]  input            Buffer containing the iv and the ciphertext.
+ * \param[in]  input_length     Size of the \p input buffer in bytes.
+ * \param[out] output           Buffer where the output is to be written.
+ * \param[in]  output_size      Size of the \p output buffer in bytes.
+ * \param[out] output_length    On success, the number of bytes that make up
+ *                              the returned output. Initialized to zero
+ *                              by the core.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p output buffer is too small.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         The size of \p iv is not acceptable for the chosen algorithm,
+ *         or the chosen algorithm does not use an IV.
+ *         The total input size passed to this operation is not valid for
+ *         this particular algorithm. For example, the algorithm is a based
+ *         on block cipher and requires a whole number of blocks, but the
+ *         total input size is not a multiple of the block size.
+ * \retval #PSA_ERROR_INVALID_PADDING
+ *         This is a decryption operation for an algorithm that includes
+ *         padding, and the ciphertext does not contain valid padding.
+ */
+psa_status_t mbedtls_psa_cipher_decrypt(const psa_key_attributes_t *attributes,
+                                        const uint8_t *key_buffer,
+                                        size_t key_buffer_size,
+                                        psa_algorithm_t alg,
+                                        const uint8_t *input,
+                                        size_t input_length,
+                                        uint8_t *output,
+                                        size_t output_size,
+                                        size_t *output_length);
+
+#endif /* PSA_CRYPTO_CIPHER_H */
diff --git a/tf-psa-crypto/core/psa_crypto_client.c b/tf-psa-crypto/core/psa_crypto_client.c
new file mode 100644
index 0000000..72f671d
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_client.c
@@ -0,0 +1,22 @@
+/*
+ *  PSA crypto client code
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+#include "psa/crypto.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_CLIENT)
+
+#include <string.h>
+#include "mbedtls/platform.h"
+
+void psa_reset_key_attributes(psa_key_attributes_t *attributes)
+{
+    memset(attributes, 0, sizeof(*attributes));
+}
+
+#endif /* MBEDTLS_PSA_CRYPTO_CLIENT */
diff --git a/tf-psa-crypto/core/psa_crypto_core.h b/tf-psa-crypto/core/psa_crypto_core.h
new file mode 100644
index 0000000..9462d2e
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_core.h
@@ -0,0 +1,957 @@
+/*
+ *  PSA crypto core internal interfaces
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_CORE_H
+#define PSA_CRYPTO_CORE_H
+
+/*
+ * Include the build-time configuration information header. Here, we do not
+ * include `"mbedtls/build_info.h"` directly but `"psa/build_info.h"`, which
+ * is basically just an alias to it. This is to ease the maintenance of the
+ * TF-PSA-Crypto repository which has a different build system and
+ * configuration.
+ */
+#include "psa/build_info.h"
+
+#include "psa/crypto.h"
+#include "psa/crypto_se_driver.h"
+#if defined(MBEDTLS_THREADING_C)
+#include "mbedtls/threading.h"
+#endif
+
+/**
+ * Tell if PSA is ready for this hash.
+ *
+ * \note            For now, only checks the state of the driver subsystem,
+ *                  not the algorithm. Might do more in the future.
+ *
+ * \param hash_alg  The hash algorithm (ignored for now).
+ *
+ * \return 1 if the driver subsytem is ready, 0 otherwise.
+ */
+int psa_can_do_hash(psa_algorithm_t hash_alg);
+
+/**
+ * Tell if PSA is ready for this cipher.
+ *
+ * \note            For now, only checks the state of the driver subsystem,
+ *                  not the algorithm. Might do more in the future.
+ *
+ * \param cipher_alg  The cipher algorithm (ignored for now).
+ *
+ * \return 1 if the driver subsytem is ready, 0 otherwise.
+ */
+int psa_can_do_cipher(psa_key_type_t key_type, psa_algorithm_t cipher_alg);
+
+typedef enum {
+    PSA_SLOT_EMPTY = 0,
+    PSA_SLOT_FILLING,
+    PSA_SLOT_FULL,
+    PSA_SLOT_PENDING_DELETION,
+} psa_key_slot_state_t;
+
+/** The data structure representing a key slot, containing key material
+ * and metadata for one key.
+ */
+typedef struct {
+    psa_key_attributes_t attr;
+
+    /*
+     * The current state of the key slot, as described in
+     * docs/architecture/psa-thread-safety/psa-thread-safety.md.
+     *
+     * Library functions can modify the state of a key slot by calling
+     * psa_key_slot_state_transition.
+     *
+     * The state variable is used to help determine whether library functions
+     * which operate on the slot succeed. For example, psa_finish_key_creation,
+     * which transfers the state of a slot from PSA_SLOT_FILLING to
+     * PSA_SLOT_FULL, must fail with error code PSA_ERROR_CORRUPTION_DETECTED
+     * if the state of the slot is not PSA_SLOT_FILLING.
+     *
+     * Library functions which traverse the array of key slots only consider
+     * slots that are in a suitable state for the function.
+     * For example, psa_get_and_lock_key_slot_in_memory, which finds a slot
+     * containing a given key ID, will only check slots whose state variable is
+     * PSA_SLOT_FULL. */
+    psa_key_slot_state_t state;
+
+    /*
+     * Number of functions registered as reading the material in the key slot.
+     *
+     * Library functions must not write directly to registered_readers
+     *
+     * A function must call psa_register_read(slot) before reading the current
+     * contents of the slot for an operation.
+     * They then must call psa_unregister_read(slot) once they have finished
+     * reading the current contents of the slot. If the key slot mutex is not
+     * held (when mutexes are enabled), this call must be done via a call to
+     * psa_unregister_read_under_mutex(slot).
+     * A function must call psa_key_slot_has_readers(slot) to check if
+     * the slot is in use for reading.
+     *
+     * This counter is used to prevent resetting the key slot while the library
+     * may access it. For example, such control is needed in the following
+     * scenarios:
+     * . In case of key slot starvation, all key slots contain the description
+     *   of a key, and the library asks for the description of a persistent
+     *   key not present in the key slots, the key slots currently accessed by
+     *   the library cannot be reclaimed to free a key slot to load the
+     *   persistent key.
+     * . In case of a multi-threaded application where one thread asks to close
+     *   or purge or destroy a key while it is in use by the library through
+     *   another thread. */
+    size_t registered_readers;
+
+    /* Dynamically allocated key data buffer.
+     * Format as specified in psa_export_key(). */
+    struct key_data {
+        uint8_t *data;
+        size_t bytes;
+    } key;
+} psa_key_slot_t;
+
+#if defined(MBEDTLS_THREADING_C)
+
+/** Perform a mutex operation and return immediately upon failure.
+ *
+ * Returns PSA_ERROR_SERVICE_FAILURE if the operation fails
+ * and status was PSA_SUCCESS.
+ *
+ * Assumptions:
+ *  psa_status_t status exists.
+ *  f is a mutex operation which returns 0 upon success.
+ */
+#define PSA_THREADING_CHK_RET(f)                       \
+    do                                                 \
+    {                                                  \
+        if ((f) != 0) {                                \
+            if (status == PSA_SUCCESS) {               \
+                return PSA_ERROR_SERVICE_FAILURE;      \
+            }                                          \
+            return status;                             \
+        }                                              \
+    } while (0);
+
+/** Perform a mutex operation and goto exit on failure.
+ *
+ * Sets status to PSA_ERROR_SERVICE_FAILURE if status was PSA_SUCCESS.
+ *
+ * Assumptions:
+ *  psa_status_t status exists.
+ *  Label exit: exists.
+ *  f is a mutex operation which returns 0 upon success.
+ */
+#define PSA_THREADING_CHK_GOTO_EXIT(f)                 \
+    do                                                 \
+    {                                                  \
+        if ((f) != 0) {                                \
+            if (status == PSA_SUCCESS) {               \
+                status = PSA_ERROR_SERVICE_FAILURE;    \
+            }                                          \
+            goto exit;                                 \
+        }                                              \
+    } while (0);
+#endif
+
+/** Test whether a key slot has any registered readers.
+ * If multi-threading is enabled, the caller must hold the
+ * global key slot mutex.
+ *
+ * \param[in] slot      The key slot to test.
+ *
+ * \return 1 if the slot has any registered readers, 0 otherwise.
+ */
+static inline int psa_key_slot_has_readers(const psa_key_slot_t *slot)
+{
+    return slot->registered_readers > 0;
+}
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+/** Get the SE slot number of a key from the key slot storing its description.
+ *
+ * \param[in]  slot  The key slot to query. This must be a key slot storing
+ *                   the description of a key of a dynamically registered
+ *                   secure element, otherwise the behaviour is undefined.
+ */
+static inline psa_key_slot_number_t psa_key_slot_get_slot_number(
+    const psa_key_slot_t *slot)
+{
+    return *((psa_key_slot_number_t *) (slot->key.data));
+}
+#endif
+
+/** Completely wipe a slot in memory, including its policy.
+ *
+ * Persistent storage is not affected.
+ * Sets the slot's state to PSA_SLOT_EMPTY.
+ * If multi-threading is enabled, the caller must hold the
+ * global key slot mutex.
+ *
+ * \param[in,out] slot  The key slot to wipe.
+ *
+ * \retval #PSA_SUCCESS
+ *         The slot has been successfully wiped.
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED
+ *         The slot's state was PSA_SLOT_FULL or PSA_SLOT_PENDING_DELETION, and
+ *         the amount of registered readers was not equal to 1. Or,
+ *         the slot's state was PSA_SLOT_EMPTY. Or,
+ *         the slot's state was PSA_SLOT_FILLING, and the amount
+ *         of registered readers was not equal to 0.
+ */
+psa_status_t psa_wipe_key_slot(psa_key_slot_t *slot);
+
+/** Try to allocate a buffer to an empty key slot.
+ *
+ * \param[in,out] slot          Key slot to attach buffer to.
+ * \param[in] buffer_length     Requested size of the buffer.
+ *
+ * \retval #PSA_SUCCESS
+ *         The buffer has been successfully allocated.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
+ *         Not enough memory was available for allocation.
+ * \retval #PSA_ERROR_ALREADY_EXISTS
+ *         Trying to allocate a buffer to a non-empty key slot.
+ */
+psa_status_t psa_allocate_buffer_to_slot(psa_key_slot_t *slot,
+                                         size_t buffer_length);
+
+/** Wipe key data from a slot. Preserves metadata such as the policy. */
+psa_status_t psa_remove_key_data_from_memory(psa_key_slot_t *slot);
+
+/** Copy key data (in export format) into an empty key slot.
+ *
+ * This function assumes that the slot does not contain
+ * any key material yet. On failure, the slot content is unchanged.
+ *
+ * \param[in,out] slot          Key slot to copy the key into.
+ * \param[in] data              Buffer containing the key material.
+ * \param data_length           Size of the key buffer.
+ *
+ * \retval #PSA_SUCCESS
+ *         The key has been copied successfully.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
+ *         Not enough memory was available for allocation of the
+ *         copy buffer.
+ * \retval #PSA_ERROR_ALREADY_EXISTS
+ *         There was other key material already present in the slot.
+ */
+psa_status_t psa_copy_key_material_into_slot(psa_key_slot_t *slot,
+                                             const uint8_t *data,
+                                             size_t data_length);
+
+/** Convert an Mbed TLS error code to a PSA error code
+ *
+ * \note This function is provided solely for the convenience of
+ *       Mbed TLS and may be removed at any time without notice.
+ *
+ * \param ret           An Mbed TLS-thrown error code
+ *
+ * \return              The corresponding PSA error code
+ */
+psa_status_t mbedtls_to_psa_error(int ret);
+
+/** Import a key in binary format.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       import_key entry point. This function behaves as an import_key
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in]  attributes       The attributes for the key to import.
+ * \param[in]  data             The buffer containing the key data in import
+ *                              format.
+ * \param[in]  data_length      Size of the \p data buffer in bytes.
+ * \param[out] key_buffer       The buffer to contain the key data in output
+ *                              format upon successful return.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes. This
+ *                              size is greater or equal to \p data_length.
+ * \param[out] key_buffer_length  The length of the data written in \p
+ *                                key_buffer in bytes.
+ * \param[out] bits             The key size in number of bits.
+ *
+ * \retval #PSA_SUCCESS  The key was imported successfully.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         The key data is not correctly formatted.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t psa_import_key_into_slot(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *data, size_t data_length,
+    uint8_t *key_buffer, size_t key_buffer_size,
+    size_t *key_buffer_length, size_t *bits);
+
+/** Export a key in binary format
+ *
+ * \note The signature of this function is that of a PSA driver export_key
+ *       entry point. This function behaves as an export_key entry point as
+ *       defined in the PSA driver interface specification.
+ *
+ * \param[in]  attributes       The attributes for the key to export.
+ * \param[in]  key_buffer       Material or context of the key to export.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[out] data             Buffer where the key data is to be written.
+ * \param[in]  data_size        Size of the \p data buffer in bytes.
+ * \param[out] data_length      On success, the number of bytes written in
+ *                              \p data
+ *
+ * \retval #PSA_SUCCESS  The key was exported successfully.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription
+ * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ */
+psa_status_t psa_export_key_internal(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    uint8_t *data, size_t data_size, size_t *data_length);
+
+/** Export a public key or the public part of a key pair in binary format.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       export_public_key entry point. This function behaves as an
+ *       export_public_key entry point as defined in the PSA driver interface
+ *       specification.
+ *
+ * \param[in]  attributes       The attributes for the key to export.
+ * \param[in]  key_buffer       Material or context of the key to export.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[out] data             Buffer where the key data is to be written.
+ * \param[in]  data_size        Size of the \p data buffer in bytes.
+ * \param[out] data_length      On success, the number of bytes written in
+ *                              \p data
+ *
+ * \retval #PSA_SUCCESS  The public key was exported successfully.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription
+ * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ */
+psa_status_t psa_export_public_key_internal(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    uint8_t *data, size_t data_size, size_t *data_length);
+
+/** Whether a key production parameters structure is the default.
+ *
+ * Calls to a key generation driver with non-default production parameters
+ * require a driver supporting custom production parameters.
+ *
+ * \param[in] params            The key production parameters to check.
+ * \param params_data_length    Size of `params->data` in bytes.
+ */
+int psa_key_production_parameters_are_default(
+    const psa_key_production_parameters_t *params,
+    size_t params_data_length);
+
+/**
+ * \brief Generate a key.
+ *
+ * \note The signature of the function is that of a PSA driver generate_key
+ *       entry point.
+ *
+ * \param[in]  attributes         The attributes for the key to generate.
+ * \param[in]  params             The production parameters from
+ *                                psa_generate_key_ext().
+ * \param      params_data_length The size of `params->data` in bytes.
+ * \param[out] key_buffer         Buffer where the key data is to be written.
+ * \param[in]  key_buffer_size    Size of \p key_buffer in bytes.
+ * \param[out] key_buffer_length  On success, the number of bytes written in
+ *                                \p key_buffer.
+ *
+ * \retval #PSA_SUCCESS
+ *         The key was generated successfully.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         Key size in bits or type not supported.
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of \p key_buffer is too small.
+ */
+psa_status_t psa_generate_key_internal(const psa_key_attributes_t *attributes,
+                                       const psa_key_production_parameters_t *params,
+                                       size_t params_data_length,
+                                       uint8_t *key_buffer,
+                                       size_t key_buffer_size,
+                                       size_t *key_buffer_length);
+
+/** Sign a message with a private key. For hash-and-sign algorithms,
+ *  this includes the hashing step.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       sign_message entry point. This function behaves as a sign_message
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \note This function will call the driver for psa_sign_hash
+ *       and go through driver dispatch again.
+ *
+ * \param[in]  attributes       The attributes of the key to use for the
+ *                              operation.
+ * \param[in]  key_buffer       The buffer containing the key context.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[in]  alg              A signature algorithm that is compatible with
+ *                              the type of the key.
+ * \param[in]  input            The input message to sign.
+ * \param[in]  input_length     Size of the \p input buffer in bytes.
+ * \param[out] signature        Buffer where the signature is to be written.
+ * \param[in]  signature_size   Size of the \p signature buffer in bytes.
+ * \param[out] signature_length On success, the number of bytes
+ *                              that make up the returned signature value.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p signature buffer is too small. You can
+ *         determine a sufficient buffer size by calling
+ *         #PSA_SIGN_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg)
+ *         where \c key_type and \c key_bits are the type and bit-size
+ *         respectively of the key.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription
+ */
+psa_status_t psa_sign_message_builtin(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *input, size_t input_length,
+    uint8_t *signature, size_t signature_size, size_t *signature_length);
+
+/** Verify the signature of a message with a public key, using
+ *  a hash-and-sign verification algorithm.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       verify_message entry point. This function behaves as a verify_message
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \note This function will call the driver for psa_verify_hash
+ *       and go through driver dispatch again.
+ *
+ * \param[in]  attributes       The attributes of the key to use for the
+ *                              operation.
+ * \param[in]  key_buffer       The buffer containing the key context.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[in]  alg              A signature algorithm that is compatible with
+ *                              the type of the key.
+ * \param[in]  input            The message whose signature is to be verified.
+ * \param[in]  input_length     Size of the \p input buffer in bytes.
+ * \param[in]  signature        Buffer containing the signature to verify.
+ * \param[in]  signature_length Size of the \p signature buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ *         The signature is valid.
+ * \retval #PSA_ERROR_INVALID_SIGNATURE
+ *         The calculation was performed successfully, but the passed
+ *         signature is not a valid signature.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ */
+psa_status_t psa_verify_message_builtin(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *input, size_t input_length,
+    const uint8_t *signature, size_t signature_length);
+
+/** Sign an already-calculated hash with a private key.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       sign_hash entry point. This function behaves as a sign_hash
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in]  attributes       The attributes of the key to use for the
+ *                              operation.
+ * \param[in]  key_buffer       The buffer containing the key context.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[in]  alg              A signature algorithm that is compatible with
+ *                              the type of the key.
+ * \param[in]  hash             The hash or message to sign.
+ * \param[in]  hash_length      Size of the \p hash buffer in bytes.
+ * \param[out] signature        Buffer where the signature is to be written.
+ * \param[in]  signature_size   Size of the \p signature buffer in bytes.
+ * \param[out] signature_length On success, the number of bytes
+ *                              that make up the returned signature value.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p signature buffer is too small. You can
+ *         determine a sufficient buffer size by calling
+ *         #PSA_SIGN_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg)
+ *         where \c key_type and \c key_bits are the type and bit-size
+ *         respectively of the key.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription
+ */
+psa_status_t psa_sign_hash_builtin(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+    uint8_t *signature, size_t signature_size, size_t *signature_length);
+
+/**
+ * \brief Verify the signature a hash or short message using a public key.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       verify_hash entry point. This function behaves as a verify_hash
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in]  attributes       The attributes of the key to use for the
+ *                              operation.
+ * \param[in]  key_buffer       The buffer containing the key context.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[in]  alg              A signature algorithm that is compatible with
+ *                              the type of the key.
+ * \param[in]  hash             The hash or message whose signature is to be
+ *                              verified.
+ * \param[in]  hash_length      Size of the \p hash buffer in bytes.
+ * \param[in]  signature        Buffer containing the signature to verify.
+ * \param[in]  signature_length Size of the \p signature buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ *         The signature is valid.
+ * \retval #PSA_ERROR_INVALID_SIGNATURE
+ *         The calculation was performed successfully, but the passed
+ *         signature is not a valid signature.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ */
+psa_status_t psa_verify_hash_builtin(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+    const uint8_t *signature, size_t signature_length);
+
+/**
+ * \brief Validate the key bit size for unstructured keys.
+ *
+ * \note  Check that the bit size is acceptable for a given key type for
+ *        unstructured keys.
+ *
+ * \param[in]  type  The key type
+ * \param[in]  bits  The number of bits of the key
+ *
+ * \retval #PSA_SUCCESS
+ *         The key type and size are valid.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         The size in bits of the key is not valid.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         The type and/or the size in bits of the key or the combination of
+ *         the two is not supported.
+ */
+psa_status_t psa_validate_unstructured_key_bit_size(psa_key_type_t type,
+                                                    size_t bits);
+
+/** Perform a key agreement and return the raw shared secret, using
+    built-in raw key agreement functions.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       key_agreement entry point. This function behaves as a key_agreement
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in]  attributes           The attributes of the key to use for the
+ *                                  operation.
+ * \param[in]  key_buffer           The buffer containing the private key
+ *                                  context.
+ * \param[in]  key_buffer_size      Size of the \p key_buffer buffer in
+ *                                  bytes.
+ * \param[in]  alg                  A key agreement algorithm that is
+ *                                  compatible with the type of the key.
+ * \param[in]  peer_key             The buffer containing the key context
+ *                                  of the peer's public key.
+ * \param[in]  peer_key_length      Size of the \p peer_key buffer in
+ *                                  bytes.
+ * \param[out] shared_secret        The buffer to which the shared secret
+ *                                  is to be written.
+ * \param[in]  shared_secret_size   Size of the \p shared_secret buffer in
+ *                                  bytes.
+ * \param[out] shared_secret_length On success, the number of bytes that make
+ *                                  up the returned shared secret.
+ * \retval #PSA_SUCCESS
+ *         Success. Shared secret successfully calculated.
+ * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription
+ * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         \p alg is not a key agreement algorithm, or
+ *         \p private_key is not compatible with \p alg,
+ *         or \p peer_key is not valid for \p alg or not compatible with
+ *         \p private_key.
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         \p shared_secret_size is too small
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         \p alg is not a supported key agreement algorithm.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription
+ * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_BAD_STATE \emptydescription
+ */
+psa_status_t psa_key_agreement_raw_builtin(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg,
+    const uint8_t *peer_key,
+    size_t peer_key_length,
+    uint8_t *shared_secret,
+    size_t shared_secret_size,
+    size_t *shared_secret_length);
+
+/**
+ * \brief Set the maximum number of ops allowed to be executed by an
+ *        interruptible function in a single call.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       interruptible_set_max_ops entry point. This function behaves as an
+ *       interruptible_set_max_ops entry point as defined in the PSA driver
+ *       interface specification for transparent drivers.
+ *
+ * \param[in]  max_ops          The maximum number of ops to be executed in a
+ *                              single call, this can be a number from 0 to
+ *                              #PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED, where 0
+ *                              is obviously the least amount of work done per
+ *                              call.
+ */
+void mbedtls_psa_interruptible_set_max_ops(uint32_t max_ops);
+
+/**
+ * \brief Get the maximum number of ops allowed to be executed by an
+ *        interruptible function in a single call.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       interruptible_get_max_ops entry point. This function behaves as an
+ *       interruptible_get_max_ops entry point as defined in the PSA driver
+ *       interface specification for transparent drivers.
+ *
+ * \return                      Maximum number of ops allowed to be executed
+ *                              by an interruptible function in a single call.
+ */
+uint32_t mbedtls_psa_interruptible_get_max_ops(void);
+
+/**
+ * \brief Get the number of ops that a hash signing operation has taken for the
+ *        previous call. If no call or work has taken place, this will return
+ *        zero.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       sign_hash_get_num_ops entry point. This function behaves as an
+ *       sign_hash_get_num_ops entry point as defined in the PSA driver
+ *       interface specification for transparent drivers.
+ *
+ * \param   operation           The \c
+ *                              mbedtls_psa_sign_hash_interruptible_operation_t
+ *                              to use. This must be initialized first.
+ *
+ * \return                      Number of ops that were completed
+ *                              in the last call to \c
+ *                              mbedtls_psa_sign_hash_complete().
+ */
+uint32_t mbedtls_psa_sign_hash_get_num_ops(
+    const mbedtls_psa_sign_hash_interruptible_operation_t *operation);
+
+/**
+ * \brief Get the number of ops that a hash verification operation has taken for
+ *        the previous call. If no call or work has taken place, this will
+ *        return zero.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       verify_hash_get_num_ops entry point. This function behaves as an
+ *       verify_hash_get_num_ops entry point as defined in the PSA driver
+ *       interface specification for transparent drivers.
+ *
+ * \param   operation           The \c
+ *                              mbedtls_psa_verify_hash_interruptible_operation_t
+ *                              to use. This must be initialized first.
+ *
+ * \return                      Number of ops that were completed
+ *                              in the last call to \c
+ *                              mbedtls_psa_verify_hash_complete().
+ */
+uint32_t mbedtls_psa_verify_hash_get_num_ops(
+    const mbedtls_psa_verify_hash_interruptible_operation_t *operation);
+
+/**
+ * \brief  Start signing a hash or short message with a private key, in an
+ *         interruptible manner.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       sign_hash_start entry point. This function behaves as a
+ *       sign_hash_start entry point as defined in the PSA driver interface
+ *       specification for transparent drivers.
+ *
+ * \param[in]  operation        The \c
+ *                              mbedtls_psa_sign_hash_interruptible_operation_t
+ *                              to use. This must be initialized first.
+ * \param[in]  attributes       The attributes of the key to use for the
+ *                              operation.
+ * \param[in]  key_buffer       The buffer containing the key context.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[in]  alg              A signature algorithm that is compatible with
+ *                              the type of the key.
+ * \param[in] hash              The hash or message to sign.
+ * \param hash_length           Size of the \p hash buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ *         The operation started successfully - call \c psa_sign_hash_complete()
+ *         with the same context to complete the operation
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         An unsupported, incorrectly formatted or incorrect type of key was
+ *         used.
+ * \retval #PSA_ERROR_NOT_SUPPORTED Either no internal interruptible operations
+ *         are currently supported, or the key type is currently unsupported.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
+ *         There was insufficient memory to load the key representation.
+ */
+psa_status_t mbedtls_psa_sign_hash_start(
+    mbedtls_psa_sign_hash_interruptible_operation_t *operation,
+    const psa_key_attributes_t *attributes, const uint8_t *key_buffer,
+    size_t key_buffer_size, psa_algorithm_t alg,
+    const uint8_t *hash, size_t hash_length);
+
+/**
+ * \brief Continue and eventually complete the action of signing a hash or
+ *        short message with a private key, in an interruptible manner.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       sign_hash_complete entry point. This function behaves as a
+ *       sign_hash_complete entry point as defined in the PSA driver interface
+ *       specification for transparent drivers.
+ *
+ * \param[in]  operation        The \c
+ *                              mbedtls_psa_sign_hash_interruptible_operation_t
+ *                              to use. This must be initialized first.
+ *
+ * \param[out] signature        Buffer where the signature is to be written.
+ * \param signature_size        Size of the \p signature buffer in bytes. This
+ *                              must be appropriate for the selected
+ *                              algorithm and key.
+ * \param[out] signature_length On success, the number of bytes that make up
+ *                              the returned signature value.
+ *
+ * \retval #PSA_SUCCESS
+ *         Operation completed successfully
+ *
+ * \retval #PSA_OPERATION_INCOMPLETE
+ *         Operation was interrupted due to the setting of \c
+ *         psa_interruptible_set_max_ops(), there is still work to be done,
+ *         please call this function again with the same operation object.
+ *
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p signature buffer is too small. You can
+ *         determine a sufficient buffer size by calling
+ *         #PSA_SIGN_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg)
+ *         where \c key_type and \c key_bits are the type and bit-size
+ *         respectively of \p key.
+ *
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription
+ */
+psa_status_t mbedtls_psa_sign_hash_complete(
+    mbedtls_psa_sign_hash_interruptible_operation_t *operation,
+    uint8_t *signature, size_t signature_size,
+    size_t *signature_length);
+
+/**
+ * \brief Abort a sign hash operation.
+ *
+ * \note The signature of this function is that of a PSA driver sign_hash_abort
+ *       entry point. This function behaves as a sign_hash_abort entry point as
+ *       defined in the PSA driver interface specification for transparent
+ *       drivers.
+ *
+ * \param[in]  operation        The \c
+ *                              mbedtls_psa_sign_hash_interruptible_operation_t
+ *                              to abort.
+ *
+ * \retval #PSA_SUCCESS
+ *         The operation was aborted successfully.
+ */
+psa_status_t mbedtls_psa_sign_hash_abort(
+    mbedtls_psa_sign_hash_interruptible_operation_t *operation);
+
+/**
+ * \brief  Start reading and verifying a hash or short message, in an
+ *         interruptible manner.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       verify_hash_start entry point. This function behaves as a
+ *       verify_hash_start entry point as defined in the PSA driver interface
+ *       specification for transparent drivers.
+ *
+ * \param[in]  operation        The \c
+ *                              mbedtls_psa_verify_hash_interruptible_operation_t
+ *                              to use. This must be initialized first.
+ * \param[in]  attributes       The attributes of the key to use for the
+ *                              operation.
+ * \param[in]  key_buffer       The buffer containing the key context.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[in]  alg              A signature algorithm that is compatible with
+ *                              the type of the key.
+ * \param[in] hash              The hash whose signature is to be verified.
+ * \param hash_length           Size of the \p hash buffer in bytes.
+ * \param[in] signature         Buffer containing the signature to verify.
+ * \param signature_length      Size of the \p signature buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ *         The operation started successfully - call \c psa_sign_hash_complete()
+ *         with the same context to complete the operation
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         An unsupported or incorrect type of key was used.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *        Either no internal interruptible operations are currently supported,
+ *         or the key type is currently unsupported.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
+ *        There was insufficient memory either to load the key representation,
+ *        or to prepare the operation.
+ */
+psa_status_t mbedtls_psa_verify_hash_start(
+    mbedtls_psa_verify_hash_interruptible_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg,
+    const uint8_t *hash, size_t hash_length,
+    const uint8_t *signature, size_t signature_length);
+
+/**
+ * \brief Continue and eventually complete the action of signing a hash or
+ *        short message with a private key, in an interruptible manner.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       sign_hash_complete entry point. This function behaves as a
+ *       sign_hash_complete entry point as defined in the PSA driver interface
+ *       specification for transparent drivers.
+ *
+ * \param[in]  operation        The \c
+ *                              mbedtls_psa_sign_hash_interruptible_operation_t
+ *                              to use. This must be initialized first.
+ *
+ * \retval #PSA_SUCCESS
+ *         Operation completed successfully, and the passed signature is valid.
+ *
+ * \retval #PSA_OPERATION_INCOMPLETE
+ *         Operation was interrupted due to the setting of \c
+ *         psa_interruptible_set_max_ops(), there is still work to be done,
+ *         please call this function again with the same operation object.
+ *
+ * \retval #PSA_ERROR_INVALID_SIGNATURE
+ *         The calculation was performed successfully, but the passed
+ *         signature is not a valid signature.
+ *
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ */
+psa_status_t mbedtls_psa_verify_hash_complete(
+    mbedtls_psa_verify_hash_interruptible_operation_t *operation);
+
+/**
+ * \brief Abort a verify signed hash operation.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       verify_hash_abort entry point. This function behaves as a
+ *       verify_hash_abort entry point as defined in the PSA driver interface
+ *       specification for transparent drivers.
+ *
+ * \param[in]  operation        The \c
+ *                              mbedtls_psa_verify_hash_interruptible_operation_t
+ *                              to abort.
+ *
+ * \retval #PSA_SUCCESS
+ *         The operation was aborted successfully.
+ */
+psa_status_t mbedtls_psa_verify_hash_abort(
+    mbedtls_psa_verify_hash_interruptible_operation_t *operation);
+
+typedef struct psa_crypto_local_input_s {
+    uint8_t *buffer;
+    size_t length;
+} psa_crypto_local_input_t;
+
+#define PSA_CRYPTO_LOCAL_INPUT_INIT ((psa_crypto_local_input_t) { NULL, 0 })
+
+/** Allocate a local copy of an input buffer and copy the contents into it.
+ *
+ * \param[in] input             Pointer to input buffer.
+ * \param[in] input_len         Length of the input buffer.
+ * \param[out] local_input      Pointer to a psa_crypto_local_input_t struct
+ *                              containing a local input copy.
+ * \return                      #PSA_SUCCESS, if the buffer was successfully
+ *                              copied.
+ * \return                      #PSA_ERROR_INSUFFICIENT_MEMORY, if a copy of
+ *                              the buffer cannot be allocated.
+ */
+psa_status_t psa_crypto_local_input_alloc(const uint8_t *input, size_t input_len,
+                                          psa_crypto_local_input_t *local_input);
+
+/** Free a local copy of an input buffer.
+ *
+ * \param[in] local_input       Pointer to a psa_crypto_local_input_t struct
+ *                              populated by a previous call to
+ *                              psa_crypto_local_input_alloc().
+ */
+void psa_crypto_local_input_free(psa_crypto_local_input_t *local_input);
+
+typedef struct psa_crypto_local_output_s {
+    uint8_t *original;
+    uint8_t *buffer;
+    size_t length;
+} psa_crypto_local_output_t;
+
+#define PSA_CRYPTO_LOCAL_OUTPUT_INIT ((psa_crypto_local_output_t) { NULL, NULL, 0 })
+
+/** Allocate a local copy of an output buffer.
+ *
+ * \note                        This does not copy any data from the original
+ *                              output buffer but only allocates a buffer
+ *                              whose contents will be copied back to the
+ *                              original in a future call to
+ *                              psa_crypto_local_output_free().
+ *
+ * \param[in] output            Pointer to output buffer.
+ * \param[in] output_len        Length of the output buffer.
+ * \param[out] local_output     Pointer to a psa_crypto_local_output_t struct to
+ *                              populate with the local output copy.
+ * \return                      #PSA_SUCCESS, if the buffer was successfully
+ *                              copied.
+ * \return                      #PSA_ERROR_INSUFFICIENT_MEMORY, if a copy of
+ *                              the buffer cannot be allocated.
+ */
+psa_status_t psa_crypto_local_output_alloc(uint8_t *output, size_t output_len,
+                                           psa_crypto_local_output_t *local_output);
+
+/** Copy from a local copy of an output buffer back to the original, then
+ *  free the local copy.
+ *
+ * \param[in] local_output      Pointer to a psa_crypto_local_output_t struct
+ *                              populated by a previous call to
+ *                              psa_crypto_local_output_alloc().
+ * \return                      #PSA_SUCCESS, if the local output was
+ *                              successfully copied back to the original.
+ * \return                      #PSA_ERROR_CORRUPTION_DETECTED, if the output
+ *                              could not be copied back to the original.
+ */
+psa_status_t psa_crypto_local_output_free(psa_crypto_local_output_t *local_output);
+
+#endif /* PSA_CRYPTO_CORE_H */
diff --git a/tf-psa-crypto/core/psa_crypto_core_common.h b/tf-psa-crypto/core/psa_crypto_core_common.h
new file mode 100644
index 0000000..98fce2c
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_core_common.h
@@ -0,0 +1,52 @@
+/**
+ * \file psa_crypto_core_common.h
+ *
+ * \brief Utility macros for internal use in the PSA cryptography core.
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_CORE_COMMON_H
+#define PSA_CRYPTO_CORE_COMMON_H
+
+/** Return an offset into a buffer.
+ *
+ * This is just the addition of an offset to a pointer, except that this
+ * function also accepts an offset of 0 into a buffer whose pointer is null.
+ * (`p + n` has undefined behavior when `p` is null, even when `n == 0`.
+ * A null pointer is a valid buffer pointer when the size is 0, for example
+ * as the result of `malloc(0)` on some platforms.)
+ *
+ * \param p     Pointer to a buffer of at least n bytes.
+ *              This may be \p NULL if \p n is zero.
+ * \param n     An offset in bytes.
+ * \return      Pointer to offset \p n in the buffer \p p.
+ *              Note that this is only a valid pointer if the size of the
+ *              buffer is at least \p n + 1.
+ */
+static inline unsigned char *psa_crypto_buffer_offset(
+    unsigned char *p, size_t n)
+{
+    return p == NULL ? NULL : p + n;
+}
+
+/** Return an offset into a read-only buffer.
+ *
+ * Similar to mbedtls_buffer_offset(), but for const pointers.
+ *
+ * \param p     Pointer to a buffer of at least n bytes.
+ *              This may be \p NULL if \p n is zero.
+ * \param n     An offset in bytes.
+ * \return      Pointer to offset \p n in the buffer \p p.
+ *              Note that this is only a valid pointer if the size of the
+ *              buffer is at least \p n + 1.
+ */
+static inline const unsigned char *psa_crypto_buffer_offset_const(
+    const unsigned char *p, size_t n)
+{
+    return p == NULL ? NULL : p + n;
+}
+
+#endif /* PSA_CRYPTO_CORE_COMMON_H */
diff --git a/tf-psa-crypto/core/psa_crypto_driver_wrappers_no_static.h b/tf-psa-crypto/core/psa_crypto_driver_wrappers_no_static.h
new file mode 100644
index 0000000..cd617f6
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_driver_wrappers_no_static.h
@@ -0,0 +1,31 @@
+/*
+ *  Function signatures for functionality that can be provided by
+ *  cryptographic accelerators.
+ */
+/*  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_DRIVER_WRAPPERS_NO_STATIC_H
+#define PSA_CRYPTO_DRIVER_WRAPPERS_NO_STATIC_H
+
+#include "psa/crypto.h"
+#include "psa/crypto_driver_common.h"
+
+psa_status_t psa_driver_wrapper_export_public_key(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    uint8_t *data, size_t data_size, size_t *data_length);
+
+psa_status_t psa_driver_wrapper_get_key_buffer_size(
+    const psa_key_attributes_t *attributes,
+    size_t *key_buffer_size);
+
+psa_status_t psa_driver_wrapper_get_builtin_key(
+    psa_drv_slot_number_t slot_number,
+    psa_key_attributes_t *attributes,
+    uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length);
+
+#endif /* PSA_CRYPTO_DRIVER_WRAPPERS_NO_STATIC_H */
+
+/* End of automatically generated file. */
diff --git a/tf-psa-crypto/core/psa_crypto_ecp.c b/tf-psa-crypto/core/psa_crypto_ecp.c
new file mode 100644
index 0000000..95baff6
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_ecp.c
@@ -0,0 +1,596 @@
+/*
+ *  PSA ECP layer on top of Mbed TLS crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include <psa/crypto.h>
+#include "psa_crypto_core.h"
+#include "psa_crypto_ecp.h"
+#include "psa_crypto_random_impl.h"
+#include "mbedtls/psa_util.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "mbedtls/platform.h"
+
+#include <mbedtls/ecdsa.h>
+#include <mbedtls/ecdh.h>
+#include <mbedtls/ecp.h>
+#include <mbedtls/error.h>
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_BASIC) || \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_IMPORT) || \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_EXPORT) || \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH)
+/* Helper function to verify if the provided EC's family and key bit size are valid.
+ *
+ * Note: "bits" parameter is used both as input and output and it might be updated
+ *       in case provided input value is not multiple of 8 ("sloppy" bits).
+ */
+static int check_ecc_parameters(psa_ecc_family_t family, size_t *bits)
+{
+    switch (family) {
+        case PSA_ECC_FAMILY_SECP_R1:
+            switch (*bits) {
+                case 192:
+                case 224:
+                case 256:
+                case 384:
+                case 521:
+                    return PSA_SUCCESS;
+                case 528:
+                    *bits = 521;
+                    return PSA_SUCCESS;
+            }
+            break;
+
+        case PSA_ECC_FAMILY_BRAINPOOL_P_R1:
+            switch (*bits) {
+                case 256:
+                case 384:
+                case 512:
+                    return PSA_SUCCESS;
+            }
+            break;
+
+        case PSA_ECC_FAMILY_MONTGOMERY:
+            switch (*bits) {
+                case 448:
+                case 255:
+                    return PSA_SUCCESS;
+                case 256:
+                    *bits = 255;
+                    return PSA_SUCCESS;
+            }
+            break;
+
+        case PSA_ECC_FAMILY_SECP_K1:
+            switch (*bits) {
+                case 192:
+                /* secp224k1 is not and will not be supported in PSA (#3541). */
+                case 256:
+                    return PSA_SUCCESS;
+            }
+            break;
+    }
+
+    return PSA_ERROR_INVALID_ARGUMENT;
+}
+
+psa_status_t mbedtls_psa_ecp_load_representation(
+    psa_key_type_t type, size_t curve_bits,
+    const uint8_t *data, size_t data_length,
+    mbedtls_ecp_keypair **p_ecp)
+{
+    mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE;
+    psa_status_t status;
+    mbedtls_ecp_keypair *ecp = NULL;
+    size_t curve_bytes = data_length;
+    int explicit_bits = (curve_bits != 0);
+
+    if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type) &&
+        PSA_KEY_TYPE_ECC_GET_FAMILY(type) != PSA_ECC_FAMILY_MONTGOMERY) {
+        /* A Weierstrass public key is represented as:
+         * - The byte 0x04;
+         * - `x_P` as a `ceiling(m/8)`-byte string, big-endian;
+         * - `y_P` as a `ceiling(m/8)`-byte string, big-endian.
+         * So its data length is 2m+1 where m is the curve size in bits.
+         */
+        if ((data_length & 1) == 0) {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+        curve_bytes = data_length / 2;
+
+        /* Montgomery public keys are represented in compressed format, meaning
+         * their curve_bytes is equal to the amount of input. */
+
+        /* Private keys are represented in uncompressed private random integer
+         * format, meaning their curve_bytes is equal to the amount of input. */
+    }
+
+    if (explicit_bits) {
+        /* With an explicit bit-size, the data must have the matching length. */
+        if (curve_bytes != PSA_BITS_TO_BYTES(curve_bits)) {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+    } else {
+        /* We need to infer the bit-size from the data. Since the only
+         * information we have is the length in bytes, the value of curve_bits
+         * at this stage is rounded up to the nearest multiple of 8. */
+        curve_bits = PSA_BYTES_TO_BITS(curve_bytes);
+    }
+
+    /* Allocate and initialize a key representation. */
+    ecp = mbedtls_calloc(1, sizeof(mbedtls_ecp_keypair));
+    if (ecp == NULL) {
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+    mbedtls_ecp_keypair_init(ecp);
+
+    status = check_ecc_parameters(PSA_KEY_TYPE_ECC_GET_FAMILY(type), &curve_bits);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    /* Load the group. */
+    grp_id = mbedtls_ecc_group_from_psa(PSA_KEY_TYPE_ECC_GET_FAMILY(type),
+                                        curve_bits);
+    if (grp_id == MBEDTLS_ECP_DP_NONE) {
+        status = PSA_ERROR_NOT_SUPPORTED;
+        goto exit;
+    }
+
+    status = mbedtls_to_psa_error(
+        mbedtls_ecp_group_load(&ecp->grp, grp_id));
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    /* Load the key material. */
+    if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type)) {
+        /* Load the public value. */
+        status = mbedtls_to_psa_error(
+            mbedtls_ecp_point_read_binary(&ecp->grp, &ecp->Q,
+                                          data,
+                                          data_length));
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+
+        /* Check that the point is on the curve. */
+        status = mbedtls_to_psa_error(
+            mbedtls_ecp_check_pubkey(&ecp->grp, &ecp->Q));
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+    } else {
+        /* Load and validate the secret value. */
+        status = mbedtls_to_psa_error(
+            mbedtls_ecp_read_key(ecp->grp.id,
+                                 ecp,
+                                 data,
+                                 data_length));
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+    }
+
+    *p_ecp = ecp;
+exit:
+    if (status != PSA_SUCCESS) {
+        mbedtls_ecp_keypair_free(ecp);
+        mbedtls_free(ecp);
+    }
+
+    return status;
+}
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_BASIC) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_IMPORT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_EXPORT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH) */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_IMPORT) || \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_EXPORT) || \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY)
+
+psa_status_t mbedtls_psa_ecp_import_key(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *data, size_t data_length,
+    uint8_t *key_buffer, size_t key_buffer_size,
+    size_t *key_buffer_length, size_t *bits)
+{
+    psa_status_t status;
+    mbedtls_ecp_keypair *ecp = NULL;
+
+    /* Parse input */
+    status = mbedtls_psa_ecp_load_representation(attributes->type,
+                                                 attributes->bits,
+                                                 data,
+                                                 data_length,
+                                                 &ecp);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (PSA_KEY_TYPE_ECC_GET_FAMILY(attributes->type) ==
+        PSA_ECC_FAMILY_MONTGOMERY) {
+        *bits = ecp->grp.nbits + 1;
+    } else {
+        *bits = ecp->grp.nbits;
+    }
+
+    /* Re-export the data to PSA export format. There is currently no support
+     * for other input formats then the export format, so this is a 1-1
+     * copy operation. */
+    status = mbedtls_psa_ecp_export_key(attributes->type,
+                                        ecp,
+                                        key_buffer,
+                                        key_buffer_size,
+                                        key_buffer_length);
+exit:
+    /* Always free the PK object (will also free contained ECP context) */
+    mbedtls_ecp_keypair_free(ecp);
+    mbedtls_free(ecp);
+
+    return status;
+}
+
+psa_status_t mbedtls_psa_ecp_export_key(psa_key_type_t type,
+                                        mbedtls_ecp_keypair *ecp,
+                                        uint8_t *data,
+                                        size_t data_size,
+                                        size_t *data_length)
+{
+    psa_status_t status;
+
+    if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type)) {
+        /* Check whether the public part is loaded */
+        if (mbedtls_ecp_is_zero(&ecp->Q)) {
+            /* Calculate the public key */
+            status = mbedtls_to_psa_error(
+                mbedtls_ecp_mul(&ecp->grp, &ecp->Q, &ecp->d, &ecp->grp.G,
+                                mbedtls_psa_get_random,
+                                MBEDTLS_PSA_RANDOM_STATE));
+            if (status != PSA_SUCCESS) {
+                return status;
+            }
+        }
+
+        status = mbedtls_to_psa_error(
+            mbedtls_ecp_point_write_binary(&ecp->grp, &ecp->Q,
+                                           MBEDTLS_ECP_PF_UNCOMPRESSED,
+                                           data_length,
+                                           data,
+                                           data_size));
+        if (status != PSA_SUCCESS) {
+            memset(data, 0, data_size);
+        }
+
+        return status;
+    } else {
+        status = mbedtls_to_psa_error(
+            mbedtls_ecp_write_key_ext(ecp, data_length, data, data_size));
+        return status;
+    }
+}
+
+psa_status_t mbedtls_psa_ecp_export_public_key(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    uint8_t *data, size_t data_size, size_t *data_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_ecp_keypair *ecp = NULL;
+
+    status = mbedtls_psa_ecp_load_representation(
+        attributes->type, attributes->bits,
+        key_buffer, key_buffer_size, &ecp);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = mbedtls_psa_ecp_export_key(
+        PSA_KEY_TYPE_ECC_PUBLIC_KEY(
+            PSA_KEY_TYPE_ECC_GET_FAMILY(attributes->type)),
+        ecp, data, data_size, data_length);
+
+    mbedtls_ecp_keypair_free(ecp);
+    mbedtls_free(ecp);
+
+    return status;
+}
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_IMPORT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_EXPORT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_GENERATE)
+psa_status_t mbedtls_psa_ecp_generate_key(
+    const psa_key_attributes_t *attributes,
+    uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    psa_ecc_family_t curve = PSA_KEY_TYPE_ECC_GET_FAMILY(
+        attributes->type);
+    mbedtls_ecp_group_id grp_id =
+        mbedtls_ecc_group_from_psa(curve, attributes->bits);
+
+    const mbedtls_ecp_curve_info *curve_info =
+        mbedtls_ecp_curve_info_from_grp_id(grp_id);
+    mbedtls_ecp_keypair ecp;
+
+    if (grp_id == MBEDTLS_ECP_DP_NONE || curve_info == NULL) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    mbedtls_ecp_keypair_init(&ecp);
+    ret = mbedtls_ecp_gen_key(grp_id, &ecp,
+                              mbedtls_psa_get_random,
+                              MBEDTLS_PSA_RANDOM_STATE);
+    if (ret != 0) {
+        mbedtls_ecp_keypair_free(&ecp);
+        return mbedtls_to_psa_error(ret);
+    }
+
+    status = mbedtls_to_psa_error(
+        mbedtls_ecp_write_key_ext(&ecp, key_buffer_length,
+                                  key_buffer, key_buffer_size));
+
+    mbedtls_ecp_keypair_free(&ecp);
+
+    return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_GENERATE */
+
+/****************************************************************/
+/* ECDSA sign/verify */
+/****************************************************************/
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)
+psa_status_t mbedtls_psa_ecdsa_sign_hash(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+    uint8_t *signature, size_t signature_size, size_t *signature_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_ecp_keypair *ecp = NULL;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    size_t curve_bytes;
+    mbedtls_mpi r, s;
+
+    status = mbedtls_psa_ecp_load_representation(attributes->type,
+                                                 attributes->bits,
+                                                 key_buffer,
+                                                 key_buffer_size,
+                                                 &ecp);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    curve_bytes = PSA_BITS_TO_BYTES(ecp->grp.pbits);
+    mbedtls_mpi_init(&r);
+    mbedtls_mpi_init(&s);
+
+    if (signature_size < 2 * curve_bytes) {
+        ret = MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL;
+        goto cleanup;
+    }
+
+    if (PSA_ALG_ECDSA_IS_DETERMINISTIC(alg)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)
+        psa_algorithm_t hash_alg = PSA_ALG_SIGN_GET_HASH(alg);
+        mbedtls_md_type_t md_alg = mbedtls_md_type_from_psa_alg(hash_alg);
+        MBEDTLS_MPI_CHK(mbedtls_ecdsa_sign_det_ext(
+                            &ecp->grp, &r, &s,
+                            &ecp->d, hash,
+                            hash_length, md_alg,
+                            mbedtls_psa_get_random,
+                            MBEDTLS_PSA_RANDOM_STATE));
+#else
+        ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE;
+        goto cleanup;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */
+    } else {
+        (void) alg;
+        MBEDTLS_MPI_CHK(mbedtls_ecdsa_sign(&ecp->grp, &r, &s, &ecp->d,
+                                           hash, hash_length,
+                                           mbedtls_psa_get_random,
+                                           MBEDTLS_PSA_RANDOM_STATE));
+    }
+
+    MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&r,
+                                             signature,
+                                             curve_bytes));
+    MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&s,
+                                             signature + curve_bytes,
+                                             curve_bytes));
+cleanup:
+    mbedtls_mpi_free(&r);
+    mbedtls_mpi_free(&s);
+    if (ret == 0) {
+        *signature_length = 2 * curve_bytes;
+    }
+
+    mbedtls_ecp_keypair_free(ecp);
+    mbedtls_free(ecp);
+
+    return mbedtls_to_psa_error(ret);
+}
+
+psa_status_t mbedtls_psa_ecp_load_public_part(mbedtls_ecp_keypair *ecp)
+{
+    int ret = 0;
+
+    /* Check whether the public part is loaded. If not, load it. */
+    if (mbedtls_ecp_is_zero(&ecp->Q)) {
+        ret = mbedtls_ecp_mul(&ecp->grp, &ecp->Q,
+                              &ecp->d, &ecp->grp.G,
+                              mbedtls_psa_get_random,
+                              MBEDTLS_PSA_RANDOM_STATE);
+    }
+
+    return mbedtls_to_psa_error(ret);
+}
+
+psa_status_t mbedtls_psa_ecdsa_verify_hash(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+    const uint8_t *signature, size_t signature_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_ecp_keypair *ecp = NULL;
+    size_t curve_bytes;
+    mbedtls_mpi r, s;
+
+    (void) alg;
+
+    status = mbedtls_psa_ecp_load_representation(attributes->type,
+                                                 attributes->bits,
+                                                 key_buffer,
+                                                 key_buffer_size,
+                                                 &ecp);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    curve_bytes = PSA_BITS_TO_BYTES(ecp->grp.pbits);
+    mbedtls_mpi_init(&r);
+    mbedtls_mpi_init(&s);
+
+    if (signature_length != 2 * curve_bytes) {
+        status = PSA_ERROR_INVALID_SIGNATURE;
+        goto cleanup;
+    }
+
+    status = mbedtls_to_psa_error(mbedtls_mpi_read_binary(&r,
+                                                          signature,
+                                                          curve_bytes));
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+
+    status = mbedtls_to_psa_error(mbedtls_mpi_read_binary(&s,
+                                                          signature + curve_bytes,
+                                                          curve_bytes));
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+
+    status = mbedtls_psa_ecp_load_public_part(ecp);
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+
+    status = mbedtls_to_psa_error(mbedtls_ecdsa_verify(&ecp->grp, hash,
+                                                       hash_length, &ecp->Q,
+                                                       &r, &s));
+cleanup:
+    mbedtls_mpi_free(&r);
+    mbedtls_mpi_free(&s);
+    mbedtls_ecp_keypair_free(ecp);
+    mbedtls_free(ecp);
+
+    return status;
+}
+
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */
+
+/****************************************************************/
+/* ECDH Key Agreement */
+/****************************************************************/
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH)
+psa_status_t mbedtls_psa_key_agreement_ecdh(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *peer_key, size_t peer_key_length,
+    uint8_t *shared_secret, size_t shared_secret_size,
+    size_t *shared_secret_length)
+{
+    psa_status_t status;
+    if (!PSA_KEY_TYPE_IS_ECC_KEY_PAIR(attributes->type) ||
+        !PSA_ALG_IS_ECDH(alg)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+    mbedtls_ecp_keypair *ecp = NULL;
+    status = mbedtls_psa_ecp_load_representation(
+        attributes->type,
+        attributes->bits,
+        key_buffer,
+        key_buffer_size,
+        &ecp);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+    mbedtls_ecp_keypair *their_key = NULL;
+    mbedtls_ecdh_context ecdh;
+    size_t bits = 0;
+    psa_ecc_family_t curve = mbedtls_ecc_group_to_psa(ecp->grp.id, &bits);
+    mbedtls_ecdh_init(&ecdh);
+
+    status = mbedtls_psa_ecp_load_representation(
+        PSA_KEY_TYPE_ECC_PUBLIC_KEY(curve),
+        bits,
+        peer_key,
+        peer_key_length,
+        &their_key);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = mbedtls_to_psa_error(
+        mbedtls_ecdh_get_params(&ecdh, their_key, MBEDTLS_ECDH_THEIRS));
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+    status = mbedtls_to_psa_error(
+        mbedtls_ecdh_get_params(&ecdh, ecp, MBEDTLS_ECDH_OURS));
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = mbedtls_to_psa_error(
+        mbedtls_ecdh_calc_secret(&ecdh,
+                                 shared_secret_length,
+                                 shared_secret, shared_secret_size,
+                                 mbedtls_psa_get_random,
+                                 MBEDTLS_PSA_RANDOM_STATE));
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+    if (PSA_BITS_TO_BYTES(bits) != *shared_secret_length) {
+        status = PSA_ERROR_CORRUPTION_DETECTED;
+    }
+exit:
+    if (status != PSA_SUCCESS) {
+        mbedtls_platform_zeroize(shared_secret, shared_secret_size);
+    }
+    mbedtls_ecdh_free(&ecdh);
+    mbedtls_ecp_keypair_free(their_key);
+    mbedtls_free(their_key);
+    mbedtls_ecp_keypair_free(ecp);
+    mbedtls_free(ecp);
+    return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_ECDH */
+
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/tf-psa-crypto/core/psa_crypto_ecp.h b/tf-psa-crypto/core/psa_crypto_ecp.h
new file mode 100644
index 0000000..a9f5d59
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_ecp.h
@@ -0,0 +1,267 @@
+/*
+ *  PSA ECP layer on top of Mbed TLS crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_ECP_H
+#define PSA_CRYPTO_ECP_H
+
+#include <psa/crypto.h>
+#include <mbedtls/ecp.h>
+
+/** Load the contents of a key buffer into an internal ECP representation
+ *
+ * \param[in] type          The type of key contained in \p data.
+ * \param[in] curve_bits    The nominal bit-size of the curve.
+ *                          It must be consistent with the representation
+ *                          passed in \p data.
+ *                          This can be 0, in which case the bit-size
+ *                          is inferred from \p data_length (which is possible
+ *                          for all key types and representation formats
+ *                          formats that are currently supported or will
+ *                          be in the foreseeable future).
+ * \param[in] data          The buffer from which to load the representation.
+ * \param[in] data_length   The size in bytes of \p data.
+ * \param[out] p_ecp        Returns a pointer to an ECP context on success.
+ *                          The caller is responsible for freeing both the
+ *                          contents of the context and the context itself
+ *                          when done.
+ */
+psa_status_t mbedtls_psa_ecp_load_representation(psa_key_type_t type,
+                                                 size_t curve_bits,
+                                                 const uint8_t *data,
+                                                 size_t data_length,
+                                                 mbedtls_ecp_keypair **p_ecp);
+
+/** Load the public part of an internal ECP, if required.
+ *
+ * \param ecp               The ECP context to load the public part for.
+ *
+ * \return PSA_SUCCESS on success, otherwise an MPI error.
+ */
+
+psa_status_t mbedtls_psa_ecp_load_public_part(mbedtls_ecp_keypair *ecp);
+
+/** Import an ECP key in binary format.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       import_key entry point. This function behaves as an import_key
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in]  attributes       The attributes for the key to import.
+ * \param[in]  data             The buffer containing the key data in import
+ *                              format.
+ * \param[in]  data_length      Size of the \p data buffer in bytes.
+ * \param[out] key_buffer       The buffer containing the key data in output
+ *                              format.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes. This
+ *                              size is greater or equal to \p data_length.
+ * \param[out] key_buffer_length  The length of the data written in \p
+ *                                key_buffer in bytes.
+ * \param[out] bits             The key size in number of bits.
+ *
+ * \retval #PSA_SUCCESS  The ECP key was imported successfully.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         The key data is not correctly formatted.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_ecp_import_key(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *data, size_t data_length,
+    uint8_t *key_buffer, size_t key_buffer_size,
+    size_t *key_buffer_length, size_t *bits);
+
+/** Export an ECP key to export representation
+ *
+ * \param[in] type          The type of key (public/private) to export
+ * \param[in] ecp           The internal ECP representation from which to export
+ * \param[out] data         The buffer to export to
+ * \param[in] data_size     The length of the buffer to export to
+ * \param[out] data_length  The amount of bytes written to \p data
+ */
+psa_status_t mbedtls_psa_ecp_export_key(psa_key_type_t type,
+                                        mbedtls_ecp_keypair *ecp,
+                                        uint8_t *data,
+                                        size_t data_size,
+                                        size_t *data_length);
+
+/** Export an ECP public key or the public part of an ECP key pair in binary
+ *  format.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       export_public_key entry point. This function behaves as an
+ *       export_public_key entry point as defined in the PSA driver interface
+ *       specification.
+ *
+ * \param[in]  attributes       The attributes for the key to export.
+ * \param[in]  key_buffer       Material or context of the key to export.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[out] data             Buffer where the key data is to be written.
+ * \param[in]  data_size        Size of the \p data buffer in bytes.
+ * \param[out] data_length      On success, the number of bytes written in
+ *                              \p data
+ *
+ * \retval #PSA_SUCCESS  The ECP public key was exported successfully.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription
+ * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ */
+psa_status_t mbedtls_psa_ecp_export_public_key(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    uint8_t *data, size_t data_size, size_t *data_length);
+
+/**
+ * \brief Generate an ECP key.
+ *
+ * \note The signature of the function is that of a PSA driver generate_key
+ *       entry point.
+ *
+ * \param[in]  attributes         The attributes for the ECP key to generate.
+ * \param[out] key_buffer         Buffer where the key data is to be written.
+ * \param[in]  key_buffer_size    Size of \p key_buffer in bytes.
+ * \param[out] key_buffer_length  On success, the number of bytes written in
+ *                                \p key_buffer.
+ *
+ * \retval #PSA_SUCCESS
+ *         The key was successfully generated.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         Key length or type not supported.
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of \p key_buffer is too small.
+ */
+psa_status_t mbedtls_psa_ecp_generate_key(
+    const psa_key_attributes_t *attributes,
+    uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length);
+
+/** Sign an already-calculated hash with ECDSA.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       sign_hash entry point. This function behaves as a sign_hash
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in]  attributes       The attributes of the ECC key to use for the
+ *                              operation.
+ * \param[in]  key_buffer       The buffer containing the ECC key context.
+ *                              format.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[in]  alg              Randomized or deterministic ECDSA algorithm.
+ * \param[in]  hash             The hash or message to sign.
+ * \param[in]  hash_length      Size of the \p hash buffer in bytes.
+ * \param[out] signature        Buffer where the signature is to be written.
+ * \param[in]  signature_size   Size of the \p signature buffer in bytes.
+ * \param[out] signature_length On success, the number of bytes
+ *                              that make up the returned signature value.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p signature buffer is too small. You can
+ *         determine a sufficient buffer size by calling
+ *         #PSA_SIGN_OUTPUT_SIZE(\c PSA_KEY_TYPE_ECC_KEY_PAIR, \c key_bits,
+ *         \p alg) where \c key_bits is the bit-size of the ECC key.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription
+ */
+psa_status_t mbedtls_psa_ecdsa_sign_hash(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+    uint8_t *signature, size_t signature_size, size_t *signature_length);
+
+/**
+ * \brief Verify an ECDSA hash or short message signature.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       verify_hash entry point. This function behaves as a verify_hash
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in]  attributes       The attributes of the ECC key to use for the
+ *                              operation.
+ * \param[in]  key_buffer       The buffer containing the ECC key context.
+ *                              format.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[in]  alg              Randomized or deterministic ECDSA algorithm.
+ * \param[in]  hash             The hash or message whose signature is to be
+ *                              verified.
+ * \param[in]  hash_length      Size of the \p hash buffer in bytes.
+ * \param[in]  signature        Buffer containing the signature to verify.
+ * \param[in]  signature_length Size of the \p signature buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ *         The signature is valid.
+ * \retval #PSA_ERROR_INVALID_SIGNATURE
+ *         The calculation was performed successfully, but the passed
+ *         signature is not a valid signature.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ */
+psa_status_t mbedtls_psa_ecdsa_verify_hash(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+    const uint8_t *signature, size_t signature_length);
+
+
+/** Perform a key agreement and return the raw ECDH shared secret.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       key_agreement entry point. This function behaves as a key_agreement
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in]  attributes           The attributes of the key to use for the
+ *                                  operation.
+ * \param[in]  key_buffer           The buffer containing the private key
+ *                                  context.
+ * \param[in]  key_buffer_size      Size of the \p key_buffer buffer in
+ *                                  bytes.
+ * \param[in]  alg                  A key agreement algorithm that is
+ *                                  compatible with the type of the key.
+ * \param[in]  peer_key             The buffer containing the key context
+ *                                  of the peer's public key.
+ * \param[in]  peer_key_length      Size of the \p peer_key buffer in
+ *                                  bytes.
+ * \param[out] shared_secret        The buffer to which the shared secret
+ *                                  is to be written.
+ * \param[in]  shared_secret_size   Size of the \p shared_secret buffer in
+ *                                  bytes.
+ * \param[out] shared_secret_length On success, the number of bytes that make
+ *                                  up the returned shared secret.
+ * \retval #PSA_SUCCESS
+ *         Success. Shared secret successfully calculated.
+ * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription
+ * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         \p alg is not a key agreement algorithm, or
+ *         \p private_key is not compatible with \p alg,
+ *         or \p peer_key is not valid for \p alg or not compatible with
+ *         \p private_key.
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         \p shared_secret_size is too small
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         \p alg is not a supported key agreement algorithm.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_key_agreement_ecdh(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *peer_key, size_t peer_key_length,
+    uint8_t *shared_secret, size_t shared_secret_size,
+    size_t *shared_secret_length);
+#endif /* PSA_CRYPTO_ECP_H */
diff --git a/tf-psa-crypto/core/psa_crypto_ffdh.c b/tf-psa-crypto/core/psa_crypto_ffdh.c
new file mode 100644
index 0000000..ae38f6d
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_ffdh.c
@@ -0,0 +1,321 @@
+/*
+ *  PSA FFDH layer on top of Mbed TLS crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+/* This header is only needed because it defines
+ * MBEDTLS_DHM_RFC7919_FFDHEXXXX_[P|G]_BIN symbols that are used in
+ * mbedtls_psa_ffdh_set_prime_generator(). Apart from that, this module
+ * only uses bignum functions for arithmetic. */
+#include <mbedtls/dhm.h>
+
+#include <psa/crypto.h>
+#include "psa_crypto_core.h"
+#include "psa_crypto_ffdh.h"
+#include "psa_crypto_random_impl.h"
+#include "mbedtls/platform.h"
+#include "mbedtls/error.h"
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_EXPORT) ||   \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_GENERATE) ||   \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_FFDH)
+static psa_status_t mbedtls_psa_ffdh_set_prime_generator(size_t key_size,
+                                                         mbedtls_mpi *P,
+                                                         mbedtls_mpi *G)
+{
+    const unsigned char *dhm_P = NULL;
+    const unsigned char *dhm_G = NULL;
+    size_t dhm_size_P = 0;
+    size_t dhm_size_G = 0;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    if (P == NULL && G == NULL) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_2048)
+    static const unsigned char dhm_P_2048[] =
+        MBEDTLS_DHM_RFC7919_FFDHE2048_P_BIN;
+    static const unsigned char dhm_G_2048[] =
+        MBEDTLS_DHM_RFC7919_FFDHE2048_G_BIN;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_2048 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_3072)
+    static const unsigned char dhm_P_3072[] =
+        MBEDTLS_DHM_RFC7919_FFDHE3072_P_BIN;
+    static const unsigned char dhm_G_3072[] =
+        MBEDTLS_DHM_RFC7919_FFDHE3072_G_BIN;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_3072 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_4096)
+    static const unsigned char dhm_P_4096[] =
+        MBEDTLS_DHM_RFC7919_FFDHE4096_P_BIN;
+    static const unsigned char dhm_G_4096[] =
+        MBEDTLS_DHM_RFC7919_FFDHE4096_G_BIN;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_4096 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_6144)
+    static const unsigned char dhm_P_6144[] =
+        MBEDTLS_DHM_RFC7919_FFDHE6144_P_BIN;
+    static const unsigned char dhm_G_6144[] =
+        MBEDTLS_DHM_RFC7919_FFDHE6144_G_BIN;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_6144 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_8192)
+    static const unsigned char dhm_P_8192[] =
+        MBEDTLS_DHM_RFC7919_FFDHE8192_P_BIN;
+    static const unsigned char dhm_G_8192[] =
+        MBEDTLS_DHM_RFC7919_FFDHE8192_G_BIN;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_8192 */
+
+    switch (key_size) {
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_2048)
+        case sizeof(dhm_P_2048):
+            dhm_P = dhm_P_2048;
+            dhm_G = dhm_G_2048;
+            dhm_size_P = sizeof(dhm_P_2048);
+            dhm_size_G = sizeof(dhm_G_2048);
+            break;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_2048 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_3072)
+        case sizeof(dhm_P_3072):
+            dhm_P = dhm_P_3072;
+            dhm_G = dhm_G_3072;
+            dhm_size_P = sizeof(dhm_P_3072);
+            dhm_size_G = sizeof(dhm_G_3072);
+            break;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_3072 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_4096)
+        case sizeof(dhm_P_4096):
+            dhm_P = dhm_P_4096;
+            dhm_G = dhm_G_4096;
+            dhm_size_P = sizeof(dhm_P_4096);
+            dhm_size_G = sizeof(dhm_G_4096);
+            break;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_4096 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_6144)
+        case sizeof(dhm_P_6144):
+            dhm_P = dhm_P_6144;
+            dhm_G = dhm_G_6144;
+            dhm_size_P = sizeof(dhm_P_6144);
+            dhm_size_G = sizeof(dhm_G_6144);
+            break;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_6144 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_8192)
+        case sizeof(dhm_P_8192):
+            dhm_P = dhm_P_8192;
+            dhm_G = dhm_G_8192;
+            dhm_size_P = sizeof(dhm_P_8192);
+            dhm_size_G = sizeof(dhm_G_8192);
+            break;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_8192 */
+        default:
+            return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (P != NULL) {
+        MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(P, dhm_P,
+                                                dhm_size_P));
+    }
+    if (G != NULL) {
+        MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(G, dhm_G,
+                                                dhm_size_G));
+    }
+
+cleanup:
+    if (ret != 0) {
+        return mbedtls_to_psa_error(ret);
+    }
+
+    return PSA_SUCCESS;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_EXPORT ||
+          MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_GENERATE ||
+          MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY ||
+          MBEDTLS_PSA_BUILTIN_ALG_FFDH */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_EXPORT) || \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY)
+psa_status_t mbedtls_psa_ffdh_export_public_key(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    uint8_t *data,
+    size_t data_size,
+    size_t *data_length)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_mpi GX, G, X, P;
+    psa_key_type_t type = attributes->type;
+
+    if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type)) {
+        if (key_buffer_size > data_size) {
+            return PSA_ERROR_BUFFER_TOO_SMALL;
+        }
+        memcpy(data, key_buffer, key_buffer_size);
+        memset(data + key_buffer_size, 0,
+               data_size - key_buffer_size);
+        *data_length = key_buffer_size;
+        return PSA_SUCCESS;
+    }
+
+    mbedtls_mpi_init(&GX); mbedtls_mpi_init(&G);
+    mbedtls_mpi_init(&X); mbedtls_mpi_init(&P);
+
+    size_t key_len = PSA_BITS_TO_BYTES(attributes->bits);
+
+    status = mbedtls_psa_ffdh_set_prime_generator(key_len, &P, &G);
+
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+
+    MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&X, key_buffer,
+                                            key_buffer_size));
+
+    MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&GX, &G, &X, &P, NULL));
+    MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&GX, data, key_len));
+
+    *data_length = key_len;
+
+    ret = 0;
+cleanup:
+    mbedtls_mpi_free(&P); mbedtls_mpi_free(&G);
+    mbedtls_mpi_free(&X); mbedtls_mpi_free(&GX);
+
+    if (status == PSA_SUCCESS && ret != 0) {
+        status = mbedtls_to_psa_error(ret);
+    }
+
+    return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_EXPORT ||
+          MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_GENERATE)
+psa_status_t mbedtls_psa_ffdh_generate_key(
+    const psa_key_attributes_t *attributes,
+    uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length)
+{
+    mbedtls_mpi X, P;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_mpi_init(&P); mbedtls_mpi_init(&X);
+    (void) attributes;
+
+    status = mbedtls_psa_ffdh_set_prime_generator(key_buffer_size, &P, NULL);
+
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+
+    /* RFC7919: Traditional finite field Diffie-Hellman has each peer choose their
+        secret exponent from the range [2, P-2].
+        Select random value in range [3, P-1] and decrease it by 1. */
+    MBEDTLS_MPI_CHK(mbedtls_mpi_random(&X, 3, &P, mbedtls_psa_get_random,
+                                       MBEDTLS_PSA_RANDOM_STATE));
+    MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&X, &X, 1));
+    MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&X, key_buffer, key_buffer_size));
+    *key_buffer_length = key_buffer_size;
+
+cleanup:
+    mbedtls_mpi_free(&P); mbedtls_mpi_free(&X);
+    if (status == PSA_SUCCESS && ret != 0) {
+        return mbedtls_to_psa_error(ret);
+    }
+
+    return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_GENERATE */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_IMPORT)
+psa_status_t mbedtls_psa_ffdh_import_key(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *data, size_t data_length,
+    uint8_t *key_buffer, size_t key_buffer_size,
+    size_t *key_buffer_length, size_t *bits)
+{
+    (void) attributes;
+
+    if (key_buffer_size < data_length) {
+        return PSA_ERROR_BUFFER_TOO_SMALL;
+    }
+    memcpy(key_buffer, data, data_length);
+    *key_buffer_length = data_length;
+    *bits = PSA_BYTES_TO_BITS(data_length);
+
+    return PSA_SUCCESS;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_IMPORT */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_FFDH)
+psa_status_t mbedtls_psa_ffdh_key_agreement(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *peer_key,
+    size_t peer_key_length,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    uint8_t *shared_secret,
+    size_t shared_secret_size,
+    size_t *shared_secret_length)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_mpi P, G, X, GY, K;
+    const size_t calculated_shared_secret_size = peer_key_length;
+
+    if (peer_key_length != key_buffer_size ||
+        calculated_shared_secret_size > shared_secret_size) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (!PSA_KEY_TYPE_IS_DH_KEY_PAIR(psa_get_key_type(attributes))) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    mbedtls_mpi_init(&P); mbedtls_mpi_init(&G);
+    mbedtls_mpi_init(&X); mbedtls_mpi_init(&GY);
+    mbedtls_mpi_init(&K);
+
+    status = mbedtls_psa_ffdh_set_prime_generator(
+        PSA_BITS_TO_BYTES(attributes->bits), &P, &G);
+
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+
+    MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&X, key_buffer,
+                                            key_buffer_size));
+
+    MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&GY, peer_key,
+                                            peer_key_length));
+
+    /* Calculate shared secret public key: K = G^(XY) mod P = GY^X mod P */
+    MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&K, &GY, &X, &P, NULL));
+
+    MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&K, shared_secret,
+                                             calculated_shared_secret_size));
+
+    *shared_secret_length = calculated_shared_secret_size;
+
+    ret = 0;
+
+cleanup:
+    mbedtls_mpi_free(&P); mbedtls_mpi_free(&G);
+    mbedtls_mpi_free(&X); mbedtls_mpi_free(&GY);
+    mbedtls_mpi_free(&K);
+
+    if (status == PSA_SUCCESS && ret != 0) {
+        status = mbedtls_to_psa_error(ret);
+    }
+
+    return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_FFDH */
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/tf-psa-crypto/core/psa_crypto_ffdh.h b/tf-psa-crypto/core/psa_crypto_ffdh.h
new file mode 100644
index 0000000..79accd1
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_ffdh.h
@@ -0,0 +1,131 @@
+/*
+ *  PSA FFDH layer on top of Mbed TLS crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_FFDH_H
+#define PSA_CRYPTO_FFDH_H
+
+#include <psa/crypto.h>
+
+/** Perform a key agreement and return the FFDH shared secret.
+ *
+ * \param[in]  attributes           The attributes of the key to use for the
+ *                                  operation.
+ * \param[in]  peer_key             The buffer containing the key context
+ *                                  of the peer's public key.
+ * \param[in]  peer_key_length      Size of the \p peer_key buffer in
+ *                                  bytes.
+ * \param[in]  key_buffer           The buffer containing the private key
+ *                                  context.
+ * \param[in]  key_buffer_size      Size of the \p key_buffer buffer in
+ *                                  bytes.
+ * \param[out] shared_secret        The buffer to which the shared secret
+ *                                  is to be written.
+ * \param[in]  shared_secret_size   Size of the \p shared_secret buffer in
+ *                                  bytes.
+ * \param[out] shared_secret_length On success, the number of bytes that make
+ *                                  up the returned shared secret.
+ * \retval #PSA_SUCCESS
+ *         Success. Shared secret successfully calculated.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         \p key_buffer_size, \p peer_key_length, \p shared_secret_size
+ *         do not match
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY   \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED   \emptydescription
+ */
+psa_status_t mbedtls_psa_ffdh_key_agreement(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *peer_key,
+    size_t peer_key_length,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    uint8_t *shared_secret,
+    size_t shared_secret_size,
+    size_t *shared_secret_length);
+
+/** Export a public key or the public part of a DH key pair in binary format.
+ *
+ * \param[in]  attributes       The attributes for the key to export.
+ * \param[in]  key_buffer       Material or context of the key to export.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[out] data             Buffer where the key data is to be written.
+ * \param[in]  data_size        Size of the \p data buffer in bytes.
+ * \param[out] data_length      On success, the number of bytes written in
+ *                              \p data
+ *
+ * \retval #PSA_SUCCESS  The public key was exported successfully.
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of \p key_buffer is too small.
+ * \retval #PSA_ERROR_NOT_PERMITTED         \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY   \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED   \emptydescription
+ */
+psa_status_t mbedtls_psa_ffdh_export_public_key(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    uint8_t *data,
+    size_t data_size,
+    size_t *data_length);
+
+/**
+ * \brief Generate DH key.
+ *
+ * \note The signature of the function is that of a PSA driver generate_key
+ *       entry point.
+ *
+ * \param[in]  attributes         The attributes for the key to generate.
+ * \param[out] key_buffer         Buffer where the key data is to be written.
+ * \param[in]  key_buffer_size    Size of \p key_buffer in bytes.
+ * \param[out] key_buffer_length  On success, the number of bytes written in
+ *                                \p key_buffer.
+ *
+ * \retval #PSA_SUCCESS
+ *         The key was generated successfully.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         Key size in bits is invalid.
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of \p key_buffer is too small.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY   \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED   \emptydescription
+ */
+psa_status_t mbedtls_psa_ffdh_generate_key(
+    const psa_key_attributes_t *attributes,
+    uint8_t *key_buffer,
+    size_t key_buffer_size,
+    size_t *key_buffer_length);
+
+/**
+ * \brief Import DH key.
+ *
+ * \note The signature of the function is that of a PSA driver import_key
+ *       entry point.
+ *
+ * \param[in]  attributes       The attributes for the key to import.
+ * \param[in]  data             The buffer containing the key data in import
+ *                              format.
+ * \param[in]  data_length      Size of the \p data buffer in bytes.
+ * \param[out] key_buffer       The buffer containing the key data in output
+ *                              format.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes. This
+ *                              size is greater or equal to \p data_length.
+ * \param[out] key_buffer_length  The length of the data written in \p
+ *                                key_buffer in bytes.
+ * \param[out] bits             The key size in number of bits.
+ *
+ * \retval #PSA_SUCCESS
+ *         The key was generated successfully.
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of \p key_buffer is too small.
+ */
+psa_status_t mbedtls_psa_ffdh_import_key(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *data, size_t data_length,
+    uint8_t *key_buffer, size_t key_buffer_size,
+    size_t *key_buffer_length, size_t *bits);
+
+#endif /* PSA_CRYPTO_FFDH_H */
diff --git a/tf-psa-crypto/core/psa_crypto_hash.c b/tf-psa-crypto/core/psa_crypto_hash.c
new file mode 100644
index 0000000..eeb7666
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_hash.c
@@ -0,0 +1,470 @@
+/*
+ *  PSA hashing layer on top of Mbed TLS software crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include <psa/crypto.h>
+#include "psa_crypto_core.h"
+#include "psa_crypto_hash.h"
+
+#include <mbedtls/error.h>
+#include <string.h>
+
+#if defined(MBEDTLS_PSA_BUILTIN_HASH)
+psa_status_t mbedtls_psa_hash_abort(
+    mbedtls_psa_hash_operation_t *operation)
+{
+    switch (operation->alg) {
+        case 0:
+            /* The object has (apparently) been initialized but it is not
+             * in use. It's ok to call abort on such an object, and there's
+             * nothing to do. */
+            break;
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5)
+        case PSA_ALG_MD5:
+            mbedtls_md5_free(&operation->ctx.md5);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160)
+        case PSA_ALG_RIPEMD160:
+            mbedtls_ripemd160_free(&operation->ctx.ripemd160);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1)
+        case PSA_ALG_SHA_1:
+            mbedtls_sha1_free(&operation->ctx.sha1);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224)
+        case PSA_ALG_SHA_224:
+            mbedtls_sha256_free(&operation->ctx.sha256);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256)
+        case PSA_ALG_SHA_256:
+            mbedtls_sha256_free(&operation->ctx.sha256);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384)
+        case PSA_ALG_SHA_384:
+            mbedtls_sha512_free(&operation->ctx.sha512);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512)
+        case PSA_ALG_SHA_512:
+            mbedtls_sha512_free(&operation->ctx.sha512);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224)
+        case PSA_ALG_SHA3_224:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256)
+        case PSA_ALG_SHA3_256:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384)
+        case PSA_ALG_SHA3_384:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+        case PSA_ALG_SHA3_512:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224) || \
+            defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256) || \
+            defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384) || \
+            defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+            mbedtls_sha3_free(&operation->ctx.sha3);
+            break;
+#endif
+        default:
+            return PSA_ERROR_BAD_STATE;
+    }
+    operation->alg = 0;
+    return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_hash_setup(
+    mbedtls_psa_hash_operation_t *operation,
+    psa_algorithm_t alg)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    /* A context must be freshly initialized before it can be set up. */
+    if (operation->alg != 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    switch (alg) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5)
+        case PSA_ALG_MD5:
+            mbedtls_md5_init(&operation->ctx.md5);
+            ret = mbedtls_md5_starts(&operation->ctx.md5);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160)
+        case PSA_ALG_RIPEMD160:
+            mbedtls_ripemd160_init(&operation->ctx.ripemd160);
+            ret = mbedtls_ripemd160_starts(&operation->ctx.ripemd160);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1)
+        case PSA_ALG_SHA_1:
+            mbedtls_sha1_init(&operation->ctx.sha1);
+            ret = mbedtls_sha1_starts(&operation->ctx.sha1);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224)
+        case PSA_ALG_SHA_224:
+            mbedtls_sha256_init(&operation->ctx.sha256);
+            ret = mbedtls_sha256_starts(&operation->ctx.sha256, 1);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256)
+        case PSA_ALG_SHA_256:
+            mbedtls_sha256_init(&operation->ctx.sha256);
+            ret = mbedtls_sha256_starts(&operation->ctx.sha256, 0);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384)
+        case PSA_ALG_SHA_384:
+            mbedtls_sha512_init(&operation->ctx.sha512);
+            ret = mbedtls_sha512_starts(&operation->ctx.sha512, 1);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512)
+        case PSA_ALG_SHA_512:
+            mbedtls_sha512_init(&operation->ctx.sha512);
+            ret = mbedtls_sha512_starts(&operation->ctx.sha512, 0);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224)
+        case PSA_ALG_SHA3_224:
+            mbedtls_sha3_init(&operation->ctx.sha3);
+            ret = mbedtls_sha3_starts(&operation->ctx.sha3, MBEDTLS_SHA3_224);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256)
+        case PSA_ALG_SHA3_256:
+            mbedtls_sha3_init(&operation->ctx.sha3);
+            ret = mbedtls_sha3_starts(&operation->ctx.sha3, MBEDTLS_SHA3_256);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384)
+        case PSA_ALG_SHA3_384:
+            mbedtls_sha3_init(&operation->ctx.sha3);
+            ret = mbedtls_sha3_starts(&operation->ctx.sha3, MBEDTLS_SHA3_384);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+        case PSA_ALG_SHA3_512:
+            mbedtls_sha3_init(&operation->ctx.sha3);
+            ret = mbedtls_sha3_starts(&operation->ctx.sha3, MBEDTLS_SHA3_512);
+            break;
+#endif
+        default:
+            return PSA_ALG_IS_HASH(alg) ?
+                   PSA_ERROR_NOT_SUPPORTED :
+                   PSA_ERROR_INVALID_ARGUMENT;
+    }
+    if (ret == 0) {
+        operation->alg = alg;
+    } else {
+        mbedtls_psa_hash_abort(operation);
+    }
+    return mbedtls_to_psa_error(ret);
+}
+
+psa_status_t mbedtls_psa_hash_clone(
+    const mbedtls_psa_hash_operation_t *source_operation,
+    mbedtls_psa_hash_operation_t *target_operation)
+{
+    switch (source_operation->alg) {
+        case 0:
+            return PSA_ERROR_BAD_STATE;
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5)
+        case PSA_ALG_MD5:
+            mbedtls_md5_clone(&target_operation->ctx.md5,
+                              &source_operation->ctx.md5);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160)
+        case PSA_ALG_RIPEMD160:
+            mbedtls_ripemd160_clone(&target_operation->ctx.ripemd160,
+                                    &source_operation->ctx.ripemd160);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1)
+        case PSA_ALG_SHA_1:
+            mbedtls_sha1_clone(&target_operation->ctx.sha1,
+                               &source_operation->ctx.sha1);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224)
+        case PSA_ALG_SHA_224:
+            mbedtls_sha256_clone(&target_operation->ctx.sha256,
+                                 &source_operation->ctx.sha256);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256)
+        case PSA_ALG_SHA_256:
+            mbedtls_sha256_clone(&target_operation->ctx.sha256,
+                                 &source_operation->ctx.sha256);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384)
+        case PSA_ALG_SHA_384:
+            mbedtls_sha512_clone(&target_operation->ctx.sha512,
+                                 &source_operation->ctx.sha512);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512)
+        case PSA_ALG_SHA_512:
+            mbedtls_sha512_clone(&target_operation->ctx.sha512,
+                                 &source_operation->ctx.sha512);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224)
+        case PSA_ALG_SHA3_224:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256)
+        case PSA_ALG_SHA3_256:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384)
+        case PSA_ALG_SHA3_384:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+        case PSA_ALG_SHA3_512:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224) || \
+            defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256) || \
+            defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384) || \
+            defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+            mbedtls_sha3_clone(&target_operation->ctx.sha3,
+                               &source_operation->ctx.sha3);
+            break;
+#endif
+        default:
+            (void) source_operation;
+            (void) target_operation;
+            return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    target_operation->alg = source_operation->alg;
+    return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_hash_update(
+    mbedtls_psa_hash_operation_t *operation,
+    const uint8_t *input,
+    size_t input_length)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    switch (operation->alg) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5)
+        case PSA_ALG_MD5:
+            ret = mbedtls_md5_update(&operation->ctx.md5,
+                                     input, input_length);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160)
+        case PSA_ALG_RIPEMD160:
+            ret = mbedtls_ripemd160_update(&operation->ctx.ripemd160,
+                                           input, input_length);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1)
+        case PSA_ALG_SHA_1:
+            ret = mbedtls_sha1_update(&operation->ctx.sha1,
+                                      input, input_length);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224)
+        case PSA_ALG_SHA_224:
+            ret = mbedtls_sha256_update(&operation->ctx.sha256,
+                                        input, input_length);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256)
+        case PSA_ALG_SHA_256:
+            ret = mbedtls_sha256_update(&operation->ctx.sha256,
+                                        input, input_length);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384)
+        case PSA_ALG_SHA_384:
+            ret = mbedtls_sha512_update(&operation->ctx.sha512,
+                                        input, input_length);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512)
+        case PSA_ALG_SHA_512:
+            ret = mbedtls_sha512_update(&operation->ctx.sha512,
+                                        input, input_length);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224)
+        case PSA_ALG_SHA3_224:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256)
+        case PSA_ALG_SHA3_256:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384)
+        case PSA_ALG_SHA3_384:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+        case PSA_ALG_SHA3_512:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+    ret = mbedtls_sha3_update(&operation->ctx.sha3,
+                              input, input_length);
+    break;
+#endif
+        default:
+            (void) input;
+            (void) input_length;
+            return PSA_ERROR_BAD_STATE;
+    }
+
+    return mbedtls_to_psa_error(ret);
+}
+
+psa_status_t mbedtls_psa_hash_finish(
+    mbedtls_psa_hash_operation_t *operation,
+    uint8_t *hash,
+    size_t hash_size,
+    size_t *hash_length)
+{
+    psa_status_t status;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    size_t actual_hash_length = PSA_HASH_LENGTH(operation->alg);
+
+    /* Fill the output buffer with something that isn't a valid hash
+     * (barring an attack on the hash and deliberately-crafted input),
+     * in case the caller doesn't check the return status properly. */
+    *hash_length = hash_size;
+    /* If hash_size is 0 then hash may be NULL and then the
+     * call to memset would have undefined behavior. */
+    if (hash_size != 0) {
+        memset(hash, '!', hash_size);
+    }
+
+    if (hash_size < actual_hash_length) {
+        status = PSA_ERROR_BUFFER_TOO_SMALL;
+        goto exit;
+    }
+
+    switch (operation->alg) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5)
+        case PSA_ALG_MD5:
+            ret = mbedtls_md5_finish(&operation->ctx.md5, hash);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160)
+        case PSA_ALG_RIPEMD160:
+            ret = mbedtls_ripemd160_finish(&operation->ctx.ripemd160, hash);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1)
+        case PSA_ALG_SHA_1:
+            ret = mbedtls_sha1_finish(&operation->ctx.sha1, hash);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224)
+        case PSA_ALG_SHA_224:
+            ret = mbedtls_sha256_finish(&operation->ctx.sha256, hash);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256)
+        case PSA_ALG_SHA_256:
+            ret = mbedtls_sha256_finish(&operation->ctx.sha256, hash);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384)
+        case PSA_ALG_SHA_384:
+            ret = mbedtls_sha512_finish(&operation->ctx.sha512, hash);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512)
+        case PSA_ALG_SHA_512:
+            ret = mbedtls_sha512_finish(&operation->ctx.sha512, hash);
+            break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224)
+        case PSA_ALG_SHA3_224:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256)
+        case PSA_ALG_SHA3_256:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384)
+        case PSA_ALG_SHA3_384:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+        case PSA_ALG_SHA3_512:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+    ret = mbedtls_sha3_finish(&operation->ctx.sha3, hash, hash_size);
+    break;
+#endif
+        default:
+            (void) hash;
+            return PSA_ERROR_BAD_STATE;
+    }
+    status = mbedtls_to_psa_error(ret);
+
+exit:
+    if (status == PSA_SUCCESS) {
+        *hash_length = actual_hash_length;
+    }
+    return status;
+}
+
+psa_status_t mbedtls_psa_hash_compute(
+    psa_algorithm_t alg,
+    const uint8_t *input,
+    size_t input_length,
+    uint8_t *hash,
+    size_t hash_size,
+    size_t *hash_length)
+{
+    mbedtls_psa_hash_operation_t operation = MBEDTLS_PSA_HASH_OPERATION_INIT;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_status_t abort_status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    *hash_length = hash_size;
+    status = mbedtls_psa_hash_setup(&operation, alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+    status = mbedtls_psa_hash_update(&operation, input, input_length);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+    status = mbedtls_psa_hash_finish(&operation, hash, hash_size, hash_length);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+exit:
+    abort_status = mbedtls_psa_hash_abort(&operation);
+    if (status == PSA_SUCCESS) {
+        return abort_status;
+    } else {
+        return status;
+    }
+
+}
+#endif /* MBEDTLS_PSA_BUILTIN_HASH */
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/tf-psa-crypto/core/psa_crypto_hash.h b/tf-psa-crypto/core/psa_crypto_hash.h
new file mode 100644
index 0000000..0a7be80
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_hash.h
@@ -0,0 +1,211 @@
+/*
+ *  PSA hashing layer on top of Mbed TLS software crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_HASH_H
+#define PSA_CRYPTO_HASH_H
+
+#include <psa/crypto.h>
+
+/** Calculate the hash (digest) of a message using Mbed TLS routines.
+ *
+ * \note The signature of this function is that of a PSA driver hash_compute
+ *       entry point. This function behaves as a hash_compute entry point as
+ *       defined in the PSA driver interface specification for transparent
+ *       drivers.
+ *
+ * \param alg               The hash algorithm to compute (\c PSA_ALG_XXX value
+ *                          such that #PSA_ALG_IS_HASH(\p alg) is true).
+ * \param[in] input         Buffer containing the message to hash.
+ * \param input_length      Size of the \p input buffer in bytes.
+ * \param[out] hash         Buffer where the hash is to be written.
+ * \param hash_size         Size of the \p hash buffer in bytes.
+ * \param[out] hash_length  On success, the number of bytes
+ *                          that make up the hash value. This is always
+ *                          #PSA_HASH_LENGTH(\p alg).
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         \p alg is not supported
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         \p hash_size is too small
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_hash_compute(
+    psa_algorithm_t alg,
+    const uint8_t *input,
+    size_t input_length,
+    uint8_t *hash,
+    size_t hash_size,
+    size_t *hash_length);
+
+/** Set up a multipart hash operation using Mbed TLS routines.
+ *
+ * \note The signature of this function is that of a PSA driver hash_setup
+ *       entry point. This function behaves as a hash_setup entry point as
+ *       defined in the PSA driver interface specification for transparent
+ *       drivers.
+ *
+ * If an error occurs at any step after a call to mbedtls_psa_hash_setup(), the
+ * operation will need to be reset by a call to mbedtls_psa_hash_abort(). The
+ * core may call mbedtls_psa_hash_abort() at any time after the operation
+ * has been initialized.
+ *
+ * After a successful call to mbedtls_psa_hash_setup(), the core must
+ * eventually terminate the operation. The following events terminate an
+ * operation:
+ * - A successful call to mbedtls_psa_hash_finish() or mbedtls_psa_hash_verify().
+ * - A call to mbedtls_psa_hash_abort().
+ *
+ * \param[in,out] operation The operation object to set up. It must have
+ *                          been initialized to all-zero and not yet be in use.
+ * \param alg               The hash algorithm to compute (\c PSA_ALG_XXX value
+ *                          such that #PSA_ALG_IS_HASH(\p alg) is true).
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         \p alg is not supported
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The operation state is not valid (it must be inactive).
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_hash_setup(
+    mbedtls_psa_hash_operation_t *operation,
+    psa_algorithm_t alg);
+
+/** Clone an Mbed TLS hash operation.
+ *
+ * \note The signature of this function is that of a PSA driver hash_clone
+ *       entry point. This function behaves as a hash_clone entry point as
+ *       defined in the PSA driver interface specification for transparent
+ *       drivers.
+ *
+ * This function copies the state of an ongoing hash operation to
+ * a new operation object. In other words, this function is equivalent
+ * to calling mbedtls_psa_hash_setup() on \p target_operation with the same
+ * algorithm that \p source_operation was set up for, then
+ * mbedtls_psa_hash_update() on \p target_operation with the same input that
+ * that was passed to \p source_operation. After this function returns, the
+ * two objects are independent, i.e. subsequent calls involving one of
+ * the objects do not affect the other object.
+ *
+ * \param[in] source_operation      The active hash operation to clone.
+ * \param[in,out] target_operation  The operation object to set up.
+ *                                  It must be initialized but not active.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The \p source_operation state is not valid (it must be active).
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The \p target_operation state is not valid (it must be inactive).
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ */
+psa_status_t mbedtls_psa_hash_clone(
+    const mbedtls_psa_hash_operation_t *source_operation,
+    mbedtls_psa_hash_operation_t *target_operation);
+
+/** Add a message fragment to a multipart Mbed TLS hash operation.
+ *
+ * \note The signature of this function is that of a PSA driver hash_update
+ *       entry point. This function behaves as a hash_update entry point as
+ *       defined in the PSA driver interface specification for transparent
+ *       drivers.
+ *
+ * The application must call mbedtls_psa_hash_setup() before calling this function.
+ *
+ * If this function returns an error status, the operation enters an error
+ * state and must be aborted by calling mbedtls_psa_hash_abort().
+ *
+ * \param[in,out] operation Active hash operation.
+ * \param[in] input         Buffer containing the message fragment to hash.
+ * \param input_length      Size of the \p input buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The operation state is not valid (it must be active).
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_hash_update(
+    mbedtls_psa_hash_operation_t *operation,
+    const uint8_t *input,
+    size_t input_length);
+
+/** Finish the calculation of the Mbed TLS-calculated hash of a message.
+ *
+ * \note The signature of this function is that of a PSA driver hash_finish
+ *       entry point. This function behaves as a hash_finish entry point as
+ *       defined in the PSA driver interface specification for transparent
+ *       drivers.
+ *
+ * The application must call mbedtls_psa_hash_setup() before calling this function.
+ * This function calculates the hash of the message formed by concatenating
+ * the inputs passed to preceding calls to mbedtls_psa_hash_update().
+ *
+ * When this function returns successfully, the operation becomes inactive.
+ * If this function returns an error status, the operation enters an error
+ * state and must be aborted by calling mbedtls_psa_hash_abort().
+ *
+ * \param[in,out] operation     Active hash operation.
+ * \param[out] hash             Buffer where the hash is to be written.
+ * \param hash_size             Size of the \p hash buffer in bytes.
+ * \param[out] hash_length      On success, the number of bytes
+ *                              that make up the hash value. This is always
+ *                              #PSA_HASH_LENGTH(\c alg) where \c alg is the
+ *                              hash algorithm that is calculated.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The operation state is not valid (it must be active).
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p hash buffer is too small. You can determine a
+ *         sufficient buffer size by calling #PSA_HASH_LENGTH(\c alg)
+ *         where \c alg is the hash algorithm that is calculated.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_hash_finish(
+    mbedtls_psa_hash_operation_t *operation,
+    uint8_t *hash,
+    size_t hash_size,
+    size_t *hash_length);
+
+/** Abort an Mbed TLS hash operation.
+ *
+ * \note The signature of this function is that of a PSA driver hash_abort
+ *       entry point. This function behaves as a hash_abort entry point as
+ *       defined in the PSA driver interface specification for transparent
+ *       drivers.
+ *
+ * Aborting an operation frees all associated resources except for the
+ * \p operation structure itself. Once aborted, the operation object
+ * can be reused for another operation by calling
+ * mbedtls_psa_hash_setup() again.
+ *
+ * You may call this function any time after the operation object has
+ * been initialized by one of the methods described in #psa_hash_operation_t.
+ *
+ * In particular, calling mbedtls_psa_hash_abort() after the operation has been
+ * terminated by a call to mbedtls_psa_hash_abort(), mbedtls_psa_hash_finish() or
+ * mbedtls_psa_hash_verify() is safe and has no effect.
+ *
+ * \param[in,out] operation     Initialized hash operation.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_hash_abort(
+    mbedtls_psa_hash_operation_t *operation);
+
+#endif /* PSA_CRYPTO_HASH_H */
diff --git a/tf-psa-crypto/core/psa_crypto_invasive.h b/tf-psa-crypto/core/psa_crypto_invasive.h
new file mode 100644
index 0000000..51c90c6
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_invasive.h
@@ -0,0 +1,92 @@
+/**
+ * \file psa_crypto_invasive.h
+ *
+ * \brief PSA cryptography module: invasive interfaces for test only.
+ *
+ * The interfaces in this file are intended for testing purposes only.
+ * They MUST NOT be made available to clients over IPC in integrations
+ * with isolation, and they SHOULD NOT be made available in library
+ * integrations except when building the library for testing.
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_INVASIVE_H
+#define PSA_CRYPTO_INVASIVE_H
+
+/*
+ * Include the build-time configuration information header. Here, we do not
+ * include `"mbedtls/build_info.h"` directly but `"psa/build_info.h"`, which
+ * is basically just an alias to it. This is to ease the maintenance of the
+ * TF-PSA-Crypto repository which has a different build system and
+ * configuration.
+ */
+#include "psa/build_info.h"
+
+#include "psa/crypto.h"
+#include "common.h"
+
+#include "mbedtls/entropy.h"
+
+#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+/** \brief Configure entropy sources.
+ *
+ * This function may only be called before a call to psa_crypto_init(),
+ * or after a call to mbedtls_psa_crypto_free() and before any
+ * subsequent call to psa_crypto_init().
+ *
+ * This function is only intended for test purposes. The functionality
+ * it provides is also useful for system integrators, but
+ * system integrators should configure entropy drivers instead of
+ * breaking through to the Mbed TLS API.
+ *
+ * \param entropy_init  Function to initialize the entropy context
+ *                      and set up the desired entropy sources.
+ *                      It is called by psa_crypto_init().
+ *                      By default this is mbedtls_entropy_init().
+ *                      This function cannot report failures directly.
+ *                      To indicate a failure, set the entropy context
+ *                      to a state where mbedtls_entropy_func() will
+ *                      return an error.
+ * \param entropy_free  Function to free the entropy context
+ *                      and associated resources.
+ *                      It is called by mbedtls_psa_crypto_free().
+ *                      By default this is mbedtls_entropy_free().
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_NOT_PERMITTED
+ *         The caller does not have the permission to configure
+ *         entropy sources.
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The library has already been initialized.
+ */
+psa_status_t mbedtls_psa_crypto_configure_entropy_sources(
+    void (* entropy_init)(mbedtls_entropy_context *ctx),
+    void (* entropy_free)(mbedtls_entropy_context *ctx));
+#endif /* !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) */
+
+#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_PSA_CRYPTO_C)
+psa_status_t psa_mac_key_can_do(
+    psa_algorithm_t algorithm,
+    psa_key_type_t key_type);
+
+psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len,
+                                   uint8_t *input_copy, size_t input_copy_len);
+
+psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_copy_len,
+                                    uint8_t *output, size_t output_len);
+
+/*
+ * Test hooks to use for memory unpoisoning/poisoning in copy functions.
+ */
+extern void (*psa_input_pre_copy_hook)(const uint8_t *input, size_t input_len);
+extern void (*psa_input_post_copy_hook)(const uint8_t *input, size_t input_len);
+extern void (*psa_output_pre_copy_hook)(const uint8_t *output, size_t output_len);
+extern void (*psa_output_post_copy_hook)(const uint8_t *output, size_t output_len);
+
+#endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_PSA_CRYPTO_C */
+
+#endif /* PSA_CRYPTO_INVASIVE_H */
diff --git a/tf-psa-crypto/core/psa_crypto_its.h b/tf-psa-crypto/core/psa_crypto_its.h
new file mode 100644
index 0000000..877063b
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_its.h
@@ -0,0 +1,131 @@
+/** \file psa_crypto_its.h
+ * \brief Interface of trusted storage that crypto is built on.
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_ITS_H
+#define PSA_CRYPTO_ITS_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <psa/crypto_types.h>
+#include <psa/crypto_values.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \brief Flags used when creating a data entry
+ */
+typedef uint32_t psa_storage_create_flags_t;
+
+/** \brief A type for UIDs used for identifying data
+ */
+typedef uint64_t psa_storage_uid_t;
+
+#define PSA_STORAGE_FLAG_NONE        0         /**< No flags to pass */
+#define PSA_STORAGE_FLAG_WRITE_ONCE (1 << 0) /**< The data associated with the uid will not be able to be modified or deleted. Intended to be used to set bits in `psa_storage_create_flags_t`*/
+
+/**
+ * \brief A container for metadata associated with a specific uid
+ */
+struct psa_storage_info_t {
+    uint32_t size;                  /**< The size of the data associated with a uid **/
+    psa_storage_create_flags_t flags;    /**< The flags set when the uid was created **/
+};
+
+/** Flag indicating that \ref psa_storage_create and \ref psa_storage_set_extended are supported */
+#define PSA_STORAGE_SUPPORT_SET_EXTENDED (1 << 0)
+
+#define PSA_ITS_API_VERSION_MAJOR  1  /**< The major version number of the PSA ITS API. It will be incremented on significant updates that may include breaking changes */
+#define PSA_ITS_API_VERSION_MINOR  1  /**< The minor version number of the PSA ITS API. It will be incremented in small updates that are unlikely to include breaking changes */
+
+/**
+ * \brief create a new or modify an existing uid/value pair
+ *
+ * \param[in] uid           the identifier for the data
+ * \param[in] data_length   The size in bytes of the data in `p_data`
+ * \param[in] p_data        A buffer containing the data
+ * \param[in] create_flags  The flags that the data will be stored with
+ *
+ * \return      A status indicating the success/failure of the operation
+ *
+ * \retval      #PSA_SUCCESS                     The operation completed successfully
+ * \retval      #PSA_ERROR_NOT_PERMITTED         The operation failed because the provided `uid` value was already created with PSA_STORAGE_FLAG_WRITE_ONCE
+ * \retval      #PSA_ERROR_NOT_SUPPORTED         The operation failed because one or more of the flags provided in `create_flags` is not supported or is not valid
+ * \retval      #PSA_ERROR_INSUFFICIENT_STORAGE  The operation failed because there was insufficient space on the storage medium
+ * \retval      #PSA_ERROR_STORAGE_FAILURE       The operation failed because the physical storage has failed (Fatal error)
+ * \retval      #PSA_ERROR_INVALID_ARGUMENT      The operation failed because one of the provided pointers(`p_data`)
+ *                                               is invalid, for example is `NULL` or references memory the caller cannot access
+ */
+psa_status_t psa_its_set(psa_storage_uid_t uid,
+                         uint32_t data_length,
+                         const void *p_data,
+                         psa_storage_create_flags_t create_flags);
+
+/**
+ * \brief Retrieve the value associated with a provided uid
+ *
+ * \param[in] uid               The uid value
+ * \param[in] data_offset       The starting offset of the data requested
+ * \param[in] data_length       the amount of data requested (and the minimum allocated size of the `p_data` buffer)
+ * \param[out] p_data           The buffer where the data will be placed upon successful completion
+ * \param[out] p_data_length    The amount of data returned in the p_data buffer
+ *
+ *
+ * \return      A status indicating the success/failure of the operation
+ *
+ * \retval      #PSA_SUCCESS                 The operation completed successfully
+ * \retval      #PSA_ERROR_DOES_NOT_EXIST    The operation failed because the provided `uid` value was not found in the storage
+ * \retval      #PSA_ERROR_STORAGE_FAILURE   The operation failed because the physical storage has failed (Fatal error)
+ * \retval      #PSA_ERROR_DATA_CORRUPT      The operation failed because stored data has been corrupted
+ * \retval      #PSA_ERROR_INVALID_ARGUMENT  The operation failed because one of the provided pointers(`p_data`, `p_data_length`)
+ *                                           is invalid. For example is `NULL` or references memory the caller cannot access.
+ *                                           In addition, this can also happen if an invalid offset was provided.
+ */
+psa_status_t psa_its_get(psa_storage_uid_t uid,
+                         uint32_t data_offset,
+                         uint32_t data_length,
+                         void *p_data,
+                         size_t *p_data_length);
+
+/**
+ * \brief Retrieve the metadata about the provided uid
+ *
+ * \param[in] uid           The uid value
+ * \param[out] p_info       A pointer to the `psa_storage_info_t` struct that will be populated with the metadata
+ *
+ * \return      A status indicating the success/failure of the operation
+ *
+ * \retval      #PSA_SUCCESS                 The operation completed successfully
+ * \retval      #PSA_ERROR_DOES_NOT_EXIST    The operation failed because the provided uid value was not found in the storage
+ * \retval      #PSA_ERROR_DATA_CORRUPT      The operation failed because stored data has been corrupted
+ * \retval      #PSA_ERROR_INVALID_ARGUMENT  The operation failed because one of the provided pointers(`p_info`)
+ *                                           is invalid, for example is `NULL` or references memory the caller cannot access
+ */
+psa_status_t psa_its_get_info(psa_storage_uid_t uid,
+                              struct psa_storage_info_t *p_info);
+
+/**
+ * \brief Remove the provided key and its associated data from the storage
+ *
+ * \param[in] uid   The uid value
+ *
+ * \return  A status indicating the success/failure of the operation
+ *
+ * \retval      #PSA_SUCCESS                  The operation completed successfully
+ * \retval      #PSA_ERROR_DOES_NOT_EXIST     The operation failed because the provided key value was not found in the storage
+ * \retval      #PSA_ERROR_NOT_PERMITTED      The operation failed because the provided key value was created with PSA_STORAGE_FLAG_WRITE_ONCE
+ * \retval      #PSA_ERROR_STORAGE_FAILURE    The operation failed because the physical storage has failed (Fatal error)
+ */
+psa_status_t psa_its_remove(psa_storage_uid_t uid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PSA_CRYPTO_ITS_H */
diff --git a/tf-psa-crypto/core/psa_crypto_mac.c b/tf-psa-crypto/core/psa_crypto_mac.c
new file mode 100644
index 0000000..8fe6218
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_mac.c
@@ -0,0 +1,496 @@
+/*
+ *  PSA MAC layer on top of Mbed TLS software crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include <psa/crypto.h>
+#include "psa_crypto_core.h"
+#include "psa_crypto_cipher.h"
+#include "psa_crypto_mac.h"
+#include <mbedtls/md.h>
+
+#include <mbedtls/error.h>
+#include "mbedtls/constant_time.h"
+#include <string.h>
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
+static psa_status_t psa_hmac_abort_internal(
+    mbedtls_psa_hmac_operation_t *hmac)
+{
+    mbedtls_platform_zeroize(hmac->opad, sizeof(hmac->opad));
+    return psa_hash_abort(&hmac->hash_ctx);
+}
+
+static psa_status_t psa_hmac_setup_internal(
+    mbedtls_psa_hmac_operation_t *hmac,
+    const uint8_t *key,
+    size_t key_length,
+    psa_algorithm_t hash_alg)
+{
+    uint8_t ipad[PSA_HMAC_MAX_HASH_BLOCK_SIZE];
+    size_t i;
+    size_t hash_size = PSA_HASH_LENGTH(hash_alg);
+    size_t block_size = PSA_HASH_BLOCK_LENGTH(hash_alg);
+    psa_status_t status;
+
+    hmac->alg = hash_alg;
+
+    /* Sanity checks on block_size, to guarantee that there won't be a buffer
+     * overflow below. This should never trigger if the hash algorithm
+     * is implemented correctly. */
+    /* The size checks against the ipad and opad buffers cannot be written
+     * `block_size > sizeof( ipad ) || block_size > sizeof( hmac->opad )`
+     * because that triggers -Wlogical-op on GCC 7.3. */
+    if (block_size > sizeof(ipad)) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+    if (block_size > sizeof(hmac->opad)) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+    if (block_size < hash_size) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    if (key_length > block_size) {
+        status = psa_hash_compute(hash_alg, key, key_length,
+                                  ipad, sizeof(ipad), &key_length);
+        if (status != PSA_SUCCESS) {
+            goto cleanup;
+        }
+    }
+    /* A 0-length key is not commonly used in HMAC when used as a MAC,
+     * but it is permitted. It is common when HMAC is used in HKDF, for
+     * example. Don't call `memcpy` in the 0-length because `key` could be
+     * an invalid pointer which would make the behavior undefined. */
+    else if (key_length != 0) {
+        memcpy(ipad, key, key_length);
+    }
+
+    /* ipad contains the key followed by garbage. Xor and fill with 0x36
+     * to create the ipad value. */
+    for (i = 0; i < key_length; i++) {
+        ipad[i] ^= 0x36;
+    }
+    memset(ipad + key_length, 0x36, block_size - key_length);
+
+    /* Copy the key material from ipad to opad, flipping the requisite bits,
+     * and filling the rest of opad with the requisite constant. */
+    for (i = 0; i < key_length; i++) {
+        hmac->opad[i] = ipad[i] ^ 0x36 ^ 0x5C;
+    }
+    memset(hmac->opad + key_length, 0x5C, block_size - key_length);
+
+    status = psa_hash_setup(&hmac->hash_ctx, hash_alg);
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+
+    status = psa_hash_update(&hmac->hash_ctx, ipad, block_size);
+
+cleanup:
+    mbedtls_platform_zeroize(ipad, sizeof(ipad));
+
+    return status;
+}
+
+static psa_status_t psa_hmac_update_internal(
+    mbedtls_psa_hmac_operation_t *hmac,
+    const uint8_t *data,
+    size_t data_length)
+{
+    return psa_hash_update(&hmac->hash_ctx, data, data_length);
+}
+
+static psa_status_t psa_hmac_finish_internal(
+    mbedtls_psa_hmac_operation_t *hmac,
+    uint8_t *mac,
+    size_t mac_size)
+{
+    uint8_t tmp[PSA_HASH_MAX_SIZE];
+    psa_algorithm_t hash_alg = hmac->alg;
+    size_t hash_size = 0;
+    size_t block_size = PSA_HASH_BLOCK_LENGTH(hash_alg);
+    psa_status_t status;
+
+    status = psa_hash_finish(&hmac->hash_ctx, tmp, sizeof(tmp), &hash_size);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+    /* From here on, tmp needs to be wiped. */
+
+    status = psa_hash_setup(&hmac->hash_ctx, hash_alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_hash_update(&hmac->hash_ctx, hmac->opad, block_size);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_hash_update(&hmac->hash_ctx, tmp, hash_size);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_hash_finish(&hmac->hash_ctx, tmp, sizeof(tmp), &hash_size);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    memcpy(mac, tmp, mac_size);
+
+exit:
+    mbedtls_platform_zeroize(tmp, hash_size);
+    return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
+static psa_status_t cmac_setup(mbedtls_psa_mac_operation_t *operation,
+                               const psa_key_attributes_t *attributes,
+                               const uint8_t *key_buffer)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+#if defined(PSA_WANT_KEY_TYPE_DES)
+    /* Mbed TLS CMAC does not accept 3DES with only two keys, nor does it accept
+     * to do CMAC with pure DES, so return NOT_SUPPORTED here. */
+    if (psa_get_key_type(attributes) == PSA_KEY_TYPE_DES &&
+        (psa_get_key_bits(attributes) == 64 ||
+         psa_get_key_bits(attributes) == 128)) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+#endif
+
+    const mbedtls_cipher_info_t *cipher_info =
+        mbedtls_cipher_info_from_psa(
+            PSA_ALG_CMAC,
+            psa_get_key_type(attributes),
+            psa_get_key_bits(attributes),
+            NULL);
+
+    if (cipher_info == NULL) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    ret = mbedtls_cipher_setup(&operation->ctx.cmac, cipher_info);
+    if (ret != 0) {
+        goto exit;
+    }
+
+    ret = mbedtls_cipher_cmac_starts(&operation->ctx.cmac,
+                                     key_buffer,
+                                     psa_get_key_bits(attributes));
+exit:
+    return mbedtls_to_psa_error(ret);
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
+
+/* Initialize this driver's MAC operation structure. Once this function has been
+ * called, mbedtls_psa_mac_abort can run and will do the right thing. */
+static psa_status_t mac_init(
+    mbedtls_psa_mac_operation_t *operation,
+    psa_algorithm_t alg)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    operation->alg = alg;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
+    if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) {
+        mbedtls_cipher_init(&operation->ctx.cmac);
+        status = PSA_SUCCESS;
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
+    if (PSA_ALG_IS_HMAC(operation->alg)) {
+        /* We'll set up the hash operation later in psa_hmac_setup_internal. */
+        operation->ctx.hmac.alg = 0;
+        status = PSA_SUCCESS;
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */
+    {
+        (void) operation;
+        status = PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    if (status != PSA_SUCCESS) {
+        memset(operation, 0, sizeof(*operation));
+    }
+    return status;
+}
+
+psa_status_t mbedtls_psa_mac_abort(mbedtls_psa_mac_operation_t *operation)
+{
+    if (operation->alg == 0) {
+        /* The object has (apparently) been initialized but it is not
+         * in use. It's ok to call abort on such an object, and there's
+         * nothing to do. */
+        return PSA_SUCCESS;
+    } else
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
+    if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) {
+        mbedtls_cipher_free(&operation->ctx.cmac);
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
+    if (PSA_ALG_IS_HMAC(operation->alg)) {
+        psa_hmac_abort_internal(&operation->ctx.hmac);
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */
+    {
+        /* Sanity check (shouldn't happen: operation->alg should
+         * always have been initialized to a valid value). */
+        goto bad_state;
+    }
+
+    operation->alg = 0;
+
+    return PSA_SUCCESS;
+
+bad_state:
+    /* If abort is called on an uninitialized object, we can't trust
+     * anything. Wipe the object in case it contains confidential data.
+     * This may result in a memory leak if a pointer gets overwritten,
+     * but it's too late to do anything about this. */
+    memset(operation, 0, sizeof(*operation));
+    return PSA_ERROR_BAD_STATE;
+}
+
+static psa_status_t psa_mac_setup(mbedtls_psa_mac_operation_t *operation,
+                                  const psa_key_attributes_t *attributes,
+                                  const uint8_t *key_buffer,
+                                  size_t key_buffer_size,
+                                  psa_algorithm_t alg)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    /* A context must be freshly initialized before it can be set up. */
+    if (operation->alg != 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    status = mac_init(operation, alg);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
+    if (PSA_ALG_FULL_LENGTH_MAC(alg) == PSA_ALG_CMAC) {
+        /* Key buffer size for CMAC is dictated by the key bits set on the
+         * attributes, and previously validated by the core on key import. */
+        (void) key_buffer_size;
+        status = cmac_setup(operation, attributes, key_buffer);
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
+    if (PSA_ALG_IS_HMAC(alg)) {
+        status = psa_hmac_setup_internal(&operation->ctx.hmac,
+                                         key_buffer,
+                                         key_buffer_size,
+                                         PSA_ALG_HMAC_GET_HASH(alg));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */
+    {
+        (void) attributes;
+        (void) key_buffer;
+        (void) key_buffer_size;
+        status = PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    if (status != PSA_SUCCESS) {
+        mbedtls_psa_mac_abort(operation);
+    }
+
+    return status;
+}
+
+psa_status_t mbedtls_psa_mac_sign_setup(
+    mbedtls_psa_mac_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg)
+{
+    return psa_mac_setup(operation, attributes,
+                         key_buffer, key_buffer_size, alg);
+}
+
+psa_status_t mbedtls_psa_mac_verify_setup(
+    mbedtls_psa_mac_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg)
+{
+    return psa_mac_setup(operation, attributes,
+                         key_buffer, key_buffer_size, alg);
+}
+
+psa_status_t mbedtls_psa_mac_update(
+    mbedtls_psa_mac_operation_t *operation,
+    const uint8_t *input,
+    size_t input_length)
+{
+    if (operation->alg == 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
+    if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) {
+        return mbedtls_to_psa_error(
+            mbedtls_cipher_cmac_update(&operation->ctx.cmac,
+                                       input, input_length));
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
+    if (PSA_ALG_IS_HMAC(operation->alg)) {
+        return psa_hmac_update_internal(&operation->ctx.hmac,
+                                        input, input_length);
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */
+    {
+        /* This shouldn't happen if `operation` was initialized by
+         * a setup function. */
+        (void) input;
+        (void) input_length;
+        return PSA_ERROR_BAD_STATE;
+    }
+}
+
+static psa_status_t psa_mac_finish_internal(
+    mbedtls_psa_mac_operation_t *operation,
+    uint8_t *mac, size_t mac_size)
+{
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
+    if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) {
+        uint8_t tmp[PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE];
+        int ret = mbedtls_cipher_cmac_finish(&operation->ctx.cmac, tmp);
+        if (ret == 0) {
+            memcpy(mac, tmp, mac_size);
+        }
+        mbedtls_platform_zeroize(tmp, sizeof(tmp));
+        return mbedtls_to_psa_error(ret);
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
+    if (PSA_ALG_IS_HMAC(operation->alg)) {
+        return psa_hmac_finish_internal(&operation->ctx.hmac,
+                                        mac, mac_size);
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */
+    {
+        /* This shouldn't happen if `operation` was initialized by
+         * a setup function. */
+        (void) operation;
+        (void) mac;
+        (void) mac_size;
+        return PSA_ERROR_BAD_STATE;
+    }
+}
+
+psa_status_t mbedtls_psa_mac_sign_finish(
+    mbedtls_psa_mac_operation_t *operation,
+    uint8_t *mac,
+    size_t mac_size,
+    size_t *mac_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    if (operation->alg == 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    status = psa_mac_finish_internal(operation, mac, mac_size);
+    if (status == PSA_SUCCESS) {
+        *mac_length = mac_size;
+    }
+
+    return status;
+}
+
+psa_status_t mbedtls_psa_mac_verify_finish(
+    mbedtls_psa_mac_operation_t *operation,
+    const uint8_t *mac,
+    size_t mac_length)
+{
+    uint8_t actual_mac[PSA_MAC_MAX_SIZE];
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    if (operation->alg == 0) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+    /* Consistency check: requested MAC length fits our local buffer */
+    if (mac_length > sizeof(actual_mac)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    status = psa_mac_finish_internal(operation, actual_mac, mac_length);
+    if (status != PSA_SUCCESS) {
+        goto cleanup;
+    }
+
+    if (mbedtls_ct_memcmp(mac, actual_mac, mac_length) != 0) {
+        status = PSA_ERROR_INVALID_SIGNATURE;
+    }
+
+cleanup:
+    mbedtls_platform_zeroize(actual_mac, sizeof(actual_mac));
+
+    return status;
+}
+
+psa_status_t mbedtls_psa_mac_compute(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg,
+    const uint8_t *input,
+    size_t input_length,
+    uint8_t *mac,
+    size_t mac_size,
+    size_t *mac_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_psa_mac_operation_t operation = MBEDTLS_PSA_MAC_OPERATION_INIT;
+
+    status = psa_mac_setup(&operation,
+                           attributes, key_buffer, key_buffer_size,
+                           alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (input_length > 0) {
+        status = mbedtls_psa_mac_update(&operation, input, input_length);
+        if (status != PSA_SUCCESS) {
+            goto exit;
+        }
+    }
+
+    status = psa_mac_finish_internal(&operation, mac, mac_size);
+    if (status == PSA_SUCCESS) {
+        *mac_length = mac_size;
+    }
+
+exit:
+    mbedtls_psa_mac_abort(&operation);
+
+    return status;
+}
+
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC || MBEDTLS_PSA_BUILTIN_ALG_CMAC */
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/tf-psa-crypto/core/psa_crypto_mac.h b/tf-psa-crypto/core/psa_crypto_mac.h
new file mode 100644
index 0000000..2f614bc
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_mac.h
@@ -0,0 +1,264 @@
+/*
+ *  PSA MAC layer on top of Mbed TLS software crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_MAC_H
+#define PSA_CRYPTO_MAC_H
+
+#include <psa/crypto.h>
+
+/** Calculate the MAC (message authentication code) of a message using Mbed TLS.
+ *
+ * \note The signature of this function is that of a PSA driver mac_compute
+ *       entry point. This function behaves as a mac_compute entry point as
+ *       defined in the PSA driver interface specification for transparent
+ *       drivers.
+ *
+ * \param[in] attributes        The attributes of the key to use for the
+ *                              operation.
+ * \param[in] key_buffer        The buffer containing the key to use for
+ *                              computing the MAC. This buffer contains the key
+ *                              in export representation as defined by
+ *                              psa_export_key() (i.e. the raw key bytes).
+ * \param key_buffer_size       Size of the \p key_buffer buffer in bytes.
+ * \param alg                   The MAC algorithm to use (\c PSA_ALG_XXX value
+ *                              such that #PSA_ALG_IS_MAC(\p alg) is true).
+ * \param[in] input             Buffer containing the input message.
+ * \param input_length          Size of the \p input buffer in bytes.
+ * \param[out] mac              Buffer where the MAC value is to be written.
+ * \param mac_size              Size of the \p mac buffer in bytes.
+ * \param[out] mac_length       On success, the number of bytes
+ *                              that make up the MAC value.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         \p alg is not supported.
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         \p mac_size is too small
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_mac_compute(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg,
+    const uint8_t *input,
+    size_t input_length,
+    uint8_t *mac,
+    size_t mac_size,
+    size_t *mac_length);
+
+/** Set up a multipart MAC calculation operation using Mbed TLS.
+ *
+ * \note The signature of this function is that of a PSA driver mac_sign_setup
+ *       entry point. This function behaves as a mac_sign_setup entry point as
+ *       defined in the PSA driver interface specification for transparent
+ *       drivers.
+ *
+ * \param[in,out] operation     The operation object to set up. It must have
+ *                              been initialized and not yet in use.
+ * \param[in] attributes        The attributes of the key to use for the
+ *                              operation.
+ * \param[in] key_buffer        The buffer containing the key to use for
+ *                              computing the MAC. This buffer contains the key
+ *                              in export representation as defined by
+ *                              psa_export_key() (i.e. the raw key bytes).
+ * \param key_buffer_size       Size of the \p key_buffer buffer in bytes.
+ * \param alg                   The MAC algorithm to use (\c PSA_ALG_XXX value
+ *                              such that #PSA_ALG_IS_MAC(\p alg) is true).
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         \p alg is not supported.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The operation state is not valid (it must be inactive).
+ */
+psa_status_t mbedtls_psa_mac_sign_setup(
+    mbedtls_psa_mac_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg);
+
+/** Set up a multipart MAC verification operation using Mbed TLS.
+ *
+ * \note The signature of this function is that of a PSA driver mac_verify_setup
+ *       entry point. This function behaves as a mac_verify_setup entry point as
+ *       defined in the PSA driver interface specification for transparent
+ *       drivers.
+ *
+ * \param[in,out] operation     The operation object to set up. It must have
+ *                              been initialized and not yet in use.
+ * \param[in] attributes        The attributes of the key to use for the
+ *                              operation.
+ * \param[in] key_buffer        The buffer containing the key to use for
+ *                              computing the MAC. This buffer contains the key
+ *                              in export representation as defined by
+ *                              psa_export_key() (i.e. the raw key bytes).
+ * \param key_buffer_size       Size of the \p key_buffer buffer in bytes.
+ * \param alg                   The MAC algorithm to use (\c PSA_ALG_XXX value
+ *                              such that #PSA_ALG_IS_MAC(\p alg) is true).
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         \p alg is not supported.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The operation state is not valid (it must be inactive).
+ */
+psa_status_t mbedtls_psa_mac_verify_setup(
+    mbedtls_psa_mac_operation_t *operation,
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer,
+    size_t key_buffer_size,
+    psa_algorithm_t alg);
+
+/** Add a message fragment to a multipart MAC operation using Mbed TLS.
+ *
+ * \note The signature of this function is that of a PSA driver mac_update
+ *       entry point. This function behaves as a mac_update entry point as
+ *       defined in the PSA driver interface specification for transparent
+ *       drivers.
+ *
+ * The PSA core calls mbedtls_psa_mac_sign_setup() or
+ * mbedtls_psa_mac_verify_setup() before calling this function.
+ *
+ * If this function returns an error status, the PSA core aborts the
+ * operation by calling mbedtls_psa_mac_abort().
+ *
+ * \param[in,out] operation Active MAC operation.
+ * \param[in] input         Buffer containing the message fragment to add to
+ *                          the MAC calculation.
+ * \param input_length      Size of the \p input buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The operation state is not valid (it must be active).
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_mac_update(
+    mbedtls_psa_mac_operation_t *operation,
+    const uint8_t *input,
+    size_t input_length);
+
+/** Finish the calculation of the MAC of a message using Mbed TLS.
+ *
+ * \note The signature of this function is that of a PSA driver mac_sign_finish
+ *       entry point. This function behaves as a mac_sign_finish entry point as
+ *       defined in the PSA driver interface specification for transparent
+ *       drivers.
+ *
+ * The PSA core calls mbedtls_psa_mac_sign_setup() before calling this function.
+ * This function calculates the MAC of the message formed by concatenating
+ * the inputs passed to preceding calls to mbedtls_psa_mac_update().
+ *
+ * Whether this function returns successfully or not, the PSA core subsequently
+ * aborts the operation by calling mbedtls_psa_mac_abort().
+ *
+ * \param[in,out] operation Active MAC operation.
+ * \param[out] mac          Buffer where the MAC value is to be written.
+ * \param mac_size          Output size requested for the MAC algorithm. The PSA
+ *                          core guarantees this is a valid MAC length for the
+ *                          algorithm and key combination passed to
+ *                          mbedtls_psa_mac_sign_setup(). It also guarantees the
+ *                          \p mac buffer is large enough to contain the
+ *                          requested output size.
+ * \param[out] mac_length   On success, the number of bytes output to buffer
+ *                          \p mac, which will be equal to the requested length
+ *                          \p mac_size.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The operation state is not valid (it must be an active mac sign
+ *         operation).
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p mac buffer is too small. A sufficient buffer size
+ *         can be determined by calling PSA_MAC_LENGTH().
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_mac_sign_finish(
+    mbedtls_psa_mac_operation_t *operation,
+    uint8_t *mac,
+    size_t mac_size,
+    size_t *mac_length);
+
+/** Finish the calculation of the MAC of a message and compare it with
+ * an expected value using Mbed TLS.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       mac_verify_finish entry point. This function behaves as a
+ *       mac_verify_finish entry point as defined in the PSA driver interface
+ *       specification for transparent drivers.
+ *
+ * The PSA core calls mbedtls_psa_mac_verify_setup() before calling this
+ * function. This function calculates the MAC of the message formed by
+ * concatenating the inputs passed to preceding calls to
+ * mbedtls_psa_mac_update(). It then compares the calculated MAC with the
+ * expected MAC passed as a parameter to this function.
+ *
+ * Whether this function returns successfully or not, the PSA core subsequently
+ * aborts the operation by calling mbedtls_psa_mac_abort().
+ *
+ * \param[in,out] operation Active MAC operation.
+ * \param[in] mac           Buffer containing the expected MAC value.
+ * \param mac_length        Length in bytes of the expected MAC value. The PSA
+ *                          core guarantees that this length is a valid MAC
+ *                          length for the algorithm and key combination passed
+ *                          to mbedtls_psa_mac_verify_setup().
+ *
+ * \retval #PSA_SUCCESS
+ *         The expected MAC is identical to the actual MAC of the message.
+ * \retval #PSA_ERROR_INVALID_SIGNATURE
+ *         The MAC of the message was calculated successfully, but it
+ *         differs from the expected MAC.
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The operation state is not valid (it must be an active mac verify
+ *         operation).
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_mac_verify_finish(
+    mbedtls_psa_mac_operation_t *operation,
+    const uint8_t *mac,
+    size_t mac_length);
+
+/** Abort a MAC operation using Mbed TLS.
+ *
+ * Aborting an operation frees all associated resources except for the
+ * \p operation structure itself. Once aborted, the operation object
+ * can be reused for another operation by calling
+ * mbedtls_psa_mac_sign_setup() or mbedtls_psa_mac_verify_setup() again.
+ *
+ * The PSA core may call this function any time after the operation object has
+ * been initialized by one of the methods described in
+ * #mbedtls_psa_mac_operation_t.
+ *
+ * In particular, calling mbedtls_psa_mac_abort() after the operation has been
+ * terminated by a call to mbedtls_psa_mac_abort(),
+ * mbedtls_psa_mac_sign_finish() or mbedtls_psa_mac_verify_finish() is safe and
+ * has no effect.
+ *
+ * \param[in,out] operation Initialized MAC operation.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_mac_abort(
+    mbedtls_psa_mac_operation_t *operation);
+
+#endif /* PSA_CRYPTO_MAC_H */
diff --git a/tf-psa-crypto/core/psa_crypto_pake.c b/tf-psa-crypto/core/psa_crypto_pake.c
new file mode 100644
index 0000000..9ac2e8c
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_pake.c
@@ -0,0 +1,571 @@
+/*
+ *  PSA PAKE layer on top of Mbed TLS software crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include <psa/crypto.h>
+#include "psa_crypto_core.h"
+#include "psa_crypto_pake.h"
+#include "psa_crypto_slot_management.h"
+
+#include <mbedtls/ecjpake.h>
+#include "psa_util_internal.h"
+
+#include <mbedtls/platform.h>
+#include <mbedtls/error.h>
+#include <string.h>
+
+/*
+ * State sequence:
+ *
+ *   psa_pake_setup()
+ *   |
+ *   |-- In any order:
+ *   |   | psa_pake_set_password_key()
+ *   |   | psa_pake_set_user()
+ *   |   | psa_pake_set_peer()
+ *   |   | psa_pake_set_role()
+ *   |
+ *   |--- In any order: (First round input before or after first round output)
+ *   |   |
+ *   |   |------ In Order
+ *   |   |       | psa_pake_output(PSA_PAKE_STEP_KEY_SHARE)
+ *   |   |       | psa_pake_output(PSA_PAKE_STEP_ZK_PUBLIC)
+ *   |   |       | psa_pake_output(PSA_PAKE_STEP_ZK_PROOF)
+ *   |   |       | psa_pake_output(PSA_PAKE_STEP_KEY_SHARE)
+ *   |   |       | psa_pake_output(PSA_PAKE_STEP_ZK_PUBLIC)
+ *   |   |       | psa_pake_output(PSA_PAKE_STEP_ZK_PROOF)
+ *   |   |
+ *   |   |------ In Order:
+ *   |           | psa_pake_input(PSA_PAKE_STEP_KEY_SHARE)
+ *   |           | psa_pake_input(PSA_PAKE_STEP_ZK_PUBLIC)
+ *   |           | psa_pake_input(PSA_PAKE_STEP_ZK_PROOF)
+ *   |           | psa_pake_input(PSA_PAKE_STEP_KEY_SHARE)
+ *   |           | psa_pake_input(PSA_PAKE_STEP_ZK_PUBLIC)
+ *   |           | psa_pake_input(PSA_PAKE_STEP_ZK_PROOF)
+ *   |
+ *   |--- In any order: (Second round input before or after second round output)
+ *   |   |
+ *   |   |------ In Order
+ *   |   |       | psa_pake_output(PSA_PAKE_STEP_KEY_SHARE)
+ *   |   |       | psa_pake_output(PSA_PAKE_STEP_ZK_PUBLIC)
+ *   |   |       | psa_pake_output(PSA_PAKE_STEP_ZK_PROOF)
+ *   |   |
+ *   |   |------ In Order:
+ *   |           | psa_pake_input(PSA_PAKE_STEP_KEY_SHARE)
+ *   |           | psa_pake_input(PSA_PAKE_STEP_ZK_PUBLIC)
+ *   |           | psa_pake_input(PSA_PAKE_STEP_ZK_PROOF)
+ *   |
+ *   psa_pake_get_implicit_key()
+ *   psa_pake_abort()
+ */
+
+/*
+ * Possible sequence of calls to implementation:
+ *
+ * |--- In any order:
+ * |   |
+ * |   |------ In Order
+ * |   |       | mbedtls_psa_pake_output(PSA_JPAKE_X1_STEP_KEY_SHARE)
+ * |   |       | mbedtls_psa_pake_output(PSA_JPAKE_X1_STEP_ZK_PUBLIC)
+ * |   |       | mbedtls_psa_pake_output(PSA_JPAKE_X1_STEP_ZK_PROOF)
+ * |   |       | mbedtls_psa_pake_output(PSA_JPAKE_X2_STEP_KEY_SHARE)
+ * |   |       | mbedtls_psa_pake_output(PSA_JPAKE_X2_STEP_ZK_PUBLIC)
+ * |   |       | mbedtls_psa_pake_output(PSA_JPAKE_X2_STEP_ZK_PROOF)
+ * |   |
+ * |   |------ In Order:
+ * |           | mbedtls_psa_pake_input(PSA_JPAKE_X1_STEP_KEY_SHARE)
+ * |           | mbedtls_psa_pake_input(PSA_JPAKE_X1_STEP_ZK_PUBLIC)
+ * |           | mbedtls_psa_pake_input(PSA_JPAKE_X1_STEP_ZK_PROOF)
+ * |           | mbedtls_psa_pake_input(PSA_JPAKE_X2_STEP_KEY_SHARE)
+ * |           | mbedtls_psa_pake_input(PSA_JPAKE_X2_STEP_ZK_PUBLIC)
+ * |           | mbedtls_psa_pake_input(PSA_JPAKE_X2_STEP_ZK_PROOF)
+ * |
+ * |--- In any order:
+ * |   |
+ * |   |------ In Order
+ * |   |       | mbedtls_psa_pake_output(PSA_JPAKE_X2S_STEP_KEY_SHARE)
+ * |   |       | mbedtls_psa_pake_output(PSA_JPAKE_X2S_STEP_ZK_PUBLIC)
+ * |   |       | mbedtls_psa_pake_output(PSA_JPAKE_X2S_STEP_ZK_PROOF)
+ * |   |
+ * |   |------ In Order:
+ * |           | mbedtls_psa_pake_input(PSA_JPAKE_X4S_STEP_KEY_SHARE)
+ * |           | mbedtls_psa_pake_input(PSA_JPAKE_X4S_STEP_ZK_PUBLIC)
+ * |           | mbedtls_psa_pake_input(PSA_JPAKE_X4S_STEP_ZK_PROOF)
+ */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE)
+static psa_status_t mbedtls_ecjpake_to_psa_error(int ret)
+{
+    switch (ret) {
+        case MBEDTLS_ERR_MPI_BAD_INPUT_DATA:
+        case MBEDTLS_ERR_ECP_BAD_INPUT_DATA:
+        case MBEDTLS_ERR_ECP_INVALID_KEY:
+        case MBEDTLS_ERR_ECP_VERIFY_FAILED:
+            return PSA_ERROR_DATA_INVALID;
+        case MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL:
+        case MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL:
+            return PSA_ERROR_BUFFER_TOO_SMALL;
+        case MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE:
+            return PSA_ERROR_NOT_SUPPORTED;
+        case MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED:
+            return PSA_ERROR_CORRUPTION_DETECTED;
+        default:
+            return PSA_ERROR_GENERIC_ERROR;
+    }
+}
+#endif
+
+#if defined(MBEDTLS_PSA_BUILTIN_PAKE)
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE)
+static psa_status_t psa_pake_ecjpake_setup(mbedtls_psa_pake_operation_t *operation)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    mbedtls_ecjpake_init(&operation->ctx.jpake);
+
+    ret = mbedtls_ecjpake_setup(&operation->ctx.jpake,
+                                operation->role,
+                                MBEDTLS_MD_SHA256,
+                                MBEDTLS_ECP_DP_SECP256R1,
+                                operation->password,
+                                operation->password_len);
+
+    mbedtls_platform_zeroize(operation->password, operation->password_len);
+
+    if (ret != 0) {
+        return mbedtls_ecjpake_to_psa_error(ret);
+    }
+
+    return PSA_SUCCESS;
+}
+#endif
+
+/* The only two JPAKE user/peer identifiers supported in built-in implementation. */
+static const uint8_t jpake_server_id[] = { 's', 'e', 'r', 'v', 'e', 'r' };
+static const uint8_t jpake_client_id[] = { 'c', 'l', 'i', 'e', 'n', 't' };
+
+psa_status_t mbedtls_psa_pake_setup(mbedtls_psa_pake_operation_t *operation,
+                                    const psa_crypto_driver_pake_inputs_t *inputs)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    size_t user_len = 0, peer_len = 0, password_len = 0;
+    uint8_t *peer = NULL, *user = NULL;
+    size_t actual_user_len = 0, actual_peer_len = 0, actual_password_len = 0;
+    psa_pake_cipher_suite_t cipher_suite = psa_pake_cipher_suite_init();
+
+    status = psa_crypto_driver_pake_get_password_len(inputs, &password_len);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = psa_crypto_driver_pake_get_user_len(inputs, &user_len);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = psa_crypto_driver_pake_get_peer_len(inputs, &peer_len);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = psa_crypto_driver_pake_get_cipher_suite(inputs, &cipher_suite);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    operation->password = mbedtls_calloc(1, password_len);
+    if (operation->password == NULL) {
+        status = PSA_ERROR_INSUFFICIENT_MEMORY;
+        goto error;
+    }
+
+    user = mbedtls_calloc(1, user_len);
+    if (user == NULL) {
+        status = PSA_ERROR_INSUFFICIENT_MEMORY;
+        goto error;
+    }
+
+    peer = mbedtls_calloc(1, peer_len);
+    if (peer == NULL) {
+        status = PSA_ERROR_INSUFFICIENT_MEMORY;
+        goto error;
+    }
+
+    status = psa_crypto_driver_pake_get_password(inputs, operation->password,
+                                                 password_len, &actual_password_len);
+    if (status != PSA_SUCCESS) {
+        goto error;
+    }
+
+    status = psa_crypto_driver_pake_get_user(inputs, user,
+                                             user_len, &actual_user_len);
+    if (status != PSA_SUCCESS) {
+        goto error;
+    }
+
+    status = psa_crypto_driver_pake_get_peer(inputs, peer,
+                                             peer_len, &actual_peer_len);
+    if (status != PSA_SUCCESS) {
+        goto error;
+    }
+
+    operation->password_len = actual_password_len;
+    operation->alg = cipher_suite.algorithm;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE)
+    if (cipher_suite.algorithm == PSA_ALG_JPAKE) {
+        if (cipher_suite.type != PSA_PAKE_PRIMITIVE_TYPE_ECC ||
+            cipher_suite.family != PSA_ECC_FAMILY_SECP_R1 ||
+            cipher_suite.bits != 256 ||
+            cipher_suite.hash != PSA_ALG_SHA_256) {
+            status = PSA_ERROR_NOT_SUPPORTED;
+            goto error;
+        }
+
+        const size_t user_peer_len = sizeof(jpake_client_id); // client and server have the same length
+        if (actual_user_len != user_peer_len ||
+            actual_peer_len != user_peer_len) {
+            status = PSA_ERROR_NOT_SUPPORTED;
+            goto error;
+        }
+
+        if (memcmp(user, jpake_client_id, actual_user_len) == 0 &&
+            memcmp(peer, jpake_server_id, actual_peer_len) == 0) {
+            operation->role = MBEDTLS_ECJPAKE_CLIENT;
+        } else
+        if (memcmp(user, jpake_server_id, actual_user_len) == 0 &&
+            memcmp(peer, jpake_client_id, actual_peer_len) == 0) {
+            operation->role = MBEDTLS_ECJPAKE_SERVER;
+        } else {
+            status = PSA_ERROR_NOT_SUPPORTED;
+            goto error;
+        }
+
+        operation->buffer_length = 0;
+        operation->buffer_offset = 0;
+
+        status = psa_pake_ecjpake_setup(operation);
+        if (status != PSA_SUCCESS) {
+            goto error;
+        }
+
+        /* Role has been set, release user/peer buffers. */
+        mbedtls_free(user); mbedtls_free(peer);
+
+        return PSA_SUCCESS;
+    } else
+#else
+    (void) operation;
+    (void) inputs;
+#endif
+    { status = PSA_ERROR_NOT_SUPPORTED; }
+
+error:
+    mbedtls_free(user); mbedtls_free(peer);
+    /* In case of failure of the setup of a multipart operation, the PSA driver interface
+     * specifies that the core does not call any other driver entry point thus does not
+     * call mbedtls_psa_pake_abort(). Therefore call it here to do the needed clean
+     * up like freeing the memory that may have been allocated to store the password.
+     */
+    mbedtls_psa_pake_abort(operation);
+    return status;
+}
+
+static psa_status_t mbedtls_psa_pake_output_internal(
+    mbedtls_psa_pake_operation_t *operation,
+    psa_crypto_driver_pake_step_t step,
+    uint8_t *output,
+    size_t output_size,
+    size_t *output_length)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    size_t length;
+    (void) step; // Unused parameter
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE)
+    /*
+     * The PSA CRYPTO PAKE and Mbed TLS JPAKE API have a different
+     * handling of output sequencing.
+     *
+     * The Mbed TLS JPAKE API outputs the whole X1+X2 and X2S steps data
+     * at once, on the other side the PSA CRYPTO PAKE api requires
+     * the KEY_SHARE/ZP_PUBLIC/ZK_PROOF parts of X1, X2 & X2S to be
+     * retrieved in sequence.
+     *
+     * In order to achieve API compatibility, the whole X1+X2 or X2S steps
+     * data is stored in an intermediate buffer at first step output call,
+     * and data is sliced down by parsing the ECPoint records in order
+     * to return the right parts on each step.
+     */
+    if (operation->alg == PSA_ALG_JPAKE) {
+        /* Initialize & write round on KEY_SHARE sequences */
+        if (step == PSA_JPAKE_X1_STEP_KEY_SHARE) {
+            ret = mbedtls_ecjpake_write_round_one(&operation->ctx.jpake,
+                                                  operation->buffer,
+                                                  sizeof(operation->buffer),
+                                                  &operation->buffer_length,
+                                                  mbedtls_psa_get_random,
+                                                  MBEDTLS_PSA_RANDOM_STATE);
+            if (ret != 0) {
+                return mbedtls_ecjpake_to_psa_error(ret);
+            }
+
+            operation->buffer_offset = 0;
+        } else if (step == PSA_JPAKE_X2S_STEP_KEY_SHARE) {
+            ret = mbedtls_ecjpake_write_round_two(&operation->ctx.jpake,
+                                                  operation->buffer,
+                                                  sizeof(operation->buffer),
+                                                  &operation->buffer_length,
+                                                  mbedtls_psa_get_random,
+                                                  MBEDTLS_PSA_RANDOM_STATE);
+            if (ret != 0) {
+                return mbedtls_ecjpake_to_psa_error(ret);
+            }
+
+            operation->buffer_offset = 0;
+        }
+
+        /*
+         * mbedtls_ecjpake_write_round_xxx() outputs thing in the format
+         * defined by draft-cragie-tls-ecjpake-01 section 7. The summary is
+         * that the data for each step is prepended with a length byte, and
+         * then they're concatenated. Additionally, the server's second round
+         * output is prepended with a 3-bytes ECParameters structure.
+         *
+         * In PSA, we output each step separately, and don't prepend the
+         * output with a length byte, even less a curve identifier, as that
+         * information is already available.
+         */
+        if (step == PSA_JPAKE_X2S_STEP_KEY_SHARE &&
+            operation->role == MBEDTLS_ECJPAKE_SERVER) {
+            /* Skip ECParameters, with is 3 bytes (RFC 8422) */
+            operation->buffer_offset += 3;
+        }
+
+        /* Read the length byte then move past it to the data */
+        length = operation->buffer[operation->buffer_offset];
+        operation->buffer_offset += 1;
+
+        if (operation->buffer_offset + length > operation->buffer_length) {
+            return PSA_ERROR_DATA_CORRUPT;
+        }
+
+        if (output_size < length) {
+            return PSA_ERROR_BUFFER_TOO_SMALL;
+        }
+
+        memcpy(output,
+               operation->buffer + operation->buffer_offset,
+               length);
+        *output_length = length;
+
+        operation->buffer_offset += length;
+
+        /* Reset buffer after ZK_PROOF sequence */
+        if ((step == PSA_JPAKE_X2_STEP_ZK_PROOF) ||
+            (step == PSA_JPAKE_X2S_STEP_ZK_PROOF)) {
+            mbedtls_platform_zeroize(operation->buffer, sizeof(operation->buffer));
+            operation->buffer_length = 0;
+            operation->buffer_offset = 0;
+        }
+
+        return PSA_SUCCESS;
+    } else
+#else
+    (void) step;
+    (void) output;
+    (void) output_size;
+    (void) output_length;
+#endif
+    { return PSA_ERROR_NOT_SUPPORTED; }
+}
+
+psa_status_t mbedtls_psa_pake_output(mbedtls_psa_pake_operation_t *operation,
+                                     psa_crypto_driver_pake_step_t step,
+                                     uint8_t *output,
+                                     size_t output_size,
+                                     size_t *output_length)
+{
+    psa_status_t status = mbedtls_psa_pake_output_internal(
+        operation, step, output, output_size, output_length);
+
+    return status;
+}
+
+static psa_status_t mbedtls_psa_pake_input_internal(
+    mbedtls_psa_pake_operation_t *operation,
+    psa_crypto_driver_pake_step_t step,
+    const uint8_t *input,
+    size_t input_length)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    (void) step; // Unused parameter
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE)
+    /*
+     * The PSA CRYPTO PAKE and Mbed TLS JPAKE API have a different
+     * handling of input sequencing.
+     *
+     * The Mbed TLS JPAKE API takes the whole X1+X2 or X4S steps data
+     * at once as input, on the other side the PSA CRYPTO PAKE api requires
+     * the KEY_SHARE/ZP_PUBLIC/ZK_PROOF parts of X1, X2 & X4S to be
+     * given in sequence.
+     *
+     * In order to achieve API compatibility, each X1+X2 or X4S step data
+     * is stored sequentially in an intermediate buffer and given to the
+     * Mbed TLS JPAKE API on the last step.
+     *
+     * This causes any input error to be only detected on the last step.
+     */
+    if (operation->alg == PSA_ALG_JPAKE) {
+        /*
+         * Copy input to local buffer and format it as the Mbed TLS API
+         * expects, i.e. as defined by draft-cragie-tls-ecjpake-01 section 7.
+         * The summary is that the data for each step is prepended with a
+         * length byte, and then they're concatenated. Additionally, the
+         * server's second round output is prepended with a 3-bytes
+         * ECParameters structure - which means we have to prepend that when
+         * we're a client.
+         */
+        if (step == PSA_JPAKE_X4S_STEP_KEY_SHARE &&
+            operation->role == MBEDTLS_ECJPAKE_CLIENT) {
+            /* We only support secp256r1. */
+            /* This is the ECParameters structure defined by RFC 8422. */
+            unsigned char ecparameters[3] = {
+                3, /* named_curve */
+                0, 23 /* secp256r1 */
+            };
+
+            if (operation->buffer_length + sizeof(ecparameters) >
+                sizeof(operation->buffer)) {
+                return PSA_ERROR_BUFFER_TOO_SMALL;
+            }
+
+            memcpy(operation->buffer + operation->buffer_length,
+                   ecparameters, sizeof(ecparameters));
+            operation->buffer_length += sizeof(ecparameters);
+        }
+
+        /*
+         * The core checks that input_length is smaller than
+         * PSA_PAKE_INPUT_MAX_SIZE.
+         * Thus no risk of integer overflow here.
+         */
+        if (operation->buffer_length + input_length + 1 > sizeof(operation->buffer)) {
+            return PSA_ERROR_BUFFER_TOO_SMALL;
+        }
+
+        /* Write the length byte */
+        operation->buffer[operation->buffer_length] = (uint8_t) input_length;
+        operation->buffer_length += 1;
+
+        /* Finally copy the data */
+        memcpy(operation->buffer + operation->buffer_length,
+               input, input_length);
+        operation->buffer_length += input_length;
+
+        /* Load buffer at each last round ZK_PROOF */
+        if (step == PSA_JPAKE_X2_STEP_ZK_PROOF) {
+            ret = mbedtls_ecjpake_read_round_one(&operation->ctx.jpake,
+                                                 operation->buffer,
+                                                 operation->buffer_length);
+
+            mbedtls_platform_zeroize(operation->buffer, sizeof(operation->buffer));
+            operation->buffer_length = 0;
+
+            if (ret != 0) {
+                return mbedtls_ecjpake_to_psa_error(ret);
+            }
+        } else if (step == PSA_JPAKE_X4S_STEP_ZK_PROOF) {
+            ret = mbedtls_ecjpake_read_round_two(&operation->ctx.jpake,
+                                                 operation->buffer,
+                                                 operation->buffer_length);
+
+            mbedtls_platform_zeroize(operation->buffer, sizeof(operation->buffer));
+            operation->buffer_length = 0;
+
+            if (ret != 0) {
+                return mbedtls_ecjpake_to_psa_error(ret);
+            }
+        }
+
+        return PSA_SUCCESS;
+    } else
+#else
+    (void) step;
+    (void) input;
+    (void) input_length;
+#endif
+    { return PSA_ERROR_NOT_SUPPORTED; }
+}
+
+psa_status_t mbedtls_psa_pake_input(mbedtls_psa_pake_operation_t *operation,
+                                    psa_crypto_driver_pake_step_t step,
+                                    const uint8_t *input,
+                                    size_t input_length)
+{
+    psa_status_t status = mbedtls_psa_pake_input_internal(
+        operation, step, input, input_length);
+
+    return status;
+}
+
+psa_status_t mbedtls_psa_pake_get_implicit_key(
+    mbedtls_psa_pake_operation_t *operation,
+    uint8_t *output, size_t output_size,
+    size_t *output_length)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE)
+    if (operation->alg == PSA_ALG_JPAKE) {
+        ret = mbedtls_ecjpake_write_shared_key(&operation->ctx.jpake,
+                                               output,
+                                               output_size,
+                                               output_length,
+                                               mbedtls_psa_get_random,
+                                               MBEDTLS_PSA_RANDOM_STATE);
+        if (ret != 0) {
+            return mbedtls_ecjpake_to_psa_error(ret);
+        }
+
+        return PSA_SUCCESS;
+    } else
+#else
+    (void) output;
+#endif
+    { return PSA_ERROR_NOT_SUPPORTED; }
+}
+
+psa_status_t mbedtls_psa_pake_abort(mbedtls_psa_pake_operation_t *operation)
+{
+    mbedtls_zeroize_and_free(operation->password, operation->password_len);
+    operation->password = NULL;
+    operation->password_len = 0;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE)
+    if (operation->alg == PSA_ALG_JPAKE) {
+        operation->role = MBEDTLS_ECJPAKE_NONE;
+        mbedtls_platform_zeroize(operation->buffer, sizeof(operation->buffer));
+        operation->buffer_length = 0;
+        operation->buffer_offset = 0;
+        mbedtls_ecjpake_free(&operation->ctx.jpake);
+    }
+#endif
+
+    operation->alg = PSA_ALG_NONE;
+
+    return PSA_SUCCESS;
+}
+
+#endif /* MBEDTLS_PSA_BUILTIN_PAKE */
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/tf-psa-crypto/core/psa_crypto_pake.h b/tf-psa-crypto/core/psa_crypto_pake.h
new file mode 100644
index 0000000..3d3ee0c
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_pake.h
@@ -0,0 +1,159 @@
+/*
+ *  PSA PAKE layer on top of Mbed TLS software crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_PAKE_H
+#define PSA_CRYPTO_PAKE_H
+
+#include <psa/crypto.h>
+
+/** Set the session information for a password-authenticated key exchange.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       pake_setup entry point. This function behaves as a pake_setup
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in,out] operation     The operation object to set up. It must have
+ *                              been initialized but not set up yet.
+ * \param[in] inputs            Inputs required for PAKE operation (role, password,
+ *                              key lifetime, cipher suite)
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         The algorithm in \p cipher_suite is not a supported PAKE algorithm,
+ *         or the PAKE primitive in \p cipher_suite is not supported or not
+ *         compatible with the PAKE algorithm, or the hash algorithm in
+ *         \p cipher_suite is not supported or not compatible with the PAKE
+ *         algorithm and primitive.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY   \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED   \emptydescription
+ */
+psa_status_t mbedtls_psa_pake_setup(mbedtls_psa_pake_operation_t *operation,
+                                    const psa_crypto_driver_pake_inputs_t *inputs);
+
+
+/** Get output for a step of a password-authenticated key exchange.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       pake_output entry point. This function behaves as a pake_output
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in,out] operation    Active PAKE operation.
+ * \param step                 The step of the algorithm for which the output is
+ *                             requested.
+ * \param[out] output          Buffer where the output is to be written in the
+ *                             format appropriate for this driver \p step. Refer to
+ *                             the documentation of psa_crypto_driver_pake_step_t for
+ *                             more information.
+ * \param output_size          Size of the \p output buffer in bytes. This must
+ *                             be at least #PSA_PAKE_OUTPUT_SIZE(\p alg, \p
+ *                             primitive, \p step) where \p alg and
+ *                             \p primitive are the PAKE algorithm and primitive
+ *                             in the operation's cipher suite, and \p step is
+ *                             the output step.
+ *
+ * \param[out] output_length   On success, the number of bytes of the returned
+ *                             output.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p output buffer is too small.
+ * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY  \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED   \emptydescription
+ * \retval #PSA_ERROR_DATA_CORRUPT          \emptydescription
+ * \retval #PSA_ERROR_DATA_INVALID          \emptydescription
+ */
+psa_status_t mbedtls_psa_pake_output(mbedtls_psa_pake_operation_t *operation,
+                                     psa_crypto_driver_pake_step_t step,
+                                     uint8_t *output,
+                                     size_t output_size,
+                                     size_t *output_length);
+
+/** Provide input for a step of a password-authenticated key exchange.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       pake_input entry point. This function behaves as a pake_input
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \note The core checks that input_length is smaller than PSA_PAKE_INPUT_MAX_SIZE.
+ *
+ * \param[in,out] operation    Active PAKE operation.
+ * \param step                 The driver step for which the input is provided.
+ * \param[in] input            Buffer containing the input in the format
+ *                             appropriate for this \p step. Refer to the
+ *                             documentation of psa_crypto_driver_pake_step_t
+ *                             for more information.
+ * \param input_length         Size of the \p input buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_INVALID_SIGNATURE
+ *         The verification fails for a zero-knowledge input step.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         the \p input is not valid for the \p operation's algorithm, cipher suite
+ *         or \p step.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         the \p input is not supported for the \p operation's algorithm, cipher
+ *         suite or \p step.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY   \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED   \emptydescription
+ * \retval #PSA_ERROR_DATA_CORRUPT          \emptydescription
+ * \retval #PSA_ERROR_DATA_INVALID          \emptydescription
+ */
+psa_status_t mbedtls_psa_pake_input(mbedtls_psa_pake_operation_t *operation,
+                                    psa_crypto_driver_pake_step_t step,
+                                    const uint8_t *input,
+                                    size_t input_length);
+
+/** Get implicitly confirmed shared secret from a PAKE.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       pake_get_implicit_key entry point. This function behaves as a
+ *       pake_get_implicit_key entry point as defined in the PSA driver
+ *       interface specification for transparent drivers.
+ *
+ * \param[in,out] operation    Active PAKE operation.
+ * \param[out] output          Output buffer for implicit key.
+ * \param      output_size     Size of the output buffer in bytes.
+ * \param[out] output_length   On success, the number of bytes of the implicit key.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         Input from a PAKE is not supported by the algorithm in the \p output
+ *         key derivation operation.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY   \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED   \emptydescription
+ * \retval #PSA_ERROR_DATA_CORRUPT          \emptydescription
+ * \retval #PSA_ERROR_DATA_INVALID          \emptydescription
+ */
+psa_status_t mbedtls_psa_pake_get_implicit_key(
+    mbedtls_psa_pake_operation_t *operation,
+    uint8_t *output, size_t output_size,
+    size_t *output_length);
+
+/** Abort a PAKE operation.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       pake_abort entry point. This function behaves as a pake_abort
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in,out] operation    The operation to abort.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED   \emptydescription
+ */
+psa_status_t mbedtls_psa_pake_abort(mbedtls_psa_pake_operation_t *operation);
+
+#endif /* PSA_CRYPTO_PAKE_H */
diff --git a/tf-psa-crypto/core/psa_crypto_random_impl.h b/tf-psa-crypto/core/psa_crypto_random_impl.h
new file mode 100644
index 0000000..533fb2e
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_random_impl.h
@@ -0,0 +1,135 @@
+/** \file psa_crypto_random_impl.h
+ *
+ * \brief PSA crypto random generator implementation abstraction.
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_RANDOM_IMPL_H
+#define PSA_CRYPTO_RANDOM_IMPL_H
+
+#include "psa_util_internal.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+
+typedef mbedtls_psa_external_random_context_t mbedtls_psa_random_context_t;
+
+#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+
+#include "mbedtls/entropy.h"
+
+/* Choose a DRBG based on configuration and availability */
+#if defined(MBEDTLS_PSA_HMAC_DRBG_MD_TYPE)
+
+#include "mbedtls/hmac_drbg.h"
+
+#elif defined(MBEDTLS_CTR_DRBG_C)
+
+#include "mbedtls/ctr_drbg.h"
+
+#elif defined(MBEDTLS_HMAC_DRBG_C)
+
+#include "mbedtls/hmac_drbg.h"
+#if defined(MBEDTLS_MD_CAN_SHA512) && defined(MBEDTLS_MD_CAN_SHA256)
+#include <limits.h>
+#if SIZE_MAX > 0xffffffff
+/* Looks like a 64-bit system, so prefer SHA-512. */
+#define MBEDTLS_PSA_HMAC_DRBG_MD_TYPE MBEDTLS_MD_SHA512
+#else
+/* Looks like a 32-bit system, so prefer SHA-256. */
+#define MBEDTLS_PSA_HMAC_DRBG_MD_TYPE MBEDTLS_MD_SHA256
+#endif
+#elif defined(MBEDTLS_MD_CAN_SHA512)
+#define MBEDTLS_PSA_HMAC_DRBG_MD_TYPE MBEDTLS_MD_SHA512
+#elif defined(MBEDTLS_MD_CAN_SHA256)
+#define MBEDTLS_PSA_HMAC_DRBG_MD_TYPE MBEDTLS_MD_SHA256
+#else
+#error "No hash algorithm available for HMAC_DBRG."
+#endif
+
+#else /* !MBEDTLS_PSA_HMAC_DRBG_MD_TYPE && !MBEDTLS_CTR_DRBG_C && !MBEDTLS_HMAC_DRBG_C*/
+
+#error "No DRBG module available for the psa_crypto module."
+
+#endif /* !MBEDTLS_PSA_HMAC_DRBG_MD_TYPE && !MBEDTLS_CTR_DRBG_C && !MBEDTLS_HMAC_DRBG_C*/
+
+#if defined(MBEDTLS_CTR_DRBG_C)
+#include "mbedtls/ctr_drbg.h"
+#elif defined(MBEDTLS_HMAC_DRBG_C)
+#include "mbedtls/hmac_drbg.h"
+#endif /* !MBEDTLS_CTR_DRBG_C && !MBEDTLS_HMAC_DRBG_C */
+
+/* The maximum number of bytes that mbedtls_psa_get_random() is expected to return. */
+#if defined(MBEDTLS_CTR_DRBG_C)
+#define MBEDTLS_PSA_RANDOM_MAX_REQUEST MBEDTLS_CTR_DRBG_MAX_REQUEST
+#elif defined(MBEDTLS_HMAC_DRBG_C)
+#define MBEDTLS_PSA_RANDOM_MAX_REQUEST MBEDTLS_HMAC_DRBG_MAX_REQUEST
+#endif
+
+#if defined(MBEDTLS_CTR_DRBG_C)
+typedef mbedtls_ctr_drbg_context            mbedtls_psa_drbg_context_t;
+#elif defined(MBEDTLS_HMAC_DRBG_C)
+typedef mbedtls_hmac_drbg_context           mbedtls_psa_drbg_context_t;
+#endif /* !MBEDTLS_CTR_DRBG_C && !MBEDTLS_HMAC_DRBG_C */
+
+typedef struct {
+    void (* entropy_init)(mbedtls_entropy_context *ctx);
+    void (* entropy_free)(mbedtls_entropy_context *ctx);
+    mbedtls_entropy_context entropy;
+    mbedtls_psa_drbg_context_t drbg;
+} mbedtls_psa_random_context_t;
+
+/** Initialize the PSA DRBG.
+ *
+ * \param p_rng        Pointer to the Mbed TLS DRBG state.
+ */
+static inline void mbedtls_psa_drbg_init(mbedtls_psa_drbg_context_t *p_rng)
+{
+#if defined(MBEDTLS_CTR_DRBG_C)
+    mbedtls_ctr_drbg_init(p_rng);
+#elif defined(MBEDTLS_HMAC_DRBG_C)
+    mbedtls_hmac_drbg_init(p_rng);
+#endif
+}
+
+/** Deinitialize the PSA DRBG.
+ *
+ * \param p_rng        Pointer to the Mbed TLS DRBG state.
+ */
+static inline void mbedtls_psa_drbg_free(mbedtls_psa_drbg_context_t *p_rng)
+{
+#if defined(MBEDTLS_CTR_DRBG_C)
+    mbedtls_ctr_drbg_free(p_rng);
+#elif defined(MBEDTLS_HMAC_DRBG_C)
+    mbedtls_hmac_drbg_free(p_rng);
+#endif
+}
+
+/** Seed the PSA DRBG.
+ *
+ * \param entropy       An entropy context to read the seed from.
+ * \param custom        The personalization string.
+ *                      This can be \c NULL, in which case the personalization
+ *                      string is empty regardless of the value of \p len.
+ * \param len           The length of the personalization string.
+ *
+ * \return              \c 0 on success.
+ * \return              An Mbed TLS error code (\c MBEDTLS_ERR_xxx) on failure.
+ */
+static inline int mbedtls_psa_drbg_seed(mbedtls_psa_drbg_context_t *drbg_ctx,
+                                        mbedtls_entropy_context *entropy,
+                                        const unsigned char *custom, size_t len)
+{
+#if defined(MBEDTLS_CTR_DRBG_C)
+    return mbedtls_ctr_drbg_seed(drbg_ctx, mbedtls_entropy_func, entropy, custom, len);
+#elif defined(MBEDTLS_HMAC_DRBG_C)
+    const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_PSA_HMAC_DRBG_MD_TYPE);
+    return mbedtls_hmac_drbg_seed(drbg_ctx, md_info, mbedtls_entropy_func, entropy, custom, len);
+#endif
+}
+
+#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+
+#endif /* PSA_CRYPTO_RANDOM_IMPL_H */
diff --git a/tf-psa-crypto/core/psa_crypto_rsa.c b/tf-psa-crypto/core/psa_crypto_rsa.c
new file mode 100644
index 0000000..2f613b3
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_rsa.c
@@ -0,0 +1,706 @@
+/*
+ *  PSA RSA layer on top of Mbed TLS crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include <psa/crypto.h>
+#include "psa/crypto_values.h"
+#include "psa_crypto_core.h"
+#include "psa_crypto_random_impl.h"
+#include "psa_crypto_rsa.h"
+#include "psa_crypto_hash.h"
+#include "mbedtls/psa_util.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "mbedtls/platform.h"
+
+#include <mbedtls/rsa.h>
+#include <mbedtls/error.h>
+#include "rsa_internal.h"
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) || \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) || \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT) || \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY)
+
+/* Mbed TLS doesn't support non-byte-aligned key sizes (i.e. key sizes
+ * that are not a multiple of 8) well. For example, there is only
+ * mbedtls_rsa_get_len(), which returns a number of bytes, and no
+ * way to return the exact bit size of a key.
+ * To keep things simple, reject non-byte-aligned key sizes. */
+static psa_status_t psa_check_rsa_key_byte_aligned(
+    const mbedtls_rsa_context *rsa)
+{
+    mbedtls_mpi n;
+    psa_status_t status;
+    mbedtls_mpi_init(&n);
+    status = mbedtls_to_psa_error(
+        mbedtls_rsa_export(rsa, &n, NULL, NULL, NULL, NULL));
+    if (status == PSA_SUCCESS) {
+        if (mbedtls_mpi_bitlen(&n) % 8 != 0) {
+            status = PSA_ERROR_NOT_SUPPORTED;
+        }
+    }
+    mbedtls_mpi_free(&n);
+    return status;
+}
+
+psa_status_t mbedtls_psa_rsa_load_representation(
+    psa_key_type_t type, const uint8_t *data, size_t data_length,
+    mbedtls_rsa_context **p_rsa)
+{
+    psa_status_t status;
+    size_t bits;
+
+    *p_rsa = mbedtls_calloc(1, sizeof(mbedtls_rsa_context));
+    if (*p_rsa == NULL) {
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+    mbedtls_rsa_init(*p_rsa);
+
+    /* Parse the data. */
+    if (PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
+        status = mbedtls_to_psa_error(mbedtls_rsa_parse_key(*p_rsa, data, data_length));
+    } else {
+        status = mbedtls_to_psa_error(mbedtls_rsa_parse_pubkey(*p_rsa, data, data_length));
+    }
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    /* The size of an RSA key doesn't have to be a multiple of 8. Mbed TLS
+     * supports non-byte-aligned key sizes, but not well. For example,
+     * mbedtls_rsa_get_len() returns the key size in bytes, not in bits. */
+    bits = PSA_BYTES_TO_BITS(mbedtls_rsa_get_len(*p_rsa));
+    if (bits > PSA_VENDOR_RSA_MAX_KEY_BITS) {
+        status = PSA_ERROR_NOT_SUPPORTED;
+        goto exit;
+    }
+    status = psa_check_rsa_key_byte_aligned(*p_rsa);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+exit:
+    return status;
+}
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */
+
+#if (defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) && \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT)) || \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY)
+psa_status_t mbedtls_psa_rsa_import_key(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *data, size_t data_length,
+    uint8_t *key_buffer, size_t key_buffer_size,
+    size_t *key_buffer_length, size_t *bits)
+{
+    psa_status_t status;
+    mbedtls_rsa_context *rsa = NULL;
+
+    /* Parse input */
+    status = mbedtls_psa_rsa_load_representation(attributes->type,
+                                                 data,
+                                                 data_length,
+                                                 &rsa);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    *bits = (psa_key_bits_t) PSA_BYTES_TO_BITS(mbedtls_rsa_get_len(rsa));
+
+    /* Re-export the data to PSA export format, such that we can store export
+     * representation in the key slot. Export representation in case of RSA is
+     * the smallest representation that's allowed as input, so a straight-up
+     * allocation of the same size as the input buffer will be large enough. */
+    status = mbedtls_psa_rsa_export_key(attributes->type,
+                                        rsa,
+                                        key_buffer,
+                                        key_buffer_size,
+                                        key_buffer_length);
+exit:
+    /* Always free the RSA object */
+    mbedtls_rsa_free(rsa);
+    mbedtls_free(rsa);
+
+    return status;
+}
+#endif /* (defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) &&
+        *  defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT)) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT) || \
+    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY)
+psa_status_t mbedtls_psa_rsa_export_key(psa_key_type_t type,
+                                        mbedtls_rsa_context *rsa,
+                                        uint8_t *data,
+                                        size_t data_size,
+                                        size_t *data_length)
+{
+    int ret;
+    uint8_t *end = data + data_size;
+
+    /* PSA Crypto API defines the format of an RSA key as a DER-encoded
+     * representation of the non-encrypted PKCS#1 RSAPrivateKey for a
+     * private key and of the RFC3279 RSAPublicKey for a public key. */
+    if (PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
+        ret = mbedtls_rsa_write_key(rsa, data, &end);
+    } else {
+        ret = mbedtls_rsa_write_pubkey(rsa, data, &end);
+    }
+
+    if (ret < 0) {
+        /* Clean up in case pk_write failed halfway through. */
+        memset(data, 0, data_size);
+        return mbedtls_to_psa_error(ret);
+    }
+
+    /* The mbedtls_pk_xxx functions write to the end of the buffer.
+     * Move the data to the beginning and erase remaining data
+     * at the original location. */
+    if (2 * (size_t) ret <= data_size) {
+        memcpy(data, data + data_size - ret, ret);
+        memset(data + data_size - ret, 0, ret);
+    } else if ((size_t) ret < data_size) {
+        memmove(data, data + data_size - ret, ret);
+        memset(data + ret, 0, data_size - ret);
+    }
+
+    *data_length = ret;
+    return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_rsa_export_public_key(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    uint8_t *data, size_t data_size, size_t *data_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_rsa_context *rsa = NULL;
+
+    status = mbedtls_psa_rsa_load_representation(
+        attributes->type, key_buffer, key_buffer_size, &rsa);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = mbedtls_psa_rsa_export_key(PSA_KEY_TYPE_RSA_PUBLIC_KEY,
+                                        rsa,
+                                        data,
+                                        data_size,
+                                        data_length);
+
+    mbedtls_rsa_free(rsa);
+    mbedtls_free(rsa);
+
+    return status;
+}
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_GENERATE)
+static psa_status_t psa_rsa_read_exponent(const uint8_t *e_bytes,
+                                          size_t e_length,
+                                          int *exponent)
+{
+    size_t i;
+    uint32_t acc = 0;
+
+    /* Mbed TLS encodes the public exponent as an int. For simplicity, only
+     * support values that fit in a 32-bit integer, which is larger than
+     * int on just about every platform anyway. */
+    if (e_length > sizeof(acc)) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+    for (i = 0; i < e_length; i++) {
+        acc = (acc << 8) | e_bytes[i];
+    }
+    if (acc > INT_MAX) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+    *exponent = acc;
+    return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_rsa_generate_key(
+    const psa_key_attributes_t *attributes,
+    const psa_key_production_parameters_t *params, size_t params_data_length,
+    uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length)
+{
+    psa_status_t status;
+    mbedtls_rsa_context rsa;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    int exponent = 65537;
+
+    if (params_data_length != 0) {
+        status = psa_rsa_read_exponent(params->data, params_data_length,
+                                       &exponent);
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
+    }
+
+    mbedtls_rsa_init(&rsa);
+    ret = mbedtls_rsa_gen_key(&rsa,
+                              mbedtls_psa_get_random,
+                              MBEDTLS_PSA_RANDOM_STATE,
+                              (unsigned int) attributes->bits,
+                              exponent);
+    if (ret != 0) {
+        return mbedtls_to_psa_error(ret);
+    }
+
+    status = mbedtls_psa_rsa_export_key(attributes->type,
+                                        &rsa, key_buffer, key_buffer_size,
+                                        key_buffer_length);
+    mbedtls_rsa_free(&rsa);
+
+    return status;
+}
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_GENERATE) */
+
+/****************************************************************/
+/* Sign/verify hashes */
+/****************************************************************/
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS)
+
+/* Decode the hash algorithm from alg and store the mbedtls encoding in
+ * md_alg. Verify that the hash length is acceptable. */
+static psa_status_t psa_rsa_decode_md_type(psa_algorithm_t alg,
+                                           size_t hash_length,
+                                           mbedtls_md_type_t *md_alg)
+{
+    psa_algorithm_t hash_alg = PSA_ALG_SIGN_GET_HASH(alg);
+    *md_alg = mbedtls_md_type_from_psa_alg(hash_alg);
+
+    /* The Mbed TLS RSA module uses an unsigned int for hash length
+     * parameters. Validate that it fits so that we don't risk an
+     * overflow later. */
+#if SIZE_MAX > UINT_MAX
+    if (hash_length > UINT_MAX) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+#endif
+
+    /* For signatures using a hash, the hash length must be correct. */
+    if (alg != PSA_ALG_RSA_PKCS1V15_SIGN_RAW) {
+        if (*md_alg == MBEDTLS_MD_NONE) {
+            return PSA_ERROR_NOT_SUPPORTED;
+        }
+        if (mbedtls_md_get_size_from_type(*md_alg) != hash_length) {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+    }
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_rsa_sign_hash(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+    uint8_t *signature, size_t signature_size, size_t *signature_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_rsa_context *rsa = NULL;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    mbedtls_md_type_t md_alg;
+
+    status = mbedtls_psa_rsa_load_representation(attributes->type,
+                                                 key_buffer,
+                                                 key_buffer_size,
+                                                 &rsa);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = psa_rsa_decode_md_type(alg, hash_length, &md_alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (signature_size < mbedtls_rsa_get_len(rsa)) {
+        status = PSA_ERROR_BUFFER_TOO_SMALL;
+        goto exit;
+    }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN)
+    if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg)) {
+        ret = mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V15,
+                                      MBEDTLS_MD_NONE);
+        if (ret == 0) {
+            ret = mbedtls_rsa_pkcs1_sign(rsa,
+                                         mbedtls_psa_get_random,
+                                         MBEDTLS_PSA_RANDOM_STATE,
+                                         md_alg,
+                                         (unsigned int) hash_length,
+                                         hash,
+                                         signature);
+        }
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS)
+    if (PSA_ALG_IS_RSA_PSS(alg)) {
+        ret = mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V21, md_alg);
+
+        if (ret == 0) {
+            ret = mbedtls_rsa_rsassa_pss_sign(rsa,
+                                              mbedtls_psa_get_random,
+                                              MBEDTLS_PSA_RANDOM_STATE,
+                                              MBEDTLS_MD_NONE,
+                                              (unsigned int) hash_length,
+                                              hash,
+                                              signature);
+        }
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS */
+    {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    if (ret == 0) {
+        *signature_length = mbedtls_rsa_get_len(rsa);
+    }
+    status = mbedtls_to_psa_error(ret);
+
+exit:
+    mbedtls_rsa_free(rsa);
+    mbedtls_free(rsa);
+
+    return status;
+}
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS)
+static int rsa_pss_expected_salt_len(psa_algorithm_t alg,
+                                     const mbedtls_rsa_context *rsa,
+                                     size_t hash_length)
+{
+    if (PSA_ALG_IS_RSA_PSS_ANY_SALT(alg)) {
+        return MBEDTLS_RSA_SALT_LEN_ANY;
+    }
+    /* Otherwise: standard salt length, i.e. largest possible salt length
+     * up to the hash length. */
+    int klen = (int) mbedtls_rsa_get_len(rsa);   // known to fit
+    int hlen = (int) hash_length; // known to fit
+    int room = klen - 2 - hlen;
+    if (room < 0) {
+        return 0;  // there is no valid signature in this case anyway
+    } else if (room > hlen) {
+        return hlen;
+    } else {
+        return room;
+    }
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS */
+
+psa_status_t mbedtls_psa_rsa_verify_hash(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+    const uint8_t *signature, size_t signature_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_rsa_context *rsa = NULL;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    mbedtls_md_type_t md_alg;
+
+    status = mbedtls_psa_rsa_load_representation(attributes->type,
+                                                 key_buffer,
+                                                 key_buffer_size,
+                                                 &rsa);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_rsa_decode_md_type(alg, hash_length, &md_alg);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (signature_length != mbedtls_rsa_get_len(rsa)) {
+        status = PSA_ERROR_INVALID_SIGNATURE;
+        goto exit;
+    }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN)
+    if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg)) {
+        ret = mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V15,
+                                      MBEDTLS_MD_NONE);
+        if (ret == 0) {
+            ret = mbedtls_rsa_pkcs1_verify(rsa,
+                                           md_alg,
+                                           (unsigned int) hash_length,
+                                           hash,
+                                           signature);
+        }
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS)
+    if (PSA_ALG_IS_RSA_PSS(alg)) {
+        ret = mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V21, md_alg);
+        if (ret == 0) {
+            int slen = rsa_pss_expected_salt_len(alg, rsa, hash_length);
+            ret = mbedtls_rsa_rsassa_pss_verify_ext(rsa,
+                                                    md_alg,
+                                                    (unsigned) hash_length,
+                                                    hash,
+                                                    md_alg,
+                                                    slen,
+                                                    signature);
+        }
+    } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS */
+    {
+        status = PSA_ERROR_INVALID_ARGUMENT;
+        goto exit;
+    }
+
+    /* Mbed TLS distinguishes "invalid padding" from "valid padding but
+     * the rest of the signature is invalid". This has little use in
+     * practice and PSA doesn't report this distinction. */
+    status = (ret == MBEDTLS_ERR_RSA_INVALID_PADDING) ?
+             PSA_ERROR_INVALID_SIGNATURE :
+             mbedtls_to_psa_error(ret);
+
+exit:
+    mbedtls_rsa_free(rsa);
+    mbedtls_free(rsa);
+
+    return status;
+}
+
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) */
+
+/****************************************************************/
+/* Asymmetric cryptography */
+/****************************************************************/
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
+static int psa_rsa_oaep_set_padding_mode(psa_algorithm_t alg,
+                                         mbedtls_rsa_context *rsa)
+{
+    psa_algorithm_t hash_alg = PSA_ALG_RSA_OAEP_GET_HASH(alg);
+    mbedtls_md_type_t md_alg = mbedtls_md_type_from_psa_alg(hash_alg);
+
+    /* Just to get the error status right, as rsa_set_padding() doesn't
+     * distinguish between "bad RSA algorithm" and "unknown hash". */
+    if (mbedtls_md_info_from_type(md_alg) == NULL) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    return mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V21, md_alg);
+}
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) */
+
+psa_status_t mbedtls_psa_asymmetric_encrypt(const psa_key_attributes_t *attributes,
+                                            const uint8_t *key_buffer,
+                                            size_t key_buffer_size,
+                                            psa_algorithm_t alg,
+                                            const uint8_t *input,
+                                            size_t input_length,
+                                            const uint8_t *salt,
+                                            size_t salt_length,
+                                            uint8_t *output,
+                                            size_t output_size,
+                                            size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    (void) key_buffer;
+    (void) key_buffer_size;
+    (void) input;
+    (void) input_length;
+    (void) salt;
+    (void) salt_length;
+    (void) output;
+    (void) output_size;
+    (void) output_length;
+
+    if (PSA_KEY_TYPE_IS_RSA(attributes->type)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \
+        defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
+        mbedtls_rsa_context *rsa = NULL;
+        status = mbedtls_psa_rsa_load_representation(attributes->type,
+                                                     key_buffer,
+                                                     key_buffer_size,
+                                                     &rsa);
+        if (status != PSA_SUCCESS) {
+            goto rsa_exit;
+        }
+
+        if (output_size < mbedtls_rsa_get_len(rsa)) {
+            status = PSA_ERROR_BUFFER_TOO_SMALL;
+            goto rsa_exit;
+        }
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) */
+        if (alg == PSA_ALG_RSA_PKCS1V15_CRYPT) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT)
+            status = mbedtls_to_psa_error(
+                mbedtls_rsa_pkcs1_encrypt(rsa,
+                                          mbedtls_psa_get_random,
+                                          MBEDTLS_PSA_RANDOM_STATE,
+                                          input_length,
+                                          input,
+                                          output));
+#else
+            status = PSA_ERROR_NOT_SUPPORTED;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT */
+        } else
+        if (PSA_ALG_IS_RSA_OAEP(alg)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
+            status = mbedtls_to_psa_error(
+                psa_rsa_oaep_set_padding_mode(alg, rsa));
+            if (status != PSA_SUCCESS) {
+                goto rsa_exit;
+            }
+
+            status = mbedtls_to_psa_error(
+                mbedtls_rsa_rsaes_oaep_encrypt(rsa,
+                                               mbedtls_psa_get_random,
+                                               MBEDTLS_PSA_RANDOM_STATE,
+                                               salt, salt_length,
+                                               input_length,
+                                               input,
+                                               output));
+#else
+            status = PSA_ERROR_NOT_SUPPORTED;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP */
+        } else {
+            status = PSA_ERROR_INVALID_ARGUMENT;
+        }
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \
+        defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
+rsa_exit:
+        if (status == PSA_SUCCESS) {
+            *output_length = mbedtls_rsa_get_len(rsa);
+        }
+
+        mbedtls_rsa_free(rsa);
+        mbedtls_free(rsa);
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) */
+    } else {
+        status = PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    return status;
+}
+
+psa_status_t mbedtls_psa_asymmetric_decrypt(const psa_key_attributes_t *attributes,
+                                            const uint8_t *key_buffer,
+                                            size_t key_buffer_size,
+                                            psa_algorithm_t alg,
+                                            const uint8_t *input,
+                                            size_t input_length,
+                                            const uint8_t *salt,
+                                            size_t salt_length,
+                                            uint8_t *output,
+                                            size_t output_size,
+                                            size_t *output_length)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    (void) key_buffer;
+    (void) key_buffer_size;
+    (void) input;
+    (void) input_length;
+    (void) salt;
+    (void) salt_length;
+    (void) output;
+    (void) output_size;
+    (void) output_length;
+
+    *output_length = 0;
+
+    if (attributes->type == PSA_KEY_TYPE_RSA_KEY_PAIR) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \
+        defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
+        mbedtls_rsa_context *rsa = NULL;
+        status = mbedtls_psa_rsa_load_representation(attributes->type,
+                                                     key_buffer,
+                                                     key_buffer_size,
+                                                     &rsa);
+        if (status != PSA_SUCCESS) {
+            goto rsa_exit;
+        }
+
+        if (input_length != mbedtls_rsa_get_len(rsa)) {
+            status = PSA_ERROR_INVALID_ARGUMENT;
+            goto rsa_exit;
+        }
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) */
+
+        if (alg == PSA_ALG_RSA_PKCS1V15_CRYPT) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT)
+            status = mbedtls_to_psa_error(
+                mbedtls_rsa_pkcs1_decrypt(rsa,
+                                          mbedtls_psa_get_random,
+                                          MBEDTLS_PSA_RANDOM_STATE,
+                                          output_length,
+                                          input,
+                                          output,
+                                          output_size));
+#else
+            status = PSA_ERROR_NOT_SUPPORTED;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT */
+        } else
+        if (PSA_ALG_IS_RSA_OAEP(alg)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
+            status = mbedtls_to_psa_error(
+                psa_rsa_oaep_set_padding_mode(alg, rsa));
+            if (status != PSA_SUCCESS) {
+                goto rsa_exit;
+            }
+
+            status = mbedtls_to_psa_error(
+                mbedtls_rsa_rsaes_oaep_decrypt(rsa,
+                                               mbedtls_psa_get_random,
+                                               MBEDTLS_PSA_RANDOM_STATE,
+                                               salt, salt_length,
+                                               output_length,
+                                               input,
+                                               output,
+                                               output_size));
+#else
+            status = PSA_ERROR_NOT_SUPPORTED;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP */
+        } else {
+            status = PSA_ERROR_INVALID_ARGUMENT;
+        }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \
+        defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
+rsa_exit:
+        mbedtls_rsa_free(rsa);
+        mbedtls_free(rsa);
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) ||
+        * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) */
+    } else {
+        status = PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    return status;
+}
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/tf-psa-crypto/core/psa_crypto_rsa.h b/tf-psa-crypto/core/psa_crypto_rsa.h
new file mode 100644
index 0000000..ffeef26
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_rsa.h
@@ -0,0 +1,327 @@
+/*
+ *  PSA RSA layer on top of Mbed TLS crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_RSA_H
+#define PSA_CRYPTO_RSA_H
+
+#include <psa/crypto.h>
+#include <mbedtls/rsa.h>
+
+/** Load the contents of a key buffer into an internal RSA representation
+ *
+ * \param[in] type          The type of key contained in \p data.
+ * \param[in] data          The buffer from which to load the representation.
+ * \param[in] data_length   The size in bytes of \p data.
+ * \param[out] p_rsa        Returns a pointer to an RSA context on success.
+ *                          The caller is responsible for freeing both the
+ *                          contents of the context and the context itself
+ *                          when done.
+ */
+psa_status_t mbedtls_psa_rsa_load_representation(psa_key_type_t type,
+                                                 const uint8_t *data,
+                                                 size_t data_length,
+                                                 mbedtls_rsa_context **p_rsa);
+
+/** Import an RSA key in binary format.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       import_key entry point. This function behaves as an import_key
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in]  attributes       The attributes for the key to import.
+ * \param[in]  data             The buffer containing the key data in import
+ *                              format.
+ * \param[in]  data_length      Size of the \p data buffer in bytes.
+ * \param[out] key_buffer       The buffer containing the key data in output
+ *                              format.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes. This
+ *                              size is greater or equal to \p data_length.
+ * \param[out] key_buffer_length  The length of the data written in \p
+ *                                key_buffer in bytes.
+ * \param[out] bits             The key size in number of bits.
+ *
+ * \retval #PSA_SUCCESS  The RSA key was imported successfully.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         The key data is not correctly formatted.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ */
+psa_status_t mbedtls_psa_rsa_import_key(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *data, size_t data_length,
+    uint8_t *key_buffer, size_t key_buffer_size,
+    size_t *key_buffer_length, size_t *bits);
+
+/** Export an RSA key to export representation
+ *
+ * \param[in] type          The type of key (public/private) to export
+ * \param[in] rsa           The internal RSA representation from which to export
+ * \param[out] data         The buffer to export to
+ * \param[in] data_size     The length of the buffer to export to
+ * \param[out] data_length  The amount of bytes written to \p data
+ */
+psa_status_t mbedtls_psa_rsa_export_key(psa_key_type_t type,
+                                        mbedtls_rsa_context *rsa,
+                                        uint8_t *data,
+                                        size_t data_size,
+                                        size_t *data_length);
+
+/** Export a public RSA key or the public part of an RSA key pair in binary
+ *  format.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       export_public_key entry point. This function behaves as an
+ *       export_public_key entry point as defined in the PSA driver interface
+ *       specification.
+ *
+ * \param[in]  attributes       The attributes for the key to export.
+ * \param[in]  key_buffer       Material or context of the key to export.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[out] data             Buffer where the key data is to be written.
+ * \param[in]  data_size        Size of the \p data buffer in bytes.
+ * \param[out] data_length      On success, the number of bytes written in
+ *                              \p data.
+ *
+ * \retval #PSA_SUCCESS  The RSA public key was exported successfully.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription
+ * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ */
+psa_status_t mbedtls_psa_rsa_export_public_key(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    uint8_t *data, size_t data_size, size_t *data_length);
+
+/**
+ * \brief Generate an RSA key.
+ *
+ * \note The signature of the function is that of a PSA driver generate_key
+ *       entry point.
+ *
+ * \param[in]  attributes         The attributes for the RSA key to generate.
+ * \param[in]  params             Production parameters for the key
+ *                                generation. This function only uses
+ *                                `params->data`,
+ *                                which contains the public exponent.
+ *                                This can be a null pointer if
+ *                                \c params_data_length is 0.
+ * \param params_data_length      Length of `params->data` in bytes.
+ *                                This can be 0, in which case the
+ *                                public exponent will be 65537.
+ * \param[out] key_buffer         Buffer where the key data is to be written.
+ * \param[in]  key_buffer_size    Size of \p key_buffer in bytes.
+ * \param[out] key_buffer_length  On success, the number of bytes written in
+ *                                \p key_buffer.
+ *
+ * \retval #PSA_SUCCESS
+ *         The key was successfully generated.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         Key length or type not supported.
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of \p key_buffer is too small.
+ */
+psa_status_t mbedtls_psa_rsa_generate_key(
+    const psa_key_attributes_t *attributes,
+    const psa_key_production_parameters_t *params, size_t params_data_length,
+    uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length);
+
+/** Sign an already-calculated hash with an RSA private key.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       sign_hash entry point. This function behaves as a sign_hash
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in]  attributes       The attributes of the RSA key to use for the
+ *                              operation.
+ * \param[in]  key_buffer       The buffer containing the RSA key context.
+ *                              format.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[in]  alg              A signature algorithm that is compatible with
+ *                              an RSA key.
+ * \param[in]  hash             The hash or message to sign.
+ * \param[in]  hash_length      Size of the \p hash buffer in bytes.
+ * \param[out] signature        Buffer where the signature is to be written.
+ * \param[in]  signature_size   Size of the \p signature buffer in bytes.
+ * \param[out] signature_length On success, the number of bytes
+ *                              that make up the returned signature value.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p signature buffer is too small. You can
+ *         determine a sufficient buffer size by calling
+ *         #PSA_SIGN_OUTPUT_SIZE(\c PSA_KEY_TYPE_RSA_KEY_PAIR, \c key_bits,
+ *         \p alg) where \c key_bits is the bit-size of the RSA key.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription
+ */
+psa_status_t mbedtls_psa_rsa_sign_hash(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+    uint8_t *signature, size_t signature_size, size_t *signature_length);
+
+/**
+ * \brief Verify the signature a hash or short message using a public RSA key.
+ *
+ * \note The signature of this function is that of a PSA driver
+ *       verify_hash entry point. This function behaves as a verify_hash
+ *       entry point as defined in the PSA driver interface specification for
+ *       transparent drivers.
+ *
+ * \param[in]  attributes       The attributes of the RSA key to use for the
+ *                              operation.
+ * \param[in]  key_buffer       The buffer containing the RSA key context.
+ *                              format.
+ * \param[in]  key_buffer_size  Size of the \p key_buffer buffer in bytes.
+ * \param[in]  alg              A signature algorithm that is compatible with
+ *                              an RSA key.
+ * \param[in]  hash             The hash or message whose signature is to be
+ *                              verified.
+ * \param[in]  hash_length      Size of the \p hash buffer in bytes.
+ * \param[in]  signature        Buffer containing the signature to verify.
+ * \param[in]  signature_length Size of the \p signature buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ *         The signature is valid.
+ * \retval #PSA_ERROR_INVALID_SIGNATURE
+ *         The calculation was performed successfully, but the passed
+ *         signature is not a valid signature.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ */
+psa_status_t mbedtls_psa_rsa_verify_hash(
+    const psa_key_attributes_t *attributes,
+    const uint8_t *key_buffer, size_t key_buffer_size,
+    psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+    const uint8_t *signature, size_t signature_length);
+
+/**
+ * \brief Encrypt a short message with a public key.
+ *
+ * \param attributes            The attributes for the key to import.
+ * \param key_buffer            Buffer where the key data is to be written.
+ * \param key_buffer_size       Size of the \p key_buffer buffer in bytes.
+ * \param input_length          Size of the \p input buffer in bytes.
+ * \param[in] salt              A salt or label, if supported by the
+ *                              encryption algorithm.
+ *                              If the algorithm does not support a
+ *                              salt, pass \c NULL.
+ *                              If the algorithm supports an optional
+ *                              salt and you do not want to pass a salt,
+ *                              pass \c NULL.
+ *
+ *                              - For #PSA_ALG_RSA_PKCS1V15_CRYPT, no salt is
+ *                                supported.
+ * \param salt_length           Size of the \p salt buffer in bytes.
+ *                              If \p salt is \c NULL, pass 0.
+ * \param[out] output           Buffer where the encrypted message is to
+ *                              be written.
+ * \param output_size           Size of the \p output buffer in bytes.
+ * \param[out] output_length    On success, the number of bytes
+ *                              that make up the returned output.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p output buffer is too small. You can
+ *         determine a sufficient buffer size by calling
+ *         #PSA_ASYMMETRIC_ENCRYPT_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg)
+ *         where \c key_type and \c key_bits are the type and bit-size
+ *         respectively of \p key.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription
+ * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The library has not been previously initialized by psa_crypto_init().
+ *         It is implementation-dependent whether a failure to initialize
+ *         results in this error code.
+ */
+psa_status_t mbedtls_psa_asymmetric_encrypt(const psa_key_attributes_t *attributes,
+                                            const uint8_t *key_buffer,
+                                            size_t key_buffer_size,
+                                            psa_algorithm_t alg,
+                                            const uint8_t *input,
+                                            size_t input_length,
+                                            const uint8_t *salt,
+                                            size_t salt_length,
+                                            uint8_t *output,
+                                            size_t output_size,
+                                            size_t *output_length);
+
+/**
+ * \brief Decrypt a short message with a private key.
+ *
+ * \param attributes            The attributes for the key to import.
+ * \param key_buffer            Buffer where the key data is to be written.
+ * \param key_buffer_size       Size of the \p key_buffer buffer in bytes.
+ * \param[in] input             The message to decrypt.
+ * \param input_length          Size of the \p input buffer in bytes.
+ * \param[in] salt              A salt or label, if supported by the
+ *                              encryption algorithm.
+ *                              If the algorithm does not support a
+ *                              salt, pass \c NULL.
+ *                              If the algorithm supports an optional
+ *                              salt and you do not want to pass a salt,
+ *                              pass \c NULL.
+ *
+ *                              - For #PSA_ALG_RSA_PKCS1V15_CRYPT, no salt is
+ *                                supported.
+ * \param salt_length           Size of the \p salt buffer in bytes.
+ *                              If \p salt is \c NULL, pass 0.
+ * \param[out] output           Buffer where the decrypted message is to
+ *                              be written.
+ * \param output_size           Size of the \c output buffer in bytes.
+ * \param[out] output_length    On success, the number of bytes
+ *                              that make up the returned output.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p output buffer is too small. You can
+ *         determine a sufficient buffer size by calling
+ *         #PSA_ASYMMETRIC_DECRYPT_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg)
+ *         where \c key_type and \c key_bits are the type and bit-size
+ *         respectively of \p key.
+ * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription
+ * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription
+ * \retval #PSA_ERROR_INVALID_PADDING \emptydescription
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The library has not been previously initialized by psa_crypto_init().
+ *         It is implementation-dependent whether a failure to initialize
+ *         results in this error code.
+ */
+psa_status_t mbedtls_psa_asymmetric_decrypt(const psa_key_attributes_t *attributes,
+                                            const uint8_t *key_buffer,
+                                            size_t key_buffer_size,
+                                            psa_algorithm_t alg,
+                                            const uint8_t *input,
+                                            size_t input_length,
+                                            const uint8_t *salt,
+                                            size_t salt_length,
+                                            uint8_t *output,
+                                            size_t output_size,
+                                            size_t *output_length);
+
+#endif /* PSA_CRYPTO_RSA_H */
diff --git a/tf-psa-crypto/core/psa_crypto_se.c b/tf-psa-crypto/core/psa_crypto_se.c
new file mode 100644
index 0000000..7a36a4f
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_se.c
@@ -0,0 +1,373 @@
+/*
+ *  PSA crypto support for secure element drivers
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+
+#include <stdint.h>
+#include <string.h>
+
+#include "psa/crypto_se_driver.h"
+
+#include "psa_crypto_se.h"
+
+#if defined(MBEDTLS_PSA_ITS_FILE_C)
+#include "psa_crypto_its.h"
+#else /* Native ITS implementation */
+#include "psa/error.h"
+#include "psa/internal_trusted_storage.h"
+#endif
+
+#include "mbedtls/platform.h"
+
+
+
+/****************************************************************/
+/* Driver lookup */
+/****************************************************************/
+
+/* This structure is identical to psa_drv_se_context_t declared in
+ * `crypto_se_driver.h`, except that some parts are writable here
+ * (non-const, or pointer to non-const). */
+typedef struct {
+    void *persistent_data;
+    size_t persistent_data_size;
+    uintptr_t transient_data;
+} psa_drv_se_internal_context_t;
+
+struct psa_se_drv_table_entry_s {
+    psa_key_location_t location;
+    const psa_drv_se_t *methods;
+    union {
+        psa_drv_se_internal_context_t internal;
+        psa_drv_se_context_t context;
+    } u;
+};
+
+static psa_se_drv_table_entry_t driver_table[PSA_MAX_SE_DRIVERS];
+
+psa_se_drv_table_entry_t *psa_get_se_driver_entry(
+    psa_key_lifetime_t lifetime)
+{
+    size_t i;
+    psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION(lifetime);
+    /* In the driver table, location=0 means an entry that isn't used.
+     * No driver has a location of 0 because it's a reserved value
+     * (which designates transparent keys). Make sure we never return
+     * a driver entry for location 0. */
+    if (location == 0) {
+        return NULL;
+    }
+    for (i = 0; i < PSA_MAX_SE_DRIVERS; i++) {
+        if (driver_table[i].location == location) {
+            return &driver_table[i];
+        }
+    }
+    return NULL;
+}
+
+const psa_drv_se_t *psa_get_se_driver_methods(
+    const psa_se_drv_table_entry_t *driver)
+{
+    return driver->methods;
+}
+
+psa_drv_se_context_t *psa_get_se_driver_context(
+    psa_se_drv_table_entry_t *driver)
+{
+    return &driver->u.context;
+}
+
+int psa_get_se_driver(psa_key_lifetime_t lifetime,
+                      const psa_drv_se_t **p_methods,
+                      psa_drv_se_context_t **p_drv_context)
+{
+    psa_se_drv_table_entry_t *driver = psa_get_se_driver_entry(lifetime);
+    if (p_methods != NULL) {
+        *p_methods = (driver ? driver->methods : NULL);
+    }
+    if (p_drv_context != NULL) {
+        *p_drv_context = (driver ? &driver->u.context : NULL);
+    }
+    return driver != NULL;
+}
+
+
+
+/****************************************************************/
+/* Persistent data management */
+/****************************************************************/
+
+static psa_status_t psa_get_se_driver_its_file_uid(
+    const psa_se_drv_table_entry_t *driver,
+    psa_storage_uid_t *uid)
+{
+    if (driver->location > PSA_MAX_SE_LOCATION) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    /* ITS file sizes are limited to 32 bits. */
+    if (driver->u.internal.persistent_data_size > UINT32_MAX) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    /* See the documentation of PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE. */
+    *uid = PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE + driver->location;
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_load_se_persistent_data(
+    const psa_se_drv_table_entry_t *driver)
+{
+    psa_status_t status;
+    psa_storage_uid_t uid;
+    size_t length;
+
+    status = psa_get_se_driver_its_file_uid(driver, &uid);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    /* Read the amount of persistent data that the driver requests.
+     * If the data in storage is larger, it is truncated. If the data
+     * in storage is smaller, silently keep what is already at the end
+     * of the output buffer. */
+    /* psa_get_se_driver_its_file_uid ensures that the size_t
+     * persistent_data_size is in range, but compilers don't know that,
+     * so cast to reassure them. */
+    return psa_its_get(uid, 0,
+                       (uint32_t) driver->u.internal.persistent_data_size,
+                       driver->u.internal.persistent_data,
+                       &length);
+}
+
+psa_status_t psa_save_se_persistent_data(
+    const psa_se_drv_table_entry_t *driver)
+{
+    psa_status_t status;
+    psa_storage_uid_t uid;
+
+    status = psa_get_se_driver_its_file_uid(driver, &uid);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    /* psa_get_se_driver_its_file_uid ensures that the size_t
+     * persistent_data_size is in range, but compilers don't know that,
+     * so cast to reassure them. */
+    return psa_its_set(uid,
+                       (uint32_t) driver->u.internal.persistent_data_size,
+                       driver->u.internal.persistent_data,
+                       0);
+}
+
+psa_status_t psa_destroy_se_persistent_data(psa_key_location_t location)
+{
+    psa_storage_uid_t uid;
+    if (location > PSA_MAX_SE_LOCATION) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+    uid = PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE + location;
+    return psa_its_remove(uid);
+}
+
+psa_status_t psa_find_se_slot_for_key(
+    const psa_key_attributes_t *attributes,
+    psa_key_creation_method_t method,
+    psa_se_drv_table_entry_t *driver,
+    psa_key_slot_number_t *slot_number)
+{
+    psa_status_t status;
+    psa_key_location_t key_location =
+        PSA_KEY_LIFETIME_GET_LOCATION(psa_get_key_lifetime(attributes));
+
+    /* If the location is wrong, it's a bug in the library. */
+    if (driver->location != key_location) {
+        return PSA_ERROR_CORRUPTION_DETECTED;
+    }
+
+    /* If the driver doesn't support key creation in any way, give up now. */
+    if (driver->methods->key_management == NULL) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    if (psa_get_key_slot_number(attributes, slot_number) == PSA_SUCCESS) {
+        /* The application wants to use a specific slot. Allow it if
+         * the driver supports it. On a system with isolation,
+         * the crypto service must check that the application is
+         * permitted to request this slot. */
+        psa_drv_se_validate_slot_number_t p_validate_slot_number =
+            driver->methods->key_management->p_validate_slot_number;
+        if (p_validate_slot_number == NULL) {
+            return PSA_ERROR_NOT_SUPPORTED;
+        }
+        status = p_validate_slot_number(&driver->u.context,
+                                        driver->u.internal.persistent_data,
+                                        attributes, method,
+                                        *slot_number);
+    } else if (method == PSA_KEY_CREATION_REGISTER) {
+        /* The application didn't specify a slot number. This doesn't
+         * make sense when registering a slot. */
+        return PSA_ERROR_INVALID_ARGUMENT;
+    } else {
+        /* The application didn't tell us which slot to use. Let the driver
+         * choose. This is the normal case. */
+        psa_drv_se_allocate_key_t p_allocate =
+            driver->methods->key_management->p_allocate;
+        if (p_allocate == NULL) {
+            return PSA_ERROR_NOT_SUPPORTED;
+        }
+        status = p_allocate(&driver->u.context,
+                            driver->u.internal.persistent_data,
+                            attributes, method,
+                            slot_number);
+    }
+    return status;
+}
+
+psa_status_t psa_destroy_se_key(psa_se_drv_table_entry_t *driver,
+                                psa_key_slot_number_t slot_number)
+{
+    psa_status_t status;
+    psa_status_t storage_status;
+    /* Normally a missing method would mean that the action is not
+     * supported. But psa_destroy_key() is not supposed to return
+     * PSA_ERROR_NOT_SUPPORTED: if you can create a key, you should
+     * be able to destroy it. The only use case for a driver that
+     * does not have a way to destroy keys at all is if the keys are
+     * locked in a read-only state: we can use the keys but not
+     * destroy them. Hence, if the driver doesn't support destroying
+     * keys, it's really a lack of permission. */
+    if (driver->methods->key_management == NULL ||
+        driver->methods->key_management->p_destroy == NULL) {
+        return PSA_ERROR_NOT_PERMITTED;
+    }
+    status = driver->methods->key_management->p_destroy(
+        &driver->u.context,
+        driver->u.internal.persistent_data,
+        slot_number);
+    storage_status = psa_save_se_persistent_data(driver);
+    return status == PSA_SUCCESS ? storage_status : status;
+}
+
+psa_status_t psa_init_all_se_drivers(void)
+{
+    size_t i;
+    for (i = 0; i < PSA_MAX_SE_DRIVERS; i++) {
+        psa_se_drv_table_entry_t *driver = &driver_table[i];
+        if (driver->location == 0) {
+            continue; /* skipping unused entry */
+        }
+        const psa_drv_se_t *methods = psa_get_se_driver_methods(driver);
+        if (methods->p_init != NULL) {
+            psa_status_t status = methods->p_init(
+                &driver->u.context,
+                driver->u.internal.persistent_data,
+                driver->location);
+            if (status != PSA_SUCCESS) {
+                return status;
+            }
+            status = psa_save_se_persistent_data(driver);
+            if (status != PSA_SUCCESS) {
+                return status;
+            }
+        }
+    }
+    return PSA_SUCCESS;
+}
+
+
+
+/****************************************************************/
+/* Driver registration */
+/****************************************************************/
+
+psa_status_t psa_register_se_driver(
+    psa_key_location_t location,
+    const psa_drv_se_t *methods)
+{
+    size_t i;
+    psa_status_t status;
+
+    if (methods->hal_version != PSA_DRV_SE_HAL_VERSION) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+    /* Driver table entries are 0-initialized. 0 is not a valid driver
+     * location because it means a transparent key. */
+    MBEDTLS_STATIC_ASSERT(PSA_KEY_LOCATION_LOCAL_STORAGE == 0,
+                          "Secure element support requires 0 to mean a local key");
+
+    if (location == PSA_KEY_LOCATION_LOCAL_STORAGE) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+    if (location > PSA_MAX_SE_LOCATION) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    for (i = 0; i < PSA_MAX_SE_DRIVERS; i++) {
+        if (driver_table[i].location == 0) {
+            break;
+        }
+        /* Check that location isn't already in use up to the first free
+         * entry. Since entries are created in order and never deleted,
+         * there can't be a used entry after the first free entry. */
+        if (driver_table[i].location == location) {
+            return PSA_ERROR_ALREADY_EXISTS;
+        }
+    }
+    if (i == PSA_MAX_SE_DRIVERS) {
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+
+    driver_table[i].location = location;
+    driver_table[i].methods = methods;
+    driver_table[i].u.internal.persistent_data_size =
+        methods->persistent_data_size;
+
+    if (methods->persistent_data_size != 0) {
+        driver_table[i].u.internal.persistent_data =
+            mbedtls_calloc(1, methods->persistent_data_size);
+        if (driver_table[i].u.internal.persistent_data == NULL) {
+            status = PSA_ERROR_INSUFFICIENT_MEMORY;
+            goto error;
+        }
+        /* Load the driver's persistent data. On first use, the persistent
+         * data does not exist in storage, and is initialized to
+         * all-bits-zero by the calloc call just above. */
+        status = psa_load_se_persistent_data(&driver_table[i]);
+        if (status != PSA_SUCCESS && status != PSA_ERROR_DOES_NOT_EXIST) {
+            goto error;
+        }
+    }
+
+    return PSA_SUCCESS;
+
+error:
+    memset(&driver_table[i], 0, sizeof(driver_table[i]));
+    return status;
+}
+
+void psa_unregister_all_se_drivers(void)
+{
+    size_t i;
+    for (i = 0; i < PSA_MAX_SE_DRIVERS; i++) {
+        if (driver_table[i].u.internal.persistent_data != NULL) {
+            mbedtls_free(driver_table[i].u.internal.persistent_data);
+        }
+    }
+    memset(driver_table, 0, sizeof(driver_table));
+}
+
+
+
+/****************************************************************/
+/* The end */
+/****************************************************************/
+
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
diff --git a/tf-psa-crypto/core/psa_crypto_se.h b/tf-psa-crypto/core/psa_crypto_se.h
new file mode 100644
index 0000000..e0bd5ac
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_se.h
@@ -0,0 +1,192 @@
+/*
+ *  PSA crypto support for secure element drivers
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_SE_H
+#define PSA_CRYPTO_SE_H
+
+/*
+ * Include the build-time configuration information header. Here, we do not
+ * include `"mbedtls/build_info.h"` directly but `"psa/build_info.h"`, which
+ * is basically just an alias to it. This is to ease the maintenance of the
+ * TF-PSA-Crypto repository which has a different build system and
+ * configuration.
+ */
+#include "psa/build_info.h"
+
+#include "psa/crypto.h"
+#include "psa/crypto_se_driver.h"
+
+/** The maximum location value that this implementation supports
+ * for a secure element.
+ *
+ * This is not a characteristic that each PSA implementation has, but a
+ * limitation of the current implementation due to the constraints imposed
+ * by storage. See #PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE.
+ *
+ * The minimum location value for a secure element is 1, like on any
+ * PSA implementation (0 means a transparent key).
+ */
+#define PSA_MAX_SE_LOCATION 255
+
+/** The base of the range of ITS file identifiers for secure element
+ * driver persistent data.
+ *
+ * We use a slice of the implementation reserved range 0xffff0000..0xffffffff,
+ * specifically the range 0xfffffe00..0xfffffeff. The length of this range
+ * drives the value of #PSA_MAX_SE_LOCATION. The identifier 0xfffffe00 is
+ * actually not used since it corresponds to #PSA_KEY_LOCATION_LOCAL_STORAGE
+ * which doesn't have a driver.
+ */
+#define PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE ((psa_key_id_t) 0xfffffe00)
+
+/** The maximum number of registered secure element driver locations. */
+#define PSA_MAX_SE_DRIVERS 4
+
+/** Unregister all secure element drivers.
+ *
+ * \warning Do not call this function while the library is in the initialized
+ *          state. This function is only intended to be called at the end
+ *          of mbedtls_psa_crypto_free().
+ */
+void psa_unregister_all_se_drivers(void);
+
+/** Initialize all secure element drivers.
+ *
+ * Called from psa_crypto_init().
+ */
+psa_status_t psa_init_all_se_drivers(void);
+
+/** A structure that describes a registered secure element driver.
+ *
+ * A secure element driver table entry contains a pointer to the
+ * driver's method table as well as the driver context structure.
+ */
+typedef struct psa_se_drv_table_entry_s psa_se_drv_table_entry_t;
+
+/** Return the secure element driver information for a lifetime value.
+ *
+ * \param lifetime              The lifetime value to query.
+ * \param[out] p_methods        On output, if there is a driver,
+ *                              \c *methods points to its method table.
+ *                              Otherwise \c *methods is \c NULL.
+ * \param[out] p_drv_context    On output, if there is a driver,
+ *                              \c *drv_context points to its context
+ *                              structure.
+ *                              Otherwise \c *drv_context is \c NULL.
+ *
+ * \retval 1
+ *         \p lifetime corresponds to a registered driver.
+ * \retval 0
+ *         \p lifetime does not correspond to a registered driver.
+ */
+int psa_get_se_driver(psa_key_lifetime_t lifetime,
+                      const psa_drv_se_t **p_methods,
+                      psa_drv_se_context_t **p_drv_context);
+
+/** Return the secure element driver table entry for a lifetime value.
+ *
+ * \param lifetime      The lifetime value to query.
+ *
+ * \return The driver table entry for \p lifetime, or
+ *         \p NULL if \p lifetime does not correspond to a registered driver.
+ */
+psa_se_drv_table_entry_t *psa_get_se_driver_entry(
+    psa_key_lifetime_t lifetime);
+
+/** Return the method table for a secure element driver.
+ *
+ * \param[in] driver    The driver table entry to access, or \c NULL.
+ *
+ * \return The driver's method table.
+ *         \c NULL if \p driver is \c NULL.
+ */
+const psa_drv_se_t *psa_get_se_driver_methods(
+    const psa_se_drv_table_entry_t *driver);
+
+/** Return the context of a secure element driver.
+ *
+ * \param[in] driver    The driver table entry to access, or \c NULL.
+ *
+ * \return A pointer to the driver context.
+ *         \c NULL if \p driver is \c NULL.
+ */
+psa_drv_se_context_t *psa_get_se_driver_context(
+    psa_se_drv_table_entry_t *driver);
+
+/** Find a free slot for a key that is to be created.
+ *
+ * This function calls the relevant method in the driver to find a suitable
+ * slot for a key with the given attributes.
+ *
+ * \param[in] attributes    Metadata about the key that is about to be created.
+ * \param[in] driver        The driver table entry to query.
+ * \param[out] slot_number  On success, a slot number that is free in this
+ *                          secure element.
+ */
+psa_status_t psa_find_se_slot_for_key(
+    const psa_key_attributes_t *attributes,
+    psa_key_creation_method_t method,
+    psa_se_drv_table_entry_t *driver,
+    psa_key_slot_number_t *slot_number);
+
+/** Destroy a key in a secure element.
+ *
+ * This function calls the relevant driver method to destroy a key
+ * and updates the driver's persistent data.
+ */
+psa_status_t psa_destroy_se_key(psa_se_drv_table_entry_t *driver,
+                                psa_key_slot_number_t slot_number);
+
+/** Load the persistent data of a secure element driver.
+ *
+ * \param driver        The driver table entry containing the persistent
+ *                      data to load from storage.
+ *
+ * \return #PSA_SUCCESS
+ * \return #PSA_ERROR_NOT_SUPPORTED
+ * \return #PSA_ERROR_DOES_NOT_EXIST
+ * \return #PSA_ERROR_STORAGE_FAILURE
+ * \return #PSA_ERROR_DATA_CORRUPT
+ * \return #PSA_ERROR_INVALID_ARGUMENT
+ */
+psa_status_t psa_load_se_persistent_data(
+    const psa_se_drv_table_entry_t *driver);
+
+/** Save the persistent data of a secure element driver.
+ *
+ * \param[in] driver    The driver table entry containing the persistent
+ *                      data to save to storage.
+ *
+ * \return #PSA_SUCCESS
+ * \return #PSA_ERROR_NOT_SUPPORTED
+ * \return #PSA_ERROR_NOT_PERMITTED
+ * \return #PSA_ERROR_NOT_SUPPORTED
+ * \return #PSA_ERROR_INSUFFICIENT_STORAGE
+ * \return #PSA_ERROR_STORAGE_FAILURE
+ * \return #PSA_ERROR_INVALID_ARGUMENT
+ */
+psa_status_t psa_save_se_persistent_data(
+    const psa_se_drv_table_entry_t *driver);
+
+/** Destroy the persistent data of a secure element driver.
+ *
+ * This is currently only used for testing.
+ *
+ * \param[in] location  The location identifier for the driver whose
+ *                      persistent data is to be erased.
+ */
+psa_status_t psa_destroy_se_persistent_data(psa_key_location_t location);
+
+
+/** The storage representation of a key whose data is in a secure element.
+ */
+typedef struct {
+    uint8_t slot_number[sizeof(psa_key_slot_number_t)];
+} psa_se_key_data_storage_t;
+
+#endif /* PSA_CRYPTO_SE_H */
diff --git a/tf-psa-crypto/core/psa_crypto_slot_management.c b/tf-psa-crypto/core/psa_crypto_slot_management.c
new file mode 100644
index 0000000..9986a44
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_slot_management.c
@@ -0,0 +1,690 @@
+/*
+ *  PSA crypto layer on top of Mbed TLS crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include "psa/crypto.h"
+
+#include "psa_crypto_core.h"
+#include "psa_crypto_driver_wrappers_no_static.h"
+#include "psa_crypto_slot_management.h"
+#include "psa_crypto_storage.h"
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+#include "psa_crypto_se.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "mbedtls/platform.h"
+#if defined(MBEDTLS_THREADING_C)
+#include "mbedtls/threading.h"
+#endif
+
+typedef struct {
+    psa_key_slot_t key_slots[MBEDTLS_PSA_KEY_SLOT_COUNT];
+    uint8_t key_slots_initialized;
+} psa_global_data_t;
+
+static psa_global_data_t global_data;
+
+static uint8_t psa_get_key_slots_initialized(void)
+{
+    uint8_t initialized;
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    initialized = global_data.key_slots_initialized;
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    return initialized;
+}
+
+int psa_is_valid_key_id(mbedtls_svc_key_id_t key, int vendor_ok)
+{
+    psa_key_id_t key_id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key);
+
+    if ((PSA_KEY_ID_USER_MIN <= key_id) &&
+        (key_id <= PSA_KEY_ID_USER_MAX)) {
+        return 1;
+    }
+
+    if (vendor_ok &&
+        (PSA_KEY_ID_VENDOR_MIN <= key_id) &&
+        (key_id <= PSA_KEY_ID_VENDOR_MAX)) {
+        return 1;
+    }
+
+    return 0;
+}
+
+/** Get the description in memory of a key given its identifier and lock it.
+ *
+ * The descriptions of volatile keys and loaded persistent keys are
+ * stored in key slots. This function returns a pointer to the key slot
+ * containing the description of a key given its identifier.
+ *
+ * The function searches the key slots containing the description of the key
+ * with \p key identifier. The function does only read accesses to the key
+ * slots. The function does not load any persistent key thus does not access
+ * any storage.
+ *
+ * For volatile key identifiers, only one key slot is queried as a volatile
+ * key with identifier key_id can only be stored in slot of index
+ * ( key_id - #PSA_KEY_ID_VOLATILE_MIN ).
+ *
+ * On success, the function locks the key slot. It is the responsibility of
+ * the caller to unlock the key slot when it does not access it anymore.
+ *
+ * If multi-threading is enabled, the caller must hold the
+ * global key slot mutex.
+ *
+ * \param key           Key identifier to query.
+ * \param[out] p_slot   On success, `*p_slot` contains a pointer to the
+ *                      key slot containing the description of the key
+ *                      identified by \p key.
+ *
+ * \retval #PSA_SUCCESS
+ *         The pointer to the key slot containing the description of the key
+ *         identified by \p key was returned.
+ * \retval #PSA_ERROR_INVALID_HANDLE
+ *         \p key is not a valid key identifier.
+ * \retval #PSA_ERROR_DOES_NOT_EXIST
+ *         There is no key with key identifier \p key in the key slots.
+ */
+static psa_status_t psa_get_and_lock_key_slot_in_memory(
+    mbedtls_svc_key_id_t key, psa_key_slot_t **p_slot)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_id_t key_id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key);
+    size_t slot_idx;
+    psa_key_slot_t *slot = NULL;
+
+    if (psa_key_id_is_volatile(key_id)) {
+        slot = &global_data.key_slots[key_id - PSA_KEY_ID_VOLATILE_MIN];
+
+        /* Check if both the PSA key identifier key_id and the owner
+         * identifier of key match those of the key slot. */
+        if ((slot->state == PSA_SLOT_FULL) &&
+            (mbedtls_svc_key_id_equal(key, slot->attr.id))) {
+            status = PSA_SUCCESS;
+        } else {
+            status = PSA_ERROR_DOES_NOT_EXIST;
+        }
+    } else {
+        if (!psa_is_valid_key_id(key, 1)) {
+            return PSA_ERROR_INVALID_HANDLE;
+        }
+
+        for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) {
+            slot = &global_data.key_slots[slot_idx];
+            /* Only consider slots which are in a full state. */
+            if ((slot->state == PSA_SLOT_FULL) &&
+                (mbedtls_svc_key_id_equal(key, slot->attr.id))) {
+                break;
+            }
+        }
+        status = (slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT) ?
+                 PSA_SUCCESS : PSA_ERROR_DOES_NOT_EXIST;
+    }
+
+    if (status == PSA_SUCCESS) {
+        status = psa_register_read(slot);
+        if (status == PSA_SUCCESS) {
+            *p_slot = slot;
+        }
+    }
+
+    return status;
+}
+
+psa_status_t psa_initialize_key_slots(void)
+{
+    /* Nothing to do: program startup and psa_wipe_all_key_slots() both
+     * guarantee that the key slots are initialized to all-zero, which
+     * means that all the key slots are in a valid, empty state. The global
+     * data mutex is already held when calling this function, so no need to
+     * lock it here, to set the flag. */
+    global_data.key_slots_initialized = 1;
+    return PSA_SUCCESS;
+}
+
+void psa_wipe_all_key_slots(void)
+{
+    size_t slot_idx;
+
+    for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) {
+        psa_key_slot_t *slot = &global_data.key_slots[slot_idx];
+        slot->registered_readers = 1;
+        slot->state = PSA_SLOT_PENDING_DELETION;
+        (void) psa_wipe_key_slot(slot);
+    }
+    /* The global data mutex is already held when calling this function. */
+    global_data.key_slots_initialized = 0;
+}
+
+psa_status_t psa_reserve_free_key_slot(psa_key_id_t *volatile_key_id,
+                                       psa_key_slot_t **p_slot)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    size_t slot_idx;
+    psa_key_slot_t *selected_slot, *unused_persistent_key_slot;
+
+    if (!psa_get_key_slots_initialized()) {
+        status = PSA_ERROR_BAD_STATE;
+        goto error;
+    }
+
+    selected_slot = unused_persistent_key_slot = NULL;
+    for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) {
+        psa_key_slot_t *slot = &global_data.key_slots[slot_idx];
+        if (slot->state == PSA_SLOT_EMPTY) {
+            selected_slot = slot;
+            break;
+        }
+
+        if ((unused_persistent_key_slot == NULL) &&
+            (slot->state == PSA_SLOT_FULL) &&
+            (!psa_key_slot_has_readers(slot)) &&
+            (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime))) {
+            unused_persistent_key_slot = slot;
+        }
+    }
+
+    /*
+     * If there is no unused key slot and there is at least one unlocked key
+     * slot containing the description of a persistent key, recycle the first
+     * such key slot we encountered. If we later need to operate on the
+     * persistent key we are evicting now, we will reload its description from
+     * storage.
+     */
+    if ((selected_slot == NULL) &&
+        (unused_persistent_key_slot != NULL)) {
+        selected_slot = unused_persistent_key_slot;
+        psa_register_read(selected_slot);
+        status = psa_wipe_key_slot(selected_slot);
+        if (status != PSA_SUCCESS) {
+            goto error;
+        }
+    }
+
+    if (selected_slot != NULL) {
+        status = psa_key_slot_state_transition(selected_slot, PSA_SLOT_EMPTY,
+                                               PSA_SLOT_FILLING);
+        if (status != PSA_SUCCESS) {
+            goto error;
+        }
+
+        *volatile_key_id = PSA_KEY_ID_VOLATILE_MIN +
+                           ((psa_key_id_t) (selected_slot - global_data.key_slots));
+        *p_slot = selected_slot;
+
+        return PSA_SUCCESS;
+    }
+    status = PSA_ERROR_INSUFFICIENT_MEMORY;
+
+error:
+    *p_slot = NULL;
+    *volatile_key_id = 0;
+
+    return status;
+}
+
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
+static psa_status_t psa_load_persistent_key_into_slot(psa_key_slot_t *slot)
+{
+    psa_status_t status = PSA_SUCCESS;
+    uint8_t *key_data = NULL;
+    size_t key_data_length = 0;
+
+    status = psa_load_persistent_key(&slot->attr,
+                                     &key_data, &key_data_length);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+    /* Special handling is required for loading keys associated with a
+     * dynamically registered SE interface. */
+    const psa_drv_se_t *drv;
+    psa_drv_se_context_t *drv_context;
+    if (psa_get_se_driver(slot->attr.lifetime, &drv, &drv_context)) {
+        psa_se_key_data_storage_t *data;
+
+        if (key_data_length != sizeof(*data)) {
+            status = PSA_ERROR_DATA_INVALID;
+            goto exit;
+        }
+        data = (psa_se_key_data_storage_t *) key_data;
+        status = psa_copy_key_material_into_slot(
+            slot, data->slot_number, sizeof(data->slot_number));
+        goto exit;
+    }
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+    status = psa_copy_key_material_into_slot(slot, key_data, key_data_length);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+exit:
+    psa_free_persistent_key_data(key_data, key_data_length);
+    return status;
+}
+#endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C */
+
+#if defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)
+
+static psa_status_t psa_load_builtin_key_into_slot(psa_key_slot_t *slot)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+    psa_key_lifetime_t lifetime = PSA_KEY_LIFETIME_VOLATILE;
+    psa_drv_slot_number_t slot_number = 0;
+    size_t key_buffer_size = 0;
+    size_t key_buffer_length = 0;
+
+    if (!psa_key_id_is_builtin(
+            MBEDTLS_SVC_KEY_ID_GET_KEY_ID(slot->attr.id))) {
+        return PSA_ERROR_DOES_NOT_EXIST;
+    }
+
+    /* Check the platform function to see whether this key actually exists */
+    status = mbedtls_psa_platform_get_builtin_key(
+        slot->attr.id, &lifetime, &slot_number);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    /* Set required key attributes to ensure get_builtin_key can retrieve the
+     * full attributes. */
+    psa_set_key_id(&attributes, slot->attr.id);
+    psa_set_key_lifetime(&attributes, lifetime);
+
+    /* Get the full key attributes from the driver in order to be able to
+     * calculate the required buffer size. */
+    status = psa_driver_wrapper_get_builtin_key(
+        slot_number, &attributes,
+        NULL, 0, NULL);
+    if (status != PSA_ERROR_BUFFER_TOO_SMALL) {
+        /* Builtin keys cannot be defined by the attributes alone */
+        if (status == PSA_SUCCESS) {
+            status = PSA_ERROR_CORRUPTION_DETECTED;
+        }
+        return status;
+    }
+
+    /* If the key should exist according to the platform, then ask the driver
+     * what its expected size is. */
+    status = psa_driver_wrapper_get_key_buffer_size(&attributes,
+                                                    &key_buffer_size);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    /* Allocate a buffer of the required size and load the builtin key directly
+     * into the (now properly sized) slot buffer. */
+    status = psa_allocate_buffer_to_slot(slot, key_buffer_size);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = psa_driver_wrapper_get_builtin_key(
+        slot_number, &attributes,
+        slot->key.data, slot->key.bytes, &key_buffer_length);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    /* Copy actual key length and core attributes into the slot on success */
+    slot->key.bytes = key_buffer_length;
+    slot->attr = attributes;
+exit:
+    if (status != PSA_SUCCESS) {
+        psa_remove_key_data_from_memory(slot);
+    }
+    return status;
+}
+#endif /* MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
+
+psa_status_t psa_get_and_lock_key_slot(mbedtls_svc_key_id_t key,
+                                       psa_key_slot_t **p_slot)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    *p_slot = NULL;
+    if (!psa_get_key_slots_initialized()) {
+        return PSA_ERROR_BAD_STATE;
+    }
+
+#if defined(MBEDTLS_THREADING_C)
+    /* We need to set status as success, otherwise CORRUPTION_DETECTED
+     * would be returned if the lock fails. */
+    status = PSA_SUCCESS;
+    /* If the key is persistent and not loaded, we cannot unlock the mutex
+     * between checking if the key is loaded and setting the slot as FULL,
+     * as otherwise another thread may load and then destroy the key
+     * in the meantime. */
+    PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+    /*
+     * On success, the pointer to the slot is passed directly to the caller
+     * thus no need to unlock the key slot here.
+     */
+    status = psa_get_and_lock_key_slot_in_memory(key, p_slot);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+#if defined(MBEDTLS_THREADING_C)
+        PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                                  &mbedtls_threading_key_slot_mutex));
+#endif
+        return status;
+    }
+
+    /* Loading keys from storage requires support for such a mechanism */
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) || \
+    defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)
+    psa_key_id_t volatile_key_id;
+
+    status = psa_reserve_free_key_slot(&volatile_key_id, p_slot);
+    if (status != PSA_SUCCESS) {
+#if defined(MBEDTLS_THREADING_C)
+        PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                                  &mbedtls_threading_key_slot_mutex));
+#endif
+        return status;
+    }
+
+    (*p_slot)->attr.id = key;
+    (*p_slot)->attr.lifetime = PSA_KEY_LIFETIME_PERSISTENT;
+
+    status = PSA_ERROR_DOES_NOT_EXIST;
+#if defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)
+    /* Load keys in the 'builtin' range through their own interface */
+    status = psa_load_builtin_key_into_slot(*p_slot);
+#endif /* MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
+
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
+    if (status == PSA_ERROR_DOES_NOT_EXIST) {
+        status = psa_load_persistent_key_into_slot(*p_slot);
+    }
+#endif /* defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) */
+
+    if (status != PSA_SUCCESS) {
+        psa_wipe_key_slot(*p_slot);
+
+        /* If the key does not exist, we need to return
+         * PSA_ERROR_INVALID_HANDLE. */
+        if (status == PSA_ERROR_DOES_NOT_EXIST) {
+            status = PSA_ERROR_INVALID_HANDLE;
+        }
+    } else {
+        /* Add implicit usage flags. */
+        psa_extend_key_usage_flags(&(*p_slot)->attr.policy.usage);
+
+        psa_key_slot_state_transition((*p_slot), PSA_SLOT_FILLING,
+                                      PSA_SLOT_FULL);
+        status = psa_register_read(*p_slot);
+    }
+
+#else /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
+    status = PSA_ERROR_INVALID_HANDLE;
+#endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
+
+    if (status != PSA_SUCCESS) {
+        *p_slot = NULL;
+    }
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+    return status;
+}
+
+psa_status_t psa_unregister_read(psa_key_slot_t *slot)
+{
+    if (slot == NULL) {
+        return PSA_SUCCESS;
+    }
+    if ((slot->state != PSA_SLOT_FULL) &&
+        (slot->state != PSA_SLOT_PENDING_DELETION)) {
+        return PSA_ERROR_CORRUPTION_DETECTED;
+    }
+
+    /* If we are the last reader and the slot is marked for deletion,
+     * we must wipe the slot here. */
+    if ((slot->state == PSA_SLOT_PENDING_DELETION) &&
+        (slot->registered_readers == 1)) {
+        return psa_wipe_key_slot(slot);
+    }
+
+    if (psa_key_slot_has_readers(slot)) {
+        slot->registered_readers--;
+        return PSA_SUCCESS;
+    }
+
+    /*
+     * As the return error code may not be handled in case of multiple errors,
+     * do our best to report if there are no registered readers. Assert with
+     * MBEDTLS_TEST_HOOK_TEST_ASSERT that there are registered readers:
+     * if the MBEDTLS_TEST_HOOKS configuration option is enabled and
+     * the function is called as part of the execution of a test suite, the
+     * execution of the test suite is stopped in error if the assertion fails.
+     */
+    MBEDTLS_TEST_HOOK_TEST_ASSERT(psa_key_slot_has_readers(slot));
+    return PSA_ERROR_CORRUPTION_DETECTED;
+}
+
+psa_status_t psa_unregister_read_under_mutex(psa_key_slot_t *slot)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+#if defined(MBEDTLS_THREADING_C)
+    /* We need to set status as success, otherwise CORRUPTION_DETECTED
+     * would be returned if the lock fails. */
+    status = PSA_SUCCESS;
+    PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+    status = psa_unregister_read(slot);
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+    return status;
+}
+
+psa_status_t psa_validate_key_location(psa_key_lifetime_t lifetime,
+                                       psa_se_drv_table_entry_t **p_drv)
+{
+    if (psa_key_lifetime_is_external(lifetime)) {
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+        /* Check whether a driver is registered against this lifetime */
+        psa_se_drv_table_entry_t *driver = psa_get_se_driver_entry(lifetime);
+        if (driver != NULL) {
+            if (p_drv != NULL) {
+                *p_drv = driver;
+            }
+            return PSA_SUCCESS;
+        }
+#else /* MBEDTLS_PSA_CRYPTO_SE_C */
+        (void) p_drv;
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+        /* Key location for external keys gets checked by the wrapper */
+        return PSA_SUCCESS;
+    } else {
+        /* Local/internal keys are always valid */
+        return PSA_SUCCESS;
+    }
+}
+
+psa_status_t psa_validate_key_persistence(psa_key_lifetime_t lifetime)
+{
+    if (PSA_KEY_LIFETIME_IS_VOLATILE(lifetime)) {
+        /* Volatile keys are always supported */
+        return PSA_SUCCESS;
+    } else {
+        /* Persistent keys require storage support */
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
+        if (PSA_KEY_LIFETIME_IS_READ_ONLY(lifetime)) {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        } else {
+            return PSA_SUCCESS;
+        }
+#else /* MBEDTLS_PSA_CRYPTO_STORAGE_C */
+        return PSA_ERROR_NOT_SUPPORTED;
+#endif /* !MBEDTLS_PSA_CRYPTO_STORAGE_C */
+    }
+}
+
+psa_status_t psa_open_key(mbedtls_svc_key_id_t key, psa_key_handle_t *handle)
+{
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) || \
+    defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)
+    psa_status_t status;
+    psa_key_slot_t *slot;
+
+    status = psa_get_and_lock_key_slot(key, &slot);
+    if (status != PSA_SUCCESS) {
+        *handle = PSA_KEY_HANDLE_INIT;
+        if (status == PSA_ERROR_INVALID_HANDLE) {
+            status = PSA_ERROR_DOES_NOT_EXIST;
+        }
+
+        return status;
+    }
+
+    *handle = key;
+
+    return psa_unregister_read_under_mutex(slot);
+
+#else /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
+    (void) key;
+    *handle = PSA_KEY_HANDLE_INIT;
+    return PSA_ERROR_NOT_SUPPORTED;
+#endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
+}
+
+psa_status_t psa_close_key(psa_key_handle_t handle)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+
+    if (psa_key_handle_is_null(handle)) {
+        return PSA_SUCCESS;
+    }
+
+#if defined(MBEDTLS_THREADING_C)
+    /* We need to set status as success, otherwise CORRUPTION_DETECTED
+     * would be returned if the lock fails. */
+    status = PSA_SUCCESS;
+    PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+    status = psa_get_and_lock_key_slot_in_memory(handle, &slot);
+    if (status != PSA_SUCCESS) {
+        if (status == PSA_ERROR_DOES_NOT_EXIST) {
+            status = PSA_ERROR_INVALID_HANDLE;
+        }
+#if defined(MBEDTLS_THREADING_C)
+        PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                                  &mbedtls_threading_key_slot_mutex));
+#endif
+        return status;
+    }
+
+    if (slot->registered_readers == 1) {
+        status = psa_wipe_key_slot(slot);
+    } else {
+        status = psa_unregister_read(slot);
+    }
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+
+    return status;
+}
+
+psa_status_t psa_purge_key(mbedtls_svc_key_id_t key)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_key_slot_t *slot;
+
+#if defined(MBEDTLS_THREADING_C)
+    /* We need to set status as success, otherwise CORRUPTION_DETECTED
+     * would be returned if the lock fails. */
+    status = PSA_SUCCESS;
+    PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+    status = psa_get_and_lock_key_slot_in_memory(key, &slot);
+    if (status != PSA_SUCCESS) {
+#if defined(MBEDTLS_THREADING_C)
+        PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                                  &mbedtls_threading_key_slot_mutex));
+#endif
+        return status;
+    }
+
+    if ((!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) &&
+        (slot->registered_readers == 1)) {
+        status = psa_wipe_key_slot(slot);
+    } else {
+        status = psa_unregister_read(slot);
+    }
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+
+    return status;
+}
+
+void mbedtls_psa_get_stats(mbedtls_psa_stats_t *stats)
+{
+    size_t slot_idx;
+
+    memset(stats, 0, sizeof(*stats));
+
+    for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) {
+        const psa_key_slot_t *slot = &global_data.key_slots[slot_idx];
+        if (psa_key_slot_has_readers(slot)) {
+            ++stats->locked_slots;
+        }
+        if (slot->state == PSA_SLOT_EMPTY) {
+            ++stats->empty_slots;
+            continue;
+        }
+        if (PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
+            ++stats->volatile_slots;
+        } else {
+            psa_key_id_t id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(slot->attr.id);
+            ++stats->persistent_slots;
+            if (id > stats->max_open_internal_key_id) {
+                stats->max_open_internal_key_id = id;
+            }
+        }
+        if (PSA_KEY_LIFETIME_GET_LOCATION(slot->attr.lifetime) !=
+            PSA_KEY_LOCATION_LOCAL_STORAGE) {
+            psa_key_id_t id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(slot->attr.id);
+            ++stats->external_slots;
+            if (id > stats->max_open_external_key_id) {
+                stats->max_open_external_key_id = id;
+            }
+        }
+    }
+}
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/tf-psa-crypto/core/psa_crypto_slot_management.h b/tf-psa-crypto/core/psa_crypto_slot_management.h
new file mode 100644
index 0000000..a84be7d
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_slot_management.h
@@ -0,0 +1,288 @@
+/*
+ *  PSA crypto layer on top of Mbed TLS crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_SLOT_MANAGEMENT_H
+#define PSA_CRYPTO_SLOT_MANAGEMENT_H
+
+#include "psa/crypto.h"
+#include "psa_crypto_core.h"
+#include "psa_crypto_se.h"
+
+/** Range of volatile key identifiers.
+ *
+ *  The last #MBEDTLS_PSA_KEY_SLOT_COUNT identifiers of the implementation
+ *  range of key identifiers are reserved for volatile key identifiers.
+ *  A volatile key identifier is equal to #PSA_KEY_ID_VOLATILE_MIN plus the
+ *  index of the key slot containing the volatile key definition.
+ */
+
+/** The minimum value for a volatile key identifier.
+ */
+#define PSA_KEY_ID_VOLATILE_MIN  (PSA_KEY_ID_VENDOR_MAX - \
+                                  MBEDTLS_PSA_KEY_SLOT_COUNT + 1)
+
+/** The maximum value for a volatile key identifier.
+ */
+#define PSA_KEY_ID_VOLATILE_MAX  PSA_KEY_ID_VENDOR_MAX
+
+/** Test whether a key identifier is a volatile key identifier.
+ *
+ * \param key_id  Key identifier to test.
+ *
+ * \retval 1
+ *         The key identifier is a volatile key identifier.
+ * \retval 0
+ *         The key identifier is not a volatile key identifier.
+ */
+static inline int psa_key_id_is_volatile(psa_key_id_t key_id)
+{
+    return (key_id >= PSA_KEY_ID_VOLATILE_MIN) &&
+           (key_id <= PSA_KEY_ID_VOLATILE_MAX);
+}
+
+/** Get the description of a key given its identifier and lock it.
+ *
+ * The descriptions of volatile keys and loaded persistent keys are stored in
+ * key slots. This function returns a pointer to the key slot containing the
+ * description of a key given its identifier.
+ *
+ * In case of a persistent key, the function loads the description of the key
+ * into a key slot if not already done.
+ *
+ * On success, the returned key slot has been registered for reading.
+ * It is the responsibility of the caller to call psa_unregister_read(slot)
+ * when they have finished reading the contents of the slot.
+ *
+ * On failure, `*p_slot` is set to NULL. This ensures that it is always valid
+ * to call psa_unregister_read on the returned slot.
+ *
+ * \param key           Key identifier to query.
+ * \param[out] p_slot   On success, `*p_slot` contains a pointer to the
+ *                      key slot containing the description of the key
+ *                      identified by \p key.
+ *
+ * \retval #PSA_SUCCESS
+ *         \p *p_slot contains a pointer to the key slot containing the
+ *         description of the key identified by \p key.
+ *         The key slot counter has been incremented.
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The library has not been initialized.
+ * \retval #PSA_ERROR_INVALID_HANDLE
+ *         \p key is not a valid key identifier.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
+ *         \p key is a persistent key identifier. The implementation does not
+ *         have sufficient resources to load the persistent key. This can be
+ *         due to a lack of empty key slot, or available memory.
+ * \retval #PSA_ERROR_DOES_NOT_EXIST
+ *         There is no key with key identifier \p key.
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription
+ */
+psa_status_t psa_get_and_lock_key_slot(mbedtls_svc_key_id_t key,
+                                       psa_key_slot_t **p_slot);
+
+/** Initialize the key slot structures.
+ *
+ * \retval #PSA_SUCCESS
+ *         Currently this function always succeeds.
+ */
+psa_status_t psa_initialize_key_slots(void);
+
+/** Delete all data from key slots in memory.
+ * This function is not thread safe, it wipes every key slot regardless of
+ * state and reader count. It should only be called when no slot is in use.
+ *
+ * This does not affect persistent storage. */
+void psa_wipe_all_key_slots(void);
+
+/** Find a free key slot and reserve it to be filled with a key.
+ *
+ * This function finds a key slot that is free,
+ * sets its state to PSA_SLOT_FILLING and then returns the slot.
+ *
+ * On success, the key slot's state is PSA_SLOT_FILLING.
+ * It is the responsibility of the caller to change the slot's state to
+ * PSA_SLOT_EMPTY/FULL once key creation has finished.
+ *
+ * If multi-threading is enabled, the caller must hold the
+ * global key slot mutex.
+ *
+ * \param[out] volatile_key_id   On success, volatile key identifier
+ *                               associated to the returned slot.
+ * \param[out] p_slot            On success, a pointer to the slot.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
+ *         There were no free key slots.
+ * \retval #PSA_ERROR_BAD_STATE \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED
+ *         This function attempted to operate on a key slot which was in an
+ *         unexpected state.
+ */
+psa_status_t psa_reserve_free_key_slot(psa_key_id_t *volatile_key_id,
+                                       psa_key_slot_t **p_slot);
+
+/** Change the state of a key slot.
+ *
+ * This function changes the state of the key slot from expected_state to
+ * new state. If the state of the slot was not expected_state, the state is
+ * unchanged.
+ *
+ * If multi-threading is enabled, the caller must hold the
+ * global key slot mutex.
+ *
+ * \param[in] slot            The key slot.
+ * \param[in] expected_state  The current state of the slot.
+ * \param[in] new_state       The new state of the slot.
+ *
+ * \retval #PSA_SUCCESS
+               The key slot's state variable is new_state.
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED
+ *             The slot's state was not expected_state.
+ */
+static inline psa_status_t psa_key_slot_state_transition(
+    psa_key_slot_t *slot, psa_key_slot_state_t expected_state,
+    psa_key_slot_state_t new_state)
+{
+    if (slot->state != expected_state) {
+        return PSA_ERROR_CORRUPTION_DETECTED;
+    }
+    slot->state = new_state;
+    return PSA_SUCCESS;
+}
+
+/** Register as a reader of a key slot.
+ *
+ * This function increments the key slot registered reader counter by one.
+ * If multi-threading is enabled, the caller must hold the
+ * global key slot mutex.
+ *
+ * \param[in] slot  The key slot.
+ *
+ * \retval #PSA_SUCCESS
+               The key slot registered reader counter was incremented.
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED
+ *             The reader counter already reached its maximum value and was not
+ *             increased, or the slot's state was not PSA_SLOT_FULL.
+ */
+static inline psa_status_t psa_register_read(psa_key_slot_t *slot)
+{
+    if ((slot->state != PSA_SLOT_FULL) ||
+        (slot->registered_readers >= SIZE_MAX)) {
+        return PSA_ERROR_CORRUPTION_DETECTED;
+    }
+    slot->registered_readers++;
+
+    return PSA_SUCCESS;
+}
+
+/** Unregister from reading a key slot.
+ *
+ * This function decrements the key slot registered reader counter by one.
+ * If the state of the slot is PSA_SLOT_PENDING_DELETION,
+ * and there is only one registered reader (the caller),
+ * this function will call psa_wipe_key_slot().
+ * If multi-threading is enabled, the caller must hold the
+ * global key slot mutex.
+ *
+ * \note To ease the handling of errors in retrieving a key slot
+ *       a NULL input pointer is valid, and the function returns
+ *       successfully without doing anything in that case.
+ *
+ * \param[in] slot  The key slot.
+ * \retval #PSA_SUCCESS
+ *             \p slot is NULL or the key slot reader counter has been
+ *             decremented (and potentially wiped) successfully.
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED
+ *             The slot's state was neither PSA_SLOT_FULL nor
+ *             PSA_SLOT_PENDING_DELETION.
+ *             Or a wipe was attempted and the slot's state was not
+ *             PSA_SLOT_PENDING_DELETION.
+ *             Or registered_readers was equal to 0.
+ */
+psa_status_t psa_unregister_read(psa_key_slot_t *slot);
+
+/** Wrap a call to psa_unregister_read in the global key slot mutex.
+ *
+ * If threading is disabled, this simply calls psa_unregister_read.
+ *
+ * \note To ease the handling of errors in retrieving a key slot
+ *       a NULL input pointer is valid, and the function returns
+ *       successfully without doing anything in that case.
+ *
+ * \param[in] slot  The key slot.
+ * \retval #PSA_SUCCESS
+ *             \p slot is NULL or the key slot reader counter has been
+ *             decremented (and potentially wiped) successfully.
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED
+ *             The slot's state was neither PSA_SLOT_FULL nor
+ *             PSA_SLOT_PENDING_DELETION.
+ *             Or a wipe was attempted and the slot's state was not
+ *             PSA_SLOT_PENDING_DELETION.
+ *             Or registered_readers was equal to 0.
+ */
+psa_status_t psa_unregister_read_under_mutex(psa_key_slot_t *slot);
+
+/** Test whether a lifetime designates a key in an external cryptoprocessor.
+ *
+ * \param lifetime      The lifetime to test.
+ *
+ * \retval 1
+ *         The lifetime designates an external key. There should be a
+ *         registered driver for this lifetime, otherwise the key cannot
+ *         be created or manipulated.
+ * \retval 0
+ *         The lifetime designates a key that is volatile or in internal
+ *         storage.
+ */
+static inline int psa_key_lifetime_is_external(psa_key_lifetime_t lifetime)
+{
+    return PSA_KEY_LIFETIME_GET_LOCATION(lifetime)
+           != PSA_KEY_LOCATION_LOCAL_STORAGE;
+}
+
+/** Validate a key's location.
+ *
+ * This function checks whether the key's attributes point to a location that
+ * is known to the PSA Core, and returns the driver function table if the key
+ * is to be found in an external location.
+ *
+ * \param[in] lifetime      The key lifetime attribute.
+ * \param[out] p_drv        On success, when a key is located in external
+ *                          storage, returns a pointer to the driver table
+ *                          associated with the key's storage location.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ */
+psa_status_t psa_validate_key_location(psa_key_lifetime_t lifetime,
+                                       psa_se_drv_table_entry_t **p_drv);
+
+/** Validate the persistence of a key.
+ *
+ * \param[in] lifetime  The key lifetime attribute.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_NOT_SUPPORTED The key is persistent but persistent keys
+ *             are not supported.
+ */
+psa_status_t psa_validate_key_persistence(psa_key_lifetime_t lifetime);
+
+/** Validate a key identifier.
+ *
+ * \param[in] key           The key identifier.
+ * \param[in] vendor_ok     Non-zero to indicate that key identifiers in the
+ *                          vendor range are allowed, volatile key identifiers
+ *                          excepted \c 0 otherwise.
+ *
+ * \retval <> 0 if the key identifier is valid, 0 otherwise.
+ */
+int psa_is_valid_key_id(mbedtls_svc_key_id_t key, int vendor_ok);
+
+#endif /* PSA_CRYPTO_SLOT_MANAGEMENT_H */
diff --git a/tf-psa-crypto/core/psa_crypto_storage.c b/tf-psa-crypto/core/psa_crypto_storage.c
new file mode 100644
index 0000000..7d1317b
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_storage.c
@@ -0,0 +1,481 @@
+/*
+ *  PSA persistent key storage
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "psa/crypto.h"
+#include "psa_crypto_storage.h"
+#include "mbedtls/platform_util.h"
+
+#if defined(MBEDTLS_PSA_ITS_FILE_C)
+#include "psa_crypto_its.h"
+#else /* Native ITS implementation */
+#include "psa/error.h"
+#include "psa/internal_trusted_storage.h"
+#endif
+
+#include "mbedtls/platform.h"
+
+
+
+/****************************************************************/
+/* Key storage */
+/****************************************************************/
+
+/* Determine a file name (ITS file identifier) for the given key identifier.
+ * The file name must be distinct from any file that is used for a purpose
+ * other than storing a key. Currently, the only such file is the random seed
+ * file whose name is PSA_CRYPTO_ITS_RANDOM_SEED_UID and whose value is
+ * 0xFFFFFF52. */
+static psa_storage_uid_t psa_its_identifier_of_slot(mbedtls_svc_key_id_t key)
+{
+#if defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
+    /* Encode the owner in the upper 32 bits. This means that if
+     * owner values are nonzero (as they are on a PSA platform),
+     * no key file will ever have a value less than 0x100000000, so
+     * the whole range 0..0xffffffff is available for non-key files. */
+    uint32_t unsigned_owner_id = MBEDTLS_SVC_KEY_ID_GET_OWNER_ID(key);
+    return ((uint64_t) unsigned_owner_id << 32) |
+           MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key);
+#else
+    /* Use the key id directly as a file name.
+     * psa_is_key_id_valid() in psa_crypto_slot_management.c
+     * is responsible for ensuring that key identifiers do not have a
+     * value that is reserved for non-key files. */
+    return key;
+#endif
+}
+
+/**
+ * \brief Load persistent data for the given key slot number.
+ *
+ * This function reads data from a storage backend and returns the data in a
+ * buffer.
+ *
+ * \param key               Persistent identifier of the key to be loaded. This
+ *                          should be an occupied storage location.
+ * \param[out] data         Buffer where the data is to be written.
+ * \param data_size         Size of the \c data buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_DATA_INVALID \emptydescription
+ * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_DOES_NOT_EXIST \emptydescription
+ */
+static psa_status_t psa_crypto_storage_load(
+    const mbedtls_svc_key_id_t key, uint8_t *data, size_t data_size)
+{
+    psa_status_t status;
+    psa_storage_uid_t data_identifier = psa_its_identifier_of_slot(key);
+    struct psa_storage_info_t data_identifier_info;
+    size_t data_length = 0;
+
+    status = psa_its_get_info(data_identifier, &data_identifier_info);
+    if (status  != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = psa_its_get(data_identifier, 0, (uint32_t) data_size, data, &data_length);
+    if (data_size  != data_length) {
+        return PSA_ERROR_DATA_INVALID;
+    }
+
+    return status;
+}
+
+int psa_is_key_present_in_storage(const mbedtls_svc_key_id_t key)
+{
+    psa_status_t ret;
+    psa_storage_uid_t data_identifier = psa_its_identifier_of_slot(key);
+    struct psa_storage_info_t data_identifier_info;
+
+    ret = psa_its_get_info(data_identifier, &data_identifier_info);
+
+    if (ret == PSA_ERROR_DOES_NOT_EXIST) {
+        return 0;
+    }
+    return 1;
+}
+
+/**
+ * \brief Store persistent data for the given key slot number.
+ *
+ * This function stores the given data buffer to a persistent storage.
+ *
+ * \param key           Persistent identifier of the key to be stored. This
+ *                      should be an unoccupied storage location.
+ * \param[in] data      Buffer containing the data to be stored.
+ * \param data_length   The number of bytes
+ *                      that make up the data.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription
+ * \retval #PSA_ERROR_ALREADY_EXISTS \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_DATA_INVALID \emptydescription
+ */
+static psa_status_t psa_crypto_storage_store(const mbedtls_svc_key_id_t key,
+                                             const uint8_t *data,
+                                             size_t data_length)
+{
+    psa_status_t status;
+    psa_storage_uid_t data_identifier = psa_its_identifier_of_slot(key);
+    struct psa_storage_info_t data_identifier_info;
+
+    if (psa_is_key_present_in_storage(key) == 1) {
+        return PSA_ERROR_ALREADY_EXISTS;
+    }
+
+    status = psa_its_set(data_identifier, (uint32_t) data_length, data, 0);
+    if (status != PSA_SUCCESS) {
+        return PSA_ERROR_DATA_INVALID;
+    }
+
+    status = psa_its_get_info(data_identifier, &data_identifier_info);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    if (data_identifier_info.size != data_length) {
+        status = PSA_ERROR_DATA_INVALID;
+        goto exit;
+    }
+
+exit:
+    if (status != PSA_SUCCESS) {
+        /* Remove the file in case we managed to create it but something
+         * went wrong. It's ok if the file doesn't exist. If the file exists
+         * but the removal fails, we're already reporting an error so there's
+         * nothing else we can do. */
+        (void) psa_its_remove(data_identifier);
+    }
+    return status;
+}
+
+psa_status_t psa_destroy_persistent_key(const mbedtls_svc_key_id_t key)
+{
+    psa_status_t ret;
+    psa_storage_uid_t data_identifier = psa_its_identifier_of_slot(key);
+    struct psa_storage_info_t data_identifier_info;
+
+    ret = psa_its_get_info(data_identifier, &data_identifier_info);
+    if (ret == PSA_ERROR_DOES_NOT_EXIST) {
+        return PSA_SUCCESS;
+    }
+
+    if (psa_its_remove(data_identifier) != PSA_SUCCESS) {
+        return PSA_ERROR_DATA_INVALID;
+    }
+
+    ret = psa_its_get_info(data_identifier, &data_identifier_info);
+    if (ret != PSA_ERROR_DOES_NOT_EXIST) {
+        return PSA_ERROR_DATA_INVALID;
+    }
+
+    return PSA_SUCCESS;
+}
+
+/**
+ * \brief Get data length for given key slot number.
+ *
+ * \param key               Persistent identifier whose stored data length
+ *                          is to be obtained.
+ * \param[out] data_length  The number of bytes that make up the data.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_DOES_NOT_EXIST \emptydescription
+ * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription
+ */
+static psa_status_t psa_crypto_storage_get_data_length(
+    const mbedtls_svc_key_id_t key,
+    size_t *data_length)
+{
+    psa_status_t status;
+    psa_storage_uid_t data_identifier = psa_its_identifier_of_slot(key);
+    struct psa_storage_info_t data_identifier_info;
+
+    status = psa_its_get_info(data_identifier, &data_identifier_info);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    *data_length = (size_t) data_identifier_info.size;
+
+    return PSA_SUCCESS;
+}
+
+/**
+ * Persistent key storage magic header.
+ */
+#define PSA_KEY_STORAGE_MAGIC_HEADER "PSA\0KEY"
+#define PSA_KEY_STORAGE_MAGIC_HEADER_LENGTH (sizeof(PSA_KEY_STORAGE_MAGIC_HEADER))
+
+typedef struct {
+    uint8_t magic[PSA_KEY_STORAGE_MAGIC_HEADER_LENGTH];
+    uint8_t version[4];
+    uint8_t lifetime[sizeof(psa_key_lifetime_t)];
+    uint8_t type[2];
+    uint8_t bits[2];
+    uint8_t policy[sizeof(psa_key_policy_t)];
+    uint8_t data_len[4];
+    uint8_t key_data[];
+} psa_persistent_key_storage_format;
+
+void psa_format_key_data_for_storage(const uint8_t *data,
+                                     const size_t data_length,
+                                     const psa_key_attributes_t *attr,
+                                     uint8_t *storage_data)
+{
+    psa_persistent_key_storage_format *storage_format =
+        (psa_persistent_key_storage_format *) storage_data;
+
+    memcpy(storage_format->magic, PSA_KEY_STORAGE_MAGIC_HEADER,
+           PSA_KEY_STORAGE_MAGIC_HEADER_LENGTH);
+    MBEDTLS_PUT_UINT32_LE(0, storage_format->version, 0);
+    MBEDTLS_PUT_UINT32_LE(attr->lifetime, storage_format->lifetime, 0);
+    MBEDTLS_PUT_UINT16_LE((uint16_t) attr->type, storage_format->type, 0);
+    MBEDTLS_PUT_UINT16_LE((uint16_t) attr->bits, storage_format->bits, 0);
+    MBEDTLS_PUT_UINT32_LE(attr->policy.usage, storage_format->policy, 0);
+    MBEDTLS_PUT_UINT32_LE(attr->policy.alg, storage_format->policy, sizeof(uint32_t));
+    MBEDTLS_PUT_UINT32_LE(attr->policy.alg2, storage_format->policy, 2 * sizeof(uint32_t));
+    MBEDTLS_PUT_UINT32_LE(data_length, storage_format->data_len, 0);
+    memcpy(storage_format->key_data, data, data_length);
+}
+
+static psa_status_t check_magic_header(const uint8_t *data)
+{
+    if (memcmp(data, PSA_KEY_STORAGE_MAGIC_HEADER,
+               PSA_KEY_STORAGE_MAGIC_HEADER_LENGTH) != 0) {
+        return PSA_ERROR_DATA_INVALID;
+    }
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_parse_key_data_from_storage(const uint8_t *storage_data,
+                                             size_t storage_data_length,
+                                             uint8_t **key_data,
+                                             size_t *key_data_length,
+                                             psa_key_attributes_t *attr)
+{
+    psa_status_t status;
+    const psa_persistent_key_storage_format *storage_format =
+        (const psa_persistent_key_storage_format *) storage_data;
+    uint32_t version;
+
+    if (storage_data_length < sizeof(*storage_format)) {
+        return PSA_ERROR_DATA_INVALID;
+    }
+
+    status = check_magic_header(storage_data);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    version = MBEDTLS_GET_UINT32_LE(storage_format->version, 0);
+    if (version != 0) {
+        return PSA_ERROR_DATA_INVALID;
+    }
+
+    *key_data_length = MBEDTLS_GET_UINT32_LE(storage_format->data_len, 0);
+    if (*key_data_length > (storage_data_length - sizeof(*storage_format)) ||
+        *key_data_length > PSA_CRYPTO_MAX_STORAGE_SIZE) {
+        return PSA_ERROR_DATA_INVALID;
+    }
+
+    if (*key_data_length == 0) {
+        *key_data = NULL;
+    } else {
+        *key_data = mbedtls_calloc(1, *key_data_length);
+        if (*key_data == NULL) {
+            return PSA_ERROR_INSUFFICIENT_MEMORY;
+        }
+        memcpy(*key_data, storage_format->key_data, *key_data_length);
+    }
+
+    attr->lifetime = MBEDTLS_GET_UINT32_LE(storage_format->lifetime, 0);
+    attr->type = MBEDTLS_GET_UINT16_LE(storage_format->type, 0);
+    attr->bits = MBEDTLS_GET_UINT16_LE(storage_format->bits, 0);
+    attr->policy.usage = MBEDTLS_GET_UINT32_LE(storage_format->policy, 0);
+    attr->policy.alg = MBEDTLS_GET_UINT32_LE(storage_format->policy, sizeof(uint32_t));
+    attr->policy.alg2 = MBEDTLS_GET_UINT32_LE(storage_format->policy, 2 * sizeof(uint32_t));
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_save_persistent_key(const psa_key_attributes_t *attr,
+                                     const uint8_t *data,
+                                     const size_t data_length)
+{
+    size_t storage_data_length;
+    uint8_t *storage_data;
+    psa_status_t status;
+
+    /* All keys saved to persistent storage always have a key context */
+    if (data == NULL || data_length == 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (data_length > PSA_CRYPTO_MAX_STORAGE_SIZE) {
+        return PSA_ERROR_INSUFFICIENT_STORAGE;
+    }
+    storage_data_length = data_length + sizeof(psa_persistent_key_storage_format);
+
+    storage_data = mbedtls_calloc(1, storage_data_length);
+    if (storage_data == NULL) {
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+
+    psa_format_key_data_for_storage(data, data_length, attr, storage_data);
+
+    status = psa_crypto_storage_store(attr->id,
+                                      storage_data, storage_data_length);
+
+    mbedtls_zeroize_and_free(storage_data, storage_data_length);
+
+    return status;
+}
+
+void psa_free_persistent_key_data(uint8_t *key_data, size_t key_data_length)
+{
+    mbedtls_zeroize_and_free(key_data, key_data_length);
+}
+
+psa_status_t psa_load_persistent_key(psa_key_attributes_t *attr,
+                                     uint8_t **data,
+                                     size_t *data_length)
+{
+    psa_status_t status = PSA_SUCCESS;
+    uint8_t *loaded_data;
+    size_t storage_data_length = 0;
+    mbedtls_svc_key_id_t key = attr->id;
+
+    status = psa_crypto_storage_get_data_length(key, &storage_data_length);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    loaded_data = mbedtls_calloc(1, storage_data_length);
+
+    if (loaded_data == NULL) {
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+
+    status = psa_crypto_storage_load(key, loaded_data, storage_data_length);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
+    status = psa_parse_key_data_from_storage(loaded_data, storage_data_length,
+                                             data, data_length, attr);
+
+    /* All keys saved to persistent storage always have a key context */
+    if (status == PSA_SUCCESS &&
+        (*data == NULL || *data_length == 0)) {
+        status = PSA_ERROR_STORAGE_FAILURE;
+    }
+
+exit:
+    mbedtls_zeroize_and_free(loaded_data, storage_data_length);
+    return status;
+}
+
+
+
+/****************************************************************/
+/* Transactions */
+/****************************************************************/
+
+#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS)
+
+psa_crypto_transaction_t psa_crypto_transaction;
+
+psa_status_t psa_crypto_save_transaction(void)
+{
+    struct psa_storage_info_t p_info;
+    psa_status_t status;
+    status = psa_its_get_info(PSA_CRYPTO_ITS_TRANSACTION_UID, &p_info);
+    if (status == PSA_SUCCESS) {
+        /* This shouldn't happen: we're trying to start a transaction while
+         * there is still a transaction that hasn't been replayed. */
+        return PSA_ERROR_CORRUPTION_DETECTED;
+    } else if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        return status;
+    }
+    return psa_its_set(PSA_CRYPTO_ITS_TRANSACTION_UID,
+                       sizeof(psa_crypto_transaction),
+                       &psa_crypto_transaction,
+                       0);
+}
+
+psa_status_t psa_crypto_load_transaction(void)
+{
+    psa_status_t status;
+    size_t length;
+    status = psa_its_get(PSA_CRYPTO_ITS_TRANSACTION_UID, 0,
+                         sizeof(psa_crypto_transaction),
+                         &psa_crypto_transaction, &length);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+    if (length != sizeof(psa_crypto_transaction)) {
+        return PSA_ERROR_DATA_INVALID;
+    }
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_stop_transaction(void)
+{
+    psa_status_t status = psa_its_remove(PSA_CRYPTO_ITS_TRANSACTION_UID);
+    /* Whether or not updating the storage succeeded, the transaction is
+     * finished now. It's too late to go back, so zero out the in-memory
+     * data. */
+    memset(&psa_crypto_transaction, 0, sizeof(psa_crypto_transaction));
+    return status;
+}
+
+#endif /* PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS */
+
+
+
+/****************************************************************/
+/* Random generator state */
+/****************************************************************/
+
+#if defined(MBEDTLS_PSA_INJECT_ENTROPY)
+psa_status_t mbedtls_psa_storage_inject_entropy(const unsigned char *seed,
+                                                size_t seed_size)
+{
+    psa_status_t status;
+    struct psa_storage_info_t p_info;
+
+    status = psa_its_get_info(PSA_CRYPTO_ITS_RANDOM_SEED_UID, &p_info);
+
+    if (PSA_ERROR_DOES_NOT_EXIST == status) { /* No seed exists */
+        status = psa_its_set(PSA_CRYPTO_ITS_RANDOM_SEED_UID, seed_size, seed, 0);
+    } else if (PSA_SUCCESS == status) {
+        /* You should not be here. Seed needs to be injected only once */
+        status = PSA_ERROR_NOT_PERMITTED;
+    }
+    return status;
+}
+#endif /* MBEDTLS_PSA_INJECT_ENTROPY */
+
+
+
+/****************************************************************/
+/* The end */
+/****************************************************************/
+
+#endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C */
diff --git a/tf-psa-crypto/core/psa_crypto_storage.h b/tf-psa-crypto/core/psa_crypto_storage.h
new file mode 100644
index 0000000..d7f5b18
--- /dev/null
+++ b/tf-psa-crypto/core/psa_crypto_storage.h
@@ -0,0 +1,385 @@
+/**
+ * \file psa_crypto_storage.h
+ *
+ * \brief PSA cryptography module: Mbed TLS key storage
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef PSA_CRYPTO_STORAGE_H
+#define PSA_CRYPTO_STORAGE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "psa/crypto.h"
+#include "psa/crypto_se_driver.h"
+
+#include <stdint.h>
+#include <string.h>
+
+/* Limit the maximum key size in storage. This should have no effect
+ * since the key size is limited in memory. */
+#define PSA_CRYPTO_MAX_STORAGE_SIZE (PSA_BITS_TO_BYTES(PSA_MAX_KEY_BITS))
+/* Sanity check: a file size must fit in 32 bits. Allow a generous
+ * 64kB of metadata. */
+#if PSA_CRYPTO_MAX_STORAGE_SIZE > 0xffff0000
+#error "PSA_CRYPTO_MAX_STORAGE_SIZE > 0xffff0000"
+#endif
+
+/** The maximum permitted persistent slot number.
+ *
+ * In Mbed Crypto 0.1.0b:
+ * - Using the file backend, all key ids are ok except 0.
+ * - Using the ITS backend, all key ids are ok except 0xFFFFFF52
+ *   (#PSA_CRYPTO_ITS_RANDOM_SEED_UID) for which the file contains the
+ *   device's random seed (if this feature is enabled).
+ * - Only key ids from 1 to #MBEDTLS_PSA_KEY_SLOT_COUNT are actually used.
+ *
+ * Since we need to preserve the random seed, avoid using that key slot.
+ * Reserve a whole range of key slots just in case something else comes up.
+ *
+ * This limitation will probably become moot when we implement client
+ * separation for key storage.
+ */
+#define PSA_MAX_PERSISTENT_KEY_IDENTIFIER PSA_KEY_ID_VENDOR_MAX
+
+/**
+ * \brief Checks if persistent data is stored for the given key slot number
+ *
+ * This function checks if any key data or metadata exists for the key slot in
+ * the persistent storage.
+ *
+ * \param key           Persistent identifier to check.
+ *
+ * \retval 0
+ *         No persistent data present for slot number
+ * \retval 1
+ *         Persistent data present for slot number
+ */
+int psa_is_key_present_in_storage(const mbedtls_svc_key_id_t key);
+
+/**
+ * \brief Format key data and metadata and save to a location for given key
+ *        slot.
+ *
+ * This function formats the key data and metadata and saves it to a
+ * persistent storage backend. The storage location corresponding to the
+ * key slot must be empty, otherwise this function will fail. This function
+ * should be called after loading the key into an internal slot to ensure the
+ * persistent key is not saved into a storage location corresponding to an
+ * already occupied non-persistent key, as well as ensuring the key data is
+ * validated.
+ *
+ * Note: This function will only succeed for key buffers which are not
+ * empty. If passed a NULL pointer or zero-length, the function will fail
+ * with #PSA_ERROR_INVALID_ARGUMENT.
+ *
+ * \param[in] attr          The attributes of the key to save.
+ *                          The key identifier field in the attributes
+ *                          determines the key's location.
+ * \param[in] data          Buffer containing the key data.
+ * \param data_length       The number of bytes that make up the key data.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_ALREADY_EXISTS \emptydescription
+ * \retval #PSA_ERROR_DATA_INVALID \emptydescription
+ * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription
+ */
+psa_status_t psa_save_persistent_key(const psa_key_attributes_t *attr,
+                                     const uint8_t *data,
+                                     const size_t data_length);
+
+/**
+ * \brief Parses key data and metadata and load persistent key for given
+ * key slot number.
+ *
+ * This function reads from a storage backend, parses the key data and
+ * metadata and writes them to the appropriate output parameters.
+ *
+ * Note: This function allocates a buffer and returns a pointer to it through
+ * the data parameter. On successful return, the pointer is guaranteed to be
+ * valid and the buffer contains at least one byte of data.
+ * psa_free_persistent_key_data() must be called on the data buffer
+ * afterwards to zeroize and free this buffer.
+ *
+ * \param[in,out] attr      On input, the key identifier field identifies
+ *                          the key to load. Other fields are ignored.
+ *                          On success, the attribute structure contains
+ *                          the key metadata that was loaded from storage.
+ * \param[out] data         Pointer to an allocated key data buffer on return.
+ * \param[out] data_length  The number of bytes that make up the key data.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_DATA_INVALID \emptydescription
+ * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription
+ * \retval #PSA_ERROR_DOES_NOT_EXIST \emptydescription
+ */
+psa_status_t psa_load_persistent_key(psa_key_attributes_t *attr,
+                                     uint8_t **data,
+                                     size_t *data_length);
+
+/**
+ * \brief Remove persistent data for the given key slot number.
+ *
+ * \param key           Persistent identifier of the key to remove
+ *                      from persistent storage.
+ *
+ * \retval #PSA_SUCCESS
+ *         The key was successfully removed,
+ *         or the key did not exist.
+ * \retval #PSA_ERROR_DATA_INVALID \emptydescription
+ */
+psa_status_t psa_destroy_persistent_key(const mbedtls_svc_key_id_t key);
+
+/**
+ * \brief Free the temporary buffer allocated by psa_load_persistent_key().
+ *
+ * This function must be called at some point after psa_load_persistent_key()
+ * to zeroize and free the memory allocated to the buffer in that function.
+ *
+ * \param key_data        Buffer for the key data.
+ * \param key_data_length Size of the key data buffer.
+ *
+ */
+void psa_free_persistent_key_data(uint8_t *key_data, size_t key_data_length);
+
+/**
+ * \brief Formats key data and metadata for persistent storage
+ *
+ * \param[in] data          Buffer containing the key data.
+ * \param data_length       Length of the key data buffer.
+ * \param[in] attr          The core attributes of the key.
+ * \param[out] storage_data Output buffer for the formatted data.
+ *
+ */
+void psa_format_key_data_for_storage(const uint8_t *data,
+                                     const size_t data_length,
+                                     const psa_key_attributes_t *attr,
+                                     uint8_t *storage_data);
+
+/**
+ * \brief Parses persistent storage data into key data and metadata
+ *
+ * \param[in] storage_data     Buffer for the storage data.
+ * \param storage_data_length  Length of the storage data buffer
+ * \param[out] key_data        On output, pointer to a newly allocated buffer
+ *                             containing the key data. This must be freed
+ *                             using psa_free_persistent_key_data()
+ * \param[out] key_data_length Length of the key data buffer
+ * \param[out] attr            On success, the attribute structure is filled
+ *                             with the loaded key metadata.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_DATA_INVALID \emptydescription
+ */
+psa_status_t psa_parse_key_data_from_storage(const uint8_t *storage_data,
+                                             size_t storage_data_length,
+                                             uint8_t **key_data,
+                                             size_t *key_data_length,
+                                             psa_key_attributes_t *attr);
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+/** This symbol is defined if transaction support is required. */
+#define PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS 1
+#endif
+
+#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS)
+
+/** The type of transaction that is in progress.
+ */
+/* This is an integer type rather than an enum for two reasons: to support
+ * unknown values when loading a transaction file, and to ensure that the
+ * type has a known size.
+ */
+typedef uint16_t psa_crypto_transaction_type_t;
+
+/** No transaction is in progress.
+ *
+ * This has the value 0, so zero-initialization sets a transaction's type to
+ * this value.
+ */
+#define PSA_CRYPTO_TRANSACTION_NONE             ((psa_crypto_transaction_type_t) 0x0000)
+
+/** A key creation transaction.
+ *
+ * This is only used for keys in an external cryptoprocessor (secure element).
+ * Keys in RAM or in internal storage are created atomically in storage
+ * (simple file creation), so they do not need a transaction mechanism.
+ */
+#define PSA_CRYPTO_TRANSACTION_CREATE_KEY       ((psa_crypto_transaction_type_t) 0x0001)
+
+/** A key destruction transaction.
+ *
+ * This is only used for keys in an external cryptoprocessor (secure element).
+ * Keys in RAM or in internal storage are destroyed atomically in storage
+ * (simple file deletion), so they do not need a transaction mechanism.
+ */
+#define PSA_CRYPTO_TRANSACTION_DESTROY_KEY      ((psa_crypto_transaction_type_t) 0x0002)
+
+/** Transaction data.
+ *
+ * This type is designed to be serialized by writing the memory representation
+ * and reading it back on the same device.
+ *
+ * \note The transaction mechanism is not thread-safe. There can only be one
+ *       single active transaction at a time.
+ *       The transaction object is #psa_crypto_transaction.
+ *
+ * \note If an API call starts a transaction, it must complete this transaction
+ *       before returning to the application.
+ *
+ * The lifetime of a transaction is the following (note that only one
+ * transaction may be active at a time):
+ *
+ * -# Call psa_crypto_prepare_transaction() to initialize the transaction
+ *    object in memory and declare the type of transaction that is starting.
+ * -# Fill in the type-specific fields of #psa_crypto_transaction.
+ * -# Call psa_crypto_save_transaction() to start the transaction. This
+ *    saves the transaction data to internal storage.
+ * -# Perform the work of the transaction by modifying files, contacting
+ *    external entities, or whatever needs doing. Note that the transaction
+ *    may be interrupted by a power failure, so you need to have a way
+ *    recover from interruptions either by undoing what has been done
+ *    so far or by resuming where you left off.
+ * -# If there are intermediate stages in the transaction, update
+ *    the fields of #psa_crypto_transaction and call
+ *    psa_crypto_save_transaction() again when each stage is reached.
+ * -# When the transaction is over, call psa_crypto_stop_transaction() to
+ *    remove the transaction data in storage and in memory.
+ *
+ * If the system crashes while a transaction is in progress, psa_crypto_init()
+ * calls psa_crypto_load_transaction() and takes care of completing or
+ * rewinding the transaction. This is done in psa_crypto_recover_transaction()
+ * in psa_crypto.c. If you add a new type of transaction, be
+ * sure to add code for it in psa_crypto_recover_transaction().
+ */
+typedef union {
+    /* Each element of this union must have the following properties
+     * to facilitate serialization and deserialization:
+     *
+     * - The element is a struct.
+     * - The first field of the struct is `psa_crypto_transaction_type_t type`.
+     * - Elements of the struct are arranged such a way that there is
+     *   no padding.
+     */
+    struct psa_crypto_transaction_unknown_s {
+        psa_crypto_transaction_type_t type;
+        uint16_t unused1;
+        uint32_t unused2;
+        uint64_t unused3;
+        uint64_t unused4;
+    } unknown;
+    /* ::type is #PSA_CRYPTO_TRANSACTION_CREATE_KEY or
+     * #PSA_CRYPTO_TRANSACTION_DESTROY_KEY. */
+    struct psa_crypto_transaction_key_s {
+        psa_crypto_transaction_type_t type;
+        uint16_t unused1;
+        psa_key_lifetime_t lifetime;
+        psa_key_slot_number_t slot;
+        mbedtls_svc_key_id_t id;
+    } key;
+} psa_crypto_transaction_t;
+
+/** The single active transaction.
+ */
+extern psa_crypto_transaction_t psa_crypto_transaction;
+
+/** Prepare for a transaction.
+ *
+ * There must not be an ongoing transaction.
+ *
+ * \param type          The type of transaction to start.
+ */
+static inline void psa_crypto_prepare_transaction(
+    psa_crypto_transaction_type_t type)
+{
+    psa_crypto_transaction.unknown.type = type;
+}
+
+/** Save the transaction data to storage.
+ *
+ * You may call this function multiple times during a transaction to
+ * atomically update the transaction state.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ */
+psa_status_t psa_crypto_save_transaction(void);
+
+/** Load the transaction data from storage, if any.
+ *
+ * This function is meant to be called from psa_crypto_init() to recover
+ * in case a transaction was interrupted by a system crash.
+ *
+ * \retval #PSA_SUCCESS
+ *         The data about the ongoing transaction has been loaded to
+ *         #psa_crypto_transaction.
+ * \retval #PSA_ERROR_DOES_NOT_EXIST
+ *         There is no ongoing transaction.
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_DATA_INVALID \emptydescription
+ * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription
+ */
+psa_status_t psa_crypto_load_transaction(void);
+
+/** Indicate that the current transaction is finished.
+ *
+ * Call this function at the very end of transaction processing.
+ * This function does not "commit" or "abort" the transaction: the storage
+ * subsystem has no concept of "commit" and "abort", just saving and
+ * removing the transaction information in storage.
+ *
+ * This function erases the transaction data in storage (if any) and
+ * resets the transaction data in memory.
+ *
+ * \retval #PSA_SUCCESS
+ *         There was transaction data in storage.
+ * \retval #PSA_ERROR_DOES_NOT_EXIST
+ *         There was no transaction data in storage.
+ * \retval #PSA_ERROR_STORAGE_FAILURE
+ *         It was impossible to determine whether there was transaction data
+ *         in storage, or the transaction data could not be erased.
+ */
+psa_status_t psa_crypto_stop_transaction(void);
+
+/** The ITS file identifier for the transaction data.
+ *
+ * 0xffffffNN = special file; 0x74 = 't' for transaction.
+ */
+#define PSA_CRYPTO_ITS_TRANSACTION_UID ((psa_key_id_t) 0xffffff74)
+
+#endif /* PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS */
+
+#if defined(MBEDTLS_PSA_INJECT_ENTROPY)
+/** Backend side of mbedtls_psa_inject_entropy().
+ *
+ * This function stores the supplied data into the entropy seed file.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription
+ * \retval #PSA_ERROR_NOT_PERMITTED
+ *         The entropy seed file already exists.
+ */
+psa_status_t mbedtls_psa_storage_inject_entropy(const unsigned char *seed,
+                                                size_t seed_size);
+#endif /* MBEDTLS_PSA_INJECT_ENTROPY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PSA_CRYPTO_STORAGE_H */
diff --git a/tf-psa-crypto/core/psa_its_file.c b/tf-psa-crypto/core/psa_its_file.c
new file mode 100644
index 0000000..9567137
--- /dev/null
+++ b/tf-psa-crypto/core/psa_its_file.c
@@ -0,0 +1,254 @@
+/*
+ *  PSA ITS simulator over stdio files.
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_ITS_FILE_C)
+
+#include "mbedtls/platform.h"
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+#include "psa_crypto_its.h"
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#if !defined(PSA_ITS_STORAGE_PREFIX)
+#define PSA_ITS_STORAGE_PREFIX ""
+#endif
+
+#define PSA_ITS_STORAGE_FILENAME_PATTERN "%08x%08x"
+#define PSA_ITS_STORAGE_SUFFIX ".psa_its"
+#define PSA_ITS_STORAGE_FILENAME_LENGTH         \
+    (sizeof(PSA_ITS_STORAGE_PREFIX) - 1 +    /*prefix without terminating 0*/ \
+     16 +  /*UID (64-bit number in hex)*/                               \
+     sizeof(PSA_ITS_STORAGE_SUFFIX) - 1 +    /*suffix without terminating 0*/ \
+     1 /*terminating null byte*/)
+#define PSA_ITS_STORAGE_TEMP \
+    PSA_ITS_STORAGE_PREFIX "tempfile" PSA_ITS_STORAGE_SUFFIX
+
+/* The maximum value of psa_storage_info_t.size */
+#define PSA_ITS_MAX_SIZE 0xffffffff
+
+#define PSA_ITS_MAGIC_STRING "PSA\0ITS\0"
+#define PSA_ITS_MAGIC_LENGTH 8
+
+/* As rename fails on Windows if the new filepath already exists,
+ * use MoveFileExA with the MOVEFILE_REPLACE_EXISTING flag instead.
+ * Returns 0 on success, nonzero on failure. */
+#if defined(_WIN32)
+#define rename_replace_existing(oldpath, newpath) \
+    (!MoveFileExA(oldpath, newpath, MOVEFILE_REPLACE_EXISTING))
+#else
+#define rename_replace_existing(oldpath, newpath) rename(oldpath, newpath)
+#endif
+
+typedef struct {
+    uint8_t magic[PSA_ITS_MAGIC_LENGTH];
+    uint8_t size[sizeof(uint32_t)];
+    uint8_t flags[sizeof(psa_storage_create_flags_t)];
+} psa_its_file_header_t;
+
+static void psa_its_fill_filename(psa_storage_uid_t uid, char *filename)
+{
+    /* Break up the UID into two 32-bit pieces so as not to rely on
+     * long long support in snprintf. */
+    mbedtls_snprintf(filename, PSA_ITS_STORAGE_FILENAME_LENGTH,
+                     "%s" PSA_ITS_STORAGE_FILENAME_PATTERN "%s",
+                     PSA_ITS_STORAGE_PREFIX,
+                     (unsigned) (uid >> 32),
+                     (unsigned) (uid & 0xffffffff),
+                     PSA_ITS_STORAGE_SUFFIX);
+}
+
+static psa_status_t psa_its_read_file(psa_storage_uid_t uid,
+                                      struct psa_storage_info_t *p_info,
+                                      FILE **p_stream)
+{
+    char filename[PSA_ITS_STORAGE_FILENAME_LENGTH];
+    psa_its_file_header_t header;
+    size_t n;
+
+    *p_stream = NULL;
+    psa_its_fill_filename(uid, filename);
+    *p_stream = fopen(filename, "rb");
+    if (*p_stream == NULL) {
+        return PSA_ERROR_DOES_NOT_EXIST;
+    }
+
+    /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */
+    mbedtls_setbuf(*p_stream, NULL);
+
+    n = fread(&header, 1, sizeof(header), *p_stream);
+    if (n != sizeof(header)) {
+        return PSA_ERROR_DATA_CORRUPT;
+    }
+    if (memcmp(header.magic, PSA_ITS_MAGIC_STRING,
+               PSA_ITS_MAGIC_LENGTH) != 0) {
+        return PSA_ERROR_DATA_CORRUPT;
+    }
+
+    p_info->size  = MBEDTLS_GET_UINT32_LE(header.size, 0);
+    p_info->flags = MBEDTLS_GET_UINT32_LE(header.flags, 0);
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t psa_its_get_info(psa_storage_uid_t uid,
+                              struct psa_storage_info_t *p_info)
+{
+    psa_status_t status;
+    FILE *stream = NULL;
+    status = psa_its_read_file(uid, p_info, &stream);
+    if (stream != NULL) {
+        fclose(stream);
+    }
+    return status;
+}
+
+psa_status_t psa_its_get(psa_storage_uid_t uid,
+                         uint32_t data_offset,
+                         uint32_t data_length,
+                         void *p_data,
+                         size_t *p_data_length)
+{
+    psa_status_t status;
+    FILE *stream = NULL;
+    size_t n;
+    struct psa_storage_info_t info;
+
+    status = psa_its_read_file(uid, &info, &stream);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+    status = PSA_ERROR_INVALID_ARGUMENT;
+    if (data_offset + data_length < data_offset) {
+        goto exit;
+    }
+#if SIZE_MAX < 0xffffffff
+    if (data_offset + data_length > SIZE_MAX) {
+        goto exit;
+    }
+#endif
+    if (data_offset + data_length > info.size) {
+        goto exit;
+    }
+
+    status = PSA_ERROR_STORAGE_FAILURE;
+#if LONG_MAX < 0xffffffff
+    while (data_offset > LONG_MAX) {
+        if (fseek(stream, LONG_MAX, SEEK_CUR) != 0) {
+            goto exit;
+        }
+        data_offset -= LONG_MAX;
+    }
+#endif
+    if (fseek(stream, data_offset, SEEK_CUR) != 0) {
+        goto exit;
+    }
+    n = fread(p_data, 1, data_length, stream);
+    if (n != data_length) {
+        goto exit;
+    }
+    status = PSA_SUCCESS;
+    if (p_data_length != NULL) {
+        *p_data_length = n;
+    }
+
+exit:
+    if (stream != NULL) {
+        fclose(stream);
+    }
+    return status;
+}
+
+psa_status_t psa_its_set(psa_storage_uid_t uid,
+                         uint32_t data_length,
+                         const void *p_data,
+                         psa_storage_create_flags_t create_flags)
+{
+    if (uid == 0) {
+        return PSA_ERROR_INVALID_HANDLE;
+    }
+
+    psa_status_t status = PSA_ERROR_STORAGE_FAILURE;
+    char filename[PSA_ITS_STORAGE_FILENAME_LENGTH];
+    FILE *stream = NULL;
+    psa_its_file_header_t header;
+    size_t n;
+
+    memcpy(header.magic, PSA_ITS_MAGIC_STRING, PSA_ITS_MAGIC_LENGTH);
+    MBEDTLS_PUT_UINT32_LE(data_length, header.size, 0);
+    MBEDTLS_PUT_UINT32_LE(create_flags, header.flags, 0);
+
+    psa_its_fill_filename(uid, filename);
+    stream = fopen(PSA_ITS_STORAGE_TEMP, "wb");
+
+    if (stream == NULL) {
+        goto exit;
+    }
+
+    /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */
+    mbedtls_setbuf(stream, NULL);
+
+    status = PSA_ERROR_INSUFFICIENT_STORAGE;
+    n = fwrite(&header, 1, sizeof(header), stream);
+    if (n != sizeof(header)) {
+        goto exit;
+    }
+    if (data_length != 0) {
+        n = fwrite(p_data, 1, data_length, stream);
+        if (n != data_length) {
+            goto exit;
+        }
+    }
+    status = PSA_SUCCESS;
+
+exit:
+    if (stream != NULL) {
+        int ret = fclose(stream);
+        if (status == PSA_SUCCESS && ret != 0) {
+            status = PSA_ERROR_INSUFFICIENT_STORAGE;
+        }
+    }
+    if (status == PSA_SUCCESS) {
+        if (rename_replace_existing(PSA_ITS_STORAGE_TEMP, filename) != 0) {
+            status = PSA_ERROR_STORAGE_FAILURE;
+        }
+    }
+    /* The temporary file may still exist, but only in failure cases where
+     * we're already reporting an error. So there's nothing we can do on
+     * failure. If the function succeeded, and in some error cases, the
+     * temporary file doesn't exist and so remove() is expected to fail.
+     * Thus we just ignore the return status of remove(). */
+    (void) remove(PSA_ITS_STORAGE_TEMP);
+    return status;
+}
+
+psa_status_t psa_its_remove(psa_storage_uid_t uid)
+{
+    char filename[PSA_ITS_STORAGE_FILENAME_LENGTH];
+    FILE *stream;
+    psa_its_fill_filename(uid, filename);
+    stream = fopen(filename, "rb");
+    if (stream == NULL) {
+        return PSA_ERROR_DOES_NOT_EXIST;
+    }
+    fclose(stream);
+    if (remove(filename) != 0) {
+        return PSA_ERROR_STORAGE_FAILURE;
+    }
+    return PSA_SUCCESS;
+}
+
+#endif /* MBEDTLS_PSA_ITS_FILE_C */
diff --git a/tf-psa-crypto/core/psa_util.c b/tf-psa-crypto/core/psa_util.c
new file mode 100644
index 0000000..4ccc5b0
--- /dev/null
+++ b/tf-psa-crypto/core/psa_util.c
@@ -0,0 +1,602 @@
+/*
+ *  PSA hashing layer on top of Mbed TLS software crypto
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+/* This is needed for MBEDTLS_ERR_XXX macros */
+#include <mbedtls/error.h>
+
+#if defined(MBEDTLS_ASN1_WRITE_C)
+#include <mbedtls/asn1write.h>
+#include <psa/crypto_sizes.h>
+#endif
+
+#include "psa_util_internal.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_CLIENT)
+
+#include <psa/crypto.h>
+
+#if defined(MBEDTLS_MD_LIGHT)
+#include <mbedtls/md.h>
+#endif
+#if defined(MBEDTLS_LMS_C)
+#include <mbedtls/lms.h>
+#endif
+#if defined(MBEDTLS_SSL_TLS_C) && \
+    (defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3))
+#include <mbedtls/ssl.h>
+#endif
+#if defined(PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY) ||    \
+    defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC)
+#include <mbedtls/rsa.h>
+#endif
+#if defined(MBEDTLS_USE_PSA_CRYPTO) && \
+    defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY)
+#include <mbedtls/ecp.h>
+#endif
+#if defined(MBEDTLS_PK_C)
+#include <mbedtls/pk.h>
+#endif
+#if defined(MBEDTLS_BLOCK_CIPHER_SOME_PSA)
+#include <mbedtls/cipher.h>
+#endif
+#include <mbedtls/entropy.h>
+
+/* PSA_SUCCESS is kept at the top of each error table since
+ * it's the most common status when everything functions properly. */
+#if defined(MBEDTLS_MD_LIGHT)
+const mbedtls_error_pair_t psa_to_md_errors[] =
+{
+    { PSA_SUCCESS,                     0 },
+    { PSA_ERROR_NOT_SUPPORTED,         MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE },
+    { PSA_ERROR_INVALID_ARGUMENT,      MBEDTLS_ERR_MD_BAD_INPUT_DATA },
+    { PSA_ERROR_INSUFFICIENT_MEMORY,   MBEDTLS_ERR_MD_ALLOC_FAILED }
+};
+#endif
+
+#if defined(MBEDTLS_BLOCK_CIPHER_SOME_PSA)
+const mbedtls_error_pair_t psa_to_cipher_errors[] =
+{
+    { PSA_SUCCESS,                     0 },
+    { PSA_ERROR_NOT_SUPPORTED,         MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE },
+    { PSA_ERROR_INVALID_ARGUMENT,      MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA },
+    { PSA_ERROR_INSUFFICIENT_MEMORY,   MBEDTLS_ERR_CIPHER_ALLOC_FAILED }
+};
+#endif
+
+#if defined(MBEDTLS_LMS_C)
+const mbedtls_error_pair_t psa_to_lms_errors[] =
+{
+    { PSA_SUCCESS,                     0 },
+    { PSA_ERROR_BUFFER_TOO_SMALL,      MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL },
+    { PSA_ERROR_INVALID_ARGUMENT,      MBEDTLS_ERR_LMS_BAD_INPUT_DATA }
+};
+#endif
+
+#if defined(MBEDTLS_SSL_TLS_C) && \
+    (defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3))
+const mbedtls_error_pair_t psa_to_ssl_errors[] =
+{
+    { PSA_SUCCESS,                     0 },
+    { PSA_ERROR_INSUFFICIENT_MEMORY,   MBEDTLS_ERR_SSL_ALLOC_FAILED },
+    { PSA_ERROR_NOT_SUPPORTED,         MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE },
+    { PSA_ERROR_INVALID_SIGNATURE,     MBEDTLS_ERR_SSL_INVALID_MAC },
+    { PSA_ERROR_INVALID_ARGUMENT,      MBEDTLS_ERR_SSL_BAD_INPUT_DATA },
+    { PSA_ERROR_BAD_STATE,             MBEDTLS_ERR_SSL_INTERNAL_ERROR },
+    { PSA_ERROR_BUFFER_TOO_SMALL,      MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL }
+};
+#endif
+
+#if defined(PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY) ||    \
+    defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC)
+const mbedtls_error_pair_t psa_to_pk_rsa_errors[] =
+{
+    { PSA_SUCCESS,                     0 },
+    { PSA_ERROR_NOT_PERMITTED,         MBEDTLS_ERR_RSA_BAD_INPUT_DATA },
+    { PSA_ERROR_INVALID_ARGUMENT,      MBEDTLS_ERR_RSA_BAD_INPUT_DATA },
+    { PSA_ERROR_INVALID_HANDLE,        MBEDTLS_ERR_RSA_BAD_INPUT_DATA },
+    { PSA_ERROR_BUFFER_TOO_SMALL,      MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE },
+    { PSA_ERROR_INSUFFICIENT_ENTROPY,  MBEDTLS_ERR_RSA_RNG_FAILED },
+    { PSA_ERROR_INVALID_SIGNATURE,     MBEDTLS_ERR_RSA_VERIFY_FAILED },
+    { PSA_ERROR_INVALID_PADDING,       MBEDTLS_ERR_RSA_INVALID_PADDING }
+};
+#endif
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO) && \
+    defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY)
+const mbedtls_error_pair_t psa_to_pk_ecdsa_errors[] =
+{
+    { PSA_SUCCESS,                     0 },
+    { PSA_ERROR_NOT_PERMITTED,         MBEDTLS_ERR_ECP_BAD_INPUT_DATA },
+    { PSA_ERROR_INVALID_ARGUMENT,      MBEDTLS_ERR_ECP_BAD_INPUT_DATA },
+    { PSA_ERROR_INVALID_HANDLE,        MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE },
+    { PSA_ERROR_BUFFER_TOO_SMALL,      MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL },
+    { PSA_ERROR_INSUFFICIENT_ENTROPY,  MBEDTLS_ERR_ECP_RANDOM_FAILED },
+    { PSA_ERROR_INVALID_SIGNATURE,     MBEDTLS_ERR_ECP_VERIFY_FAILED }
+};
+#endif
+
+int psa_generic_status_to_mbedtls(psa_status_t status)
+{
+    switch (status) {
+        case PSA_SUCCESS:
+            return 0;
+        case PSA_ERROR_NOT_SUPPORTED:
+            return MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED;
+        case PSA_ERROR_CORRUPTION_DETECTED:
+            return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+        case PSA_ERROR_COMMUNICATION_FAILURE:
+        case PSA_ERROR_HARDWARE_FAILURE:
+            return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED;
+        case PSA_ERROR_NOT_PERMITTED:
+        default:
+            return MBEDTLS_ERR_ERROR_GENERIC_ERROR;
+    }
+}
+
+int psa_status_to_mbedtls(psa_status_t status,
+                          const mbedtls_error_pair_t *local_translations,
+                          size_t local_errors_num,
+                          int (*fallback_f)(psa_status_t))
+{
+    for (size_t i = 0; i < local_errors_num; i++) {
+        if (status == local_translations[i].psa_status) {
+            return local_translations[i].mbedtls_error;
+        }
+    }
+    return fallback_f(status);
+}
+
+#if defined(MBEDTLS_PK_C)
+int psa_pk_status_to_mbedtls(psa_status_t status)
+{
+    switch (status) {
+        case PSA_ERROR_INVALID_HANDLE:
+            return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT;
+        case PSA_ERROR_BUFFER_TOO_SMALL:
+            return MBEDTLS_ERR_PK_BUFFER_TOO_SMALL;
+        case PSA_ERROR_NOT_SUPPORTED:
+            return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
+        case PSA_ERROR_INVALID_ARGUMENT:
+            return MBEDTLS_ERR_PK_INVALID_ALG;
+        case PSA_ERROR_NOT_PERMITTED:
+            return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+        case PSA_ERROR_INSUFFICIENT_MEMORY:
+            return MBEDTLS_ERR_PK_ALLOC_FAILED;
+        case PSA_ERROR_BAD_STATE:
+            return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
+        case PSA_ERROR_DATA_CORRUPT:
+        case PSA_ERROR_DATA_INVALID:
+        case PSA_ERROR_STORAGE_FAILURE:
+            return MBEDTLS_ERR_PK_FILE_IO_ERROR;
+        default:
+            return psa_generic_status_to_mbedtls(status);
+    }
+}
+#endif /* MBEDTLS_PK_C */
+
+/****************************************************************/
+/* Key management */
+/****************************************************************/
+
+#if defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY)
+psa_ecc_family_t mbedtls_ecc_group_to_psa(mbedtls_ecp_group_id grpid,
+                                          size_t *bits)
+{
+    switch (grpid) {
+#if defined(MBEDTLS_ECP_HAVE_SECP192R1)
+        case MBEDTLS_ECP_DP_SECP192R1:
+            *bits = 192;
+            return PSA_ECC_FAMILY_SECP_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_SECP224R1)
+        case MBEDTLS_ECP_DP_SECP224R1:
+            *bits = 224;
+            return PSA_ECC_FAMILY_SECP_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_SECP256R1)
+        case MBEDTLS_ECP_DP_SECP256R1:
+            *bits = 256;
+            return PSA_ECC_FAMILY_SECP_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_SECP384R1)
+        case MBEDTLS_ECP_DP_SECP384R1:
+            *bits = 384;
+            return PSA_ECC_FAMILY_SECP_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_SECP521R1)
+        case MBEDTLS_ECP_DP_SECP521R1:
+            *bits = 521;
+            return PSA_ECC_FAMILY_SECP_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_BP256R1)
+        case MBEDTLS_ECP_DP_BP256R1:
+            *bits = 256;
+            return PSA_ECC_FAMILY_BRAINPOOL_P_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_BP384R1)
+        case MBEDTLS_ECP_DP_BP384R1:
+            *bits = 384;
+            return PSA_ECC_FAMILY_BRAINPOOL_P_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_BP512R1)
+        case MBEDTLS_ECP_DP_BP512R1:
+            *bits = 512;
+            return PSA_ECC_FAMILY_BRAINPOOL_P_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_CURVE25519)
+        case MBEDTLS_ECP_DP_CURVE25519:
+            *bits = 255;
+            return PSA_ECC_FAMILY_MONTGOMERY;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_SECP192K1)
+        case MBEDTLS_ECP_DP_SECP192K1:
+            *bits = 192;
+            return PSA_ECC_FAMILY_SECP_K1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_SECP224K1)
+    /* secp224k1 is not and will not be supported in PSA (#3541). */
+#endif
+#if defined(MBEDTLS_ECP_HAVE_SECP256K1)
+        case MBEDTLS_ECP_DP_SECP256K1:
+            *bits = 256;
+            return PSA_ECC_FAMILY_SECP_K1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_CURVE448)
+        case MBEDTLS_ECP_DP_CURVE448:
+            *bits = 448;
+            return PSA_ECC_FAMILY_MONTGOMERY;
+#endif
+        default:
+            *bits = 0;
+            return 0;
+    }
+}
+
+mbedtls_ecp_group_id mbedtls_ecc_group_from_psa(psa_ecc_family_t family,
+                                                size_t bits)
+{
+    switch (family) {
+        case PSA_ECC_FAMILY_SECP_R1:
+            switch (bits) {
+#if defined(PSA_WANT_ECC_SECP_R1_192)
+                case 192:
+                    return MBEDTLS_ECP_DP_SECP192R1;
+#endif
+#if defined(PSA_WANT_ECC_SECP_R1_224)
+                case 224:
+                    return MBEDTLS_ECP_DP_SECP224R1;
+#endif
+#if defined(PSA_WANT_ECC_SECP_R1_256)
+                case 256:
+                    return MBEDTLS_ECP_DP_SECP256R1;
+#endif
+#if defined(PSA_WANT_ECC_SECP_R1_384)
+                case 384:
+                    return MBEDTLS_ECP_DP_SECP384R1;
+#endif
+#if defined(PSA_WANT_ECC_SECP_R1_521)
+                case 521:
+                    return MBEDTLS_ECP_DP_SECP521R1;
+#endif
+            }
+            break;
+
+        case PSA_ECC_FAMILY_BRAINPOOL_P_R1:
+            switch (bits) {
+#if defined(PSA_WANT_ECC_BRAINPOOL_P_R1_256)
+                case 256:
+                    return MBEDTLS_ECP_DP_BP256R1;
+#endif
+#if defined(PSA_WANT_ECC_BRAINPOOL_P_R1_384)
+                case 384:
+                    return MBEDTLS_ECP_DP_BP384R1;
+#endif
+#if defined(PSA_WANT_ECC_BRAINPOOL_P_R1_512)
+                case 512:
+                    return MBEDTLS_ECP_DP_BP512R1;
+#endif
+            }
+            break;
+
+        case PSA_ECC_FAMILY_MONTGOMERY:
+            switch (bits) {
+#if defined(PSA_WANT_ECC_MONTGOMERY_255)
+                case 255:
+                    return MBEDTLS_ECP_DP_CURVE25519;
+#endif
+#if defined(PSA_WANT_ECC_MONTGOMERY_448)
+                case 448:
+                    return MBEDTLS_ECP_DP_CURVE448;
+#endif
+            }
+            break;
+
+        case PSA_ECC_FAMILY_SECP_K1:
+            switch (bits) {
+#if defined(PSA_WANT_ECC_SECP_K1_192)
+                case 192:
+                    return MBEDTLS_ECP_DP_SECP192K1;
+#endif
+#if defined(PSA_WANT_ECC_SECP_K1_224)
+            /* secp224k1 is not and will not be supported in PSA (#3541). */
+#endif
+#if defined(PSA_WANT_ECC_SECP_K1_256)
+                case 256:
+                    return MBEDTLS_ECP_DP_SECP256K1;
+#endif
+            }
+            break;
+    }
+
+    return MBEDTLS_ECP_DP_NONE;
+}
+#endif /* PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY */
+
+/* Wrapper function allowing the classic API to use the PSA RNG.
+ *
+ * `mbedtls_psa_get_random(MBEDTLS_PSA_RANDOM_STATE, ...)` calls
+ * `psa_generate_random(...)`. The state parameter is ignored since the
+ * PSA API doesn't support passing an explicit state.
+ */
+int mbedtls_psa_get_random(void *p_rng,
+                           unsigned char *output,
+                           size_t output_size)
+{
+    /* This function takes a pointer to the RNG state because that's what
+     * classic mbedtls functions using an RNG expect. The PSA RNG manages
+     * its own state internally and doesn't let the caller access that state.
+     * So we just ignore the state parameter, and in practice we'll pass
+     * NULL. */
+    (void) p_rng;
+    psa_status_t status = psa_generate_random(output, output_size);
+    if (status == PSA_SUCCESS) {
+        return 0;
+    } else {
+        return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
+    }
+}
+
+#endif /* MBEDTLS_PSA_CRYPTO_CLIENT */
+
+#if defined(MBEDTLS_PSA_UTIL_HAVE_ECDSA)
+
+/**
+ * \brief  Convert a single raw coordinate to DER ASN.1 format. The output der
+ *         buffer is filled backward (i.e. starting from its end).
+ *
+ * \param raw_buf           Buffer containing the raw coordinate to be
+ *                          converted.
+ * \param raw_len           Length of raw_buf in bytes. This must be > 0.
+ * \param der_buf_start     Pointer to the beginning of the buffer which
+ *                          will be filled with the DER converted data.
+ * \param der_buf_end       End of the buffer used to store the DER output.
+ *
+ * \return                  On success, the amount of data (in bytes) written to
+ *                          the DER buffer.
+ * \return                  MBEDTLS_ERR_ASN1_BUF_TOO_SMALL if the provided der
+ *                          buffer is too small to contain all the converted data.
+ * \return                  MBEDTLS_ERR_ASN1_INVALID_DATA if the input raw
+ *                          coordinate is null (i.e. all zeros).
+ *
+ * \warning                 Raw and der buffer must not be overlapping.
+ */
+static int convert_raw_to_der_single_int(const unsigned char *raw_buf, size_t raw_len,
+                                         unsigned char *der_buf_start,
+                                         unsigned char *der_buf_end)
+{
+    unsigned char *p = der_buf_end;
+    int len;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    /* ASN.1 DER encoding requires minimal length, so skip leading 0s.
+     * Provided input MPIs should not be 0, but as a failsafe measure, still
+     * detect that and return error in case. */
+    while (*raw_buf == 0x00) {
+        ++raw_buf;
+        --raw_len;
+        if (raw_len == 0) {
+            return MBEDTLS_ERR_ASN1_INVALID_DATA;
+        }
+    }
+    len = (int) raw_len;
+
+    /* Copy the raw coordinate to the end of der_buf. */
+    if ((p - der_buf_start) < len) {
+        return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
+    }
+    p -= len;
+    memcpy(p, raw_buf, len);
+
+    /* If MSb is 1, ASN.1 requires that we prepend a 0. */
+    if (*p & 0x80) {
+        if ((p - der_buf_start) < 1) {
+            return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
+        }
+        --p;
+        *p = 0x00;
+        ++len;
+    }
+
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, der_buf_start, len));
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, der_buf_start, MBEDTLS_ASN1_INTEGER));
+
+    return len;
+}
+
+int mbedtls_ecdsa_raw_to_der(size_t bits, const unsigned char *raw, size_t raw_len,
+                             unsigned char *der, size_t der_size, size_t *der_len)
+{
+    unsigned char r[PSA_BITS_TO_BYTES(PSA_VENDOR_ECC_MAX_CURVE_BITS)];
+    unsigned char s[PSA_BITS_TO_BYTES(PSA_VENDOR_ECC_MAX_CURVE_BITS)];
+    const size_t coordinate_len = PSA_BITS_TO_BYTES(bits);
+    size_t len = 0;
+    unsigned char *p = der + der_size;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    if (raw_len != (2 * coordinate_len)) {
+        return MBEDTLS_ERR_ASN1_INVALID_DATA;
+    }
+
+    /* Since raw and der buffers might overlap, dump r and s before starting
+     * the conversion. */
+    memcpy(r, raw, coordinate_len);
+    memcpy(s, raw + coordinate_len, coordinate_len);
+
+    /* der buffer will initially be written starting from its end so we pick s
+     * first and then r. */
+    ret = convert_raw_to_der_single_int(s, coordinate_len, der, p);
+    if (ret < 0) {
+        return ret;
+    }
+    p -= ret;
+    len += ret;
+
+    ret = convert_raw_to_der_single_int(r, coordinate_len, der, p);
+    if (ret < 0) {
+        return ret;
+    }
+    p -= ret;
+    len += ret;
+
+    /* Add ASN.1 header (len + tag). */
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, der, len));
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, der,
+                                                     MBEDTLS_ASN1_CONSTRUCTED |
+                                                     MBEDTLS_ASN1_SEQUENCE));
+
+    /* memmove the content of der buffer to its beginnig. */
+    memmove(der, p, len);
+    *der_len = len;
+
+    return 0;
+}
+
+/**
+ * \brief Convert a single integer from ASN.1 DER format to raw.
+ *
+ * \param der               Buffer containing the DER integer value to be
+ *                          converted.
+ * \param der_len           Length of the der buffer in bytes.
+ * \param raw               Output buffer that will be filled with the
+ *                          converted data. This should be at least
+ *                          coordinate_size bytes and it must be zeroed before
+ *                          calling this function.
+ * \param coordinate_size   Size (in bytes) of a single coordinate in raw
+ *                          format.
+ *
+ * \return                  On success, the amount of DER data parsed from the
+ *                          provided der buffer.
+ * \return                  MBEDTLS_ERR_ASN1_UNEXPECTED_TAG if the integer tag
+ *                          is missing in the der buffer.
+ * \return                  MBEDTLS_ERR_ASN1_LENGTH_MISMATCH if the integer
+ *                          is null (i.e. all zeros) or if the output raw buffer
+ *                          is too small to contain the converted raw value.
+ *
+ * \warning                 Der and raw buffers must not be overlapping.
+ */
+static int convert_der_to_raw_single_int(unsigned char *der, size_t der_len,
+                                         unsigned char *raw, size_t coordinate_size)
+{
+    unsigned char *p = der;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    size_t unpadded_len, padding_len = 0;
+
+    /* Get the length of ASN.1 element (i.e. the integer we need to parse). */
+    ret = mbedtls_asn1_get_tag(&p, p + der_len, &unpadded_len,
+                               MBEDTLS_ASN1_INTEGER);
+    if (ret != 0) {
+        return ret;
+    }
+
+    /* It's invalid to have:
+     * - unpadded_len == 0.
+     * - MSb set without a leading 0x00 (leading 0x00 is checked below). */
+    if (((unpadded_len == 0) || (*p & 0x80) != 0)) {
+        return MBEDTLS_ERR_ASN1_INVALID_DATA;
+    }
+
+    /* Skip possible leading zero */
+    if (*p == 0x00) {
+        p++;
+        unpadded_len--;
+        /* It is not allowed to have more than 1 leading zero.
+         * Ignore the case in which unpadded_len = 0 because that's a 0 encoded
+         * in ASN.1 format (i.e. 020100). */
+        if ((unpadded_len > 0) && (*p == 0x00)) {
+            return MBEDTLS_ERR_ASN1_INVALID_DATA;
+        }
+    }
+
+    if (unpadded_len > coordinate_size) {
+        /* Parsed number is longer than the maximum expected value. */
+        return MBEDTLS_ERR_ASN1_INVALID_DATA;
+    }
+    padding_len = coordinate_size - unpadded_len;
+    /* raw buffer was already zeroed by the calling function so zero-padding
+     * operation is skipped here. */
+    memcpy(raw + padding_len, p, unpadded_len);
+    p += unpadded_len;
+
+    return (int) (p - der);
+}
+
+int mbedtls_ecdsa_der_to_raw(size_t bits, const unsigned char *der, size_t der_len,
+                             unsigned char *raw, size_t raw_size, size_t *raw_len)
+{
+    unsigned char raw_tmp[PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE];
+    unsigned char *p = (unsigned char *) der;
+    size_t data_len;
+    size_t coordinate_size = PSA_BITS_TO_BYTES(bits);
+    int ret;
+
+    /* The output raw buffer should be at least twice the size of a raw
+     * coordinate in order to store r and s. */
+    if (raw_size < coordinate_size * 2) {
+        return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
+    }
+
+    /* Check that the provided input DER buffer has the right header. */
+    ret = mbedtls_asn1_get_tag(&p, der + der_len, &data_len,
+                               MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+    if (ret != 0) {
+        return ret;
+    }
+
+    memset(raw_tmp, 0, 2 * coordinate_size);
+
+    /* Extract r */
+    ret = convert_der_to_raw_single_int(p, data_len, raw_tmp, coordinate_size);
+    if (ret < 0) {
+        return ret;
+    }
+    p += ret;
+    data_len -= ret;
+
+    /* Extract s */
+    ret = convert_der_to_raw_single_int(p, data_len, raw_tmp + coordinate_size,
+                                        coordinate_size);
+    if (ret < 0) {
+        return ret;
+    }
+    p += ret;
+    data_len -= ret;
+
+    /* Check that we consumed all the input der data. */
+    if ((size_t) (p - der) != der_len) {
+        return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH;
+    }
+
+    memcpy(raw, raw_tmp, 2 * coordinate_size);
+    *raw_len = 2 * coordinate_size;
+
+    return 0;
+}
+
+#endif /* MBEDTLS_PSA_UTIL_HAVE_ECDSA */
diff --git a/tf-psa-crypto/core/psa_util_internal.h b/tf-psa-crypto/core/psa_util_internal.h
new file mode 100644
index 0000000..70a08a0
--- /dev/null
+++ b/tf-psa-crypto/core/psa_util_internal.h
@@ -0,0 +1,100 @@
+/**
+ * \file psa_util_internal.h
+ *
+ * \brief Internal utility functions for use of PSA Crypto.
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef MBEDTLS_PSA_UTIL_INTERNAL_H
+#define MBEDTLS_PSA_UTIL_INTERNAL_H
+
+/* Include the public header so that users only need one include. */
+#include "mbedtls/psa_util.h"
+
+#include "psa/crypto.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_CLIENT)
+
+/*************************************************************************
+ * FFDH
+ ************************************************************************/
+
+#define MBEDTLS_PSA_MAX_FFDH_PUBKEY_LENGTH \
+    PSA_KEY_EXPORT_FFDH_PUBLIC_KEY_MAX_SIZE(PSA_VENDOR_FFDH_MAX_KEY_BITS)
+
+/*************************************************************************
+ * ECC
+ ************************************************************************/
+
+#define MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH \
+    PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS)
+
+#define MBEDTLS_PSA_MAX_EC_KEY_PAIR_LENGTH \
+    PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS)
+
+/*************************************************************************
+ * Error translation
+ ************************************************************************/
+
+typedef struct {
+    /* Error codes used by PSA crypto are in -255..-128, fitting in 16 bits. */
+    int16_t psa_status;
+    /* Error codes used by Mbed TLS are in one of the ranges
+     * -127..-1 (low-level) or -32767..-4096 (high-level with a low-level
+     * code optionally added), fitting in 16 bits. */
+    int16_t mbedtls_error;
+} mbedtls_error_pair_t;
+
+#if defined(MBEDTLS_MD_LIGHT)
+extern const mbedtls_error_pair_t psa_to_md_errors[4];
+#endif
+
+#if defined(MBEDTLS_BLOCK_CIPHER_SOME_PSA)
+extern const mbedtls_error_pair_t psa_to_cipher_errors[4];
+#endif
+
+#if defined(MBEDTLS_LMS_C)
+extern const mbedtls_error_pair_t psa_to_lms_errors[3];
+#endif
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3)
+extern const mbedtls_error_pair_t psa_to_ssl_errors[7];
+#endif
+
+#if defined(PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY) ||    \
+    defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC)
+extern const mbedtls_error_pair_t psa_to_pk_rsa_errors[8];
+#endif
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO) && \
+    defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY)
+extern const mbedtls_error_pair_t psa_to_pk_ecdsa_errors[7];
+#endif
+
+/* Generic fallback function for error translation,
+ * when the received state was not module-specific. */
+int psa_generic_status_to_mbedtls(psa_status_t status);
+
+/* This function iterates over provided local error translations,
+ * and if no match was found - calls the fallback error translation function. */
+int psa_status_to_mbedtls(psa_status_t status,
+                          const mbedtls_error_pair_t *local_translations,
+                          size_t local_errors_num,
+                          int (*fallback_f)(psa_status_t));
+
+/* The second out of three-stage error handling functions of the pk module,
+ * acts as a fallback after RSA / ECDSA error translation, and if no match
+ * is found, it itself calls psa_generic_status_to_mbedtls. */
+int psa_pk_status_to_mbedtls(psa_status_t status);
+
+/* Utility macro to shorten the defines of error translator in modules. */
+#define PSA_TO_MBEDTLS_ERR_LIST(status, error_list, fallback_f)       \
+    psa_status_to_mbedtls(status, error_list,                         \
+                          sizeof(error_list)/sizeof(error_list[0]),   \
+                          fallback_f)
+
+#endif /* MBEDTLS_PSA_CRYPTO_CLIENT */
+#endif /* MBEDTLS_PSA_UTIL_INTERNAL_H */