aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ports/stm32/Makefile24
-rwxr-xr-xports/stm32/mboot/Makefile16
-rw-r--r--ports/stm32/mboot/Particle.h10
-rw-r--r--ports/stm32/mboot/README.md28
-rw-r--r--ports/stm32/mboot/dfu.h10
-rw-r--r--ports/stm32/mboot/fsload.c45
-rw-r--r--ports/stm32/mboot/fwupdate.py7
-rw-r--r--ports/stm32/mboot/gzstream.c16
-rw-r--r--ports/stm32/mboot/gzstream.h3
-rw-r--r--ports/stm32/mboot/main.c68
-rw-r--r--ports/stm32/mboot/mboot.h7
-rw-r--r--ports/stm32/mboot/mboot_pack_dfu.py260
-rw-r--r--ports/stm32/mboot/pack.c282
-rw-r--r--ports/stm32/mboot/pack.h82
-rw-r--r--ports/stm32/mboot/stm32_generic.ld4
-rw-r--r--ports/stm32/mboot/vfs_fat.c2
-rw-r--r--ports/stm32/mboot/vfs_lfs.c4
-rw-r--r--ports/stm32/mpconfigport.mk7
18 files changed, 844 insertions, 31 deletions
diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile
index a9dac03d2..d8c9772b2 100644
--- a/ports/stm32/Makefile
+++ b/ports/stm32/Makefile
@@ -30,7 +30,7 @@ FROZEN_MANIFEST ?= boards/manifest.py
# include py core make definitions
include $(TOP)/py/py.mk
-GIT_SUBMODULES = lib/lwip lib/mbedtls lib/mynewt-nimble lib/stm32lib
+GIT_SUBMODULES = lib/libhydrogen lib/lwip lib/mbedtls lib/mynewt-nimble lib/stm32lib
MCU_SERIES_UPPER = $(shell echo $(MCU_SERIES) | tr '[:lower:]' '[:upper:]')
CMSIS_MCU_LOWER = $(shell echo $(CMSIS_MCU) | tr '[:upper:]' '[:lower:]')
@@ -41,6 +41,7 @@ HAL_DIR=lib/stm32lib/STM32$(MCU_SERIES_UPPER)xx_HAL_Driver
USBDEV_DIR=usbdev
#USBHOST_DIR=usbhost
DFU=$(TOP)/tools/dfu.py
+MBOOT_PACK_DFU = mboot/mboot_pack_dfu.py
# may need to prefix dfu-util with sudo
USE_PYDFU ?= 1
PYDFU ?= $(TOP)/tools/pydfu.py
@@ -546,7 +547,13 @@ $(PY_BUILD)/formatfloat.o: COPT += -Os
$(PY_BUILD)/parsenum.o: COPT += -Os
$(PY_BUILD)/mpprint.o: COPT += -Os
-all: $(TOP)/lib/stm32lib/README.md $(BUILD)/firmware.dfu $(BUILD)/firmware.hex
+all: $(TOP)/lib/stm32lib/README.md all_main $(BUILD)/firmware.hex
+
+ifeq ($(MBOOT_ENABLE_PACKING),1)
+all_main: $(BUILD)/firmware.pack.dfu
+else
+all_main: $(BUILD)/firmware.dfu
+endif
# For convenience, automatically fetch required submodules if they don't exist
$(TOP)/lib/stm32lib/README.md:
@@ -607,6 +614,11 @@ define GENERATE_DFU
$(1)
endef
+define GENERATE_PACK_DFU
+ $(ECHO) "GEN $(1)"
+ $(Q)$(PYTHON) $(MBOOT_PACK_DFU) --keys $(MBOOT_PACK_KEYS_FILE) pack-dfu --gzip $(MBOOT_PACK_CHUNKSIZE) $(2) $(1)
+endef
+
define GENERATE_HEX
$(ECHO) "GEN $(1)"
$(Q)$(OBJCOPY) -O ihex $(2) $(1)
@@ -614,8 +626,13 @@ endef
.PHONY: deploy deploy-stlink deploy-openocd
+ifeq ($(MBOOT_ENABLE_PACKING),1)
+deploy: $(BUILD)/firmware.pack.dfu
+ $(call RUN_DFU,$^)
+else
deploy: $(BUILD)/firmware.dfu
$(call RUN_DFU,$^)
+endif
# A board should specify TEXT0_ADDR if to use a different location than the
# default for the firmware memory location. A board can also optionally define
@@ -662,6 +679,9 @@ $(BUILD)/firmware.dfu: $(BUILD)/firmware0.bin $(BUILD)/firmware1.bin
$(call GENERATE_DFU,$@,$(word 1,$^),$(TEXT0_ADDR),$(word 2,$^),$(TEXT1_ADDR))
endif
+$(BUILD)/firmware.pack.dfu: $(BUILD)/firmware.dfu $(BOARD_DIR)/mboot_keys.h
+ $(call GENERATE_PACK_DFU,$@,$<)
+
$(BUILD)/firmware.hex: $(BUILD)/firmware.elf
$(call GENERATE_HEX,$@,$^)
diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile
index 44372b0e8..ad2fda3ee 100755
--- a/ports/stm32/mboot/Makefile
+++ b/ports/stm32/mboot/Makefile
@@ -12,6 +12,12 @@ BOARD_DIR ?= $(abspath ../boards/$(BOARD))
# that can be built with or without mboot.
USE_MBOOT ?= 1
+# Set MBOOT_ENABLE_PACKING to 1 to enable DFU packing with encryption and signing.
+# Ensure the MBOOT_PACK_xxx values match stm32/Makefile, to build matching application firmware.
+MBOOT_ENABLE_PACKING ?= 0
+MBOOT_PACK_CHUNKSIZE ?= 16384
+MBOOT_PACK_KEYS_FILE ?= $(BOARD_DIR)/mboot_keys.h
+
# Sanity check that the board configuration directory exists
ifeq ($(wildcard $(BOARD_DIR)/.),)
$(error Invalid BOARD specified: $(BOARD_DIR))
@@ -110,6 +116,7 @@ SRC_C = \
elem.c \
fsload.c \
gzstream.c \
+ pack.c \
vfs_fat.c \
vfs_lfs.c \
drivers/bus/softspi.c \
@@ -129,6 +136,15 @@ SRC_O = \
$(SYSTEM_FILE) \
ports/stm32/resethandler.o \
+ifeq ($(MBOOT_ENABLE_PACKING), 1)
+
+SRC_C += lib/libhydrogen/hydrogen.c
+
+CFLAGS += -DMBOOT_ENABLE_PACKING=1 -DPARTICLE -DPLATFORM_ID=3
+CFLAGS += -DMBOOT_PACK_CHUNKSIZE=$(MBOOT_PACK_CHUNKSIZE)
+CFLAGS += -DMBOOT_PACK_KEYS_FILE=\"$(MBOOT_PACK_KEYS_FILE)\"
+endif
+
$(BUILD)/$(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_ll_usb.o: CFLAGS += -Wno-attributes
SRC_HAL = $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\
hal_cortex.c \
diff --git a/ports/stm32/mboot/Particle.h b/ports/stm32/mboot/Particle.h
new file mode 100644
index 000000000..5e0953739
--- /dev/null
+++ b/ports/stm32/mboot/Particle.h
@@ -0,0 +1,10 @@
+// Header for libhydrogen use only. Act like a Particle board for the random
+// implementation. This code is not actually called when just decrypting and
+// verifying a signature, but a correct implementation is provided anyway.
+
+#include "py/mphal.h"
+#include "rng.h"
+
+static inline uint32_t HAL_RNG_GetRandomNumber(void) {
+ return rng_get();
+}
diff --git a/ports/stm32/mboot/README.md b/ports/stm32/mboot/README.md
index d8aa6d456..59213eb61 100644
--- a/ports/stm32/mboot/README.md
+++ b/ports/stm32/mboot/README.md
@@ -155,6 +155,34 @@ firmware.dfu.gz stored on the default FAT filesystem:
The 0x80000000 value is the address understood by Mboot as the location of
the external SPI flash, configured via `MBOOT_SPIFLASH_ADDR`.
+Signed and encrypted DFU support
+--------------------------------
+
+Mboot optionally supports signing and encrypting the binary firmware in the DFU file.
+In general this is refered to as a packed DFU file. This requires additional settings
+in the board config and requires the `pyhy` Python module to be installed for `python3`
+to be used when building packed firmware, eg:
+
+ $ pip3 install pyhy
+
+In addition to the changes made to mpconfigboard.mk earlier, for encrypted
+support you also need to add:
+
+ MBOOT_ENABLE_PACKING = 1
+
+You will also need to generate signing and encryption keys which will be built into
+mboot and used for all subsequent installations of firmware. This can be done via:
+
+ $ python3 ports/stm32/mboot/mboot_pack_dfu.py generate-keys
+
+This command generates a `mboot_keys.h` file which should be stored in the board
+definition folder (next to mpconfigboard.mk).
+
+Once you build the firmware, the `firmware.pack.dfu` file will contain the encrypted
+and signed firmware, and can be deployed via USB DFU, or by copying it to the device's
+internal filesystem (if `MBOOT_FSLOAD` is enabled). `firmware.dfu` is still unencrypted
+and can be directly flashed with jtag etc.
+
Example: Mboot on PYBv1.x
-------------------------
diff --git a/ports/stm32/mboot/dfu.h b/ports/stm32/mboot/dfu.h
index a1d4d10d0..73db3545d 100644
--- a/ports/stm32/mboot/dfu.h
+++ b/ports/stm32/mboot/dfu.h
@@ -39,6 +39,14 @@
#define MBOOT_ERROR_STR_INVALID_ADDRESS_IDX 0x11
#define MBOOT_ERROR_STR_INVALID_ADDRESS "Address out of range"
+#if MBOOT_ENABLE_PACKING
+#define MBOOT_ERROR_STR_INVALID_SIG_IDX 0x12
+#define MBOOT_ERROR_STR_INVALID_SIG "Invalid signature in file"
+
+#define MBOOT_ERROR_STR_INVALID_READ_IDX 0x13
+#define MBOOT_ERROR_STR_INVALID_READ "Read support disabled on encrypted bootloader"
+#endif
+
// DFU class requests
enum {
DFU_DETACH = 0,
@@ -104,6 +112,6 @@ typedef struct _dfu_state_t {
uint8_t buf[DFU_XFER_SIZE] __attribute__((aligned(4)));
} dfu_context_t;
-static dfu_context_t dfu_context SECTION_NOZERO_BSS;
+extern dfu_context_t dfu_context;
#endif // MICROPY_INCLUDED_STM32_MBOOT_DFU_H
diff --git a/ports/stm32/mboot/fsload.c b/ports/stm32/mboot/fsload.c
index 1e1ad7a04..591b670aa 100644
--- a/ports/stm32/mboot/fsload.c
+++ b/ports/stm32/mboot/fsload.c
@@ -28,6 +28,7 @@
#include "py/mphal.h"
#include "mboot.h"
+#include "pack.h"
#include "vfs.h"
#if MBOOT_FSLOAD
@@ -36,13 +37,43 @@
#error Must enable at least one VFS component
#endif
+#if MBOOT_ENABLE_PACKING
+// Packed DFU files are gzip'd internally, not on the outside, so reads of the file
+// just read the file directly.
+
+static void *input_stream_data;
+static stream_read_t input_stream_read_meth;
+
+static inline int input_stream_init(void *stream_data, stream_read_t stream_read) {
+ input_stream_data = stream_data;
+ input_stream_read_meth = stream_read;
+ return 0;
+}
+
+static inline int input_stream_read(size_t len, uint8_t *buf) {
+ return input_stream_read_meth(input_stream_data, buf, len);
+}
+
+#else
+// Standard (non-packed) DFU files must be gzip'd externally / on the outside, so
+// reads of the file go through gz_stream.
+
+static inline int input_stream_init(void *stream_data, stream_read_t stream_read) {
+ return gz_stream_init_from_stream(stream_data, stream_read);
+}
+
+static inline int input_stream_read(size_t len, uint8_t *buf) {
+ return gz_stream_read(len, buf);
+}
+#endif
+
static int fsload_program_file(bool write_to_flash) {
// Parse DFU
uint8_t buf[512];
size_t file_offset;
// Read file header, <5sBIB
- int res = gz_stream_read(11, buf);
+ int res = input_stream_read(11, buf);
if (res != 11) {
return -1;
}
@@ -62,7 +93,7 @@ static int fsload_program_file(bool write_to_flash) {
uint32_t total_size = get_le32(buf + 6);
// Read target header, <6sBi255sII
- res = gz_stream_read(274, buf);
+ res = input_stream_read(274, buf);
if (res != 274) {
return -1;
}
@@ -82,7 +113,7 @@ static int fsload_program_file(bool write_to_flash) {
// Parse each element
for (size_t elem = 0; elem < num_elems; ++elem) {
// Read element header, <II
- res = gz_stream_read(8, buf);
+ res = input_stream_read(8, buf);
if (res != 8) {
return -1;
}
@@ -92,6 +123,7 @@ static int fsload_program_file(bool write_to_flash) {
uint32_t elem_addr = get_le32(buf);
uint32_t elem_size = get_le32(buf + 4);
+ #if !MBOOT_ENABLE_PACKING
// Erase flash before writing
if (write_to_flash) {
uint32_t addr = elem_addr;
@@ -102,6 +134,7 @@ static int fsload_program_file(bool write_to_flash) {
}
}
}
+ #endif
// Read element data and possibly write to flash
for (uint32_t s = elem_size; s;) {
@@ -109,7 +142,7 @@ static int fsload_program_file(bool write_to_flash) {
if (l > sizeof(buf)) {
l = sizeof(buf);
}
- res = gz_stream_read(l, buf);
+ res = input_stream_read(l, buf);
if (res != l) {
return -1;
}
@@ -135,7 +168,7 @@ static int fsload_program_file(bool write_to_flash) {
}
// Read trailing info
- res = gz_stream_read(16, buf);
+ res = input_stream_read(16, buf);
if (res != 16) {
return -1;
}
@@ -151,7 +184,7 @@ static int fsload_validate_and_program_file(void *stream, const stream_methods_t
led_state_all(pass == 0 ? 2 : 4);
int res = meth->open(stream, fname);
if (res == 0) {
- res = gz_stream_init(stream, meth->read);
+ res = input_stream_init(stream, meth->read);
if (res == 0) {
res = fsload_program_file(pass == 0 ? false : true);
}
diff --git a/ports/stm32/mboot/fwupdate.py b/ports/stm32/mboot/fwupdate.py
index dab5fa663..65284a6ac 100644
--- a/ports/stm32/mboot/fwupdate.py
+++ b/ports/stm32/mboot/fwupdate.py
@@ -157,14 +157,15 @@ def update_mboot(filename):
def update_mpy(filename, fs_base, fs_len, fs_type=VFS_FAT):
- # Check firmware is of .dfu.gz type
+ # Check firmware is of .dfu or .dfu.gz type
try:
with open(filename, "rb") as f:
hdr = uzlib.DecompIO(f, 16 + 15).read(6)
except Exception:
- hdr = None
+ with open(filename, "rb") as f:
+ hdr = f.read(6)
if hdr != b"DfuSe\x01":
- print("Firmware must be a .dfu.gz file.")
+ print("Firmware must be a .dfu(.gz) file.")
return
ELEM_TYPE_END = 1
diff --git a/ports/stm32/mboot/gzstream.c b/ports/stm32/mboot/gzstream.c
index 4f8a4a40c..6530539f4 100644
--- a/ports/stm32/mboot/gzstream.c
+++ b/ports/stm32/mboot/gzstream.c
@@ -31,7 +31,7 @@
#include "gzstream.h"
#include "mboot.h"
-#if MBOOT_FSLOAD
+#if MBOOT_FSLOAD || MBOOT_ENABLE_PACKING
#define DICT_SIZE (1 << 15)
@@ -61,7 +61,17 @@ static int gz_stream_read_src(TINF_DATA *tinf) {
return gz_stream.buf[0];
}
-int gz_stream_init(void *stream_data, stream_read_t stream_read) {
+int gz_stream_init_from_raw_data(const uint8_t *data, size_t len) {
+ memset(&gz_stream.tinf, 0, sizeof(gz_stream.tinf));
+ gz_stream.tinf.source = data;
+ gz_stream.tinf.source_limit = data + len;
+
+ uzlib_uncompress_init(&gz_stream.tinf, gz_stream.dict, DICT_SIZE);
+
+ return 0;
+}
+
+int gz_stream_init_from_stream(void *stream_data, stream_read_t stream_read) {
gz_stream.stream_data = stream_data;
gz_stream.stream_read = stream_read;
@@ -97,4 +107,4 @@ int gz_stream_read(size_t len, uint8_t *buf) {
return gz_stream.tinf.dest - buf;
}
-#endif // MBOOT_FSLOAD
+#endif // MBOOT_FSLOAD || MBOOT_ENABLE_PACKING
diff --git a/ports/stm32/mboot/gzstream.h b/ports/stm32/mboot/gzstream.h
index ec11ba79b..d3d93aa65 100644
--- a/ports/stm32/mboot/gzstream.h
+++ b/ports/stm32/mboot/gzstream.h
@@ -39,7 +39,8 @@ typedef struct _stream_methods_t {
stream_read_t read;
} stream_methods_t;
-int gz_stream_init(void *stream_data, stream_read_t stream_read);
+int gz_stream_init_from_raw_data(const uint8_t *data, size_t len);
+int gz_stream_init_from_stream(void *stream_data, stream_read_t stream_read);
int gz_stream_read(size_t len, uint8_t *buf);
#endif // MICROPY_INCLUDED_STM32_MBOOT_GZSTREAM_H
diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c
index 99187e3ef..0846d97cf 100644
--- a/ports/stm32/mboot/main.c
+++ b/ports/stm32/mboot/main.c
@@ -37,6 +37,7 @@
#include "mboot.h"
#include "powerctrl.h"
#include "dfu.h"
+#include "pack.h"
// This option selects whether to use explicit polling or IRQs for USB events.
// In some test cases polling mode can run slightly faster, but it uses more power.
@@ -56,11 +57,17 @@
// Configure PLL to give the desired CPU freq
#undef MICROPY_HW_FLASH_LATENCY
#if defined(STM32F4) || defined(STM32F7)
-#define CORE_PLL_FREQ (48000000)
-#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_1
+ #if MBOOT_ENABLE_PACKING
+ // With encryption/signing/compression, a faster CPU makes processing much faster.
+ #define CORE_PLL_FREQ (96000000)
+ #define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_3
+ #else
+ #define CORE_PLL_FREQ (48000000)
+ #define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_1
+ #endif
#elif defined(STM32H7)
-#define CORE_PLL_FREQ (96000000)
-#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_2
+ #define CORE_PLL_FREQ (96000000)
+ #define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_2
#endif
#undef MICROPY_HW_CLK_PLLM
#undef MICROPY_HW_CLK_PLLN
@@ -87,7 +94,8 @@
// These bits are used to detect valid application firmware at APPLICATION_ADDR
#define APP_VALIDITY_BITS (0x00000003)
-#define MP_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+// Global dfu state
+dfu_context_t dfu_context SECTION_NOZERO_BSS;
static void do_reset(void);
@@ -140,7 +148,7 @@ void HAL_Delay(uint32_t ms) {
mp_hal_delay_ms(ms);
}
-static void __fatal_error(const char *msg) {
+NORETURN static void __fatal_error(const char *msg) {
NVIC_SystemReset();
for (;;) {
}
@@ -547,7 +555,7 @@ static int spiflash_page_erase(mp_spiflash_t *spif, uint32_t addr, uint32_t n_bl
}
#endif
-int do_page_erase(uint32_t addr, uint32_t *next_addr) {
+int hw_page_erase(uint32_t addr, uint32_t *next_addr) {
int ret = -1;
led0_state(LED0_STATE_ON);
@@ -573,7 +581,7 @@ int do_page_erase(uint32_t addr, uint32_t *next_addr) {
return ret;
}
-void do_read(uint32_t addr, int len, uint8_t *buf) {
+void hw_read(uint32_t addr, int len, uint8_t *buf) {
led0_state(LED0_STATE_FAST_FLASH);
#if defined(MBOOT_SPIFLASH_ADDR)
if (MBOOT_SPIFLASH_ADDR <= addr && addr < MBOOT_SPIFLASH_ADDR + MBOOT_SPIFLASH_BYTE_SIZE) {
@@ -592,7 +600,7 @@ void do_read(uint32_t addr, int len, uint8_t *buf) {
led0_state(LED0_STATE_SLOW_FLASH);
}
-int do_write(uint32_t addr, const uint8_t *src8, size_t len) {
+int hw_write(uint32_t addr, const uint8_t *src8, size_t len) {
int ret = -1;
led0_state(LED0_STATE_FAST_FLASH);
#if defined(MBOOT_SPIFLASH_ADDR)
@@ -616,6 +624,34 @@ int do_write(uint32_t addr, const uint8_t *src8, size_t len) {
return ret;
}
+int do_page_erase(uint32_t addr, uint32_t *next_addr) {
+ #if MBOOT_ENABLE_PACKING
+ // Erase handled automatically for packed mode.
+ return 0;
+ #else
+ return hw_page_erase(addr, next_addr);
+ #endif
+}
+
+void do_read(uint32_t addr, int len, uint8_t *buf) {
+ #if MBOOT_ENABLE_PACKING
+ // Read disabled on packed (encrypted) mode.
+ dfu_context.status = DFU_STATUS_ERROR_FILE;
+ dfu_context.error = MBOOT_ERROR_STR_INVALID_READ_IDX;
+ led0_state(LED0_STATE_SLOW_INVERTED_FLASH);
+ #else
+ hw_read(addr, len, buf);
+ #endif
+}
+
+int do_write(uint32_t addr, const uint8_t *src8, size_t len) {
+ #if MBOOT_ENABLE_PACKING
+ return mboot_pack_write(addr, src8, len);
+ #else
+ return hw_write(addr, src8, len);
+ #endif
+}
+
/******************************************************************************/
// I2C slave interface
@@ -1068,6 +1104,16 @@ static uint8_t *pyb_usbdd_StrDescriptor(USBD_HandleTypeDef *pdev, uint8_t idx, u
USBD_GetString((uint8_t*)MBOOT_ERROR_STR_INVALID_ADDRESS, str_desc, length);
return str_desc;
+ #if MBOOT_ENABLE_PACKING
+ case MBOOT_ERROR_STR_INVALID_SIG_IDX:
+ USBD_GetString((uint8_t*)MBOOT_ERROR_STR_INVALID_SIG, str_desc, length);
+ return str_desc;
+
+ case MBOOT_ERROR_STR_INVALID_READ_IDX:
+ USBD_GetString((uint8_t*)MBOOT_ERROR_STR_INVALID_READ, str_desc, length);
+ return str_desc;
+ #endif
+
default:
return NULL;
}
@@ -1388,6 +1434,10 @@ enter_bootloader:
mp_spiflash_init(MBOOT_SPIFLASH2_SPIFLASH);
#endif
+ #if MBOOT_ENABLE_PACKING
+ mboot_pack_init();
+ #endif
+
#if MBOOT_FSLOAD
if ((initial_r0 & 0xffffff80) == 0x70ad0080) {
// Application passed through elements, validate then process them
diff --git a/ports/stm32/mboot/mboot.h b/ports/stm32/mboot/mboot.h
index 37929665e..e4ed3cecc 100644
--- a/ports/stm32/mboot/mboot.h
+++ b/ports/stm32/mboot/mboot.h
@@ -36,6 +36,9 @@
#define ELEM_DATA_START (&_estack[0])
#define ELEM_DATA_MAX (&_estack[ELEM_DATA_SIZE])
+#define NORETURN __attribute__((noreturn))
+#define MP_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
enum {
ELEM_TYPE_END = 1,
ELEM_TYPE_MOUNT,
@@ -53,6 +56,10 @@ extern uint8_t _estack[ELEM_DATA_SIZE];
uint32_t get_le32(const uint8_t *b);
void led_state_all(unsigned int mask);
+int hw_page_erase(uint32_t addr, uint32_t *next_addr);
+void hw_read(uint32_t addr, int len, uint8_t *buf);
+int hw_write(uint32_t addr, const uint8_t *src8, size_t len);
+
int do_page_erase(uint32_t addr, uint32_t *next_addr);
void do_read(uint32_t addr, int len, uint8_t *buf);
int do_write(uint32_t addr, const uint8_t *src8, size_t len);
diff --git a/ports/stm32/mboot/mboot_pack_dfu.py b/ports/stm32/mboot/mboot_pack_dfu.py
new file mode 100644
index 000000000..47382f591
--- /dev/null
+++ b/ports/stm32/mboot/mboot_pack_dfu.py
@@ -0,0 +1,260 @@
+# This file is part of the MicroPython project, http://micropython.org/
+#
+# The MIT License (MIT)
+#
+# Copyright (c) 2020-2021 Damien P. George
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# 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 OR COPYRIGHT HOLDERS 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.
+"""
+Utility to create compressed, encrypted and signed DFU files.
+"""
+
+import argparse
+import os
+import re
+import struct
+import sys
+import zlib
+
+sys.path.append(os.path.dirname(__file__) + "/../../../tools")
+import dfu
+
+try:
+ import pyhy
+except ImportError:
+ raise SystemExit(
+ "ERROR: pyhy not found. Please install python pyhy for encrypted mboot support: pip3 install pyhy"
+ )
+
+
+# Currenty supported version of a packed DFU file.
+MBOOT_PACK_HEADER_VERSION = 1
+
+# Must match MBOOT_PACK_HYDRO_CONTEXT in mboot/pack.h
+MBOOT_PACK_HYDRO_CONTEXT = "mbootenc"
+
+# Must match enum in mboot/pack.h.
+MBOOT_PACK_CHUNK_META = 0
+MBOOT_PACK_CHUNK_FULL_SIG = 1
+MBOOT_PACK_CHUNK_FW_RAW = 2
+MBOOT_PACK_CHUNK_FW_GZIP = 3
+
+
+class Keys:
+ def __init__(self, filename):
+ self.filename = filename
+
+ def generate(self):
+ kp = pyhy.hydro_sign_keygen()
+ self.sign_sk = kp.sk
+ self.sign_pk = kp.pk
+ self.secretbox = pyhy.hydro_secretbox_keygen()
+
+ def _save_data(self, name, data, file_, hide=False):
+ prefix = "//" if hide else ""
+ data = ",".join("0x{:02x}".format(b) for b in data)
+ file_.write("{}const uint8_t {}[] = {{{}}};\n".format(prefix, name, data))
+
+ def _load_data(self, name, line):
+ line = line.split(name + "[] = ")
+ if len(line) != 2:
+ raise Exception("malformed input keys: {}".format(line))
+ data = line[1].strip()
+ return bytes(int(value, 16) for value in data[1:-2].split(","))
+
+ def save(self):
+ with open(self.filename, "w") as f:
+ self._save_data("mboot_pack_sign_secret_key", self.sign_sk, f, hide=True)
+ self._save_data("mboot_pack_sign_public_key", self.sign_pk, f)
+ self._save_data("mboot_pack_secretbox_key", self.secretbox, f)
+
+ def load(self):
+ with open(self.filename) as f:
+ self.sign_sk = self._load_data("mboot_pack_sign_secret_key", f.readline())
+ self.sign_pk = self._load_data("mboot_pack_sign_public_key", f.readline())
+ self.secretbox = self._load_data("mboot_pack_secretbox_key", f.readline())
+
+
+def dfu_read(filename):
+ elems = []
+
+ with open(filename, "rb") as f:
+ hdr = f.read(11)
+ sig, ver, size, num_targ = struct.unpack("<5sBIB", hdr)
+ file_offset = 11
+
+ for i in range(num_targ):
+ hdr = f.read(274)
+ sig, alt, has_name, name, t_size, num_elem = struct.unpack("<6sBi255sII", hdr)
+
+ file_offset += 274
+ file_offset_t = file_offset
+ for j in range(num_elem):
+ hdr = f.read(8)
+ addr, e_size = struct.unpack("<II", hdr)
+ data = f.read(e_size)
+ elems.append((addr, data))
+ file_offset += 8 + e_size
+
+ if t_size != file_offset - file_offset_t:
+ raise Exception("corrupt DFU {} {}".format(t_size, file_offset - file_offset_t))
+
+ if size != file_offset:
+ raise Exception("corrupt DFU {} {}".format(size, file_offset))
+
+ hdr = f.read(16)
+ hdr = struct.unpack("<HHHH3sBI", hdr)
+ vid_pid = "0x{:04x}:0x{:04x}".format(hdr[2], hdr[1])
+
+ return vid_pid, elems
+
+
+def compress(data):
+ c = zlib.compressobj(level=9, memLevel=9, wbits=-15) # wsize=15, no header
+ return c.compress(data) + c.flush()
+
+
+def encrypt(keys, data):
+ return pyhy.hydro_secretbox_encrypt(data, 0, MBOOT_PACK_HYDRO_CONTEXT, keys.secretbox)
+
+
+def sign(keys, data):
+ return pyhy.hydro_sign_create(data, MBOOT_PACK_HYDRO_CONTEXT, keys.sign_sk)
+
+
+def pack_chunk(keys, format_, chunk_addr, chunk_payload):
+ header = struct.pack(
+ "<BBBBII", MBOOT_PACK_HEADER_VERSION, format_, 0, 0, chunk_addr, len(chunk_payload)
+ )
+ chunk = header + chunk_payload
+ sig = sign(keys, chunk)
+ chunk = chunk + sig
+ return chunk
+
+
+def data_chunks(data, n):
+ for i in range(0, len(data), n):
+ yield data[i : i + n]
+
+
+def generate_keys(keys, args):
+ keys.generate()
+ keys.save()
+
+
+def pack_dfu(keys, args):
+ chunk_size = int(args.chunk_size[0])
+
+ # Load previously generated keys.
+ keys.load()
+
+ # Read the input DFU file.
+ vid_pid, elems = dfu_read(args.infile[0])
+
+ # Ensure firmware sections are processed in order of destination memory address.
+ elems = sorted(elems, key=lambda e: e[0])
+
+ # Build list of packed chunks.
+ target = []
+ full_fw = b""
+ full_signature_payload = b""
+ for address, fw in elems:
+ # Update full firmware and full signature chunk.
+ full_fw += fw
+ full_signature_payload += struct.pack("<II", address, len(fw))
+
+ # Split the firmware into chunks, encrypt and sign the chunks
+ # then register them as individual DFU targets.
+ for i, chunk in enumerate(data_chunks(fw, chunk_size)):
+ chunk_addr = address + i * chunk_size
+ if args.gzip:
+ chunk = compress(chunk)
+ chunk = encrypt(keys, chunk)
+ chunk = pack_chunk(
+ keys,
+ MBOOT_PACK_CHUNK_FW_GZIP if args.gzip else MBOOT_PACK_CHUNK_FW_RAW,
+ chunk_addr,
+ chunk,
+ )
+ target.append({"address": chunk_addr, "data": chunk})
+
+ # Add full signature to targets, at location following the last chunk.
+ chunk_addr += chunk_size
+ sig = sign(keys, full_fw)
+ full_signature_payload += sig
+ full_signature_chunk = pack_chunk(
+ keys, MBOOT_PACK_CHUNK_FULL_SIG, chunk_addr, full_signature_payload
+ )
+ target.append({"address": chunk_addr, "data": full_signature_chunk})
+
+ # Build the packed DFU file of all the encrypted and signed chunks.
+ dfu.build(args.outfile[0], [target], vid_pid)
+
+ # Verify the packed DFU file.
+ verify_pack_dfu(keys, args.outfile[0])
+
+
+def verify_pack_dfu(keys, filename):
+ full_sig = pyhy.hydro_sign(MBOOT_PACK_HYDRO_CONTEXT)
+ _, elems = dfu_read(filename)
+ for addr, data in elems:
+ header = struct.unpack("<BBBBII", data[:12])
+ chunk = data[12 : 12 + header[5]]
+ sig = data[12 + header[5] :]
+ sig_pass = pyhy.hydro_sign_verify(
+ sig, data[:12] + chunk, MBOOT_PACK_HYDRO_CONTEXT, keys.sign_pk
+ )
+ assert sig_pass
+ if header[1] == MBOOT_PACK_CHUNK_FULL_SIG:
+ actual_sig = chunk[-64:]
+ else:
+ chunk = pyhy.hydro_secretbox_decrypt(
+ chunk, 0, MBOOT_PACK_HYDRO_CONTEXT, keys.secretbox
+ )
+ assert chunk is not None
+ if header[1] == MBOOT_PACK_CHUNK_FW_GZIP:
+ chunk = zlib.decompress(chunk, wbits=-15)
+ full_sig.update(chunk)
+ full_sig_pass = full_sig.final_verify(actual_sig, keys.sign_pk)
+ assert full_sig_pass
+
+
+def main():
+ cmd_parser = argparse.ArgumentParser(description="Build signed/encrypted DFU files")
+ cmd_parser.add_argument("-k", "--keys", default="mboot_keys.h", help="filename for keys")
+ subparsers = cmd_parser.add_subparsers()
+
+ parser_gk = subparsers.add_parser("generate-keys", help="generate keys")
+ parser_gk.set_defaults(func=generate_keys)
+
+ parser_ed = subparsers.add_parser("pack-dfu", help="encrypt and sign a DFU file")
+ parser_ed.add_argument("-z", "--gzip", action="store_true", help="compress chunks")
+ parser_ed.add_argument("chunk_size", nargs=1, help="maximum size in bytes of each chunk")
+ parser_ed.add_argument("infile", nargs=1, help="input DFU file")
+ parser_ed.add_argument("outfile", nargs=1, help="output DFU file")
+ parser_ed.set_defaults(func=pack_dfu)
+
+ args = cmd_parser.parse_args()
+
+ keys = Keys(args.keys)
+ args.func(keys, args)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ports/stm32/mboot/pack.c b/ports/stm32/mboot/pack.c
new file mode 100644
index 000000000..63f98fed1
--- /dev/null
+++ b/ports/stm32/mboot/pack.c
@@ -0,0 +1,282 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020-2021 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 OR COPYRIGHT HOLDERS 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.
+ */
+
+#include <string.h>
+
+#include "dfu.h"
+#include "gzstream.h"
+#include "mboot.h"
+#include "pack.h"
+
+#if MBOOT_ENABLE_PACKING
+
+// Keys provided externally by the board, will be built into mboot flash.
+#include MBOOT_PACK_KEYS_FILE
+
+// Encrypted dfu files using gzip require a decompress buffer. Larger can be faster.
+// This setting is independent to the incoming encrypted/signed/compressed DFU file.
+#ifndef MBOOT_PACK_GZIP_BUFFER_SIZE
+#define MBOOT_PACK_GZIP_BUFFER_SIZE (2048)
+#endif
+
+// State to manage automatic flash erasure.
+static uint32_t erased_base_addr;
+static uint32_t erased_top_addr;
+
+// DFU chunk buffer, used to cache incoming blocks of data from USB.
+static uint32_t firmware_chunk_base_addr;
+static mboot_pack_chunk_buf_t firmware_chunk_buf;
+
+// Temporary buffer for decrypted data.
+static uint8_t decrypted_buf[MBOOT_PACK_DFU_CHUNK_BUF_SIZE] __attribute__((aligned(8)));
+
+// Temporary buffer for uncompressing.
+static uint8_t uncompressed_buf[MBOOT_PACK_GZIP_BUFFER_SIZE] __attribute__((aligned(8)));
+
+// Buffer to hold the start of the firmware, which is only written once the
+// entire firmware is validated. This is 8 bytes due to STM32WB MCUs requiring
+// that a double-word write to flash can only be done once (due to ECC).
+static uint8_t firmware_head[8];
+
+void mboot_pack_init(void) {
+ erased_base_addr = 0;
+ erased_top_addr = 0;
+ firmware_chunk_base_addr = 0;
+}
+
+// In encrypted mode the erase is automatically managed.
+// Note: this scheme requires blocks be written in sequence, which is the case.
+static int mboot_pack_erase(uint32_t addr, size_t len) {
+ while (!(erased_base_addr <= addr && addr + len <= erased_top_addr)) {
+ uint32_t erase;
+ if (erased_base_addr <= addr && addr < erased_top_addr) {
+ erase = erased_top_addr;
+ } else {
+ erase = addr;
+ erased_base_addr = addr;
+ }
+ uint32_t next_addr;
+ int ret = hw_page_erase(erase, &next_addr);
+ if (ret != 0) {
+ return ret;
+ }
+ erased_top_addr = next_addr;
+ }
+ return 0;
+}
+
+// Commit an unencrypted and uncompressed chunk of firmware to the flash.
+static int mboot_pack_commit_chunk(uint32_t addr, uint8_t *data, size_t len) {
+ // Erase any required sectors before writing.
+ int ret = mboot_pack_erase(addr, len);
+ if (ret != 0) {
+ return ret;
+ }
+
+ if (addr == APPLICATION_ADDR) {
+ // Don't write the very start of the firmware, just copy it into a temporary buffer.
+ // It will be written only if the full firmware passes the checksum/signature.
+ memcpy(firmware_head, data, sizeof(firmware_head));
+ addr += sizeof(firmware_head);
+ data += sizeof(firmware_head);
+ len -= sizeof(firmware_head);
+ }
+
+ // Commit this piece of the firmware.
+ return hw_write(addr, data, len);
+}
+
+// Handle a chunk with the full firmware signature.
+static int mboot_pack_handle_full_sig(void) {
+ if (firmware_chunk_buf.header.length < hydro_sign_BYTES) {
+ return -1;
+ }
+
+ uint8_t *full_sig = &firmware_chunk_buf.data[firmware_chunk_buf.header.length - hydro_sign_BYTES];
+ uint32_t *region_data = (uint32_t *)&firmware_chunk_buf.data[0];
+ size_t num_regions = (full_sig - (uint8_t *)region_data) / sizeof(uint32_t) / 2;
+
+ uint8_t *buf = decrypted_buf;
+ const size_t buf_alloc = sizeof(decrypted_buf);
+
+ // Compute the signature of the full firmware.
+ hydro_sign_state sign_state;
+ hydro_sign_init(&sign_state, MBOOT_PACK_HYDRO_CONTEXT);
+ for (size_t region = 0; region < num_regions; ++region) {
+ uint32_t addr = region_data[2 * region];
+ uint32_t len = region_data[2 * region + 1];
+ while (len) {
+ uint32_t l = len <= buf_alloc ? len : buf_alloc;
+ hw_read(addr, l, buf);
+ if (addr == APPLICATION_ADDR) {
+ // The start of the firmware was not yet written to flash so copy
+ // it out of the temporary buffer to compute the full signature.
+ memcpy(buf, firmware_head, sizeof(firmware_head));
+ }
+ int ret = hydro_sign_update(&sign_state, buf, l);
+ if (ret != 0) {
+ return -1;
+ }
+ addr += l;
+ len -= l;
+ }
+ }
+
+ // Verify the signature of the full firmware.
+ int ret = hydro_sign_final_verify(&sign_state, full_sig, mboot_pack_sign_public_key);
+ if (ret != 0) {
+ dfu_context.status = DFU_STATUS_ERROR_VERIFY;
+ dfu_context.error = MBOOT_ERROR_STR_INVALID_SIG_IDX;
+ return -1;
+ }
+
+ // Full firmware passed the signature check.
+ // Write the start of the firmware so it boots.
+ return hw_write(APPLICATION_ADDR, firmware_head, sizeof(firmware_head));
+}
+
+// Handle a chunk with firmware data.
+static int mboot_pack_handle_firmware(void) {
+ const uint8_t *fw_data = &firmware_chunk_buf.data[0];
+ const size_t fw_len = firmware_chunk_buf.header.length;
+
+ // Decrypt the chunk.
+ if (hydro_secretbox_decrypt(decrypted_buf, fw_data, fw_len, 0, MBOOT_PACK_HYDRO_CONTEXT, mboot_pack_secretbox_key) != 0) {
+ dfu_context.status = DFU_STATUS_ERROR_VERIFY;
+ dfu_context.error = MBOOT_ERROR_STR_INVALID_SIG_IDX;
+ return -1;
+ }
+
+ // Use the decrypted message contents going formward.
+ size_t len = fw_len - hydro_secretbox_HEADERBYTES;
+ uint32_t addr = firmware_chunk_buf.header.address;
+
+ if (firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_FW_GZIP) {
+ // Decompress chunk data.
+ gz_stream_init_from_raw_data(decrypted_buf, len);
+ for (;;) {
+ int read = gz_stream_read(sizeof(uncompressed_buf), uncompressed_buf);
+ if (read == 0) {
+ return 0; // finished decompressing
+ } else if (read < 0) {
+ return -1; // error reading
+ }
+ int ret = mboot_pack_commit_chunk(addr, uncompressed_buf, read);
+ if (ret != 0) {
+ return ret;
+ }
+ addr += read;
+ }
+ } else {
+ // Commit chunk data directly.
+ return mboot_pack_commit_chunk(addr, decrypted_buf, len);
+ }
+}
+
+int mboot_pack_write(uint32_t addr, const uint8_t *src8, size_t len) {
+ if (addr == APPLICATION_ADDR) {
+ // Base address of main firmware, reset any previous state
+ firmware_chunk_base_addr = 0;
+ }
+
+ if (firmware_chunk_base_addr == 0) {
+ // First piece of data starting a new chunk, so set the base address.
+ firmware_chunk_base_addr = addr;
+ }
+
+ if (addr < firmware_chunk_base_addr) {
+ // Address out of range.
+ firmware_chunk_base_addr = 0;
+ return -1;
+ }
+
+ size_t offset = addr - firmware_chunk_base_addr;
+ if (offset + len > sizeof(firmware_chunk_buf)) {
+ // Address/length out of range.
+ firmware_chunk_base_addr = 0;
+ return -1;
+ }
+
+ // Copy in the new data piece into the chunk buffer.
+ memcpy((uint8_t *)&firmware_chunk_buf + offset, src8, len);
+
+ if (offset + len < sizeof(firmware_chunk_buf.header)) {
+ // Don't have the header yet.
+ return 0;
+ }
+
+ if (firmware_chunk_buf.header.header_vers != MBOOT_PACK_HEADER_VERSION) {
+ // Chunk header has the wrong version.
+ dfu_context.status = DFU_STATUS_ERROR_FILE;
+ dfu_context.error = MBOOT_ERROR_STR_INVALID_SIG_IDX;
+ return -1;
+ }
+
+ if (firmware_chunk_buf.header.address != firmware_chunk_base_addr) {
+ // Chunk address doesn't agree with dfu address, abort.
+ dfu_context.status = DFU_STATUS_ERROR_ADDRESS;
+ dfu_context.error = MBOOT_ERROR_STR_INVALID_SIG_IDX;
+ return -1;
+ }
+
+ if (offset + len < sizeof(firmware_chunk_buf.header) + firmware_chunk_buf.header.length + sizeof(firmware_chunk_buf.signature)) {
+ // Don't have the full chunk yet.
+ return 0;
+ }
+
+ // Have the full chunk in firmware_chunk_buf, process it now.
+
+ // Reset the chunk base address for the next chunk that comes in.
+ firmware_chunk_base_addr = 0;
+
+ // Verify the signature of the chunk.
+ const size_t fw_len = firmware_chunk_buf.header.length;
+ const uint8_t *sig = &firmware_chunk_buf.data[0] + fw_len;
+ if (hydro_sign_verify(sig, &firmware_chunk_buf, sizeof(firmware_chunk_buf.header) + fw_len,
+ MBOOT_PACK_HYDRO_CONTEXT, mboot_pack_sign_public_key) != 0) {
+ // Signature failed
+ dfu_context.status = DFU_STATUS_ERROR_VERIFY;
+ dfu_context.error = MBOOT_ERROR_STR_INVALID_SIG_IDX;
+ return -1;
+ }
+
+ // Signature passed, we have valid chunk.
+
+ if (firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_META) {
+ // Ignore META chunks.
+ return 0;
+ } else if (firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_FULL_SIG) {
+ return mboot_pack_handle_full_sig();
+ } else if (firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_FW_RAW
+ || firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_FW_GZIP) {
+ return mboot_pack_handle_firmware();
+ } else {
+ // Unsupported contents.
+ return -1;
+ }
+}
+
+#endif // MBOOT_ENABLE_PACKING
diff --git a/ports/stm32/mboot/pack.h b/ports/stm32/mboot/pack.h
new file mode 100644
index 000000000..195f297ca
--- /dev/null
+++ b/ports/stm32/mboot/pack.h
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017-2019 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 OR COPYRIGHT HOLDERS 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.
+ */
+#ifndef MICROPY_INCLUDED_STM32_MBOOT_PACK_H
+#define MICROPY_INCLUDED_STM32_MBOOT_PACK_H
+
+#include <stdint.h>
+#include "py/mphal.h"
+
+#if MBOOT_ENABLE_PACKING
+
+#include "lib/libhydrogen/hydrogen.h"
+
+// Encrypted & signed bootloader support
+
+/******************************************************************************/
+// Interface
+
+#define MBOOT_PACK_HEADER_VERSION (1)
+
+// Used by libhydrogen for signing and secretbox context.
+#define MBOOT_PACK_HYDRO_CONTEXT "mbootenc"
+
+// Maximum size of the firmware payload.
+#define MBOOT_PACK_DFU_CHUNK_BUF_SIZE (MBOOT_PACK_CHUNKSIZE + hydro_secretbox_HEADERBYTES)
+
+enum mboot_pack_chunk_format {
+ MBOOT_PACK_CHUNK_META = 0,
+ MBOOT_PACK_CHUNK_FULL_SIG = 1,
+ MBOOT_PACK_CHUNK_FW_RAW = 2,
+ MBOOT_PACK_CHUNK_FW_GZIP = 3,
+};
+
+// Each DFU chunk transfered has this header to validate it.
+
+typedef struct _mboot_pack_chunk_buf_t {
+ struct {
+ uint8_t header_vers;
+ uint8_t format; // enum mboot_pack_chunk_format
+ uint8_t _pad[2];
+ uint32_t address;
+ uint32_t length; // number of bytes in following "data" payload, excluding "signature"
+ } header;
+ uint8_t data[MBOOT_PACK_DFU_CHUNK_BUF_SIZE];
+ uint8_t signature[hydro_sign_BYTES];
+} mboot_pack_chunk_buf_t;
+
+// Signing and encryption keys, stored in mboot flash, provided externally.
+extern const uint8_t mboot_pack_sign_public_key[hydro_sign_PUBLICKEYBYTES];
+extern const uint8_t mboot_pack_secretbox_key[hydro_secretbox_KEYBYTES];
+
+/******************************************************************************/
+// Implementation
+
+void mboot_pack_init(void);
+int mboot_pack_write(uint32_t addr, const uint8_t *src8, size_t len);
+
+#endif // MBOOT_ENABLE_PACKING
+
+#endif // MICROPY_INCLUDED_STM32_MBOOT_PACK_H
diff --git a/ports/stm32/mboot/stm32_generic.ld b/ports/stm32/mboot/stm32_generic.ld
index 0504d6d6a..ade434967 100644
--- a/ports/stm32/mboot/stm32_generic.ld
+++ b/ports/stm32/mboot/stm32_generic.ld
@@ -6,11 +6,11 @@
MEMORY
{
FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 32K /* sector 0 (can be 32K) */
- RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K
+ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 120K
}
/* produce a link error if there is not this amount of RAM for these sections */
-_minimum_stack_size = 2K;
+_minimum_stack_size = 8K;
/* Define tho top end of the stack. The stack is full descending so begins just
above last byte of RAM. Note that EABI requires the stack to be 8-byte
diff --git a/ports/stm32/mboot/vfs_fat.c b/ports/stm32/mboot/vfs_fat.c
index 20de074f0..5120bdb10 100644
--- a/ports/stm32/mboot/vfs_fat.c
+++ b/ports/stm32/mboot/vfs_fat.c
@@ -42,7 +42,7 @@ DRESULT disk_read(void *pdrv, BYTE *buf, DWORD sector, UINT count) {
vfs_fat_context_t *ctx = pdrv;
if (0 <= sector && sector < ctx->bdev_byte_len / 512) {
- do_read(ctx->bdev_base_addr + sector * SECSIZE, count * SECSIZE, buf);
+ hw_read(ctx->bdev_base_addr + sector * SECSIZE, count * SECSIZE, buf);
return RES_OK;
}
diff --git a/ports/stm32/mboot/vfs_lfs.c b/ports/stm32/mboot/vfs_lfs.c
index dec7c015f..e4dc511db 100644
--- a/ports/stm32/mboot/vfs_lfs.c
+++ b/ports/stm32/mboot/vfs_lfs.c
@@ -72,7 +72,7 @@ static uint8_t lfs_lookahead_buffer[LFS_LOOKAHEAD_SIZE];
static int dev_read(const struct LFSx_API (config) * c, LFSx_API(block_t) block, LFSx_API(off_t) off, void *buffer, LFSx_API(size_t) size) {
VFS_LFSx_CONTEXT_T *ctx = c->context;
if (0 <= block && block < ctx->config.block_count) {
- do_read(ctx->bdev_base_addr + block * ctx->config.block_size + off, size, buffer);
+ hw_read(ctx->bdev_base_addr + block * ctx->config.block_size + off, size, buffer);
return LFSx_MACRO(_ERR_OK);
}
return LFSx_MACRO(_ERR_IO);
@@ -93,7 +93,7 @@ static int dev_sync(const struct LFSx_API (config) * c) {
int VFS_LFSx_MOUNT(VFS_LFSx_CONTEXT_T *ctx, uint32_t base_addr, uint32_t byte_len) {
// Read start of superblock.
uint8_t buf[48];
- do_read(base_addr, sizeof(buf), buf);
+ hw_read(base_addr, sizeof(buf), buf);
// Verify littlefs and detect block size.
if (memcmp(&buf[SUPERBLOCK_MAGIC_OFFSET], "littlefs", 8) != 0) {
diff --git a/ports/stm32/mpconfigport.mk b/ports/stm32/mpconfigport.mk
index c6b3ddc74..98ae93d20 100644
--- a/ports/stm32/mpconfigport.mk
+++ b/ports/stm32/mpconfigport.mk
@@ -1,4 +1,4 @@
-# Enable/disable extra modules
+# Enable/disable extra modules and features
# wiznet5k module for ethernet support; valid values are:
# 0 : no Wiznet support
@@ -11,3 +11,8 @@ MICROPY_PY_CC3K ?= 0
# VFS FAT FS support
MICROPY_VFS_FAT ?= 1
+
+# Encrypted/signed bootloader support (ensure the MBOOT_PACK_xxx values match stm32/mboot/Makefile)
+MBOOT_ENABLE_PACKING ?= 0
+MBOOT_PACK_CHUNKSIZE ?= 16384
+MBOOT_PACK_KEYS_FILE ?= $(BOARD_DIR)/mboot_keys.h