bootutil: Allow the usage of builtin keys in verification

Introduce a new MCUBOOT_BUILTIN_KEY option to enable the usage of
builtin keys for signature verification. This way the details of the key
handling mechanism are abstracted away from the boot code and this
responsibility is delegated to the given crypto library.
This is an alternative option to the existing MCUBOOT_HW_KEY feature,
however in this case we can entirely rely on key IDs and not only the
code, but also the image metadata does not contain any public key data.

Change-Id: Id01b67951310549b2734730c58bfa7210a2d5236
Signed-off-by: David Vincze <david.vincze@arm.com>
diff --git a/boot/bootutil/src/image_validate.c b/boot/bootutil/src/image_validate.c
index d045a3e..054e8e4 100644
--- a/boot/bootutil/src/image_validate.c
+++ b/boot/bootutil/src/image_validate.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2017-2019 Linaro LTD
  * Copyright (c) 2016-2019 JUUL Labs
- * Copyright (c) 2019-2023 Arm Limited
+ * Copyright (c) 2019-2024 Arm Limited
  *
  * Original license:
  *
@@ -201,7 +201,27 @@
 #    define SIG_BUF_SIZE 32 /* no signing, sha256 digest only */
 #endif
 
+#if (defined(MCUBOOT_HW_KEY)       + \
+     defined(MCUBOOT_BUILTIN_KEY)) > 1
+#error "Please use either MCUBOOT_HW_KEY or the MCUBOOT_BUILTIN_KEY feature."
+#endif
+
 #ifdef EXPECTED_SIG_TLV
+
+#if !defined(MCUBOOT_BUILTIN_KEY)
+#if !defined(MCUBOOT_HW_KEY)
+/* The key TLV contains the hash of the public key. */
+#   define EXPECTED_KEY_TLV     IMAGE_TLV_KEYHASH
+#   define KEY_BUF_SIZE         IMAGE_HASH_SIZE
+#else
+/* The key TLV contains the whole public key.
+ * Add a few extra bytes to the key buffer size for encoding and
+ * for public exponent.
+ */
+#   define EXPECTED_KEY_TLV     IMAGE_TLV_PUBKEY
+#   define KEY_BUF_SIZE         (SIG_BUF_SIZE + 24)
+#endif /* !MCUBOOT_HW_KEY */
+
 #if !defined(MCUBOOT_HW_KEY)
 static int
 bootutil_find_key(uint8_t *keyhash, uint8_t keyhash_len)
@@ -228,7 +248,7 @@
     bootutil_sha_drop(&sha_ctx);
     return -1;
 }
-#else
+#else /* !MCUBOOT_HW_KEY */
 extern unsigned int pub_key_len;
 static int
 bootutil_find_key(uint8_t image_index, uint8_t *key, uint16_t key_len)
@@ -266,7 +286,8 @@
     return -1;
 }
 #endif /* !MCUBOOT_HW_KEY */
-#endif
+#endif /* !MCUBOOT_BUILTIN_KEY */
+#endif /* EXPECTED_SIG_TLV */
 
 /**
  * Reads the value of an image's security counter.
@@ -344,10 +365,16 @@
     int image_hash_valid = 0;
 #ifdef EXPECTED_SIG_TLV
     FIH_DECLARE(valid_signature, FIH_FAILURE);
+#ifndef MCUBOOT_BUILTIN_KEY
     int key_id = -1;
+#else
+    /* Pass a key ID equal to the image index, the underlying crypto library
+     * is responsible for mapping the image index to a builtin key ID.
+     */
+    int key_id = image_index;
+#endif /* !MCUBOOT_BUILTIN_KEY */
 #ifdef MCUBOOT_HW_KEY
-    /* Few extra bytes for encoding and for public exponent. */
-    uint8_t key_buf[SIG_BUF_SIZE + 24];
+    uint8_t key_buf[KEY_BUF_SIZE];
 #endif
 #endif /* EXPECTED_SIG_TLV */
     struct image_tlv_iter it;
@@ -411,44 +438,34 @@
             }
 
             image_hash_valid = 1;
-#ifdef EXPECTED_SIG_TLV
-#ifndef MCUBOOT_HW_KEY
-        } else if (type == IMAGE_TLV_KEYHASH) {
+#ifdef EXPECTED_KEY_TLV
+        } else if (type == EXPECTED_KEY_TLV) {
             /*
              * Determine which key we should be checking.
              */
-            if (len > IMAGE_HASH_SIZE) {
+            if (len > KEY_BUF_SIZE) {
                 rc = -1;
                 goto out;
             }
+#ifndef MCUBOOT_HW_KEY
             rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, len);
             if (rc) {
                 goto out;
             }
             key_id = bootutil_find_key(buf, len);
-            /*
-             * The key may not be found, which is acceptable.  There
-             * can be multiple signatures, each preceded by a key.
-             */
 #else
-        } else if (type == IMAGE_TLV_PUBKEY) {
-            /*
-             * Determine which key we should be checking.
-             */
-            if (len > sizeof(key_buf)) {
-                rc = -1;
-                goto out;
-            }
             rc = LOAD_IMAGE_DATA(hdr, fap, off, key_buf, len);
             if (rc) {
                 goto out;
             }
             key_id = bootutil_find_key(image_index, key_buf, len);
+#endif /* !MCUBOOT_HW_KEY */
             /*
              * The key may not be found, which is acceptable.  There
              * can be multiple signatures, each preceded by a key.
              */
-#endif /* !MCUBOOT_HW_KEY */
+#endif /* EXPECTED_KEY_TLV */
+#ifdef EXPECTED_SIG_TLV
         } else if (type == EXPECTED_SIG_TLV) {
             /* Ignore this signature if it is out of bounds. */
             if (key_id < 0 || key_id >= bootutil_key_cnt) {
diff --git a/docs/design.md b/docs/design.md
index 27262bd..8539ae0 100755
--- a/docs/design.md
+++ b/docs/design.md
@@ -3,7 +3,7 @@
 
   - Copyright (c) 2017-2020 Linaro LTD
   - Copyright (c) 2017-2019 JUUL Labs
-  - Copyright (c) 2019-2023 Arm Limited
+  - Copyright (c) 2019-2024 Arm Limited
 
   - Original license:
 
@@ -1182,8 +1182,15 @@
 
 By default, the whole public key is embedded in the bootloader code and its
 hash is added to the image manifest as a KEYHASH TLV entry. As an alternative
-the bootloader can be made independent of the keys by setting the
-`MCUBOOT_HW_KEY` option. In this case the hash of the public key must be
+the bootloader can be made independent of the keys (avoiding the incorporation
+of the public key into the code) by using one of the following options:
+`MCUBOOT_HW_KEY` or `MCUBOOT_BUILTIN_KEY`.
+
+Using any of these options makes MCUboot independent from the public key(s).
+The key(s) can be provisioned any time and by different parties.
+
+Hardware KEYs support options details:
+- `MCUBOOT_HW_KEY`: In this case the hash of the public key must be
 provisioned to the target device and MCUboot must be able to retrieve the
 key-hash from there. For this reason the target must provide a definition
 for the `boot_retrieve_public_key_hash()` function which is declared in
@@ -1193,8 +1200,17 @@
 hash (KEYHASH TLV). During boot the public key is validated before using it for
 signature verification, MCUboot calculates the hash of the public key from the
 TLV area and compares it with the key-hash that was retrieved from the device.
-This way MCUboot is independent from the public key(s). The key(s) can be
-provisioned any time and by different parties.
+- `MCUBOOT_BUILTIN_KEY`: With this option the whole public key(s) used for
+signature verification must be provisioned to the target device and the used
+[cryptographic library](PORTING.md) must support the usage of builtin keys based
+on key IDs. In this case, neither the code nor the image metadata needs to
+contain any public key data. During image validation only a key ID is passed to
+the verifier function. The key handling is entirely the responsibility of the
+crypto library and the details of the key handling mechanism are abstracted away
+from the boot code.\
+***Note:*** *At the moment the usage of builtin keys is only available with the*
+*PSA Crypto API based crypto backend (`MCUBOOT_USE_PSA_CRYPTO`) for ECDSA*
+*signatures.*
 
 ## [Protected TLVs](#protected-tlvs)
 
diff --git a/samples/mcuboot_config/mcuboot_config.template.h b/samples/mcuboot_config/mcuboot_config.template.h
index 989ec61..24e65f1 100644
--- a/samples/mcuboot_config/mcuboot_config.template.h
+++ b/samples/mcuboot_config/mcuboot_config.template.h
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2018 Open Source Foundries Limited
- * Copyright (c) 2019 Arm Limited
+ * Copyright (c) 2019-2024 Arm Limited
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -33,6 +33,18 @@
 /* Uncomment for ECDSA signatures using curve P-256. */
 /* #define MCUBOOT_SIGN_EC256 */
 
+/*
+ * Public key handling
+ *
+ * Choose one or none from the different public key handling options.
+ */
+
+/* Uncomment to use key hash(es) instead of incorporating
+ * the public key into the code. */
+/* #define MCUBOOT_HW_KEY */
+/* Uncomment to use builtin key(s) instead of incorporating
+ * the public key into the code. */
+/* #define MCUBOOT_BUILTIN_KEY */
 
 /*
  * Upgrade mode
@@ -149,7 +161,7 @@
 /* If a OS ports support single thread mode or is bare-metal then:
  * This macro implements call that switches CPU to an idle state, from which
  * the CPU may be woken up by, for example, UART transmission event.
- * 
+ *
  * Otherwise this macro should be no-op.
  */
 #define MCUBOOT_CPU_IDLE() \