1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/sys/suit/v4/cbor.c
Kaspar Schleiser b899a9f362 examples/suit_update: add SUIT draft v4 example & test
This commit adds an example application showcasing SUIT draft v4
firmware updates.

It includes a test script suitable for local or CI testing.

Co-authored-by: Alexandre Abadie <alexandre.abadie@inria.fr>
Co-authored-by: Koen Zandberg <koen@bergzand.net>
Co-authored-by: Francisco Molina <femolina@uc.cl>
2019-10-09 13:51:29 +02:00

276 lines
7.5 KiB
C

/*
* Copyright (C) 2018 Freie Universität Berlin
* Copyright (C) 2018 Inria
* 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.
*/
/**
* @ingroup sys_suit
* @{
*
* @file
* @brief SUIT manifest parser library for CBOR based manifests
*
* @author Koen Zandberg <koen@bergzand.net>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
* @}
*/
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "suit/v4/handlers.h"
#include "suit/v4/suit.h"
#include "suit/v4/policy.h"
#include "cbor.h"
#include "cose/sign.h"
#include "public_key.h"
#include "log.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static suit_manifest_handler_t _manifest_get_auth_wrapper_handler(int key);
typedef suit_manifest_handler_t (*suit_manifest_handler_getter_t)(int key);
int suit_cbor_map_iterate_init(CborValue *map, CborValue *it)
{
if (!cbor_value_is_map(map)) {
LOG_INFO("suit_v4_parse(): manifest not a map\n)");
return SUIT_ERR_INVALID_MANIFEST;
}
cbor_value_enter_container(map, it);
return SUIT_OK;
}
int suit_cbor_map_iterate(CborValue *it, CborValue *key, CborValue *value)
{
if (cbor_value_at_end(it)) {
return 0;
}
*key = *it;
cbor_value_advance(it);
*value = *it;
cbor_value_advance(it);
return 1;
}
int suit_cbor_get_int(const CborValue *it, int *out)
{
if (!cbor_value_is_integer(it)) {
LOG_DEBUG("expected integer type, got %u\n", cbor_value_get_type(it));
return SUIT_ERR_INVALID_MANIFEST;
}
/* This check tests whether the integer fits into "int", thus the check
* is platform dependent. This is for lack of specification of actually
* allowed values, to be made explicit at some point. */
if (cbor_value_get_int_checked(it, out) == CborErrorDataTooLarge) {
LOG_DEBUG("integer doesn't fit into int type\n");
return SUIT_ERR_INVALID_MANIFEST;
}
return SUIT_OK;
}
int suit_cbor_get_string(const CborValue *it, const uint8_t **buf, size_t *len)
{
if (!(cbor_value_is_text_string(it) || cbor_value_is_byte_string(it) || cbor_value_is_length_known(it))) {
return SUIT_ERR_INVALID_MANIFEST;
}
CborValue next = *it;
cbor_value_get_string_length(it, len);
cbor_value_advance(&next);
*buf = next.ptr - *len;
return SUIT_OK;
}
int suit_cbor_get_uint32(const CborValue *it, uint32_t *out)
{
int res;
int64_t val;
if (!cbor_value_is_unsigned_integer(it)) {
return CborErrorIllegalType;
}
if ((res = cbor_value_get_int64_checked(it, &val))) {
return res;
}
if (val > 0xFFFFFFFF) {
return CborErrorDataTooLarge;
}
*out = (val & 0xFFFFFFFF);
return CborNoError;
}
int suit_cbor_get_uint(const CborValue *it, unsigned *out)
{
return suit_cbor_get_uint32(it, (uint32_t *)out);
}
int suit_cbor_subparse(CborParser *parser, CborValue *bseq, CborValue *it)
{
const uint8_t *bytes;
size_t bytes_len = 0;
if (!cbor_value_is_byte_string(bseq)) {
LOG_DEBUG("suit_cbor_subparse(): bseq not a byte string\n");
return -1;
}
suit_cbor_get_string(bseq, &bytes, &bytes_len);
return cbor_parser_init(bytes, bytes_len, SUIT_TINYCBOR_VALIDATION_MODE, parser,
it);
}
static int _v4_parse(suit_v4_manifest_t *manifest, const uint8_t *buf,
size_t len, suit_manifest_handler_getter_t getter)
{
CborParser parser;
CborValue it, map, key, value;
CborError err = cbor_parser_init(buf, len, SUIT_TINYCBOR_VALIDATION_MODE,
&parser, &it);
if (err != 0) {
return SUIT_ERR_INVALID_MANIFEST;
}
map = it;
if (suit_cbor_map_iterate_init(&map, &it) != SUIT_OK) {
LOG_DEBUG("manifest not a map!\n");
return SUIT_ERR_INVALID_MANIFEST;
}
LOG_DEBUG("jumping into map\n)");
while (suit_cbor_map_iterate(&it, &key, &value)) {
int integer_key;
if (suit_cbor_get_int(&key, &integer_key) != SUIT_OK){
return SUIT_ERR_INVALID_MANIFEST;
}
LOG_DEBUG("got key val=%i\n", integer_key);
suit_manifest_handler_t handler = getter(integer_key);
if (handler) {
int res = handler(manifest, integer_key, &value);
LOG_DEBUG("handler res=%i\n", res);
if (res < 0) {
LOG_INFO("handler returned <0\n)");
return SUIT_ERR_INVALID_MANIFEST;
}
}
else {
LOG_DEBUG("no handler found\n");
}
}
cbor_value_leave_container(&map, &it);
return SUIT_OK;
}
int suit_v4_parse(suit_v4_manifest_t *manifest, const uint8_t *buf,
size_t len)
{
manifest->buf = buf;
manifest->len = len;
return _v4_parse(manifest, buf, len, _manifest_get_auth_wrapper_handler);
}
static int _auth_handler(suit_v4_manifest_t *manifest, int key, CborValue *it)
{
(void)key;
const uint8_t *cose_buf;
size_t cose_len = 0;
int res = suit_cbor_get_string(it, &cose_buf, &cose_len);
if (res < 0) {
LOG_INFO("Unable to get COSE signature\n");
return SUIT_ERR_INVALID_MANIFEST;
}
res = cose_sign_decode(&manifest->verify, cose_buf, cose_len);
if (res < 0) {
LOG_INFO("Unable to parse COSE signature\n");
return SUIT_ERR_INVALID_MANIFEST;
}
return 0;
}
static int _manifest_handler(suit_v4_manifest_t *manifest, int key, CborValue *it)
{
(void)key;
const uint8_t *manifest_buf;
size_t manifest_len;
suit_cbor_get_string(it, &manifest_buf, &manifest_len);
/* Validate the COSE struct first now that we have the payload */
cose_sign_decode_set_payload(&manifest->verify, manifest_buf, manifest_len);
/* 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(&manifest->verify, &signature)) {
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,
public_key, NULL, NULL);
LOG_INFO("suit: verifying manifest signature...\n");
int verification = cose_sign_verify(&manifest->verify, &signature,
&pkey, manifest->validation_buf, SUIT_COSE_BUF_SIZE);
if (verification != 0) {
LOG_INFO("Unable to validate signature\n");
return SUIT_ERR_SIGNATURE;
}
return _v4_parse(manifest, manifest_buf,
manifest_len, suit_manifest_get_manifest_handler);
}
static suit_manifest_handler_t _suit_manifest_get_handler(int key,
const suit_manifest_handler_t *handlers,
size_t len)
{
if (key < 0 || (size_t)key >= len) {
return NULL;
}
return handlers[key];
}
/* begin{code-style-ignore} */
static suit_manifest_handler_t _auth_handlers[] = {
[ 0] = NULL,
[ 1] = _auth_handler,
[ 2] = _manifest_handler,
};
/* end{code-style-ignore} */
static const unsigned _auth_handlers_len = ARRAY_SIZE(_auth_handlers);
static suit_manifest_handler_t _manifest_get_auth_wrapper_handler(int key)
{
return _suit_manifest_get_handler(key, _auth_handlers,
_auth_handlers_len);
}