sys/suit: Add SUIT draft ietf-v3 firmware upgrade module
Co-authored-by: Kaspar Schleiser <kaspar@schleiser.de>
This commit is contained in:
parent
009a317b14
commit
14bdf8f46b
20
Makefile.dep
20
Makefile.dep
@ -979,25 +979,29 @@ ifneq (,$(filter sock_dtls, $(USEMODULE)))
|
||||
USEMODULE += sock_udp
|
||||
endif
|
||||
|
||||
ifneq (,$(filter suit_v4_%,$(USEMODULE)))
|
||||
USEMODULE += suit_v4
|
||||
endif
|
||||
|
||||
ifneq (,$(filter suit_v4,$(USEMODULE)))
|
||||
ifneq (,$(filter suit,$(USEMODULE)))
|
||||
USEPKG += nanocbor
|
||||
USEPKG += libcose
|
||||
USEMODULE += libcose_crypt_c25519
|
||||
USEMODULE += suit_conditions
|
||||
USEMODULE += uuid
|
||||
|
||||
# tests/suit_manifest has some mock implementations,
|
||||
# only add the non-mock dependencies if not building that test.
|
||||
ifeq (,$(filter suit_transport_mock,$(USEMODULE)))
|
||||
# SUIT depends on riotboot support and some extra riotboot modules
|
||||
FEATURES_REQUIRED += riotboot
|
||||
USEMODULE += riotboot_slot
|
||||
USEMODULE += riotboot_flashwrite
|
||||
USEMODULE += riotboot_flashwrite_verify_sha256
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(filter suit_conditions,$(USEMODULE)))
|
||||
USEMODULE += uuid
|
||||
ifneq (,$(filter suit_transport_%, $(USEMODULE)))
|
||||
USEMODULE += suit_transport
|
||||
endif
|
||||
|
||||
ifneq (,$(filter suit_transport_coap, $(USEMODULE)))
|
||||
USEMODULE += nanocoap
|
||||
endif
|
||||
|
||||
ifneq (,$(filter suit_%,$(USEMODULE)))
|
||||
|
||||
@ -480,6 +480,10 @@ ELFFILE ?= $(BINDIR)/$(APPLICATION).elf
|
||||
HEXFILE ?= $(ELFFILE:.elf=.hex)
|
||||
BINFILE ?= $(ELFFILE:.elf=.bin)
|
||||
|
||||
|
||||
# include basic suit-tool and key handling
|
||||
include $(RIOTMAKE)/suit.v3.base.inc.mk
|
||||
|
||||
# include bootloaders support. It should be included early to allow using
|
||||
# variables defined in `riotboot.mk` for `FLASHFILE` before it is evaluated.
|
||||
# It should be included after defining 'BINFILE' for 'riotboot.bin' handling.
|
||||
|
||||
@ -146,8 +146,8 @@ riotboot/flash: riotboot/flash-slot0 riotboot/flash-bootloader
|
||||
FLASHFILE = $(RIOTBOOT_EXTENDED_BIN)
|
||||
|
||||
# include suit targets
|
||||
ifneq (,$(filter suit_v4, $(USEMODULE)))
|
||||
include $(RIOTMAKE)/suit.v4.inc.mk
|
||||
ifneq (,$(filter suit, $(USEMODULE)))
|
||||
include $(RIOTMAKE)/suit.inc.mk
|
||||
endif
|
||||
|
||||
else
|
||||
|
||||
@ -91,13 +91,10 @@ PSEUDOMODULES += stdin
|
||||
PSEUDOMODULES += stdio_ethos
|
||||
PSEUDOMODULES += stdio_cdc_acm
|
||||
PSEUDOMODULES += stdio_uart_rx
|
||||
PSEUDOMODULES += suit_%
|
||||
PSEUDOMODULES += suit_transport_%
|
||||
PSEUDOMODULES += wakaama_objects_%
|
||||
PSEUDOMODULES += zptr
|
||||
|
||||
# handle suit_v4 being a distinct module
|
||||
NO_PSEUDOMODULES += suit_v4
|
||||
|
||||
# print ascii representation in function od_hex_dump()
|
||||
PSEUDOMODULES += od_string
|
||||
|
||||
|
||||
40
makefiles/suit.v3.base.inc.mk
Normal file
40
makefiles/suit.v3.base.inc.mk
Normal file
@ -0,0 +1,40 @@
|
||||
#
|
||||
# path to suit-tool
|
||||
SUIT_TOOL ?= $(RIOTBASE)/dist/tools/suit_v3/suit-manifest-generator/bin/suit-tool
|
||||
|
||||
#
|
||||
# SUIT encryption keys
|
||||
#
|
||||
|
||||
# Specify key to use.
|
||||
# Will use $(SUIT_KEY_DIR)/$(SUIT_KEY).pem as combined private/public key
|
||||
# files.
|
||||
SUIT_KEY ?= default
|
||||
|
||||
ifeq (1, $(RIOT_CI_BUILD))
|
||||
SUIT_KEY_DIR ?= $(BINDIR)
|
||||
else
|
||||
SUIT_KEY_DIR ?= $(RIOTBASE)/keys
|
||||
endif
|
||||
|
||||
SUIT_SEC ?= $(SUIT_KEY_DIR)/$(SUIT_KEY).pem
|
||||
|
||||
SUIT_PUB_HDR = $(BINDIR)/riotbuild/public_key.h
|
||||
SUIT_PUB_HDR_DIR = $(dir $(SUIT_PUB_HDR))
|
||||
CFLAGS += -I$(SUIT_PUB_HDR_DIR)
|
||||
BUILDDEPS += $(SUIT_PUB_HDR)
|
||||
|
||||
$(SUIT_SEC): $(CLEAN)
|
||||
@echo suit: generating key in $(SUIT_KEY_DIR)
|
||||
@mkdir -p $(SUIT_KEY_DIR)
|
||||
@$(RIOTBASE)/dist/tools/suit_v3/gen_key.py $(SUIT_SEC)
|
||||
|
||||
# set FORCE so switching between keys using "SUIT_KEY=foo make ..."
|
||||
# triggers a rebuild even if the new key would otherwise not (because the other
|
||||
# key's mtime is too far back).
|
||||
$(SUIT_PUB_HDR): $(SUIT_SEC) FORCE | $(CLEAN)
|
||||
@mkdir -p $(SUIT_PUB_HDR_DIR)
|
||||
@$(SUIT_TOOL) pubkey -k $(SUIT_SEC) \
|
||||
| '$(LAZYSPONGE)' $(LAZYSPONGE_FLAGS) '$@'
|
||||
|
||||
suit/genkey: $(SUIT_SEC)
|
||||
72
makefiles/suit.v3.inc.mk
Normal file
72
makefiles/suit.v3.inc.mk
Normal file
@ -0,0 +1,72 @@
|
||||
#
|
||||
# This file contains stuff related to SUIT manifest generation.
|
||||
# It depends on SUIT key generation, which can be found in
|
||||
# makefiles/suit.v3.base.inc.mk
|
||||
#
|
||||
#
|
||||
SUIT_COAP_BASEPATH ?= fw/$(BOARD)
|
||||
SUIT_COAP_SERVER ?= localhost
|
||||
SUIT_COAP_ROOT ?= coap://$(SUIT_COAP_SERVER)/$(SUIT_COAP_BASEPATH)
|
||||
SUIT_COAP_FSROOT ?= $(RIOTBASE)/coaproot
|
||||
|
||||
#
|
||||
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
|
||||
|
||||
SUIT_NOTIFY_VERSION ?= latest
|
||||
SUIT_NOTIFY_MANIFEST ?= $(APPLICATION)-riot.suitv3_signed.$(SUIT_NOTIFY_VERSION).bin
|
||||
|
||||
# Long manifest names require more buffer space when parsing
|
||||
export CFLAGS += -DCONFIG_SOCK_URLPATH_MAXLEN=128
|
||||
|
||||
SUIT_VENDOR ?= "riot-os.org"
|
||||
SUIT_SEQNR ?= $(APP_VER)
|
||||
SUIT_CLASS ?= $(BOARD)
|
||||
|
||||
#
|
||||
$(SUIT_MANIFEST): $(SLOT0_RIOT_BIN) $(SLOT1_RIOT_BIN)
|
||||
$(RIOTBASE)/dist/tools/suit_v3/gen_manifest.py \
|
||||
--urlroot $(SUIT_COAP_ROOT) \
|
||||
--seqnr $(SUIT_SEQNR) \
|
||||
--uuid-vendor $(SUIT_VENDOR) \
|
||||
--uuid-class $(SUIT_CLASS) \
|
||||
-o $@.tmp \
|
||||
$(SLOT0_RIOT_BIN):$(SLOT0_OFFSET) \
|
||||
$(SLOT1_RIOT_BIN):$(SLOT1_OFFSET)
|
||||
|
||||
$(SUIT_TOOL) create -f suit -i $@.tmp -o $@
|
||||
|
||||
rm -f $@.tmp
|
||||
|
||||
|
||||
$(SUIT_MANIFEST_SIGNED): $(SUIT_MANIFEST) $(SUIT_SEC)
|
||||
$(SUIT_TOOL) sign -k $(SUIT_SEC) -m $(SUIT_MANIFEST) -o $@
|
||||
|
||||
$(SUIT_MANIFEST_LATEST): $(SUIT_MANIFEST)
|
||||
@ln -f -s $< $@
|
||||
|
||||
$(SUIT_MANIFEST_SIGNED_LATEST): $(SUIT_MANIFEST_SIGNED)
|
||||
@ln -f -s $< $@
|
||||
|
||||
SUIT_MANIFESTS := $(SUIT_MANIFEST) \
|
||||
$(SUIT_MANIFEST_LATEST) \
|
||||
$(SUIT_MANIFEST_SIGNED) \
|
||||
$(SUIT_MANIFEST_SIGNED_LATEST)
|
||||
|
||||
suit/manifest: $(SUIT_MANIFESTS)
|
||||
|
||||
suit/publish: $(SUIT_MANIFESTS) $(SLOT0_RIOT_BIN) $(SLOT1_RIOT_BIN)
|
||||
@mkdir -p $(SUIT_COAP_FSROOT)/$(SUIT_COAP_BASEPATH)
|
||||
@cp $^ $(SUIT_COAP_FSROOT)/$(SUIT_COAP_BASEPATH)
|
||||
@for file in $^; do \
|
||||
echo "published \"$$file\""; \
|
||||
echo " as \"$(SUIT_COAP_ROOT)/$$(basename $$file)\""; \
|
||||
done
|
||||
|
||||
suit/notify: | $(filter suit/publish, $(MAKECMDGOALS))
|
||||
@test -n "$(SUIT_CLIENT)" || { echo "error: SUIT_CLIENT unset!"; false; }
|
||||
aiocoap-client -m POST "coap://$(SUIT_CLIENT)/suit/trigger" \
|
||||
--payload "$(SUIT_COAP_ROOT)/$(SUIT_NOTIFY_MANIFEST)" && \
|
||||
echo "Triggered $(SUIT_CLIENT) to update."
|
||||
207
sys/include/suit.h
Normal file
207
sys/include/suit.h
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Koen Zandberg
|
||||
* 2019 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
/**
|
||||
* @defgroup sys_suit_v3 SUIT IETF draft v3
|
||||
* @ingroup sys_suit
|
||||
* @brief SUIT manifest handling
|
||||
*
|
||||
* @see https://tools.ietf.org/html/draft-ietf-suit-manifest-03
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @brief Handler functions for SUIT manifests
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SUIT_V3_SUIT_H
|
||||
#define SUIT_V3_SUIT_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cose/sign.h"
|
||||
#include "nanocbor/nanocbor.h"
|
||||
#include "uuid.h"
|
||||
#include "riotboot/flashwrite.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Buffer size used for Cose
|
||||
*/
|
||||
#ifndef SUIT_COSE_BUF_SIZE
|
||||
#define SUIT_COSE_BUF_SIZE (180U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Maximum number of components supported in a SUIT manifest
|
||||
*/
|
||||
#define SUIT_V3_COMPONENT_MAX (1U)
|
||||
|
||||
/**
|
||||
* @brief Current SUIT serialization format version
|
||||
*
|
||||
* see https://tools.ietf.org/html/draft-ietf-suit-manifest-03#section-7 for
|
||||
* details
|
||||
*/
|
||||
#define SUIT_VERSION (1)
|
||||
|
||||
/**
|
||||
* @brief COSE signature OK
|
||||
*/
|
||||
#define SUIT_STATE_COSE_AUTHENTICATED (1 << 1)
|
||||
|
||||
/**
|
||||
* @brief COSE payload matches SUIT manifest digest
|
||||
*/
|
||||
#define SUIT_STATE_FULLY_AUTHENTICATED (1 << 2)
|
||||
|
||||
/**
|
||||
* @brief SUIT error codes
|
||||
*/
|
||||
typedef enum {
|
||||
SUIT_OK = 0, /**< Manifest parsed and validated */
|
||||
SUIT_ERR_INVALID_MANIFEST = -1, /**< Unexpected CBOR structure detected */
|
||||
SUIT_ERR_UNSUPPORTED = -2, /**< Unsupported SUIT feature detected */
|
||||
SUIT_ERR_NOT_SUPPORTED = -3, /**< Unsupported features detected */
|
||||
SUIT_ERR_COND = -4, /**< Conditionals evaluate to false */
|
||||
SUIT_ERR_SEQUENCE_NUMBER = -5, /**< Sequence number less or equal to
|
||||
current sequence number */
|
||||
SUIT_ERR_SIGNATURE = -6, /**< Unable to verify signature */
|
||||
SUIT_ERR_DIGEST_MISMATCH = -7, /**< Digest mismatch with COSE and SUIT */
|
||||
} suit_error_t;
|
||||
|
||||
/**
|
||||
* @brief SUIT payload digest algorithms
|
||||
*
|
||||
* Unofficial list from
|
||||
* [suit-manifest-generator](https://github.com/ARMmbed/suit-manifest-generator)
|
||||
*/
|
||||
typedef enum {
|
||||
SUIT_DIGEST_NONE = 0, /**< No digest algo supplied */
|
||||
SUIT_DIGEST_SHA256 = 1, /**< SHA256 */
|
||||
SUIT_DIGEST_SHA384 = 2, /**< SHA384 */
|
||||
SUIT_DIGEST_SHA512 = 3, /**< SHA512 */
|
||||
} suit_digest_t;
|
||||
|
||||
/**
|
||||
* @brief SUIT payload digest types
|
||||
*
|
||||
* Unofficial list from
|
||||
* [suit-manifest-generator](https://github.com/ARMmbed/suit-manifest-generator)
|
||||
*/
|
||||
typedef enum {
|
||||
SUIT_DIGEST_TYPE_RAW = 1, /**< Raw payload digest */
|
||||
SUIT_DIGEST_TYPE_INSTALLED = 2, /**< Installed firmware digest */
|
||||
SUIT_DIGEST_TYPE_CIPHERTEXT = 3, /**< Ciphertext digest */
|
||||
SUIT_DIGEST_TYPE_PREIMAGE = 4 /**< Pre-image digest */
|
||||
} suit_digest_type_t;
|
||||
|
||||
/**
|
||||
* @brief SUIT component types
|
||||
*
|
||||
* Unofficial list from
|
||||
* [suit-manifest-generator](https://github.com/ARMmbed/suit-manifest-generator)
|
||||
*/
|
||||
enum {
|
||||
SUIT_COMPONENT_IDENTIFIER = 1, /**< Identifier component */
|
||||
SUIT_COMPONENT_SIZE = 2, /**< Size component */
|
||||
SUIT_COMPONENT_DIGEST = 3, /**< Digest component */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief SUIT component struct
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t size; /**< Size */
|
||||
nanocbor_value_t identifier; /**< Identifier */
|
||||
nanocbor_value_t url; /**< Url */
|
||||
nanocbor_value_t digest; /**< Digest */
|
||||
} suit_component_t;
|
||||
|
||||
/**
|
||||
* @brief SUIT manifest struct
|
||||
*/
|
||||
typedef struct {
|
||||
const uint8_t *buf; /**< ptr to the buffer of the manifest */
|
||||
size_t len; /**< length of the manifest */
|
||||
const uint8_t *cose_payload; /**< ptr to the payload of the COSE sign */
|
||||
size_t cose_payload_len; /**< length of the COSE payload */
|
||||
uint32_t validated; /**< bitfield of validated policies */
|
||||
uint32_t state; /**< bitfield holding state information */
|
||||
/** List of components in the manifest */
|
||||
suit_component_t components[SUIT_V3_COMPONENT_MAX];
|
||||
unsigned components_len; /**< Current number of components */
|
||||
uint32_t component_current; /**< Current component index */
|
||||
riotboot_flashwrite_t *writer; /**< Pointer to the riotboot flash writer */
|
||||
/** Manifest validation buffer */
|
||||
uint8_t validation_buf[SUIT_COSE_BUF_SIZE];
|
||||
char *urlbuf; /**< Buffer containing the manifest url */
|
||||
size_t urlbuf_len; /**< Length of the manifest url */
|
||||
} suit_manifest_t;
|
||||
|
||||
/**
|
||||
* @brief Bit flags used to determine if SUIT manifest contains components
|
||||
*/
|
||||
#define SUIT_MANIFEST_HAVE_COMPONENTS (0x1)
|
||||
/**
|
||||
* @brief Bit flags used to determine if SUIT manifest contains an image
|
||||
*/
|
||||
#define SUIT_MANIFEST_HAVE_IMAGE (0x2)
|
||||
|
||||
/**
|
||||
* @brief Parse a manifest
|
||||
*
|
||||
* @note The buffer is still required after parsing, please don't reuse the
|
||||
* buffer while the @p manifest is used
|
||||
*
|
||||
* @param[in] manifest manifest context to store information in
|
||||
* @param[in] buf buffer to parse the manifest from
|
||||
* @param[in] len length of the manifest data in the buffer
|
||||
*
|
||||
* @return SUIT_OK on parseable manifest
|
||||
* @return negative @ref suit_error_t code on error
|
||||
*/
|
||||
int suit_parse(suit_manifest_t *manifest, const uint8_t *buf, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Check a manifest policy
|
||||
*
|
||||
* @param[in] manifest manifest context to check the policy for
|
||||
*
|
||||
* @return 0 on valid manifest policy
|
||||
* @return -1 on invalid manifest policy
|
||||
*/
|
||||
int suit_policy_check(suit_manifest_t *manifest);
|
||||
|
||||
/**
|
||||
* @brief Helper function for writing bytes on flash a specified offset
|
||||
*
|
||||
* @param[in] arg ptr to the SUIT manifest
|
||||
* @param[in] offset offset to write to on flash
|
||||
* @param[in] buf bytes to write
|
||||
* @param[in] len length of bytes to write
|
||||
* @param[in] more whether more data is coming
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int suit_flashwrite_helper(void *arg, size_t offset, uint8_t *buf, size_t len,
|
||||
int more);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SUIT_V3_SUIT_H */
|
||||
/** @} */
|
||||
201
sys/include/suit/handlers.h
Normal file
201
sys/include/suit/handlers.h
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Koen Zandberg
|
||||
* 2019 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* 2019 Inria
|
||||
* 2019 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
/**
|
||||
* @ingroup sys_suit_v3
|
||||
* @brief SUIT draft-ietf-suit-manifest-03 manifest handlers
|
||||
*
|
||||
* @experimental
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @brief Handler functions for SUIT manifests
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*/
|
||||
|
||||
#ifndef SUIT_V3_HANDLERS_H
|
||||
#define SUIT_V3_HANDLERS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "suit.h"
|
||||
#include "uuid.h"
|
||||
#include "nanocbor/nanocbor.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name SUIT outer wrapper identifiers
|
||||
* @{
|
||||
*/
|
||||
#define SUIT_WRAPPER_AUTHENTICATION (2)
|
||||
#define SUIT_WRAPPER_MANIFEST (3)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name SUIT container identifiers
|
||||
* @{
|
||||
*/
|
||||
#define SUIT_CONTAINER_VERSION (1)
|
||||
#define SUIT_CONTAINER_SEQ_NO (2)
|
||||
#define SUIT_CONTAINER_COMMON (3)
|
||||
#define SUIT_CONTAINER_DEPS_RESOLUTION (7)
|
||||
#define SUIT_CONTAINER_PAYLOAD_FETCH (8)
|
||||
#define SUIT_CONTAINER_INSTALL (9)
|
||||
#define SUIT_CONTAINER_VALIDATE (10)
|
||||
#define SUIT_CONTAINER_LOAD (11)
|
||||
#define SUIT_CONTAINER_RUN (12)
|
||||
#define SUIT_CONTAINER_TEXT (13)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name SUIT common section identifiers
|
||||
* @{
|
||||
*/
|
||||
#define SUIT_COMMON_DEPENDENCIES (1)
|
||||
#define SUIT_COMMON_COMPONENTS (2)
|
||||
#define SUIT_COMMON_DEP_COMPONENTS (3)
|
||||
#define SUIT_COMMON_COMMAND_SEQUENCE (4)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name SUIT condition identifiers
|
||||
* @{
|
||||
*/
|
||||
#define SUIT_COND_VENDOR_ID (1)
|
||||
#define SUIT_COND_CLASS_ID (2)
|
||||
#define SUIT_COND_IMAGE_MATCH (3)
|
||||
#define SUIT_COND_USE_BEFORE (4)
|
||||
#define SUIT_COND_COMPONENT_OFFSET (5)
|
||||
#define SUIT_COND_DEVICE_ID (24)
|
||||
#define SUIT_COND_IMAGE_NOT_MATCH (25)
|
||||
#define SUIT_COND_MIN_BATTERY (26)
|
||||
#define SUIT_COND_UPDATE_AUTHZ (27)
|
||||
#define SUIT_COND_VERSION (28)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name SUIT directive identifiers
|
||||
* @{
|
||||
*/
|
||||
#define SUIT_DIR_SET_COMPONENT_IDX (12)
|
||||
#define SUIT_DIR_SET_DEPENDENCY_IDX (13)
|
||||
#define SUIT_DIR_ABORT (14)
|
||||
#define SUIT_DIR_TRY_EACH (15)
|
||||
#define SUIT_DIR_PROCESS_DEPS (18)
|
||||
#define SUIT_DIR_SET_PARAM (19)
|
||||
#define SUIT_DIR_OVERRIDE_PARAM (20)
|
||||
#define SUIT_DIR_FETCH (21)
|
||||
#define SUIT_DIR_COPY (22)
|
||||
#define SUIT_DIR_RUN (23)
|
||||
#define SUIT_DIR_WAIT (29)
|
||||
#define SUIT_DIR_RUN_SEQUENCE (30)
|
||||
#define SUIT_DIR_RUN_WITH_ARGS (31)
|
||||
#define SUIT_DIR_SWAP (32)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief suit handler prototype
|
||||
*
|
||||
* @param manifest SUIT manifest context
|
||||
* @param key SUIT map index of this content
|
||||
* @param it nanocbor_value_t iterator to the content
|
||||
*
|
||||
* @return SUIT_OK on success
|
||||
* @return negative on error
|
||||
*/
|
||||
typedef int (*suit_manifest_handler_t)(suit_manifest_t *manifest, int key,
|
||||
nanocbor_value_t *it);
|
||||
|
||||
/**
|
||||
* @brief global handler reference
|
||||
*/
|
||||
extern const suit_manifest_handler_t suit_global_handlers[];
|
||||
extern const size_t suit_global_handlers_len;
|
||||
|
||||
/**
|
||||
* @brief SUIT sequence handler reference
|
||||
*/
|
||||
extern const suit_manifest_handler_t suit_sequence_handlers[];
|
||||
|
||||
/**
|
||||
* @brief SUIT sequence handler length
|
||||
*/
|
||||
extern const size_t suit_sequence_handlers_len;
|
||||
|
||||
/**
|
||||
* @brief SUIT container handlers reference
|
||||
*/
|
||||
extern const suit_manifest_handler_t suit_container_handlers[];
|
||||
|
||||
/**
|
||||
* @brief length of the SUIT container handlers
|
||||
*/
|
||||
extern const size_t suit_container_handlers_len;
|
||||
|
||||
/**
|
||||
* @brief SUIT common handlers reference
|
||||
*/
|
||||
extern const suit_manifest_handler_t suit_common_handlers[];
|
||||
|
||||
/**
|
||||
* @brief length of the SUIT common handlers
|
||||
*/
|
||||
extern const size_t suit_common_handlers_len;
|
||||
|
||||
/**
|
||||
* @brief Manifest structure handler function
|
||||
*
|
||||
* Iterates over the supplied nanocbor map or array and calls the manifest
|
||||
* handler function for every key.
|
||||
*
|
||||
* @param manifest SUIT manifest context
|
||||
* @param it Nanocbor map/array element
|
||||
* @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 negative on error, see @ref suit_v3_error_t
|
||||
*/
|
||||
int suit_handle_manifest_structure(suit_manifest_t *manifest,
|
||||
nanocbor_value_t *it,
|
||||
const suit_manifest_handler_t *handlers,
|
||||
size_t handlers_len);
|
||||
|
||||
/**
|
||||
* @brief Byte string wrapped manifest structure handler function
|
||||
*
|
||||
* Extracts the nanocbor byte string and Iterates over the CBOR map or array
|
||||
* contained in the bytestring and calls the manifest handler function for
|
||||
* every key.
|
||||
*
|
||||
* @param manifest SUIT manifest context
|
||||
* @param bseq Nanocbor byte string
|
||||
* @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 negative on error, see @ref suit_v3_error_t
|
||||
*/
|
||||
int suit_handle_manifest_structure_bstr(suit_manifest_t *manifest,
|
||||
nanocbor_value_t *bseq,
|
||||
const suit_manifest_handler_t *handlers,
|
||||
size_t handlers_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SUIT_V3_HANDLERS_H */
|
||||
/** @} */
|
||||
59
sys/include/suit/policy.h
Normal file
59
sys/include/suit/policy.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Koen Zandberg
|
||||
* 2019 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* 2019 Inria
|
||||
* 2019 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
/**
|
||||
* @ingroup sys_suit_v3
|
||||
* @brief SUIT policy definitions
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @brief SUIT policy definitions
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SUIT_V3_POLICY_H
|
||||
#define SUIT_V3_POLICY_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "uuid.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name bitfield of required policies
|
||||
* @{
|
||||
*/
|
||||
#define SUIT_VALIDATED_AUTH 0x1 /**< currently unused */
|
||||
#define SUIT_VALIDATED_VERSION 0x2 /**< SUIT format version */
|
||||
#define SUIT_VALIDATED_SEQ_NR 0x4 /**< new seq nr > old seq nr */
|
||||
#define SUIT_VALIDATED_VENDOR 0x8 /**< vendor UUID matches */
|
||||
#define SUIT_VALIDATED_CLASS 0x10 /**< class UUID matches */
|
||||
#define SUIT_VALIDATED_DEVICE 0x20 /**< device UUID matches */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief SUIT default policy
|
||||
*/
|
||||
#define SUIT_DEFAULT_POLICY \
|
||||
(SUIT_VALIDATED_VERSION | SUIT_VALIDATED_SEQ_NR | SUIT_VALIDATED_VENDOR | \
|
||||
SUIT_VALIDATED_CLASS)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SUIT_V3_POLICY_H */
|
||||
/** @} */
|
||||
@ -22,7 +22,7 @@
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "suit/coap.h"
|
||||
#include "suit/transport/coap.h"
|
||||
|
||||
|
||||
int _suit_handler(int argc, char **argv)
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
SUBMODULES := 1
|
||||
|
||||
# don't complain about missing submodule .c file.
|
||||
# necessary to not fail for suit_v*_*.
|
||||
SUBMODULES_NOFORCE := 1
|
||||
|
||||
DIRS += v4
|
||||
ifneq (,$(filter suit_transport_%,$(USEMODULE)))
|
||||
DIRS += transport
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
|
||||
97
sys/suit/handlers.c
Normal file
97
sys/suit/handlers.c
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Koen Zandberg
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
/**
|
||||
* @ingroup sys_suit_v3
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief SUIT draft-ietf-suit-manifest-03 content handler helper
|
||||
* functions
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <nanocbor/nanocbor.h>
|
||||
|
||||
#include "suit/handlers.h"
|
||||
#include "suit.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
static suit_manifest_handler_t _get_handler(int key,
|
||||
const suit_manifest_handler_t *map,
|
||||
size_t len)
|
||||
{
|
||||
if (key < 0 || (size_t)key >= len) {
|
||||
return NULL;
|
||||
}
|
||||
return map[key];
|
||||
}
|
||||
|
||||
int suit_handle_manifest_structure(suit_manifest_t *manifest,
|
||||
nanocbor_value_t *it,
|
||||
const suit_manifest_handler_t *handlers,
|
||||
size_t handlers_len)
|
||||
{
|
||||
LOG_DEBUG("Handling command sequence\n");
|
||||
nanocbor_value_t container;
|
||||
|
||||
if ((nanocbor_enter_array(it, &container) < 0) &&
|
||||
(nanocbor_enter_map(it, &container) < 0)) {
|
||||
LOG_DEBUG("Neither array nor map: %d\n", nanocbor_get_type(it));
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
|
||||
while (!nanocbor_at_end(&container)) {
|
||||
int32_t key;
|
||||
if (nanocbor_get_int32(&container, &key) < 0) {
|
||||
LOG_DEBUG("No key found: %d\n", nanocbor_get_type(&container));
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
nanocbor_value_t value = container;
|
||||
LOG_DEBUG("Executing handler with key %" PRIi32 "\n", key);
|
||||
suit_manifest_handler_t handler = _get_handler(key, handlers,
|
||||
handlers_len);
|
||||
if (!handler) {
|
||||
return SUIT_ERR_UNSUPPORTED;
|
||||
}
|
||||
int res = handler(manifest, key, &value);
|
||||
if (res < 0) {
|
||||
LOG_DEBUG("Sequence handler error\n");
|
||||
return res;
|
||||
}
|
||||
nanocbor_skip(&container);
|
||||
}
|
||||
nanocbor_leave_container(it, &container);
|
||||
LOG_DEBUG("Leaving sequence handler\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int suit_handle_manifest_structure_bstr(suit_manifest_t *manifest,
|
||||
nanocbor_value_t *bseq,
|
||||
const suit_manifest_handler_t *handlers,
|
||||
size_t handlers_len)
|
||||
{
|
||||
const uint8_t *buf;
|
||||
size_t len;
|
||||
|
||||
LOG_DEBUG("Handling command sequence starting with CBOR type %d\n",
|
||||
nanocbor_get_type(bseq));
|
||||
if (nanocbor_get_bstr(bseq, &buf, &len) < 0) {
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
|
||||
nanocbor_value_t it;
|
||||
nanocbor_decoder_init(&it, buf, len);
|
||||
return suit_handle_manifest_structure(manifest, &it, handlers,
|
||||
handlers_len);
|
||||
}
|
||||
112
sys/suit/handlers_common.c
Normal file
112
sys/suit/handlers_common.c
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Koen Zandberg
|
||||
* 2020 Inria
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
/**
|
||||
* @ingroup sys_suit_v3
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief SUIT draft-ietf-suit-manifest-03 Handler implementations for
|
||||
* the Common manifest sections
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <nanocbor/nanocbor.h>
|
||||
|
||||
#ifdef MODULE_SUIT_COAP
|
||||
#include "suit/coap.h"
|
||||
#endif
|
||||
|
||||
#include "kernel_defines.h"
|
||||
#include "suit/handlers.h"
|
||||
#include "suit.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
static int _component_handler(suit_manifest_t *manifest, int key,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
(void)manifest;
|
||||
(void)key;
|
||||
const uint8_t *subcbor;
|
||||
size_t sub_size;
|
||||
nanocbor_value_t _it;
|
||||
nanocbor_get_bstr(it, &subcbor, &sub_size);
|
||||
nanocbor_decoder_init(&_it, subcbor, sub_size);
|
||||
|
||||
/* This is a list of lists, something like:
|
||||
* [
|
||||
* [ "sda" "firmwareA" ],
|
||||
* [ "sda" "firmwareB" ]
|
||||
* ]
|
||||
* */
|
||||
nanocbor_value_t arr;
|
||||
if (nanocbor_enter_array(&_it, &arr) < 0) {
|
||||
LOG_DEBUG("components field not an array %d\n", nanocbor_get_type(it));
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
unsigned n = 0;
|
||||
while (!nanocbor_at_end(&arr)) {
|
||||
nanocbor_value_t comp;
|
||||
if (nanocbor_enter_array(&arr, &comp) < 0) {
|
||||
LOG_DEBUG("component elements field not an array %d\n",
|
||||
nanocbor_get_type(it));
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
while (!nanocbor_at_end(&comp)) {
|
||||
const uint8_t *identifier;
|
||||
size_t id_len;
|
||||
if (nanocbor_get_bstr(&comp, &identifier, &id_len) < 0) {
|
||||
LOG_DEBUG("Component name not a byte string\n");
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
}
|
||||
nanocbor_leave_container(&arr, &comp);
|
||||
n++;
|
||||
}
|
||||
if (n > 1) {
|
||||
LOG_INFO("More than 1 component found, exiting\n");
|
||||
return SUIT_ERR_UNSUPPORTED;
|
||||
}
|
||||
manifest->state |= SUIT_MANIFEST_HAVE_COMPONENTS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _dependencies_handler(suit_manifest_t *manifest, int key,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
(void)manifest;
|
||||
(void)key;
|
||||
(void)it;
|
||||
/* No dependency support */
|
||||
return SUIT_ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
int _common_sequence_handler(suit_manifest_t *manifest, int key,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
(void)key;
|
||||
LOG_DEBUG("Starting conditional sequence handler\n");
|
||||
return suit_handle_manifest_structure_bstr(manifest, it,
|
||||
suit_sequence_handlers,
|
||||
suit_sequence_handlers_len);
|
||||
}
|
||||
|
||||
/* begin{code-style-ignore} */
|
||||
const suit_manifest_handler_t suit_common_handlers[] = {
|
||||
[SUIT_COMMON_DEPENDENCIES] = _dependencies_handler,
|
||||
[SUIT_COMMON_COMPONENTS] = _component_handler,
|
||||
[SUIT_COMMON_COMMAND_SEQUENCE] = _common_sequence_handler,
|
||||
};
|
||||
/* end{code-style-ignore} */
|
||||
|
||||
const size_t suit_common_handlers_len = ARRAY_SIZE(suit_common_handlers);
|
||||
332
sys/suit/handlers_common_seq.c
Normal file
332
sys/suit/handlers_common_seq.c
Normal file
@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Koen Zandberg
|
||||
* 2020 Inria
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
/**
|
||||
* @ingroup sys_suit_v3
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief SUIT draft-ietf-suit-manifest-03 Handlers for the command
|
||||
* sequences in the common section
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <nanocbor/nanocbor.h>
|
||||
|
||||
#include "kernel_defines.h"
|
||||
#include "suit/coap.h"
|
||||
#include "suit/conditions.h"
|
||||
#include "suit/handlers.h"
|
||||
#include "suit/policy.h"
|
||||
#include "suit.h"
|
||||
#include "riotboot/hdr.h"
|
||||
#include "riotboot/slot.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
static int _validate_uuid(suit_manifest_t *manifest,
|
||||
nanocbor_value_t *it,
|
||||
uuid_t *uuid)
|
||||
{
|
||||
(void)manifest;
|
||||
const uint8_t *uuid_manifest_ptr;
|
||||
size_t len = sizeof(uuid_t);
|
||||
char uuid_str[UUID_STR_LEN + 1];
|
||||
char uuid_str2[UUID_STR_LEN + 1];
|
||||
if (nanocbor_get_bstr(it, &uuid_manifest_ptr, &len) < 0) {
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
|
||||
uuid_to_string((uuid_t *)uuid_manifest_ptr, uuid_str);
|
||||
uuid_to_string(uuid, uuid_str2);
|
||||
LOG_INFO("Comparing %s to %s from manifest\n", uuid_str2, uuid_str);
|
||||
return uuid_equal(uuid, (uuid_t *)uuid_manifest_ptr)
|
||||
? SUIT_OK
|
||||
: SUIT_ERR_COND;
|
||||
}
|
||||
|
||||
static int _cond_vendor_handler(suit_manifest_t *manifest,
|
||||
int key,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
(void)key;
|
||||
LOG_INFO("validating vendor ID\n");
|
||||
int rc = _validate_uuid(manifest, it, suit_get_vendor_id());
|
||||
if (rc == SUIT_OK) {
|
||||
LOG_INFO("validating vendor ID: OK\n");
|
||||
manifest->validated |= SUIT_VALIDATED_VENDOR;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int _cond_class_handler(suit_manifest_t *manifest,
|
||||
int key,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
(void)key;
|
||||
LOG_INFO("validating class id\n");
|
||||
int rc = _validate_uuid(manifest, it, suit_get_class_id());
|
||||
if (rc == SUIT_OK) {
|
||||
LOG_INFO("validating class id: OK\n");
|
||||
manifest->validated |= SUIT_VALIDATED_CLASS;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int _cond_comp_offset(suit_manifest_t *manifest,
|
||||
int key,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
(void)manifest;
|
||||
(void)key;
|
||||
uint32_t offset;
|
||||
|
||||
int rc = nanocbor_get_uint32(it, &offset);
|
||||
if (rc < 0) {
|
||||
LOG_WARNING("_cond_comp_offset(): expected int, got rc=%i type=%i\n",
|
||||
rc, nanocbor_get_type(it));
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
uint32_t other_offset = (uint32_t)riotboot_slot_offset(
|
||||
riotboot_slot_other());
|
||||
|
||||
LOG_INFO("Comparing manifest offset %u with other slot offset %u\n",
|
||||
(unsigned)offset, (unsigned)other_offset);
|
||||
return other_offset == offset ? SUIT_OK : SUIT_ERR_COND;
|
||||
}
|
||||
|
||||
static int _dtv_set_comp_idx(suit_manifest_t *manifest,
|
||||
int key,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
(void)key;
|
||||
if (nanocbor_get_type(it) == NANOCBOR_TYPE_FLOAT) {
|
||||
LOG_DEBUG("_dtv_set_comp_idx() ignoring boolean and floats\n)");
|
||||
nanocbor_skip(it);
|
||||
}
|
||||
else if (nanocbor_get_uint32(it, &manifest->component_current) < 0) {
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
LOG_DEBUG("Setting component index to %d\n",
|
||||
(int)manifest->component_current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _dtv_run_seq_cond(suit_manifest_t *manifest,
|
||||
int key,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
(void)key;
|
||||
LOG_DEBUG("Starting conditional sequence handler\n");
|
||||
return suit_handle_manifest_structure_bstr(manifest, it,
|
||||
suit_sequence_handlers,
|
||||
suit_sequence_handlers_len);
|
||||
}
|
||||
|
||||
static int _dtv_try_each(suit_manifest_t *manifest,
|
||||
int key, nanocbor_value_t *it)
|
||||
{
|
||||
(void)key;
|
||||
LOG_DEBUG("Starting suit-directive-try-each handler\n");
|
||||
nanocbor_value_t container;
|
||||
|
||||
if ((nanocbor_enter_array(it, &container) < 0) &&
|
||||
(nanocbor_enter_map(it, &container) < 0)) {
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
|
||||
int res = SUIT_ERR_COND;
|
||||
while (!nanocbor_at_end(&container)) {
|
||||
nanocbor_value_t _container = container;
|
||||
/* `_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);
|
||||
|
||||
nanocbor_skip(&container);
|
||||
|
||||
if (res != SUIT_ERR_COND) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _param_get_uri_list(suit_manifest_t *manifest,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
LOG_DEBUG("got url list\n");
|
||||
manifest->components[manifest->component_current].url = *it;
|
||||
return 0;
|
||||
}
|
||||
static int _param_get_digest(suit_manifest_t *manifest, nanocbor_value_t *it)
|
||||
{
|
||||
LOG_DEBUG("got digest\n");
|
||||
manifest->components[manifest->component_current].digest = *it;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _param_get_img_size(suit_manifest_t *manifest,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
int res = nanocbor_get_uint32(it, &manifest->components[0].size);
|
||||
|
||||
if (res < 0) {
|
||||
LOG_DEBUG("error getting image size\n");
|
||||
return res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _dtv_set_param(suit_manifest_t *manifest, int key,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
(void)key;
|
||||
/* `it` points to the entry of the map containing the type and value */
|
||||
nanocbor_value_t map;
|
||||
|
||||
nanocbor_enter_map(it, &map);
|
||||
|
||||
while (!nanocbor_at_end(&map)) {
|
||||
/* map points to the key of the param */
|
||||
int32_t param_key;
|
||||
nanocbor_get_int32(&map, ¶m_key);
|
||||
LOG_DEBUG("Setting component index to %" PRIi32 "\n",
|
||||
manifest->component_current);
|
||||
LOG_DEBUG("param_key=%" PRIi32 "\n", param_key);
|
||||
int res;
|
||||
switch (param_key) {
|
||||
case 6: /* SUIT URI LIST */
|
||||
res = _param_get_uri_list(manifest, &map);
|
||||
break;
|
||||
case 11: /* SUIT DIGEST */
|
||||
res = _param_get_digest(manifest, &map);
|
||||
break;
|
||||
case 12: /* SUIT IMAGE SIZE */
|
||||
res = _param_get_img_size(manifest, &map);
|
||||
break;
|
||||
default:
|
||||
LOG_DEBUG("Unsupported parameter %" PRIi32 "\n", param_key);
|
||||
res = SUIT_ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
nanocbor_skip(&map);
|
||||
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return SUIT_OK;
|
||||
}
|
||||
|
||||
static int _dtv_fetch(suit_manifest_t *manifest, int key,
|
||||
nanocbor_value_t *_it)
|
||||
{
|
||||
(void)key; (void)_it;
|
||||
LOG_DEBUG("_dtv_fetch() key=%i\n", key);
|
||||
|
||||
const uint8_t *url;
|
||||
size_t url_len;
|
||||
|
||||
int err = nanocbor_get_tstr(&manifest->components[0].url, &url, &url_len);
|
||||
if (err < 0) {
|
||||
LOG_DEBUG("URL parsing failed\n)");
|
||||
return err;
|
||||
}
|
||||
memcpy(manifest->urlbuf, url, url_len);
|
||||
manifest->urlbuf[url_len] = '\0';
|
||||
|
||||
LOG_DEBUG("_dtv_fetch() fetching \"%s\" (url_len=%u)\n", manifest->urlbuf,
|
||||
(unsigned)url_len);
|
||||
|
||||
int target_slot = riotboot_slot_other();
|
||||
riotboot_flashwrite_init(manifest->writer, target_slot);
|
||||
|
||||
int res = -1;
|
||||
|
||||
if (0) {}
|
||||
#ifdef MODULE_SUIT_TRANSPORT_COAP
|
||||
else if (strncmp(manifest->urlbuf, "coap://", 7) == 0) {
|
||||
res = suit_coap_get_blockwise_url(manifest->urlbuf, COAP_BLOCKSIZE_64,
|
||||
suit_flashwrite_helper,
|
||||
manifest);
|
||||
}
|
||||
#endif
|
||||
#ifdef MODULE_SUIT_TRANSPORT_MOCK
|
||||
else if (strncmp(manifest->urlbuf, "test://", 7) == 0) {
|
||||
res = SUIT_OK;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
LOG_WARNING("suit: unsupported URL scheme!\n)");
|
||||
return res;
|
||||
}
|
||||
|
||||
if (res) {
|
||||
LOG_INFO("image download failed\n)");
|
||||
return res;
|
||||
}
|
||||
|
||||
manifest->state |= SUIT_MANIFEST_HAVE_IMAGE;
|
||||
|
||||
LOG_DEBUG("Update OK\n");
|
||||
return SUIT_OK;
|
||||
}
|
||||
|
||||
static int _dtv_verify_image_match(suit_manifest_t *manifest, int key,
|
||||
nanocbor_value_t *_it)
|
||||
{
|
||||
(void)key; (void)_it;
|
||||
LOG_DEBUG("dtv_image_match\n");
|
||||
const uint8_t *digest;
|
||||
size_t digest_len;
|
||||
int target_slot = riotboot_slot_other();
|
||||
|
||||
LOG_INFO("Verifying image digest\n");
|
||||
nanocbor_value_t _v = manifest->components[0].digest;
|
||||
int res = nanocbor_get_subcbor(&_v, &digest, &digest_len);
|
||||
if (res < 0) {
|
||||
LOG_DEBUG("Unable to parse digest structure\n");
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
|
||||
/* "digest" points to a 36 byte string that includes the digest type.
|
||||
* riotboot_flashwrite_verify_sha256() is only interested in the 32b digest,
|
||||
* so shift the pointer accordingly.
|
||||
*/
|
||||
res = riotboot_flashwrite_verify_sha256(digest + 4,
|
||||
manifest->components[0].size,
|
||||
target_slot);
|
||||
if (res != 0) {
|
||||
return SUIT_ERR_COND;
|
||||
}
|
||||
return SUIT_OK;
|
||||
}
|
||||
|
||||
/* begin{code-style-ignore} */
|
||||
const suit_manifest_handler_t suit_sequence_handlers[] = {
|
||||
[SUIT_COND_VENDOR_ID] = _cond_vendor_handler,
|
||||
[SUIT_COND_CLASS_ID] = _cond_class_handler,
|
||||
[SUIT_COND_IMAGE_MATCH] = _dtv_verify_image_match,
|
||||
[SUIT_COND_COMPONENT_OFFSET] = _cond_comp_offset,
|
||||
[SUIT_DIR_SET_COMPONENT_IDX] = _dtv_set_comp_idx,
|
||||
[SUIT_DIR_TRY_EACH] = _dtv_try_each,
|
||||
[SUIT_DIR_SET_PARAM] = _dtv_set_param,
|
||||
[SUIT_DIR_OVERRIDE_PARAM] = _dtv_set_param,
|
||||
[SUIT_DIR_FETCH] = _dtv_fetch,
|
||||
[SUIT_DIR_RUN_SEQUENCE] = _dtv_run_seq_cond,
|
||||
};
|
||||
/* end{code-style-ignore} */
|
||||
|
||||
const size_t suit_sequence_handlers_len = ARRAY_SIZE(suit_sequence_handlers);
|
||||
149
sys/suit/handlers_container.c
Normal file
149
sys/suit/handlers_container.c
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Koen Zandberg
|
||||
* 2020 Inria
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
/**
|
||||
* @ingroup sys_suit_v3
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief SUIT draft-ietf-suit-manifest-03 Handlers for the outer SUIT
|
||||
* container
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <cose/sign.h>
|
||||
#include <nanocbor/nanocbor.h>
|
||||
|
||||
#include "hashes/sha256.h"
|
||||
#include "kernel_defines.h"
|
||||
#include "log.h"
|
||||
#include "public_key.h"
|
||||
#include "suit/conditions.h"
|
||||
#include "suit/handlers.h"
|
||||
#include "suit.h"
|
||||
|
||||
static int _auth_handler(suit_manifest_t *manifest, int key,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
(void)key;
|
||||
cose_sign_dec_t verify;
|
||||
const uint8_t *cose_buf;
|
||||
const uint8_t *cose_container;
|
||||
size_t container_len;
|
||||
size_t cose_len = 0;
|
||||
/* It is a list of cose signatures */
|
||||
int res = nanocbor_get_bstr(it, &cose_container, &container_len);
|
||||
if (res < 0) {
|
||||
LOG_INFO("Unable to get COSE signature\n");
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
|
||||
nanocbor_value_t _cont, arr;
|
||||
nanocbor_decoder_init(&_cont, cose_container, container_len);
|
||||
|
||||
int rc = nanocbor_enter_array(&_cont, &arr);
|
||||
if (rc < 0) {
|
||||
LOG_INFO("Unable to enter COSE signatures\n");
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
|
||||
uint32_t tag;
|
||||
nanocbor_get_tag(&arr, &tag);
|
||||
arr.remaining++;
|
||||
res = nanocbor_get_subcbor(&arr, &cose_buf, &cose_len);
|
||||
if (res < 0) {
|
||||
LOG_INFO("Unable to get subcbor: %d\n", res);
|
||||
}
|
||||
|
||||
res = cose_sign_decode(&verify, cose_buf, cose_len);
|
||||
if (res < 0) {
|
||||
LOG_INFO("Unable to parse COSE signature\n");
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
|
||||
/* Iterate over signatures, should only be a single signature */
|
||||
cose_signature_dec_t signature;
|
||||
|
||||
cose_sign_signature_iter_init(&signature);
|
||||
if (!cose_sign_signature_iter(&verify, &signature)) {
|
||||
LOG_INFO("Unable to get signature iteration\n");
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
|
||||
/* Initialize key from hardcoded public key */
|
||||
cose_key_t pkey;
|
||||
cose_key_init(&pkey);
|
||||
cose_key_set_keys(&pkey, COSE_EC_CURVE_ED25519, COSE_ALGO_EDDSA,
|
||||
(uint8_t *)public_key, NULL, NULL);
|
||||
|
||||
LOG_INFO("suit: verifying manifest signature\n");
|
||||
int verification = cose_sign_verify(&verify, &signature,
|
||||
&pkey, manifest->validation_buf,
|
||||
SUIT_COSE_BUF_SIZE);
|
||||
if (verification != 0) {
|
||||
LOG_INFO("Unable to validate signature: %d\n", verification);
|
||||
return SUIT_ERR_SIGNATURE;
|
||||
}
|
||||
|
||||
manifest->cose_payload = verify.payload;
|
||||
manifest->cose_payload_len = verify.payload_len;
|
||||
manifest->state |= SUIT_STATE_COSE_AUTHENTICATED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _manifest_handler(suit_manifest_t *manifest, int key,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
(void)key;
|
||||
const uint8_t *manifest_buf;
|
||||
size_t manifest_len;
|
||||
|
||||
if (!(manifest->state & SUIT_STATE_COSE_AUTHENTICATED)) {
|
||||
return SUIT_ERR_SIGNATURE;
|
||||
}
|
||||
|
||||
nanocbor_value_t cbor_buf = *it;
|
||||
|
||||
nanocbor_get_subcbor(&cbor_buf, &manifest_buf, &manifest_len);
|
||||
|
||||
uint8_t digest_struct[4 + SHA256_DIGEST_LENGTH] =
|
||||
/* CBOR array of length 2, sha256 digest and a bytestring of SHA256
|
||||
* length
|
||||
*/
|
||||
{ 0x82, 0x02, 0x58, SHA256_DIGEST_LENGTH };
|
||||
sha256(manifest_buf, manifest_len, digest_struct + 4);
|
||||
|
||||
/* The COSE payload and the sha256 of the manifest itself is public info and
|
||||
* verification does not depend on secret info. No need for cryptographic
|
||||
* memcmp here */
|
||||
if (memcmp(digest_struct, manifest->cose_payload,
|
||||
sizeof(digest_struct)) != 0) {
|
||||
LOG_ERROR("SUIT manifest digest and COSE digest mismatch\n");
|
||||
return SUIT_ERR_DIGEST_MISMATCH;
|
||||
}
|
||||
|
||||
manifest->state |= SUIT_STATE_FULLY_AUTHENTICATED;
|
||||
|
||||
LOG_DEBUG("Starting global sequence handler\n");
|
||||
return suit_handle_manifest_structure_bstr(manifest, it,
|
||||
suit_global_handlers,
|
||||
suit_global_handlers_len);
|
||||
}
|
||||
|
||||
/* begin{code-style-ignore} */
|
||||
const suit_manifest_handler_t suit_container_handlers[] = {
|
||||
[SUIT_WRAPPER_AUTHENTICATION] = _auth_handler,
|
||||
[SUIT_WRAPPER_MANIFEST] = _manifest_handler,
|
||||
};
|
||||
/* end{code-style-ignore} */
|
||||
|
||||
const size_t suit_container_handlers_len = ARRAY_SIZE(suit_container_handlers);
|
||||
106
sys/suit/handlers_global.c
Normal file
106
sys/suit/handlers_global.c
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Koen Zandberg
|
||||
* 2020 Inria
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
/**
|
||||
* @ingroup sys_suit_v3
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief SUIT draft-ietf-suit-manifest-03 Handlers for the global SUIT
|
||||
* manifest content.
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <nanocbor/nanocbor.h>
|
||||
|
||||
#include "kernel_defines.h"
|
||||
#include "log.h"
|
||||
#include "suit/conditions.h"
|
||||
#include "suit/handlers.h"
|
||||
#include "suit/policy.h"
|
||||
#include "suit.h"
|
||||
|
||||
extern int _common_sequence_handler(suit_manifest_t *manifest, int key,
|
||||
nanocbor_value_t *it);
|
||||
|
||||
static int _version_handler(suit_manifest_t *manifest, int key,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
(void)manifest;
|
||||
(void)key;
|
||||
/* Validate manifest version */
|
||||
int32_t version = -1;
|
||||
if (nanocbor_get_int32(it, &version) >= 0) {
|
||||
if (version == SUIT_VERSION) {
|
||||
manifest->validated |= SUIT_VALIDATED_VERSION;
|
||||
LOG_INFO("suit: validated manifest version\n)");
|
||||
return SUIT_OK;
|
||||
}
|
||||
}
|
||||
return SUIT_ERR_SEQUENCE_NUMBER;
|
||||
}
|
||||
|
||||
static int _seq_no_handler(suit_manifest_t *manifest, int key,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
(void)key;
|
||||
|
||||
int32_t seq_nr;
|
||||
|
||||
if (nanocbor_get_int32(it, &seq_nr) < 0) {
|
||||
LOG_INFO("Unable to get sequence number\n");
|
||||
return SUIT_ERR_INVALID_MANIFEST;
|
||||
}
|
||||
const riotboot_hdr_t *hdr = riotboot_slot_get_hdr(riotboot_slot_current());
|
||||
if (seq_nr <= (int32_t)hdr->version) {
|
||||
LOG_INFO("%" PRId32 " <= %" PRId32 "\n", seq_nr, hdr->version);
|
||||
LOG_INFO("seq_nr <= running image\n)");
|
||||
return SUIT_ERR_SEQUENCE_NUMBER;
|
||||
}
|
||||
|
||||
hdr = riotboot_slot_get_hdr(riotboot_slot_other());
|
||||
if (riotboot_hdr_validate(hdr) == 0) {
|
||||
if (seq_nr <= (int32_t)hdr->version) {
|
||||
LOG_INFO("%" PRIu32 " <= %" PRIu32 "\n", seq_nr, hdr->version);
|
||||
LOG_INFO("seq_nr <= other image\n)");
|
||||
return SUIT_ERR_SEQUENCE_NUMBER;
|
||||
}
|
||||
}
|
||||
LOG_INFO("suit: validated sequence number\n)");
|
||||
manifest->validated |= SUIT_VALIDATED_SEQ_NR;
|
||||
return SUIT_OK;
|
||||
|
||||
}
|
||||
|
||||
static int _common_handler(suit_manifest_t *manifest, int key,
|
||||
nanocbor_value_t *it)
|
||||
{
|
||||
(void)key;
|
||||
LOG_DEBUG("Starting common section handler\n");
|
||||
return suit_handle_manifest_structure_bstr(manifest, it,
|
||||
suit_common_handlers,
|
||||
suit_common_handlers_len);
|
||||
}
|
||||
|
||||
/* begin{code-style-ignore} */
|
||||
const suit_manifest_handler_t suit_global_handlers[] = {
|
||||
[ 0] = NULL,
|
||||
[SUIT_CONTAINER_VERSION] = _version_handler,
|
||||
[SUIT_CONTAINER_SEQ_NO] = _seq_no_handler,
|
||||
[SUIT_CONTAINER_COMMON] = _common_handler,
|
||||
/* Install and validate both consist of a command sequence */
|
||||
[SUIT_CONTAINER_INSTALL] = _common_sequence_handler,
|
||||
[SUIT_CONTAINER_VALIDATE] = _common_sequence_handler,
|
||||
};
|
||||
/* end{code-style-ignore} */
|
||||
|
||||
const size_t suit_global_handlers_len = ARRAY_SIZE(suit_global_handlers);
|
||||
36
sys/suit/policy.c
Normal file
36
sys/suit/policy.c
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* 2019 Inria
|
||||
* 2019 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
/**
|
||||
* @ingroup sys_suit_v3
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief SUIT draft-ietf-suit-manifest-03 policy checking code
|
||||
*
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "suit/policy.h"
|
||||
#include "suit.h"
|
||||
|
||||
int suit_policy_check(suit_manifest_t *manifest)
|
||||
{
|
||||
if (SUIT_DEFAULT_POLICY & ~(manifest->validated)) {
|
||||
LOG_INFO("SUIT policy check failed!\n");
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
LOG_INFO("SUIT policy check OK.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
49
sys/suit/suit.c
Normal file
49
sys/suit/suit.c
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Freie Universität Berlin
|
||||
* Copyright (C) 2019 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* 2020 Inria
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
/**
|
||||
* @ingroup sys_suit
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief SUIT draft-ietf-suit-manifest-03 manifest parser library for
|
||||
* CBOR based manifests
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <nanocbor/nanocbor.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "suit/handlers.h"
|
||||
#include "suit/policy.h"
|
||||
#include "suit.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
int suit_parse(suit_manifest_t *manifest, const uint8_t *buf,
|
||||
size_t len)
|
||||
{
|
||||
nanocbor_value_t it;
|
||||
|
||||
manifest->buf = buf;
|
||||
manifest->len = len;
|
||||
nanocbor_decoder_init(&it, buf, len);
|
||||
LOG_DEBUG("Starting container sequence handler\n");
|
||||
return suit_handle_manifest_structure(manifest, &it,
|
||||
suit_container_handlers,
|
||||
suit_container_handlers_len);
|
||||
}
|
||||
5
sys/suit/transport/Makefile
Normal file
5
sys/suit/transport/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
MODULE := suit_transport
|
||||
SUBMODULES := 1
|
||||
BASE_MODULE := suit_transport
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
@ -40,8 +40,8 @@
|
||||
#include "riotboot/flashwrite.h"
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_SUIT_V4
|
||||
#include "suit/v4/suit.h"
|
||||
#ifdef MODULE_SUIT
|
||||
#include "suit.h"
|
||||
#endif
|
||||
|
||||
#if defined(MODULE_PROGRESS_BAR)
|
||||
@ -74,8 +74,9 @@ static char _stack[SUIT_COAP_STACKSIZE];
|
||||
static char _url[SUIT_URL_MAX];
|
||||
static uint8_t _manifest_buf[SUIT_MANIFEST_BUFSIZE];
|
||||
|
||||
#ifdef MODULE_SUIT_V4
|
||||
static inline void _print_download_progress(size_t offset, size_t len, uint32_t image_size)
|
||||
#ifdef MODULE_SUIT_V3
|
||||
static inline void _print_download_progress(size_t offset, size_t len,
|
||||
uint32_t image_size)
|
||||
{
|
||||
(void)offset;
|
||||
(void)len;
|
||||
@ -145,6 +146,7 @@ static inline uint32_t deadline_from_interval(int32_t interval)
|
||||
static inline uint32_t deadline_left(uint32_t deadline)
|
||||
{
|
||||
int32_t left = (int32_t)(deadline - _now());
|
||||
|
||||
if (left < 0) {
|
||||
left = 0;
|
||||
}
|
||||
@ -163,7 +165,9 @@ static ssize_t _nanocoap_request(sock_udp_t *sock, coap_pkt_t *pkt, size_t len)
|
||||
uint32_t timeout = COAP_ACK_TIMEOUT * US_PER_SEC;
|
||||
uint32_t deadline = deadline_from_interval(timeout);
|
||||
|
||||
unsigned tries_left = COAP_MAX_RETRANSMIT + 1; /* add 1 for initial transmit */
|
||||
/* add 1 for initial transmit */
|
||||
unsigned tries_left = COAP_MAX_RETRANSMIT + 1;
|
||||
|
||||
while (tries_left) {
|
||||
if (res == -EAGAIN) {
|
||||
res = sock_udp_send(sock, buf, pdu_len, NULL);
|
||||
@ -210,14 +214,19 @@ static ssize_t _nanocoap_request(sock_udp_t *sock, coap_pkt_t *pkt, size_t len)
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _fetch_block(coap_pkt_t *pkt, uint8_t *buf, sock_udp_t *sock, const char *path, coap_blksize_t blksize, size_t num)
|
||||
static int _fetch_block(coap_pkt_t *pkt, uint8_t *buf, sock_udp_t *sock,
|
||||
const char *path, coap_blksize_t blksize, size_t num)
|
||||
{
|
||||
uint8_t *pktpos = buf;
|
||||
|
||||
pkt->hdr = (coap_hdr_t *)buf;
|
||||
|
||||
pktpos += coap_build_hdr(pkt->hdr, COAP_TYPE_CON, NULL, 0, COAP_METHOD_GET, num);
|
||||
pktpos += coap_build_hdr(pkt->hdr, COAP_TYPE_CON, NULL, 0, COAP_METHOD_GET,
|
||||
num);
|
||||
pktpos += coap_opt_put_uri_path(pktpos, 0, path);
|
||||
pktpos += coap_opt_put_uint(pktpos, COAP_OPT_URI_PATH, COAP_OPT_BLOCK2, (num << 4) | blksize);
|
||||
pktpos +=
|
||||
coap_opt_put_uint(pktpos, COAP_OPT_URI_PATH, COAP_OPT_BLOCK2,
|
||||
(num << 4) | blksize);
|
||||
|
||||
pkt->payload = pktpos;
|
||||
pkt->payload_len = 0;
|
||||
@ -248,14 +257,12 @@ int suit_coap_get_blockwise(sock_udp_ep_t *remote, const char *path,
|
||||
/* HACK: use random local port */
|
||||
local.port = 0x8000 + (xtimer_now_usec() % 0XFFF);
|
||||
|
||||
|
||||
sock_udp_t sock;
|
||||
int res = sock_udp_create(&sock, &local, remote, 0);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int more = 1;
|
||||
size_t num = 0;
|
||||
res = -1;
|
||||
@ -269,7 +276,8 @@ int suit_coap_get_blockwise(sock_udp_ep_t *remote, const char *path,
|
||||
coap_get_block2(&pkt, &block2);
|
||||
more = block2.more;
|
||||
|
||||
if (callback(arg, block2.offset, pkt.payload, pkt.payload_len, more)) {
|
||||
if (callback(arg, block2.offset, pkt.payload, pkt.payload_len,
|
||||
more)) {
|
||||
DEBUG("callback res != 0, aborting.\n");
|
||||
res = -1;
|
||||
goto out;
|
||||
@ -351,20 +359,22 @@ ssize_t suit_coap_get_blockwise_url_buf(const char *url,
|
||||
{
|
||||
_buf_t _buf = { .ptr = buf, .len = len };
|
||||
int res = suit_coap_get_blockwise_url(url, blksize, _2buf, &_buf);
|
||||
|
||||
return (res < 0) ? (ssize_t)res : (ssize_t)_buf.offset;
|
||||
}
|
||||
|
||||
static void _suit_handle_url(const char *url)
|
||||
{
|
||||
LOG_INFO("suit_coap: downloading \"%s\"\n", url);
|
||||
ssize_t size = suit_coap_get_blockwise_url_buf(url, COAP_BLOCKSIZE_64, _manifest_buf,
|
||||
ssize_t size = suit_coap_get_blockwise_url_buf(url, COAP_BLOCKSIZE_64,
|
||||
_manifest_buf,
|
||||
SUIT_MANIFEST_BUFSIZE);
|
||||
if (size >= 0) {
|
||||
LOG_INFO("suit_coap: got manifest with size %u\n", (unsigned)size);
|
||||
|
||||
riotboot_flashwrite_t writer;
|
||||
#ifdef MODULE_SUIT_V4
|
||||
suit_v4_manifest_t manifest;
|
||||
#ifdef MODULE_SUIT_V3
|
||||
suit_manifest_t manifest;
|
||||
memset(&manifest, 0, sizeof(manifest));
|
||||
|
||||
manifest.writer = &writer;
|
||||
@ -372,18 +382,18 @@ static void _suit_handle_url(const char *url)
|
||||
manifest.urlbuf_len = SUIT_URL_MAX;
|
||||
|
||||
int res;
|
||||
if ((res = suit_v4_parse(&manifest, _manifest_buf, size)) != SUIT_OK) {
|
||||
LOG_INFO("suit_v4_parse() failed. res=%i\n", res);
|
||||
if ((res = suit_v3_parse(&manifest, _manifest_buf, size)) != SUIT_OK) {
|
||||
LOG_INFO("suit_v3_parse() failed. res=%i\n", res);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("suit_v4_parse() success\n");
|
||||
LOG_INFO("suit_v3_parse() success\n");
|
||||
if (!(manifest.state & SUIT_MANIFEST_HAVE_IMAGE)) {
|
||||
LOG_INFO("manifest parsed, but no image fetched\n");
|
||||
return;
|
||||
}
|
||||
|
||||
res = suit_v4_policy_check(&manifest);
|
||||
res = suit_v3_policy_check(&manifest);
|
||||
if (res) {
|
||||
return;
|
||||
}
|
||||
@ -393,7 +403,8 @@ static void _suit_handle_url(const char *url)
|
||||
LOG_INFO("suit_coap: finalizing image flash\n");
|
||||
riotboot_flashwrite_finish(&writer);
|
||||
|
||||
const riotboot_hdr_t *hdr = riotboot_slot_get_hdr(riotboot_slot_other());
|
||||
const riotboot_hdr_t *hdr = riotboot_slot_get_hdr(
|
||||
riotboot_slot_other());
|
||||
riotboot_hdr_print(hdr);
|
||||
xtimer_sleep(1);
|
||||
|
||||
@ -414,7 +425,7 @@ static void _suit_handle_url(const char *url)
|
||||
int suit_flashwrite_helper(void *arg, size_t offset, uint8_t *buf, size_t len,
|
||||
int more)
|
||||
{
|
||||
suit_v4_manifest_t *manifest = (suit_v4_manifest_t *)arg;
|
||||
suit_manifest_t *manifest = (suit_manifest_t *)arg;
|
||||
riotboot_flashwrite_t *writer = manifest->writer;
|
||||
|
||||
if (offset == 0) {
|
||||
@ -428,7 +439,8 @@ int suit_flashwrite_helper(void *arg, size_t offset, uint8_t *buf, size_t len,
|
||||
}
|
||||
|
||||
if (writer->offset != offset) {
|
||||
LOG_WARNING("_suit_flashwrite(): writer->offset=%u, offset==%u, aborting\n",
|
||||
LOG_WARNING(
|
||||
"_suit_flashwrite(): writer->offset=%u, offset==%u, aborting\n",
|
||||
(unsigned)writer->offset, (unsigned)offset);
|
||||
return -1;
|
||||
}
|
||||
@ -485,6 +497,7 @@ static ssize_t _slot_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len,
|
||||
{
|
||||
/* context is passed either as NULL or 0x1 for /active or /inactive */
|
||||
char c = '0';
|
||||
|
||||
if (context) {
|
||||
c += riotboot_slot_other();
|
||||
}
|
||||
@ -534,7 +547,8 @@ static const coap_resource_t _subtree[] = {
|
||||
{ "/suit/slot/active", COAP_METHOD_GET, _slot_handler, NULL },
|
||||
{ "/suit/slot/inactive", COAP_METHOD_GET, _slot_handler, (void *)0x1 },
|
||||
#endif
|
||||
{ "/suit/trigger", COAP_METHOD_PUT | COAP_METHOD_POST, _trigger_handler, NULL },
|
||||
{ "/suit/trigger", COAP_METHOD_PUT | COAP_METHOD_POST, _trigger_handler,
|
||||
NULL },
|
||||
{ "/suit/version", COAP_METHOD_GET, _version_handler, NULL },
|
||||
};
|
||||
|
||||
81
sys/suit/transport/mock.c
Normal file
81
sys/suit/transport/mock.c
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "kernel_defines.h"
|
||||
#include "cpu_conf.h"
|
||||
|
||||
#include "riotboot/flashwrite.h"
|
||||
#include "riotboot/hdr.h"
|
||||
|
||||
#define SLOT0_OFFSET 0x1000
|
||||
#define SLOT1_OFFSET 0x2000
|
||||
|
||||
static riotboot_hdr_t _riotboot_slots[] = {
|
||||
{ .magic_number = RIOTBOOT_MAGIC,
|
||||
.version = 1,
|
||||
.start_addr=0x100000,
|
||||
},
|
||||
{ .magic_number = RIOTBOOT_MAGIC,
|
||||
.version = 2,
|
||||
.start_addr=0x200000,
|
||||
},
|
||||
};
|
||||
|
||||
const riotboot_hdr_t * const riotboot_slots[] = {
|
||||
&_riotboot_slots[0],
|
||||
&_riotboot_slots[1],
|
||||
};
|
||||
|
||||
const unsigned riotboot_slot_numof = ARRAY_SIZE(riotboot_slots);
|
||||
|
||||
static int _current_slot;
|
||||
|
||||
int riotboot_slot_current(void)
|
||||
{
|
||||
return _current_slot;
|
||||
}
|
||||
|
||||
int riotboot_slot_other(void)
|
||||
{
|
||||
return (_current_slot == 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
const riotboot_hdr_t *riotboot_slot_get_hdr(unsigned slot)
|
||||
{
|
||||
assert(slot < riotboot_slot_numof);
|
||||
|
||||
return riotboot_slots[slot];
|
||||
}
|
||||
|
||||
size_t riotboot_slot_offset(unsigned slot)
|
||||
{
|
||||
return (slot == 0) ? SLOT0_OFFSET : SLOT1_OFFSET;
|
||||
}
|
||||
|
||||
int riotboot_flashwrite_init_raw(riotboot_flashwrite_t *state, int target_slot,
|
||||
size_t offset)
|
||||
{
|
||||
(void)state;
|
||||
(void)target_slot;
|
||||
(void)offset;
|
||||
puts("riotboot_flashwrite_init_raw() empty mock");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int riotboot_flashwrite_verify_sha256(const uint8_t *sha256_digest,
|
||||
size_t img_size, int target_slot)
|
||||
{
|
||||
(void)sha256_digest;
|
||||
(void)img_size;
|
||||
(void)target_slot;
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user