This closes #1.
Merge remote-tracking branch 'd3zd3z/zephyr'
diff --git a/boot/bootutil/design.txt b/boot/bootutil/design.txt
index 47c4d28..bfa12d2 100644
--- a/boot/bootutil/design.txt
+++ b/boot/bootutil/design.txt
@@ -125,6 +125,79 @@
In addition to the two image slots, the boot loader requires a scratch area to
allow for reliable image swapping.
+*** BOOT STATES
+
+Logically, you can think of a pair of values associated with each image slot:
+pending and confirmed. On startup, the boot loader determines the state of the
+device by inspecting each pair of values. These values have the following
+meanings:
+
+* pending: Indicates whether the image should be used on the next reboot; can
+ hold one of three values:
+ " " (unset): Don't use image on next boot
+ "T" (temporary): Use image on next boot; absent subsequent confirm command,
+ revert to original image on second reboot.
+ "P" (permanent): Use image on next boot and all subsequent boots
+
+* confirmed: always use image unless excluded by a test image.
+
+In English, when the user wants to run the secondary image, they set the
+pending flag for the second slot and reboot the device. On startup, the boot
+loader will swap the two images in flash, clear the secondary slot's pending
+flag, and run the newly-copied image in slot 0. If the user set the pending
+flag to "temporary," then this is only a temporary state; if the device reboots
+again, the boot loader swaps the images back to their original slots and boots
+into the original image. If the user doesn't want to revert to the original
+state, they can make the current state permanent by setting the confirmed flag
+in slot 0.
+
+Switching to an alternate image is a two-step process (set + confirm) to
+prevent a device from becoming "bricked" by bad firmware. If the device
+crashes immediately upon booting the second image, the boot loader reverts to
+the working image, rather than repeatedly rebooting into the bad image.
+
+Alternatively, if the user is confident that the second image is good, they can
+set and confirm in a single action by setting the pending flag to "permanent."
+
+The following set of tables illustrate the four possible states that the device
+can be in:
+
+ | slot-0 | slot-1 |
+ ---------------+--------+--------|
+ pending | | |
+ confirmed | X | |
+ ---------------+--------+--------'
+ Image 0 confirmed; |
+ No change on reboot |
+ ---------------------------------'
+
+ | slot-0 | slot-1 |
+ ---------------+--------+--------|
+ pending | | T |
+ confirmed | X | |
+ ---------------+--------+--------'
+ Image 0 confirmed; |
+ Test image 1 on next reboot |
+ ---------------------------------'
+
+ | slot-0 | slot-1 |
+ ---------------+--------+--------|
+ pending | | P |
+ confirmed | X | |
+ ---------------+--------+--------'
+ Image 0 confirmed; |
+ Use image 1 permanently on boot |
+ ---------------------------------'
+
+ | slot-0 | slot-1 |
+ ---------------+--------+--------|
+ pending | | |
+ confirmed | | X |
+ ---------------+--------+--------'
+ Testing image 0; |
+ Revert to image 1 on next reboot |
+ ---------------------------------'
+
*** BOOT VECTOR
At startup, the boot loader determines which of the above three states the
@@ -190,7 +263,7 @@
| slot-0 | slot-1 |
-----------------+--------+--------|
magic | Unset | Unset |
- image-ok | Any | N/A |
+ image-ok | Any | Any |
-----------------+--------+--------'
pending | | |
confirmed | X | |
@@ -203,20 +276,33 @@
| slot-0 | slot-1 |
-----------------+--------+--------|
magic | Any | Good |
- image-ok | Any | N/A |
+ image-ok | Any | Unset |
-----------------+--------+--------'
- pending | | X |
+ pending | | T |
confirmed | X | |
-----------------+--------+--------'
swap: test |
-----------------------------------'
-
+
State III
| slot-0 | slot-1 |
-----------------+--------+--------|
+ magic | Any | Good |
+ image-ok | Any | 0x01 |
+ -----------------+--------+--------'
+ pending | | P |
+ confirmed | X | |
+ -----------------+--------+--------'
+ swap: permanent |
+ -----------------------------------'
+
+
+ State IV
+ | slot-0 | slot-1 |
+ -----------------+--------+--------|
magic | Good | Unset |
- image-ok | 0xff | N/A |
+ image-ok | 0xff | Any |
-----------------+--------+--------'
pending | | |
confirmed | | X |
@@ -225,11 +311,11 @@
-----------------------------------'
- State IV
+ State V
| slot-0 | slot-1 |
-----------------+--------+--------|
magic | Good | Unset |
- image-ok | 0x01 | N/A |
+ image-ok | 0x01 | Any |
-----------------+--------+--------'
pending | | |
confirmed | X | |
@@ -264,70 +350,16 @@
C. Boot into image in slot 0.
-*** BOOT STATES
-
-Logically, you can think of a pair of flags associated with each image slot:
-pending and confirmed. On startup, the boot loader determines the state of the
-device by inspecting each pair of flags. These flags have the following
-meanings:
-
-* pending: image gets tested on next reboot; absent subsequent confirm command,
- revert to original image on second reboot.
-* confirmed: always use image unless excluded by a test image.
-
-In English, when the user wants to run the secondary image, they set the
-pending flag for the second slot and reboot the device. On startup, the boot
-loader will swap the two images in flash, clear the secondary slot's pending
-flag, and run the newly-copied image in slot 0. This is a temporary state; if
-the device reboots again, the boot loader swaps the images back to their
-original slots and boots into the original image. If the user doesn't want to
-revert to the original state, they can make the current state permanent by
-setting the confirmed flag in slot 0.
-
-Switching to an alternate image is a two-step process (set + confirm) to
-prevent a device from becoming "bricked" by bad firmware. If the device
-crashes immediately upon booting the second image, the boot loader reverts to
-the working image, rather than repeatedly rebooting into the bad image.
-
-The following set of tables illustrate the three possible states that the
-device can be in:
-
- | slot-0 | slot-1 |
- ---------------+--------+--------|
- pending | | |
- confirmed | X | |
- ---------------+--------+--------'
- Image 0 confirmed; |
- No change on reboot |
- ---------------------------------'
-
- | slot-0 | slot-1 |
- ---------------+--------+--------|
- pending | | X |
- confirmed | X | |
- ---------------+--------+--------'
- Image 0 confirmed; |
- Test image 1 on next reboot |
- ---------------------------------'
-
- | slot-0 | slot-1 |
- ---------------+--------+--------|
- pending | | |
- confirmed | | X |
- ---------------+--------+--------'
- Testing image 0; |
- Revert to image 1 on next reboot |
- ---------------------------------'
-
*** IMAGE SWAPPING
The boot loader swaps the contents of the two image slots for two reasons:
- * User has issued an "image test" operation; the image in slot-1 should be
- run once (state II).
+ * User has issued a "set pending" operation; the image in slot-1 should be
+ run once (state II) or repeatedly (state III), depending on whether a
+ permanent swap was specified.
* Test image rebooted without being confirmed; the boot loader should
- revert to the original image currently in slot-1 (state III).
+ revert to the original image currently in slot-1 (state IV).
If the boot vector indicates that the image in the secondary slot should be
run, the boot loader needs to copy it to the primary slot. The image currently
@@ -357,21 +389,32 @@
3. Persist completion of swap procedure to slot 0 image trailer.
-The additional caveats in step 2f are necessary so that the slot 1 image trailer
-can be written by the user at a later time. With the image trailer unwritten,
-the user can test the image in slot 1 (i.e., transition to state II).
+The additional caveats in step 2f are necessary so that the slot 1 image
+trailer can be written by the user at a later time. With the image trailer
+unwritten, the user can test the image in slot 1 (i.e., transition to state
+II).
-The particulars of step 3 vary depending on whether an image is being tested or
-reverted:
+The particulars of step 3 vary depending on whether an image is being tested,
+permanently used, or reverted:
* test:
o Write slot0.copy_done = 1
- (should now be in state III)
+ (swap caused the following values to be written:
+ slot0.magic = BOOT_MAGIC
+ slot0.image_ok = Unset)
+ (should now be in state IV)
+
+ * permanent:
+ o Write slot0.copy_done = 1
+ (swap caused the following values to be written:
+ slot0.magic = BOOT_MAGIC
+ slot0.image_ok = 0x01)
+ (should now be in state V)
* revert:
o Write slot0.magic = BOOT_MAGIC
o Write slot0.copy_done = 1
o Write slot0.image_ok = 1
- (should now be in state IV)
+ (should now be in state V)
*** SWAP STATUS
diff --git a/boot/bootutil/include/bootutil/bootutil.h b/boot/bootutil/include/bootutil/bootutil.h
index 4473da9..0947f50 100644
--- a/boot/bootutil/include/bootutil/bootutil.h
+++ b/boot/bootutil/include/bootutil/bootutil.h
@@ -26,10 +26,19 @@
extern "C" {
#endif
+/** Just boot whatever is in slot 0. */
#define BOOT_SWAP_TYPE_NONE 1
+
+/** Swap to slot 1. Absent a confirm command, revert back on next boot. */
#define BOOT_SWAP_TYPE_TEST 2
-#define BOOT_SWAP_TYPE_REVERT 3
-#define BOOT_SWAP_TYPE_FAIL 4
+
+/** Swap to slot 1 permanently. */
+#define BOOT_SWAP_TYPE_PERM 3
+
+/** Swap back to alternate slot. A confirm changes this state to NONE. */
+#define BOOT_SWAP_TYPE_REVERT 4
+
+#define BOOT_SWAP_TYPE_FAIL 0xff
struct image_header;
/**
@@ -53,7 +62,7 @@
int boot_swap_type(void);
-int boot_set_pending(void);
+int boot_set_pending(int permanent);
int boot_set_confirmed(void);
#define SPLIT_GO_OK (0)
diff --git a/boot/bootutil/include/bootutil/image.h b/boot/bootutil/include/bootutil/image.h
index ce98e0c..70ce7fb 100644
--- a/boot/bootutil/include/bootutil/image.h
+++ b/boot/bootutil/include/bootutil/image.h
@@ -39,8 +39,14 @@
#define IMAGE_F_PIC 0x00000001 /* Not currently supported. */
#define IMAGE_F_SHA256 0x00000002 /* Image contains hash TLV */
#define IMAGE_F_PKCS15_RSA2048_SHA256 0x00000004 /* PKCS15 w/RSA and SHA */
-#define IMAGE_F_ECDSA224_SHA256 0x00000008 /* ECDSA256 over SHA256 */
+#define IMAGE_F_ECDSA224_SHA256 0x00000008 /* ECDSA224 over SHA256 */
#define IMAGE_F_NON_BOOTABLE 0x00000010 /* Split image app. */
+#define IMAGE_F_ECDSA256_SHA256 0x00000020 /* ECDSA256 over SHA256 */
+
+/*
+ * ECSDA224 is with NIST P-224
+ * ECSDA256 is with NIST P-256
+ */
/*
* Image trailer TLV types.
@@ -48,6 +54,7 @@
#define IMAGE_TLV_SHA256 1 /* SHA256 of image hdr and body */
#define IMAGE_TLV_RSA2048 2 /* RSA2048 of hash output */
#define IMAGE_TLV_ECDSA224 3 /* ECDSA of hash output */
+#define IMAGE_TLV_ECDSA256 4 /* ECDSA of hash output */
struct image_version {
uint8_t iv_major;
diff --git a/boot/bootutil/pkg.yml b/boot/bootutil/pkg.yml
index 11d5321..4e7ec30 100644
--- a/boot/bootutil/pkg.yml
+++ b/boot/bootutil/pkg.yml
@@ -31,3 +31,6 @@
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/defs"
- "@apache-mynewt-core/sys/flash_map"
+
+pkg.deps.BOOTUTIL_SIGN_EC256:
+ - crypto/tinycrypt
diff --git a/boot/bootutil/signed_images.md b/boot/bootutil/signed_images.md
index 64869c1..80bac7b 100644
--- a/boot/bootutil/signed_images.md
+++ b/boot/bootutil/signed_images.md
@@ -50,22 +50,27 @@
Now the public key is in file called image_sign_pub.der.
+For ECDSA224 these commands are similar.
+
+openssl ecparam -name secp224r1 -genkey -noout -out image_sign.pem
+openssl ec -in image_sign.pem -pubout -outform DER -out image_sign_pub.der
+
+And then the ECDSA256.
+openssl ecparam -name prime256v1 -genkey -noout -out image_sign.pem
+openssl ec -in image_sign.pem -pubout -outform DER -out image_sign_pub.der
+
## Creating a key package
xxd -i image_sign_pub.der image_sign_pub.c.import
Then you need to create a package containing this key, or keys.
-In the pkg.yml for this package, you advertise feature IMAGE_KEYS_RSA or
-IMAGE_KEYS_EC.
-Once this is done, bootloader will expect keys to be filled in
-'bootutil_keys', and the number of keys to be in 'bootutil_key_cnt'.
## Sample pkg.yml
This gets bootutil to turn on image signature validation.
pkg.name: libs/mykeys
pkg.deps:
- - libs/bootutil
+ - "@apache-mynewt-core/boot/bootutil"
## Sample source file
This exports the keys.
@@ -93,3 +98,6 @@
After you've created the key package, you must include it in the build
for bootloader. So modify the pkg.yml for apps/boot to include it.
+
+The syscfg variable to enable ECDSA224 is BOOTUTIL_SIGN_EC, and
+BOOTUTIL_SIGN_EC256 for ECDS256.
diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c
index f3ab9d5..87895ba 100644
--- a/boot/bootutil/src/bootutil_misc.c
+++ b/boot/bootutil/src/bootutil_misc.c
@@ -45,6 +45,7 @@
uint8_t bsw_magic_slot0;
uint8_t bsw_magic_slot1;
uint8_t bsw_image_ok_slot0;
+ uint8_t bsw_image_ok_slot1;
uint8_t bsw_swap_type;
};
@@ -58,7 +59,7 @@
/* | slot-0 | slot-1 |
*----------+------------+------------|
* magic | Unset | Unset |
- * image-ok | Any | N/A |
+ * image-ok | Any | Any |
* ---------+------------+------------'
* swap: none |
* -----------------------------------'
@@ -66,6 +67,7 @@
.bsw_magic_slot0 = BOOT_MAGIC_UNSET,
.bsw_magic_slot1 = BOOT_MAGIC_UNSET,
.bsw_image_ok_slot0 = 0,
+ .bsw_image_ok_slot1 = 0,
.bsw_swap_type = BOOT_SWAP_TYPE_NONE,
},
@@ -73,22 +75,39 @@
/* | slot-0 | slot-1 |
*----------+------------+------------|
* magic | Any | Good |
- * image-ok | Any | N/A |
- * ---------+------------+------------'
+ * image-ok | Any | Unset |
+ * ---------+------------+------------`
* swap: test |
* -----------------------------------'
*/
.bsw_magic_slot0 = 0,
.bsw_magic_slot1 = BOOT_MAGIC_GOOD,
.bsw_image_ok_slot0 = 0,
+ .bsw_image_ok_slot1 = 0xff,
.bsw_swap_type = BOOT_SWAP_TYPE_TEST,
},
{
/* | slot-0 | slot-1 |
*----------+------------+------------|
+ * magic | Any | Good |
+ * image-ok | Any | 0x01 |
+ * ---------+------------+------------`
+ * swap: permanent |
+ * -----------------------------------'
+ */
+ .bsw_magic_slot0 = 0,
+ .bsw_magic_slot1 = BOOT_MAGIC_GOOD,
+ .bsw_image_ok_slot0 = 0,
+ .bsw_image_ok_slot1 = 0x01,
+ .bsw_swap_type = BOOT_SWAP_TYPE_PERM,
+ },
+
+ {
+ /* | slot-0 | slot-1 |
+ *----------+------------+------------|
* magic | Good | Unset |
- * image-ok | 0xff | N/A |
+ * image-ok | 0xff | Any |
* ---------+------------+------------'
* swap: revert (test image running) |
* -----------------------------------'
@@ -96,6 +115,7 @@
.bsw_magic_slot0 = BOOT_MAGIC_GOOD,
.bsw_magic_slot1 = BOOT_MAGIC_UNSET,
.bsw_image_ok_slot0 = 0xff,
+ .bsw_image_ok_slot1 = 0,
.bsw_swap_type = BOOT_SWAP_TYPE_REVERT,
},
@@ -103,7 +123,7 @@
/* | slot-0 | slot-1 |
*----------+------------+------------|
* magic | Good | Unset |
- * image-ok | 0x01 | N/A |
+ * image-ok | 0x01 | Any |
* ---------+------------+------------'
* swap: none (confirmed test image) |
* -----------------------------------'
@@ -111,6 +131,7 @@
.bsw_magic_slot0 = BOOT_MAGIC_GOOD,
.bsw_magic_slot1 = BOOT_MAGIC_UNSET,
.bsw_image_ok_slot0 = 0x01,
+ .bsw_image_ok_slot1 = 0,
.bsw_swap_type = BOOT_SWAP_TYPE_NONE,
},
};
@@ -343,7 +364,9 @@
(table->bsw_magic_slot1 == 0 ||
table->bsw_magic_slot1 == state_slot1.magic) &&
(table->bsw_image_ok_slot0 == 0 ||
- table->bsw_image_ok_slot0 == state_slot0.image_ok)) {
+ table->bsw_image_ok_slot0 == state_slot0.image_ok) &&
+ (table->bsw_image_ok_slot1 == 0 ||
+ table->bsw_image_ok_slot1 == state_slot1.image_ok)) {
return table->bsw_swap_type;
}
@@ -357,10 +380,15 @@
* Marks the image in slot 1 as pending. On the next reboot, the system will
* perform a one-time boot of the slot 1 image.
*
+ * @param permanent Whether the image should be used permanently or
+ * only tested once:
+ * 0=run image once, then confirm or revert.
+ * 1=run image forever.
+ *
* @return 0 on success; nonzero on failure.
*/
int
-boot_set_pending(void)
+boot_set_pending(int permanent)
{
const struct flash_area *fap;
struct boot_swap_state state_slot1;
@@ -386,6 +414,10 @@
rc = boot_write_magic(fap);
}
+ if (rc == 0 && permanent) {
+ rc = boot_write_image_ok(fap);
+ }
+
flash_area_close(fap);
return rc;
diff --git a/boot/bootutil/src/image_ec.c b/boot/bootutil/src/image_ec.c
index 6ddac59..de08b13 100644
--- a/boot/bootutil/src/image_ec.c
+++ b/boot/bootutil/src/image_ec.c
@@ -17,12 +17,13 @@
* under the License.
*/
+#include <string.h>
+
#include "syscfg/syscfg.h"
#if MYNEWT_VAL(BOOTUTIL_SIGN_EC)
#include "bootutil/sign_key.h"
-#include "mbedtls/sha256.h"
#include "mbedtls/ecdsa.h"
#include "mbedtls/oid.h"
#include "mbedtls/asn1.h"
@@ -36,7 +37,7 @@
static const uint8_t ec_secp224r1_oid[] = MBEDTLS_OID_EC_GRP_SECP224R1;
/*
- * Parse the public key used for signing. Simple RSA format.
+ * Parse the public key used for signing.
*/
static int
bootutil_parse_eckey(mbedtls_ecdsa_context *ctx, uint8_t **p, uint8_t *end)
diff --git a/boot/bootutil/src/image_ec256.c b/boot/bootutil/src/image_ec256.c
new file mode 100644
index 0000000..f59a8f0
--- /dev/null
+++ b/boot/bootutil/src/image_ec256.c
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 <string.h>
+
+#include "syscfg/syscfg.h"
+
+#if MYNEWT_VAL(BOOTUTIL_SIGN_EC256)
+#include "bootutil/sign_key.h"
+
+#include "mbedtls/oid.h"
+#include "mbedtls/asn1.h"
+
+#include "tinycrypt/ecc_dsa.h"
+#include "bootutil_priv.h"
+
+/*
+ * Declaring these like this adds NULL termination.
+ */
+static const uint8_t ec_pubkey_oid[] = MBEDTLS_OID_EC_ALG_UNRESTRICTED;
+static const uint8_t ec_secp256r1_oid[] = MBEDTLS_OID_EC_GRP_SECP256R1;
+
+/*
+ * Parse the public key used for signing.
+ */
+static int
+tinycrypt_import_key(EccPoint *pubkey, uint8_t *cp, uint8_t *end)
+{
+ size_t len;
+ mbedtls_asn1_buf alg;
+ mbedtls_asn1_buf param;
+
+ if (mbedtls_asn1_get_tag(&cp, end, &len,
+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) {
+ return -1;
+ }
+ end = cp + len;
+
+ if (mbedtls_asn1_get_alg(&cp, end, &alg, ¶m)) {
+ return -2;
+ }
+ if (alg.len != sizeof(ec_pubkey_oid) - 1 ||
+ memcmp(alg.p, ec_pubkey_oid, sizeof(ec_pubkey_oid) - 1)) {
+ return -3;
+ }
+ if (param.len != sizeof(ec_secp256r1_oid) - 1 ||
+ memcmp(param.p, ec_secp256r1_oid, sizeof(ec_secp256r1_oid) - 1)) {
+ return -4;
+ }
+ if (mbedtls_asn1_get_bitstring_null(&cp, end, &len)) {
+ return -6;
+ }
+ if (cp + len != end) {
+ return -7;
+ }
+
+ if (len != 2 * NUM_ECC_BYTES + 1) {
+ return -8;
+ }
+ if (cp[0] != 0x04) {
+ return -9;
+ }
+
+ ecc_bytes2native(pubkey->x, cp + 1);
+ ecc_bytes2native(pubkey->y, cp + 1 + NUM_ECC_BYTES);
+
+ return 0;
+}
+
+/*
+ * cp points to ASN1 string containing an integer.
+ * Verify the tag, and that the length is 32 bytes.
+ */
+static int
+tinycrypt_read_bigint(uint32_t i[NUM_ECC_DIGITS], uint8_t **cp, uint8_t *end)
+{
+ size_t len;
+ uint8_t bigint[NUM_ECC_BYTES];
+
+ if (mbedtls_asn1_get_tag(cp, end, &len, MBEDTLS_ASN1_INTEGER)) {
+ return -3;
+ }
+ if (len > NUM_ECC_BYTES) {
+ memcpy(bigint, *cp + len - NUM_ECC_BYTES, NUM_ECC_BYTES);
+ } else {
+ memset(bigint, 0, NUM_ECC_BYTES - len);
+ memcpy(bigint + NUM_ECC_BYTES - len, *cp, len);
+ }
+ *cp += len;
+ ecc_bytes2native(i, bigint);
+ return 0;
+}
+
+/*
+ * Read in signature. Signature has r and s encoded as integers.
+ */
+static int
+tinycrypt_decode_sig(uint32_t r[NUM_ECC_DIGITS], uint32_t s[NUM_ECC_DIGITS],
+ uint8_t *cp, uint8_t *end)
+{
+ int rc;
+ size_t len;
+
+ rc = mbedtls_asn1_get_tag(&cp, end, &len,
+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+ if (rc) {
+ return -1;
+ }
+ if (cp + len != end) {
+ return -2;
+ }
+ rc = tinycrypt_read_bigint(r, &cp, end);
+ if (rc) {
+ return -3;
+ }
+ rc = tinycrypt_read_bigint(s, &cp, end);
+ if (rc) {
+ return -4;
+ }
+ return 0;
+}
+
+int
+bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, int slen,
+ uint8_t key_id)
+{
+ int rc;
+ uint8_t *cp;
+ uint8_t *end;
+ EccPoint ctx;
+ uint32_t r[NUM_ECC_DIGITS];
+ uint32_t s[NUM_ECC_DIGITS];
+ uint32_t hash_t[NUM_ECC_DIGITS];
+
+ cp = (uint8_t *)bootutil_keys[key_id].key;
+ end = cp + *bootutil_keys[key_id].len;
+
+ rc = tinycrypt_import_key(&ctx, cp, end);
+ if (rc) {
+ return -1;
+ }
+
+ rc = tinycrypt_decode_sig(r, s, sig, sig + slen);
+ if (rc) {
+ return -1;
+ }
+
+ /*
+ * This is simplified, as the hash length is also 32 bytes.
+ */
+ if (hlen != NUM_ECC_BYTES) {
+ return -1;
+ }
+
+ ecc_bytes2native(hash_t, hash);
+ rc = ecdsa_verify(&ctx, hash_t, r, s);
+ if (rc == 1) {
+ return 0;
+ } else {
+ return -2;
+ }
+}
+#endif /* MYNEWT_VAL(BOOTUTIL_SIGN_EC256) */
diff --git a/boot/bootutil/src/image_validate.c b/boot/bootutil/src/image_validate.c
index 98fdc9b..9ab08b7 100644
--- a/boot/bootutil/src/image_validate.c
+++ b/boot/bootutil/src/image_validate.c
@@ -93,7 +93,8 @@
uint32_t off;
uint32_t size;
uint32_t sha_off = 0;
-#if MYNEWT_VAL(BOOTUTIL_SIGN_RSA) || MYNEWT_VAL(BOOTUTIL_SIGN_EC)
+#if MYNEWT_VAL(BOOTUTIL_SIGN_RSA) || MYNEWT_VAL(BOOTUTIL_SIGN_EC) || \
+ MYNEWT_VAL(BOOTUTIL_SIGN_EC256)
uint32_t sig_off = 0;
uint32_t sig_len = 0;
#endif
@@ -112,6 +113,11 @@
return -1;
}
#endif
+#if MYNEWT_VAL(BOOTUTIL_SIGN_EC256)
+ if ((hdr->ih_flags & IMAGE_F_ECDSA256_SHA256) == 0) {
+ return -1;
+ }
+#endif
if ((hdr->ih_flags & IMAGE_F_SHA256) == 0) {
return -1;
}
@@ -159,6 +165,15 @@
sig_len = tlv.it_len;
}
#endif
+#if MYNEWT_VAL(BOOTUTIL_SIGN_EC256)
+ if (tlv.it_type == IMAGE_TLV_ECDSA256) {
+ if (tlv.it_len < 72) { /* oids + 2 * 32 bytes */
+ return -1;
+ }
+ sig_off = off + sizeof(tlv);
+ sig_len = tlv.it_len;
+ }
+#endif
}
if (hdr->ih_flags & IMAGE_F_SHA256) {
if (!sha_off) {
@@ -175,7 +190,8 @@
return -1;
}
}
-#if MYNEWT_VAL(BOOTUTIL_SIGN_RSA) || MYNEWT_VAL(BOOTUTIL_SIGN_EC)
+#if MYNEWT_VAL(BOOTUTIL_SIGN_RSA) || MYNEWT_VAL(BOOTUTIL_SIGN_EC) || \
+ MYNEWT_VAL(BOOTUTIL_SIGN_EC256)
if (!sig_off) {
/*
* Header said there should be PKCS1.v5 signature, no TLV
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index 9d85511..eaa9565 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -44,9 +44,9 @@
struct {
struct image_header hdr;
struct flash_area *sectors;
+ int num_sectors;
} imgs[BOOT_NUM_SLOTS];
- int num_img_sectors;
struct flash_area scratch_sector;
uint8_t write_sz;
@@ -143,6 +143,7 @@
static const uint8_t boot_swap_trans_table[][2] = {
/* From To */
{ BOOT_SWAP_TYPE_REVERT, BOOT_SWAP_TYPE_NONE },
+ { BOOT_SWAP_TYPE_PERM, BOOT_SWAP_TYPE_NONE },
{ BOOT_SWAP_TYPE_TEST, BOOT_SWAP_TYPE_REVERT },
};
@@ -251,7 +252,15 @@
for (i = 0; i < BOOT_NUM_SLOTS; i++) {
rc = boot_read_image_header(i, &boot_data.imgs[i].hdr);
if (rc != 0) {
- return rc;
+ /* If at least one header was read successfully, then the boot
+ * loader can attempt a boot. Failure to read any headers is a
+ * fatal error.
+ */
+ if (i > 0) {
+ return 0;
+ } else {
+ return rc;
+ }
}
}
@@ -277,6 +286,56 @@
return elem_sz;
}
+static uint32_t
+boot_total_img_size(int slot)
+{
+ const struct image_header *hdr;
+
+ hdr = &boot_data.imgs[slot].hdr;
+ return hdr->ih_hdr_size + hdr->ih_img_size + hdr->ih_tlv_size;
+}
+
+static int
+boot_slots_compatible(void)
+{
+ const struct flash_area *sector0;
+ const struct flash_area *sector1;
+ uint32_t slot_size;
+ int i;
+
+ /* Ensure both image slots have identical sector layouts. */
+ if (boot_data.imgs[0].num_sectors != boot_data.imgs[1].num_sectors) {
+ return 0;
+ }
+
+ slot_size = 0;
+ for (i = 0; i < boot_data.imgs[0].num_sectors; i++) {
+ sector0 = boot_data.imgs[0].sectors + i;
+ sector1 = boot_data.imgs[1].sectors + i;
+ if (sector0->fa_size != sector1->fa_size) {
+ return 0;
+ }
+
+ slot_size += sector0->fa_size;
+ }
+
+ /* Ensure images are small enough that they leave room for the image
+ * trailers.
+ */
+ if (slot_size <
+ boot_total_img_size(0) + boot_trailer_sz(boot_data.write_sz)) {
+
+ return 0;
+ }
+ if (slot_size <
+ boot_total_img_size(1) + boot_trailer_sz(boot_data.write_sz)) {
+
+ return 0;
+ }
+
+ return 1;
+}
+
/**
* Determines the sector layout of both image slots and the scratch area.
* This information is necessary for calculating the number of bytes to erase
@@ -286,13 +345,10 @@
static int
boot_read_sectors(void)
{
- const struct flash_area *sector0;
- const struct flash_area *sector1;
const struct flash_area *scratch;
int num_sectors_slot0;
int num_sectors_slot1;
int rc;
- int i;
num_sectors_slot0 = BOOT_MAX_IMG_SECTORS;
rc = flash_area_to_sectors(FLASH_AREA_IMAGE_0, &num_sectors_slot0,
@@ -300,6 +356,7 @@
if (rc != 0) {
return BOOT_EFLASH;
}
+ boot_data.imgs[0].num_sectors = num_sectors_slot0;
num_sectors_slot1 = BOOT_MAX_IMG_SECTORS;
rc = flash_area_to_sectors(FLASH_AREA_IMAGE_1, &num_sectors_slot1,
@@ -307,27 +364,13 @@
if (rc != 0) {
return BOOT_EFLASH;
}
+ boot_data.imgs[1].num_sectors = num_sectors_slot1;
rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &scratch);
if (rc != 0) {
return BOOT_EFLASH;
}
boot_data.scratch_sector = *scratch;
- boot_data.write_sz = hal_flash_align(scratch->fa_device_id);
-
- /* Ensure both image slots have identical sector layouts. */
- if (num_sectors_slot0 != num_sectors_slot1) {
- return BOOT_EFLASH;
- }
- for (i = 0; i < num_sectors_slot0; i++) {
- sector0 = boot_data.imgs[0].sectors + i;
- sector1 = boot_data.imgs[1].sectors + i;
- if (sector0->fa_size != sector1->fa_size) {
- return BOOT_EFLASH;
- }
- }
-
- boot_data.num_img_sectors = num_sectors_slot0;
boot_data.write_sz = boot_write_sz();
@@ -836,7 +879,7 @@
int swap_idx;
swap_idx = 0;
- last_sector_idx = boot_data.num_img_sectors - 1;
+ last_sector_idx = boot_data.imgs[0].num_sectors - 1;
while (last_sector_idx >= 0) {
sz = boot_copy_sz(last_sector_idx, &first_sector_idx);
if (swap_idx >= bs->idx) {
@@ -919,6 +962,54 @@
}
/**
+ * Performs an image swap if one is required.
+ *
+ * @param out_swap_type On success, the type of swap performed gets
+ * written here.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+boot_swap_if_needed(int *out_swap_type)
+{
+ struct boot_status bs;
+ int swap_type;
+ int rc;
+
+ /* Determine if we rebooted in the middle of an image swap
+ * operation.
+ */
+ rc = boot_read_status(&bs);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* If a partial swap was detected, complete it. */
+ if (bs.idx != 0 || bs.state != 0) {
+ rc = boot_copy_image(&bs);
+ assert(rc == 0);
+
+ /* Extrapolate the type of the partial swap. We need this
+ * information to know how to mark the swap complete in flash.
+ */
+ swap_type = boot_previous_swap_type();
+ } else {
+ swap_type = boot_validated_swap_type();
+ switch (swap_type) {
+ case BOOT_SWAP_TYPE_TEST:
+ case BOOT_SWAP_TYPE_PERM:
+ case BOOT_SWAP_TYPE_REVERT:
+ rc = boot_copy_image(&bs);
+ assert(rc == 0);
+ break;
+ }
+ }
+
+ *out_swap_type = swap_type;
+ return 0;
+}
+
+/**
* Prepares the booting process. This function moves images around in flash as
* appropriate, and tells you what address to boot from.
*
@@ -929,7 +1020,6 @@
int
boot_go(struct boot_rsp *rsp)
{
- struct boot_status bs;
int swap_type;
int slot;
int rc;
@@ -937,7 +1027,7 @@
/* The array of slot sectors are defined here (as opposed to file scope) so
* that they don't get allocated for non-boot-loader apps. This is
* necessary because the gcc option "-fdata-sections" doesn't seem to have
- * any effect for some reason.
+ * any effect in older gcc versions (e.g., 4.8.4).
*/
static struct flash_area slot0_sectors[BOOT_MAX_IMG_SECTORS];
static struct flash_area slot1_sectors[BOOT_MAX_IMG_SECTORS];
@@ -956,30 +1046,16 @@
return rc;
}
- /* Determine if we rebooted in the middle of an image swap operation. */
- rc = boot_read_status(&bs);
- if (rc != 0) {
- return rc;
- }
-
- /* If a partial swap was detected, complete it. */
- if (bs.idx != 0 || bs.state != 0) {
- rc = boot_copy_image(&bs);
- assert(rc == 0);
-
- /* Extrapolate the type of the partial swap. We need this information
- * to know how to mark the swap complete in flash.
- */
- swap_type = boot_previous_swap_type();
- } else {
- swap_type = boot_validated_swap_type();
- switch (swap_type) {
- case BOOT_SWAP_TYPE_TEST:
- case BOOT_SWAP_TYPE_REVERT:
- rc = boot_copy_image(&bs);
- assert(rc == 0);
- break;
+ /* If the image slots aren't compatible, no swap is possible. Just boot
+ * into slot 0.
+ */
+ if (boot_slots_compatible()) {
+ rc = boot_swap_if_needed(&swap_type);
+ if (rc != 0) {
+ return rc;
}
+ } else {
+ swap_type = BOOT_SWAP_TYPE_NONE;
}
switch (swap_type) {
@@ -988,6 +1064,7 @@
break;
case BOOT_SWAP_TYPE_TEST:
+ case BOOT_SWAP_TYPE_PERM:
slot = 1;
boot_finalize_test_swap();
break;
diff --git a/boot/bootutil/syscfg.yml b/boot/bootutil/syscfg.yml
index 2951c03..e896bf9 100644
--- a/boot/bootutil/syscfg.yml
+++ b/boot/bootutil/syscfg.yml
@@ -25,3 +25,6 @@
BOOTUTIL_SIGN_EC:
description: 'TBD'
value: '0'
+ BOOTUTIL_SIGN_EC256:
+ description: 'TBD'
+ value: '0'
diff --git a/boot/bootutil/test/src/boot_test.c b/boot/bootutil/test/src/boot_test.c
index a0e8069..478bf85 100644
--- a/boot/bootutil/test/src/boot_test.c
+++ b/boot/bootutil/test/src/boot_test.c
@@ -51,6 +51,9 @@
TEST_CASE_DECL(boot_test_invalid_hash)
TEST_CASE_DECL(boot_test_revert)
TEST_CASE_DECL(boot_test_revert_continue)
+TEST_CASE_DECL(boot_test_permanent)
+TEST_CASE_DECL(boot_test_permanent_continue)
+TEST_CASE_DECL(boot_test_img_too_big);
TEST_SUITE(boot_test_main)
{
@@ -71,6 +74,9 @@
boot_test_invalid_hash();
boot_test_revert();
boot_test_revert_continue();
+ boot_test_permanent();
+ boot_test_permanent_continue();
+ boot_test_img_too_big();
}
int
diff --git a/boot/bootutil/test/src/boot_test.h b/boot/bootutil/test/src/boot_test.h
index 072dfbb..cd15e0d 100644
--- a/boot/bootutil/test/src/boot_test.h
+++ b/boot/bootutil/test/src/boot_test.h
@@ -60,6 +60,7 @@
uint8_t boot_test_util_byte_at(int img_msb, uint32_t image_offset);
uint8_t boot_test_util_flash_align(void);
+uint32_t boot_test_util_slot_size(int slot);
void boot_test_util_init_flash(void);
void boot_test_util_copy_area(int from_area_idx, int to_area_idx);
void boot_test_util_swap_areas(int area_idx1, int area_idx2);
@@ -67,6 +68,7 @@
int slot);
void boot_test_util_write_hash(const struct image_header *hdr, int slot);
void boot_test_util_mark_revert(void);
+void boot_test_util_mark_swap_perm(void);
void boot_test_util_verify_area(const struct flash_area *area_desc,
const struct image_header *hdr,
uint32_t image_addr, int img_msb);
diff --git a/boot/bootutil/test/src/boot_test_utils.c b/boot/bootutil/test/src/boot_test_utils.c
index dc6fbb0..c8583f0 100644
--- a/boot/bootutil/test/src/boot_test_utils.c
+++ b/boot/bootutil/test/src/boot_test_utils.c
@@ -60,12 +60,36 @@
boot_test_util_flash_align(void)
{
const struct flash_area *fap;
+ uint8_t align;
int rc;
rc = flash_area_open(FLASH_AREA_IMAGE_0, &fap);
TEST_ASSERT_FATAL(rc == 0);
- return flash_area_align(fap);
+ align = flash_area_align(fap);
+
+ flash_area_close(fap);
+
+ return align;
+}
+
+uint32_t
+boot_test_util_slot_size(int slot)
+{
+ const struct flash_area *fap;
+ uint32_t size;
+ int area_id;
+ int rc;
+
+ area_id = flash_area_id_from_image_slot(slot);
+ rc = flash_area_open(area_id, &fap);
+ TEST_ASSERT_FATAL(rc == 0);
+
+ size = fap->fa_size;
+
+ flash_area_close(fap);
+
+ return size;
}
void
@@ -323,6 +347,18 @@
}
void
+boot_test_util_mark_swap_perm(void)
+{
+ struct boot_swap_state state_slot0 = {
+ .magic = BOOT_MAGIC_GOOD,
+ .copy_done = 0x01,
+ .image_ok = 0x01,
+ };
+
+ boot_test_util_write_swap_state(FLASH_AREA_IMAGE_0, &state_slot0);
+}
+
+void
boot_test_util_verify_area(const struct flash_area *area_desc,
const struct image_header *hdr,
uint32_t image_addr, int img_msb)
@@ -505,6 +541,10 @@
expected_swap_type = BOOT_SWAP_TYPE_REVERT;
break;
+ case BOOT_SWAP_TYPE_PERM:
+ expected_swap_type = BOOT_SWAP_TYPE_NONE;
+ break;
+
case BOOT_SWAP_TYPE_REVERT:
expected_swap_type = BOOT_SWAP_TYPE_NONE;
break;
diff --git a/boot/bootutil/test/src/testcases/boot_test_img_too_big.c b/boot/bootutil/test/src/testcases/boot_test_img_too_big.c
new file mode 100644
index 0000000..c72607b
--- /dev/null
+++ b/boot/bootutil/test/src/testcases/boot_test_img_too_big.c
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 "boot_test.h"
+
+TEST_CASE(boot_test_img_too_big)
+{
+ uint32_t slot_size;
+
+ struct image_header hdr0 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 5 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 5, 21, 432 },
+ };
+
+ struct image_header hdr1 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 0,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ boot_test_util_init_flash();
+
+ /* Make image in slot 1 so large that there is no room for an image
+ * trailer.
+ */
+ slot_size = boot_test_util_slot_size(1);
+ hdr1.ih_img_size = slot_size - 10;
+
+ boot_test_util_write_image(&hdr0, 0);
+ boot_test_util_write_hash(&hdr0, 0);
+ boot_test_util_write_image(&hdr1, 1);
+ boot_test_util_write_hash(&hdr1, 1);
+
+ /* Ensure the boot loader does not interpret the end of the image as a
+ * trailer.
+ */
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_NONE, &hdr0, &hdr1);
+}
diff --git a/boot/bootutil/test/src/testcases/boot_test_invalid_hash.c b/boot/bootutil/test/src/testcases/boot_test_invalid_hash.c
index 515ab45..bac3f7e 100644
--- a/boot/bootutil/test/src/testcases/boot_test_invalid_hash.c
+++ b/boot/bootutil/test/src/testcases/boot_test_invalid_hash.c
@@ -52,7 +52,7 @@
&tlv, sizeof(tlv));
TEST_ASSERT(rc == 0);
- rc = boot_set_pending();
+ rc = boot_set_pending(0);
TEST_ASSERT(rc == 0);
boot_test_util_verify_all(BOOT_SWAP_TYPE_NONE, &hdr0, NULL);
diff --git a/boot/bootutil/test/src/testcases/boot_test_no_flag_has_hash.c b/boot/bootutil/test/src/testcases/boot_test_no_flag_has_hash.c
index 61c8f40..b6a8ec5 100644
--- a/boot/bootutil/test/src/testcases/boot_test_no_flag_has_hash.c
+++ b/boot/bootutil/test/src/testcases/boot_test_no_flag_has_hash.c
@@ -45,7 +45,7 @@
boot_test_util_write_image(&hdr1, 1);
boot_test_util_write_hash(&hdr1, 1);
- rc = boot_set_pending();
+ rc = boot_set_pending(0);
TEST_ASSERT(rc == 0);
boot_test_util_verify_all(BOOT_SWAP_TYPE_NONE, &hdr0, NULL);
diff --git a/boot/bootutil/test/src/testcases/boot_test_no_hash.c b/boot/bootutil/test/src/testcases/boot_test_no_hash.c
index 639a1fb..13fe7e1 100644
--- a/boot/bootutil/test/src/testcases/boot_test_no_hash.c
+++ b/boot/bootutil/test/src/testcases/boot_test_no_hash.c
@@ -44,7 +44,7 @@
boot_test_util_write_hash(&hdr0, 0);
boot_test_util_write_image(&hdr1, 1);
- rc = boot_set_pending();
+ rc = boot_set_pending(0);
TEST_ASSERT(rc == 0);
boot_test_util_verify_all(BOOT_SWAP_TYPE_NONE, &hdr0, NULL);
diff --git a/boot/bootutil/test/src/testcases/boot_test_nv_bs_11.c b/boot/bootutil/test/src/testcases/boot_test_nv_bs_11.c
index efa967e..eae13bd 100644
--- a/boot/bootutil/test/src/testcases/boot_test_nv_bs_11.c
+++ b/boot/bootutil/test/src/testcases/boot_test_nv_bs_11.c
@@ -46,7 +46,7 @@
boot_test_util_write_hash(&hdr0, 0);
boot_test_util_write_image(&hdr1, 1);
boot_test_util_write_hash(&hdr1, 1);
- rc = boot_set_pending();
+ rc = boot_set_pending(0);
boot_test_util_copy_area(5, BOOT_TEST_AREA_IDX_SCRATCH);
status.idx = 0;
diff --git a/boot/bootutil/test/src/testcases/boot_test_nv_bs_11_2areas.c b/boot/bootutil/test/src/testcases/boot_test_nv_bs_11_2areas.c
index eec22f8..ba09ea1 100644
--- a/boot/bootutil/test/src/testcases/boot_test_nv_bs_11_2areas.c
+++ b/boot/bootutil/test/src/testcases/boot_test_nv_bs_11_2areas.c
@@ -46,7 +46,7 @@
boot_test_util_write_hash(&hdr0, 0);
boot_test_util_write_image(&hdr1, 1);
boot_test_util_write_hash(&hdr1, 1);
- rc = boot_set_pending();
+ rc = boot_set_pending(0);
TEST_ASSERT_FATAL(rc == 0);
boot_test_util_swap_areas(2, 5);
diff --git a/boot/bootutil/test/src/testcases/boot_test_nv_ns_01.c b/boot/bootutil/test/src/testcases/boot_test_nv_ns_01.c
index 0481ef0..8abd90e 100644
--- a/boot/bootutil/test/src/testcases/boot_test_nv_ns_01.c
+++ b/boot/bootutil/test/src/testcases/boot_test_nv_ns_01.c
@@ -33,7 +33,7 @@
boot_test_util_write_image(&hdr, 1);
boot_test_util_write_hash(&hdr, 1);
- boot_set_pending();
+ boot_set_pending(0);
boot_test_util_verify_all(BOOT_SWAP_TYPE_REVERT, NULL, &hdr);
}
diff --git a/boot/bootutil/test/src/testcases/boot_test_permanent.c b/boot/bootutil/test/src/testcases/boot_test_permanent.c
new file mode 100644
index 0000000..489ebd6
--- /dev/null
+++ b/boot/bootutil/test/src/testcases/boot_test_permanent.c
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 "boot_test.h"
+
+TEST_CASE(boot_test_permanent)
+{
+ int rc;
+
+ struct image_header hdr0 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 5 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 5, 21, 432 },
+ };
+
+ struct image_header hdr1 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 32 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr0, 0);
+ boot_test_util_write_hash(&hdr0, 0);
+ boot_test_util_write_image(&hdr1, 1);
+ boot_test_util_write_hash(&hdr1, 1);
+
+ rc = boot_set_pending(1);
+ TEST_ASSERT_FATAL(rc == 0);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_PERM, &hdr0, &hdr1);
+}
diff --git a/boot/bootutil/test/src/testcases/boot_test_permanent_continue.c b/boot/bootutil/test/src/testcases/boot_test_permanent_continue.c
new file mode 100644
index 0000000..2417df0
--- /dev/null
+++ b/boot/bootutil/test/src/testcases/boot_test_permanent_continue.c
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 "boot_test.h"
+
+TEST_CASE(boot_test_permanent_continue)
+{
+ struct boot_status status;
+ int rc;
+
+ struct image_header hdr0 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 5 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 5, 21, 432 },
+ };
+
+ struct image_header hdr1 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 32 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr0, 0);
+ boot_test_util_write_hash(&hdr0, 0);
+ boot_test_util_write_image(&hdr1, 1);
+ boot_test_util_write_hash(&hdr1, 1);
+
+ /* Indicate that the image in slot 0 is being permanently used. */
+ boot_test_util_mark_swap_perm();
+
+ boot_test_util_swap_areas(2, 5);
+
+ status.idx = 1;
+ status.state = 0;
+
+ rc = boot_write_status(&status);
+ TEST_ASSERT_FATAL(rc == 0);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_PERM, &hdr0, &hdr1);
+}
diff --git a/boot/bootutil/test/src/testcases/boot_test_vb_ns_11.c b/boot/bootutil/test/src/testcases/boot_test_vb_ns_11.c
index c6f7b93..e9b961d 100644
--- a/boot/bootutil/test/src/testcases/boot_test_vb_ns_11.c
+++ b/boot/bootutil/test/src/testcases/boot_test_vb_ns_11.c
@@ -46,7 +46,7 @@
boot_test_util_write_image(&hdr1, 1);
boot_test_util_write_hash(&hdr1, 1);
- rc = boot_set_pending();
+ rc = boot_set_pending(0);
TEST_ASSERT(rc == 0);
boot_test_util_verify_all(BOOT_SWAP_TYPE_TEST, &hdr0, &hdr1);
diff --git a/boot/bootutil/test/src/testcases/boot_test_vm_ns_01.c b/boot/bootutil/test/src/testcases/boot_test_vm_ns_01.c
index 3c89033..a539fa2 100644
--- a/boot/bootutil/test/src/testcases/boot_test_vm_ns_01.c
+++ b/boot/bootutil/test/src/testcases/boot_test_vm_ns_01.c
@@ -35,7 +35,7 @@
boot_test_util_write_image(&hdr, 1);
boot_test_util_write_hash(&hdr, 1);
- rc = boot_set_pending();
+ rc = boot_set_pending(0);
TEST_ASSERT(rc == 0);
boot_test_util_verify_all(BOOT_SWAP_TYPE_REVERT, NULL, &hdr);
diff --git a/boot/bootutil/test/src/testcases/boot_test_vm_ns_11_2areas.c b/boot/bootutil/test/src/testcases/boot_test_vm_ns_11_2areas.c
index 490a634..24b5da2 100644
--- a/boot/bootutil/test/src/testcases/boot_test_vm_ns_11_2areas.c
+++ b/boot/bootutil/test/src/testcases/boot_test_vm_ns_11_2areas.c
@@ -46,7 +46,7 @@
boot_test_util_write_image(&hdr1, 1);
boot_test_util_write_hash(&hdr1, 1);
- rc = boot_set_pending();
+ rc = boot_set_pending(0);
TEST_ASSERT(rc == 0);
boot_test_util_verify_all(BOOT_SWAP_TYPE_TEST, &hdr0, &hdr1);
diff --git a/boot/bootutil/test/src/testcases/boot_test_vm_ns_11_b.c b/boot/bootutil/test/src/testcases/boot_test_vm_ns_11_b.c
index 914242b..e1a87c7 100644
--- a/boot/bootutil/test/src/testcases/boot_test_vm_ns_11_b.c
+++ b/boot/bootutil/test/src/testcases/boot_test_vm_ns_11_b.c
@@ -46,7 +46,7 @@
boot_test_util_write_image(&hdr1, 1);
boot_test_util_write_hash(&hdr1, 1);
- rc = boot_set_pending();
+ rc = boot_set_pending(0);
TEST_ASSERT(rc == 0);
boot_test_util_verify_all(BOOT_SWAP_TYPE_TEST, &hdr0, &hdr1);