boot: bootutil: add x25519 encrypted image support

- Define new TLV for carrying information for x25519 based encrypted
  images.
- Add routines to parse embedded encryption key, generated shared
  secret and image decryption key.

Signed-off-by: Fabio Utzig <utzig@apache.org>
diff --git a/boot/bootutil/include/bootutil/enc_key.h b/boot/bootutil/include/bootutil/enc_key.h
index d6dd976..866dcf7 100644
--- a/boot/bootutil/include/bootutil/enc_key.h
+++ b/boot/bootutil/include/bootutil/enc_key.h
@@ -42,11 +42,14 @@
 #define TLV_ENC_RSA_SZ    256
 #define TLV_ENC_KW_SZ     24
 #define TLV_ENC_EC256_SZ  (65 + 32 + 16)
+#define TLV_ENC_X25519_SZ (32 + 32 + 16)
 
 #if defined(MCUBOOT_ENCRYPT_RSA)
 #define BOOT_ENC_TLV_SIZE TLV_ENC_RSA_SZ
 #elif defined(MCUBOOT_ENCRYPT_EC256)
 #define BOOT_ENC_TLV_SIZE TLV_ENC_EC256_SZ
+#elif defined(MCUBOOT_ENCRYPT_X25519)
+#define BOOT_ENC_TLV_SIZE TLV_ENC_X25519_SZ
 #else
 #define BOOT_ENC_TLV_SIZE TLV_ENC_KW_SZ
 #endif
diff --git a/boot/bootutil/include/bootutil/image.h b/boot/bootutil/include/bootutil/image.h
index a73dbf6..db64812 100644
--- a/boot/bootutil/include/bootutil/image.h
+++ b/boot/bootutil/include/bootutil/image.h
@@ -84,6 +84,7 @@
 #define IMAGE_TLV_ENC_RSA2048       0x30   /* Key encrypted with RSA-OAEP-2048 */
 #define IMAGE_TLV_ENC_KW128         0x31   /* Key encrypted with AES-KW-128 */
 #define IMAGE_TLV_ENC_EC256         0x32   /* Key encrypted with ECIES-EC256 */
+#define IMAGE_TLV_ENC_X25519        0x33   /* Key encrypted with ECIES-X25519 */
 #define IMAGE_TLV_DEPENDENCY        0x40   /* Image depends on other image */
 #define IMAGE_TLV_SEC_CNT           0x50   /* security counter */
 #define IMAGE_TLV_BOOT_RECORD       0x60   /* measured boot record */
diff --git a/boot/bootutil/src/encrypted.c b/boot/bootutil/src/encrypted.c
index 951c800..abc9fdd 100644
--- a/boot/bootutil/src/encrypted.c
+++ b/boot/bootutil/src/encrypted.c
@@ -44,10 +44,13 @@
 #endif
 
 #if defined(MCUBOOT_ENCRYPT_EC256)
-#include "tinycrypt/utils.h"
-#include "tinycrypt/constants.h"
 #include "tinycrypt/ecc.h"
 #include "tinycrypt/ecc_dh.h"
+#endif
+
+#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519)
+#include "tinycrypt/utils.h"
+#include "tinycrypt/constants.h"
 #include "tinycrypt/ctr_mode.h"
 #include "tinycrypt/hmac.h"
 #include "mbedtls/oid.h"
@@ -203,6 +206,9 @@
 static const uint8_t ec_pubkey_oid[] = MBEDTLS_OID_EC_ALG_UNRESTRICTED;
 static const uint8_t ec_secp256r1_oid[] = MBEDTLS_OID_EC_GRP_SECP256R1;
 
+#define SHARED_KEY_LEN NUM_ECC_BYTES
+#define PRIV_KEY_LEN   NUM_ECC_BYTES
+
 /*
  * Parses the output of `imgtool keygen`, which produces a PKCS#8 elliptic
  * curve keypair. See RFC5208 and RFC5915.
@@ -275,7 +281,68 @@
 
     return 0;
 }
+#endif /* defined(MCUBOOT_ENCRYPT_EC256) */
 
+#if defined(MCUBOOT_ENCRYPT_X25519)
+#define X25519_OID "\x6e"
+static const uint8_t ec_pubkey_oid[] = MBEDTLS_OID_ISO_IDENTIFIED_ORG \
+                                       MBEDTLS_OID_ORG_GOV X25519_OID;
+
+extern int X25519(uint8_t out_shared_key[32], const uint8_t private_key[32],
+                  const uint8_t peer_public_value[32]);
+
+#define SHARED_KEY_LEN 32
+#define PRIV_KEY_LEN   32
+
+static int
+parse_x25519_enckey(uint8_t **p, uint8_t *end, uint8_t *pk)
+{
+    size_t len;
+    int version;
+    mbedtls_asn1_buf alg;
+    mbedtls_asn1_buf param;
+
+    if (mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
+                                           MBEDTLS_ASN1_SEQUENCE) != 0) {
+        return -1;
+    }
+
+    if (*p + len != end) {
+        return -2;
+    }
+
+    version = 0;
+    if (mbedtls_asn1_get_int(p, end, &version) || version != 0) {
+        return -3;
+    }
+
+    if (mbedtls_asn1_get_alg(p, end, &alg, &param) != 0) {
+        return -4;
+    }
+
+    if (alg.len != sizeof(ec_pubkey_oid) - 1 ||
+        memcmp(alg.p, ec_pubkey_oid, sizeof(ec_pubkey_oid) - 1)) {
+        return -5;
+    }
+
+    if (mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING) != 0) {
+        return -6;
+    }
+
+    if (mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING) != 0) {
+        return -7;
+    }
+
+    if (len != PRIV_KEY_LEN) {
+        return -8;
+    }
+
+    memcpy(pk, *p, PRIV_KEY_LEN);
+    return 0;
+}
+#endif /* defined(MCUBOOT_ENCRYPT_X25519) */
+
+#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519)
 /*
  * HKDF as described by RFC5869.
  *
@@ -423,6 +490,13 @@
 #    define EC_CIPHERKEY_INDEX  (65 + 32)
 _Static_assert(EC_CIPHERKEY_INDEX + 16 == EXPECTED_ENC_LEN,
         "Please fix ECIES-P256 component indexes");
+#elif defined(MCUBOOT_ENCRYPT_X25519)
+#    define EXPECTED_ENC_TLV    IMAGE_TLV_ENC_X25519
+#    define EC_PUBK_INDEX       (0)
+#    define EC_TAG_INDEX        (32)
+#    define EC_CIPHERKEY_INDEX  (32 + 32)
+_Static_assert(EC_CIPHERKEY_INDEX + 16 == EXPECTED_ENC_LEN,
+        "Please fix ECIES-X25519 component indexes");
 #endif
 
 /*
@@ -440,15 +514,15 @@
     uint8_t *cpend;
     size_t olen;
 #endif
-#if defined(MCUBOOT_ENCRYPT_EC256)
+#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519)
     struct tc_hmac_state_struct hmac;
     struct tc_aes_key_sched_struct aes;
     uint8_t tag[TC_SHA256_DIGEST_SIZE];
-    uint8_t shared[NUM_ECC_BYTES];
+    uint8_t shared[SHARED_KEY_LEN];
     uint8_t derived_key[TC_AES_KEY_SIZE + TC_SHA256_DIGEST_SIZE];
     uint8_t *cp;
     uint8_t *cpend;
-    uint8_t pk[NUM_ECC_BYTES];
+    uint8_t pk[PRIV_KEY_LEN];
     uint8_t counter[TC_AES_BLOCK_SIZE];
     uint16_t len;
 #endif
@@ -471,12 +545,16 @@
             NULL, 0, &olen, buf, enckey, BOOT_ENC_KEY_SIZE);
     mbedtls_rsa_free(&rsa);
 
-#elif defined(MCUBOOT_ENCRYPT_KW)
+#endif /* defined(MCUBOOT_ENCRYPT_RSA) */
+
+#if defined(MCUBOOT_ENCRYPT_KW)
 
     assert(*bootutil_enc_key.len == 16);
     rc = key_unwrap(buf, enckey);
 
-#elif defined(MCUBOOT_ENCRYPT_EC256)
+#endif /* defined(MCUBOOT_ENCRYPT_KW) */
+
+#if defined(MCUBOOT_ENCRYPT_EC256)
 
     cp = (uint8_t *)bootutil_enc_key.key;
     cpend = cp + *bootutil_enc_key.len;
@@ -508,12 +586,41 @@
         return -1;
     }
 
+#endif /* defined(MCUBOOT_ENCRYPT_EC256) */
+
+#if defined(MCUBOOT_ENCRYPT_X25519)
+
+    cp = (uint8_t *)bootutil_enc_key.key;
+    cpend = cp + *bootutil_enc_key.len;
+
+    /*
+     * Load the stored X25519 decryption private key
+     */
+
+    rc = parse_x25519_enckey(&cp, cpend, pk);
+    if (rc) {
+        return rc;
+    }
+
+    /*
+     * First "element" in the TLV is the curve point (public key)
+     */
+
+    rc = X25519(shared, pk, &buf[EC_PUBK_INDEX]);
+    if (!rc) {
+        return -1;
+    }
+
+#endif /* defined(MCUBOOT_ENCRYPT_X25519) */
+
+#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519)
+
     /*
      * Expand shared secret to create keys for AES-128-CTR + HMAC-SHA256
      */
 
     len = TC_AES_KEY_SIZE + TC_SHA256_DIGEST_SIZE;
-    rc = hkdf(shared, NUM_ECC_BYTES, (uint8_t *)"MCUBoot_ECIES_v1", 16,
+    rc = hkdf(shared, SHARED_KEY_LEN, (uint8_t *)"MCUBoot_ECIES_v1", 16,
             derived_key, &len);
     if (rc != 0 || len != (TC_AES_KEY_SIZE + TC_SHA256_DIGEST_SIZE)) {
         return -1;
@@ -566,7 +673,7 @@
 
     rc = 0;
 
-#endif
+#endif /* defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519) */
 
     return rc;
 }