suit: Upgrade manifest parser to IETF-v9 compliance

This commit is contained in:
Koen Zandberg 2020-03-23 10:07:28 +01:00
parent 799ee40ea7
commit c8ecc9c3ca
No known key found for this signature in database
GPG Key ID: 0895A893E6D2985B
8 changed files with 336 additions and 153 deletions

View File

@ -14,10 +14,10 @@
* @experimental * @experimental
* *
* @note The current implementation of this specification is based on the * @note The current implementation of this specification is based on the
* IETF-SUIT-v3 draft. The module is still experimental and will change to * IETF-SUIT-v9 draft. The module is still experimental and will change to
* match future draft specifications * match future draft specifications
* *
* @see https://tools.ietf.org/html/draft-ietf-suit-manifest-03 * @see https://tools.ietf.org/html/draft-ietf-suit-manifest-09
* *
* @{ * @{
* *
@ -52,7 +52,9 @@ extern "C" {
/** /**
* @brief Maximum number of components supported in a SUIT manifest * @brief Maximum number of components supported in a SUIT manifest
*/ */
#define SUIT_COMPONENT_MAX (1U) #ifndef CONFIG_SUIT_COMPONENT_MAX
#define CONFIG_SUIT_COMPONENT_MAX (1U)
#endif
/** /**
* @brief Current SUIT serialization format version * @brief Current SUIT serialization format version
@ -126,13 +128,59 @@ enum {
}; };
/** /**
* @brief SUIT component struct * @name SUIT parameters
* @{
*/
typedef enum {
SUIT_PARAMETER_VENDOR_IDENTIFIER = 1,
SUIT_PARAMETER_CLASS_IDENTIFIER = 2,
SUIT_PARAMETER_IMAGE_DIGEST = 3,
SUIT_PARAMETER_USE_BEFORE = 4,
SUIT_PARAMETER_COMPONENT_OFFSET = 5,
SUIT_PARAMETER_STRICT_ORDER = 12,
SUIT_PARAMETER_SOFT_FAILURE = 13,
SUIT_PARAMETER_IMAGE_SIZE = 14,
SUIT_PARAMETER_ENCRYPTION_INFO = 18,
SUIT_PARAMETER_COMPRESSION_INFO = 19,
SUIT_PARAMETER_UNPACK_INFO = 20,
SUIT_PARAMETER_URI = 21,
SUIT_PARAMETER_SOURCE_COMPONENT = 22,
SUIT_PARAMETER_RUN_ARGS = 23,
SUIT_PARAMETER_DEVICE_IDENTIFIER = 24,
SUIT_PARAMETER_MINIMUM_BATTERY = 26,
SUIT_PARAMETER_UPDATE_PRIORITY = 27,
SUIT_PARAMETER_VERSION = 28,
SUIT_PARAMETER_WAIT_INFO = 29,
SUIT_PARAMETER_URI_LIST = 30,
} suit_parameter_t;
/** @} */
/**
* @brief SUIT parameter reference
*
* A 16-bit offset is enough to reference content inside the manifest itself.
*/ */
typedef struct { typedef struct {
uint32_t size; /**< Size */ uint16_t offset; /**< offset to the start of the content */
nanocbor_value_t identifier; /**< Identifier */ } suit_param_ref_t;
nanocbor_value_t url; /**< Url */
nanocbor_value_t digest; /**< Digest */ /**
* @brief SUIT component struct as decoded from the manifest
*
* The parameters are references to CBOR-encoded information in the manifest.
*/
typedef struct {
suit_param_ref_t identifier; /**< Component identifier */
suit_param_ref_t param_vendor_id; /**< Vendor ID */
suit_param_ref_t param_class_id; /**< Class ID */
suit_param_ref_t param_digest; /**< Payload verification digest */
suit_param_ref_t param_uri; /**< Payload fetch URI */
suit_param_ref_t param_size; /**< Payload size */
/**
* @brief Component offset inside the device memory.
*/
suit_param_ref_t param_component_offset;
} suit_component_t; } suit_component_t;
/** /**
@ -146,9 +194,9 @@ typedef struct {
uint32_t validated; /**< bitfield of validated policies */ uint32_t validated; /**< bitfield of validated policies */
uint32_t state; /**< bitfield holding state information */ uint32_t state; /**< bitfield holding state information */
/** List of components in the manifest */ /** List of components in the manifest */
suit_component_t components[SUIT_COMPONENT_MAX]; suit_component_t components[CONFIG_SUIT_COMPONENT_MAX];
unsigned components_len; /**< Current number of components */ unsigned components_len; /**< Current number of components */
uint32_t component_current; /**< Current component index */ uint8_t component_current; /**< Current component index */
riotboot_flashwrite_t *writer; /**< Pointer to the riotboot flash writer */ riotboot_flashwrite_t *writer; /**< Pointer to the riotboot flash writer */
/** Manifest validation buffer */ /** Manifest validation buffer */
uint8_t validation_buf[SUIT_COSE_BUF_SIZE]; uint8_t validation_buf[SUIT_COSE_BUF_SIZE];
@ -165,6 +213,20 @@ typedef struct {
*/ */
#define SUIT_MANIFEST_HAVE_IMAGE (0x2) #define SUIT_MANIFEST_HAVE_IMAGE (0x2)
/**
* @brief Component index representing all components
*
* Used when suit-directive-set-component-index = True
*/
#define SUIT_MANIFEST_COMPONENT_ALL (UINT8_MAX)
/**
* @brief Component index representing no components
*
* Used when suit-directive-set-component-index = False
*/
#define SUIT_MANIFEST_COMPONENT_NONE (SUIT_MANIFEST_COMPONENT_ALL - 1)
/** /**
* @brief Parse a manifest * @brief Parse a manifest
* *

View File

@ -135,14 +135,14 @@ extern const suit_manifest_handler_t suit_command_sequence_handlers[];
extern const size_t suit_command_sequence_handlers_len; extern const size_t suit_command_sequence_handlers_len;
/** /**
* @brief SUIT container handlers reference * @brief SUIT envelope handlers reference
*/ */
extern const suit_manifest_handler_t suit_container_handlers[]; extern const suit_manifest_handler_t suit_envelope_handlers[];
/** /**
* @brief length of the SUIT container handlers * @brief length of the SUIT envelope handlers
*/ */
extern const size_t suit_container_handlers_len; extern const size_t suit_envelope_handlers_len;
/** /**
* @brief SUIT common handlers reference * @brief SUIT common handlers reference
@ -193,6 +193,36 @@ int suit_handle_manifest_structure_bstr(suit_manifest_t *manifest,
const suit_manifest_handler_t *handlers, const suit_manifest_handler_t *handlers,
size_t handlers_len); size_t handlers_len);
/**
* @brief Create an internal @ref suit_param_ref_t from a NanoCBOR value
* reference.
*
* The resulting @p ref only contains a 16 bit offset to the location of the
* NanoCBOR value, saving some RAM for each stored reference.
*
* @param manifest SUIT manifest context
* @param ref reference to store
* @param val NanoCBOR value to convert to a reference
*
* @returns The offset of the nanocbor value inside the manifest
* bytestring
*/
uint16_t suit_param_ref_to_cbor(suit_manifest_t *manifest,
suit_param_ref_t *ref,
nanocbor_value_t *val);
/**
* @brief Create a NanoCBOR value reference from an internal
* @ref suit_param_ref_t.
*
* @param manifest SUIT manifest context
* @param ref reference to parse
* @param val NanoCBOR value to restore
*/
void suit_param_cbor_to_ref(suit_manifest_t *manifest,
suit_param_ref_t *ref,
nanocbor_value_t *val);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -20,6 +20,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <nanocbor/nanocbor.h> #include <nanocbor/nanocbor.h>
#include <assert.h>
#include "suit/handlers.h" #include "suit/handlers.h"
#include "suit.h" #include "suit.h"
@ -36,6 +37,24 @@ static suit_manifest_handler_t _get_handler(int key,
return map[key]; return map[key];
} }
uint16_t suit_param_ref_to_cbor(suit_manifest_t *manifest,
suit_param_ref_t *ref,
nanocbor_value_t *val)
{
size_t len = manifest->len - ref->offset;
const uint8_t *start = manifest->buf + ref->offset;
nanocbor_decoder_init(val, start, len);
return ref->offset;
}
void suit_param_cbor_to_ref(suit_manifest_t *manifest,
suit_param_ref_t *ref,
nanocbor_value_t *val)
{
assert(val->cur >= manifest->buf);
ref->offset = val->cur - manifest->buf;
}
int suit_handle_manifest_structure(suit_manifest_t *manifest, int suit_handle_manifest_structure(suit_manifest_t *manifest,
nanocbor_value_t *it, nanocbor_value_t *it,
const suit_manifest_handler_t *handlers, const suit_manifest_handler_t *handlers,

View File

@ -24,6 +24,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <nanocbor/nanocbor.h> #include <nanocbor/nanocbor.h>
#include <assert.h>
#include "kernel_defines.h" #include "kernel_defines.h"
#include "suit/conditions.h" #include "suit/conditions.h"
@ -39,22 +40,33 @@
#include "log.h" #include "log.h"
static suit_component_t *_get_component(suit_manifest_t *manifest)
{
/* Out-of-bounds check has been done in the _dtv_set_comp_idx, True/False
* not handled here intentionally */
assert(manifest->component_current < CONFIG_SUIT_COMPONENT_MAX);
return &manifest->components[manifest->component_current];
}
static int _validate_uuid(suit_manifest_t *manifest, static int _validate_uuid(suit_manifest_t *manifest,
nanocbor_value_t *it, suit_param_ref_t *ref,
uuid_t *uuid) uuid_t *uuid)
{ {
(void)manifest;
const uint8_t *uuid_manifest_ptr; const uint8_t *uuid_manifest_ptr;
size_t len = sizeof(uuid_t); size_t len = sizeof(uuid_t);
char uuid_str[UUID_STR_LEN + 1]; nanocbor_value_t it;
char uuid_str2[UUID_STR_LEN + 1];
if (nanocbor_get_bstr(it, &uuid_manifest_ptr, &len) < 0) { if ((suit_param_ref_to_cbor(manifest, ref, &it) == 0) ||
(nanocbor_get_bstr(&it, &uuid_manifest_ptr, &len) < 0)) {
return SUIT_ERR_INVALID_MANIFEST; return SUIT_ERR_INVALID_MANIFEST;
} }
char uuid_str[UUID_STR_LEN + 1];
char uuid_str2[UUID_STR_LEN + 1];
uuid_to_string((uuid_t *)uuid_manifest_ptr, uuid_str); uuid_to_string((uuid_t *)uuid_manifest_ptr, uuid_str);
uuid_to_string(uuid, uuid_str2); uuid_to_string(uuid, uuid_str2);
LOG_INFO("Comparing %s to %s from manifest\n", uuid_str2, uuid_str); LOG_INFO("Comparing %s to %s from manifest\n", uuid_str2, uuid_str);
return uuid_equal(uuid, (uuid_t *)uuid_manifest_ptr) return uuid_equal(uuid, (uuid_t *)uuid_manifest_ptr)
? SUIT_OK ? SUIT_OK
: SUIT_ERR_COND; : SUIT_ERR_COND;
@ -65,8 +77,11 @@ static int _cond_vendor_handler(suit_manifest_t *manifest,
nanocbor_value_t *it) nanocbor_value_t *it)
{ {
(void)key; (void)key;
(void)it;
LOG_INFO("validating vendor ID\n"); LOG_INFO("validating vendor ID\n");
int rc = _validate_uuid(manifest, it, suit_get_vendor_id()); suit_component_t *comp = _get_component(manifest);
int rc = _validate_uuid(manifest, &comp->param_vendor_id,
suit_get_vendor_id());
if (rc == SUIT_OK) { if (rc == SUIT_OK) {
LOG_INFO("validating vendor ID: OK\n"); LOG_INFO("validating vendor ID: OK\n");
manifest->validated |= SUIT_VALIDATED_VENDOR; manifest->validated |= SUIT_VALIDATED_VENDOR;
@ -79,8 +94,11 @@ static int _cond_class_handler(suit_manifest_t *manifest,
nanocbor_value_t *it) nanocbor_value_t *it)
{ {
(void)key; (void)key;
(void)it;
LOG_INFO("validating class id\n"); LOG_INFO("validating class id\n");
int rc = _validate_uuid(manifest, it, suit_get_class_id()); suit_component_t *comp = _get_component(manifest);
int rc = _validate_uuid(manifest, &comp->param_class_id,
suit_get_class_id());
if (rc == SUIT_OK) { if (rc == SUIT_OK) {
LOG_INFO("validating class id: OK\n"); LOG_INFO("validating class id: OK\n");
manifest->validated |= SUIT_VALIDATED_CLASS; manifest->validated |= SUIT_VALIDATED_CLASS;
@ -95,18 +113,24 @@ static int _cond_comp_offset(suit_manifest_t *manifest,
(void)manifest; (void)manifest;
(void)key; (void)key;
uint32_t offset; uint32_t offset;
uint32_t report;
int rc = nanocbor_get_uint32(it, &offset); suit_component_t *comp = _get_component(manifest);
if (rc < 0) {
LOG_WARNING("_cond_comp_offset(): expected int, got rc=%i type=%i\n", /* Grab offset from param */
rc, nanocbor_get_type(it)); if (nanocbor_get_uint32(it, &report) < 0) {
LOG_WARNING("_cond_comp_offset(): expected None param\n");
return SUIT_ERR_INVALID_MANIFEST; return SUIT_ERR_INVALID_MANIFEST;
} }
nanocbor_value_t param_offset;
suit_param_ref_to_cbor(manifest, &comp->param_component_offset,
&param_offset);
nanocbor_get_uint32(&param_offset, &offset);
uint32_t other_offset = (uint32_t)riotboot_slot_offset( uint32_t other_offset = (uint32_t)riotboot_slot_offset(
riotboot_slot_other()); riotboot_slot_other());
LOG_INFO("Comparing manifest offset %u with other slot offset %u\n", LOG_INFO("Comparing manifest offset %"PRIx32" with other slot offset %"PRIx32"\n",
(unsigned)offset, (unsigned)other_offset); offset, other_offset);
return other_offset == offset ? SUIT_OK : SUIT_ERR_COND; return other_offset == offset ? SUIT_OK : SUIT_ERR_COND;
} }
@ -115,17 +139,26 @@ static int _dtv_set_comp_idx(suit_manifest_t *manifest,
nanocbor_value_t *it) nanocbor_value_t *it)
{ {
(void)key; (void)key;
if (nanocbor_get_type(it) == NANOCBOR_TYPE_FLOAT) { bool index = false;
LOG_DEBUG("_dtv_set_comp_idx() ignoring boolean and floats\n)"); uint32_t new_index;
nanocbor_skip(it);
/* It can be a bool, meaning all or none of the components */
if (nanocbor_get_bool(it, &index) >= 0) {
new_index = index ?
SUIT_MANIFEST_COMPONENT_ALL : SUIT_MANIFEST_COMPONENT_NONE;
} }
else if (nanocbor_get_uint32(it, &manifest->component_current) < 0) { /* It can be a positive integer, meaning one of the components */
else if (nanocbor_get_uint32(it, &new_index) < 0) {
return SUIT_ERR_INVALID_MANIFEST; return SUIT_ERR_INVALID_MANIFEST;
} }
if (manifest->component_current >= SUIT_COMPONENT_MAX) { /* And if it is an integer it must be within the allowed bounds */
else if (new_index >= CONFIG_SUIT_COMPONENT_MAX) {
return SUIT_ERR_INVALID_MANIFEST; return SUIT_ERR_INVALID_MANIFEST;
} }
LOG_DEBUG("Setting component index to %d\n",
/* Update the manifest context */
manifest->component_current = new_index;
LOG_INFO("Setting component index to %d\n",
(int)manifest->component_current); (int)manifest->component_current);
return 0; return 0;
} }
@ -158,9 +191,9 @@ static int _dtv_try_each(suit_manifest_t *manifest,
nanocbor_value_t _container = container; nanocbor_value_t _container = container;
/* `_container` should be CBOR _bstr wrapped according to the spec, but /* `_container` should be CBOR _bstr wrapped according to the spec, but
* it is not */ * it is not */
res = suit_handle_manifest_structure(manifest, &_container, res = suit_handle_manifest_structure_bstr(manifest, &_container,
suit_command_sequence_handlers, suit_command_sequence_handlers,
suit_command_sequence_handlers_len); suit_command_sequence_handlers_len);
nanocbor_skip(&container); nanocbor_skip(&container);
@ -172,32 +205,6 @@ static int _dtv_try_each(suit_manifest_t *manifest,
return res; 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, static int _dtv_set_param(suit_manifest_t *manifest, int key,
nanocbor_value_t *it) nanocbor_value_t *it)
{ {
@ -207,34 +214,50 @@ static int _dtv_set_param(suit_manifest_t *manifest, int key,
nanocbor_enter_map(it, &map); nanocbor_enter_map(it, &map);
suit_component_t *comp = _get_component(manifest);
while (!nanocbor_at_end(&map)) { while (!nanocbor_at_end(&map)) {
/* map points to the key of the param */ /* map points to the key of the param */
int32_t param_key; int32_t param_key;
nanocbor_get_int32(&map, &param_key); if (nanocbor_get_int32(&map, &param_key) < 0) {
LOG_DEBUG("Current component index: %" PRIi32 "\n", return SUIT_ERR_INVALID_MANIFEST;
manifest->component_current); }
LOG_DEBUG("param_key=%" PRIi32 "\n", param_key); LOG_DEBUG("param_key=%" PRIi32 "\n", param_key);
int res; unsigned int type = nanocbor_get_type(&map);
/* Filter 'complex' types and only allow int, nint, bstr and tstr types
* for parameter values */
if (type > NANOCBOR_TYPE_TSTR) {
return SUIT_ERR_INVALID_MANIFEST;
}
suit_param_ref_t *ref;
switch (param_key) { switch (param_key) {
case 6: /* SUIT URI LIST */ case SUIT_PARAMETER_VENDOR_IDENTIFIER:
res = _param_get_uri_list(manifest, &map); ref = &comp->param_vendor_id;
break; break;
case 11: /* SUIT DIGEST */ case SUIT_PARAMETER_CLASS_IDENTIFIER:
res = _param_get_digest(manifest, &map); ref = &comp->param_class_id;
break; break;
case 12: /* SUIT IMAGE SIZE */ case SUIT_PARAMETER_IMAGE_DIGEST:
res = _param_get_img_size(manifest, &map); ref = &comp->param_digest;
break;
case SUIT_PARAMETER_COMPONENT_OFFSET:
ref = &comp->param_component_offset;
break;
case SUIT_PARAMETER_IMAGE_SIZE:
ref = &comp->param_size;
break;
case SUIT_PARAMETER_URI:
ref = &comp->param_uri;
break; break;
default: default:
LOG_DEBUG("Unsupported parameter %" PRIi32 "\n", param_key); LOG_DEBUG("Unsupported parameter %" PRIi32 "\n", param_key);
res = SUIT_ERR_UNSUPPORTED; return SUIT_ERR_UNSUPPORTED;
} }
suit_param_cbor_to_ref(manifest, ref, &map);
/* Simple skip is sufficient to skip non-complex types */
nanocbor_skip(&map); nanocbor_skip(&map);
if (res) {
return res;
}
} }
return SUIT_OK; return SUIT_OK;
} }
@ -247,8 +270,12 @@ static int _dtv_fetch(suit_manifest_t *manifest, int key,
const uint8_t *url; const uint8_t *url;
size_t url_len; size_t url_len;
suit_component_t *comp = _get_component(manifest);
int err = nanocbor_get_tstr(&manifest->components[0].url, &url, &url_len); nanocbor_value_t param_uri;
suit_param_ref_to_cbor(manifest, &comp->param_uri,
&param_uri);
int err = nanocbor_get_tstr(&param_uri, &url, &url_len);
if (err < 0) { if (err < 0) {
LOG_DEBUG("URL parsing failed\n)"); LOG_DEBUG("URL parsing failed\n)");
return err; return err;
@ -293,6 +320,25 @@ static int _dtv_fetch(suit_manifest_t *manifest, int key,
return SUIT_OK; return SUIT_OK;
} }
static int _get_digest(nanocbor_value_t *bstr, const uint8_t **digest, size_t *digest_len)
{
/* Bstr is a byte string with a cbor array containing the type and the
* digest */
const uint8_t *digest_struct;
size_t digest_struct_len;
uint32_t digest_type;
nanocbor_value_t digest_it;
nanocbor_value_t arr_it;
nanocbor_get_bstr(bstr, &digest_struct, &digest_struct_len);
nanocbor_decoder_init(&digest_it, digest_struct, digest_struct_len);
nanocbor_enter_array(&digest_it, &arr_it);
nanocbor_get_uint32(&arr_it, &digest_type);
return nanocbor_get_bstr(&arr_it, digest, digest_len);
}
static int _dtv_verify_image_match(suit_manifest_t *manifest, int key, static int _dtv_verify_image_match(suit_manifest_t *manifest, int key,
nanocbor_value_t *_it) nanocbor_value_t *_it)
{ {
@ -301,21 +347,30 @@ static int _dtv_verify_image_match(suit_manifest_t *manifest, int key,
const uint8_t *digest; const uint8_t *digest;
size_t digest_len; size_t digest_len;
int target_slot = riotboot_slot_other(); int target_slot = riotboot_slot_other();
suit_component_t *comp = _get_component(manifest);
uint32_t img_size;
nanocbor_value_t param_size;
if ((suit_param_ref_to_cbor(manifest, &comp->param_size, &param_size) == 0) ||
(nanocbor_get_uint32(&param_size, &img_size) < 0)) {
return SUIT_ERR_INVALID_MANIFEST;
}
LOG_INFO("Verifying image digest\n"); LOG_INFO("Verifying image digest\n");
nanocbor_value_t _v = manifest->components[0].digest; nanocbor_value_t _v;
int res = nanocbor_get_subcbor(&_v, &digest, &digest_len); if (suit_param_ref_to_cbor(manifest, &comp->param_digest, &_v) == 0) {
return SUIT_ERR_INVALID_MANIFEST;
}
int res = _get_digest(&_v, &digest, &digest_len);
if (res < 0) { if (res < 0) {
LOG_DEBUG("Unable to parse digest structure\n"); LOG_DEBUG("Unable to parse digest structure\n");
return SUIT_ERR_INVALID_MANIFEST; return SUIT_ERR_INVALID_MANIFEST;
} }
/* "digest" points to a 36 byte string that includes the digest type. res = riotboot_flashwrite_verify_sha256(digest,
* riotboot_flashwrite_verify_sha256() is only interested in the 32b digest, img_size,
* so shift the pointer accordingly.
*/
res = riotboot_flashwrite_verify_sha256(digest + 4,
manifest->components[0].size,
target_slot); target_slot);
if (res != 0) { if (res != 0) {
return SUIT_ERR_COND; return SUIT_ERR_COND;

View File

@ -35,11 +35,6 @@ static int _component_handler(suit_manifest_t *manifest, int key,
{ {
(void)manifest; (void)manifest;
(void)key; (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: /* This is a list of lists, something like:
* [ * [
@ -48,18 +43,26 @@ static int _component_handler(suit_manifest_t *manifest, int key,
* ] * ]
* */ * */
nanocbor_value_t arr; nanocbor_value_t arr;
if (nanocbor_enter_array(&_it, &arr) < 0) { if (nanocbor_enter_array(it, &arr) < 0) {
LOG_DEBUG("components field not an array %d\n", nanocbor_get_type(it)); LOG_DEBUG("components field not an array %d\n", nanocbor_get_type(it));
return SUIT_ERR_INVALID_MANIFEST; return SUIT_ERR_INVALID_MANIFEST;
} }
unsigned n = 0; unsigned n = 0;
while (!nanocbor_at_end(&arr)) { while (!nanocbor_at_end(&arr)) {
if (n >= CONFIG_SUIT_COMPONENT_MAX) {
LOG_INFO("Too many components found: %u, Supported: %u\n",
n, CONFIG_SUIT_COMPONENT_MAX);
return SUIT_ERR_UNSUPPORTED;
}
suit_component_t *component = &manifest->components[n];
nanocbor_value_t comp; nanocbor_value_t comp;
if (nanocbor_enter_array(&arr, &comp) < 0) { if (nanocbor_enter_array(&arr, &comp) < 0) {
LOG_DEBUG("component elements field not an array %d\n", LOG_DEBUG("component elements field not an array %d\n",
nanocbor_get_type(it)); nanocbor_get_type(it));
return SUIT_ERR_INVALID_MANIFEST; return SUIT_ERR_INVALID_MANIFEST;
} }
suit_param_cbor_to_ref(manifest, &component->identifier, &comp);
/* Ensure that all parts of the identifier are a bstr */
while (!nanocbor_at_end(&comp)) { while (!nanocbor_at_end(&comp)) {
const uint8_t *identifier; const uint8_t *identifier;
size_t id_len; size_t id_len;
@ -71,11 +74,10 @@ static int _component_handler(suit_manifest_t *manifest, int key,
nanocbor_leave_container(&arr, &comp); nanocbor_leave_container(&arr, &comp);
n++; n++;
} }
if (n > 1) { manifest->components_len = n;
LOG_INFO("More than 1 component found, exiting\n"); if (n) {
return SUIT_ERR_UNSUPPORTED; manifest->state |= SUIT_MANIFEST_HAVE_COMPONENTS;
} }
manifest->state |= SUIT_MANIFEST_HAVE_COMPONENTS;
return 0; return 0;
} }

View File

@ -38,45 +38,12 @@ static int _auth_handler(suit_manifest_t *manifest, int key,
(void)key; (void)key;
cose_sign_dec_t verify; cose_sign_dec_t verify;
const uint8_t *cose_buf; const uint8_t *cose_buf;
const uint8_t *cose_container; const uint8_t *auth_container;
size_t container_len; size_t auth_container_len;
size_t cose_len = 0; size_t cose_len = 0;
/* It is a list of cose signatures */ /* It is a list of cose signatures */
int res = nanocbor_get_bstr(it, &cose_container, &container_len); if (nanocbor_get_bstr(it, &auth_container, &auth_container_len) < 0) {
if (res < 0) { LOG_INFO("Unable to get auth container\n");
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; return SUIT_ERR_INVALID_MANIFEST;
} }
@ -86,20 +53,55 @@ static int _auth_handler(suit_manifest_t *manifest, int key,
cose_key_set_keys(&pkey, COSE_EC_CURVE_ED25519, COSE_ALGO_EDDSA, cose_key_set_keys(&pkey, COSE_EC_CURVE_ED25519, COSE_ALGO_EDDSA,
(uint8_t *)public_key, NULL, NULL); (uint8_t *)public_key, NULL, NULL);
LOG_INFO("suit: verifying manifest signature\n"); nanocbor_value_t _cont, arr;
int verification = cose_sign_verify(&verify, &signature, nanocbor_decoder_init(&_cont, auth_container, auth_container_len);
&pkey, manifest->validation_buf,
SUIT_COSE_BUF_SIZE); int rc = nanocbor_enter_array(&_cont, &arr);
if (verification != 0) { if (rc < 0) {
LOG_INFO("Unable to validate signature: %d\n", verification); LOG_INFO("Unable to enter COSE signatures\n");
return SUIT_ERR_SIGNATURE; return SUIT_ERR_INVALID_MANIFEST;
} }
manifest->cose_payload = verify.payload; int res = SUIT_ERR_SIGNATURE;
manifest->cose_payload_len = verify.payload_len;
manifest->state |= SUIT_STATE_COSE_AUTHENTICATED;
return 0; while (!nanocbor_at_end(&arr)) {
res = nanocbor_get_bstr(&arr, &cose_buf, &cose_len);
if (res < 0) {
LOG_INFO("Unable to get COSE bstr: %d\n", res);
return SUIT_ERR_INVALID_MANIFEST;
}
if (!(manifest->state & SUIT_STATE_COSE_AUTHENTICATED)) {
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;
}
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) {
manifest->state |= SUIT_STATE_COSE_AUTHENTICATED;
res = SUIT_OK;
manifest->cose_payload = verify.payload;
manifest->cose_payload_len = verify.payload_len;
}
else {
LOG_INFO("Unable to validate signature: %d\n", verification);
}
}
}
return res;
} }
static int _manifest_handler(suit_manifest_t *manifest, int key, static int _manifest_handler(suit_manifest_t *manifest, int key,
@ -142,10 +144,10 @@ static int _manifest_handler(suit_manifest_t *manifest, int key,
} }
/* begin{code-style-ignore} */ /* begin{code-style-ignore} */
const suit_manifest_handler_t suit_container_handlers[] = { const suit_manifest_handler_t suit_envelope_handlers[] = {
[SUIT_WRAPPER_AUTHENTICATION] = _auth_handler, [SUIT_WRAPPER_AUTHENTICATION] = _auth_handler,
[SUIT_WRAPPER_MANIFEST] = _manifest_handler, [SUIT_WRAPPER_MANIFEST] = _manifest_handler,
}; };
/* end{code-style-ignore} */ /* end{code-style-ignore} */
const size_t suit_container_handlers_len = ARRAY_SIZE(suit_container_handlers); const size_t suit_envelope_handlers_len = ARRAY_SIZE(suit_envelope_handlers);

View File

@ -42,8 +42,8 @@ int suit_parse(suit_manifest_t *manifest, const uint8_t *buf,
manifest->buf = buf; manifest->buf = buf;
manifest->len = len; manifest->len = len;
nanocbor_decoder_init(&it, buf, len); nanocbor_decoder_init(&it, buf, len);
LOG_DEBUG("Starting container sequence handler\n"); LOG_DEBUG("Starting envelope sequence handler\n");
return suit_handle_manifest_structure(manifest, &it, return suit_handle_manifest_structure(manifest, &it,
suit_container_handlers, suit_envelope_handlers,
suit_container_handlers_len); suit_envelope_handlers_len);
} }

View File

@ -42,6 +42,7 @@
#ifdef MODULE_SUIT #ifdef MODULE_SUIT
#include "suit.h" #include "suit.h"
#include "suit/handlers.h"
#endif #endif
#if defined(MODULE_PROGRESS_BAR) #if defined(MODULE_PROGRESS_BAR)
@ -75,14 +76,26 @@ static char _url[SUIT_URL_MAX];
static uint8_t _manifest_buf[SUIT_MANIFEST_BUFSIZE]; static uint8_t _manifest_buf[SUIT_MANIFEST_BUFSIZE];
#ifdef MODULE_SUIT #ifdef MODULE_SUIT
static inline void _print_download_progress(size_t offset, size_t len, static inline void _print_download_progress(suit_manifest_t *manifest,
uint32_t image_size) size_t offset, size_t len)
{ {
(void)manifest;
(void)offset; (void)offset;
(void)len; (void)len;
(void)image_size;
DEBUG("_suit_flashwrite(): writing %u bytes at pos %u\n", len, offset); DEBUG("_suit_flashwrite(): writing %u bytes at pos %u\n", len, offset);
#if defined(MODULE_PROGRESS_BAR) #if defined(MODULE_PROGRESS_BAR)
uint32_t image_size;
nanocbor_value_t param_size;
suit_param_ref_t *ref_size =
&manifest->components[manifest->component_current].param_size;
/* Grab the total image size from the manifest */
if ((suit_param_ref_to_cbor(manifest, ref_size, &param_size) == 0) ||
(nanocbor_get_uint32(&param_size, &image_size) < 0)) {
/* Early exit if the total image size can't be determined */
return;
}
if (image_size != 0) { if (image_size != 0) {
char _suffix[7] = { 0 }; char _suffix[7] = { 0 };
uint8_t _progress = 100 * (offset + len) / image_size; uint8_t _progress = 100 * (offset + len) / image_size;
@ -419,7 +432,7 @@ int suit_flashwrite_helper(void *arg, size_t offset, uint8_t *buf, size_t len,
return -1; return -1;
} }
_print_download_progress(offset, len, manifest->components[0].size); _print_download_progress(manifest, offset, len);
return riotboot_flashwrite_putbytes(writer, buf, len, more); return riotboot_flashwrite_putbytes(writer, buf, len, more);
} }