Read and write X25519 and X448 private keys

Signed-off-by: Jethro Beekman <jethro@fortanix.com>
Co-authored-by: Gijs Kwakkel <gijs.kwakkel@fortanix.com>
Signed-off-by: Gijs Kwakkel <gijs.kwakkel@fortanix.com>
diff --git a/ChangeLog.d/rfc8410.txt b/ChangeLog.d/rfc8410.txt
new file mode 100644
index 0000000..e2984ee
--- /dev/null
+++ b/ChangeLog.d/rfc8410.txt
@@ -0,0 +1,3 @@
+Features
+   * Add support for reading and writing X25519 and X448
+     public and private keys in RFC 8410 format using the existing PK APIs.
diff --git a/include/mbedtls/asn1write.h b/include/mbedtls/asn1write.h
index da73759..3c5072c 100644
--- a/include/mbedtls/asn1write.h
+++ b/include/mbedtls/asn1write.h
@@ -164,6 +164,27 @@
                                             size_t par_len);
 
 /**
+ * \brief           Write an AlgorithmIdentifier sequence in ASN.1 format.
+ *
+ * \note            This function works backwards in data buffer.
+ *
+ * \param p         The reference to the current position pointer.
+ * \param start     The start of the buffer, for bounds-checking.
+ * \param oid       The OID of the algorithm to write.
+ * \param oid_len   The length of the algorithm's OID.
+ * \param par_len   The length of the parameters, which must be already written.
+ * \param has_par   If there are any parameters. If 0, par_len must be 0. If 1
+ *                  and \p par_len is 0, NULL parameters are added.
+ *
+ * \return          The number of bytes written to \p p on success.
+ * \return          A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure.
+ */
+int mbedtls_asn1_write_algorithm_identifier_ext(unsigned char **p,
+                                                const unsigned char *start,
+                                                const char *oid, size_t oid_len,
+                                                size_t par_len, int has_par);
+
+/**
  * \brief           Write a boolean tag (#MBEDTLS_ASN1_BOOLEAN) and value
  *                  in ASN.1 format.
  *
diff --git a/include/mbedtls/oid.h b/include/mbedtls/oid.h
index a72f51c..1cbf968 100644
--- a/include/mbedtls/oid.h
+++ b/include/mbedtls/oid.h
@@ -90,6 +90,9 @@
 #define MBEDTLS_OID_OIW_SECSIG                  MBEDTLS_OID_ORG_OIW "\x03"
 #define MBEDTLS_OID_OIW_SECSIG_ALG              MBEDTLS_OID_OIW_SECSIG "\x02"
 #define MBEDTLS_OID_OIW_SECSIG_SHA1             MBEDTLS_OID_OIW_SECSIG_ALG "\x1a"
+#define MBEDTLS_OID_ORG_THAWTE                  "\x65"          /* thawte(101) */
+#define MBEDTLS_OID_THAWTE                      MBEDTLS_OID_ISO_IDENTIFIED_ORG \
+        MBEDTLS_OID_ORG_THAWTE
 #define MBEDTLS_OID_ORG_CERTICOM                "\x81\x04"  /* certicom(132) */
 #define MBEDTLS_OID_CERTICOM                    MBEDTLS_OID_ISO_IDENTIFIED_ORG \
         MBEDTLS_OID_ORG_CERTICOM
@@ -437,6 +440,15 @@
  *   ecdsa-with-SHA2(3) 4 } */
 #define MBEDTLS_OID_ECDSA_SHA512            MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 "\x04"
 
+/*
+ * EC key algorithms from RFC 8410
+ */
+
+#define MBEDTLS_OID_X25519                  MBEDTLS_OID_THAWTE "\x6e" /**< id-X25519    OBJECT IDENTIFIER ::= { 1 3 101 110 } */
+#define MBEDTLS_OID_X448                    MBEDTLS_OID_THAWTE "\x6f" /**< id-X448      OBJECT IDENTIFIER ::= { 1 3 101 111 } */
+#define MBEDTLS_OID_ED25519                 MBEDTLS_OID_THAWTE "\x70" /**< id-Ed25519   OBJECT IDENTIFIER ::= { 1 3 101 112 } */
+#define MBEDTLS_OID_ED448                   MBEDTLS_OID_THAWTE "\x71" /**< id-Ed448     OBJECT IDENTIFIER ::= { 1 3 101 113 } */
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -531,6 +543,30 @@
  */
 int mbedtls_oid_get_oid_by_ec_grp(mbedtls_ecp_group_id grp_id,
                                   const char **oid, size_t *olen);
+
+/**
+ * \brief          Translate AlgorithmIdentifier OID into an EC group identifier,
+ *                 for curves that are directly encoded at this level
+ *
+ * \param oid      OID to use
+ * \param grp_id   place to store group id
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_ec_grp_algid(const mbedtls_asn1_buf *oid, mbedtls_ecp_group_id *grp_id);
+
+/**
+ * \brief          Translate EC group identifier into AlgorithmIdentifier OID,
+ *                 for curves that are directly encoded at this level
+ *
+ * \param grp_id   EC group identifier
+ * \param oid      place to store ASN.1 OID string pointer
+ * \param olen     length of the OID
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_oid_by_ec_grp_algid(mbedtls_ecp_group_id grp_id,
+                                        const char **oid, size_t *olen);
 #endif /* MBEDTLS_ECP_LIGHT */
 
 /**
diff --git a/library/asn1write.c b/library/asn1write.c
index b9d586a..c65d937 100644
--- a/library/asn1write.c
+++ b/library/asn1write.c
@@ -195,13 +195,22 @@
                                             const char *oid, size_t oid_len,
                                             size_t par_len)
 {
+    return mbedtls_asn1_write_algorithm_identifier_ext(p, start, oid, oid_len, par_len, 1);
+}
+
+int mbedtls_asn1_write_algorithm_identifier_ext(unsigned char **p, const unsigned char *start,
+                                                const char *oid, size_t oid_len,
+                                                size_t par_len, int has_par)
+{
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     size_t len = 0;
 
-    if (par_len == 0) {
-        MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_null(p, start));
-    } else {
-        len += par_len;
+    if (has_par) {
+        if (par_len == 0) {
+            MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_null(p, start));
+        } else {
+            len += par_len;
+        }
     }
 
     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_oid(p, start, oid, oid_len));
diff --git a/library/oid.c b/library/oid.c
index 80cadcd..8e57fe4 100644
--- a/library/oid.c
+++ b/library/oid.c
@@ -533,7 +533,7 @@
 
 #if defined(MBEDTLS_ECP_LIGHT)
 /*
- * For namedCurve (RFC 5480)
+ * For elliptic curves that use namedCurve inside ECParams (RFC 5480)
  */
 typedef struct {
     mbedtls_oid_descriptor_t    descriptor;
@@ -621,6 +621,47 @@
                         oid_ecp_grp,
                         mbedtls_ecp_group_id,
                         grp_id)
+
+/*
+ * For Elliptic Curve algorithms that are directly
+ * encoded in the AlgorithmIdentifier (RFC 8410)
+ */
+typedef struct {
+    mbedtls_oid_descriptor_t    descriptor;
+    mbedtls_ecp_group_id        grp_id;
+} oid_ecp_grp_algid_t;
+
+static const oid_ecp_grp_algid_t oid_ecp_grp_algid[] =
+{
+#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
+    {
+        OID_DESCRIPTOR(MBEDTLS_OID_X25519,               "X25519",       "X25519"),
+        MBEDTLS_ECP_DP_CURVE25519,
+    },
+#endif /* MBEDTLS_ECP_DP_CURVE25519_ENABLED */
+#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+    {
+        OID_DESCRIPTOR(MBEDTLS_OID_X448,                 "X448",         "X448"),
+        MBEDTLS_ECP_DP_CURVE448,
+    },
+#endif /* MBEDTLS_ECP_DP_CURVE448_ENABLED */
+    {
+        NULL_OID_DESCRIPTOR,
+        MBEDTLS_ECP_DP_NONE,
+    },
+};
+
+FN_OID_TYPED_FROM_ASN1(oid_ecp_grp_algid_t, grp_id_algid, oid_ecp_grp_algid)
+FN_OID_GET_ATTR1(mbedtls_oid_get_ec_grp_algid,
+                 oid_ecp_grp_algid_t,
+                 grp_id_algid,
+                 mbedtls_ecp_group_id,
+                 grp_id)
+FN_OID_GET_OID_BY_ATTR1(mbedtls_oid_get_oid_by_ec_grp_algid,
+                        oid_ecp_grp_algid_t,
+                        oid_ecp_grp_algid,
+                        mbedtls_ecp_group_id,
+                        grp_id)
 #endif /* MBEDTLS_ECP_LIGHT */
 
 #if defined(MBEDTLS_CIPHER_C)
diff --git a/library/pkparse.c b/library/pkparse.c
index 800e352..617e4fd 100644
--- a/library/pkparse.c
+++ b/library/pkparse.c
@@ -32,8 +32,9 @@
 #if defined(MBEDTLS_RSA_C)
 #include "mbedtls/rsa.h"
 #endif
-#if defined(MBEDTLS_ECP_C)
 #include "mbedtls/ecp.h"
+#if defined(MBEDTLS_RSA_C) || defined(MBEDTLS_ECP_C)
+#include "pkwrite.h"
 #endif
 #if defined(MBEDTLS_ECDSA_C)
 #include "mbedtls/ecdsa.h"
@@ -232,7 +233,7 @@
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     unsigned char *p = params->p;
-    const unsigned char * const end = params->p + params->len;
+    const unsigned char *const end = params->p + params->len;
     const unsigned char *end_field, *end_curve;
     size_t len;
     int ver;
@@ -404,7 +405,6 @@
             mbedtls_mpi_get_bit(&grp->G.Y, 0) == mbedtls_mpi_get_bit(&ref.G.Y, 0)) {
             break;
         }
-
     }
 
 cleanup:
@@ -495,6 +495,116 @@
     return 0;
 }
 
+#if defined(MBEDTLS_ECP_LIGHT)
+/*
+ * Helper function for deriving a public key from its private counterpart.
+ */
+static int pk_derive_public_key(mbedtls_ecp_keypair *eck,
+                                const unsigned char *d, size_t d_len,
+                                int (*f_rng)(void *, unsigned char *, size_t), void *p_rng)
+{
+    int ret;
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    psa_status_t status, destruction_status;
+    psa_key_attributes_t key_attr = PSA_KEY_ATTRIBUTES_INIT;
+    size_t curve_bits;
+    psa_ecc_family_t curve = mbedtls_ecc_group_to_psa(eck->grp.id, &curve_bits);
+    /* This buffer is used to store the private key at first and then the
+     * public one (but not at the same time). Therefore we size it for the
+     * latter since it's bigger. */
+    unsigned char key_buf[MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH];
+    size_t key_len;
+    mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT;
+
+    (void) f_rng;
+    (void) p_rng;
+
+    psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(curve));
+    psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_EXPORT);
+
+    status = psa_import_key(&key_attr, d, d_len, &key_id);
+    ret = psa_pk_status_to_mbedtls(status);
+    if (ret != 0) {
+        return ret;
+    }
+
+    mbedtls_platform_zeroize(key_buf, sizeof(key_buf));
+
+    status = psa_export_public_key(key_id, key_buf, sizeof(key_buf), &key_len);
+    ret = psa_pk_status_to_mbedtls(status);
+    destruction_status = psa_destroy_key(key_id);
+    if (ret != 0) {
+        return ret;
+    } else if (destruction_status != PSA_SUCCESS) {
+        return psa_pk_status_to_mbedtls(destruction_status);
+    }
+
+    ret = mbedtls_ecp_point_read_binary(&eck->grp, &eck->Q, key_buf, key_len);
+#else /* MBEDTLS_USE_PSA_CRYPTO */
+    (void) d;
+    (void) d_len;
+
+    ret = mbedtls_ecp_mul(&eck->grp, &eck->Q, &eck->d, &eck->grp.G, f_rng, p_rng);
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+    return ret;
+}
+
+#if defined(MBEDTLS_PK_HAVE_RFC8410_CURVES)
+
+/*
+ * Load an RFC8410 EC key, which doesn't have any parameters
+ */
+static int pk_use_ecparams_rfc8410(const mbedtls_asn1_buf *params,
+                                   mbedtls_ecp_group_id grp_id,
+                                   mbedtls_ecp_group *grp)
+{
+    if (params->tag != 0 || params->len != 0) {
+        return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT;
+    }
+
+    return mbedtls_ecp_group_load(grp, grp_id);
+}
+
+/*
+ * Parse an RFC 8410 encoded private EC key
+ *
+ * CurvePrivateKey ::= OCTET STRING
+ */
+static int pk_parse_key_rfc8410_der(mbedtls_ecp_keypair *eck,
+                                    unsigned char *key, size_t keylen, const unsigned char *end,
+                                    int (*f_rng)(void *, unsigned char *, size_t), void *p_rng)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    size_t len;
+
+    if ((ret = mbedtls_asn1_get_tag(&key, (key + keylen), &len, MBEDTLS_ASN1_OCTET_STRING)) != 0) {
+        return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret);
+    }
+
+    if (key + len != end) {
+        return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT;
+    }
+
+    if ((ret = mbedtls_mpi_read_binary_le(&eck->d, key, len)) != 0) {
+        mbedtls_ecp_keypair_free(eck);
+        return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret);
+    }
+
+    if ((ret = pk_derive_public_key(eck, key, len, f_rng, p_rng)) != 0) {
+        mbedtls_ecp_keypair_free(eck);
+        return ret;
+    }
+
+    if ((ret = mbedtls_ecp_check_privkey(&eck->grp, &eck->d)) != 0) {
+        mbedtls_ecp_keypair_free(eck);
+        return ret;
+    }
+
+    return 0;
+}
+#endif /* MBEDTLS_PK_HAVE_RFC8410_CURVES */
+#endif /* MBEDTLS_ECP_LIGHT */
+
 /*
  * EC public key is an EC point
  *
@@ -591,7 +701,8 @@
  */
 static int pk_get_pk_alg(unsigned char **p,
                          const unsigned char *end,
-                         mbedtls_pk_type_t *pk_alg, mbedtls_asn1_buf *params)
+                         mbedtls_pk_type_t *pk_alg, mbedtls_asn1_buf *params,
+                         mbedtls_ecp_group_id *ec_grp_id)
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     mbedtls_asn1_buf alg_oid;
@@ -602,7 +713,18 @@
         return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_ALG, ret);
     }
 
-    if (mbedtls_oid_get_pk_alg(&alg_oid, pk_alg) != 0) {
+    ret = mbedtls_oid_get_pk_alg(&alg_oid, pk_alg);
+#if defined(MBEDTLS_ECP_LIGHT)
+    if (ret == MBEDTLS_ERR_OID_NOT_FOUND) {
+        ret = mbedtls_oid_get_ec_grp_algid(&alg_oid, ec_grp_id);
+        if (ret == 0) {
+            *pk_alg = MBEDTLS_PK_ECKEY;
+        }
+    }
+#else
+    (void) ec_grp_id;
+#endif
+    if (ret != 0) {
         return MBEDTLS_ERR_PK_UNKNOWN_PK_ALG;
     }
 
@@ -630,6 +752,7 @@
     size_t len;
     mbedtls_asn1_buf alg_params;
     mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE;
+    mbedtls_ecp_group_id ec_grp_id = MBEDTLS_ECP_DP_NONE;
     const mbedtls_pk_info_t *pk_info;
 
     if ((ret = mbedtls_asn1_get_tag(p, end, &len,
@@ -639,7 +762,7 @@
 
     end = *p + len;
 
-    if ((ret = pk_get_pk_alg(p, end, &pk_alg, &alg_params)) != 0) {
+    if ((ret = pk_get_pk_alg(p, end, &pk_alg, &alg_params, &ec_grp_id)) != 0) {
         return ret;
     }
 
@@ -667,7 +790,14 @@
 #endif /* MBEDTLS_RSA_C */
 #if defined(MBEDTLS_ECP_LIGHT)
     if (pk_alg == MBEDTLS_PK_ECKEY_DH || pk_alg == MBEDTLS_PK_ECKEY) {
-        ret = pk_use_ecparams(&alg_params, &mbedtls_pk_ec(*pk)->grp);
+#if defined(MBEDTLS_PK_HAVE_RFC8410_CURVES)
+        if (mbedtls_pk_is_rfc8410_curve(ec_grp_id)) {
+            ret = pk_use_ecparams_rfc8410(&alg_params, ec_grp_id, &mbedtls_pk_ec(*pk)->grp);
+        } else
+#endif
+        {
+            ret = pk_use_ecparams(&alg_params, &mbedtls_pk_ec(*pk)->grp);
+        }
         if (ret == 0) {
             ret = pk_get_ecpubkey(p, end, mbedtls_pk_ec(*pk));
         }
@@ -877,57 +1007,6 @@
 #endif /* MBEDTLS_RSA_C */
 
 #if defined(MBEDTLS_ECP_LIGHT)
-#if defined(MBEDTLS_USE_PSA_CRYPTO)
-/*
- * Helper function for deriving a public key from its private counterpart by
- * using PSA functions.
- */
-static int pk_derive_public_key(mbedtls_ecp_group *grp, mbedtls_ecp_point *Q,
-                                const mbedtls_mpi *d)
-{
-    psa_status_t status, destruction_status;
-    psa_key_attributes_t key_attr = PSA_KEY_ATTRIBUTES_INIT;
-    size_t curve_bits;
-    psa_ecc_family_t curve = mbedtls_ecc_group_to_psa(grp->id, &curve_bits);
-    /* This buffer is used to store the private key at first and then the
-     * public one (but not at the same time). Therefore we size it for the
-     * latter since it's bigger. */
-    unsigned char key_buf[MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH];
-    size_t key_len = PSA_BITS_TO_BYTES(curve_bits);
-    mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT;
-    int ret;
-
-    psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(curve));
-    psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_EXPORT);
-
-    ret = mbedtls_mpi_write_binary(d, key_buf, key_len);
-    if (ret != 0) {
-        return ret;
-    }
-
-    status = psa_import_key(&key_attr, key_buf, key_len, &key_id);
-    ret = psa_pk_status_to_mbedtls(status);
-    if (ret != 0) {
-        return ret;
-    }
-
-    mbedtls_platform_zeroize(key_buf, sizeof(key_buf));
-
-    status = psa_export_public_key(key_id, key_buf, sizeof(key_buf), &key_len);
-    ret = psa_pk_status_to_mbedtls(status);
-    destruction_status = psa_destroy_key(key_id);
-    if (ret != 0) {
-        return ret;
-    } else if (destruction_status != PSA_SUCCESS) {
-        return psa_pk_status_to_mbedtls(destruction_status);
-    }
-
-    ret = mbedtls_ecp_point_read_binary(grp, Q, key_buf, key_len);
-
-    return ret;
-}
-#endif /* MBEDTLS_USE_PSA_CRYPTO */
-
 /*
  * Parse a SEC1 encoded private EC key
  */
@@ -937,9 +1016,10 @@
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     int version, pubkey_done;
-    size_t len;
+    size_t len, d_len;
     mbedtls_asn1_buf params = { 0, 0, NULL };
     unsigned char *p = (unsigned char *) key;
+    unsigned char *d;
     unsigned char *end = p + keylen;
     unsigned char *end2;
 
@@ -972,6 +1052,8 @@
         return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret);
     }
 
+    d = p;
+    d_len = len;
     if ((ret = mbedtls_mpi_read_binary(&eck->d, p, len)) != 0) {
         mbedtls_ecp_keypair_free(eck);
         return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret);
@@ -1035,20 +1117,10 @@
     }
 
     if (!pubkey_done) {
-#if defined(MBEDTLS_USE_PSA_CRYPTO)
-        (void) f_rng;
-        (void) p_rng;
-        if ((ret = pk_derive_public_key(&eck->grp, &eck->Q, &eck->d)) != 0) {
+        if ((ret = pk_derive_public_key(eck, d, d_len, f_rng, p_rng)) != 0) {
             mbedtls_ecp_keypair_free(eck);
             return ret;
         }
-#else /* MBEDTLS_USE_PSA_CRYPTO */
-        if ((ret = mbedtls_ecp_mul(&eck->grp, &eck->Q, &eck->d, &eck->grp.G,
-                                   f_rng, p_rng)) != 0) {
-            mbedtls_ecp_keypair_free(eck);
-            return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret);
-        }
-#endif /* MBEDTLS_USE_PSA_CRYPTO */
     }
 
     if ((ret = mbedtls_ecp_check_privkey(&eck->grp, &eck->d)) != 0) {
@@ -1084,9 +1156,10 @@
     unsigned char *p = (unsigned char *) key;
     unsigned char *end = p + keylen;
     mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE;
+    mbedtls_ecp_group_id ec_grp_id = MBEDTLS_ECP_DP_NONE;
     const mbedtls_pk_info_t *pk_info;
 
-#if !defined(MBEDTLS_ECP_C)
+#if !defined(MBEDTLS_ECP_LIGHT)
     (void) f_rng;
     (void) p_rng;
 #endif
@@ -1122,7 +1195,7 @@
         return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_VERSION, ret);
     }
 
-    if ((ret = pk_get_pk_alg(&p, end, &pk_alg, &params)) != 0) {
+    if ((ret = pk_get_pk_alg(&p, end, &pk_alg, &params, &ec_grp_id)) != 0) {
         return ret;
     }
 
@@ -1153,10 +1226,24 @@
 #endif /* MBEDTLS_RSA_C */
 #if defined(MBEDTLS_ECP_LIGHT)
     if (pk_alg == MBEDTLS_PK_ECKEY || pk_alg == MBEDTLS_PK_ECKEY_DH) {
-        if ((ret = pk_use_ecparams(&params, &mbedtls_pk_ec(*pk)->grp)) != 0 ||
-            (ret = pk_parse_key_sec1_der(mbedtls_pk_ec(*pk), p, len, f_rng, p_rng)) != 0) {
-            mbedtls_pk_free(pk);
-            return ret;
+#if defined(MBEDTLS_PK_HAVE_RFC8410_CURVES)
+        if (mbedtls_pk_is_rfc8410_curve(ec_grp_id)) {
+            if ((ret =
+                     pk_use_ecparams_rfc8410(&params, ec_grp_id, &mbedtls_pk_ec(*pk)->grp)) != 0 ||
+                (ret =
+                     pk_parse_key_rfc8410_der(mbedtls_pk_ec(*pk), p, len, end, f_rng,
+                                              p_rng)) != 0) {
+                mbedtls_pk_free(pk);
+                return ret;
+            }
+        } else
+#endif
+        {
+            if ((ret = pk_use_ecparams(&params, &mbedtls_pk_ec(*pk)->grp)) != 0 ||
+                (ret = pk_parse_key_sec1_der(mbedtls_pk_ec(*pk), p, len, f_rng, p_rng)) != 0) {
+                mbedtls_pk_free(pk);
+                return ret;
+            }
         }
     } else
 #endif /* MBEDTLS_ECP_LIGHT */
@@ -1557,7 +1644,7 @@
          */
         p = pem.buf;
 
-        ret = mbedtls_pk_parse_subpubkey(&p,  p + pem.buflen, ctx);
+        ret = mbedtls_pk_parse_subpubkey(&p, p + pem.buflen, ctx);
         mbedtls_pem_free(&pem);
         return ret;
     } else if (ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT) {
diff --git a/library/pkwrite.c b/library/pkwrite.c
index 3c1a408..c753aa9 100644
--- a/library/pkwrite.c
+++ b/library/pkwrite.c
@@ -213,7 +213,8 @@
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     unsigned char *c;
-    size_t len = 0, par_len = 0, oid_len;
+    int has_par = 1;
+    size_t len = 0, par_len = 0, oid_len = 0;
     mbedtls_pk_type_t pk_type;
     const char *oid;
 
@@ -243,7 +244,17 @@
     pk_type = mbedtls_pk_get_type(key);
 #if defined(MBEDTLS_ECP_LIGHT)
     if (pk_type == MBEDTLS_PK_ECKEY) {
-        MBEDTLS_ASN1_CHK_ADD(par_len, pk_write_ec_param(&c, buf, mbedtls_pk_ec(*key)));
+        mbedtls_ecp_keypair *ec = mbedtls_pk_ec(*key);
+
+        ret = mbedtls_oid_get_oid_by_ec_grp_algid(ec->grp.id, &oid, &oid_len);
+
+        if (ret == 0) {
+            has_par = 0;
+        } else if (ret == MBEDTLS_ERR_OID_NOT_FOUND) {
+            MBEDTLS_ASN1_CHK_ADD(par_len, pk_write_ec_param(&c, buf, ec));
+        } else {
+            return ret;
+        }
     }
 #endif /* MBEDTLS_ECP_LIGHT */
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
@@ -253,6 +264,8 @@
         mbedtls_svc_key_id_t key_id;
         psa_ecc_family_t curve;
         size_t bits;
+        size_t oid2_len = 0;
+        const char *oid2;
 
         key_id = *((mbedtls_svc_key_id_t *) key->pk_ctx);
         if (PSA_SUCCESS != psa_get_key_attributes(key_id, &attributes)) {
@@ -269,7 +282,7 @@
             }
 
             ret = mbedtls_psa_get_ecc_oid_from_id(curve, bits,
-                                                  &oid, &oid_len);
+                                                  &oid2, &oid2_len);
             if (ret != 0) {
                 return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
             }
@@ -277,8 +290,8 @@
             /* Write EC algorithm parameters; that's akin
              * to pk_write_ec_param() above. */
             MBEDTLS_ASN1_CHK_ADD(par_len, mbedtls_asn1_write_oid(&c, buf,
-                                                                 oid,
-                                                                 oid_len));
+                                                                 oid2,
+                                                                 oid2_len));
 
             /* The rest of the function works as for legacy EC contexts. */
             pk_type = MBEDTLS_PK_ECKEY;
@@ -291,13 +304,15 @@
     }
 #endif /* MBEDTLS_USE_PSA_CRYPTO */
 
-    if ((ret = mbedtls_oid_get_oid_by_pk_alg(pk_type, &oid,
-                                             &oid_len)) != 0) {
-        return ret;
+    if (oid_len == 0) {
+        if ((ret = mbedtls_oid_get_oid_by_pk_alg(pk_type, &oid,
+                                                 &oid_len)) != 0) {
+            return ret;
+        }
     }
 
-    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_algorithm_identifier(&c, buf, oid, oid_len,
-                                                                      par_len));
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_algorithm_identifier_ext(&c, buf, oid, oid_len,
+                                                                          par_len, has_par));
 
     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len));
     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_CONSTRUCTED |
@@ -306,6 +321,55 @@
     return (int) len;
 }
 
+#if defined(MBEDTLS_ECP_LIGHT)
+#if defined(MBEDTLS_PK_HAVE_RFC8410_CURVES)
+/*
+ * RFC8410
+ *
+ * OneAsymmetricKey ::= SEQUENCE {
+ *    version Version,
+ *    privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ *    privateKey PrivateKey,
+ *    attributes [0] IMPLICIT Attributes OPTIONAL,
+ *    ...,
+ *    [[2: publicKey [1] IMPLICIT PublicKey OPTIONAL ]],
+ *    ...
+ * }
+ *
+ * CurvePrivateKey ::= OCTET STRING
+ */
+static int pk_write_ec_rfc8410_der(unsigned char **p, unsigned char *buf,
+                                   mbedtls_ecp_keypair *ec)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    size_t len = 0;
+    size_t oid_len = 0;
+    const char *oid;
+
+    /* privateKey */
+    MBEDTLS_ASN1_CHK_ADD(len, pk_write_ec_private(p, buf, ec));
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, buf, len));
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, buf, MBEDTLS_ASN1_OCTET_STRING));
+
+    /* privateKeyAlgorithm */
+    if ((ret = mbedtls_oid_get_oid_by_ec_grp_algid(ec->grp.id, &oid, &oid_len)) != 0) {
+        return ret;
+    }
+    MBEDTLS_ASN1_CHK_ADD(len,
+                         mbedtls_asn1_write_algorithm_identifier_ext(p, buf, oid, oid_len, 0, 0));
+
+    /* version */
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_int(p, buf, 0));
+
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, buf, len));
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, buf, MBEDTLS_ASN1_CONSTRUCTED |
+                                                     MBEDTLS_ASN1_SEQUENCE));
+
+    return (int) len;
+}
+#endif /* MBEDTLS_PK_HAVE_RFC8410_CURVES */
+#endif /* MBEDTLS_ECP_LIGHT */
+
 int mbedtls_pk_write_key_der(const mbedtls_pk_context *key, unsigned char *buf, size_t size)
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
@@ -409,6 +473,12 @@
         mbedtls_ecp_keypair *ec = mbedtls_pk_ec(*key);
         size_t pub_len = 0, par_len = 0;
 
+#if defined(MBEDTLS_PK_HAVE_RFC8410_CURVES)
+        if (mbedtls_pk_is_rfc8410_curve(ec->grp.id)) {
+            return pk_write_ec_rfc8410_der(&c, buf, ec);
+        }
+#endif
+
         /*
          * RFC 5915, or SEC1 Appendix C.4
          *
@@ -472,6 +542,8 @@
 #define PEM_END_PRIVATE_KEY_RSA     "-----END RSA PRIVATE KEY-----\n"
 #define PEM_BEGIN_PRIVATE_KEY_EC    "-----BEGIN EC PRIVATE KEY-----\n"
 #define PEM_END_PRIVATE_KEY_EC      "-----END EC PRIVATE KEY-----\n"
+#define PEM_BEGIN_PRIVATE_KEY_PKCS8 "-----BEGIN PRIVATE KEY-----\n"
+#define PEM_END_PRIVATE_KEY_PKCS8   "-----END PRIVATE KEY-----\n"
 
 #define PUB_DER_MAX_BYTES                                                   \
     (MBEDTLS_PK_RSA_PUB_DER_MAX_BYTES > MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES ? \
@@ -519,8 +591,16 @@
 #endif
 #if defined(MBEDTLS_ECP_LIGHT)
     if (mbedtls_pk_get_type(key) == MBEDTLS_PK_ECKEY) {
-        begin = PEM_BEGIN_PRIVATE_KEY_EC;
-        end = PEM_END_PRIVATE_KEY_EC;
+#if defined(MBEDTLS_PK_HAVE_RFC8410_CURVES)
+        if (mbedtls_pk_is_rfc8410_curve(mbedtls_pk_ec(*key)->grp.id)) {
+            begin = PEM_BEGIN_PRIVATE_KEY_PKCS8;
+            end = PEM_END_PRIVATE_KEY_PKCS8;
+        } else
+#endif
+        {
+            begin = PEM_BEGIN_PRIVATE_KEY_EC;
+            end = PEM_END_PRIVATE_KEY_EC;
+        }
     } else
 #endif
     return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
diff --git a/library/pkwrite.h b/library/pkwrite.h
index 8aebd0c..537bd0f 100644
--- a/library/pkwrite.h
+++ b/library/pkwrite.h
@@ -105,4 +105,27 @@
 
 #endif /* MBEDTLS_ECP_C */
 
+#if defined(MBEDTLS_ECP_LIGHT)
+#include "mbedtls/ecp.h"
+
+#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) || defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+#define MBEDTLS_PK_HAVE_RFC8410_CURVES
+
+static inline int mbedtls_pk_is_rfc8410_curve(mbedtls_ecp_group_id id)
+{
+#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
+    if (id == MBEDTLS_ECP_DP_CURVE25519) {
+        return 1;
+    }
+#endif
+#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+    if (id == MBEDTLS_ECP_DP_CURVE448) {
+        return 1;
+    }
+#endif
+    return 0;
+}
+#endif /* MBEDTLS_ECP_DP_CURVE25519_ENABLED || MBEDTLS_ECP_DP_CURVE448_ENABLED */
+#endif /* MBEDTLS_ECP_LIGHT */
+
 #endif /* MBEDTLS_PK_WRITE_H */
diff --git a/tests/data_files/Makefile b/tests/data_files/Makefile
index 257903c..bf61706 100644
--- a/tests/data_files/Makefile
+++ b/tests/data_files/Makefile
@@ -1003,6 +1003,38 @@
 	$(OPENSSL) ec -pubin -in $< -out $@ -conv_form compressed
 all_final += ec_bp512_pub.comp.pem
 
+ec_x25519_prv.der:
+	$(OPENSSL) genpkey -algorithm X25519 -out $@ -outform DER
+all_final += ec_x25519_prv.der
+
+ec_x25519_pub.der: ec_x25519_pub.der
+	$(OPENSSL) pkey -in $< -inform DER -out $@ -outform DER
+all_final += ec_x25519_pub.der
+
+ec_x25519_prv.pem: ec_x25519_prv.pem
+	$(OPENSSL) pkey -in $< -inform DER -out $@
+all_final += ec_x25519_prv.pem
+
+ec_x25519_pub.pem: ec_x25519_pub.pem
+	$(OPENSSL) pkey -in $< -inform DER -out $@
+all_final += ec_x25519_pub.pem
+
+ec_x448_prv.der:
+	$(OPENSSL) genpkey -algorithm X448 -out $@ -outform DER
+all_final += ec_x448_prv.der
+
+ec_x448_pub.der: ec_x448_pub.der
+	$(OPENSSL) pkey -in $< -inform DER -out $@ -outform DER
+all_final += ec_x448_pub.der
+
+ec_x448_prv.pem: ec_x448_prv.pem
+	$(OPENSSL) pkey -in $< -inform DER -out $@
+all_final += ec_x448_prv.pem
+
+ec_x448_pub.pem: ec_x448_pub.pem
+	$(OPENSSL) pkey -in $< -inform DER -out $@
+all_final += ec_x448_pub.pem
+
 ################################################################
 #### Convert PEM keys to DER format
 ################################################################
diff --git a/tests/data_files/ec_x25519_prv.der b/tests/data_files/ec_x25519_prv.der
new file mode 100644
index 0000000..ea23733
--- /dev/null
+++ b/tests/data_files/ec_x25519_prv.der
Binary files differ
diff --git a/tests/data_files/ec_x25519_prv.pem b/tests/data_files/ec_x25519_prv.pem
new file mode 100644
index 0000000..0072240
--- /dev/null
+++ b/tests/data_files/ec_x25519_prv.pem
@@ -0,0 +1,3 @@
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VuBCIEILBtgpZVVDpRy6NuU1IrwKz9YK9ZRmVV+z4eeWhyqxpZ
+-----END PRIVATE KEY-----
diff --git a/tests/data_files/ec_x25519_pub.der b/tests/data_files/ec_x25519_pub.der
new file mode 100644
index 0000000..922cb76
--- /dev/null
+++ b/tests/data_files/ec_x25519_pub.der
Binary files differ
diff --git a/tests/data_files/ec_x25519_pub.pem b/tests/data_files/ec_x25519_pub.pem
new file mode 100644
index 0000000..2a36b5b
--- /dev/null
+++ b/tests/data_files/ec_x25519_pub.pem
@@ -0,0 +1,3 @@
+-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VuAyEAm8Ow6T2CM/5qi6YTiUjMEqkTYtXC7YFYTbBatUGcnRE=
+-----END PUBLIC KEY-----
diff --git a/tests/data_files/ec_x448_prv.der b/tests/data_files/ec_x448_prv.der
new file mode 100644
index 0000000..f6d52f7
--- /dev/null
+++ b/tests/data_files/ec_x448_prv.der
Binary files differ
diff --git a/tests/data_files/ec_x448_prv.pem b/tests/data_files/ec_x448_prv.pem
new file mode 100644
index 0000000..7bca661
--- /dev/null
+++ b/tests/data_files/ec_x448_prv.pem
@@ -0,0 +1,4 @@
+-----BEGIN PRIVATE KEY-----
+MEYCAQAwBQYDK2VvBDoEOGTpCDYD9atLDMYwDnjdwUTkjO4ZMB/uacXKw+4iUiED
+oe50tXsIxi5REqWe2YOoL7eD7npOGRPt
+-----END PRIVATE KEY-----
diff --git a/tests/data_files/ec_x448_pub.der b/tests/data_files/ec_x448_pub.der
new file mode 100644
index 0000000..7c44c91
--- /dev/null
+++ b/tests/data_files/ec_x448_pub.der
Binary files differ
diff --git a/tests/data_files/ec_x448_pub.pem b/tests/data_files/ec_x448_pub.pem
new file mode 100644
index 0000000..306e10c
--- /dev/null
+++ b/tests/data_files/ec_x448_pub.pem
@@ -0,0 +1,4 @@
+-----BEGIN PUBLIC KEY-----
+MEIwBQYDK2VvAzkAlrVhn5KDNBt3nL38B9mqGKqrPwnah3ynJgaWQ5IcLzv6zZT+
+TIjhGQ1NFGWwgtOV8UqU2tO4pYQ=
+-----END PUBLIC KEY-----
diff --git a/tests/suites/test_suite_pkparse.data b/tests/suites/test_suite_pkparse.data
index 3a53dc0..1bd1af2 100644
--- a/tests/suites/test_suite_pkparse.data
+++ b/tests/suites/test_suite_pkparse.data
@@ -974,6 +974,22 @@
 depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECP_LIGHT:MBEDTLS_ECP_DP_BP512R1_ENABLED
 pk_parse_public_keyfile_ec:"data_files/ec_bp512_pub.comp.pem":0
 
+Parse Public EC Key #10 (RFC 8410, DER, X25519)
+depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECP_LIGHT:MBEDTLS_ECP_DP_CURVE25519_ENABLED
+pk_parse_public_keyfile_ec:"data_files/ec_x25519_pub.der":0
+
+Parse Public EC Key #11 (RFC 8410, DER, X448)
+depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECP_LIGHT:MBEDTLS_ECP_DP_CURVE448_ENABLED
+pk_parse_public_keyfile_ec:"data_files/ec_x448_pub.der":0
+
+Parse Public EC Key #12 (RFC 8410, PEM, X25519)
+depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECP_LIGHT:MBEDTLS_ECP_DP_CURVE25519_ENABLED
+pk_parse_public_keyfile_ec:"data_files/ec_x25519_pub.pem":0
+
+Parse Public EC Key #13 (RFC 8410, PEM, X448)
+depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECP_LIGHT:MBEDTLS_ECP_DP_CURVE448_ENABLED
+pk_parse_public_keyfile_ec:"data_files/ec_x448_pub.pem":0
+
 Parse EC Key #1 (SEC1 DER)
 depends_on:MBEDTLS_ECP_LIGHT:MBEDTLS_ECP_DP_SECP192R1_ENABLED
 pk_parse_keyfile_ec:"data_files/ec_prv.sec1.der":"NULL":0
@@ -1082,6 +1098,22 @@
 depends_on:MBEDTLS_ECP_LIGHT:MBEDTLS_ECP_DP_SECP256K1_ENABLED:MBEDTLS_PK_PARSE_EC_EXTENDED
 pk_parse_keyfile_ec:"data_files/ec_prv.specdom.der":"NULL":0
 
+Parse EC Key #16 (RFC 8410, DER, X25519)
+depends_on:MBEDTLS_ECP_LIGHT:MBEDTLS_ECP_DP_CURVE25519_ENABLED
+pk_parse_keyfile_ec:"data_files/ec_x25519_prv.der":"NULL":0
+
+Parse EC Key #17 (RFC 8410, DER, X448)
+depends_on:MBEDTLS_ECP_LIGHT:MBEDTLS_ECP_DP_CURVE448_ENABLED
+pk_parse_keyfile_ec:"data_files/ec_x448_prv.der":"NULL":0
+
+Parse EC Key #18 (RFC 8410, PEM, X25519)
+depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECP_LIGHT:MBEDTLS_ECP_DP_CURVE25519_ENABLED
+pk_parse_keyfile_ec:"data_files/ec_x25519_prv.pem":"NULL":0
+
+Parse EC Key #19 (RFC 8410, PEM, X448)
+depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECP_LIGHT:MBEDTLS_ECP_DP_CURVE448_ENABLED
+pk_parse_keyfile_ec:"data_files/ec_x448_prv.pem":"NULL":0
+
 Key ASN1 (No data)
 pk_parse_key:"":MBEDTLS_ERR_PK_KEY_INVALID_FORMAT
 
@@ -1163,3 +1195,30 @@
 Key ASN1 (ECPrivateKey, empty parameters)
 depends_on:MBEDTLS_ECP_LIGHT
 pk_parse_key:"30070201010400a000":MBEDTLS_ERR_PK_KEY_INVALID_FORMAT
+
+Key ASN1 (OneAsymmetricKey X25519, doesn't match masking requirements, from RFC8410 Appendix A but made into version 0)
+depends_on:MBEDTLS_ECP_LIGHT
+pk_parse_key:"302e020100300506032b656e04220420f8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f":MBEDTLS_ERR_PK_KEY_INVALID_FORMAT
+
+Key ASN1 (OneAsymmetricKey X25519, with invalid optional AlgorithIdentifier parameters)
+depends_on:MBEDTLS_ECP_LIGHT
+pk_parse_key:"3030020100300706032b656e050004220420b06d829655543a51cba36e53522bc0acfd60af59466555fb3e1e796872ab1a59":MBEDTLS_ERR_PK_KEY_INVALID_FORMAT
+
+Key ASN1 (OneAsymmetricKey X25519, with NULL private key)
+depends_on:MBEDTLS_ECP_LIGHT
+pk_parse_key:"300e020100300506032b656e04020500":MBEDTLS_ERR_PK_KEY_INVALID_FORMAT
+
+Key ASN1 (OneAsymmetricKey with invalid AlgorithIdentifier)
+pk_parse_key:"3013020100300a06082b0601040181fd5904020500":MBEDTLS_ERR_PK_KEY_INVALID_FORMAT
+
+Key ASN1 (OneAsymmetricKey X25519, with unsupported attributes)
+depends_on:MBEDTLS_ECP_LIGHT
+pk_parse_key:"304f020100300506032b656e04220420b06d829655543a51cba36e53522bc0acfd60af59466555fb3e1e796872ab1a59a01f301d060a2a864886f70d01090914310f0c0d437572646c6520436861697273":MBEDTLS_ERR_PK_KEY_INVALID_FORMAT
+
+Key ASN1 (OneAsymmetricKey X25519, unsupported version 2 with public key)
+depends_on:MBEDTLS_ECP_LIGHT
+pk_parse_key:"3051020101300506032b656e04220420b06d829655543a51cba36e53522bc0acfd60af59466555fb3e1e796872ab1a598121009bc3b0e93d8233fe6a8ba6138948cc12a91362d5c2ed81584db05ab5419c9d11":MBEDTLS_ERR_PK_KEY_INVALID_FORMAT
+
+Key ASN1 (OneAsymmetricKey X25519, unsupported version 2 with public key and unsupported attributes)
+depends_on:MBEDTLS_ECP_LIGHT
+pk_parse_key:"3072020101300506032b656e04220420b06d829655543a51cba36e53522bc0acfd60af59466555fb3e1e796872ab1a59a01f301d060a2a864886f70d01090914310f0c0d437572646c65204368616972738121009bc3b0e93d8233fe6a8ba6138948cc12a91362d5c2ed81584db05ab5419c9d11":MBEDTLS_ERR_PK_KEY_INVALID_FORMAT
diff --git a/tests/suites/test_suite_pkwrite.data b/tests/suites/test_suite_pkwrite.data
index 34bf0da..33f1b06 100644
--- a/tests/suites/test_suite_pkwrite.data
+++ b/tests/suites/test_suite_pkwrite.data
@@ -38,6 +38,14 @@
 depends_on:MBEDTLS_ECP_LIGHT:MBEDTLS_ECP_DP_BP512R1_ENABLED
 pk_write_pubkey_check:"data_files/ec_bp512_pub.der":TEST_DER
 
+Public key write check EC X25519
+depends_on:MBEDTLS_ECP_LIGHT:MBEDTLS_BASE64_C:MBEDTLS_ECP_DP_CURVE25519_ENABLED
+pk_write_pubkey_check:"data_files/ec_x25519_pub.pem":TEST_PEM
+
+Public key write check EC X448
+depends_on:MBEDTLS_ECP_LIGHT:MBEDTLS_BASE64_C:MBEDTLS_ECP_DP_CURVE448_ENABLED
+pk_write_pubkey_check:"data_files/ec_x448_pub.pem":TEST_PEM
+
 Private key write check RSA
 depends_on:MBEDTLS_RSA_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_PEM_WRITE_C
 pk_write_key_check:"data_files/server1.key":TEST_PEM
@@ -113,3 +121,11 @@
 Derive public key EC Brainpool 512 bits
 depends_on:MBEDTLS_ECP_LIGHT:MBEDTLS_ECP_DP_BP512R1_ENABLED
 pk_write_public_from_private:"data_files/ec_bp512_prv.der":"data_files/ec_bp512_pub.der"
+
+Private key write check EC X25519
+depends_on:MBEDTLS_ECP_LIGHT:MBEDTLS_BASE64_C:MBEDTLS_ECP_DP_CURVE25519_ENABLED
+pk_write_key_check:"data_files/ec_x25519_prv.pem":TEST_PEM
+
+Private key write check EC X448
+depends_on:MBEDTLS_ECP_LIGHT:MBEDTLS_BASE64_C:MBEDTLS_ECP_DP_CURVE448_ENABLED
+pk_write_key_check:"data_files/ec_x448_prv.pem":TEST_PEM