examples/suit: Refactor to draft ietf-v3

Co-authored-by: Kaspar Schleiser <kaspar@schleiser.de>
This commit is contained in:
Koen Zandberg 2020-02-26 14:52:39 +01:00
parent 413b91326f
commit 1d1d5b6f0d
No known key found for this signature in database
GPG Key ID: 0E63411F8FCA8247
10 changed files with 121 additions and 84 deletions

View File

@ -44,10 +44,7 @@ QUIET ?= 1
#
USEMODULE += nanocoap_sock sock_util
USEMODULE += suit suit_coap
# SUIT draft v4 support:
USEMODULE += suit_v4
USEMODULE += suit suit_transport_coap
# Display a progress bar during firmware download
USEMODULE += progress_bar
@ -97,6 +94,10 @@ TEST_EXTRA_FILES += $(SLOT_RIOT_ELFS) $(SUIT_SEC) $(SUIT_PUB)
# a terminal is opened to synchronize.
TESTRUNNER_RESET_AFTER_TERM ?= 1
# This can be removed as soon as the Pi-fleet runners support an Openssl version
# with ed25519 support.
TEST_ON_CI_BLACKLIST = all
include $(RIOTBASE)/Makefile.include
.PHONY: host-tools

View File

@ -127,7 +127,7 @@ In another terminal, run:
[setup-wireless]: #Setup-a-wireless-device-behind-a-border-router
If the workflow for updating using ethos is successful, you can try doing the
same over "real" network interfaces, by updating a node that is connected
same over wireless network interfaces, by updating a node that is connected
wirelessly with a border router in between.
Depending on your device you can use BLE or 802.15.4.
@ -369,22 +369,59 @@ see 6 pairs of messages indicating where (filepath) the file was published and
the corresponding coap resource URI
...
published "/home/francisco/workspace/RIOT/examples/suit_update/bin/samr21-xpro/suit_update-riot.suitv4_signed.1557135946.bin"
as "coap://[2001:db8::1]/fw/samr21-xpro/suit_update-riot.suitv4_signed.1557135946.bin"
published "/home/francisco/workspace/RIOT/examples/suit_update/bin/samr21-xpro/suit_update-riot.suitv4_signed.latest.bin"
as "coap://[2001:db8::1]/fw/samr21-xpro/suit_update-riot.suitv4_signed.latest.bin"
published "/home/francisco/workspace/RIOT/examples/suit_update/bin/samr21-xpro/suit_update-riot.suitv3_signed.1557135946.bin"
as "coap://[2001:db8::1]/fw/samr21-xpro/suit_update-riot.suitv3_signed.1557135946.bin"
published "/home/francisco/workspace/RIOT/examples/suit_update/bin/samr21-xpro/suit_update-riot.suitv3_signed.latest.bin"
as "coap://[2001:db8::1]/fw/samr21-xpro/suit_update-riot.suitv3_signed.latest.bin"
...
### Notify an update to the device
[update-notify]: #Norify-an-update-to-the-device
If the network has been started with a standalone node, the RIOT node should be
reachable via link-local `fe80::2%riot0` on the ethos interface. If it was setup as a
wireless device it will be reachable via its global address, something like `2001:db8::7b7e:3255:1313:8d96`
reachable via link-local EUI64 address on the ethos interface, e.g:
Iface 5 HWaddr: 02:BE:74:C0:2F:B9
L2-PDU:1500 MTU:1500 HL:64 RTR
RTR_ADV
Source address length: 6
Link type: wired
inet6 addr: fe80::7b7e:3255:1313:8d96 scope: link VAL
inet6 addr: fe80::2 scope: link VAL
inet6 group: ff02::2
inet6 group: ff02::1
inet6 group: ff02::1:ffc0:2fb9
inet6 group: ff02::1:ff00:2
the EUI64 link local address is `fe80::7b7e:3255:1313:8d96` and
SUIT_CLIENT=[fe80::7b7e:3255:1313:8d96%riot0].
If it was setup as a wireless device it will be reachable via its global
address, e.g:
Iface 6 HWaddr: 0D:96 Channel: 26 Page: 0 NID: 0x23
Long HWaddr: 79:7E:32:55:13:13:8D:96
TX-Power: 0dBm State: IDLE max. Retrans.: 3 CSMA Retries: 4
AUTOACK ACK_REQ CSMA L2-PDU:102 MTU:1280 HL:64 RTR
RTR_ADV 6LO IPHC
Source address length: 8
Link type: wireless
inet6 addr: fe80::7b7e:3255:1313:8d96 scope: link VAL
inet6 addr: 2001:db8::7b7e:3255:1313:8d96 scope: global VAL
inet6 group: ff02::2
inet6 group: ff02::1
inet6 group: ff02::1:ff17:dd59
inet6 group: ff02::1:ff00:2
the global address is `2001:db8::7b7e:3255:1313:8d96` and
SUIT_CLIENT=[2001:db8::7b7e:3255:1313:8d96].
- In wired mode:
$ SUIT_COAP_SERVER=[2001:db8::1] SUIT_CLIENT=[fe80::2%riot0] BOARD=samr21-xpro make -C examples/suit_update suit/notify
$ SUIT_COAP_SERVER=[2001:db8::1] SUIT_CLIENT=[fe80::7b7e:3255:1313:8d96%riot] BOARD=samr21-xpro make -C examples/suit_update suit/notify
- In wireless mode:
@ -394,16 +431,12 @@ wireless device it will be reachable via its global address, something like `200
This notifies the node of a new available manifest. Once the notification is
received by the device, it fetches it.
If using `suit-v4` the node hangs for a couple of seconds when verifying the
If using `suit-v3` the node hangs for a couple of seconds when verifying the
signature:
....
INFO # suit_coap: got manifest with size 545
INFO # jumping into map
INFO # )got key val=1
INFO # handler res=0
INFO # got key val=2
INFO # suit: verifying manifest signature...
suit_coap: got manifest with size 470
suit: verifying manifest signature
....
Once the signature is validated it continues validating other parts of the
@ -412,12 +445,12 @@ Among these validations it checks some condition like firmware offset position
in regards to the running slot to see witch firmware image to fetch.
....
INFO # Handling handler with key 10 at 0x2b981
INFO # Comparing manifest offset 4096 with other slot offset 4096
....
INFO # Handling handler with key 10 at 0x2b981
INFO # Comparing manifest offset 133120 with other slot offset 4096
INFO # Sequence handler error
suit: validated manifest version
)suit: validated sequence number
)validating vendor ID
Comparing 547d0d74-6d3a-5a92-9662-4881afd9407b to 547d0d74-6d3a-5a92-9662-4881afd9407b from manifest
validating vendor ID: OK
validating class id
....
Once the manifest validation is complete, the application fetches the image
@ -432,27 +465,22 @@ displayed during this step:
Once the new image is written, a final validation is performed and, in case of
success, the application reboots on the new slot:
2019-04-05 16:19:26,363 - INFO # riotboot: verifying digest at 0x20003f37 (img at: 0x20800 size: 80212)
2019-04-05 16:19:26,704 - INFO # handler res=0
2019-04-05 16:19:26,705 - INFO # got key val=10
2019-04-05 16:19:26,707 - INFO # no handler found
2019-04-05 16:19:26,708 - INFO # got key val=12
2019-04-05 16:19:26,709 - INFO # no handler found
2019-04-05 16:19:26,711 - INFO # handler res=0
2019-04-05 16:19:26,713 - INFO # suit_v4_parse() success
2019-04-05 16:19:26,715 - INFO # SUIT policy check OK.
2019-04-05 16:19:26,718 - INFO # suit_coap: finalizing image flash
2019-04-05 16:19:26,725 - INFO # riotboot_flashwrite: riotboot flashing completed successfully
2019-04-05 16:19:26,728 - INFO # Image magic_number: 0x544f4952
2019-04-05 16:19:26,730 - INFO # Image Version: 0x5ca76390
2019-04-05 16:19:26,733 - INFO # Image start address: 0x00020900
2019-04-05 16:19:26,738 - INFO # Header chksum: 0x13b466db
Verifying image digest
riotboot: verifying digest at 0x1fffbd15 (img at: 0x1000 size: 77448)
Verifying image digest
riotboot: verifying digest at 0x1fffbd15 (img at: 0x1000 size: 77448)
suit_parse() success
SUIT policy check OK.
suit_coap: finalizing image flash
riotboot_flashwrite: riotboot flashing completed successfully
Image magic_number: 0x544f4952
Image Version: 0x5e71f662
Image start address: 0x00001100
Header chksum: 0x745a0376
main(): This is RIOT! (Version: 2019.04-devel-606-gaa7b-ota_suit_v2)
main(): This is RIOT! (Version: 2020.04)
RIOT SUIT update example application
running from slot 1
Waiting for address autoconfiguration...
The slot number should have changed from after the application reboots.
You can do the publish-notify sequence several times to verify this.
@ -466,11 +494,9 @@ For the suit_update to work there are important modules that aren't normally bui
in a RIOT application:
* riotboot
* riotboot_hdr
* riotboot_slot
* riotboot_flashwrite
* suit
* suit_coap
* suit_v4
* suit_transport_coap
#### riotboot
@ -492,21 +518,21 @@ The flash memory will be divided in the following way:
The riotboot part of the flash will not be changed during suit_updates but
be flashed a first time with at least one slot with suit_capable fw.
$ BOARD=samr21-xpro make -C examples/suit_update clean riotboot/flash
$ BOARD=samr21-xpro make -C examples/suit_update clean flash
When calling make with the riotboot/flash argument it will flash the bootloader
When calling make with the `flash` argument it will flash the bootloader
and then to slot0 a copy of the firmware you intend to build.
New images must be of course written to the inactive slot, the device mist be able
to boot from the previous image in case the update had some kind of error, eg:
the image corresponds to the wrong slot.
The active/inactive coap resources is used so the publisher can send a manifest
built for the inactive slot.
On boot the bootloader will check the riotboot_hdr and boot on the newest
On boot the bootloader will check the `riotboot_hdr` and boot on the newest
image.
`riotboot_flashwrite` module is needed to be able to write the new firmware to
the inactive slot.
riotboot is not supported by all boards. The default board is `samr21-xpro`,
but any board supporting `riotboot`, `flashpage` and with 256kB of flash should
be able to run the demo.
@ -516,7 +542,7 @@ be able to run the demo.
The suit module encloses all the other suit_related module. Formally this only
includes the `sys/suit` directory into the build system dirs.
- **suit_coap**
- **suit_transport_coap**
To enable support for suit_updates over coap a new thread is created.
This thread will expose 4 suit related resources:
@ -533,9 +559,9 @@ When a new manifest url is received on the trigger resource a message is resent
to the coap thread with the manifest's url. The thread will then fetch the
manifest by a block coap request to the specified url.
- **support for v4**
- **support for v3**
This includes v4 manifest support. When a url is received in the /suit/trigger
This includes v3 manifest support. When a url is received in the /suit/trigger
coap resource it will trigger a coap blockwise fetch of the manifest. When this
manifest is received it will be parsed. The signature of the manifest will be
verified and then the rest of the manifest content. If the received manifest is valid it
@ -610,20 +636,19 @@ The following variables are defined in makefiles/suit.inc.mk:
The following convention is used when naming a manifest
SUIT_MANIFEST ?= $(BINDIR_APP)-riot.suitv4.$(APP_VER).bin
SUIT_MANIFEST_LATEST ?= $(BINDIR_APP)-riot.suitv4.latest.bin
SUIT_MANIFEST_SIGNED ?= $(BINDIR_APP)-riot.suitv4_signed.$(APP_VER).bin
SUIT_MANIFEST_SIGNED_LATEST ?= $(BINDIR_APP)-riot.suitv4_signed.latest.bin
SUIT_MANIFEST ?= $(BINDIR_APP)-riot.suitv3.$(APP_VER).bin
SUIT_MANIFEST_LATEST ?= $(BINDIR_APP)-riot.suitv3.latest.bin
SUIT_MANIFEST_SIGNED ?= $(BINDIR_APP)-riot.suitv3_signed.$(APP_VER).bin
SUIT_MANIFEST_SIGNED_LATEST ?= $(BINDIR_APP)-riot.suitv3_signed.latest.bin
The following default values are using for generating the manifest:
SUIT_VENDOR ?= RIOT
SUIT_VERSION ?= $(APP_VER)
SUIT_VENDOR ?= "riot-os.org"
SUIT_SEQNR ?= $(APP_VER)
SUIT_CLASS ?= $(BOARD)
SUIT_KEY ?= default
SUIT_KEY_DIR ?= $(RIOTBASE)/keys
SUIT_SEC ?= $(SUIT_KEY_DIR)/$(SUIT_KEY)
SUIT_PUB ?= $(SUIT_KEY_DIR)/$(SUIT_KEY).pub
SUIT_SEC ?= $(SUIT_KEY_DIR)/$(SUIT_KEY).pem
All files (both slot binaries, both manifests, copies of manifests with
"latest" instead of `$APP_VER` in riotboot build) are copied into the folder
@ -640,7 +665,7 @@ The following recipes are defined in makefiles/suit.inc.mk:
suit/manifest: creates a non signed and signed manifest, and also a latest tag for these.
It uses following parameters:
- $(SUIT_KEY): name of keypair to sign the manifest
- $(SUIT_KEY): name of key to sign the manifest
- $(SUIT_COAP_ROOT): coap root address
- $(SUIT_CLASS)
- $(SUIT_VERSION)

View File

@ -11,7 +11,7 @@
#include <string.h>
#include "net/nanocoap.h"
#include "suit/coap.h"
#include "suit/transport/coap.h"
static ssize_t _riot_board_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void *context)
{

View File

@ -26,7 +26,7 @@
#include "shell.h"
#include "suit/coap.h"
#include "suit/transport/coap.h"
#include "riotboot/slot.h"
#ifdef MODULE_PERIPH_GPIO

View File

@ -138,7 +138,7 @@ def _test_invalid_version(child, client, app_ver):
publish(TMPDIR.name, COAP_HOST, app_ver - 1)
notify(COAP_HOST, client, app_ver - 1)
child.expect_exact("suit_coap: trigger received")
child.expect_exact("suit: verifying manifest signature...")
child.expect_exact("suit: verifying manifest signature")
child.expect_exact("seq_nr <= running image")
@ -146,7 +146,7 @@ def _test_invalid_signature(child, client, app_ver):
publish(TMPDIR.name, COAP_HOST, app_ver + 1, 'invalid_keys')
notify(COAP_HOST, client, app_ver + 1)
child.expect_exact("suit_coap: trigger received")
child.expect_exact("suit: verifying manifest signature...")
child.expect_exact("suit: verifying manifest signature")
child.expect_exact("Unable to validate signature")
@ -156,7 +156,7 @@ def _test_successful_update(child, client, app_ver):
publish(TMPDIR.name, COAP_HOST, version)
notify(COAP_HOST, client, version)
child.expect_exact("suit_coap: trigger received")
child.expect_exact("suit: verifying manifest signature...")
child.expect_exact("suit: verifying manifest signature")
child.expect(
r"riotboot_flashwrite: initializing update to target slot (\d+)\r\n",
timeout=MANIFEST_TIMEOUT,

View File

@ -127,12 +127,12 @@ extern const size_t suit_global_handlers_len;
/**
* @brief SUIT sequence handler reference
*/
extern const suit_manifest_handler_t suit_sequence_handlers[];
extern const suit_manifest_handler_t suit_command_sequence_handlers[];
/**
* @brief SUIT sequence handler length
*/
extern const size_t suit_sequence_handlers_len;
extern const size_t suit_command_sequence_handlers_len;
/**
* @brief SUIT container handlers reference
@ -185,7 +185,7 @@ int suit_handle_manifest_structure(suit_manifest_t *manifest,
* @param handlers Array of SUIT manifest handlers to use
* @param handlers_len Length of the SUIT manifest handlers
*
* @returns SUIT_OK if all handlers executed succesfully
* @returns SUIT_OK if all handlers executed successfully
* @returns negative on error, see @ref suit_v3_error_t
*/
int suit_handle_manifest_structure_bstr(suit_manifest_t *manifest,

View File

@ -12,7 +12,10 @@
*
* @file
* @brief SUIT Handlers for the command sequences in the common section of
* a SUIT manifest
* a SUIT manifest.
*
* This file contains the functions to handle command sequences from a SUIT
* manifest. This includes both directives and conditions.
*
* @author Koen Zandberg <koen@bergzand.net>
*
@ -134,8 +137,8 @@ static int _dtv_run_seq_cond(suit_manifest_t *manifest,
(void)key;
LOG_DEBUG("Starting conditional sequence handler\n");
return suit_handle_manifest_structure_bstr(manifest, it,
suit_sequence_handlers,
suit_sequence_handlers_len);
suit_command_sequence_handlers,
suit_command_sequence_handlers_len);
}
static int _dtv_try_each(suit_manifest_t *manifest,
@ -156,8 +159,8 @@ static int _dtv_try_each(suit_manifest_t *manifest,
/* `_container` should be CBOR _bstr wrapped according to the spec, but
* it is not */
res = suit_handle_manifest_structure(manifest, &_container,
suit_sequence_handlers,
suit_sequence_handlers_len);
suit_command_sequence_handlers,
suit_command_sequence_handlers_len);
nanocbor_skip(&container);
@ -321,7 +324,7 @@ static int _dtv_verify_image_match(suit_manifest_t *manifest, int key,
}
/* begin{code-style-ignore} */
const suit_manifest_handler_t suit_sequence_handlers[] = {
const suit_manifest_handler_t suit_command_sequence_handlers[] = {
[SUIT_COND_VENDOR_ID] = _cond_vendor_handler,
[SUIT_COND_CLASS_ID] = _cond_class_handler,
[SUIT_COND_IMAGE_MATCH] = _dtv_verify_image_match,
@ -335,4 +338,4 @@ const suit_manifest_handler_t suit_sequence_handlers[] = {
};
/* end{code-style-ignore} */
const size_t suit_sequence_handlers_len = ARRAY_SIZE(suit_sequence_handlers);
const size_t suit_command_sequence_handlers_len = ARRAY_SIZE(suit_command_sequence_handlers);

View File

@ -11,7 +11,10 @@
* @{
*
* @file
* @brief SUIT handler implementations for the Common manifest sections
* @brief SUIT handler implementations for the manifest Common sections
*
* This file contains functions to handle the common info sections of a manifest.
* This includes components, dependencies and command sequences.
*
* @author Koen Zandberg <koen@bergzand.net>
*
@ -92,8 +95,8 @@ int _common_sequence_handler(suit_manifest_t *manifest, int key,
(void)key;
LOG_DEBUG("Starting conditional sequence handler\n");
return suit_handle_manifest_structure_bstr(manifest, it,
suit_sequence_handlers,
suit_sequence_handlers_len);
suit_command_sequence_handlers,
suit_command_sequence_handlers_len);
}
/* begin{code-style-ignore} */

View File

@ -11,7 +11,10 @@
* @{
*
* @file
* @brief SUIT handlers for the SUIT envelope container
* @brief SUIT handlers for the SUIT outer wrapper
*
* This file contains the handlers for the content of the SUIT outer wrapper.
* This includes the authentication wrapper and the manifest itself.
*
* @author Koen Zandberg <koen@bergzand.net>
*

View File

@ -13,6 +13,8 @@
* @file
* @brief SUIT handlers for the global SUIT manifest content.
*
* This file includes the implementation of the SUIT manifest content.
*
* @author Koen Zandberg <koen@bergzand.net>
*
* @}