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");