Merge pull request #8703 from valeriosetti/issue7765-guards-in-asn1

Conversion function between raw and DER ECDSA signatures (guards in ASN1)
diff --git a/ChangeLog.d/7765.txt b/ChangeLog.d/7765.txt
new file mode 100644
index 0000000..3dd6b5d
--- /dev/null
+++ b/ChangeLog.d/7765.txt
@@ -0,0 +1,3 @@
+Features
+   * Add functions mbedtls_ecdsa_raw_to_der() and mbedtls_ecdsa_der_to_raw() to
+     convert ECDSA signatures between raw and DER (ASN.1) formats.
diff --git a/include/mbedtls/asn1.h b/include/mbedtls/asn1.h
index 3c3bfad..ff019f4 100644
--- a/include/mbedtls/asn1.h
+++ b/include/mbedtls/asn1.h
@@ -197,7 +197,8 @@
 }
 mbedtls_asn1_named_data;
 
-#if defined(MBEDTLS_ASN1_PARSE_C) || defined(MBEDTLS_X509_CREATE_C)
+#if defined(MBEDTLS_ASN1_PARSE_C) || defined(MBEDTLS_X509_CREATE_C) || \
+    defined(MBEDTLS_PSA_UTIL_HAVE_ECDSA)
 /**
  * \brief       Get the length of an ASN.1 element.
  *              Updates the pointer to immediately behind the length.
@@ -244,7 +245,7 @@
 int mbedtls_asn1_get_tag(unsigned char **p,
                          const unsigned char *end,
                          size_t *len, int tag);
-#endif /* MBEDTLS_ASN1_PARSE_C || MBEDTLS_X509_CREATE_C */
+#endif /* MBEDTLS_ASN1_PARSE_C || MBEDTLS_X509_CREATE_C || MBEDTLS_PSA_UTIL_HAVE_ECDSA */
 
 #if defined(MBEDTLS_ASN1_PARSE_C)
 /**
diff --git a/include/mbedtls/asn1write.h b/include/mbedtls/asn1write.h
index 7af4aba..0c5a85a 100644
--- a/include/mbedtls/asn1write.h
+++ b/include/mbedtls/asn1write.h
@@ -36,7 +36,8 @@
 extern "C" {
 #endif
 
-#if defined(MBEDTLS_ASN1_WRITE_C) || defined(MBEDTLS_X509_USE_C)
+#if defined(MBEDTLS_ASN1_WRITE_C) || defined(MBEDTLS_X509_USE_C) || \
+    defined(MBEDTLS_PSA_UTIL_HAVE_ECDSA)
 /**
  * \brief           Write a length field in ASN.1 format.
  *
@@ -65,7 +66,7 @@
  */
 int mbedtls_asn1_write_tag(unsigned char **p, const unsigned char *start,
                            unsigned char tag);
-#endif /* MBEDTLS_ASN1_WRITE_C || MBEDTLS_X509_USE_C */
+#endif /* MBEDTLS_ASN1_WRITE_C || MBEDTLS_X509_USE_C || MBEDTLS_PSA_UTIL_HAVE_ECDSA*/
 
 #if defined(MBEDTLS_ASN1_WRITE_C)
 /**
diff --git a/include/mbedtls/config_adjust_legacy_crypto.h b/include/mbedtls/config_adjust_legacy_crypto.h
index 818a8c6..064699c 100644
--- a/include/mbedtls/config_adjust_legacy_crypto.h
+++ b/include/mbedtls/config_adjust_legacy_crypto.h
@@ -391,6 +391,13 @@
 #define MBEDTLS_SHA256_USE_ARMV8_A_CRYPTO_ONLY
 #endif
 
+/* psa_util file features some ECDSA conversion functions, to convert between
+ * legacy's ASN.1 DER format and PSA's raw one. */
+#if defined(MBEDTLS_ECDSA_C) || (defined(MBEDTLS_PSA_CRYPTO_C) && \
+    (defined(PSA_WANT_ALG_ECDSA) || defined(PSA_WANT_ALG_DETERMINISTIC_ECDSA)))
+#define MBEDTLS_PSA_UTIL_HAVE_ECDSA
+#endif
+
 /* Some internal helpers to determine which keys are availble. */
 #if (!defined(MBEDTLS_USE_PSA_CRYPTO) && defined(MBEDTLS_AES_C)) || \
     (defined(MBEDTLS_USE_PSA_CRYPTO) && defined(PSA_WANT_KEY_TYPE_AES))
diff --git a/include/mbedtls/psa_util.h b/include/mbedtls/psa_util.h
index 47724c6..984f031 100644
--- a/include/mbedtls/psa_util.h
+++ b/include/mbedtls/psa_util.h
@@ -16,6 +16,11 @@
 
 #include "psa/crypto.h"
 
+/* ASN1 defines used in the ECDSA conversion functions.
+ * Note: intentionally not adding MBEDTLS_ASN1_[PARSE|WRITE]_C guards here
+ * otherwise error codes would be unknown in test_suite_psa_crypto_util.data.*/
+#include <mbedtls/asn1write.h>
+
 #if defined(MBEDTLS_PSA_CRYPTO_C)
 
 /* Expose whatever RNG the PSA subsystem uses to applications using the
@@ -175,8 +180,50 @@
 {
     return (mbedtls_md_type_t) (psa_alg & PSA_ALG_HASH_MASK);
 }
+#endif /* MBEDTLS_PSA_CRYPTO_C */
+
+#if defined(MBEDTLS_PSA_UTIL_HAVE_ECDSA)
+
+/** Convert an ECDSA signature from raw format to DER ASN.1 format.
+ *
+ * \param       bits        Size of each coordinate in bits.
+ * \param       raw         Buffer that contains the signature in raw format.
+ * \param       raw_len     Length of \p raw in bytes. This must be
+ *                          PSA_BITS_TO_BYTES(bits) bytes.
+ * \param[out]  der         Buffer that will be filled with the converted DER
+ *                          output. It can overlap with raw buffer.
+ * \param       der_size    Size of \p der in bytes. It is enough if \p der_size
+ *                          is at least the size of the actual output. (The size
+ *                          of the output can vary depending on the presence of
+ *                          leading zeros in the data.) You can use
+ *                          #MBEDTLS_ECDSA_MAX_SIG_LEN(\p bits) to determine a
+ *                          size that is large enough for all signatures for a
+ *                          given value of \p bits.
+ * \param[out]  der_len     On success it contains the amount of valid data
+ *                          (in bytes) written to \p der. It's undefined
+ *                          in case of failure.
+ */
+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);
+
+/** Convert an ECDSA signature from DER ASN.1 format to raw format.
+ *
+ * \param       bits        Size of each coordinate in bits.
+ * \param       der         Buffer that contains the signature in DER format.
+ * \param       der_len     Size of \p der in bytes.
+ * \param[out]  raw         Buffer that will be filled with the converted raw
+ *                          signature. It can overlap with der buffer.
+ * \param       raw_size    Size of \p raw in bytes. Must be at least
+ *                          2 * PSA_BITS_TO_BYTES(bits) bytes.
+ * \param[out]  raw_len     On success it is updated with the amount of valid
+ *                          data (in bytes) written to \p raw. It's undefined
+ *                          in case of failure.
+ */
+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);
+
+#endif /* MBEDTLS_PSA_UTIL_HAVE_ECDSA */
 
 /**@}*/
 
-#endif /* MBEDTLS_PSA_CRYPTO_C */
 #endif /* MBEDTLS_PSA_UTIL_H */
diff --git a/library/asn1parse.c b/library/asn1parse.c
index c02b233..e33fdf7 100644
--- a/library/asn1parse.c
+++ b/library/asn1parse.c
@@ -7,7 +7,8 @@
 
 #include "common.h"
 
-#if defined(MBEDTLS_ASN1_PARSE_C) || defined(MBEDTLS_X509_CREATE_C)
+#if defined(MBEDTLS_ASN1_PARSE_C) || defined(MBEDTLS_X509_CREATE_C) || \
+    defined(MBEDTLS_PSA_UTIL_HAVE_ECDSA)
 
 #include "mbedtls/asn1.h"
 #include "mbedtls/platform_util.h"
@@ -73,7 +74,7 @@
 
     return mbedtls_asn1_get_len(p, end, len);
 }
-#endif /* MBEDTLS_ASN1_PARSE_C || MBEDTLS_X509_CREATE_C */
+#endif /* MBEDTLS_ASN1_PARSE_C || MBEDTLS_X509_CREATE_C || MBEDTLS_PSA_UTIL_HAVE_ECDSA */
 
 #if defined(MBEDTLS_ASN1_PARSE_C)
 int mbedtls_asn1_get_bool(unsigned char **p,
diff --git a/library/asn1write.c b/library/asn1write.c
index 114091d..775a9ef 100644
--- a/library/asn1write.c
+++ b/library/asn1write.c
@@ -7,7 +7,8 @@
 
 #include "common.h"
 
-#if defined(MBEDTLS_ASN1_WRITE_C) || defined(MBEDTLS_X509_USE_C)
+#if defined(MBEDTLS_ASN1_WRITE_C) || defined(MBEDTLS_X509_USE_C) || \
+    defined(MBEDTLS_PSA_UTIL_HAVE_ECDSA)
 
 #include "mbedtls/asn1write.h"
 #include "mbedtls/error.h"
@@ -62,7 +63,7 @@
 
     return 1;
 }
-#endif /* MBEDTLS_ASN1_WRITE_C || MBEDTLS_X509_USE_C */
+#endif /* MBEDTLS_ASN1_WRITE_C || MBEDTLS_X509_USE_C || MBEDTLS_PSA_UTIL_HAVE_ECDSA */
 
 #if defined(MBEDTLS_ASN1_WRITE_C)
 static int mbedtls_asn1_write_len_and_tag(unsigned char **p,
diff --git a/library/pk_wrap.c b/library/pk_wrap.c
index f8ce099..2e00d4a 100644
--- a/library/pk_wrap.c
+++ b/library/pk_wrap.c
@@ -29,6 +29,7 @@
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
 #include "psa_util_internal.h"
 #include "psa/crypto.h"
+#include "mbedtls/psa_util.h"
 
 #if defined(MBEDTLS_RSA_C)
 #include "pkwrite.h"
@@ -522,66 +523,6 @@
 
 #if defined(MBEDTLS_PK_CAN_ECDSA_VERIFY)
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
-/*
- * An ASN.1 encoded signature is a sequence of two ASN.1 integers. Parse one of
- * those integers and convert it to the fixed-length encoding expected by PSA.
- */
-static int extract_ecdsa_sig_int(unsigned char **from, const unsigned char *end,
-                                 unsigned char *to, size_t to_len)
-{
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    size_t unpadded_len, padding_len;
-
-    if ((ret = mbedtls_asn1_get_tag(from, end, &unpadded_len,
-                                    MBEDTLS_ASN1_INTEGER)) != 0) {
-        return ret;
-    }
-
-    while (unpadded_len > 0 && **from == 0x00) {
-        (*from)++;
-        unpadded_len--;
-    }
-
-    if (unpadded_len > to_len || unpadded_len == 0) {
-        return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH;
-    }
-
-    padding_len = to_len - unpadded_len;
-    memset(to, 0x00, padding_len);
-    memcpy(to + padding_len, *from, unpadded_len);
-    (*from) += unpadded_len;
-
-    return 0;
-}
-
-/*
- * Convert a signature from an ASN.1 sequence of two integers
- * to a raw {r,s} buffer. Note: the provided sig buffer must be at least
- * twice as big as int_size.
- */
-static int extract_ecdsa_sig(unsigned char **p, const unsigned char *end,
-                             unsigned char *sig, size_t int_size)
-{
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    size_t tmp_size;
-
-    if ((ret = mbedtls_asn1_get_tag(p, end, &tmp_size,
-                                    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) {
-        return ret;
-    }
-
-    /* Extract r */
-    if ((ret = extract_ecdsa_sig_int(p, end, sig, int_size)) != 0) {
-        return ret;
-    }
-    /* Extract s */
-    if ((ret = extract_ecdsa_sig_int(p, end, sig + int_size, int_size)) != 0) {
-        return ret;
-    }
-
-    return 0;
-}
-
 /* Common helper for ECDSA verify using PSA functions. */
 static int ecdsa_verify_psa(unsigned char *key, size_t key_len,
                             psa_ecc_family_t curve, size_t curve_bits,
@@ -593,6 +534,7 @@
     mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT;
     psa_algorithm_t psa_sig_md = PSA_ALG_ECDSA_ANY;
     size_t signature_len = PSA_ECDSA_SIGNATURE_SIZE(curve_bits);
+    size_t converted_sig_len;
     unsigned char extracted_sig[PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE];
     unsigned char *p;
     psa_status_t status;
@@ -617,11 +559,14 @@
     }
 
     p = (unsigned char *) sig;
-    /* extract_ecdsa_sig's last parameter is the size
-     * of each integer to be parsed, so it's actually half
-     * the size of the signature. */
-    if ((ret = extract_ecdsa_sig(&p, sig + sig_len, extracted_sig,
-                                 signature_len/2)) != 0) {
+    ret = mbedtls_ecdsa_der_to_raw(curve_bits, p, sig_len, extracted_sig,
+                                   sizeof(extracted_sig), &converted_sig_len);
+    if (ret != 0) {
+        goto cleanup;
+    }
+
+    if (converted_sig_len != signature_len) {
+        ret = MBEDTLS_ERR_PK_BAD_INPUT_DATA;
         goto cleanup;
     }
 
@@ -632,10 +577,6 @@
         goto cleanup;
     }
 
-    if (p != sig + sig_len) {
-        ret = MBEDTLS_ERR_PK_SIG_LEN_MISMATCH;
-        goto cleanup;
-    }
     ret = 0;
 
 cleanup:
@@ -737,90 +678,6 @@
 
 #if defined(MBEDTLS_PK_CAN_ECDSA_SIGN)
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
-/*
- * Simultaneously convert and move raw MPI from the beginning of a buffer
- * to an ASN.1 MPI at the end of the buffer.
- * See also mbedtls_asn1_write_mpi().
- *
- * p: pointer to the end of the output buffer
- * start: start of the output buffer, and also of the mpi to write at the end
- * n_len: length of the mpi to read from start
- */
-static int asn1_write_mpibuf(unsigned char **p, unsigned char *start,
-                             size_t n_len)
-{
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    size_t len = 0;
-
-    if ((size_t) (*p - start) < n_len) {
-        return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
-    }
-
-    len = n_len;
-    *p -= len;
-    memmove(*p, start, len);
-
-    /* ASN.1 DER encoding requires minimal length, so skip leading 0s.
-     * Neither r nor s should be 0, but as a failsafe measure, still detect
-     * that rather than overflowing the buffer in case of a PSA error. */
-    while (len > 0 && **p == 0x00) {
-        ++(*p);
-        --len;
-    }
-
-    /* this is only reached if the signature was invalid */
-    if (len == 0) {
-        return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED;
-    }
-
-    /* if the msb is 1, ASN.1 requires that we prepend a 0.
-     * Neither r nor s can be 0, so we can assume len > 0 at all times. */
-    if (**p & 0x80) {
-        if (*p - start < 1) {
-            return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
-        }
-
-        *--(*p) = 0x00;
-        len += 1;
-    }
-
-    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len));
-    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start,
-                                                     MBEDTLS_ASN1_INTEGER));
-
-    return (int) len;
-}
-
-/* Transcode signature from PSA format to ASN.1 sequence.
- * See ecdsa_signature_to_asn1 in ecdsa.c, but with byte buffers instead of
- * MPIs, and in-place.
- *
- * [in/out] sig: the signature pre- and post-transcoding
- * [in/out] sig_len: signature length pre- and post-transcoding
- * [int] buf_len: the available size the in/out buffer
- */
-static int pk_ecdsa_sig_asn1_from_psa(unsigned char *sig, size_t *sig_len,
-                                      size_t buf_len)
-{
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    size_t len = 0;
-    const size_t rs_len = *sig_len / 2;
-    unsigned char *p = sig + buf_len;
-
-    MBEDTLS_ASN1_CHK_ADD(len, asn1_write_mpibuf(&p, sig + rs_len, rs_len));
-    MBEDTLS_ASN1_CHK_ADD(len, asn1_write_mpibuf(&p, sig, rs_len));
-
-    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, sig, len));
-    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, sig,
-                                                     MBEDTLS_ASN1_CONSTRUCTED |
-                                                     MBEDTLS_ASN1_SEQUENCE));
-
-    memmove(sig, p, len);
-    *sig_len = len;
-
-    return 0;
-}
-
 /* Common helper for ECDSA sign using PSA functions.
  * Instead of extracting key's properties in order to check which kind of ECDSA
  * signature it supports, we try both deterministic and non-deterministic.
@@ -831,6 +688,15 @@
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     psa_status_t status;
+    psa_key_attributes_t key_attr = PSA_KEY_ATTRIBUTES_INIT;
+    size_t key_bits = 0;
+
+    status = psa_get_key_attributes(key_id, &key_attr);
+    if (status != PSA_SUCCESS) {
+        return PSA_PK_ECDSA_TO_MBEDTLS_ERR(status);
+    }
+    key_bits = psa_get_key_bits(&key_attr);
+    psa_reset_key_attributes(&key_attr);
 
     status = psa_sign_hash(key_id,
                            PSA_ALG_DETERMINISTIC_ECDSA(mbedtls_md_psa_alg_from_type(md_alg)),
@@ -849,7 +715,7 @@
     }
 
 done:
-    ret = pk_ecdsa_sig_asn1_from_psa(sig, sig_len, sig_size);
+    ret = mbedtls_ecdsa_raw_to_der(key_bits, sig, *sig_len, sig, sig_size, sig_len);
 
     return ret;
 }
diff --git a/library/psa_util.c b/library/psa_util.c
index 41586e2..fd119bf 100644
--- a/library/psa_util.c
+++ b/library/psa_util.c
@@ -8,14 +8,20 @@
 
 #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_C)
 
 #include <psa/crypto.h>
 
-#include "psa_util_internal.h"
-
-/* The following includes are needed for MBEDTLS_ERR_XXX macros */
-#include <mbedtls/error.h>
 #if defined(MBEDTLS_MD_LIGHT)
 #include <mbedtls/md.h>
 #endif
@@ -331,3 +337,239 @@
 #endif /* PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY */
 
 #endif /* MBEDTLS_PSA_CRYPTO_C */
+
+#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/tests/scripts/all.sh b/tests/scripts/all.sh
index a484f10..af32c06 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -1079,8 +1079,8 @@
     echo "!MBEDTLS_AES_ONLY_128_BIT_KEY_LENGTH" >> $expected
     # No PSA equivalent - used to skip decryption tests in PSA-ECB, CBC/XTS/NIST_KW/DES
     echo "!MBEDTLS_BLOCK_CIPHER_NO_DECRYPT" >> $expected
-    # This is used by import_rsa_made_up() in test_suite_psa_crypto in order
-    # to build a fake RSA key of the wanted size based on
+    # MBEDTLS_ASN1_WRITE_C is used by import_rsa_made_up() in test_suite_psa_crypto
+    # in order to build a fake RSA key of the wanted size based on
     # PSA_VENDOR_RSA_MAX_KEY_BITS. The legacy module is only used by
     # the test code and that's probably the most convenient way of achieving
     # the test's goal.
diff --git a/tests/suites/test_suite_psa_crypto_util.data b/tests/suites/test_suite_psa_crypto_util.data
new file mode 100644
index 0000000..807007b
--- /dev/null
+++ b/tests/suites/test_suite_psa_crypto_util.data
@@ -0,0 +1,167 @@
+ECDSA Raw -> DER, 256bit, Success
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_raw_to_der:256:"11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":"30440220111111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":0
+
+ECDSA Raw -> DER, 256bit, DER buffer too small
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_raw_to_der:256:"11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":"304402201111111111111111111111111111111111111111111111111111111111111111022022222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_BUF_TOO_SMALL
+
+ECDSA Raw -> DER, 256bit, Null r
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_raw_to_der:256:"00000000000000000000000000000000000000000000000000000000000000002222222222222222222222222222222222222222222222222222222222222222":"30440220111111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_INVALID_DATA
+
+ECDSA Raw -> DER, 256bit, Null s
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_raw_to_der:256:"11111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000":"30440220111111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_INVALID_DATA
+
+ECDSA Raw -> DER, 256bit, r with MSb set
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_raw_to_der:256:"91111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":"3045022100911111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":0
+
+ECDSA Raw -> DER, 256bit, s with MSb set
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_raw_to_der:256:"1111111111111111111111111111111111111111111111111111111111111111A222222222222222222222222222222222222222222222222222222222222222":"304502201111111111111111111111111111111111111111111111111111111111111111022100A222222222222222222222222222222222222222222222222222222222222222":0
+
+ECDSA Raw -> DER, 256bit, both r and s with MSb set
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_raw_to_der:256:"A111111111111111111111111111111111111111111111111111111111111111A222222222222222222222222222222222222222222222222222222222222222":"3046022100A111111111111111111111111111111111111111111111111111111111111111022100A222222222222222222222222222222222222222222222222222222222222222":0
+
+ECDSA Raw -> DER, 256bit, r and s only 1 byte of data
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_raw_to_der:256:"00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000022":"3006020111020122":0
+
+ECDSA Raw -> DER, 256bit, r and s only 1 byte of data with MSb set
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_raw_to_der:256:"000000000000000000000000000000000000000000000000000000000000009100000000000000000000000000000000000000000000000000000000000000A2":"300802020091020200A2":0
+
+ECDSA Raw -> DER, 256bit, Invalid raw signature (r 1 byte shorter)
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_raw_to_der:256:"111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":"30440220111111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_INVALID_DATA
+
+ECDSA Raw -> DER, 256bit, Invalid raw signature (r and s 1 byte shorter)
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_raw_to_der:256:"1111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222":"30440220111111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_INVALID_DATA
+
+ECDSA Raw -> DER, 256bit, Invalid raw signature (r 1 byte longer)
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_raw_to_der:256:"1111111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":"30440220111111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_INVALID_DATA
+
+ECDSA Raw -> DER, 256bit, Invalid raw signature (r and s 1 byte longer)
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_raw_to_der:256:"111111111111111111111111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222222222222222222":"30440220111111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_INVALID_DATA
+
+ECDSA DER -> Raw, 256bit, Success
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"30440220111111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":"11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":0
+
+ECDSA DER -> Raw, 256bit, Raw buffer too small
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"30440220111111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":"111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_BUF_TOO_SMALL
+
+ECDSA DER -> Raw, 256bit, Wrong sequence tag
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"40440220111111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":"11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_UNEXPECTED_TAG
+
+ECDSA DER -> Raw, 256bit, Invalid sequence length
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"30ff0220111111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":"11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_INVALID_LENGTH
+
+ECDSA DER -> Raw, 256bit, Wrong integer tag
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"30440120111111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":"11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_UNEXPECTED_TAG
+
+ECDSA DER -> Raw, 256bit, Wrong r integer length (1 byte smaller than the actual size)
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"30440219111111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":"11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_UNEXPECTED_TAG
+
+ECDSA DER -> Raw, 256bit, Wrong r integer length (1 byte larger than the actual size)
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"30440221111111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":"11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_INVALID_DATA
+
+ECDSA DER -> Raw, 256bit, Wrong s integer length (1 byte smaller than the actual size)
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"30440220111111111111111111111111111111111111111111111111111111111111111102192222222222222222222222222222222222222222222222222222222222222222":"11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_LENGTH_MISMATCH
+
+ECDSA DER -> Raw, 256bit, Wrong s integer length (1 byte larger than the actual size)
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"30440220111111111111111111111111111111111111111111111111111111111111111102212222222222222222222222222222222222222222222222222222222222222222":"11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_OUT_OF_DATA
+
+ECDSA DER -> Raw, 256bit, r size 1 byte larger than allowed for output raw coordinate
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"3045022111111111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":"11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_INVALID_DATA
+
+ECDSA DER -> Raw, 256bit, r with MSb set
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"3045022100911111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":"91111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":0
+
+ECDSA DER -> Raw, 256bit, Invalid r all zeros
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"30440220000000000000000000000000000000000000000000000000000000000000000002202222222222222222222222222222222222222222222222222222222222222222":"00000000000000000000000000000000000000000000000000000000000000002222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_INVALID_DATA
+
+ECDSA DER -> Raw, 256bit, Invalid s all zeros
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"30440220111111111111111111111111111111111111111111111111111111111111111102200000000000000000000000000000000000000000000000000000000000000000":"11111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000":MBEDTLS_ERR_ASN1_INVALID_DATA
+
+ECDSA DER -> Raw, 256bit, Valid r only 1 zero byte
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"302502010002202222222222222222222222222222222222222222222222222222222222222222":"00000000000000000000000000000000000000000000000000000000000000002222222222222222222222222222222222222222222222222222222222222222":0
+
+ECDSA DER -> Raw, 256bit, Valid s only 1 zero byte
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"302502201111111111111111111111111111111111111111111111111111111111111111020100":"11111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000":0
+
+ECDSA DER -> Raw, 256bit, Invalid 0-length r
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"3024020002202222222222222222222222222222222222222222222222222222222222222222":"00000000000000000000000000000000000000000000000000000000000000002222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_INVALID_DATA
+
+ECDSA DER -> Raw, 256bit, Invalid 0-length s
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"3024022011111111111111111111111111111111111111111111111111111111111111110200":"11111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000":MBEDTLS_ERR_ASN1_INVALID_DATA
+
+ECDSA DER -> Raw, 256bit, Invalid r 2 leading zeros
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"3027020300000102202222222222222222222222222222222222222222222222222222222222222222":"00000000000000000000000000000000000000000000000000000000000000002222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_INVALID_DATA
+
+ECDSA DER -> Raw, 256bit, Invalid s 2 leading zeros
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"3027022011111111111111111111111111111111111111111111111111111111111111110203000001":"11111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000":MBEDTLS_ERR_ASN1_INVALID_DATA
+
+ECDSA DER -> Raw, 256bit, Invalid r: MSb set without leading zero
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_der_to_raw:256:"30440220911111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222":"11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":MBEDTLS_ERR_ASN1_INVALID_DATA
+
+# 512/521 bit sizes are useful to test sequence's length encoded with 2 bytes.
+ECDSA Raw -> DER, 512bit, Success
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 512
+ecdsa_raw_to_der:512:"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222":"308184024011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111024022222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222":0
+
+# 512/521 bit sizes are useful to test sequence's length encoded with 2 bytes.
+ECDSA DER -> Raw, 512bit, Success
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 512
+ecdsa_der_to_raw:512:"308184024011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111024022222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222":"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222":0
+
+# 512/521 bit sizes are useful to test sequence's length encoded with 2 bytes.
+ECDSA Raw -> DER, 521bit, Success
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 521
+ecdsa_raw_to_der:521:"011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111012222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222":"30818802420111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110242012222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222":0
+
+# 512/521 bit sizes are useful to test sequence's length encoded with 2 bytes.
+ECDSA DER -> Raw, 521bit, Success
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 521
+ecdsa_der_to_raw:521:"30818802420111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110242012222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222":"011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111012222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222":0
+
+ECDSA Raw -> DER, 256bit, Incremental DER buffer sizes
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_raw_to_der_incremental:256:"91111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222":"3045022100911111111111111111111111111111111111111111111111111111111111111102202222222222222222222222222222222222222222222222222222222222222222"
+
+ECDSA Raw -> DER, 512bit, Incremental DER buffer sizes
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 512
+ecdsa_raw_to_der_incremental:512:"9111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222":"30818502410091111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111024022222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222"
+
+ECDSA Raw -> DER, 521bit, Incremental DER buffer sizes
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 521
+ecdsa_raw_to_der_incremental:521:"011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111012222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222":"30818802420111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110242012222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222"
+
+ECDSA Raw -> DER, 256bit, DER buffer of minimal length (1 byte per integer)
+depends_on:PSA_VENDOR_ECC_MAX_CURVE_BITS >= 256
+ecdsa_raw_to_der_incremental:256:"00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002":"3006020101020102"
diff --git a/tests/suites/test_suite_psa_crypto_util.function b/tests/suites/test_suite_psa_crypto_util.function
new file mode 100644
index 0000000..2d8915e
--- /dev/null
+++ b/tests/suites/test_suite_psa_crypto_util.function
@@ -0,0 +1,91 @@
+/* BEGIN_HEADER */
+#include <test/helpers.h>
+#include <mbedtls/psa_util.h>
+/* END_HEADER */
+
+/* BEGIN_CASE depends_on:MBEDTLS_PSA_UTIL_HAVE_ECDSA */
+void ecdsa_raw_to_der(int key_bits, data_t *input, data_t *exp_result, int exp_ret)
+{
+    unsigned char *tmp_buf = NULL;
+    size_t tmp_buf_len = exp_result->len;
+    size_t ret_len;
+
+    TEST_CALLOC(tmp_buf, tmp_buf_len);
+
+    TEST_EQUAL(mbedtls_ecdsa_raw_to_der(key_bits, input->x, input->len,
+                                        tmp_buf, tmp_buf_len, &ret_len), exp_ret);
+
+    if (exp_ret == 0) {
+        ASSERT_COMPARE(exp_result->x, exp_result->len, tmp_buf, ret_len);
+    }
+
+exit:
+    mbedtls_free(tmp_buf);
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:MBEDTLS_PSA_UTIL_HAVE_ECDSA */
+void ecdsa_raw_to_der_incremental(int key_bits, data_t *input, data_t *exp_result)
+{
+    unsigned char *tmp_buf = NULL;
+    size_t ret_len;
+    size_t i;
+
+    /* Test with an output buffer smaller than required (expexted to fail). */
+    for (i = 1; i < exp_result->len; i++) {
+        TEST_CALLOC(tmp_buf, i);
+        TEST_ASSERT(mbedtls_ecdsa_raw_to_der(key_bits, input->x, input->len,
+                                             tmp_buf, i, &ret_len) != 0);
+        mbedtls_free(tmp_buf);
+        tmp_buf = NULL;
+    }
+    /* Test with an output buffer larger/equal than required (expexted to
+     * succeed). */
+    for (i = exp_result->len; i < (2 * exp_result->len); i++) {
+        TEST_CALLOC(tmp_buf, i);
+        TEST_ASSERT(mbedtls_ecdsa_raw_to_der(key_bits, input->x, input->len,
+                                             tmp_buf, i, &ret_len) == 0);
+        mbedtls_free(tmp_buf);
+        tmp_buf = NULL;
+    }
+
+exit:
+    mbedtls_free(tmp_buf);
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:MBEDTLS_PSA_UTIL_HAVE_ECDSA */
+void ecdsa_der_to_raw(int key_bits, data_t *input, data_t *exp_result, int exp_ret)
+{
+    unsigned char *in_buf = NULL;
+    size_t in_buf_len;
+    unsigned char *out_buf = NULL;
+    size_t out_buf_len = exp_result->len;
+    size_t ret_len;
+
+    TEST_CALLOC(out_buf, out_buf_len);
+
+    /* Verify that parsing of truncated input always fails. */
+    for (in_buf_len = 1; in_buf_len < input->len; in_buf_len++) {
+        /* We alloc a copy of input buffer with limited length so that sanitizers
+         * can detect overreads. */
+        TEST_CALLOC(in_buf, in_buf_len);
+        memcpy(in_buf, input->x, in_buf_len);
+        TEST_ASSERT(mbedtls_ecdsa_der_to_raw(key_bits, in_buf, in_buf_len,
+                                             out_buf, out_buf_len, &ret_len) != 0);
+        mbedtls_free(in_buf);
+        in_buf = NULL;
+    }
+
+    TEST_EQUAL(mbedtls_ecdsa_der_to_raw(key_bits, input->x, input->len,
+                                        out_buf, out_buf_len, &ret_len), exp_ret);
+
+    if (exp_ret == 0) {
+        ASSERT_COMPARE(exp_result->x, exp_result->len, out_buf, ret_len);
+    }
+
+exit:
+    mbedtls_free(in_buf);
+    mbedtls_free(out_buf);
+}
+/* END_CASE */