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/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;