Add TLV iterator API
This introduces an API which allows for iteration over an image's TLVs
without resorting to low-level implementation details. All previous TLV
low-level handling was updated to comply with this new interface, and it
also makes it easier for external code to handle TLVs.
The API provides two functions:
1) To start a new iterator:
```
int bootutil_tlv_iter_begin(struct image_tlv_iter *it,
const struct image_header *hdr,
const struct flash_area *fap, uint8_t type,
bool prot);
```
2) To iterate over existing TLVs of given type:
```
int bootutil_tlv_iter_next(struct image_tlv_iter *it, uint32_t *off,
uint16_t *len, uint8_t *type);
```
A type of IMAGE_TLV_ANY was added to allow for iteration over all TLVs.
Low-level TLV access functions were removed from API, but low-level
structs are still visible in the namespace.
Signed-off-by: Fabio Utzig <utzig@apache.org>
diff --git a/boot/bootutil/include/bootutil/image.h b/boot/bootutil/include/bootutil/image.h
index 88874ab..9cb7508 100644
--- a/boot/bootutil/include/bootutil/image.h
+++ b/boot/bootutil/include/bootutil/image.h
@@ -82,6 +82,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_DEPENDENCY 0x40 /* Image depends on other image */
+#define IMAGE_TLV_ANY 0xff /* Used to iterate over all TLV */
struct image_version {
uint8_t iv_major;
@@ -144,6 +145,23 @@
uint8_t *tmp_buf, uint32_t tmp_buf_sz,
uint8_t *seed, int seed_len, uint8_t *out_hash);
+struct image_tlv_iter {
+ const struct image_header *hdr;
+ const struct flash_area *fap;
+ uint8_t type;
+ bool prot;
+ uint16_t prot_len;
+ uint32_t tlv_off;
+ uint32_t tlv_end;
+};
+
+int bootutil_tlv_iter_begin(struct image_tlv_iter *it,
+ const struct image_header *hdr,
+ const struct flash_area *fap, uint8_t type,
+ bool prot);
+int bootutil_tlv_iter_next(struct image_tlv_iter *it, uint32_t *off,
+ uint16_t *len, uint8_t *type);
+
#ifdef __cplusplus
}
#endif
diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h
index 51d27a9..eb839a6 100644
--- a/boot/bootutil/src/bootutil_priv.h
+++ b/boot/bootutil/src/bootutil_priv.h
@@ -251,9 +251,6 @@
const uint8_t *enckey);
int boot_read_enc_key(int image_index, uint8_t slot, uint8_t *enckey);
#endif
-int boot_find_tlv_offs(const struct image_header *hdr,
- const struct flash_area *fap,
- uint32_t *off, uint32_t *end);
/*
* Accessors for the contents of struct boot_loader_state.
diff --git a/boot/bootutil/src/encrypted.c b/boot/bootutil/src/encrypted.c
index b4c5727..032b46b 100644
--- a/boot/bootutil/src/encrypted.c
+++ b/boot/bootutil/src/encrypted.c
@@ -223,11 +223,10 @@
size_t olen;
#endif
uint32_t off;
- uint32_t end;
- struct image_tlv tlv;
+ uint16_t len;
+ struct image_tlv_iter it;
uint8_t buf[TLV_ENC_RSA_SZ];
uint8_t slot;
- uint8_t enckey_type;
int rc;
rc = flash_area_id_to_multi_image_slot(image_index, fap->fa_id);
@@ -241,34 +240,23 @@
return 1;
}
- rc = boot_find_tlv_offs(hdr, fap, &off, &end);
+ rc = bootutil_tlv_iter_begin(&it, hdr, fap, EXPECTED_ENC_TLV, false);
if (rc) {
return -1;
}
- for (enckey_type = 0; off < end; off += sizeof(tlv) + tlv.it_len) {
- rc = flash_area_read(fap, off, &tlv, sizeof tlv);
- if (rc) {
- return rc;
- }
-
- if (tlv.it_type == EXPECTED_ENC_TLV) {
- if (tlv.it_len != EXPECTED_ENC_LEN) {
- return -1;
- }
- rc = flash_area_read(fap, off + sizeof(tlv), buf, EXPECTED_ENC_LEN);
- if (rc) {
- return -1;
- }
- enckey_type = EXPECTED_ENC_TLV;
- break;
- }
+ rc = bootutil_tlv_iter_next(&it, &off, &len, NULL);
+ if (rc != 0) {
+ return rc;
}
- if (enckey_type == 0) {
+ if (len != EXPECTED_ENC_LEN) {
return -1;
- } else if (enckey_type != EXPECTED_ENC_TLV) {
- return 0;
+ }
+
+ rc = flash_area_read(fap, off, buf, EXPECTED_ENC_LEN);
+ if (rc) {
+ return -1;
}
#if defined(MCUBOOT_ENCRYPT_RSA)
diff --git a/boot/bootutil/src/image_validate.c b/boot/bootutil/src/image_validate.c
index 171ad66..1381808 100644
--- a/boot/bootutil/src/image_validate.c
+++ b/boot/bootutil/src/image_validate.c
@@ -222,13 +222,14 @@
int seed_len, uint8_t *out_hash)
{
uint32_t off;
- uint32_t end;
+ uint16_t len;
+ uint8_t type;
int sha256_valid = 0;
#ifdef EXPECTED_SIG_TLV
int valid_signature = 0;
int key_id = -1;
#endif
- struct image_tlv tlv;
+ struct image_tlv_iter it;
uint8_t buf[SIG_BUF_SIZE];
uint8_t hash[32];
int rc;
@@ -243,7 +244,7 @@
memcpy(out_hash, hash, 32);
}
- rc = boot_find_tlv_offs(hdr, fap, &off, &end);
+ rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false);
if (rc) {
return rc;
}
@@ -252,21 +253,23 @@
* Traverse through all of the TLVs, performing any checks we know
* and are able to do.
*/
- for (; off < end; off += sizeof(tlv) + tlv.it_len) {
- rc = flash_area_read(fap, off, &tlv, sizeof tlv);
- if (rc) {
- return rc;
+ while (true) {
+ rc = bootutil_tlv_iter_next(&it, &off, &len, &type);
+ if (rc < 0) {
+ return -1;
+ } else if (rc > 0) {
+ break;
}
- if (tlv.it_type == IMAGE_TLV_SHA256) {
+ if (type == IMAGE_TLV_SHA256) {
/*
* Verify the SHA256 image hash. This must always be
* present.
*/
- if (tlv.it_len != sizeof(hash)) {
+ if (len != sizeof(hash)) {
return -1;
}
- rc = flash_area_read(fap, off + sizeof(tlv), buf, sizeof hash);
+ rc = flash_area_read(fap, off, buf, sizeof hash);
if (rc) {
return rc;
}
@@ -276,36 +279,36 @@
sha256_valid = 1;
#ifdef EXPECTED_SIG_TLV
- } else if (tlv.it_type == IMAGE_TLV_KEYHASH) {
+ } else if (type == IMAGE_TLV_KEYHASH) {
/*
* Determine which key we should be checking.
*/
- if (tlv.it_len > 32) {
+ if (len > 32) {
return -1;
}
- rc = flash_area_read(fap, off + sizeof tlv, buf, tlv.it_len);
+ rc = flash_area_read(fap, off, buf, len);
if (rc) {
return rc;
}
- key_id = bootutil_find_key(buf, tlv.it_len);
+ 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 if (tlv.it_type == 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) {
key_id = -1;
continue;
}
- if (!EXPECTED_SIG_LEN(tlv.it_len) || tlv.it_len > sizeof(buf)) {
+ if (!EXPECTED_SIG_LEN(len) || len > sizeof(buf)) {
return -1;
}
- rc = flash_area_read(fap, off + sizeof(tlv), buf, tlv.it_len);
+ rc = flash_area_read(fap, off, buf, len);
if (rc) {
return -1;
}
- rc = bootutil_verify_sig(hash, sizeof(hash), buf, tlv.it_len, key_id);
+ rc = bootutil_verify_sig(hash, sizeof(hash), buf, len, key_id);
if (rc == 0) {
valid_signature = 1;
}
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index 1634312..045be52 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -243,38 +243,6 @@
}
/*
- * Locate the TLVs in an image.
- *
- * @param hdr The image_header struct of the image being checked
- * @param fap flash_area struct of the slot storing the image being checked
- * @param off Address of the first TLV (after TLV info)
- * @param end Address where TLV area ends
- *
- * Returns 0 on success.
- */
-int
-boot_find_tlv_offs(const struct image_header *hdr, const struct flash_area *fap,
- uint32_t *off, uint32_t *end)
-{
- struct image_tlv_info info;
- uint32_t off_;
-
- off_ = BOOT_TLV_OFF(hdr);
-
- if (flash_area_read(fap, off_, &info, sizeof(info))) {
- return BOOT_EFLASH;
- }
-
- if (info.it_magic != IMAGE_TLV_INFO_MAGIC) {
- return BOOT_EBADIMAGE;
- }
-
- *end = off_ + info.it_tlv_tot;
- *off = off_ + sizeof(info);
- return 0;
-}
-
-/*
* Compute the total size of the given image. Includes the size of
* the TLVs.
*/
@@ -283,6 +251,7 @@
boot_read_image_size(struct boot_loader_state *state, int slot, uint32_t *size)
{
const struct flash_area *fap;
+ struct image_tlv_info info;
uint32_t off;
int area_id;
int rc;
@@ -298,10 +267,19 @@
goto done;
}
- rc = boot_find_tlv_offs(boot_img_hdr(state, slot), fap, &off, size);
- if (rc != 0) {
+ off = BOOT_TLV_OFF(boot_img_hdr(state, slot));
+
+ if (flash_area_read(fap, off, &info, sizeof(info))) {
+ rc = BOOT_EFLASH;
goto done;
}
+
+ if (info.it_magic != IMAGE_TLV_INFO_MAGIC) {
+ rc = BOOT_EBADIMAGE;
+ goto done;
+ }
+
+ *size = off + info.it_tlv_tot;
rc = 0;
done:
@@ -1884,11 +1862,10 @@
boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t slot)
{
const struct flash_area *fap;
- struct image_tlv tlv;
+ struct image_tlv_iter it;
struct image_dependency dep;
uint32_t off;
- uint32_t end;
- bool dep_tlvs_found = false;
+ uint16_t len;
int area_id;
int rc;
@@ -1899,56 +1876,43 @@
goto done;
}
- rc = boot_find_tlv_offs(boot_img_hdr(state, slot), fap, &off, &end);
+ rc = bootutil_tlv_iter_begin(&it, boot_img_hdr(state, slot), fap,
+ IMAGE_TLV_DEPENDENCY, true);
if (rc != 0) {
goto done;
}
- /* Traverse through all of the TLVs to find the dependency TLVs. */
- for (; off < end; off += sizeof(tlv) + tlv.it_len) {
- rc = flash_area_read(fap, off, &tlv, sizeof(tlv));
- if (rc != 0) {
- rc = BOOT_EFLASH;
- goto done;
- }
-
- if (tlv.it_type == IMAGE_TLV_DEPENDENCY) {
- dep_tlvs_found = true;
-
- if (tlv.it_len != sizeof(dep)) {
- rc = BOOT_EBADIMAGE;
- goto done;
- }
-
- rc = flash_area_read(fap, off + sizeof(tlv), &dep, tlv.it_len);
- if (rc != 0) {
- rc = BOOT_EFLASH;
- goto done;
- }
-
- if (dep.image_id >= BOOT_IMAGE_NUMBER) {
- rc = BOOT_EBADARGS;
- goto done;
- }
-
- /* Verify dependency and modify the swap type if not satisfied. */
- rc = boot_verify_slot_dependency(state, &dep);
- if (rc != 0) {
- /* Dependency not satisfied. */
- goto done;
- }
-
- /* Dependency satisfied, no action needed.
- * Continue with the next TLV entry.
- */
- } else if (dep_tlvs_found) {
- /* The dependency TLVs are contiguous in the TLV area. If a
- * dependency had already been found and the last read TLV
- * has a different type then there are no more dependency TLVs.
- * The search can be finished.
- */
+ while (true) {
+ rc = bootutil_tlv_iter_next(&it, &off, &len, NULL);
+ if (rc < 0) {
+ return -1;
+ } else if (rc > 0) {
+ rc = 0;
break;
}
+
+ if (len != sizeof(dep)) {
+ rc = BOOT_EBADIMAGE;
+ goto done;
+ }
+
+ rc = flash_area_read(fap, off, &dep, len);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ if (dep.image_id >= BOOT_IMAGE_NUMBER) {
+ rc = BOOT_EBADARGS;
+ goto done;
+ }
+
+ /* Verify dependency and modify the swap type if not satisfied. */
+ rc = boot_verify_slot_dependency(state, &dep);
+ if (rc != 0) {
+ /* Dependency not satisfied. */
+ goto done;
+ }
}
done:
diff --git a/boot/bootutil/src/tlv.c b/boot/bootutil/src/tlv.c
new file mode 100644
index 0000000..aaebee3
--- /dev/null
+++ b/boot/bootutil/src/tlv.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2019 JUUL Labs
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+
+#include "bootutil/bootutil.h"
+#include "bootutil/image.h"
+#include "bootutil_priv.h"
+
+/*
+ * Initialize a TLV iterator.
+ *
+ * @param it An iterator struct
+ * @param hdr image_header of the slot's image
+ * @param fap flash_area of the slot which is storing the image
+ * @param type Type of TLV to look for
+ * @param prot true if TLV has to be stored in the protected area, false otherwise
+ *
+ * @returns 0 if the TLV iterator was succesfully started
+ * -1 on errors
+ */
+int
+bootutil_tlv_iter_begin(struct image_tlv_iter *it, const struct image_header *hdr,
+ const struct flash_area *fap, uint8_t type, bool prot)
+{
+ uint32_t off_;
+ struct image_tlv_info info;
+
+ if (it == NULL || hdr == NULL || fap == NULL) {
+ return -1;
+ }
+
+ off_ = BOOT_TLV_OFF(hdr);
+ if (flash_area_read(fap, off_, &info, sizeof(info))) {
+ return -1;
+ }
+
+ if (info.it_magic != IMAGE_TLV_INFO_MAGIC) {
+ return -1;
+ }
+
+ it->hdr = hdr;
+ it->fap = fap;
+ it->type = type;
+ it->prot = prot;
+ off_ += sizeof(info);
+ it->tlv_off = off_;
+ it->prot_len = off_ + it->hdr->ih_protect_tlv_size;
+ it->tlv_end = off_ + info.it_tlv_tot;
+ return 0;
+}
+
+/*
+ * Find next TLV
+ *
+ * @param it The image TLV iterator struct
+ * @param off The offset of the TLV's payload in flash
+ * @param len The length of the TLV's payload
+ * @param type If not NULL returns the type of TLV found
+ *
+ * @returns 0 if a TLV with with matching type was found
+ * 1 if no more TLVs with matching type are available
+ * -1 on errors
+ */
+int
+bootutil_tlv_iter_next(struct image_tlv_iter *it, uint32_t *off, uint16_t *len,
+ uint8_t *type)
+{
+ struct image_tlv tlv;
+ int rc;
+
+ if (it == NULL || it->hdr == NULL || it->fap == NULL) {
+ return -1;
+ }
+
+ while (it->tlv_off < it->tlv_end) {
+ rc = flash_area_read(it->fap, it->tlv_off, &tlv, sizeof tlv);
+ if (rc) {
+ return -1;
+ }
+
+ /* No more TLVs in the protected area */
+ if (it->prot && it->tlv_off >= it->prot_len) {
+ return 1;
+ }
+
+ if (it->type == IMAGE_TLV_ANY || tlv.it_type == it->type) {
+ if (type != NULL) {
+ *type = tlv.it_type;
+ }
+ *off = it->tlv_off + sizeof(tlv);
+ *len = tlv.it_len;
+ it->tlv_off += sizeof(tlv) + tlv.it_len;
+ return 0;
+ }
+
+ it->tlv_off += sizeof(tlv) + tlv.it_len;
+ }
+
+ return 1;
+}
diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt
index 55ba173..ded117d 100644
--- a/boot/zephyr/CMakeLists.txt
+++ b/boot/zephyr/CMakeLists.txt
@@ -110,6 +110,7 @@
${BOOT_DIR}/bootutil/src/image_ec256.c
${BOOT_DIR}/bootutil/src/image_ed25519.c
${BOOT_DIR}/bootutil/src/caps.c
+ ${BOOT_DIR}/bootutil/src/tlv.c
)
if(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256)
diff --git a/sim/mcuboot-sys/build.rs b/sim/mcuboot-sys/build.rs
index 3e419bd..922219f 100644
--- a/sim/mcuboot-sys/build.rs
+++ b/sim/mcuboot-sys/build.rs
@@ -190,6 +190,7 @@
conf.file("../../boot/bootutil/src/loader.c");
conf.file("../../boot/bootutil/src/caps.c");
conf.file("../../boot/bootutil/src/bootutil_misc.c");
+ conf.file("../../boot/bootutil/src/tlv.c");
conf.file("csupport/run.c");
conf.include("../../boot/bootutil/include");
conf.include("csupport");