From 41c8ccc67d331e7f45b29083b2bb3d032e12657c Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Fri, 16 Jun 2017 15:15:58 +0200 Subject: [PATCH 01/32] sys/hashes/sha256: add missing unistd.h include --- sys/include/hashes/sha256.h | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/include/hashes/sha256.h b/sys/include/hashes/sha256.h index 0682c7e57b36..adb315238210 100644 --- a/sys/include/hashes/sha256.h +++ b/sys/include/hashes/sha256.h @@ -47,6 +47,7 @@ #define HASHES_SHA256_H #include +#include #ifdef __cplusplus extern "C" { From e76031b5fc1e523a7f2df9ee2bfe53fa3905ae3c Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Thu, 15 Jun 2017 15:41:41 +0200 Subject: [PATCH 02/32] riotboot: introduce riot-based minimal bootloader --- Makefile.dep | 13 ++++++ Makefile.include | 3 ++ dist/riotboot/Makefile | 12 ++++++ dist/riotboot/README.md | 6 +++ dist/riotboot/main.c | 61 +++++++++++++++++++++++++++ makefiles/riotboot.mk | 91 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 186 insertions(+) create mode 100644 dist/riotboot/Makefile create mode 100644 dist/riotboot/README.md create mode 100644 dist/riotboot/main.c create mode 100644 makefiles/riotboot.mk diff --git a/Makefile.dep b/Makefile.dep index 40836f1d6350..5e04ca80e658 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -753,6 +753,19 @@ ifneq (,$(filter uuid,$(USEMODULE))) USEMODULE += random endif +ifneq (,$(RIOTBOOT_SLOT0_SIZE)) + FEATURES_PROVIDED += riotboot +endif + +ifneq (,$(filter ota_%,$(USEMODULE))) + USEMODULE += ota +endif + +ifneq (,$(filter ota_coap,$(USEMODULE))) + USEMODULE += firmware + USEMODULE += nanocoap +endif + # always select gpio (until explicit dependencies are sorted out) FEATURES_OPTIONAL += periph_gpio diff --git a/Makefile.include b/Makefile.include index 7736b8dde52d..be53aca3889d 100644 --- a/Makefile.include +++ b/Makefile.include @@ -628,6 +628,9 @@ CFLAGS += -include '$(RIOTBUILD_CONFIG_HEADER_C)' # include mcuboot support include $(RIOTMAKE)/mcuboot.mk +# include riotboot support +include $(RIOTMAKE)/riotboot.mk + # include Murdock helpers include $(RIOTMAKE)/murdock.inc.mk diff --git a/dist/riotboot/Makefile b/dist/riotboot/Makefile new file mode 100644 index 000000000000..70d6ac8c3051 --- /dev/null +++ b/dist/riotboot/Makefile @@ -0,0 +1,12 @@ +APPLICATION = riotboot + +BOARD ?= samr21-xpro + +USEMODULE += firmware + +CFLAGS += -DNDEBUG -DLOG_LEVEL=LOG_NONE +DISABLE_MODULE += auto_init + +RIOTBASE ?= $(CURDIR)/../.. + +include $(RIOTBASE)/Makefile.include diff --git a/dist/riotboot/README.md b/dist/riotboot/README.md new file mode 100644 index 000000000000..cd6a0ca67ea3 --- /dev/null +++ b/dist/riotboot/README.md @@ -0,0 +1,6 @@ +# Overview + +This folder contains a simple bootloader that allows ping-pong style +over-the-air updates. + +Please see examples/ota as example. diff --git a/dist/riotboot/main.c b/dist/riotboot/main.c new file mode 100644 index 000000000000..c5e3bd478c6e --- /dev/null +++ b/dist/riotboot/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * 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 bootloader + * @{ + * + * @file + * @brief RIOT Bootloader + * + * @author Kaspar Schleiser + * @author Francisco Acosta + * + * @} + */ + +#include "firmware.h" +#include "cpu.h" +#include "panic.h" + +void kernel_init(void) +{ + uint32_t version = 0; + uint32_t slot = 0; + + /* skip slot 0 (which points to the bootloader) */ + for (unsigned i = 1; i < firmware_num_slots; i++) { + firmware_metadata_t *slot_metadata = firmware_get_metadata(i); + if (firmware_validate_metadata_checksum(slot_metadata)) { + /* skip slot if metadata broken */ + continue; + } + if (slot_metadata->start_addr != firmware_get_image_startaddr(i)) { + continue; + } + if (!slot || slot_metadata->version > version) { + version = slot_metadata->version; + slot = i; + } + } + + if (slot) { + firmware_jump_to_slot(slot); + } + + /* serious trouble! */ + while (1) {} +} + +NORETURN void core_panic(core_panic_t crash_code, const char *message) +{ + (void)crash_code; + (void)message; + while (1) {} +} diff --git a/makefiles/riotboot.mk b/makefiles/riotboot.mk new file mode 100644 index 000000000000..36b297c9d4d1 --- /dev/null +++ b/makefiles/riotboot.mk @@ -0,0 +1,91 @@ +ifdef RIOTBOOT_SLOT0_SIZE + +.PHONY: riotboot/flash riotboot/flash-bootloader riotboot/flash-slot1 riotboot/flash-slot2 \ + riotboot/verify-image + +RIOTBOOT ?= $(RIOTBASE)/tools/riotboot/bin/$(BOARD)/riotboot.elf +CFLAGS += -I$(BINDIR)/riotbuild + +RIOTBOOT_SECKEY ?= $(RIOTBASE)/dist/tools/firmware/sec.key +RIOTBOOT_PUBKEY ?= $(RIOTBASE)/dist/tools/firmware/pub.key + +FIRMWARE_TOOL ?= $(RIOTBASE)/dist/tools/firmware/bin/firmware +RIOTBOOT_HDR_LEN := 256 + +APP_ID ?= 0 +APP_VER ?= 0 + +$(BINDIR)/$(APPLICATION)-%.elf: link + $(Q) \ + $(_LINK) \ + $(LINKFLAGPREFIX)--defsym=_rom_offset=$(OFFSET) \ + $(LINKFLAGPREFIX)--defsym=_rom_length=$(LENGTH) \ + -o $@ + +# slot 1 targets +SLOT1_OFFSET := $$(($(RIOTBOOT_SLOT0_SIZE) + $(RIOTBOOT_HDR_LEN))) +SLOT1_LENGTH := $$(($(RIOTBOOT_SLOT1_SIZE) - $(RIOTBOOT_HDR_LEN))) + +$(BINDIR)/$(APPLICATION)-slot1.elf: OFFSET=$(SLOT1_OFFSET) +$(BINDIR)/$(APPLICATION)-slot1.elf: LENGTH=$(SLOT1_LENGTH) + +# slot 2 targets +SLOT2_OFFSET := $$(($(RIOTBOOT_SLOT0_SIZE) + $(RIOTBOOT_SLOT2_SIZE) + $(RIOTBOOT_HDR_LEN))) +SLOT2_LENGTH := $$(($(RIOTBOOT_SLOT2_SIZE) - $(RIOTBOOT_HDR_LEN))) + +$(BINDIR)/$(APPLICATION)-slot2.elf: OFFSET=$(SLOT2_OFFSET) +$(BINDIR)/$(APPLICATION)-slot2.elf: LENGTH=$(SLOT2_LENGTH) + +# signing targets +$(BINDIR)/$(APPLICATION)-%.bin: + @echo "creating $@..." + $(Q) $(OBJCOPY) -Obinary $< $@.tmp + $(Q) $(FIRMWARE_TOOL) sign $@.tmp $(APP_VER) $(APP_ID) $(OFFSET) $(RIOTBOOT_SECKEY) - > $@ + $(Q) cat $@.tmp >> $@ + $(Q) rm $@.tmp + +$(BINDIR)/$(APPLICATION)-slot1.bin: OFFSET=$(SLOT1_OFFSET) +$(BINDIR)/$(APPLICATION)-slot1.bin: $(BINDIR)/$(APPLICATION)-slot1.elf + +$(BINDIR)/$(APPLICATION)-slot2.bin: OFFSET=$(SLOT2_OFFSET) +$(BINDIR)/$(APPLICATION)-slot2.bin: $(BINDIR)/$(APPLICATION)-slot2.elf + +# creating pubkey header file +link: | $(BINDIR)/riotbuild/ota_pubkey.h +$(BINDIR)/riotbuild/ota_pubkey.h: $(RIOTBOOT_PUBKEY) + @mkdir -p $(@D) + @{ \ + echo "static const unsigned char ota_public_key[] = {"; \ + cat $< | xxd -i ; \ + echo "};"; \ + } > $@ + +riotboot: $(BINDIR)/$(APPLICATION)-slot1.bin $(BINDIR)/$(APPLICATION)-slot2.bin $(BINDIR)/riotbuild/ota_pubkey.h + +riotboot/verify-image: + $(FIRMWARE_TOOL) verify $(BINDIR)/$(APPLICATION)-slot1.bin $(RIOTBOOT_PUBKEY) + $(FIRMWARE_TOOL) verify $(BINDIR)/$(APPLICATION)-slot2.bin $(RIOTBOOT_PUBKEY) + +riotboot/flash-bootloader: + $(Q)/usr/bin/env -i \ + PATH=$(PATH) BOARD=$(BOARD) \ + make --no-print-directory -C $(RIOTBASE)/dist/riotboot flash + +riotboot/flash-slot1: FLASH_ADDR=$(RIOTBOOT_SLOT0_SIZE) +riotboot/flash-slot1: HEXFILE=$(BINDIR)/$(APPLICATION)-slot1.bin +riotboot/flash-slot1: $(BINDIR)/$(APPLICATION)-slot1.bin riotboot/flash-bootloader + $(FLASHER) $(FFLAGS) + +riotboot/flash-slot2: FLASH_ADDR=$$(($(RIOTBOOT_SLOT0_SIZE) + $(RIOTBOOT_SLOT1_SIZE))) +riotboot/flash-slot2: HEXFILE=$(BINDIR)/$(APPLICATION)-slot2.bin +riotboot/flash-slot2: $(BINDIR)/$(APPLICATION)-slot2.bin riotboot/flash-bootloader + $(FLASHER) $(FFLAGS) + +riotboot/flash: riotboot/flash-slot1 + +else +riotboot: + $(Q)echo "error: riotboot not supported on board $(BOARD)!" + $(Q)false + +endif # RIOTBOOT_SLOT0_SIZE From d7523e8c2e519e8891cccecb4b1da047ea34d408 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Fri, 16 Jun 2017 15:15:32 +0200 Subject: [PATCH 03/32] sys/firmware: initial import of firmware management functions --- Makefile.dep | 8 + sys/firmware/Makefile | 6 + sys/firmware/firmware.c | 204 +++++++++++++++++++++++++ sys/firmware/update.c | 157 ++++++++++++++++++++ sys/include/firmware.h | 271 ++++++++++++++++++++++++++++++++++ sys/include/firmware_update.h | 117 +++++++++++++++ 6 files changed, 763 insertions(+) create mode 100644 sys/firmware/Makefile create mode 100644 sys/firmware/firmware.c create mode 100644 sys/firmware/update.c create mode 100644 sys/include/firmware.h create mode 100644 sys/include/firmware_update.h diff --git a/Makefile.dep b/Makefile.dep index 5e04ca80e658..9efbd0302bbe 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -766,6 +766,14 @@ ifneq (,$(filter ota_coap,$(USEMODULE))) USEMODULE += nanocoap endif +ifneq (,$(filter firmware,$(USEMODULE))) + USEMODULE += checksum + USEMODULE += hashes + USEPKG += tweetnacl + FEATURES_REQUIRED += riotboot + FEATURES_REQUIRED += periph_flashpage +endif + # always select gpio (until explicit dependencies are sorted out) FEATURES_OPTIONAL += periph_gpio diff --git a/sys/firmware/Makefile b/sys/firmware/Makefile new file mode 100644 index 000000000000..d47a06569849 --- /dev/null +++ b/sys/firmware/Makefile @@ -0,0 +1,6 @@ +CFLAGS += -DFIRMWARE_NUM_SLOTS=3 +CFLAGS += -DSLOT0_SIZE=$(RIOTBOOT_SLOT0_SIZE) +CFLAGS += -DSLOT1_SIZE=$(RIOTBOOT_SLOT1_SIZE) +CFLAGS += -DSLOT2_SIZE=$(RIOTBOOT_SLOT2_SIZE) + +include $(RIOTBASE)/Makefile.base diff --git a/sys/firmware/firmware.c b/sys/firmware/firmware.c new file mode 100644 index 000000000000..1433d496f638 --- /dev/null +++ b/sys/firmware/firmware.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * 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_firmware + * @{ + * + * @file + * @brief Firmware module + * + * @author Kaspar Schleiser + * @author Francisco Acosta + * + * @} + */ + +#include + +#ifdef RIOT_VERSION +#include "cpu.h" +#include "log.h" +#else +#include +#define LOG_INFO(...) printf(__VA_ARGS__) +#endif + +#include "firmware.h" +#include "checksum/fletcher32.h" +#include "tweetnacl.h" + +void firmware_metadata_print(firmware_metadata_t *metadata) +{ + printf("Firmware magic_number: 0x%08x\n", (unsigned)metadata->magic_number); + printf("Firmware Size: %"PRIu32"\n", metadata->size); + printf("Firmware APPID: %#x\n", (unsigned)metadata->appid); + printf("Firmware Version: %#x\n", (unsigned)metadata->version); + printf("Firmware start address: 0x%08x\n", (unsigned)metadata->start_addr); + printf("Firmware HASH: "); + for (unsigned long i = 0; i < sizeof(metadata->hash); i++) { + printf("%02x ", metadata->hash[i]); + } + printf("\n"); + printf("Firmware chksum: 0x%08x\n", (unsigned)metadata->chksum); + printf("Firmware signature: "); + for (unsigned long i = 0; i < sizeof(metadata->sig); i++) { + printf("%02x ", metadata->sig[i]); + } + printf("\n"); +} + +int firmware_validate_metadata_checksum(firmware_metadata_t *metadata) +{ + if (memcmp(metadata, "RIOT", 4)) { + LOG_INFO("%s: metadata magic number invalid\n", __func__); + return -1; + } + + int res = firmware_metadata_checksum(metadata) == metadata->chksum ? 0 : -1; + if (res) { + LOG_INFO("%s: metadata checksum invalid\n", __func__); + } + + return res; +} + +int firmware_validate_metadata_signature(firmware_metadata_t *metadata, const unsigned char *pk) +{ + if (firmware_validate_metadata_checksum(metadata)) { + return -1; + } + + unsigned char sm[FIRMWARE_SIGN_BYTES + crypto_sign_BYTES]; + memcpy(sm, ((unsigned char *)metadata) + FIRMWARE_SIGN_BYTES, crypto_sign_BYTES); + memcpy(sm + crypto_sign_BYTES, metadata, FIRMWARE_SIGN_BYTES); + + unsigned char m[FIRMWARE_SIGN_BYTES + crypto_sign_BYTES]; + unsigned long long mlen; + int res = crypto_sign_open(m, &mlen, sm, FIRMWARE_SIGN_BYTES + crypto_sign_BYTES, pk); + if (res) { + LOG_INFO("%s: metadata signature invalid\n", __func__); + } + return res; +} + +uint32_t firmware_metadata_checksum(firmware_metadata_t *metadata) +{ + return fletcher32((uint16_t*)metadata, FIRMWARE_CHECKSUM_LEN / 2); +} + +int firmware_sign_metadata(firmware_metadata_t *metadata, unsigned char *sk) +{ + unsigned char sm[FIRMWARE_SIGN_BYTES + crypto_sign_BYTES]; + unsigned long long smlen; + crypto_sign(sm, &smlen, (unsigned char*)metadata, FIRMWARE_SIGN_BYTES, sk); + memcpy(metadata->sig, sm, crypto_sign_BYTES); + return 0; +} + +#ifdef RIOT_VERSION +#ifndef BOARD_NATIVE +static const unsigned _firmware_slot_start[] = { + CPU_FLASH_BASE, + CPU_FLASH_BASE + SLOT0_SIZE, + CPU_FLASH_BASE + SLOT0_SIZE + SLOT1_SIZE +}; + +const unsigned firmware_num_slots = sizeof(_firmware_slot_start)/sizeof(unsigned); + +void firmware_jump_to_image(firmware_metadata_t *metadata) +{ + uint32_t addr = (unsigned)metadata + (unsigned)sizeof(firmware_metadata_t); + cpu_jump_to_image(addr); +} + +int firmware_current_slot(void) +{ + unsigned base_addr = cpu_get_image_baseaddr() - sizeof(firmware_metadata_t); + + for (unsigned i = 1; i < firmware_num_slots; i++) { + if (base_addr == _firmware_slot_start[i]) { + return i; + } + } + + return -1; +} + +int firmware_target_slot(void) +{ + /* TODO: find logic for more than one slot */ + return firmware_current_slot() == 1 ? 2 : 1; +} + +firmware_metadata_t *firmware_get_metadata(unsigned slot) { + assert (slot < FIRMWARE_NUM_SLOTS); + return (firmware_metadata_t *)_firmware_slot_start[slot]; +} + +unsigned firmware_get_image_startaddr(unsigned slot) +{ + assert (slot < FIRMWARE_NUM_SLOTS); + return _firmware_slot_start[slot] + sizeof(firmware_metadata_t); +} + +void firmware_jump_to_slot(unsigned slot) +{ + firmware_jump_to_image(firmware_get_metadata(slot)); +} + +void firmware_dump_slot_addrs(void) +{ + for (unsigned i = 1; i < FIRMWARE_NUM_SLOTS; i++) { + printf("slot %u: metadata: 0x%08x image: 0x%08x\n", i, \ + (unsigned)_firmware_slot_start[i], firmware_get_image_startaddr(i)); + } +} +#else /* BOARD_NATIVE */ +const unsigned firmware_num_slots = 1; + +void firmware_jump_to_image(firmware_metadata_t *metadata) +{ + (void)metadata; + printf("%s native stub\n", __func__); +} + +int firmware_current_slot(void) +{ + printf("%s native stub\n", __func__); + + return 1; +} + +firmware_metadata_t *firmware_get_metadata(unsigned slot) +{ + (void)slot; + printf("%s native stub\n", __func__); + return NULL; +} + +unsigned firmware_get_image_startaddr(unsigned slot) +{ + (void)slot; + printf("%s native stub\n", __func__); + return 0; +} + +void firmware_jump_to_slot(unsigned slot) +{ + (void)slot; + printf("%s native stub\n", __func__); +} + +void firmware_dump_slot_addrs(void) +{ + printf("%s native stub\n", __func__); +} +#endif +#endif /* RIOT_VERSION */ diff --git a/sys/firmware/update.c b/sys/firmware/update.c new file mode 100644 index 000000000000..89b0bab13e0b --- /dev/null +++ b/sys/firmware/update.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * + * 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_firmware + * @{ + * + * @file + * @brief Firmware update helper functions + * + * @author Kaspar Schleiser + * + * @} + */ + +#include + +#include "firmware_update.h" +#include "log.h" +#include "ota_pubkey.h" +#include "od.h" + +static inline size_t min(size_t a, size_t b) +{ + return a <= b ? a : b; +} + +int firmware_update_init(firmware_update_t *firmware_update, int target_slot) +{ + LOG_INFO("ota: initializing update to target slot %i\n", + target_slot); + + memset(firmware_update, 0, sizeof(firmware_update_t)); + + firmware_update->target_slot = target_slot; + firmware_update->flashpage = flashpage_page(firmware_get_metadata(target_slot)); + firmware_update->state = FIRMWARE_UPDATE_INITIALIZED; + + return 0; +} + +int firmware_update_putbytes(firmware_update_t *state, size_t offset, const uint8_t *bytes, size_t len) +{ + firmware_metadata_t *metadata = &state->metadata_buf; + + LOG_DEBUG("ota: processing bytes %u-%u\n", state->offset, len); + + while (len) { + size_t flashpage_pos = state->offset % FLASHPAGE_SIZE; + size_t flashpage_avail = FLASHPAGE_SIZE - flashpage_pos; + + size_t left = 1; + + if (state->state == FIRMWARE_UPDATE_VERIFIED) { + left = (metadata->size + sizeof(firmware_metadata_t) - offset); + len = min(len, left); + } + + size_t to_copy = min(flashpage_avail, len); + + memcpy(state->flashpage_buf + flashpage_pos, bytes, to_copy); + flashpage_avail -= to_copy; + + state->offset += to_copy; + flashpage_pos += to_copy; + bytes += to_copy; + len -= to_copy; + left -= to_copy; + + if ((state->state != FIRMWARE_UPDATE_VERIFIED) && ((offset + to_copy) >= sizeof(firmware_metadata_t))) { + /* copy metadata from flashpage buffer to metadata buffer */ + memcpy(metadata, state->flashpage_buf, sizeof(firmware_metadata_t)); + + LOG_INFO("ota: verifying metadata ...\n"); + if (metadata->start_addr != firmware_get_image_startaddr(state->target_slot)) { + LOG_WARNING("ota: start address doesn't match selected slot. Aborting.\n"); + LOG_WARNING("ota: (image start=%p slot start=%p)\n", (void *)metadata->start_addr, \ + (void *)firmware_get_image_startaddr(state->target_slot)); + return -1; + } + + /* check metadata magic nr, checksum and signature */ + if (firmware_validate_metadata_signature(metadata, ota_public_key)) { + LOG_WARNING("ota: verification failed!\n"); + return -1; + } + else { + state->state = FIRMWARE_UPDATE_VERIFIED; + LOG_INFO("ota: verification successful\n"); + } + + /* clear metadata from flashpage */ + memset(state->flashpage_buf, '\0', sizeof(firmware_metadata_t)); + } + + if ((!flashpage_avail) || (!left)) { + LOG_DEBUG("ota: writing to flashpage %u\n", state->flashpage); + if (flashpage_write_and_verify(state->flashpage, state->flashpage_buf) != FLASHPAGE_OK) { + LOG_WARNING("ota: error writing flashpage %u!\n", state->flashpage); + return -1; + } + state->flashpage++; + } + } + + return 0; +} + +int firmware_update_finish(firmware_update_t *state) +{ + int res = -1; + + void *slot_start = firmware_get_metadata(state->target_slot); + firmware_metadata_t *metadata = &state->metadata_buf; + + LOG_DEBUG("ota: verifying hash...\n"); + sha256_init(&state->sha256); + sha256_update(&state->sha256, slot_start + sizeof(firmware_metadata_t), metadata->size); + + sha256_final(&state->sha256, state->flashpage_buf); + + if (memcmp(state->flashpage_buf, metadata->hash, SHA256_DIGEST_LENGTH)) { + LOG_WARNING("ota: image hash incorrect!\n"); + goto out; + } + + LOG_DEBUG("ota: hash verified, re-flashing first block...\n"); + void *firstpage; + if (sizeof(firmware_metadata_t) < FLASHPAGE_SIZE) { + firstpage = state->flashpage_buf; + memcpy(firstpage, metadata, sizeof(firmware_metadata_t)); + memcpy(firstpage + sizeof(firmware_metadata_t), \ + slot_start + sizeof(firmware_metadata_t), \ + FLASHPAGE_SIZE - sizeof(firmware_metadata_t)); + } + else { + firstpage = metadata; + } + + int flashpage = flashpage_page(slot_start); + if (flashpage_write_and_verify(flashpage, firstpage) != FLASHPAGE_OK) { + LOG_WARNING("ota: re-flashing first block failed!\n"); + goto out; + } + + LOG_INFO("ota: firmware flashing completed successfully\n"); + res = 0; + +out: + state->state = FIRMWARE_UPDATE_IDLE; + return res; +} diff --git a/sys/include/firmware.h b/sys/include/firmware.h new file mode 100644 index 000000000000..d7104d57067e --- /dev/null +++ b/sys/include/firmware.h @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2018 Kaspar Schleiser + * 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. + */ + +/** + * @defgroup sys_firmware Firmware Updates + * @ingroup sys + * @{ + * + * # riotboot minimal firmware update infrastructure + * + * ## Overview + * + * riotboot the name of a minimal bootloader application and infrastructure. It + * consists of + * + * - the application "riotboot" in "dist/riotboot" which serves as + * minimal bootloader + * + * - the modules "firmware" and "firmware_update" that are used + * in an image to handle verification and flash-writing + * + * - the module "ota_coap" which serves as transport for the updates + * + * - a tool in dist/tools/formware which handles key generation + * and firmware image signing. + * + * - a couple of make targets to glue everything together + * + * ## Concept + * + * riotboot expects the flash to be split in three parts: one for the + * bootloader itself, and two slots for images. + * Of the latter, one slot holds the running (or latest) image, while the other is + * used to write a newer image. + * + * The bootloader will, on reset, verify the checksum of the image slots, then + * choose the one with the highest version number that has a valid checksum. If + * all slots have the same version number, the first one will be booted. If none + * of the slots has a valid checksum, no image will be booted and the bootloader + * will enter "while(1);". + * + * Any image that the bootloader can load is supposed to be signed by the firmware + * tool, which will take an image, sign it using tweetnacl prefixes the image with + * corresponding metadata. + * + * The metadata contains + * + * - "RIOT" as magic number + * - an application version + * - an application ID + * - a checksum + * - a signature + * + * The bootloader only cares about checksum and application version. It expects a + * running image to verify the metadata. + * + * All image transportation and verification is supposed to be done by a running + * image. + * + * The user must update using a binary that has been linked & signed for the + * desired target slot. + * The slot that contains the running image cannot be overwritten. + * + * The module "firmware_update" provides a simple API that can be fed image data + * in arbitrary block sizes. The API is similar to stream hashing. There's + * "firmware_update_init()", "firmware_update_put_bytes()" and + * "firmware_update_finish()". The module transparently handles safe image + * verification and flah writing. + * + * A tool in dist/tools/firmware can create the necessary keys using tweetnacl, + * and also sign/verify firmware images. + * + * A module in sys/firmware contains common code for firmware metadata handling. + * + * ## Threat vector + * + * If properly used, riotboot allows secure updates of running firmware. + * Only update requests that have a valid signature will cause a write to flash. + * Only updates with a higher version number will be considered, protecting from + * replay or downgrade attacks, given a node is running the latest version. + * Updates cannot put the node in undefined state if an upgrade is aborted at any + * time or otherwise invalid. + * Only correctly written and verified updates are considered by the bootloader. + * + * ## Usage + * + * riotboot needs a public key to be compiled in the image for verification purposes. + * Create a keypair using dist/tools/firware. + * + * riotboot needs some board specific support, mainly slot size configuration, in + * order to work for a specific board. + * + * If that support is in place, it is easiest to use the riotboot specific make + * targets in order to get the support on the device. + * + * See README.md in examples/ota for examples. + * + * @file + * @brief Firmware Updates + * + * @author Kaspar Schleiser + * @author Francisco Acosta + * + * @} + */ + +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "hashes/sha256.h" +#include "tweetnacl.h" + +/** + * @brief Provisional length for signed hash + */ +#define FIRMWARE_SIG_LEN (crypto_sign_BYTES) + +/** + * @brief Length of secret Ed25519 key + */ +#define FIRMWARE_SECKEY_LEN (crypto_sign_SECRETKEYBYTES) + +/** + * @brief Length of public Ed25519 key + */ +#define FIRMWARE_PUBKEY_LEN (crypto_sign_PUBLICKEYBYTES) + +/** + * @brief Number of metadata bytes that get checksummed + */ +#define FIRMWARE_CHECKSUM_LEN (20) + +/** + * @brief Length of bytes to be signed + */ +#define FIRMWARE_SIGN_BYTES (FIRMWARE_CHECKSUM_LEN + 4 + SHA256_DIGEST_LENGTH) + +/** + * @brief Length of metadata prefixed to firmware binaries + */ +#ifndef FIRMWARE_METADATA_SIZE +#define FIRMWARE_METADATA_SIZE (256) +#endif + +/** + * @brief Length of padding to align metadata size + */ +#define FIRMWARE_PADDING (FIRMWARE_METADATA_SIZE-(FIRMWARE_SIGN_BYTES + FIRMWARE_SIG_LEN)) + +/** + * @brief Real length of metadata without padding + */ +#define FIRMWARE_METADATA_REALLEN (sizeof(firmware_metadata_t) - FIRMWARE_PADDING) + +/** + * @brief Structure to store firmware metadata + * @{ + */ +typedef struct { + uint32_t magic_number; /**< metadata magic_number (always "RIOT") */ + uint32_t appid; /**< Integer representing the application ID*/ + uint32_t version; /**< Integer representing firmware version */ + uint32_t size; /**< Size of firmware image (w/o metadata) */ + uint32_t start_addr; /**< Start address in flash */ + uint32_t chksum; /**< checksum of metadata */ + uint8_t hash[SHA256_DIGEST_LENGTH]; /**< SHA256 Hash of firmware image */ + uint8_t sig[FIRMWARE_SIG_LEN]; /**< Firmware signature */ + uint8_t pad[FIRMWARE_PADDING]; /**< padding to total of + FIRMWARE_METADATA_SIZE bytes */ +} firmware_metadata_t; +/** @} */ + +/** + * @brief Print formatted FW image metadata to STDIO + * + * @param[in] metadata ptr to firmware metadata + * + */ +void firmware_metadata_print(firmware_metadata_t *metadata); + +/** + * @brief Validate FW image metadata + * + * @param[in] metadata ptr to firmware metadata + * + */ +int firmware_validate_metadata_checksum(firmware_metadata_t *metadata); + +/** + * @brief Calculate metadata checksum + * + * @param[in] metadata ptr to firmware metadata + * + */ +uint32_t firmware_metadata_checksum(firmware_metadata_t *metadata); + +/** + * @brief Sign metadata + * + * @param[in] metadata ptr to firmware metadata + * @param[in] sk NaCL secret signing key to use + * + */ +int firmware_sign_metadata(firmware_metadata_t *metadata, unsigned char *sk); + +/** + * @brief Validate FW metadata signature + * + * @param[in] metadata ptr to firmware metadata + * @param[in] pk NaCL public signing key to use + * + */ +int firmware_validate_metadata_signature(firmware_metadata_t *metadata, const unsigned char *pk); + +/** + * @brief Jump to image + * + * @param[in] metadata ptr to firmware metadata + * + */ +void firmware_jump_to_image(firmware_metadata_t *metadata); + +/** + * @brief Get currently running image slot + */ +int firmware_current_slot(void); + +/** + * @brief Get next (to be empty) image slot + */ +int firmware_target_slot(void); + +/** + * @brief Get metadata of firmware slot + */ +firmware_metadata_t *firmware_get_metadata(unsigned slot); + +/** + * @brief Get jump-to address of firmware slot + */ +unsigned firmware_get_image_startaddr(unsigned slot); + +/** + * @brief Boot into image in slot @p slot + */ +void firmware_jump_to_slot(unsigned slot); + +/** + * @brief Dump firmware slot addresses + */ +void firmware_dump_slot_addrs(void); + +extern const unsigned firmware_num_slots; + +#ifdef __cplusplus +} +#endif + +#endif /* FIRMWARE_H */ diff --git a/sys/include/firmware_update.h b/sys/include/firmware_update.h new file mode 100644 index 000000000000..3085d92d37fa --- /dev/null +++ b/sys/include/firmware_update.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2018 Kaspar Schleiser + * + * 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_firmware + * @{ + * + * @file + * @brief Over the air (OTA) update module + * + * This module provides an API to verify and write an updated firmware to flash. + * + * The API is similar to stream hashing functions: + * + * 1. initialize state structure using firmware_update_init() + * 2. put some data using firmware_update_putbytes() + * 3. repeat 2. until all data has been received + * 4. finish update using firmware_update_finish() + * + * The module will *not* automatically reboot after an image has been + * successfully written. + * + * Under the hood, the module tries to abstract verification of the + * cryptographic image signature and writing to flash. + * Should the metadata or its signature be invalid, firmware_put_bytes() will + * return -1. + * + * The module makes sure that at no point in time an invalid image is bootable. + * The algorithm for that makes use of the bootloader verifying checksum and + * works as follows: + * + * 1. erase first block (making its checksum invalid) + * 2. write image + * 3. as soon as the whole metadata has been received, check it, abort on error + * 4. check hash of (first block + image), abort on error + * 5. write first block + * + * @author Kaspar Schleiser + * + * @} + */ + +#ifndef FIRMWARE_UPDATE_H +#define FIRMWARE_UPDATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "firmware.h" +#include "periph/flashpage.h" + +/** + * @brief Possible firmware update states + */ +enum { + FIRMWARE_UPDATE_IDLE, /**< no firmware update in progress */ + FIRMWARE_UPDATE_INITIALIZED, /**< firmware update in progress, awaiting + verification */ + FIRMWARE_UPDATE_VERIFIED, /**< firmware update in progress & verified */ +}; + +/** + * @brief firmware update state structure + */ +typedef struct { + unsigned state; /**< state (see above enum) */ + int target_slot; /**< update targets this slot */ + size_t offset; /**< update is at this position */ + unsigned flashpage; /**< update is at this flashpage */ + sha256_context_t sha256; /**< sha256 context used by this update */ + uint8_t flashpage_buf[FLASHPAGE_SIZE]; /**< flash writing buffer */ + firmware_metadata_t metadata_buf; /**< metadata scratch buffer */ +} firmware_update_t; + +/** + * @brief Initialize firmware update + * + * @param[in/out] state ptr to preallocated state structure + * @param[in] target_slot slot to write update into + * + * @returns 0 on success, <0 otherwise + */ +int firmware_update_init(firmware_update_t *state, int target_slot); + +/** + * @brief Feed bytes into update process + * + * @param[in/out] state ptr to previously used update state + * @param[in] offset offset of @p bytes (from image start) + * @param[in] bytes ptr to data + * @param[in] len len of data + * + * @returns 0 on success, <0 otherwise + */ +int firmware_update_putbytes(firmware_update_t *state, size_t offset + const uint8_t *bytes, size_t len); + +/** + * @brief Finish a firmware update + * + * @param[in] state ptr to previously used state structure + * + * @returns 0 on success, <0 otherwise + */ +int firmware_update_finish(firmware_update_t *state); + +#ifdef __cplusplus +} +#endif + +#endif /* FIRMWARE_UPDATE_H */ From 1a6c9258e4959d5d1e699e8c10d95b4709b2236d Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Fri, 16 Jun 2017 15:16:36 +0200 Subject: [PATCH 04/32] dist/tools/firmware: initial import of firmware metadata tools --- dist/tools/firmware/Makefile | 45 +++++++++++++ dist/tools/firmware/common.c | 92 ++++++++++++++++++++++++++ dist/tools/firmware/common.h | 27 ++++++++ dist/tools/firmware/genkeys.c | 39 +++++++++++ dist/tools/firmware/main.c | 39 +++++++++++ dist/tools/firmware/randombytes.c | 62 ++++++++++++++++++ dist/tools/firmware/sign.c | 104 ++++++++++++++++++++++++++++++ dist/tools/firmware/verify.c | 67 +++++++++++++++++++ 8 files changed, 475 insertions(+) create mode 100644 dist/tools/firmware/Makefile create mode 100644 dist/tools/firmware/common.c create mode 100644 dist/tools/firmware/common.h create mode 100644 dist/tools/firmware/genkeys.c create mode 100644 dist/tools/firmware/main.c create mode 100644 dist/tools/firmware/randombytes.c create mode 100644 dist/tools/firmware/sign.c create mode 100644 dist/tools/firmware/verify.c diff --git a/dist/tools/firmware/Makefile b/dist/tools/firmware/Makefile new file mode 100644 index 000000000000..3e8626bdeff0 --- /dev/null +++ b/dist/tools/firmware/Makefile @@ -0,0 +1,45 @@ +PKG_NAME = tweetnacl +PKG_URL = https://github.com/RIOT-OS/tweetnacl +PKG_VERSION = 7ea05c7098a16c87fa66e9166ce301666f3f2623 +PKG_LICENSE = PD +PKG_BUILDDIR = bin/tweetnacl_src +GITCACHE = ../git/git-cache + +RIOTBASE := ../../.. +RIOT_INCLUDE := $(RIOTBASE)/sys/include +SHA256_DIR := $(RIOTBASE)/sys/hashes +SHA256_INCLUDE := $(RIOT_INCLUDE)/hashes +TWEETNACL_DIR := $(PKG_BUILDDIR) +TWEETNACL_SRC := $(TWEETNACL_DIR)/tweetnacl.c randombytes.c +TWEETNACL_HDR := $(TWEETNACL_DIR)/tweetnacl.h +COMMON_SRC := common.c +COMMON_HDR := common.h + +RIOT_FIRMWARE_SRC := \ + $(SHA256_DIR)/sha256.c \ + $(RIOTBASE)/sys/checksum/fletcher32.c \ + $(RIOTBASE)/sys/firmware/firmware.c + +RIOT_FIRMWARE_HDR := $(RIOT_INCLUDE)/firmware.h \ + $(RIOT_INCLUDE)/hashes/sha256.h \ + $(RIOT_INCLUDE)/checksum/fletcher32.h + +FIRMWARE_SRC := $(COMMON_SRC) $(TWEETNACL_SRC) $(RIOT_FIRMWARE_SRC) \ + main.c verify.c genkeys.c sign.c + +FIRMWARE_HDR := $(COMMON_HDR) $(RIOT_FIRMWARE_HDR) + +CFLAGS += -g -I. -O3 -Wall -Wextra -pedantic -std=c99 + +all: bin/firmware + +bin/: + mkdir -p bin + +bin/firmware: git-download $(FIRMWARE_HDR) $(FIRMWARE_SRC) Makefile | bin/ + $(CC) $(CFLAGS) -I$(RIOT_INCLUDE) -I$(TWEETNACL_DIR) $(FIRMWARE_SRC) -o $@ + +clean:: + rm -rf bin/firmware + +include $(RIOTBASE)/pkg/pkg.mk diff --git a/dist/tools/firmware/common.c b/dist/tools/firmware/common.c new file mode 100644 index 000000000000..98b7ce0245f4 --- /dev/null +++ b/dist/tools/firmware/common.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file LICENSE for more details. + */ + +#include +#include +#include +#include + +#include "hashes/sha256.h" + +off_t fsize(const char *filename) +{ + struct stat st; + + if (stat(filename, &st) == 0) { + return st.st_size; + } + + return -1; +} + +int to_file(const char *filename, void *buf, size_t len) +{ + int fd; + + if (strcmp("-", filename)) { + fd = open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); + } + else { + fd = STDOUT_FILENO; + } + + if (fd > 0) { + ssize_t res = write(fd, buf, len); + close(fd); + return res == (ssize_t)len; + } + else { + return fd; + } +} + +int from_file(const char *filename, void *buf, size_t len) +{ + int fd = open(filename, O_RDONLY); + + if (fd > 0) { + ssize_t res = read(fd, buf, len); + close(fd); + return res == (ssize_t)len; + } + else { + return fd; + } +} + +ssize_t do_sha256(const char *filename, void *tgt, off_t offset) +{ + sha256_context_t sha256; + + sha256_init(&sha256); + + ssize_t bytes_read; + ssize_t total = 0; + char buf[1024]; + + int fd = open(filename, O_RDONLY); + if (!fd) { + return -1; + } + + if (offset) { + if (lseek(fd, offset, SEEK_SET) == -1) { + return -1; + } + } + + while ((bytes_read = read(fd, buf, sizeof(buf)))) { + sha256_update(&sha256, buf, bytes_read); + total += bytes_read; + } + + sha256_final(&sha256, tgt); + + close(fd); + + return total; +} diff --git a/dist/tools/firmware/common.h b/dist/tools/firmware/common.h new file mode 100644 index 000000000000..078b87d2f60d --- /dev/null +++ b/dist/tools/firmware/common.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file LICENSE for more details. + */ + +#ifndef COMMON_H +#define COMMON_H + +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +off_t fsize(const char *filename); +int to_file(const char *filename, void *buf, size_t len); +int from_file(const char *filename, void *buf, size_t len); +int do_sha256(const char *filename, void *tgt, size_t offset); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* COMMON_H */ diff --git a/dist/tools/firmware/genkeys.c b/dist/tools/firmware/genkeys.c new file mode 100644 index 000000000000..82cf5a0a3df3 --- /dev/null +++ b/dist/tools/firmware/genkeys.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file LICENSE for more details. + */ + +#include + +#include "common.h" +#include "tweetnacl.h" + +const char genkeys_usage[] = "firmware genkeys "; + +int genkeys(int argc, const char *argv[]) +{ + unsigned char pk[crypto_sign_PUBLICKEYBYTES]; + unsigned char sk[crypto_sign_SECRETKEYBYTES]; + + if (argc < 3) { + fprintf(stderr, "usage: %s\n", genkeys_usage); + return 1; + } + + crypto_sign_keypair(pk,sk); + + const char err[] = "error writing to %s\n"; + if (!to_file(argv[1], sk, sizeof(sk))) { + fprintf(stderr, err, argv[1]); + return -1; + } + + if (!to_file(argv[2], pk, sizeof(pk))) { + fprintf(stderr, err, argv[2]); + return -1; + } + + return 0; +} diff --git a/dist/tools/firmware/main.c b/dist/tools/firmware/main.c new file mode 100644 index 000000000000..9106787ce0bd --- /dev/null +++ b/dist/tools/firmware/main.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file LICENSE for more details. + */ + +#include +#include + +int verify(int argc, char *argv[]); +extern const char verify_usage[]; + +int genkeys(int argc, char *argv[]); +extern const char genkeys_usage[]; + +int sign(int argc, char *argv[]); +extern const char sign_usage[]; + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + goto usage; + } + if (!strcmp(argv[1], "verify")) { + return verify(argc, argv); + } else if (!strcmp(argv[1], "genkeys")) { + return genkeys(argc - 1, &argv[1]); + } else if (!strcmp(argv[1], "sign")) { + return sign(argc - 1, &argv[1]); + } + +usage: + fprintf(stderr, "usage: %s\n" \ + " %s\n" \ + " %s\n", \ + genkeys_usage, sign_usage, verify_usage); + return 1; +} diff --git a/dist/tools/firmware/randombytes.c b/dist/tools/firmware/randombytes.c new file mode 100644 index 000000000000..e9170bb9ae6a --- /dev/null +++ b/dist/tools/firmware/randombytes.c @@ -0,0 +1,62 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * randombytes/devurandom.h version 20080713 + * D. J. Bernstein + * Public domain. + */ + +#include +#include +#include +#include + +/* it's really stupid that there isn't a syscall for this */ + +static int fd = -1; + +void randombytes(unsigned char *x,unsigned long long xlen) +{ + int i; + + if (fd == -1) { + for (;;) { + fd = open("/dev/urandom",O_RDONLY); + if (fd != -1) break; + sleep(1); + } + } + + while (xlen > 0) { + if (xlen < 1048576) i = xlen; else i = 1048576; + + i = read(fd,x,i); + if (i < 1) { + sleep(1); + continue; + } + + x += i; + xlen -= i; + } +} diff --git a/dist/tools/firmware/sign.c b/dist/tools/firmware/sign.c new file mode 100644 index 000000000000..eed159206e56 --- /dev/null +++ b/dist/tools/firmware/sign.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017 Kaspar Schleiser + * 2016 Mark Solters + * 2016 Inria + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/** + * @ingroup FW + * @file + * @brief Meta-data generation and signing for FW images + * + * @author Mark Solters + * @author Francisco Acosta + * @author Kaspar Schleiser + */ + +#include +#include +#include +#include + +#include "firmware.h" +#include "common.h" + +const char sign_usage[] = "firmware sign "; + +int sign(int argc, char *argv[]) +{ + firmware_metadata_t metadata; + unsigned char sk[FIRMWARE_SECKEY_LEN]; + ssize_t firmware_size; + + memset(&metadata, '\0', sizeof(firmware_metadata_t)); + + if (argc < 7) { + fprintf(stderr, "usage: %s\n", sign_usage); + return -1; + } + + if (!from_file(argv[5], sk, sizeof(sk))) { + fprintf(stderr, "Error: cannot read keyfile\n"); + return(1); + } + + firmware_size = do_sha256(argv[1], metadata.hash, 0); + if (firmware_size == -1) { + fprintf(stderr, "Error: cannot read firmware file\n"); + return(1); + } + + /* Generate FW image metadata */ + memcpy(&metadata.magic_number, "RIOT", 4); + metadata.size = firmware_size; + sscanf(argv[2], "%x", (unsigned int *)&(metadata.version)); + sscanf(argv[3], "%x", &(metadata.appid)); + sscanf(argv[4], "%u", &(metadata.start_addr)); + + /* calculate metadata checksum */ + metadata.chksum = firmware_metadata_checksum(&metadata); + + /* sign */ + firmware_sign_metadata(&metadata, sk); + + /* talk to the user */ + if (strcmp(argv[6], "-")) { + firmware_metadata_print(&metadata); + printf("Metadata size: %lu\n", sizeof(firmware_metadata_t)); + } + + /* Write the metadata */ + if (!to_file(argv[6], &metadata, sizeof(firmware_metadata_t))) { + fprintf(stderr, "Error: cannot write output\n"); + return(1); + } + + return 0; +} diff --git a/dist/tools/firmware/verify.c b/dist/tools/firmware/verify.c new file mode 100644 index 000000000000..a1032aea9725 --- /dev/null +++ b/dist/tools/firmware/verify.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file LICENSE for more details. + */ + +#include +#include +#include + +#include "firmware.h" +#include "common.h" +#include "tweetnacl.h" + +const char verify_usage[] = "firmware verify "; + +int verify(int argc, char *argv[]) +{ + unsigned char pubkey[FIRMWARE_PUBKEY_LEN]; + firmware_metadata_t metadata; + + if (argc < 4) { + goto usage; + } + + if (!from_file(argv[2], &metadata, sizeof(metadata))) { + fprintf(stderr, "error reading image file\n"); + return 1; + } + + if (!from_file(argv[3], pubkey, sizeof(pubkey))) { + fprintf(stderr, "error reading keyfile\n"); + return 1; + } + + int res = firmware_validate_metadata_signature(&metadata, pubkey) ? 1 : 0; + if (res) { + printf("signature check failed\n"); + return -1; + } else { + printf("signature check passed\n"); + } + + off_t size = fsize(argv[2]); + if (size == sizeof(firmware_metadata_t)) { + printf("hash check skipped\n"); + goto out; + } + + char hash[SHA256_DIGEST_LENGTH]; + do_sha256(argv[2], hash, sizeof(firmware_metadata_t)); + res = memcmp(hash, metadata.hash, SHA256_DIGEST_LENGTH) ? 1 : 0; + if (res) { + printf("hash check failed\n"); + } else { + printf("hash check passed\n"); + } + +out: + return res; + +usage: + fprintf(stderr, "usage: %s\n", verify_usage); + return 1; +} + From 9ee8444a5de93934aa9fa8c3daa800614e54c735 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Thu, 5 Oct 2017 17:13:11 +0200 Subject: [PATCH 05/32] cpu/cortexm_common: set MSP instead of PSP in cpu_jump_to_image() --- cpu/cortexm_common/include/cpu.h | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/cpu/cortexm_common/include/cpu.h b/cpu/cortexm_common/include/cpu.h index 48e355ea2e2c..5b45e7794731 100644 --- a/cpu/cortexm_common/include/cpu.h +++ b/cpu/cortexm_common/include/cpu.h @@ -126,6 +126,44 @@ static inline void cortexm_isr_end(void) } } +/** + * @brief Jumps to another image in flash + * + * This function is supposed to be called by a bootloader application. + * + * @param[in] image_address address in flash of other image + */ +static inline void cpu_jump_to_image(uint32_t image_address) +{ + /* Disable IRQ */ + __disable_irq(); + + /* set MSP */ + __set_MSP(*(uint32_t*)image_address); + + /* skip stack pointer */ + image_address += 4; + + /* load the images reset_vector address */ + uint32_t destination_address = *(uint32_t*)image_address; + + /* Make sure the Thumb State bit is set. */ + destination_address |= 0x1; + + /* Branch execution */ + __asm("BX %0" :: "r" (destination_address)); +} + +/* The following register is only present for Cortex-M0+, -M3, -M4 and -M7 CPUs */ +#if defined(CPU_ARCH_CORTEX_M0PLUS) || defined(CPU_ARCH_CORTEX_M3) || \ + defined(CPU_ARCH_CORTEX_M4) || defined(CPU_ARCH_CORTEX_M4F) || \ + defined(CPU_ARCH_CORTEX_M7) +static inline unsigned cpu_get_image_baseaddr(void) +{ + return (unsigned)SCB->VTOR; +} +#endif + #ifdef __cplusplus } #endif From 68060854cbb5b921ecac404e6f94eec824da3e95 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Wed, 7 Feb 2018 16:50:59 +0100 Subject: [PATCH 06/32] edbg: add FLASH_ADDR as offset, don't erase before flash --- makefiles/tools/edbg.inc.mk | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/makefiles/tools/edbg.inc.mk b/makefiles/tools/edbg.inc.mk index 5738b49cd0a3..dc322653ac10 100644 --- a/makefiles/tools/edbg.inc.mk +++ b/makefiles/tools/edbg.inc.mk @@ -10,7 +10,10 @@ HEXFILE = $(BINFILE) ifneq (,$(DEBUG_ADAPTER_ID)) EDBG_ARGS += --serial $(DEBUG_ADAPTER_ID) endif -FFLAGS ?= $(EDBG_ARGS) -t $(EDBG_DEVICE_TYPE) -b -e -v -p -f $(HEXFILE) + +EDBG_ARGS += -o $(FLASH_ADDR) + +FFLAGS ?= $(EDBG_ARGS) -t $(EDBG_DEVICE_TYPE) -b -v -p -f $(HEXFILE) ifeq ($(RIOT_EDBG),$(FLASHER)) FLASHDEPS += $(RIOT_EDBG) From cbf324a66d0c925dd02eca290e17c350945e97b2 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Wed, 7 Feb 2018 16:51:56 +0100 Subject: [PATCH 07/32] cpu/cortexm: change rom offset logic in cortexm.ld --- cpu/cortexm_common/ldscripts/cortexm.ld | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cpu/cortexm_common/ldscripts/cortexm.ld b/cpu/cortexm_common/ldscripts/cortexm.ld index cb19c10935b1..3b9e4c28bece 100644 --- a/cpu/cortexm_common/ldscripts/cortexm.ld +++ b/cpu/cortexm_common/ldscripts/cortexm.ld @@ -1,5 +1,6 @@ /* - * Copyright (C) 2017 Inria + * Copyright (C) 2018 Kaspar Schleiser + * 2017 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 @@ -14,16 +15,17 @@ * @brief Memory definitions for the Cortex-M family * * @author Francisco Acosta + * @author Kaspar Schleiser * * @} */ -_boot_offset = DEFINED( _rom_offset ) ? _rom_offset : 0x0 ; +_rom_offset = DEFINED( _rom_offset ) ? _rom_offset : 0x0 ; MEMORY { - rom (rx) : ORIGIN = _rom_start_addr + _boot_offset, LENGTH = _rom_length - _boot_offset - ram (w!rx) : ORIGIN = _ram_start_addr, LENGTH = _ram_length + rom (rx) : ORIGIN = _rom_start_addr + _rom_offset, LENGTH = _rom_length + ram (w!rx) : ORIGIN = _ram_start_addr, LENGTH = _ram_length } INCLUDE cortexm_base.ld From 17d5596ace906fcf5bd25a5d312bc03900751a65 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Wed, 7 Feb 2018 16:52:36 +0100 Subject: [PATCH 08/32] boards/samr21-xpro: add riotboot configuration --- boards/samr21-xpro/Makefile.include | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/boards/samr21-xpro/Makefile.include b/boards/samr21-xpro/Makefile.include index ba753cc0a92b..9b7e7924ba32 100644 --- a/boards/samr21-xpro/Makefile.include +++ b/boards/samr21-xpro/Makefile.include @@ -5,4 +5,9 @@ export CPU_MODEL = samr21g18a # set edbg device type EDBG_DEVICE_TYPE = atmel_cm0p +# configure riotboot slots +export RIOTBOOT_SLOT0_SIZE ?= 0x1000 +export RIOTBOOT_SLOT1_SIZE ?= 0x1f800 +export RIOTBOOT_SLOT2_SIZE ?= 0x1f800 + include $(RIOTMAKE)/boards/sam0.inc.mk From 09a6a3d69d6e283c49eb6637f37fb3ed489f78cd Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Wed, 14 Mar 2018 13:49:49 +0100 Subject: [PATCH 09/32] sys/ota: introduce CoAP OTA module --- makefiles/pseudomodules.inc.mk | 1 + sys/Makefile | 3 + sys/include/net/ota/coap.h | 57 ++++++++++++++++ sys/net/application_layer/ota/Makefile | 2 + sys/net/application_layer/ota/coap.c | 92 ++++++++++++++++++++++++++ 5 files changed, 155 insertions(+) create mode 100644 sys/include/net/ota/coap.h create mode 100644 sys/net/application_layer/ota/Makefile create mode 100644 sys/net/application_layer/ota/coap.c diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index ebb54ee0a9cf..8ab5c45d10f7 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -60,6 +60,7 @@ PSEUDOMODULES += newlib PSEUDOMODULES += newlib_gnu_source PSEUDOMODULES += newlib_nano PSEUDOMODULES += openthread +PSEUDOMODULES += ota_% PSEUDOMODULES += pktqueue PSEUDOMODULES += printf_float PSEUDOMODULES += prng diff --git a/sys/Makefile b/sys/Makefile index f08ec774aed1..e3a0a5e44b5b 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -133,6 +133,9 @@ endif ifneq (,$(filter rdcli_simple,$(USEMODULE))) DIRS += net/application_layer/rdcli_simple endif +ifneq (,$(filter ota_%,$(USEMODULE))) + DIRS += net/application_layer/ota +endif DIRS += $(dir $(wildcard $(addsuffix /Makefile, $(USEMODULE)))) diff --git a/sys/include/net/ota/coap.h b/sys/include/net/ota/coap.h new file mode 100644 index 000000000000..a491e09f6dc6 --- /dev/null +++ b/sys/include/net/ota/coap.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 Kaspar Schleiser + * + * 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_firmware + * @{ + * + * @file + * @brief Firmware update via CoAP API + * + * See examples/ota for example usage. + * + * @author Kaspar Schleiser + * + */ + +#ifndef OTA_COAP_H +#define OTA_COAP_H + +#include + +#include "net/nanocoap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief CoAP OTA handler function + * + * @param[in] pkt CoAP pkt to handle + * @param[out] buf reply buffer + * @param[in] len reply buffer length + * @param[in] context unused + * + * @returns length of reply packet + */ +ssize_t ota_coap_handler(coap_pkt_t* pkt, uint8_t *buf, size_t len, void *context); + +/** + * @brief Macro for integrating handler into main CoAP resource list + */ +#define COAP_OTA_HANDLER \ + { "/firmware", COAP_PUT | COAP_POST, ota_coap_handler, NULL } + +#ifdef __cplusplus +} +#endif + +#endif /* OTA_COAP_H */ + +/** @} */ diff --git a/sys/net/application_layer/ota/Makefile b/sys/net/application_layer/ota/Makefile new file mode 100644 index 000000000000..fa2f1007c91e --- /dev/null +++ b/sys/net/application_layer/ota/Makefile @@ -0,0 +1,2 @@ +SUBMODULES:=1 +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/application_layer/ota/coap.c b/sys/net/application_layer/ota/coap.c new file mode 100644 index 000000000000..bb7012f639ec --- /dev/null +++ b/sys/net/application_layer/ota/coap.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2018 Kaspar Schleiser + * + * 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_firmware + * @{ + * + * @file + * @brief Firmware update cia CoAP implementation + * + * @author Kaspar Schleiser + * + * @} + */ + +#include "firmware_update.h" +#include "log.h" +#include "net/nanocoap.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static firmware_update_t _state; + +ssize_t ota_coap_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void *context) +{ + (void)context; + + uint32_t result = COAP_CODE_204; + int res; + + coap_block1_t block1; + int blockwise = coap_get_block1(pkt, &block1); + + LOG_INFO("ota: received bytes %u-%u", \ + (unsigned)block1.offset, (unsigned)block1.offset + pkt->payload_len); + + if (_state.state == FIRMWARE_UPDATE_VERIFIED) { + unsigned total = _state.metadata_buf.size + FIRMWARE_METADATA_SIZE; + LOG_INFO(" of %u (left=%u)\n", total, + total - block1.offset - pkt->payload_len); + } + else { + LOG_INFO("\n"); + } + + if (block1.offset == 0) { + /* initialize firmware upgrade state struct */ + firmware_update_init(&_state, firmware_target_slot()); + } + + if (_state.state == FIRMWARE_UPDATE_IDLE) { + res = -1; + } + else { + if (block1.offset == _state.offset) { + res = firmware_update_putbytes(&_state, block1.offset, pkt->payload, pkt->payload_len); + } + else { + LOG_INFO("coap_ota_handler(): ignoring already received block\n"); + res = 0; + } + } + + if (res) { + result = COAP_CODE_NOT_ACCEPTABLE; + _state.state = FIRMWARE_UPDATE_IDLE; + LOG_INFO("coap_ota_handler(): unexpected packet\n"); + } + else if (block1.more == 1) { + result = COAP_CODE_231; + } + + if (!res && (!blockwise || !block1.more)) { + if (firmware_update_finish(&_state) != 0) { + result = COAP_CODE_BAD_OPTION; + } + } + + ssize_t reply_len = coap_build_reply(pkt, result, buf, len, 0); + uint8_t *pkt_pos = (uint8_t *)pkt->hdr + reply_len; + if (!res) { + pkt_pos += coap_put_block1_ok(pkt_pos, &block1, 0); + } + + return pkt_pos - (uint8_t *)pkt->hdr; +} From d1f54c53542e2f7846cae7bac5cedb457b2ff56e Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Wed, 14 Mar 2018 13:59:52 +0100 Subject: [PATCH 10/32] examples/ota: initial commit of OTA example --- examples/ota/Makefile | 48 +++++++++++++++++++++++++++ examples/ota/README.md | 21 ++++++++++++ examples/ota/main.c | 73 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 examples/ota/Makefile create mode 100644 examples/ota/README.md create mode 100644 examples/ota/main.c diff --git a/examples/ota/Makefile b/examples/ota/Makefile new file mode 100644 index 000000000000..577788ef6915 --- /dev/null +++ b/examples/ota/Makefile @@ -0,0 +1,48 @@ +# name of your application +APPLICATION = ota_example + +# If no BOARD is found in the environment, use this default: +BOARD ?= samr21-xpro + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# Signature verification needs quite a lot of stack +CFLAGS += "-DTHREAD_STACKSIZE_MAIN=((6*THREAD_STACKSIZE_DEFAULT)+THREAD_EXTRA_STACKSIZE_PRINTF)" + +# include ota modules +USEMODULE += ota_coap +USEMODULE += firmware + +# include CoAP server +USEMODULE += nanocoap_sock + +# Include packages that pull up and auto-init the link layer. +# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present +USEMODULE += gnrc_netdev_default +USEMODULE += auto_init_gnrc_netif +# Specify the mandatory networking modules for IPv6 and UDP +USEMODULE += gnrc_ipv6_default +USEMODULE += gnrc_udp +USEMODULE += gnrc_sock_udp +# Additional networking modules that can be dropped if not needed +USEMODULE += gnrc_icmpv6_echo + +# include this for printing IP addresses +USEMODULE += shell_commands + +include $(RIOTBASE)/Makefile.include + +# Set a custom channel if needed +ifneq (,$(filter cc110x,$(USEMODULE))) # radio is cc110x sub-GHz + DEFAULT_CHANNEL ?= 0 + CFLAGS += -DCC110X_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) +else + ifneq (,$(filter at86rf212b,$(USEMODULE))) # radio is IEEE 802.15.4 sub-GHz + DEFAULT_CHANNEL ?= 5 + CFLAGS += -DIEEE802154_DEFAULT_SUBGHZ_CHANNEL=$(DEFAULT_CHANNEL) + else # radio is IEEE 802.15.4 2.4 GHz + DEFAULT_CHANNEL ?= 26 + CFLAGS += -DIEEE802154_DEFAULT_CHANNEL=$(DEFAULT_CHANNEL) + endif +endif diff --git a/examples/ota/README.md b/examples/ota/README.md new file mode 100644 index 000000000000..ef7225aee5f6 --- /dev/null +++ b/examples/ota/README.md @@ -0,0 +1,21 @@ +# Introduction + +This example intends to explain usage of the riotboot OTA update scheme using +CoAP. + +# Quickstart + +- run "make" in "dist/tools/firmware" + +- run "bin/firmware genkeys sec.key pub.key" in "dist/tools/firmware" to create + keypair + +- flash image and bootloader + $ "BOARD=samr21-xpro APP_VER=$(data +%s) make -j4 riotboot/flash + +- recompile using + $ BOARD=samr21-xpro APP_VER=$(data +%s) make -j4 clean riotboot + +- send upate via coap, e.g., + $ coap-cli -m put coap://[ + * + * 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 examples + * @{ + * + * @file + * @brief CoAP / OTA example server application (using nanocoap) + * + * @author Kaspar Schleiser + * @} + */ + +#include + +#include "net/nanocoap.h" +#include "net/nanocoap_sock.h" +#include "net/ota/coap.h" +#include "firmware.h" +#include "periph/pm.h" + +#include "xtimer.h" + +#define COAP_INBUF_SIZE (256U) + +#define MAIN_QUEUE_SIZE (8) +static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; + +/* import "ifconfig" shell command, used for printing addresses */ +extern int _gnrc_netif_config(int argc, char **argv); + +/* must be sorted by path (alphabetically) */ +const coap_resource_t coap_resources[] = { + COAP_WELL_KNOWN_CORE_DEFAULT_HANDLER, + COAP_OTA_HANDLER, +}; + +const unsigned coap_resources_numof = sizeof(coap_resources) / sizeof(coap_resources[0]); + +int main(void) +{ + printf("RIOT OTA update over CoAP example application\n"); + + /* print some information about the running image */ + unsigned current_slot = firmware_current_slot(); + firmware_metadata_t *metadata = firmware_get_metadata(current_slot); + printf("firmware: running from slot %u\n", current_slot); + firmware_metadata_print(metadata); + + /* nanocoap_server uses gnrc sock which uses gnrc which needs a msg queue */ + msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE); + + puts("Waiting for address autoconfiguration..."); + xtimer_sleep(3); + + /* print network addresses */ + puts("Configured network interfaces:"); + _gnrc_netif_config(0, NULL); + + /* initialize nanocoap server instance */ + uint8_t buf[COAP_INBUF_SIZE]; + sock_udp_ep_t local = { .port=COAP_PORT, .family=AF_INET6 }; + nanocoap_server(&local, buf, sizeof(buf)); + + /* should be never reached */ + return 0; +} From a88500734bec8484ae9ede65068f404c59a270f8 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Mon, 9 Apr 2018 15:55:40 +0200 Subject: [PATCH 11/32] fixup! riotboot: introduce riot-based minimal bootloader --- makefiles/riotboot.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/riotboot.mk b/makefiles/riotboot.mk index 36b297c9d4d1..63961e83357e 100644 --- a/makefiles/riotboot.mk +++ b/makefiles/riotboot.mk @@ -1,4 +1,4 @@ -ifdef RIOTBOOT_SLOT0_SIZE +ifneq (,$(filter riotboot%, $(MAKECMDGOALS))) .PHONY: riotboot/flash riotboot/flash-bootloader riotboot/flash-slot1 riotboot/flash-slot2 \ riotboot/verify-image From 05c17e86a096f43a773f976011c72d2e30926762 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Tue, 10 Apr 2018 23:31:25 +0200 Subject: [PATCH 12/32] fixup! cpu/cortexm: change rom offset logic in cortexm.ld --- cpu/cortexm_common/ldscripts/cortexm.ld | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cpu/cortexm_common/ldscripts/cortexm.ld b/cpu/cortexm_common/ldscripts/cortexm.ld index 3b9e4c28bece..2a7d0be2af21 100644 --- a/cpu/cortexm_common/ldscripts/cortexm.ld +++ b/cpu/cortexm_common/ldscripts/cortexm.ld @@ -1,6 +1,7 @@ /* - * Copyright (C) 2018 Kaspar Schleiser - * 2017 Inria + * Copyright (C) 2017 Inria + * 2018 Kaspar Schleiser + * * * 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 From c01eb239eb002192ab73e7ffa75f403bde9dc40b Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Tue, 10 Apr 2018 23:31:26 +0200 Subject: [PATCH 13/32] fixup! dist/tools/firmware: initial import of firmware metadata tools --- dist/tools/firmware/genkeys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/tools/firmware/genkeys.c b/dist/tools/firmware/genkeys.c index 82cf5a0a3df3..368b159e9b1f 100644 --- a/dist/tools/firmware/genkeys.c +++ b/dist/tools/firmware/genkeys.c @@ -22,7 +22,7 @@ int genkeys(int argc, const char *argv[]) return 1; } - crypto_sign_keypair(pk,sk); + crypto_sign_keypair(pk, sk); const char err[] = "error writing to %s\n"; if (!to_file(argv[1], sk, sizeof(sk))) { From 16dfec7e09ff394644b88585056206551d43d5ed Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Tue, 10 Apr 2018 23:31:26 +0200 Subject: [PATCH 14/32] fixup! dist/tools/firmware: initial import of firmware metadata tools --- dist/tools/firmware/main.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/dist/tools/firmware/main.c b/dist/tools/firmware/main.c index 9106787ce0bd..c5944b502990 100644 --- a/dist/tools/firmware/main.c +++ b/dist/tools/firmware/main.c @@ -24,16 +24,18 @@ int main(int argc, char *argv[]) } if (!strcmp(argv[1], "verify")) { return verify(argc, argv); - } else if (!strcmp(argv[1], "genkeys")) { + } + else if (!strcmp(argv[1], "genkeys")) { return genkeys(argc - 1, &argv[1]); - } else if (!strcmp(argv[1], "sign")) { + } + else if (!strcmp(argv[1], "sign")) { return sign(argc - 1, &argv[1]); } usage: - fprintf(stderr, "usage: %s\n" \ - " %s\n" \ - " %s\n", \ - genkeys_usage, sign_usage, verify_usage); + fprintf(stderr, "usage: %s\n" + " %s\n" + " %s\n", + genkeys_usage, sign_usage, verify_usage); return 1; } From 58887f48501f5b525bc4eb34bcb97547e4d6469b Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Tue, 10 Apr 2018 23:31:26 +0200 Subject: [PATCH 15/32] fixup! dist/tools/firmware: initial import of firmware metadata tools --- dist/tools/firmware/sign.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/tools/firmware/sign.c b/dist/tools/firmware/sign.c index eed159206e56..3fefdaa2c058 100644 --- a/dist/tools/firmware/sign.c +++ b/dist/tools/firmware/sign.c @@ -1,7 +1,7 @@ /* - * Copyright (c) 2017 Kaspar Schleiser - * 2016 Mark Solters + * Copyright (c) 2016 Mark Solters * 2016 Inria + * 2017 Kaspar Schleiser * * All rights reserved. * @@ -32,7 +32,7 @@ */ /** - * @ingroup FW + * @ingroup tools * @file * @brief Meta-data generation and signing for FW images * From 5e35628183c573fc6b2f885a9a262d506092232b Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Tue, 10 Apr 2018 23:31:26 +0200 Subject: [PATCH 16/32] fixup! dist/tools/firmware: initial import of firmware metadata tools --- dist/tools/firmware/verify.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/dist/tools/firmware/verify.c b/dist/tools/firmware/verify.c index a1032aea9725..de2233e72495 100644 --- a/dist/tools/firmware/verify.c +++ b/dist/tools/firmware/verify.c @@ -21,7 +21,8 @@ int verify(int argc, char *argv[]) firmware_metadata_t metadata; if (argc < 4) { - goto usage; + fprintf(stderr, "usage: %s\n", verify_usage); + return 1; } if (!from_file(argv[2], &metadata, sizeof(metadata))) { @@ -59,9 +60,4 @@ int verify(int argc, char *argv[]) out: return res; - -usage: - fprintf(stderr, "usage: %s\n", verify_usage); - return 1; } - From 3ab924bb00de9682767e59c692ea2fc8485d2b9e Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Tue, 10 Apr 2018 23:31:26 +0200 Subject: [PATCH 17/32] fixup! examples/ota: initial commit of OTA example --- examples/ota/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/ota/README.md b/examples/ota/README.md index ef7225aee5f6..16a334e89310 100644 --- a/examples/ota/README.md +++ b/examples/ota/README.md @@ -11,11 +11,13 @@ CoAP. keypair - flash image and bootloader + $ "BOARD=samr21-xpro APP_VER=$(data +%s) make -j4 riotboot/flash -- recompile using +- recompile using + $ BOARD=samr21-xpro APP_VER=$(data +%s) make -j4 clean riotboot - send upate via coap, e.g., - $ coap-cli -m put coap://[ Date: Tue, 10 Apr 2018 23:31:26 +0200 Subject: [PATCH 18/32] fixup! sys/firmware: initial import of firmware management functions --- sys/firmware/firmware.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/sys/firmware/firmware.c b/sys/firmware/firmware.c index 1433d496f638..77a864dbcaba 100644 --- a/sys/firmware/firmware.c +++ b/sys/firmware/firmware.c @@ -37,7 +37,7 @@ void firmware_metadata_print(firmware_metadata_t *metadata) { printf("Firmware magic_number: 0x%08x\n", (unsigned)metadata->magic_number); - printf("Firmware Size: %"PRIu32"\n", metadata->size); + printf("Firmware Size: %" PRIu32 "\n", metadata->size); printf("Firmware APPID: %#x\n", (unsigned)metadata->appid); printf("Firmware Version: %#x\n", (unsigned)metadata->version); printf("Firmware start address: 0x%08x\n", (unsigned)metadata->start_addr); @@ -90,14 +90,15 @@ int firmware_validate_metadata_signature(firmware_metadata_t *metadata, const un uint32_t firmware_metadata_checksum(firmware_metadata_t *metadata) { - return fletcher32((uint16_t*)metadata, FIRMWARE_CHECKSUM_LEN / 2); + return fletcher32((uint16_t *)metadata, FIRMWARE_CHECKSUM_LEN / 2); } int firmware_sign_metadata(firmware_metadata_t *metadata, unsigned char *sk) { unsigned char sm[FIRMWARE_SIGN_BYTES + crypto_sign_BYTES]; unsigned long long smlen; - crypto_sign(sm, &smlen, (unsigned char*)metadata, FIRMWARE_SIGN_BYTES, sk); + + crypto_sign(sm, &smlen, (unsigned char *)metadata, FIRMWARE_SIGN_BYTES, sk); memcpy(metadata->sig, sm, crypto_sign_BYTES); return 0; } @@ -110,11 +111,12 @@ static const unsigned _firmware_slot_start[] = { CPU_FLASH_BASE + SLOT0_SIZE + SLOT1_SIZE }; -const unsigned firmware_num_slots = sizeof(_firmware_slot_start)/sizeof(unsigned); +const unsigned firmware_num_slots = sizeof(_firmware_slot_start) / sizeof(unsigned); void firmware_jump_to_image(firmware_metadata_t *metadata) { uint32_t addr = (unsigned)metadata + (unsigned)sizeof(firmware_metadata_t); + cpu_jump_to_image(addr); } @@ -137,14 +139,15 @@ int firmware_target_slot(void) return firmware_current_slot() == 1 ? 2 : 1; } -firmware_metadata_t *firmware_get_metadata(unsigned slot) { - assert (slot < FIRMWARE_NUM_SLOTS); +firmware_metadata_t *firmware_get_metadata(unsigned slot) +{ + assert(slot < FIRMWARE_NUM_SLOTS); return (firmware_metadata_t *)_firmware_slot_start[slot]; } unsigned firmware_get_image_startaddr(unsigned slot) { - assert (slot < FIRMWARE_NUM_SLOTS); + assert(slot < FIRMWARE_NUM_SLOTS); return _firmware_slot_start[slot] + sizeof(firmware_metadata_t); } @@ -156,8 +159,8 @@ void firmware_jump_to_slot(unsigned slot) void firmware_dump_slot_addrs(void) { for (unsigned i = 1; i < FIRMWARE_NUM_SLOTS; i++) { - printf("slot %u: metadata: 0x%08x image: 0x%08x\n", i, \ - (unsigned)_firmware_slot_start[i], firmware_get_image_startaddr(i)); + printf("slot %u: metadata: 0x%08x image: 0x%08x\n", i, + (unsigned)_firmware_slot_start[i], firmware_get_image_startaddr(i)); } } #else /* BOARD_NATIVE */ From 11c96f782f5248a83233d09ee47d52f38ddd3897 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Tue, 10 Apr 2018 23:31:26 +0200 Subject: [PATCH 19/32] fixup! sys/firmware: initial import of firmware management functions --- sys/include/firmware.h | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/sys/include/firmware.h b/sys/include/firmware.h index d7104d57067e..545f8caa9176 100644 --- a/sys/include/firmware.h +++ b/sys/include/firmware.h @@ -16,8 +16,8 @@ * * ## Overview * - * riotboot the name of a minimal bootloader application and infrastructure. It - * consists of + * riotboot is the name of a minimal bootloader application and infrastructure. + * It consists of * * - the application "riotboot" in "dist/riotboot" which serves as * minimal bootloader @@ -27,7 +27,7 @@ * * - the module "ota_coap" which serves as transport for the updates * - * - a tool in dist/tools/formware which handles key generation + * - a tool in dist/tools/firmware which handles key generation * and firmware image signing. * * - a couple of make targets to glue everything together @@ -70,8 +70,8 @@ * The module "firmware_update" provides a simple API that can be fed image data * in arbitrary block sizes. The API is similar to stream hashing. There's * "firmware_update_init()", "firmware_update_put_bytes()" and - * "firmware_update_finish()". The module transparently handles safe image - * verification and flah writing. + * "firmware_update_finish()". The module transparently handles image + * verification and flash writing. * * A tool in dist/tools/firmware can create the necessary keys using tweetnacl, * and also sign/verify firmware images. @@ -91,7 +91,7 @@ * ## Usage * * riotboot needs a public key to be compiled in the image for verification purposes. - * Create a keypair using dist/tools/firware. + * Create a keypair using dist/tools/firmware. * * riotboot needs some board specific support, mainly slot size configuration, in * order to work for a specific board. @@ -234,26 +234,40 @@ void firmware_jump_to_image(firmware_metadata_t *metadata); /** * @brief Get currently running image slot + * + * returns nr of currently active slot */ int firmware_current_slot(void); /** * @brief Get next (to be empty) image slot + * + * returns free target slot */ int firmware_target_slot(void); /** * @brief Get metadata of firmware slot + * + * @param[in] slot slot nr to work on + * + * returns metadata of image slot nr @p slot */ firmware_metadata_t *firmware_get_metadata(unsigned slot); /** * @brief Get jump-to address of firmware slot + * + * @param[in] slot slot nr to work on + * + * @returns address of first byte of @p slot */ unsigned firmware_get_image_startaddr(unsigned slot); /** * @brief Boot into image in slot @p slot + * + * @param[in] slot slot nr to jump to */ void firmware_jump_to_slot(unsigned slot); @@ -262,6 +276,9 @@ void firmware_jump_to_slot(unsigned slot); */ void firmware_dump_slot_addrs(void); +/** + * @brief Number of configured firmware slots (incl. bootloader slot) + */ extern const unsigned firmware_num_slots; #ifdef __cplusplus From 719d0f98e34153a4e7048b894bd442980a4c6a6e Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Tue, 10 Apr 2018 23:31:26 +0200 Subject: [PATCH 20/32] fixup! sys/ota: introduce CoAP OTA module --- sys/net/application_layer/ota/coap.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sys/net/application_layer/ota/coap.c b/sys/net/application_layer/ota/coap.c index bb7012f639ec..c6f65f8e94ba 100644 --- a/sys/net/application_layer/ota/coap.c +++ b/sys/net/application_layer/ota/coap.c @@ -11,7 +11,7 @@ * @{ * * @file - * @brief Firmware update cia CoAP implementation + * @brief Firmware update via CoAP implementation * * @author Kaspar Schleiser * @@ -37,7 +37,7 @@ ssize_t ota_coap_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void *contex coap_block1_t block1; int blockwise = coap_get_block1(pkt, &block1); - LOG_INFO("ota: received bytes %u-%u", \ + LOG_INFO("ota: received bytes %u-%u", (unsigned)block1.offset, (unsigned)block1.offset + pkt->payload_len); if (_state.state == FIRMWARE_UPDATE_VERIFIED) { @@ -59,7 +59,8 @@ ssize_t ota_coap_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void *contex } else { if (block1.offset == _state.offset) { - res = firmware_update_putbytes(&_state, block1.offset, pkt->payload, pkt->payload_len); + res = firmware_update_putbytes(&_state, block1.offset, + pkt->payload, pkt->payload_len); } else { LOG_INFO("coap_ota_handler(): ignoring already received block\n"); From 7ac7411f80563b843e5dc5518a2de1ea27c4f7ca Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Wed, 11 Apr 2018 12:44:01 +0200 Subject: [PATCH 21/32] fixup! fixup! examples/ota: initial commit of OTA example --- examples/ota/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/ota/README.md b/examples/ota/README.md index 16a334e89310..c8dc357bc7b2 100644 --- a/examples/ota/README.md +++ b/examples/ota/README.md @@ -12,11 +12,11 @@ CoAP. - flash image and bootloader - $ "BOARD=samr21-xpro APP_VER=$(data +%s) make -j4 riotboot/flash + $ "BOARD=samr21-xpro APP_VER=$(date +%s) make -j4 riotboot/flash - recompile using - $ BOARD=samr21-xpro APP_VER=$(data +%s) make -j4 clean riotboot + $ BOARD=samr21-xpro APP_VER=$(date +%s) make -j4 clean riotboot - send upate via coap, e.g., From 49788a0aa3bea79e644d802995d4aa619b3b2126 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Wed, 11 Apr 2018 17:38:13 +0200 Subject: [PATCH 22/32] fixup! fixup! riotboot: introduce riot-based minimal bootloader --- makefiles/riotboot.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/riotboot.mk b/makefiles/riotboot.mk index 63961e83357e..8943ab971fda 100644 --- a/makefiles/riotboot.mk +++ b/makefiles/riotboot.mk @@ -51,7 +51,7 @@ $(BINDIR)/$(APPLICATION)-slot2.bin: OFFSET=$(SLOT2_OFFSET) $(BINDIR)/$(APPLICATION)-slot2.bin: $(BINDIR)/$(APPLICATION)-slot2.elf # creating pubkey header file -link: | $(BINDIR)/riotbuild/ota_pubkey.h +$(RIOTBUILD_CONFIG_HEADER_C): $(BINDIR)/riotbuild/ota_pubkey.h $(BINDIR)/riotbuild/ota_pubkey.h: $(RIOTBOOT_PUBKEY) @mkdir -p $(@D) @{ \ From 1bb4fe0bdbd636f09e8bdd2da039dd8b1e741f86 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Wed, 11 Apr 2018 17:38:13 +0200 Subject: [PATCH 23/32] fixup! sys/firmware: initial import of firmware management functions --- sys/include/firmware_update.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/include/firmware_update.h b/sys/include/firmware_update.h index 3085d92d37fa..e9684fd395f5 100644 --- a/sys/include/firmware_update.h +++ b/sys/include/firmware_update.h @@ -98,7 +98,7 @@ int firmware_update_init(firmware_update_t *state, int target_slot); * * @returns 0 on success, <0 otherwise */ -int firmware_update_putbytes(firmware_update_t *state, size_t offset +int firmware_update_putbytes(firmware_update_t *state, size_t offset, const uint8_t *bytes, size_t len); /** From 0ab298fcc16d3ed7810528c21cf6a9dd1238c89b Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Mon, 23 Apr 2018 11:41:29 +0200 Subject: [PATCH 24/32] fixup! fixup! fixup! riotboot: introduce riot-based minimal bootloader --- makefiles/riotboot.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makefiles/riotboot.mk b/makefiles/riotboot.mk index 8943ab971fda..400132a914cc 100644 --- a/makefiles/riotboot.mk +++ b/makefiles/riotboot.mk @@ -1,4 +1,4 @@ -ifneq (,$(filter riotboot%, $(MAKECMDGOALS))) +ifneq (,$(filter riotboot,$(FEATURES_USED))) .PHONY: riotboot/flash riotboot/flash-bootloader riotboot/flash-slot1 riotboot/flash-slot2 \ riotboot/verify-image @@ -85,7 +85,7 @@ riotboot/flash: riotboot/flash-slot1 else riotboot: - $(Q)echo "error: riotboot not supported on board $(BOARD)!" + $(Q)echo "error: riotboot feature not selected! (try FEATURES_REQUIRED += riotboot)" $(Q)false endif # RIOTBOOT_SLOT0_SIZE From d2ad1a03267b75b75768824b6090d06d1dd321aa Mon Sep 17 00:00:00 2001 From: Koen Zandberg Date: Fri, 20 Apr 2018 16:50:55 +0200 Subject: [PATCH 25/32] firmware: don't depend on sizeof(firmware_metadata_t) --- sys/firmware/firmware.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sys/firmware/firmware.c b/sys/firmware/firmware.c index 77a864dbcaba..719d36980737 100644 --- a/sys/firmware/firmware.c +++ b/sys/firmware/firmware.c @@ -115,14 +115,14 @@ const unsigned firmware_num_slots = sizeof(_firmware_slot_start) / sizeof(unsign void firmware_jump_to_image(firmware_metadata_t *metadata) { - uint32_t addr = (unsigned)metadata + (unsigned)sizeof(firmware_metadata_t); + uint32_t addr = (unsigned)metadata + FIRMWARE_METADATA_SIZE; cpu_jump_to_image(addr); } int firmware_current_slot(void) { - unsigned base_addr = cpu_get_image_baseaddr() - sizeof(firmware_metadata_t); + unsigned base_addr = cpu_get_image_baseaddr() - FIRMWARE_METADATA_SIZE; for (unsigned i = 1; i < firmware_num_slots; i++) { if (base_addr == _firmware_slot_start[i]) { @@ -148,7 +148,7 @@ firmware_metadata_t *firmware_get_metadata(unsigned slot) unsigned firmware_get_image_startaddr(unsigned slot) { assert(slot < FIRMWARE_NUM_SLOTS); - return _firmware_slot_start[slot] + sizeof(firmware_metadata_t); + return _firmware_slot_start[slot] + FIRMWARE_METADATA_SIZE; } void firmware_jump_to_slot(unsigned slot) From a122045546cbc61ac685684e3f56919281f349c6 Mon Sep 17 00:00:00 2001 From: Koen Zandberg Date: Tue, 24 Apr 2018 11:51:43 +0200 Subject: [PATCH 26/32] Split update into firmware_simple and firmware_flashwrite --- dist/tools/firmware/Makefile | 3 +- dist/tools/firmware/sign.c | 21 +- dist/tools/firmware/verify.c | 9 +- examples/ota/main.c | 3 +- sys/firmware/firmware.c | 54 +---- sys/firmware/firmware_flashwrite.c | 110 +++++++++++ sys/firmware/firmware_simple.c | 187 ++++++++++++++++++ sys/firmware/update.c | 157 --------------- sys/include/firmware.h | 69 +------ .../flashwrite.h} | 67 +++---- sys/include/firmware/simple.h | 109 ++++++++++ sys/net/application_layer/ota/coap.c | 14 +- 12 files changed, 469 insertions(+), 334 deletions(-) create mode 100644 sys/firmware/firmware_flashwrite.c create mode 100644 sys/firmware/firmware_simple.c delete mode 100644 sys/firmware/update.c rename sys/include/{firmware_update.h => firmware/flashwrite.h} (50%) create mode 100644 sys/include/firmware/simple.h diff --git a/dist/tools/firmware/Makefile b/dist/tools/firmware/Makefile index 3e8626bdeff0..27de609fb818 100644 --- a/dist/tools/firmware/Makefile +++ b/dist/tools/firmware/Makefile @@ -18,7 +18,8 @@ COMMON_HDR := common.h RIOT_FIRMWARE_SRC := \ $(SHA256_DIR)/sha256.c \ $(RIOTBASE)/sys/checksum/fletcher32.c \ - $(RIOTBASE)/sys/firmware/firmware.c + $(RIOTBASE)/sys/firmware/firmware.c \ + $(RIOTBASE)/sys/firmware/firmware_simple.c RIOT_FIRMWARE_HDR := $(RIOT_INCLUDE)/firmware.h \ $(RIOT_INCLUDE)/hashes/sha256.h \ diff --git a/dist/tools/firmware/sign.c b/dist/tools/firmware/sign.c index 3fefdaa2c058..102142f14eae 100644 --- a/dist/tools/firmware/sign.c +++ b/dist/tools/firmware/sign.c @@ -47,17 +47,18 @@ #include #include "firmware.h" +#include "firmware/simple.h" #include "common.h" const char sign_usage[] = "firmware sign "; int sign(int argc, char *argv[]) { - firmware_metadata_t metadata; + firmware_simple_t metadata; unsigned char sk[FIRMWARE_SECKEY_LEN]; ssize_t firmware_size; - memset(&metadata, '\0', sizeof(firmware_metadata_t)); + memset(&metadata, '\0', sizeof(firmware_simple_t)); if (argc < 7) { fprintf(stderr, "usage: %s\n", sign_usage); @@ -76,26 +77,26 @@ int sign(int argc, char *argv[]) } /* Generate FW image metadata */ - memcpy(&metadata.magic_number, "RIOT", 4); + memcpy(&metadata.metadata.magic_number, "RIOT", 4); metadata.size = firmware_size; - sscanf(argv[2], "%x", (unsigned int *)&(metadata.version)); + sscanf(argv[2], "%x", (unsigned int *)&(metadata.metadata.version)); sscanf(argv[3], "%x", &(metadata.appid)); - sscanf(argv[4], "%u", &(metadata.start_addr)); + sscanf(argv[4], "%u", &(metadata.metadata.start_addr)); /* calculate metadata checksum */ - metadata.chksum = firmware_metadata_checksum(&metadata); + metadata.metadata.chksum = firmware_metadata_checksum(&metadata.metadata); /* sign */ - firmware_sign_metadata(&metadata, sk); + firmware_simple_sign(&metadata, sk); /* talk to the user */ if (strcmp(argv[6], "-")) { - firmware_metadata_print(&metadata); - printf("Metadata size: %lu\n", sizeof(firmware_metadata_t)); + firmware_simple_print(&metadata); + printf("Metadata size: %lu\n", sizeof(firmware_simple_t)); } /* Write the metadata */ - if (!to_file(argv[6], &metadata, sizeof(firmware_metadata_t))) { + if (!to_file(argv[6], &metadata, FIRMWARE_METADATA_SIZE)) { fprintf(stderr, "Error: cannot write output\n"); return(1); } diff --git a/dist/tools/firmware/verify.c b/dist/tools/firmware/verify.c index de2233e72495..62b5adda4fc4 100644 --- a/dist/tools/firmware/verify.c +++ b/dist/tools/firmware/verify.c @@ -10,6 +10,7 @@ #include #include "firmware.h" +#include "firmware/simple.h" #include "common.h" #include "tweetnacl.h" @@ -18,7 +19,7 @@ const char verify_usage[] = "firmware verify "; int verify(int argc, char *argv[]) { unsigned char pubkey[FIRMWARE_PUBKEY_LEN]; - firmware_metadata_t metadata; + firmware_simple_t metadata; if (argc < 4) { fprintf(stderr, "usage: %s\n", verify_usage); @@ -35,7 +36,7 @@ int verify(int argc, char *argv[]) return 1; } - int res = firmware_validate_metadata_signature(&metadata, pubkey) ? 1 : 0; + int res = firmware_simple_validate_signature(&metadata, pubkey) ? 1 : 0; if (res) { printf("signature check failed\n"); return -1; @@ -44,13 +45,13 @@ int verify(int argc, char *argv[]) } off_t size = fsize(argv[2]); - if (size == sizeof(firmware_metadata_t)) { + if (size == sizeof(firmware_simple_t)) { printf("hash check skipped\n"); goto out; } char hash[SHA256_DIGEST_LENGTH]; - do_sha256(argv[2], hash, sizeof(firmware_metadata_t)); + do_sha256(argv[2], hash, sizeof(firmware_simple_t)); res = memcmp(hash, metadata.hash, SHA256_DIGEST_LENGTH) ? 1 : 0; if (res) { printf("hash check failed\n"); diff --git a/examples/ota/main.c b/examples/ota/main.c index 11829ce97a87..eb3b22384c9d 100644 --- a/examples/ota/main.c +++ b/examples/ota/main.c @@ -23,6 +23,7 @@ #include "net/nanocoap_sock.h" #include "net/ota/coap.h" #include "firmware.h" +#include "firmware/simple.h" #include "periph/pm.h" #include "xtimer.h" @@ -51,7 +52,7 @@ int main(void) unsigned current_slot = firmware_current_slot(); firmware_metadata_t *metadata = firmware_get_metadata(current_slot); printf("firmware: running from slot %u\n", current_slot); - firmware_metadata_print(metadata); + firmware_simple_print((firmware_simple_t*)metadata); /* nanocoap_server uses gnrc sock which uses gnrc which needs a msg queue */ msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE); diff --git a/sys/firmware/firmware.c b/sys/firmware/firmware.c index 719d36980737..6a94d84b2697 100644 --- a/sys/firmware/firmware.c +++ b/sys/firmware/firmware.c @@ -32,27 +32,6 @@ #include "firmware.h" #include "checksum/fletcher32.h" -#include "tweetnacl.h" - -void firmware_metadata_print(firmware_metadata_t *metadata) -{ - printf("Firmware magic_number: 0x%08x\n", (unsigned)metadata->magic_number); - printf("Firmware Size: %" PRIu32 "\n", metadata->size); - printf("Firmware APPID: %#x\n", (unsigned)metadata->appid); - printf("Firmware Version: %#x\n", (unsigned)metadata->version); - printf("Firmware start address: 0x%08x\n", (unsigned)metadata->start_addr); - printf("Firmware HASH: "); - for (unsigned long i = 0; i < sizeof(metadata->hash); i++) { - printf("%02x ", metadata->hash[i]); - } - printf("\n"); - printf("Firmware chksum: 0x%08x\n", (unsigned)metadata->chksum); - printf("Firmware signature: "); - for (unsigned long i = 0; i < sizeof(metadata->sig); i++) { - printf("%02x ", metadata->sig[i]); - } - printf("\n"); -} int firmware_validate_metadata_checksum(firmware_metadata_t *metadata) { @@ -69,40 +48,11 @@ int firmware_validate_metadata_checksum(firmware_metadata_t *metadata) return res; } -int firmware_validate_metadata_signature(firmware_metadata_t *metadata, const unsigned char *pk) -{ - if (firmware_validate_metadata_checksum(metadata)) { - return -1; - } - - unsigned char sm[FIRMWARE_SIGN_BYTES + crypto_sign_BYTES]; - memcpy(sm, ((unsigned char *)metadata) + FIRMWARE_SIGN_BYTES, crypto_sign_BYTES); - memcpy(sm + crypto_sign_BYTES, metadata, FIRMWARE_SIGN_BYTES); - - unsigned char m[FIRMWARE_SIGN_BYTES + crypto_sign_BYTES]; - unsigned long long mlen; - int res = crypto_sign_open(m, &mlen, sm, FIRMWARE_SIGN_BYTES + crypto_sign_BYTES, pk); - if (res) { - LOG_INFO("%s: metadata signature invalid\n", __func__); - } - return res; -} - uint32_t firmware_metadata_checksum(firmware_metadata_t *metadata) { return fletcher32((uint16_t *)metadata, FIRMWARE_CHECKSUM_LEN / 2); } -int firmware_sign_metadata(firmware_metadata_t *metadata, unsigned char *sk) -{ - unsigned char sm[FIRMWARE_SIGN_BYTES + crypto_sign_BYTES]; - unsigned long long smlen; - - crypto_sign(sm, &smlen, (unsigned char *)metadata, FIRMWARE_SIGN_BYTES, sk); - memcpy(metadata->sig, sm, crypto_sign_BYTES); - return 0; -} - #ifdef RIOT_VERSION #ifndef BOARD_NATIVE static const unsigned _firmware_slot_start[] = { @@ -115,9 +65,7 @@ const unsigned firmware_num_slots = sizeof(_firmware_slot_start) / sizeof(unsign void firmware_jump_to_image(firmware_metadata_t *metadata) { - uint32_t addr = (unsigned)metadata + FIRMWARE_METADATA_SIZE; - - cpu_jump_to_image(addr); + cpu_jump_to_image(metadata->start_addr); } int firmware_current_slot(void) diff --git a/sys/firmware/firmware_flashwrite.c b/sys/firmware/firmware_flashwrite.c new file mode 100644 index 000000000000..afdf9c5ecc2b --- /dev/null +++ b/sys/firmware/firmware_flashwrite.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * + * 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_firmware + * @{ + * + * @file + * @brief Firmware update helper functions + * + * @author Kaspar Schleiser + * + * @} + */ + +#include + +#include "firmware/flashwrite.h" +#include "log.h" +#include "od.h" + +static inline size_t min(size_t a, size_t b) +{ + return a <= b ? a : b; +} + +int firmware_flashwrite_init(firmware_flashwrite_t *state, int target_slot, + size_t offset) +{ + LOG_INFO("ota: initializing update to target slot %i\n", + target_slot); + + memset(state, 0, sizeof(firmware_flashwrite_t)); + + state->offset = offset; + state->target_slot = target_slot; + state->flashpage = flashpage_page(firmware_get_metadata(target_slot)); + + return 0; +} + +int firmware_flashwrite_putbytes(firmware_flashwrite_t *state, + const uint8_t *bytes, size_t len, bool more) +{ + LOG_INFO("ota: processing bytes %u-%u\n", state->offset, state->offset + len - 1); + + while (len) { + size_t flashpage_pos = state->offset % FLASHPAGE_SIZE; + size_t flashpage_avail = FLASHPAGE_SIZE - flashpage_pos; + + size_t to_copy = min(flashpage_avail, len); + + memcpy(state->flashpage_buf + flashpage_pos, bytes, to_copy); + flashpage_avail -= to_copy; + + state->offset += to_copy; + flashpage_pos += to_copy; + bytes += to_copy; + len -= to_copy; + if ((!flashpage_avail) || (!more)) { + if (flashpage_write_and_verify(state->flashpage, state->flashpage_buf) != FLASHPAGE_OK) { + LOG_WARNING("ota: error writing flashpage %u!\n", state->flashpage); + return -1; + } + state->flashpage++; + } + } + + return 0; +} + +int firmware_flashwrite_finish(firmware_flashwrite_t *state, + firmware_metadata_t *metadata, size_t len) +{ + int res = -1; + + void *slot_start = firmware_get_metadata(state->target_slot); + size_t metadata_space = (void*)metadata->start_addr - slot_start; + + void *firstpage; + + if (len < FLASHPAGE_SIZE) { + firstpage = state->flashpage_buf; + memset(firstpage, 0, metadata_space); + memcpy(firstpage, metadata, len); + memcpy(firstpage + metadata_space, \ + slot_start + metadata_space, \ + FLASHPAGE_SIZE - metadata_space); + } + else { + firstpage = metadata; + } + + int flashpage = flashpage_page(slot_start); + if (flashpage_write_and_verify(flashpage, firstpage) != FLASHPAGE_OK) { + LOG_WARNING("ota: re-flashing first block failed!\n"); + goto out; + } + + LOG_INFO("ota: firmware flashing completed successfully\n"); + res = 0; + +out: + return res; +} diff --git a/sys/firmware/firmware_simple.c b/sys/firmware/firmware_simple.c new file mode 100644 index 000000000000..ad6a83221523 --- /dev/null +++ b/sys/firmware/firmware_simple.c @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * 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_firmware + * @{ + * + * @file + * @brief Simple firmware format update module + * + * @author Kaspar Schleiser + * @author Francisco Acosta + * + * @} + */ + +#include + +#ifdef RIOT_VERSION +#include "log.h" +#include "ota_pubkey.h" +#else +#include +#define LOG_INFO(...) printf(__VA_ARGS__) +#endif + +#include "firmware.h" +#include "firmware/simple.h" + +static inline size_t min(size_t a, size_t b) +{ + return a <= b ? a : b; +} + +void firmware_simple_print(firmware_simple_t *simple) +{ + printf("Firmware magic_number: 0x%08x\n", (unsigned)simple->metadata.magic_number); + printf("Firmware Version: %#x\n", (unsigned)simple->metadata.version); + printf("Firmware start address: 0x%08x\n", (unsigned)simple->metadata.start_addr); + /* Only output full info if the metadata_type matches */ + if (simple->metadata.metadata_type == FIRMWARE_METADATA_RIOTBOOT) + { + printf("Firmware APPID: %#x\n", (unsigned)simple->appid); + printf("Firmware Size: %" PRIu32 "\n", simple->size); + printf("Firmware HASH: "); + for (unsigned long i = 0; i < sizeof(simple->hash); i++) { + printf("%02x ", simple->hash[i]); + } + printf("\n"); + printf("Firmware chksum: 0x%08x\n", (unsigned)simple->metadata.chksum); + printf("Firmware signature: "); + for (unsigned long i = 0; i < sizeof(simple->sig); i++) { + printf("%02x ", simple->sig[i]); + } + printf("\n"); + } + else { + printf("Firmware of unknown type\n"); + } +} + +int firmware_simple_validate_signature(firmware_simple_t *simple, const unsigned char *pk) +{ + if (firmware_validate_metadata_checksum(&simple->metadata)) { + return -1; + } + + unsigned char sm[FIRMWARE_SIGN_BYTES + crypto_sign_BYTES]; + memcpy(sm, ((unsigned char *)simple) + FIRMWARE_SIGN_BYTES, crypto_sign_BYTES); + memcpy(sm + crypto_sign_BYTES, simple, FIRMWARE_SIGN_BYTES); + + unsigned char m[FIRMWARE_SIGN_BYTES + crypto_sign_BYTES]; + unsigned long long mlen; + int res = crypto_sign_open(m, &mlen, sm, FIRMWARE_SIGN_BYTES + crypto_sign_BYTES, pk); + if (res) { + LOG_INFO("%s: RIOTboot metadata signature invalid\n", __func__); + } + return res; +} + +int firmware_simple_sign(firmware_simple_t *simple, unsigned char *sk) +{ + unsigned char sm[FIRMWARE_SIGN_BYTES + crypto_sign_BYTES]; + unsigned long long smlen; + + crypto_sign(sm, &smlen, (unsigned char *)simple, FIRMWARE_SIGN_BYTES, sk); + memcpy(simple->sig, sm, crypto_sign_BYTES); + return 0; +} + +#ifdef RIOT_VERSION +static ssize_t _simple_recv_metadata(firmware_simple_update_t *state, const uint8_t *buf, size_t len) +{ + size_t to_copy = min(len, FIRMWARE_METADATA_SIZE - state->writer.offset); + /* copy metadata from packet to the firmware update struct */ + memcpy(state->m.metadata_buf + state->writer.offset, + buf, + to_copy); + if ((state->writer.offset + to_copy) >= FIRMWARE_METADATA_SIZE - 1) { + /* Full metadata received */ + firmware_simple_t *metadata = &state->m.metadata; + LOG_INFO("ota: verifying metadata ...\n"); + if (metadata->metadata.start_addr != firmware_get_image_startaddr(state->writer.target_slot)) { + LOG_WARNING("ota: start address doesn't match selected slot. Aborting.\n"); + LOG_WARNING("ota: (image start=%p slot start=%p)\n", (void *)metadata->metadata.start_addr, \ + (void *)firmware_get_image_startaddr(state->writer.target_slot)); + return -1; + } + + /* check metadata magic nr, checksum and signature */ + if (firmware_simple_validate_signature(metadata, ota_public_key)) { + LOG_WARNING("ota: verification failed!\n"); + return -1; + } + else { + state->state = FIRMWARE_UPDATE_VERIFIED; + LOG_INFO("ota: verification successful\n"); + } + state->writer.flashpage++; + } + return to_copy; +} + +int firmware_simple_init(firmware_simple_update_t *state) +{ + state->state = FIRMWARE_UPDATE_INITIALIZED; + return firmware_flashwrite_init(&state->writer, firmware_target_slot(), 0); +} + +int firmware_simple_putbytes(firmware_simple_update_t *state, const uint8_t *bytes, size_t len) +{ + /* metadata copy */ + if (state->state != FIRMWARE_UPDATE_VERIFIED) { + ssize_t res = _simple_recv_metadata(state, bytes, len); + if (res < 0 ) { + return res; + } + state->writer.offset += res; + bytes += res; + len -= res; + } + /* firmware copy */ + if (state->state == FIRMWARE_UPDATE_VERIFIED && len) { + /* Calculate if there are more bytes that should be written */ + size_t metadata_size = state->m.metadata.metadata.start_addr - + (uint32_t)firmware_get_metadata(state->writer.target_slot); + bool more = (state->m.metadata.size + metadata_size) - + (state->writer.offset + len); + return firmware_flashwrite_putbytes(&state->writer, bytes, len, more); + } + return 0; +} + +int firmware_simple_finish(firmware_simple_update_t *state) +{ + firmware_simple_t *metadata = &state->m.metadata; + + LOG_DEBUG("ota: verifying hash...\n"); + sha256_init(&state->sha); + sha256_update(&state->sha, (void*)metadata->metadata.start_addr, + metadata->size); + + sha256_final(&state->sha, state->writer.flashpage_buf); + + if (memcmp(state->writer.flashpage_buf, metadata->hash, SHA256_DIGEST_LENGTH)) { + LOG_WARNING("ota: image hash incorrect!\n"); + state->state = FIRMWARE_UPDATE_IDLE; + return -1; + } + + LOG_DEBUG("ota: hash verified, re-flashing first block...\n"); + if (firmware_flashwrite_finish(&state->writer, + (firmware_metadata_t*)metadata, sizeof(firmware_simple_t)) != 0) { + return -1; + } + + LOG_INFO("ota: firmware flashing completed successfully\n"); + return 0; + +} +#endif diff --git a/sys/firmware/update.c b/sys/firmware/update.c deleted file mode 100644 index 89b0bab13e0b..000000000000 --- a/sys/firmware/update.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2017 Kaspar Schleiser - * - * 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_firmware - * @{ - * - * @file - * @brief Firmware update helper functions - * - * @author Kaspar Schleiser - * - * @} - */ - -#include - -#include "firmware_update.h" -#include "log.h" -#include "ota_pubkey.h" -#include "od.h" - -static inline size_t min(size_t a, size_t b) -{ - return a <= b ? a : b; -} - -int firmware_update_init(firmware_update_t *firmware_update, int target_slot) -{ - LOG_INFO("ota: initializing update to target slot %i\n", - target_slot); - - memset(firmware_update, 0, sizeof(firmware_update_t)); - - firmware_update->target_slot = target_slot; - firmware_update->flashpage = flashpage_page(firmware_get_metadata(target_slot)); - firmware_update->state = FIRMWARE_UPDATE_INITIALIZED; - - return 0; -} - -int firmware_update_putbytes(firmware_update_t *state, size_t offset, const uint8_t *bytes, size_t len) -{ - firmware_metadata_t *metadata = &state->metadata_buf; - - LOG_DEBUG("ota: processing bytes %u-%u\n", state->offset, len); - - while (len) { - size_t flashpage_pos = state->offset % FLASHPAGE_SIZE; - size_t flashpage_avail = FLASHPAGE_SIZE - flashpage_pos; - - size_t left = 1; - - if (state->state == FIRMWARE_UPDATE_VERIFIED) { - left = (metadata->size + sizeof(firmware_metadata_t) - offset); - len = min(len, left); - } - - size_t to_copy = min(flashpage_avail, len); - - memcpy(state->flashpage_buf + flashpage_pos, bytes, to_copy); - flashpage_avail -= to_copy; - - state->offset += to_copy; - flashpage_pos += to_copy; - bytes += to_copy; - len -= to_copy; - left -= to_copy; - - if ((state->state != FIRMWARE_UPDATE_VERIFIED) && ((offset + to_copy) >= sizeof(firmware_metadata_t))) { - /* copy metadata from flashpage buffer to metadata buffer */ - memcpy(metadata, state->flashpage_buf, sizeof(firmware_metadata_t)); - - LOG_INFO("ota: verifying metadata ...\n"); - if (metadata->start_addr != firmware_get_image_startaddr(state->target_slot)) { - LOG_WARNING("ota: start address doesn't match selected slot. Aborting.\n"); - LOG_WARNING("ota: (image start=%p slot start=%p)\n", (void *)metadata->start_addr, \ - (void *)firmware_get_image_startaddr(state->target_slot)); - return -1; - } - - /* check metadata magic nr, checksum and signature */ - if (firmware_validate_metadata_signature(metadata, ota_public_key)) { - LOG_WARNING("ota: verification failed!\n"); - return -1; - } - else { - state->state = FIRMWARE_UPDATE_VERIFIED; - LOG_INFO("ota: verification successful\n"); - } - - /* clear metadata from flashpage */ - memset(state->flashpage_buf, '\0', sizeof(firmware_metadata_t)); - } - - if ((!flashpage_avail) || (!left)) { - LOG_DEBUG("ota: writing to flashpage %u\n", state->flashpage); - if (flashpage_write_and_verify(state->flashpage, state->flashpage_buf) != FLASHPAGE_OK) { - LOG_WARNING("ota: error writing flashpage %u!\n", state->flashpage); - return -1; - } - state->flashpage++; - } - } - - return 0; -} - -int firmware_update_finish(firmware_update_t *state) -{ - int res = -1; - - void *slot_start = firmware_get_metadata(state->target_slot); - firmware_metadata_t *metadata = &state->metadata_buf; - - LOG_DEBUG("ota: verifying hash...\n"); - sha256_init(&state->sha256); - sha256_update(&state->sha256, slot_start + sizeof(firmware_metadata_t), metadata->size); - - sha256_final(&state->sha256, state->flashpage_buf); - - if (memcmp(state->flashpage_buf, metadata->hash, SHA256_DIGEST_LENGTH)) { - LOG_WARNING("ota: image hash incorrect!\n"); - goto out; - } - - LOG_DEBUG("ota: hash verified, re-flashing first block...\n"); - void *firstpage; - if (sizeof(firmware_metadata_t) < FLASHPAGE_SIZE) { - firstpage = state->flashpage_buf; - memcpy(firstpage, metadata, sizeof(firmware_metadata_t)); - memcpy(firstpage + sizeof(firmware_metadata_t), \ - slot_start + sizeof(firmware_metadata_t), \ - FLASHPAGE_SIZE - sizeof(firmware_metadata_t)); - } - else { - firstpage = metadata; - } - - int flashpage = flashpage_page(slot_start); - if (flashpage_write_and_verify(flashpage, firstpage) != FLASHPAGE_OK) { - LOG_WARNING("ota: re-flashing first block failed!\n"); - goto out; - } - - LOG_INFO("ota: firmware flashing completed successfully\n"); - res = 0; - -out: - state->state = FIRMWARE_UPDATE_IDLE; - return res; -} diff --git a/sys/include/firmware.h b/sys/include/firmware.h index 545f8caa9176..974a9cb12aac 100644 --- a/sys/include/firmware.h +++ b/sys/include/firmware.h @@ -53,9 +53,8 @@ * * - "RIOT" as magic number * - an application version - * - an application ID + * - Start address * - a checksum - * - a signature * * The bootloader only cares about checksum and application version. It expects a * running image to verify the metadata. @@ -119,33 +118,12 @@ extern "C" { #include -#include "hashes/sha256.h" -#include "tweetnacl.h" - -/** - * @brief Provisional length for signed hash - */ -#define FIRMWARE_SIG_LEN (crypto_sign_BYTES) - -/** - * @brief Length of secret Ed25519 key - */ -#define FIRMWARE_SECKEY_LEN (crypto_sign_SECRETKEYBYTES) - -/** - * @brief Length of public Ed25519 key - */ -#define FIRMWARE_PUBKEY_LEN (crypto_sign_PUBLICKEYBYTES) - /** * @brief Number of metadata bytes that get checksummed + * + * Includes magic number, version, and start address. */ -#define FIRMWARE_CHECKSUM_LEN (20) - -/** - * @brief Length of bytes to be signed - */ -#define FIRMWARE_SIGN_BYTES (FIRMWARE_CHECKSUM_LEN + 4 + SHA256_DIGEST_LENGTH) +#define FIRMWARE_CHECKSUM_LEN (16) /** * @brief Length of metadata prefixed to firmware binaries @@ -154,11 +132,6 @@ extern "C" { #define FIRMWARE_METADATA_SIZE (256) #endif -/** - * @brief Length of padding to align metadata size - */ -#define FIRMWARE_PADDING (FIRMWARE_METADATA_SIZE-(FIRMWARE_SIGN_BYTES + FIRMWARE_SIG_LEN)) - /** * @brief Real length of metadata without padding */ @@ -170,26 +143,14 @@ extern "C" { */ typedef struct { uint32_t magic_number; /**< metadata magic_number (always "RIOT") */ - uint32_t appid; /**< Integer representing the application ID*/ uint32_t version; /**< Integer representing firmware version */ - uint32_t size; /**< Size of firmware image (w/o metadata) */ uint32_t start_addr; /**< Start address in flash */ + uint32_t metadata_type; /**< Type of metadata, 16 bits type, + 16 bit version */ uint32_t chksum; /**< checksum of metadata */ - uint8_t hash[SHA256_DIGEST_LENGTH]; /**< SHA256 Hash of firmware image */ - uint8_t sig[FIRMWARE_SIG_LEN]; /**< Firmware signature */ - uint8_t pad[FIRMWARE_PADDING]; /**< padding to total of - FIRMWARE_METADATA_SIZE bytes */ } firmware_metadata_t; /** @} */ -/** - * @brief Print formatted FW image metadata to STDIO - * - * @param[in] metadata ptr to firmware metadata - * - */ -void firmware_metadata_print(firmware_metadata_t *metadata); - /** * @brief Validate FW image metadata * @@ -206,24 +167,6 @@ int firmware_validate_metadata_checksum(firmware_metadata_t *metadata); */ uint32_t firmware_metadata_checksum(firmware_metadata_t *metadata); -/** - * @brief Sign metadata - * - * @param[in] metadata ptr to firmware metadata - * @param[in] sk NaCL secret signing key to use - * - */ -int firmware_sign_metadata(firmware_metadata_t *metadata, unsigned char *sk); - -/** - * @brief Validate FW metadata signature - * - * @param[in] metadata ptr to firmware metadata - * @param[in] pk NaCL public signing key to use - * - */ -int firmware_validate_metadata_signature(firmware_metadata_t *metadata, const unsigned char *pk); - /** * @brief Jump to image * diff --git a/sys/include/firmware_update.h b/sys/include/firmware/flashwrite.h similarity index 50% rename from sys/include/firmware_update.h rename to sys/include/firmware/flashwrite.h index e9684fd395f5..cf8053e17bcf 100644 --- a/sys/include/firmware_update.h +++ b/sys/include/firmware/flashwrite.h @@ -13,21 +13,21 @@ * @file * @brief Over the air (OTA) update module * - * This module provides an API to verify and write an updated firmware to flash. + * This module provides an API to reliably write a firmware image to flash. * * The API is similar to stream hashing functions: * - * 1. initialize state structure using firmware_update_init() - * 2. put some data using firmware_update_putbytes() + * 1. initialize state structure using firmware_flashwrite_init() + * 2. put some data using firmware_flashwrite_putbytes() * 3. repeat 2. until all data has been received - * 4. finish update using firmware_update_finish() + * 4. finish update using firmware_flashwrite_finish() * * The module will *not* automatically reboot after an image has been * successfully written. * - * Under the hood, the module tries to abstract verification of the - * cryptographic image signature and writing to flash. - * Should the metadata or its signature be invalid, firmware_put_bytes() will + * Under the hood, the module tries to abstract page sizes for writing the image + * to flash. Verification of the image is left to the caller. + * If the data is not correctly written, firmware_put_bytes() will * return -1. * * The module makes sure that at no point in time an invalid image is bootable. @@ -36,17 +36,16 @@ * * 1. erase first block (making its checksum invalid) * 2. write image - * 3. as soon as the whole metadata has been received, check it, abort on error - * 4. check hash of (first block + image), abort on error - * 5. write first block + * 3. write first block * * @author Kaspar Schleiser + * @author Koen Zandberg * * @} */ -#ifndef FIRMWARE_UPDATE_H -#define FIRMWARE_UPDATE_H +#ifndef FIRMWARE_FLASHWRITE_H +#define FIRMWARE_FLASHWRITE_H #ifdef __cplusplus extern "C" { @@ -55,63 +54,55 @@ extern "C" { #include "firmware.h" #include "periph/flashpage.h" -/** - * @brief Possible firmware update states - */ -enum { - FIRMWARE_UPDATE_IDLE, /**< no firmware update in progress */ - FIRMWARE_UPDATE_INITIALIZED, /**< firmware update in progress, awaiting - verification */ - FIRMWARE_UPDATE_VERIFIED, /**< firmware update in progress & verified */ -}; - /** * @brief firmware update state structure */ typedef struct { - unsigned state; /**< state (see above enum) */ - int target_slot; /**< update targets this slot */ - size_t offset; /**< update is at this position */ - unsigned flashpage; /**< update is at this flashpage */ - sha256_context_t sha256; /**< sha256 context used by this update */ - uint8_t flashpage_buf[FLASHPAGE_SIZE]; /**< flash writing buffer */ - firmware_metadata_t metadata_buf; /**< metadata scratch buffer */ -} firmware_update_t; + int target_slot; /**< update targets this slot */ + size_t offset; /**< update is at this position */ + unsigned flashpage; /**< update is at this flashpage */ + uint8_t flashpage_buf[FLASHPAGE_SIZE]; /**< flash writing buffer */ +} firmware_flashwrite_t; /** * @brief Initialize firmware update * * @param[in/out] state ptr to preallocated state structure * @param[in] target_slot slot to write update into + * @param[in] offset Bytes offset to start write at * * @returns 0 on success, <0 otherwise */ -int firmware_update_init(firmware_update_t *state, int target_slot); +int firmware_flashwrite_init(firmware_flashwrite_t *state, int target_slot, size_t offset); /** - * @brief Feed bytes into update process + * @brief Feed bytes into the firmware writer * * @param[in/out] state ptr to previously used update state * @param[in] offset offset of @p bytes (from image start) * @param[in] bytes ptr to data * @param[in] len len of data + * @param[in] more Whether more data is comming * * @returns 0 on success, <0 otherwise */ -int firmware_update_putbytes(firmware_update_t *state, size_t offset, - const uint8_t *bytes, size_t len); +int firmware_flashwrite_putbytes(firmware_flashwrite_t *state, + const uint8_t *bytes, size_t len, bool more); /** - * @brief Finish a firmware update + * @brief Finish a firmware update * - * @param[in] state ptr to previously used state structure + * @param[in] state ptr to previously used state structure + * @param[in] metadata Metadata to write on the first page + * @param[in] len Size of the metadata in bytes * * @returns 0 on success, <0 otherwise */ -int firmware_update_finish(firmware_update_t *state); +int firmware_flashwrite_finish(firmware_flashwrite_t *state, + firmware_metadata_t *metadata, size_t len); #ifdef __cplusplus } #endif -#endif /* FIRMWARE_UPDATE_H */ +#endif /* FIRMWARE_FLASHWRITE_H */ diff --git a/sys/include/firmware/simple.h b/sys/include/firmware/simple.h new file mode 100644 index 000000000000..c2675191b23b --- /dev/null +++ b/sys/include/firmware/simple.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2018 Kaspar Schleiser + * 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. + */ + +#ifndef FIRMWARE_SIMPLE_H +#define FIRMWARE_SIMPLE_H + +#include "firmware.h" +#include "hashes/sha256.h" +#ifdef RIOT_VERSION +#include "firmware/flashwrite.h" +#endif + +#include "tweetnacl.h" + +/** + * @brief Provisional length for signed hash + */ +#define FIRMWARE_SIG_LEN (crypto_sign_BYTES) + +/** + * @brief Length of secret Ed25519 key + */ +#define FIRMWARE_SECKEY_LEN (crypto_sign_SECRETKEYBYTES) + +/** + * @brief Length of public Ed25519 key + */ +#define FIRMWARE_PUBKEY_LEN (crypto_sign_PUBLICKEYBYTES) + +#define FIRMWARE_METADATA_RIOTBOOT (0x0100) + +/** + * @brief Length of bytes to be signed + */ +#define FIRMWARE_SIGN_BYTES (sizeof(firmware_simple_t) - FIRMWARE_SIG_LEN) + +typedef struct { + firmware_metadata_t metadata; /**< generic bootloader specific firmware info */ + uint32_t appid; /**< Application type ID */ + uint32_t size; /**< Size of firmware image */ + uint8_t hash[SHA256_DIGEST_LENGTH]; /**< SHA256 Hash of firmware image */ + uint8_t sig[FIRMWARE_SIG_LEN]; /**< Firmware Signature */ +} firmware_simple_t; + +/** + * @brief Possible firmware update states + */ +enum { + FIRMWARE_UPDATE_IDLE, /**< no firmware update in progress */ + FIRMWARE_UPDATE_INITIALIZED, /**< firmware update in progress, awaiting + verification */ + FIRMWARE_UPDATE_VERIFIED, /**< firmware update in progress & verified */ +}; + +#ifdef RIOT_VERSION + +/** + * @brief Firmware update state struct + */ +typedef struct { + firmware_flashwrite_t writer; /**< flashwrite state struct */ + unsigned state; /**< state (see above enum) */ + union { + uint8_t metadata_buf[FIRMWARE_METADATA_SIZE]; + firmware_simple_t metadata; + } m; + sha256_context_t sha; /**< SHA256 digest state struct */ +} firmware_simple_update_t; + +int firmware_simple_init(firmware_simple_update_t *state); + +int firmware_simple_putbytes(firmware_simple_update_t *state, const uint8_t *bytes, size_t len); + +int firmware_simple_finish(firmware_simple_update_t *state); + +#endif /* RIOT_VERSION */ + +/** + * @brief Print formatted FW image metadata to STDIO + * + * @param[in] metadata ptr to firmware metadata + * + */ +void firmware_simple_print(firmware_simple_t *riotboot); + +/** + * @brief Sign metadata + * + * @param[in] metadata ptr to firmware metadata + * @param[in] sk NaCL secret signing key to use + * + */ +int firmware_simple_sign(firmware_simple_t *riotboot, unsigned char *sk); + +/** + * @brief Validate FW metadata signature + * + * @param[in] metadata ptr to firmware metadata + * @param[in] pk NaCL public signing key to use + * + */ +int firmware_simple_validate_signature(firmware_simple_t *riotboot, const unsigned char *pk); +#endif /* FIRMWARE_SIMPLE_H */ diff --git a/sys/net/application_layer/ota/coap.c b/sys/net/application_layer/ota/coap.c index c6f65f8e94ba..18a26f5cc435 100644 --- a/sys/net/application_layer/ota/coap.c +++ b/sys/net/application_layer/ota/coap.c @@ -18,14 +18,14 @@ * @} */ -#include "firmware_update.h" +#include "firmware/simple.h" #include "log.h" #include "net/nanocoap.h" #define ENABLE_DEBUG (0) #include "debug.h" -static firmware_update_t _state; +static firmware_simple_update_t _state; ssize_t ota_coap_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void *context) { @@ -41,7 +41,7 @@ ssize_t ota_coap_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void *contex (unsigned)block1.offset, (unsigned)block1.offset + pkt->payload_len); if (_state.state == FIRMWARE_UPDATE_VERIFIED) { - unsigned total = _state.metadata_buf.size + FIRMWARE_METADATA_SIZE; + unsigned total = _state.m.metadata.size + FIRMWARE_METADATA_SIZE; LOG_INFO(" of %u (left=%u)\n", total, total - block1.offset - pkt->payload_len); } @@ -51,15 +51,15 @@ ssize_t ota_coap_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void *contex if (block1.offset == 0) { /* initialize firmware upgrade state struct */ - firmware_update_init(&_state, firmware_target_slot()); + firmware_simple_init(&_state); } if (_state.state == FIRMWARE_UPDATE_IDLE) { res = -1; } else { - if (block1.offset == _state.offset) { - res = firmware_update_putbytes(&_state, block1.offset, + if (block1.offset == _state.writer.offset) { + res = firmware_simple_putbytes(&_state, pkt->payload, pkt->payload_len); } else { @@ -78,7 +78,7 @@ ssize_t ota_coap_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void *contex } if (!res && (!blockwise || !block1.more)) { - if (firmware_update_finish(&_state) != 0) { + if (firmware_simple_finish(&_state) != 0) { result = COAP_CODE_BAD_OPTION; } } From 1087b6d942cdac8bcca46143748be64c0a6d8d84 Mon Sep 17 00:00:00 2001 From: Koen Zandberg Date: Wed, 2 May 2018 15:53:50 +0200 Subject: [PATCH 27/32] firmware: remove metadata_type from struct --- dist/tools/firmware/sign.c | 2 ++ sys/firmware/firmware_simple.c | 2 +- sys/include/firmware.h | 4 +--- sys/include/firmware/simple.h | 2 ++ 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/dist/tools/firmware/sign.c b/dist/tools/firmware/sign.c index 102142f14eae..2614531b8b55 100644 --- a/dist/tools/firmware/sign.c +++ b/dist/tools/firmware/sign.c @@ -86,6 +86,8 @@ int sign(int argc, char *argv[]) /* calculate metadata checksum */ metadata.metadata.chksum = firmware_metadata_checksum(&metadata.metadata); + metadata.metadata_type = FIRMWARE_METADATA_RIOTBOOT; + /* sign */ firmware_simple_sign(&metadata, sk); diff --git a/sys/firmware/firmware_simple.c b/sys/firmware/firmware_simple.c index ad6a83221523..f0dd852efdc7 100644 --- a/sys/firmware/firmware_simple.c +++ b/sys/firmware/firmware_simple.c @@ -44,7 +44,7 @@ void firmware_simple_print(firmware_simple_t *simple) printf("Firmware Version: %#x\n", (unsigned)simple->metadata.version); printf("Firmware start address: 0x%08x\n", (unsigned)simple->metadata.start_addr); /* Only output full info if the metadata_type matches */ - if (simple->metadata.metadata_type == FIRMWARE_METADATA_RIOTBOOT) + if (simple->metadata_type == FIRMWARE_METADATA_RIOTBOOT) { printf("Firmware APPID: %#x\n", (unsigned)simple->appid); printf("Firmware Size: %" PRIu32 "\n", simple->size); diff --git a/sys/include/firmware.h b/sys/include/firmware.h index 974a9cb12aac..72f0c3e2bb49 100644 --- a/sys/include/firmware.h +++ b/sys/include/firmware.h @@ -123,7 +123,7 @@ extern "C" { * * Includes magic number, version, and start address. */ -#define FIRMWARE_CHECKSUM_LEN (16) +#define FIRMWARE_CHECKSUM_LEN (12) /** * @brief Length of metadata prefixed to firmware binaries @@ -145,8 +145,6 @@ typedef struct { uint32_t magic_number; /**< metadata magic_number (always "RIOT") */ uint32_t version; /**< Integer representing firmware version */ uint32_t start_addr; /**< Start address in flash */ - uint32_t metadata_type; /**< Type of metadata, 16 bits type, - 16 bit version */ uint32_t chksum; /**< checksum of metadata */ } firmware_metadata_t; /** @} */ diff --git a/sys/include/firmware/simple.h b/sys/include/firmware/simple.h index c2675191b23b..9922f39289a0 100644 --- a/sys/include/firmware/simple.h +++ b/sys/include/firmware/simple.h @@ -42,6 +42,8 @@ typedef struct { firmware_metadata_t metadata; /**< generic bootloader specific firmware info */ + uint32_t metadata_type; /**< Type of metadata, 16 bits type, + 16 bit version */ uint32_t appid; /**< Application type ID */ uint32_t size; /**< Size of firmware image */ uint8_t hash[SHA256_DIGEST_LENGTH]; /**< SHA256 Hash of firmware image */ From 2ac74b3b3f5b27f1fd92b2c0c1804d1b8557f91f Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Fri, 1 Jun 2018 11:25:51 +0200 Subject: [PATCH 28/32] sys/firmware: some updates - add comment to clarify firmware_current_slot() 1 loop start - use metadata->start_addr in firmware_get_image_startaddr() --- sys/firmware/firmware.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sys/firmware/firmware.c b/sys/firmware/firmware.c index 6a94d84b2697..14ee01d9acab 100644 --- a/sys/firmware/firmware.c +++ b/sys/firmware/firmware.c @@ -72,6 +72,7 @@ int firmware_current_slot(void) { unsigned base_addr = cpu_get_image_baseaddr() - FIRMWARE_METADATA_SIZE; + /* don't consider bootloader slot, thus start at 1 */ for (unsigned i = 1; i < firmware_num_slots; i++) { if (base_addr == _firmware_slot_start[i]) { return i; @@ -96,7 +97,7 @@ firmware_metadata_t *firmware_get_metadata(unsigned slot) unsigned firmware_get_image_startaddr(unsigned slot) { assert(slot < FIRMWARE_NUM_SLOTS); - return _firmware_slot_start[slot] + FIRMWARE_METADATA_SIZE; + return firmware_get_metadata(slot)->start_addr; } void firmware_jump_to_slot(unsigned slot) From 6a0ec116d3d9a2bfe1417aee81ca682522c40e21 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Fri, 1 Jun 2018 11:27:47 +0200 Subject: [PATCH 29/32] sys/firmware: remove FIRMWARE_NUM_SLOTS, use firmware_num_slots --- sys/firmware/Makefile | 1 - sys/firmware/firmware.c | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/sys/firmware/Makefile b/sys/firmware/Makefile index d47a06569849..b59eb29ea1a6 100644 --- a/sys/firmware/Makefile +++ b/sys/firmware/Makefile @@ -1,4 +1,3 @@ -CFLAGS += -DFIRMWARE_NUM_SLOTS=3 CFLAGS += -DSLOT0_SIZE=$(RIOTBOOT_SLOT0_SIZE) CFLAGS += -DSLOT1_SIZE=$(RIOTBOOT_SLOT1_SIZE) CFLAGS += -DSLOT2_SIZE=$(RIOTBOOT_SLOT2_SIZE) diff --git a/sys/firmware/firmware.c b/sys/firmware/firmware.c index 14ee01d9acab..ce4e737928e2 100644 --- a/sys/firmware/firmware.c +++ b/sys/firmware/firmware.c @@ -90,13 +90,13 @@ int firmware_target_slot(void) firmware_metadata_t *firmware_get_metadata(unsigned slot) { - assert(slot < FIRMWARE_NUM_SLOTS); + assert(slot < firmware_num_slots); return (firmware_metadata_t *)_firmware_slot_start[slot]; } unsigned firmware_get_image_startaddr(unsigned slot) { - assert(slot < FIRMWARE_NUM_SLOTS); + assert(slot < firmware_num_slots); return firmware_get_metadata(slot)->start_addr; } @@ -107,7 +107,7 @@ void firmware_jump_to_slot(unsigned slot) void firmware_dump_slot_addrs(void) { - for (unsigned i = 1; i < FIRMWARE_NUM_SLOTS; i++) { + for (unsigned i = 1; i < firmware_num_slots; i++) { printf("slot %u: metadata: 0x%08x image: 0x%08x\n", i, (unsigned)_firmware_slot_start[i], firmware_get_image_startaddr(i)); } From d4ca843a90055f3d2140feb61bf8958994c71e33 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Fri, 1 Jun 2018 11:52:23 +0200 Subject: [PATCH 30/32] sys: firmware_flashwrite: fix log message prefixes --- sys/firmware/firmware_flashwrite.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sys/firmware/firmware_flashwrite.c b/sys/firmware/firmware_flashwrite.c index afdf9c5ecc2b..0b5c65e4c767 100644 --- a/sys/firmware/firmware_flashwrite.c +++ b/sys/firmware/firmware_flashwrite.c @@ -21,9 +21,11 @@ #include #include "firmware/flashwrite.h" -#include "log.h" #include "od.h" +#define LOG_PREFIX "firmware_flashwrite: " +#include "log.h" + static inline size_t min(size_t a, size_t b) { return a <= b ? a : b; @@ -32,7 +34,7 @@ static inline size_t min(size_t a, size_t b) int firmware_flashwrite_init(firmware_flashwrite_t *state, int target_slot, size_t offset) { - LOG_INFO("ota: initializing update to target slot %i\n", + LOG_INFO(LOG_PREFIX "initializing update to target slot %i\n", target_slot); memset(state, 0, sizeof(firmware_flashwrite_t)); @@ -47,7 +49,7 @@ int firmware_flashwrite_init(firmware_flashwrite_t *state, int target_slot, int firmware_flashwrite_putbytes(firmware_flashwrite_t *state, const uint8_t *bytes, size_t len, bool more) { - LOG_INFO("ota: processing bytes %u-%u\n", state->offset, state->offset + len - 1); + LOG_INFO(LOG_PREFIX "processing bytes %u-%u\n", state->offset, state->offset + len - 1); while (len) { size_t flashpage_pos = state->offset % FLASHPAGE_SIZE; @@ -64,7 +66,7 @@ int firmware_flashwrite_putbytes(firmware_flashwrite_t *state, len -= to_copy; if ((!flashpage_avail) || (!more)) { if (flashpage_write_and_verify(state->flashpage, state->flashpage_buf) != FLASHPAGE_OK) { - LOG_WARNING("ota: error writing flashpage %u!\n", state->flashpage); + LOG_WARNING(LOG_PREFIX "error writing flashpage %u!\n", state->flashpage); return -1; } state->flashpage++; @@ -98,11 +100,11 @@ int firmware_flashwrite_finish(firmware_flashwrite_t *state, int flashpage = flashpage_page(slot_start); if (flashpage_write_and_verify(flashpage, firstpage) != FLASHPAGE_OK) { - LOG_WARNING("ota: re-flashing first block failed!\n"); + LOG_WARNING(LOG_PREFIX "re-flashing first block failed!\n"); goto out; } - LOG_INFO("ota: firmware flashing completed successfully\n"); + LOG_INFO(LOG_PREFIX "firmware flashing completed successfully\n"); res = 0; out: From 143db71861759a0841275d065b5d938b57e7ca0d Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Fri, 1 Jun 2018 12:08:48 +0200 Subject: [PATCH 31/32] sys/firmware: simple: fix log prefix --- sys/firmware/firmware_simple.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/sys/firmware/firmware_simple.c b/sys/firmware/firmware_simple.c index f0dd852efdc7..1c044a74a836 100644 --- a/sys/firmware/firmware_simple.c +++ b/sys/firmware/firmware_simple.c @@ -22,6 +22,8 @@ #include +#define LOG_PREFIX "firmware_simple: " + #ifdef RIOT_VERSION #include "log.h" #include "ota_pubkey.h" @@ -105,22 +107,22 @@ static ssize_t _simple_recv_metadata(firmware_simple_update_t *state, const uint if ((state->writer.offset + to_copy) >= FIRMWARE_METADATA_SIZE - 1) { /* Full metadata received */ firmware_simple_t *metadata = &state->m.metadata; - LOG_INFO("ota: verifying metadata ...\n"); + LOG_INFO(LOG_PREFIX "verifying metadata ...\n"); if (metadata->metadata.start_addr != firmware_get_image_startaddr(state->writer.target_slot)) { - LOG_WARNING("ota: start address doesn't match selected slot. Aborting.\n"); - LOG_WARNING("ota: (image start=%p slot start=%p)\n", (void *)metadata->metadata.start_addr, \ + LOG_WARNING(LOG_PREFIX "start address doesn't match selected slot. Aborting.\n"); + LOG_WARNING(LOG_PREFIX "(image start=%p slot start=%p)\n", (void *)metadata->metadata.start_addr, \ (void *)firmware_get_image_startaddr(state->writer.target_slot)); return -1; } /* check metadata magic nr, checksum and signature */ if (firmware_simple_validate_signature(metadata, ota_public_key)) { - LOG_WARNING("ota: verification failed!\n"); + LOG_WARNING(LOG_PREFIX "verification failed!\n"); return -1; } else { state->state = FIRMWARE_UPDATE_VERIFIED; - LOG_INFO("ota: verification successful\n"); + LOG_INFO(LOG_PREFIX "verification successful\n"); } state->writer.flashpage++; } @@ -161,7 +163,7 @@ int firmware_simple_finish(firmware_simple_update_t *state) { firmware_simple_t *metadata = &state->m.metadata; - LOG_DEBUG("ota: verifying hash...\n"); + LOG_DEBUG(LOG_PREFIX "verifying hash...\n"); sha256_init(&state->sha); sha256_update(&state->sha, (void*)metadata->metadata.start_addr, metadata->size); @@ -169,18 +171,18 @@ int firmware_simple_finish(firmware_simple_update_t *state) sha256_final(&state->sha, state->writer.flashpage_buf); if (memcmp(state->writer.flashpage_buf, metadata->hash, SHA256_DIGEST_LENGTH)) { - LOG_WARNING("ota: image hash incorrect!\n"); + LOG_WARNING(LOG_PREFIX "image hash incorrect!\n"); state->state = FIRMWARE_UPDATE_IDLE; return -1; } - LOG_DEBUG("ota: hash verified, re-flashing first block...\n"); + LOG_DEBUG(LOG_PREFIX "hash verified, re-flashing first block...\n"); if (firmware_flashwrite_finish(&state->writer, (firmware_metadata_t*)metadata, sizeof(firmware_simple_t)) != 0) { return -1; } - LOG_INFO("ota: firmware flashing completed successfully\n"); + LOG_INFO(LOG_PREFIX "firmware flashing completed successfully\n"); return 0; } From 714ba810e687f38d1a906c41db9df2fe7ca70134 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Fri, 1 Jun 2018 15:28:18 +0200 Subject: [PATCH 32/32] sys: firmware_simple: add size check --- sys/firmware/firmware_flashwrite.c | 9 +++++++++ sys/firmware/firmware_simple.c | 25 +++++++++++++++++++++---- sys/include/firmware/flashwrite.h | 2 ++ sys/include/firmware/simple.h | 5 +++-- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/sys/firmware/firmware_flashwrite.c b/sys/firmware/firmware_flashwrite.c index 0b5c65e4c767..55080b1a1d92 100644 --- a/sys/firmware/firmware_flashwrite.c +++ b/sys/firmware/firmware_flashwrite.c @@ -31,6 +31,15 @@ static inline size_t min(size_t a, size_t b) return a <= b ? a : b; } +size_t firmware_flashwrite_slotsize(const firmware_flashwrite_t *state) +{ + switch (state->target_slot) { + case 1: return SLOT1_SIZE; + case 2: return SLOT2_SIZE; + default: return 0; + } +} + int firmware_flashwrite_init(firmware_flashwrite_t *state, int target_slot, size_t offset) { diff --git a/sys/firmware/firmware_simple.c b/sys/firmware/firmware_simple.c index 1c044a74a836..2207c3363a74 100644 --- a/sys/firmware/firmware_simple.c +++ b/sys/firmware/firmware_simple.c @@ -20,6 +20,7 @@ * @} */ +#include #include #define LOG_PREFIX "firmware_simple: " @@ -67,9 +68,20 @@ void firmware_simple_print(firmware_simple_t *simple) } } -int firmware_simple_validate_signature(firmware_simple_t *simple, const unsigned char *pk) +int firmware_simple_validate_size(firmware_simple_update_t *simple) +{ + if (simple->m.metadata.size <= firmware_flashwrite_slotsize(&simple->writer)) { + return 0; + } + else { + return -1; + } +} + +int firmware_simple_validate(firmware_simple_t *simple, const unsigned char *pk) { if (firmware_validate_metadata_checksum(&simple->metadata)) { + LOG_WARNING("%s: metadata chacksum invalid\n", __func__); return -1; } @@ -81,7 +93,7 @@ int firmware_simple_validate_signature(firmware_simple_t *simple, const unsigned unsigned long long mlen; int res = crypto_sign_open(m, &mlen, sm, FIRMWARE_SIGN_BYTES + crypto_sign_BYTES, pk); if (res) { - LOG_INFO("%s: RIOTboot metadata signature invalid\n", __func__); + LOG_WARNING("%s: RIOTboot metadata signature invalid\n", __func__); } return res; } @@ -115,8 +127,13 @@ static ssize_t _simple_recv_metadata(firmware_simple_update_t *state, const uint return -1; } - /* check metadata magic nr, checksum and signature */ - if (firmware_simple_validate_signature(metadata, ota_public_key)) { + if (firmware_simple_validate_size(state)) { + LOG_WARNING("%s: firmware size invalid\n", __func__); + return -ENOSPC; + } + + /* check metadata magic nr, checksum, signature and general validity */ + if (firmware_simple_validate(metadata, ota_public_key)) { LOG_WARNING(LOG_PREFIX "verification failed!\n"); return -1; } diff --git a/sys/include/firmware/flashwrite.h b/sys/include/firmware/flashwrite.h index cf8053e17bcf..591acc043da6 100644 --- a/sys/include/firmware/flashwrite.h +++ b/sys/include/firmware/flashwrite.h @@ -101,6 +101,8 @@ int firmware_flashwrite_putbytes(firmware_flashwrite_t *state, int firmware_flashwrite_finish(firmware_flashwrite_t *state, firmware_metadata_t *metadata, size_t len); +size_t firmware_flashwrite_slotsize(const firmware_flashwrite_t *state); + #ifdef __cplusplus } #endif diff --git a/sys/include/firmware/simple.h b/sys/include/firmware/simple.h index 9922f39289a0..eeaa4bef776b 100644 --- a/sys/include/firmware/simple.h +++ b/sys/include/firmware/simple.h @@ -101,11 +101,12 @@ void firmware_simple_print(firmware_simple_t *riotboot); int firmware_simple_sign(firmware_simple_t *riotboot, unsigned char *sk); /** - * @brief Validate FW metadata signature + * @brief Validate FW metadata * * @param[in] metadata ptr to firmware metadata * @param[in] pk NaCL public signing key to use * */ -int firmware_simple_validate_signature(firmware_simple_t *riotboot, const unsigned char *pk); +int firmware_simple_validate(firmware_simple_t *riotboot, const unsigned char *pk); + #endif /* FIRMWARE_SIMPLE_H */