first commit
This commit is contained in:
parent
ed6f7d422a
commit
3a773d13c2
11
stm32/mk4-bootloader/.gitignore
vendored
Normal file
11
stm32/mk4-bootloader/.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# build products, see Makefile:
|
||||
bootloader.lss
|
||||
bootloader.sym
|
||||
bootloader.dfu
|
||||
bootloader.bin.tmp
|
||||
checksums.txt
|
||||
version-full.txt
|
||||
version.txt
|
||||
|
||||
# dev shortcut
|
||||
hal
|
||||
222
stm32/mk4-bootloader/Makefile
Normal file
222
stm32/mk4-bootloader/Makefile
Normal file
@ -0,0 +1,222 @@
|
||||
# (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
# "Bootloader" Makefile for Mk4 Hardware
|
||||
#
|
||||
# Targets:
|
||||
# all - make everything, look for dafu.elf inparticular
|
||||
# clean - delete intermediates
|
||||
# clobber - delete all build products
|
||||
#
|
||||
|
||||
# Toolchain
|
||||
TOOLCHAIN = arm-none-eabi-
|
||||
CC = $(TOOLCHAIN)gcc
|
||||
OBJDUMP = $(TOOLCHAIN)objdump
|
||||
OBJCOPY = $(TOOLCHAIN)objcopy
|
||||
NM = $(TOOLCHAIN)nm
|
||||
SIZE = $(TOOLCHAIN)size
|
||||
|
||||
MPY_TOP = ../../external/micropython
|
||||
PYTHON_MAKE_DFU = $(MPY_TOP)/tools/dfu.py
|
||||
PYTHON_DO_DFU = $(MPY_TOP)/tools/pydfu.py
|
||||
|
||||
# Basename of all targets
|
||||
TARGET_NAME = bootloader
|
||||
|
||||
# Source files. Important: Add them also to link-script.ld to control placement.
|
||||
OBJS += startup.o assets/screens.o
|
||||
OBJS += enable.o dispatch.o verify.o oled.o clocks.o storage.o constant_time.o rng.o ae.o
|
||||
OBJS += delay.o gpio.o pins.o version.o sflash.o console.o psram.o
|
||||
OBJS += stm32l4xx_hal_firewall.o stm32l4xx_hal_gpio.o stm32l4xx_hal_spi.o
|
||||
OBJS += stm32l4xx_hal_rcc.o stm32l4xx_hal_rcc_ex.o
|
||||
OBJS += faster_sha256.o micro-ecc/uECC.o
|
||||
OBJS += stm32l4xx_hal_hash.o stm32l4xx_hal_hash_ex.o stm32l4xx_hal_usart.o
|
||||
OBJS += stm32l4xx_hal_qspi.o
|
||||
|
||||
#OBJS = $(addsuffix .o, $(basename $(C_SRCS) $(ASM_SRCS)))
|
||||
|
||||
# Turn off some suprious warnings
|
||||
micro-ecc/uECC.o: c_flags += -Wno-undef -Wno-redundant-decls
|
||||
|
||||
# Headers for chip stuff (assumes STM32L476 chip)
|
||||
STM32LIB_PATH = ../../external/micropython/lib
|
||||
|
||||
# Where we will end up in the memory map (at start of flash)
|
||||
# - reserve last 8k flash page for other purposes
|
||||
BL_FLASH_BASE = 0x08000000
|
||||
BL_FLASH_SIZE = 0xe000
|
||||
BL_FLASH_LAST = 0x0800e000
|
||||
|
||||
# Final 8k bytes of flash reserved for secret data (not code)
|
||||
# - must be page-aligned, contains pairing secret
|
||||
BL_NVROM_BASE = 0x0800e000 # = BL_FLASH_LAST
|
||||
BL_NVROM_SIZE = 0x2000 # = 8k
|
||||
|
||||
# Bottom 8k of SRAM1 is reserved for us.
|
||||
# We wipe whole thing before and after using it.
|
||||
# - highest 1k page set for write-protect (but not secret)
|
||||
BL_SRAM_BASE = 0x20000000
|
||||
BL_SRAM_SIZE = 0x00001c00
|
||||
|
||||
# Compiler flags.
|
||||
CFLAGS = -I. -Wall --std=gnu99 -Os -g3 \
|
||||
-mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mtune=cortex-m4 \
|
||||
-ffunction-sections -fdata-sections \
|
||||
-mcpu=cortex-m4 -DMCU_SERIES_L4 -DSTM32L4A6xx
|
||||
|
||||
# -flto -fdata-sections -ffunction-sections -funsigned-char -funsigned-bitfields
|
||||
|
||||
# Pass in the locations of stuff
|
||||
CFLAGS += -D BL_FLASH_BASE=$(BL_FLASH_BASE) -D BL_FLASH_SIZE=$(BL_FLASH_SIZE)
|
||||
CFLAGS += -D BL_NVROM_BASE=$(BL_NVROM_BASE) -D BL_NVROM_SIZE=$(BL_NVROM_SIZE)
|
||||
CFLAGS += -D BL_SRAM_BASE=$(BL_SRAM_BASE) -D BL_SRAM_SIZE=$(BL_SRAM_SIZE)
|
||||
|
||||
# Header file search path
|
||||
INC_PATHS = $(STM32LIB_PATH)/stm32lib/CMSIS/STM32L4xx/Include \
|
||||
$(STM32LIB_PATH)/stm32lib/STM32L4xx_HAL_Driver/Inc \
|
||||
$(STM32LIB_PATH)/cmsis/inc
|
||||
|
||||
CFLAGS += $(foreach INC,$(INC_PATHS),-I$(INC))
|
||||
|
||||
# Specialized linker-script here. Not the standard one!
|
||||
#
|
||||
LINKER_SCRIPT = link-script.ld
|
||||
|
||||
LDFLAGS += -flto -Wl,--gc-sections --specs=nano.specs -Wl,-T$(LINKER_SCRIPT)
|
||||
LDFLAGS += -nostartfiles
|
||||
LDFLAGS += -Wl,--defsym,BL_FLASH_BASE=$(BL_FLASH_BASE)
|
||||
LDFLAGS += -Wl,--defsym,BL_FLASH_SIZE=$(BL_FLASH_SIZE)
|
||||
LDFLAGS += -Wl,--defsym,BL_NVROM_BASE=$(BL_NVROM_BASE)
|
||||
LDFLAGS += -Wl,--defsym,BL_NVROM_SIZE=$(BL_NVROM_SIZE)
|
||||
LDFLAGS += -Wl,--defsym,BL_SRAM_BASE=$(BL_SRAM_BASE)
|
||||
LDFLAGS += -Wl,--defsym,BL_SRAM_SIZE=$(BL_SRAM_SIZE)
|
||||
LDFLAGS += -Wl,-Map=$(TARGET_NAME).map
|
||||
|
||||
ASFLAGS += -Wa,--defsym,BL_FLASH_BASE=$(BL_FLASH_BASE) -Wa,--defsym,BL_FLASH_SIZE=$(BL_FLASH_SIZE)
|
||||
ASFLAGS += -Wa,--defsym,BL_SRAM_BASE=$(BL_SRAM_BASE) -Wa,--defsym,BL_SRAM_SIZE=$(BL_SRAM_SIZE)
|
||||
|
||||
|
||||
TARGET_ELF = $(TARGET_NAME).elf
|
||||
TARGETS = $(TARGET_NAME).lss $(TARGET_NAME).bin $(TARGET_NAME).sym $(TARGET_NAME).dfu
|
||||
TARGETS += sigheader.py
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
# recompile on any change, because with a small project like this...
|
||||
$(OBJS): Makefile
|
||||
|
||||
$(TARGETS): $(TARGET_ELF) Makefile
|
||||
|
||||
sigheader.py: sigheader.h mk-sigheader.py
|
||||
python3 mk-sigheader.py
|
||||
|
||||
# link step
|
||||
$(TARGET_ELF): $(OBJS) $(LINKER_SCRIPT) Makefile
|
||||
$(CC) $(CFLAGS) -o $(TARGET_ELF) $(LDFLAGS) $(OBJS)
|
||||
$(SIZE) -Ax $@
|
||||
|
||||
# detailed listing, very handy
|
||||
%.lss: $(TARGET_ELF)
|
||||
$(OBJDUMP) -h -S $< > $@
|
||||
|
||||
# symbol dump, meh
|
||||
%.sym: $(TARGET_ELF)
|
||||
$(NM) -n $< > $@
|
||||
|
||||
# raw binary, forced to right size, pad w/ 0xff
|
||||
%.bin: $(TARGET_ELF)
|
||||
$(OBJCOPY) -O binary --pad-to $(BL_FLASH_LAST) --gap-fill 0xff $< $@.tmp
|
||||
dd bs=$(shell printf "%d" $(BL_FLASH_SIZE)) count=1 if=$@.tmp of=$@
|
||||
|
||||
# dfu packaged file
|
||||
%.dfu: %.bin %.lss
|
||||
$(PYTHON_MAKE_DFU) -b $(BL_FLASH_BASE):$< $@
|
||||
|
||||
assets/screens.c: assets/Makefile assets/*.png assets/convert.py
|
||||
(cd assets; $(MAKE))
|
||||
|
||||
|
||||
# some hard to generate bits...
|
||||
ae.o: ae_config.h
|
||||
|
||||
ae_config.h: keylayout.py secel_config.py
|
||||
python3 ./keylayout.py
|
||||
|
||||
dfu-slow: $(TARGET_NAME).dfu
|
||||
dfu-util -d 0483:df11 -a 0 -D $<
|
||||
|
||||
dfu: $(TARGET_NAME).dfu
|
||||
$(PYTHON_DO_DFU) -u $<
|
||||
|
||||
# upload current production version (or latest release version anyway)
|
||||
latest:
|
||||
$(PYTHON_DO_DFU) -u `ls -t1 releases/*/bootloader.dfu | head -1`
|
||||
|
||||
# make a 'release' build
|
||||
release: code-committed check-fontawesome clean all capture
|
||||
release: CFLAGS += -DRELEASE=1 -Werror
|
||||
|
||||
check-fontawesome:
|
||||
# You must have commerical license for Font Awesome (altho fallback looks ok)
|
||||
test -f assets/FontAwesome5Pro-Light-300.otf
|
||||
|
||||
.PHONY: code-committed
|
||||
code-committed:
|
||||
@echo ""
|
||||
@echo "Are all changes commited already?"
|
||||
git diff --stat --exit-code .
|
||||
@echo '... yes'
|
||||
|
||||
# these files are what we capture and store for each release.
|
||||
DELIVERABLES = $(TARGET_NAME).dfu $(TARGET_NAME).bin $(TARGET_NAME).lss
|
||||
|
||||
checksums.txt: $(DELIVERABLES)
|
||||
shasum -a 256 $(DELIVERABLES) > $@
|
||||
|
||||
# Track released versions
|
||||
.PHONY: capture
|
||||
capture: version.txt version-full.txt $(DELIVERABLES) checksums.txt
|
||||
V=`cat version.txt` && cat checksums.txt > releases/$$V.txt && cat version-full.txt >> releases/$$V.txt && mkdir -p releases/$$V; cp $(DELIVERABLES) releases/$$V
|
||||
@echo
|
||||
@echo " Version: " `cat version.txt`
|
||||
@echo
|
||||
V=`cat version.txt` && git tag -am "Bootloader version $$V" "bootloader-"$$V
|
||||
git add -f releases/*/bootloader.* releases/*.txt
|
||||
|
||||
# Pull out the version string from binary object (already linked in) and
|
||||
# construct a text file (version.txt) with those contents
|
||||
version.txt version-full.txt: version.o Makefile
|
||||
$(OBJCOPY) -O binary -j .rodata.version_string version.o version-tmp.txt
|
||||
cat version-tmp.txt | sed -e 's/ .*//' | sed -e 's/ .*//' > version.txt
|
||||
cat version-tmp.txt | tr '\0' '\n' > version-full.txt
|
||||
@echo
|
||||
@echo "Version string: " `cat version-full.txt`
|
||||
@echo
|
||||
$(RM) version-tmp.txt
|
||||
|
||||
# nice version numbers.
|
||||
BUILD_TIME = $(shell date '+%Y%m%d.%H%M%S')
|
||||
BRANCH = $(shell git rev-parse --abbrev-ref HEAD)
|
||||
SHA_VERSION = $(shell git rev-parse --short HEAD)
|
||||
GIT_HASH = "$(BRANCH)@$(SHA_VERSION)"
|
||||
version.o: CFLAGS += -DBUILD_TIME='"$(BUILD_TIME)"' -DGIT_HASH='$(GIT_HASH)'
|
||||
version.o: Makefile
|
||||
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJS)
|
||||
|
||||
clobber: clean
|
||||
$(RM) $(TARGETS)
|
||||
|
||||
debug:
|
||||
arm-none-eabi-gdb bootloader.elf -x gogo.gdb
|
||||
|
||||
xxx:
|
||||
@echo CFLAGS = $(CFLAGS)
|
||||
@echo
|
||||
@echo OBJS = $(OBJS)
|
||||
|
||||
tags:
|
||||
ctags -f .tags *.[ch] -R $(INC_PATHS) $(STM32LIB_PATH)/stm32lib/STM32L4xx_HAL_Driver/*/*.h
|
||||
|
||||
2070
stm32/mk4-bootloader/ae.c
Normal file
2070
stm32/mk4-bootloader/ae.c
Normal file
File diff suppressed because it is too large
Load Diff
188
stm32/mk4-bootloader/ae.h
Normal file
188
stm32/mk4-bootloader/ae.h
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
//
|
||||
// Atmel ATECC508A and 608A related code. Trying to keep this able to handle both devices.
|
||||
//
|
||||
|
||||
//#define FOR_508 1
|
||||
#define FOR_608 1
|
||||
|
||||
// Opcodes from table 9-4, page 51
|
||||
//
|
||||
typedef enum {
|
||||
OP_CheckMac = 0x28, OP_Counter = 0x24, OP_DeriveKey = 0x1C, OP_ECDH = 0x43,
|
||||
OP_GenDig = 0x15, OP_GenKey = 0x40, OP_Info = 0x30,
|
||||
OP_Lock = 0x17, OP_MAC = 0x08, OP_Nonce = 0x16,
|
||||
OP_PrivWrite = 0x46, OP_Random = 0x1B, OP_Read = 0x02, OP_Sign = 0x41,
|
||||
OP_SHA = 0x47, OP_UpdateExtra = 0x20, OP_Verify = 0x45, OP_Write = 0x12,
|
||||
#if FOR_508
|
||||
OP_HMAC = 0x11,
|
||||
OP_Pause = 0x01,
|
||||
#elif FOR_608
|
||||
OP_AES = 0x51,
|
||||
OP_KDF = 0x56,
|
||||
OP_SecureBoot = 0x80,
|
||||
OP_SelftTest = 0x77,
|
||||
#endif
|
||||
} aeopcode_t;
|
||||
|
||||
// Status/Error Codes that occur in 4-byte groups. See page 50, table 9-3.
|
||||
#define AE_COMMAND_OK 0x00
|
||||
#define AE_CHECKMAC_FAIL 0x01
|
||||
#define AE_PARSE_ERROR 0x03
|
||||
#define AE_ECC_FAULT 0x05
|
||||
#define AE_SELFTEST_ERROR 0x07
|
||||
#define AE_EXEC_ERROR 0x0f
|
||||
#define AE_AFTER_WAKE 0x11
|
||||
#define AE_WATCHDOG_EXPIRE 0xEE
|
||||
#define AE_COMM_ERROR 0xFF
|
||||
|
||||
// Basic pin/uart setup
|
||||
void ae_setup(void);
|
||||
|
||||
// Call this lots! It's quick and clears volatile state on device.
|
||||
void ae_reset_chip(void);
|
||||
|
||||
// Test if chip responds correctly, and do some setup; returns error string if fail.
|
||||
const char *ae_probe(void);
|
||||
|
||||
// Use the chip as SHA256 "accelerator"
|
||||
int ae_sha256(const uint8_t *msg, int msg_len, uint8_t digest[32]);
|
||||
|
||||
// Read a one-byte response (ie. status code, see below)
|
||||
int ae_read1(void);
|
||||
|
||||
// Read and check CRC over N bytes, wrapped in 3-bytes of framing overhead.
|
||||
// Fails with non-zero if unable to read after 3 tries. Not clever about variable length.
|
||||
int ae_read_n(uint8_t len, uint8_t *buf);
|
||||
|
||||
// Write and lock a "slot". length must be 32, regardless of actual slot size
|
||||
int ae_write_data_slot(int slot_num, const uint8_t *data, int len, bool lock_it);
|
||||
|
||||
// Read first 32 bytes out of a slot. len must be 4 or 32.
|
||||
int ae_read_data_slot(int slot_num, uint8_t *data, int len);
|
||||
|
||||
// Read and write to slots that are encrypted (must know that before using)
|
||||
// - can specific different lenghts
|
||||
int ae_encrypted_read(int data_slot, int read_kn, const uint8_t read_key[32], uint8_t *data, int len);
|
||||
int ae_encrypted_write(int data_slot, int write_kn, const uint8_t write_key[32], const uint8_t *data, int len);
|
||||
|
||||
// read/write exactly 32 bytes
|
||||
int ae_encrypted_read32(int data_slot, int blk, int read_kn,
|
||||
const uint8_t read_key[32], uint8_t data[32]);
|
||||
int ae_encrypted_write32(int data_slot, int blk, int write_kn,
|
||||
const uint8_t write_key[32], const uint8_t data[32]);
|
||||
|
||||
// Use the pairing secret to validate ourselves to AE chip.
|
||||
int ae_pair_unlock(void);
|
||||
|
||||
// Do a CheckMac operation. Caution: don't rely on return value, it can be faked.
|
||||
int ae_checkmac(uint8_t keynum, const uint8_t secret[32]);
|
||||
|
||||
// Verify the chip and I know the same value for a keynum. Cannot be faked by MitM.
|
||||
int ae_checkmac_hard(uint8_t keynum, const uint8_t secret[32]);
|
||||
|
||||
// Send a one-byte command, maybe with args.
|
||||
void ae_send(aeopcode_t opcode, uint8_t p1, uint16_t p2);
|
||||
// .. same but with body data as well.
|
||||
void ae_send_n(aeopcode_t opcode, uint8_t p1, uint16_t p2, const uint8_t *data, uint8_t data_len);
|
||||
|
||||
// Return the waiting time (max) for specific opcode.
|
||||
int ae_delay_time(aeopcode_t opcode);
|
||||
|
||||
// Refresh the chip's watchdog timer.
|
||||
void ae_keep_alive(void);
|
||||
|
||||
// Pick a fresh random number.
|
||||
int ae_random(uint8_t randout[32]);
|
||||
|
||||
// Roll (derive) a key using random number we forget. One way!
|
||||
int ae_destroy_key(int keynum);
|
||||
|
||||
// Ask the chip to make a digest of a counter's value or data slot's contents.
|
||||
int ae_gendig_counter(int counter_num, const uint32_t expected_value, uint8_t digest[32]);
|
||||
int ae_gendig_slot(int slot_num, const uint8_t slot_contents[32], uint8_t digest[32]);
|
||||
|
||||
// Verify the chip has arrived at the same digest we have.
|
||||
bool ae_is_correct_tempkey(const uint8_t expected_tempkey[32]);
|
||||
|
||||
// Do Info(p1=2) command, and return result; p1=3 if get_gpio
|
||||
uint16_t ae_get_info(void);
|
||||
|
||||
// Bits in Info(p1=2) response
|
||||
#define I_TempKey_KeyId(n) ((n >> 8) & 0x0f)
|
||||
#define I_TempKey_SourceFlag(n) ((n >> 12) & 0x1)
|
||||
#define I_TempKey_GenDigData(n) ((n >> 13) & 0x1)
|
||||
#define I_TempKey_GenKeyData(n) ((n >> 14) & 0x1)
|
||||
#define I_TempKey_NoMacFlag(n) ((n >> 15) & 0x1)
|
||||
|
||||
#define I_EEPROM_RNG(n) ((n >> 0) & 0x1)
|
||||
#define I_SRAM_RNG(n) ((n >> 1) & 0x1)
|
||||
#define I_AuthValid(n) ((n >> 2) & 0x1)
|
||||
#define I_AuthKey(n) ((n >> 3) & 0x0f)
|
||||
#define I_TempKey_Valid(n) ((n >> 7) & 0x1)
|
||||
|
||||
// Do a dance that unlocks various keys. Return T if it fails.
|
||||
int ae_unlock_ip(uint8_t keynum, const uint8_t secret[32]);
|
||||
|
||||
// Load Tempkey with a Nonce value that we both know, but
|
||||
// is random and we both know is random too!
|
||||
int ae_pick_nonce(const uint8_t num_in[20], uint8_t tempkey[32]);
|
||||
|
||||
// Read a one-way counter (there are 2 of these)
|
||||
int ae_get_counter(uint32_t *result, uint8_t counter_number);
|
||||
|
||||
// Add onto a counter. Slow; has to go by one.
|
||||
int ae_add_counter(uint32_t *result, uint8_t counter_number, int incr);
|
||||
|
||||
// Perform HMAC on the chip, using a particular key.
|
||||
//int ae_hmac(uint8_t keynum, const uint8_t *msg, uint16_t msg_len, uint8_t digest[32]);
|
||||
int ae_hmac32(uint8_t keynum, const uint8_t *msg, uint8_t digest[32]);
|
||||
|
||||
// Read config area (not confidential)
|
||||
int ae_config_read(uint8_t config[128]);
|
||||
|
||||
// Load TempKey with indicated value, exactly.
|
||||
int ae_load_nonce(const uint8_t nonce[32]);
|
||||
|
||||
// Return the serial number.
|
||||
// Nine bytes of serial number. First 2 bytes always 0x0123 and last one 0xEE
|
||||
int ae_get_serial(uint8_t serial[6]);
|
||||
|
||||
// Control the LED. Might require authenticaiton first.
|
||||
int ae_set_gpio(int state);
|
||||
|
||||
// Set the GPIO using secure hash generated somehow already.
|
||||
int ae_set_gpio_secure(uint8_t digest[32]);
|
||||
|
||||
// Return current state of GPIO pin
|
||||
uint8_t ae_get_gpio(void);
|
||||
|
||||
// One-time config and lockdown of the chip. Never call unless you just
|
||||
// picked the original pairing secret.
|
||||
int ae_setup_config(void);
|
||||
|
||||
// Read a byte from config area, or -1 if fail.
|
||||
int ae_read_config_byte(int offset);
|
||||
|
||||
// Read a 4-byte area from config area, or -1 if fail.
|
||||
int ae_read_config_word(int offset, uint8_t *dest);
|
||||
|
||||
// Call this if possible mitm is detected.
|
||||
extern void fatal_mitm(void) __attribute__((noreturn));
|
||||
|
||||
#if FOR_608
|
||||
// Update the match-counter with a new number.
|
||||
int ae_write_match_count(uint32_t count, const uint8_t *write_key);
|
||||
|
||||
// Perform many key iterations and read out the result. Designed to be slow.
|
||||
int ae_stretch_iter(const uint8_t start[32], uint8_t end[32], int iterations);
|
||||
|
||||
// Mix in (via HMAC) the contents of a specific key on the device.
|
||||
int ae_mixin_key(uint8_t keynum, const uint8_t start[32], uint8_t end[32]);
|
||||
|
||||
#endif
|
||||
|
||||
// EOF
|
||||
164
stm32/mk4-bootloader/ae_config.h
Normal file
164
stm32/mk4-bootloader/ae_config.h
Normal file
@ -0,0 +1,164 @@
|
||||
// autogenerated; see bootloader/keylayout.py
|
||||
|
||||
#ifdef FOR_608
|
||||
|
||||
// bytes [16..84) of chip config area
|
||||
#define AE_CHIP_CONFIG_1 { \
|
||||
0xe1, 0x00, 0x61, 0x00, 0x00, 0x00, 0x8f, 0x2d, 0x8f, 0x80, \
|
||||
0x8f, 0x43, 0xaf, 0x80, 0x00, 0x43, 0x00, 0x43, 0x8f, 0x47, \
|
||||
0xc3, 0x43, 0xc3, 0x43, 0xc7, 0x47, 0x00, 0x47, 0x00, 0x00, \
|
||||
0x8f, 0x4d, 0x8f, 0x43, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, \
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 \
|
||||
}
|
||||
|
||||
|
||||
// bytes [90..128) of chip config area
|
||||
#define AE_CHIP_CONFIG_2 { \
|
||||
0x02, 0x15, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x5c, 0x00, \
|
||||
0xbc, 0x01, 0xfc, 0x01, 0xbc, 0x01, 0x9c, 0x01, 0x9c, 0x01, \
|
||||
0xfc, 0x01, 0xdc, 0x03, 0xdc, 0x03, 0xdc, 0x07, 0x9c, 0x01, \
|
||||
0x3c, 0x00, 0xfc, 0x01, 0xdc, 0x01, 0x3c, 0x00 \
|
||||
}
|
||||
|
||||
|
||||
// key/slot usage and names
|
||||
#define KEYNUM_pairing 1
|
||||
#define KEYNUM_pin_stretch 2
|
||||
#define KEYNUM_main_pin 3
|
||||
#define KEYNUM_pin_attempt 4
|
||||
#define KEYNUM_lastgood 5
|
||||
#define KEYNUM_match_count 6
|
||||
#define KEYNUM_duress_pin 7
|
||||
#define KEYNUM_long_secret 8
|
||||
#define KEYNUM_secret 9
|
||||
#define KEYNUM_duress_secret 10
|
||||
#define KEYNUM_duress_lastgood 11
|
||||
#define KEYNUM_brickme 13
|
||||
#define KEYNUM_firmware 14
|
||||
|
||||
/*
|
||||
|
||||
RevNum: 00006002 len=4
|
||||
Chip type: atecc608a
|
||||
AES_Enable = 0x1
|
||||
I2C_Enable = 0x0
|
||||
GPIO Mode = 0x1
|
||||
GPIO Default = 0x0
|
||||
GPIO Detect (vs authout) = 0x0
|
||||
GPIO SignalKey/KeyId = 0xe
|
||||
I2C_Address(sic) = 0xe1
|
||||
CountMatchKey = 0x6
|
||||
CounterMatch enable = 1
|
||||
ChipMode = 0x0
|
||||
|
||||
Slot[0] = 0x0000 = SlotConfig(ReadKey=0, NoMac=0, LimitedUse=0, EncryptRead=0, IsSecret=0, WriteKey=0, WriteConfig=0)=0x0000
|
||||
KeyConfig[0] = 0x3c00 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=1, ReqRandom=0, ReqAuth=0, AuthKey=0, PersistentDisable=0, RFU=0, X509id=0)=0x003c
|
||||
|
||||
Slot[1] = 0x8f2d = SlotConfig(ReadKey=15, NoMac=0, LimitedUse=0, EncryptRead=0, IsSecret=1, WriteKey=13, WriteConfig=2)=0x2d8f
|
||||
KeyConfig[1] = 0x5c00 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=0, ReqRandom=1, ReqAuth=0, AuthKey=0, PersistentDisable=0, RFU=0, X509id=0)=0x005c
|
||||
|
||||
Slot[2] = 0x8f80 = SlotConfig(ReadKey=15, NoMac=0, LimitedUse=0, EncryptRead=0, IsSecret=1, WriteKey=0, WriteConfig=8)=0x808f
|
||||
KeyConfig[2] = 0xbc01 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=1, ReqRandom=0, ReqAuth=1, AuthKey=1, PersistentDisable=0, RFU=0, X509id=0)=0x01bc
|
||||
|
||||
Slot[3] = 0x8f43 = SlotConfig(ReadKey=15, NoMac=0, LimitedUse=0, EncryptRead=0, IsSecret=1, WriteKey=3, WriteConfig=4)=0x438f
|
||||
KeyConfig[3] = 0xfc01 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=1, ReqRandom=1, ReqAuth=1, AuthKey=1, PersistentDisable=0, RFU=0, X509id=0)=0x01fc
|
||||
|
||||
Slot[4] = 0xaf80 = SlotConfig(ReadKey=15, NoMac=0, LimitedUse=1, EncryptRead=0, IsSecret=1, WriteKey=0, WriteConfig=8)=0x80af
|
||||
KeyConfig[4] = 0xbc01 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=1, ReqRandom=0, ReqAuth=1, AuthKey=1, PersistentDisable=0, RFU=0, X509id=0)=0x01bc
|
||||
|
||||
Slot[5] = 0x0043 = SlotConfig(ReadKey=0, NoMac=0, LimitedUse=0, EncryptRead=0, IsSecret=0, WriteKey=3, WriteConfig=4)=0x4300
|
||||
KeyConfig[5] = 0x9c01 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=0, ReqRandom=0, ReqAuth=1, AuthKey=1, PersistentDisable=0, RFU=0, X509id=0)=0x019c
|
||||
|
||||
Slot[6] = 0x0043 = SlotConfig(ReadKey=0, NoMac=0, LimitedUse=0, EncryptRead=0, IsSecret=0, WriteKey=3, WriteConfig=4)=0x4300
|
||||
KeyConfig[6] = 0x9c01 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=0, ReqRandom=0, ReqAuth=1, AuthKey=1, PersistentDisable=0, RFU=0, X509id=0)=0x019c
|
||||
|
||||
Slot[7] = 0x8f47 = SlotConfig(ReadKey=15, NoMac=0, LimitedUse=0, EncryptRead=0, IsSecret=1, WriteKey=7, WriteConfig=4)=0x478f
|
||||
KeyConfig[7] = 0xfc01 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=1, ReqRandom=1, ReqAuth=1, AuthKey=1, PersistentDisable=0, RFU=0, X509id=0)=0x01fc
|
||||
|
||||
Slot[8] = 0xc343 = SlotConfig(ReadKey=3, NoMac=0, LimitedUse=0, EncryptRead=1, IsSecret=1, WriteKey=3, WriteConfig=4)=0x43c3
|
||||
KeyConfig[8] = 0xdc03 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=0, ReqRandom=1, ReqAuth=1, AuthKey=3, PersistentDisable=0, RFU=0, X509id=0)=0x03dc
|
||||
|
||||
Slot[9] = 0xc343 = SlotConfig(ReadKey=3, NoMac=0, LimitedUse=0, EncryptRead=1, IsSecret=1, WriteKey=3, WriteConfig=4)=0x43c3
|
||||
KeyConfig[9] = 0xdc03 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=0, ReqRandom=1, ReqAuth=1, AuthKey=3, PersistentDisable=0, RFU=0, X509id=0)=0x03dc
|
||||
|
||||
Slot[10] = 0xc747 = SlotConfig(ReadKey=7, NoMac=0, LimitedUse=0, EncryptRead=1, IsSecret=1, WriteKey=7, WriteConfig=4)=0x47c7
|
||||
KeyConfig[10] = 0xdc07 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=0, ReqRandom=1, ReqAuth=1, AuthKey=7, PersistentDisable=0, RFU=0, X509id=0)=0x07dc
|
||||
|
||||
Slot[11] = 0x0047 = SlotConfig(ReadKey=0, NoMac=0, LimitedUse=0, EncryptRead=0, IsSecret=0, WriteKey=7, WriteConfig=4)=0x4700
|
||||
KeyConfig[11] = 0x9c01 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=0, ReqRandom=0, ReqAuth=1, AuthKey=1, PersistentDisable=0, RFU=0, X509id=0)=0x019c
|
||||
|
||||
Slot[12] = 0x0000 = SlotConfig(ReadKey=0, NoMac=0, LimitedUse=0, EncryptRead=0, IsSecret=0, WriteKey=0, WriteConfig=0)=0x0000
|
||||
KeyConfig[12] = 0x3c00 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=1, ReqRandom=0, ReqAuth=0, AuthKey=0, PersistentDisable=0, RFU=0, X509id=0)=0x003c
|
||||
|
||||
Slot[13] = 0x8f4d = SlotConfig(ReadKey=15, NoMac=0, LimitedUse=0, EncryptRead=0, IsSecret=1, WriteKey=13, WriteConfig=4)=0x4d8f
|
||||
KeyConfig[13] = 0xfc01 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=1, ReqRandom=1, ReqAuth=1, AuthKey=1, PersistentDisable=0, RFU=0, X509id=0)=0x01fc
|
||||
|
||||
Slot[14] = 0x8f43 = SlotConfig(ReadKey=15, NoMac=0, LimitedUse=0, EncryptRead=0, IsSecret=1, WriteKey=3, WriteConfig=4)=0x438f
|
||||
KeyConfig[14] = 0xdc01 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=0, ReqRandom=1, ReqAuth=1, AuthKey=1, PersistentDisable=0, RFU=0, X509id=0)=0x01dc
|
||||
|
||||
Slot[15] = 0x0000 = SlotConfig(ReadKey=0, NoMac=0, LimitedUse=0, EncryptRead=0, IsSecret=0, WriteKey=0, WriteConfig=0)=0x0000
|
||||
KeyConfig[15] = 0x3c00 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=1, ReqRandom=0, ReqAuth=0, AuthKey=0, PersistentDisable=0, RFU=0, X509id=0)=0x003c
|
||||
|
||||
Counter[0]: ffffffff00000000 len=8
|
||||
Counter[1]: ffffffff00000000 len=8
|
||||
UseLock = 0x0
|
||||
VolatileKeyPermission = 0x0
|
||||
SecureBoot: 0000 len=2
|
||||
KldfvLoc = 0xf0
|
||||
KdflvStr: 0000 len=2
|
||||
UserExtra = 0x0
|
||||
UserExtraAdd = 0x0
|
||||
LockValue = 0x55
|
||||
LockConfig = 0x55
|
||||
SlotLocked: ffff len=2
|
||||
ChipOptions: 0215 len=2
|
||||
ChipOptions = ChipOptions(POSTEnable=0, IOProtKeyEnable=1, KDFAESEnable=0, mustbezero=0, ECDHProt=1, KDFProt=1, IOProtKey=1)=0x1502
|
||||
X509format: 00000000 len=4
|
||||
|
||||
*/
|
||||
#endif /* FOR_608 */
|
||||
|
||||
|
||||
#ifdef FOR_508
|
||||
|
||||
// bytes [16..84) of chip config area
|
||||
#define AE_CHIP_CONFIG_1 { \
|
||||
0xe1, 0x00, 0x55, 0x00, 0x00, 0x00, 0x8f, 0x2d, 0x8f, 0x80, \
|
||||
0x8f, 0x43, 0x8f, 0x44, 0x00, 0x43, 0x00, 0x44, 0x8f, 0x47, \
|
||||
0x8f, 0x48, 0xc3, 0x43, 0xc4, 0x44, 0xc7, 0x47, 0xc8, 0x48, \
|
||||
0x8f, 0x4d, 0x8f, 0x43, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, \
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, \
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff \
|
||||
}
|
||||
|
||||
|
||||
// bytes [90..128) of chip config area
|
||||
#define AE_CHIP_CONFIG_2 { \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x5c, 0x00, \
|
||||
0xbc, 0x01, 0xfc, 0x01, 0xfc, 0x01, 0x9c, 0x01, 0x9c, 0x01, \
|
||||
0xfc, 0x01, 0xfc, 0x01, 0xdc, 0x03, 0xdc, 0x04, 0xdc, 0x07, \
|
||||
0xdc, 0x08, 0xfc, 0x01, 0xdc, 0x01, 0x3c, 0x00 \
|
||||
}
|
||||
|
||||
|
||||
// key/slot usage and names
|
||||
#define KEYNUM_pairing 1
|
||||
#define KEYNUM_words 2
|
||||
#define KEYNUM_pin_1 3
|
||||
#define KEYNUM_pin_2 4
|
||||
#define KEYNUM_lastgood_1 5
|
||||
#define KEYNUM_lastgood_2 6
|
||||
#define KEYNUM_pin_3 7
|
||||
#define KEYNUM_pin_4 8
|
||||
#define KEYNUM_secret_1 9
|
||||
#define KEYNUM_secret_2 10
|
||||
#define KEYNUM_secret_3 11
|
||||
#define KEYNUM_secret_4 12
|
||||
#define KEYNUM_brickme 13
|
||||
#define KEYNUM_firmware 14
|
||||
#endif /* FOR_508 */
|
||||
|
||||
|
||||
2
stm32/mk4-bootloader/assets/.gitignore
vendored
Normal file
2
stm32/mk4-bootloader/assets/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Buy the fine product and copy file here (minus spaces)
|
||||
FontAwesome5Pro-Light-300.otf
|
||||
BIN
stm32/mk4-bootloader/assets/FontAwesome407.otf
Normal file
BIN
stm32/mk4-bootloader/assets/FontAwesome407.otf
Normal file
Binary file not shown.
4
stm32/mk4-bootloader/assets/Makefile
Normal file
4
stm32/mk4-bootloader/assets/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
all screens.c screens.h: convert.py
|
||||
python3 convert.py
|
||||
|
||||
13
stm32/mk4-bootloader/assets/README.md
Normal file
13
stm32/mk4-bootloader/assets/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
# Boot Loader screens
|
||||
|
||||

|
||||
|
||||
If you own the Pro version of Font Awesome, copy file `FontAwesome5Pro-Light-300.otf`
|
||||
int this directory and the code will use that in preference to the free 4.7 icons.
|
||||
|
||||
# Credits
|
||||
|
||||
- [Fontawesome version 4.7.0 (free version)](http://fontawesome.io/)
|
||||
|
||||
- [Zevv-pepp](http://zevv.nl/play/code/zevv-peep/)
|
||||
BIN
stm32/mk4-bootloader/assets/background.png
Normal file
BIN
stm32/mk4-bootloader/assets/background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 90 B |
222
stm32/mk4-bootloader/assets/convert.py
Normal file
222
stm32/mk4-bootloader/assets/convert.py
Normal file
@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
# Plan:
|
||||
# - RLE compress that and create binary object to go in flash
|
||||
# - large symbol in middle and some text under it
|
||||
# - one line of small text for english speakers
|
||||
#
|
||||
# pip3 install Pillow
|
||||
#
|
||||
import os
|
||||
from PIL import Image, ImageOps, ImageFont, ImageDraw
|
||||
from itertools import groupby
|
||||
|
||||
# screen size
|
||||
WH = (128, 64)
|
||||
|
||||
ICON_SIZE = 24 # was 40 ... anything less than 24 is ugly.
|
||||
|
||||
# PROBLEM: this file costs money... altho free version looks okay too
|
||||
if os.path.exists('FontAwesome5Pro-Light-300.otf'):
|
||||
awesome = ImageFont.truetype('FontAwesome5Pro-Light-300.otf', ICON_SIZE)
|
||||
else:
|
||||
awesome = ImageFont.truetype('FontAwesome407.otf', ICON_SIZE)
|
||||
|
||||
sm_font = ImageFont.load('zevv-peep-iso8859-15-07x14.pil')
|
||||
lg_font = ImageFont.load('zevv-peep-iso8859-15-10x20.pil')
|
||||
|
||||
# I loaded FontAwesome as a system font my desktop, and cut-n-pasted the symbols from FontBook.
|
||||
# You can also copy the Unicode code point from the FA website.
|
||||
icons = {
|
||||
'lemon': '',
|
||||
'clock': '',
|
||||
'usb': '\uf287',
|
||||
'download': '',
|
||||
'history': '',
|
||||
'bug': '',
|
||||
'x-circle': '',
|
||||
#'bomb-spook': ' ', # bomb / half-space / sunglasses guy
|
||||
'bomb-spook': ' ', # bomb / spaces / sunglasses guy
|
||||
'spook': '', # sunglasses guy
|
||||
'recycle': '\uf1b8',
|
||||
'trash': '\uf2ed',
|
||||
'thumbs-down': '',
|
||||
'thumbs-up': '',
|
||||
'graph-up': '',
|
||||
'logout': '',
|
||||
'ticket': '',
|
||||
'dots': '',
|
||||
}
|
||||
|
||||
def make_background():
|
||||
img = Image.open('background.png').convert('1', dither=0)
|
||||
assert img.size == WH
|
||||
|
||||
if 0:
|
||||
# LATER: this costs too many bytes!
|
||||
# add a logo
|
||||
logo = Image.open('chip-logo.png').convert('1', dither=0)
|
||||
img.paste(logo, (4,4))
|
||||
|
||||
# version # .. but little time to see it, and consumes too much ROM
|
||||
#d = ImageDraw.Draw(img)
|
||||
#d.text( (128-20, 5), 'v1', font=sm_font, fill=1)
|
||||
|
||||
return img
|
||||
|
||||
def make_frame(img, txt, icon_name, text_pos=None, icon_pos=3, icon_xpos=0):
|
||||
rv = img.copy()
|
||||
d = ImageDraw.Draw(rv)
|
||||
|
||||
if icon_name:
|
||||
w,h = d.textsize(icons[icon_name], font=awesome)
|
||||
icon_pos += (40 - ICON_SIZE)/2
|
||||
d.text( (64-(w/2)+icon_xpos, icon_pos), icons[icon_name], font=awesome, fill=1)
|
||||
text_pos = text_pos or 56
|
||||
else:
|
||||
text_pos = text_pos or 40
|
||||
|
||||
w,h = d.textsize(txt, font=sm_font)
|
||||
assert w <= 128, "Message too wide: " + repr(txt)
|
||||
d.text( (64-(w/2), text_pos-h), txt, font=sm_font, fill=1)
|
||||
|
||||
return rv
|
||||
|
||||
def rev(n):
|
||||
return int('{:08b}'.format(n)[::-1], 2)
|
||||
|
||||
def rle_compress(orig):
|
||||
# simple RLE:
|
||||
# - first byte is flag + length (flag mask=0x80)
|
||||
# - next byte is repeated length times if flag=0
|
||||
# - or folloing length bytes are sent verbatim
|
||||
# - must add up to full screen contents.
|
||||
# - add zero at end
|
||||
|
||||
def flush(misc):
|
||||
rv = b''
|
||||
while misc:
|
||||
here = min(len(misc), 127)
|
||||
rv += bytes([0x80 + here]) + misc[0:here]
|
||||
misc = misc[here:]
|
||||
return rv
|
||||
|
||||
out = b''
|
||||
misc = b''
|
||||
|
||||
for ch, group in groupby(orig):
|
||||
dups = len(list(group))
|
||||
if dups <= 2:
|
||||
misc += bytes([ch] * dups)
|
||||
continue
|
||||
|
||||
out += flush(misc)
|
||||
misc = b''
|
||||
|
||||
while dups:
|
||||
here = min(dups, 127)
|
||||
out += bytes([0x00 + here, ch])
|
||||
dups -= here
|
||||
|
||||
out += flush(misc)
|
||||
|
||||
return out + b'\0'
|
||||
|
||||
|
||||
def serialize(img, label, fp):
|
||||
|
||||
img = ImageOps.mirror(img)
|
||||
#img = ImageOps.flip(img)
|
||||
|
||||
assert img.size == WH, "wrong size: " + repr(img.size)
|
||||
|
||||
img = img.rotate(-90, expand=1)
|
||||
assert img.size == (64, 128)
|
||||
|
||||
img = ImageOps.mirror(img)
|
||||
img = ImageOps.flip(img)
|
||||
|
||||
raw = img.tobytes()
|
||||
|
||||
# OLED layout (dependant on settings during it's config)
|
||||
# - FF81818100.. will draw a C shape in top left corner
|
||||
# raw = b'\xff\x81\x81\x81' + (b'\0'*1020)
|
||||
# - and each byte is reversed, etc.
|
||||
|
||||
assert len(raw) == 64*128//8, "Wrong size?"
|
||||
|
||||
reorg = bytearray(1024)
|
||||
j = 0
|
||||
for x in range(8):
|
||||
for y in range(128):
|
||||
reorg[j] = rev(raw[(y*8)+x])
|
||||
j += 1
|
||||
|
||||
final = rle_compress(reorg)
|
||||
|
||||
fp.write('const unsigned char %s[%d] = {\n' % (label, len(final)))
|
||||
fp.write(', '.join('0x%02x'%i for i in final))
|
||||
fp.write('\n};\n\n')
|
||||
|
||||
return len(final)
|
||||
|
||||
# Actual screens and their contents.
|
||||
#
|
||||
# - getting the exact hight/position of the text aligned w/ 8-bits helps alot sometimes
|
||||
# - rare screens don't need to be pretty
|
||||
#
|
||||
results = [
|
||||
( 'verify', 'Verifying', 'clock', {} ),
|
||||
( 'blankish', '. . .', None, dict(text_pos=36) ), # shown while we boot micropython (momentary)
|
||||
( 'fatal', '#fwf', None, dict(text_pos=38) ), # don't waste space on rarely-seen screens
|
||||
( 'mitm', '-/-', None, {} ), # don't waste space on rarely-seen screens
|
||||
#( 'brick', '', 'ticket', dict(icon_pos=12) ), # was: icon=Trash / I am brick.
|
||||
( 'brick', 'Bricked', None, dict() ), # was: icon=ticket
|
||||
#( 'dfu', 'Send Upgrade', 'download', {} ), # was beautiful, but won't be seen with RDP=2
|
||||
( 'dfu', 'DFU', None, dict(text_pos=37) ),
|
||||
( 'downgrade', 'Downgrade?', 'history', {} ),
|
||||
( 'corrupt', 'Firmware?', 'lemon', {} ),
|
||||
( 'logout', 'Logout Done', 'logout', {}),
|
||||
( 'devmode', 'Danger! Caution!', 'bomb-spook', dict(icon_xpos=0)), # was 2
|
||||
( 'upgrading', 'Upgrading', 'graph-up', {}),
|
||||
( 'replug', 'Replug', None, {}), # visible in factory only
|
||||
]
|
||||
|
||||
if __name__ == '__main__':
|
||||
prefix = 'screen_';
|
||||
out = open("screens.c", 'wt')
|
||||
out.write("// autogenerated by assets/convert.py\n\n")
|
||||
|
||||
bg = make_background()
|
||||
sampler = Image.new('1', (128+8, len(results) * (64+8)), 1)
|
||||
|
||||
y = 6
|
||||
total = 0
|
||||
for label, txt, icon, args in results:
|
||||
if 0:
|
||||
# no icons at all
|
||||
icon = None
|
||||
elif 0:
|
||||
# minimal icons
|
||||
icon = None if label not in ('verify', 'devmode', 'logout', 'brick') else icon
|
||||
|
||||
img = make_frame(bg, txt, icon, **args)
|
||||
sampler.paste(img, (4, y))
|
||||
y += 64+4
|
||||
|
||||
total += serialize(img, prefix+label, out)
|
||||
|
||||
out.close()
|
||||
|
||||
out = open("screens.h", 'wt')
|
||||
out.write("// autogenerated by assets/convert.py\n\n")
|
||||
|
||||
for label, txt, icon, _ in results:
|
||||
out.write('\nextern const unsigned char %s[];\n\n' % (prefix+label))
|
||||
|
||||
print("Files created! %d bytes ROM used. See 'sampler.png'" % total)
|
||||
#sampler.show()
|
||||
sampler.save('sampler.png')
|
||||
|
||||
34
stm32/mk4-bootloader/assets/font-awesome-LICENSE.txt
Normal file
34
stm32/mk4-bootloader/assets/font-awesome-LICENSE.txt
Normal file
@ -0,0 +1,34 @@
|
||||
Font Awesome Free License
|
||||
-------------------------
|
||||
|
||||
Font Awesome Free is free, open source, and GPL friendly. You can use it for
|
||||
commercial projects, open source projects, or really almost whatever you want.
|
||||
Full Font Awesome Free license: https://fontawesome.com/license.
|
||||
|
||||
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
|
||||
In the Font Awesome Free download, the CC BY 4.0 license applies to all icons
|
||||
packaged as SVG and JS file types.
|
||||
|
||||
# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL)
|
||||
In the Font Awesome Free download, the SIL OLF license applies to all icons
|
||||
packaged as web and desktop font files.
|
||||
|
||||
# Code: MIT License (https://opensource.org/licenses/MIT)
|
||||
In the Font Awesome Free download, the MIT license applies to all non-font and
|
||||
non-icon files.
|
||||
|
||||
# Attribution
|
||||
Attribution is required by MIT, SIL OLF, and CC BY licenses. Downloaded Font
|
||||
Awesome Free files already contain embedded comments with sufficient
|
||||
attribution, so you shouldn't need to do anything additional when using these
|
||||
files normally.
|
||||
|
||||
We've kept attribution comments terse, so we ask that you do not actively work
|
||||
to remove them from files, especially code. They're a great way for folks to
|
||||
learn about Font Awesome.
|
||||
|
||||
# Brand Icons
|
||||
All brand icons are trademarks of their respective owners. The use of these
|
||||
trademarks does not indicate endorsement of the trademark holder by Font
|
||||
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
|
||||
to represent the company, product, or service to which they refer.**
|
||||
BIN
stm32/mk4-bootloader/assets/sampler.png
Normal file
BIN
stm32/mk4-bootloader/assets/sampler.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
50
stm32/mk4-bootloader/assets/screens.c
Normal file
50
stm32/mk4-bootloader/assets/screens.c
Normal file
@ -0,0 +1,50 @@
|
||||
// autogenerated by assets/convert.py
|
||||
|
||||
const unsigned char screen_verify[193] = {
|
||||
0x7f, 0x00, 0x3a, 0x00, 0x83, 0x80, 0xc0, 0xc0, 0x06, 0xe0, 0x03, 0xc0, 0x81, 0x80, 0x6f, 0x00, 0x95, 0xf0, 0xfc, 0xfe, 0x1f, 0x07, 0x03, 0x01, 0x81, 0x80, 0x80, 0xfc, 0xfc, 0x00, 0x01, 0x01, 0x03, 0x0f, 0x7f, 0xfe, 0xf8, 0xc0, 0x6b, 0x00, 0x88, 0x07, 0x1f, 0x3f, 0x7c, 0xf0, 0xe0, 0xc0, 0xc1, 0x04, 0x81, 0x89, 0x80, 0xc0, 0xc0, 0xe0, 0xf8, 0x7f, 0x3f, 0x0f, 0x01, 0x70, 0x00, 0x82, 0x01, 0x01, 0x07, 0x03, 0x82, 0x01, 0x01, 0x5c, 0x00, 0x81, 0xf8, 0x04, 0x00, 0x83, 0xf8, 0x00, 0x80, 0x04, 0x40, 0x84, 0x80, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x86, 0x80, 0x00, 0x00, 0x40, 0x40, 0xd8, 0x04, 0x00, 0x81, 0xf0, 0x03, 0x08, 0x83, 0x10, 0x00, 0xc0, 0x04, 0x00, 0x86, 0xc0, 0x00, 0x00, 0x40, 0x40, 0xd8, 0x03, 0x00, 0x82, 0xc0, 0x80, 0x03, 0x40, 0x83, 0x80, 0x00, 0x80, 0x03, 0x40, 0x82, 0x80, 0xc0, 0x42, 0x00, 0x88, 0x01, 0x06, 0x18, 0x18, 0x06, 0x01, 0x00, 0x0f, 0x04, 0x12, 0x83, 0x13, 0x00, 0x1f, 0x09, 0x00, 0x81, 0x1f, 0x03, 0x00, 0x82, 0x01, 0x1f, 0x03, 0x01, 0x83, 0x00, 0x00, 0x47, 0x03, 0x88, 0x82, 0x84, 0x7f, 0x04, 0x00, 0x81, 0x1f, 0x03, 0x00, 0x81, 0x1f, 0x04, 0x00, 0x83, 0x1f, 0x00, 0x47, 0x03, 0x88, 0x82, 0x84, 0x7f, 0x7f, 0x00, 0x22, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char screen_blankish[45] = {
|
||||
0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x34, 0x00, 0x81, 0x80, 0x0d, 0x00, 0x81, 0x80, 0x0d, 0x00, 0x81, 0x80, 0x62, 0x00, 0x83, 0x01, 0x03, 0x01, 0x0b, 0x00, 0x83, 0x01, 0x03, 0x01, 0x0b, 0x00, 0x83, 0x01, 0x03, 0x01, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x34, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char screen_fatal[68] = {
|
||||
0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x36, 0x00, 0x89, 0x90, 0xfc, 0x90, 0x90, 0xfc, 0x90, 0x00, 0x40, 0xfc, 0x03, 0x42, 0x8b, 0x04, 0x00, 0xf0, 0x00, 0x00, 0xc0, 0x00, 0xf0, 0x00, 0x40, 0xfc, 0x03, 0x42, 0x81, 0x04, 0x66, 0x00, 0x84, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x81, 0x07, 0x05, 0x00, 0x89, 0x03, 0x04, 0x02, 0x03, 0x04, 0x03, 0x00, 0x00, 0x07, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x39, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char screen_mitm[34] = {
|
||||
0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x43, 0x00, 0x83, 0xc0, 0x30, 0x0c, 0x73, 0x00, 0x06, 0x01, 0x84, 0x00, 0x30, 0x0c, 0x03, 0x04, 0x00, 0x06, 0x01, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x39, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char screen_brick[106] = {
|
||||
0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x2b, 0x00, 0x81, 0xf8, 0x03, 0x88, 0x85, 0xf0, 0x00, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x86, 0x80, 0x00, 0x00, 0x40, 0x40, 0xd8, 0x03, 0x00, 0x81, 0x80, 0x04, 0x40, 0x8a, 0x80, 0x00, 0xf8, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x80, 0x04, 0x40, 0x83, 0x80, 0x00, 0x80, 0x03, 0x40, 0x82, 0x80, 0xf8, 0x50, 0x00, 0x81, 0x1f, 0x04, 0x10, 0x83, 0x0f, 0x00, 0x1f, 0x09, 0x00, 0x81, 0x1f, 0x03, 0x00, 0x81, 0x0f, 0x04, 0x10, 0x8a, 0x08, 0x00, 0x1f, 0x02, 0x03, 0x04, 0x08, 0x10, 0x00, 0x0f, 0x04, 0x12, 0x83, 0x13, 0x00, 0x0f, 0x03, 0x10, 0x82, 0x08, 0x1f, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x2b, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char screen_dfu[55] = {
|
||||
0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x39, 0x00, 0x81, 0xff, 0x03, 0x01, 0x84, 0x02, 0xfc, 0x00, 0xff, 0x04, 0x11, 0x83, 0x01, 0x00, 0xff, 0x04, 0x00, 0x81, 0xff, 0x6c, 0x00, 0x81, 0x03, 0x03, 0x02, 0x84, 0x01, 0x00, 0x00, 0x03, 0x06, 0x00, 0x81, 0x01, 0x04, 0x02, 0x81, 0x01, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x39, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char screen_downgrade[198] = {
|
||||
0x7f, 0x00, 0x36, 0x00, 0x85, 0xc0, 0xc0, 0x80, 0x00, 0x80, 0x03, 0xc0, 0x05, 0xe0, 0x84, 0xc0, 0xc0, 0x80, 0x80, 0x6f, 0x00, 0x06, 0x3f, 0x8f, 0x3b, 0x11, 0x01, 0x01, 0xf8, 0xf9, 0x01, 0x01, 0x03, 0x07, 0x1f, 0xff, 0xfe, 0xf8, 0xc0, 0x6d, 0x00, 0x86, 0x20, 0x70, 0xf8, 0xf0, 0xe0, 0xe3, 0x04, 0xc3, 0x89, 0xc0, 0xe0, 0xe0, 0xf0, 0x7c, 0x7f, 0x1f, 0x0f, 0x01, 0x71, 0x00, 0x09, 0x01, 0x5a, 0x00, 0x81, 0xf8, 0x03, 0x08, 0x84, 0x10, 0xe0, 0x00, 0x80, 0x04, 0x40, 0x83, 0x80, 0x00, 0xc0, 0x04, 0x00, 0x84, 0xc0, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x83, 0x80, 0x00, 0x80, 0x03, 0x40, 0x85, 0x80, 0xc0, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x83, 0x80, 0x00, 0x00, 0x04, 0x40, 0x83, 0x80, 0x00, 0x80, 0x03, 0x40, 0x84, 0x80, 0xf8, 0x00, 0x80, 0x04, 0x40, 0x87, 0x80, 0x00, 0x08, 0x08, 0x88, 0x48, 0x30, 0x3c, 0x00, 0x81, 0x1f, 0x03, 0x10, 0x84, 0x08, 0x07, 0x00, 0x0f, 0x04, 0x10, 0x8a, 0x0f, 0x00, 0x0f, 0x10, 0x08, 0x0f, 0x10, 0x0f, 0x00, 0x1f, 0x04, 0x00, 0x83, 0x1f, 0x00, 0x47, 0x03, 0x88, 0x84, 0x84, 0x7f, 0x00, 0x1f, 0x06, 0x00, 0x81, 0x0e, 0x03, 0x11, 0x84, 0x09, 0x1f, 0x00, 0x0f, 0x03, 0x10, 0x84, 0x08, 0x1f, 0x00, 0x0f, 0x04, 0x12, 0x84, 0x13, 0x00, 0x00, 0x1b, 0x7f, 0x00, 0x22, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char screen_corrupt[179] = {
|
||||
0x7f, 0x00, 0x3b, 0x00, 0x03, 0x80, 0x87, 0xc0, 0x40, 0x40, 0x60, 0x60, 0x40, 0x40, 0x04, 0xc0, 0x81, 0x80, 0x6c, 0x00, 0x87, 0xc0, 0xf8, 0x1c, 0x06, 0x03, 0x01, 0x01, 0x0b, 0x00, 0x83, 0x0f, 0xff, 0xc0, 0x6b, 0x00, 0x83, 0xff, 0xff, 0x00, 0x04, 0x80, 0x03, 0x00, 0x04, 0x80, 0x86, 0xc0, 0xe0, 0x70, 0x38, 0x1e, 0x07, 0x6c, 0x00, 0x81, 0x01, 0x03, 0x03, 0x81, 0x01, 0x08, 0x03, 0x82, 0x01, 0x01, 0x5d, 0x00, 0x81, 0xf8, 0x04, 0x88, 0x86, 0x08, 0x00, 0x00, 0x40, 0x40, 0xd8, 0x03, 0x00, 0x82, 0xc0, 0x80, 0x03, 0x40, 0x8a, 0x80, 0x00, 0xc0, 0x40, 0x40, 0x80, 0x40, 0x80, 0x00, 0xc0, 0x04, 0x00, 0x83, 0xc0, 0x00, 0x00, 0x04, 0x40, 0x84, 0x80, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x83, 0x80, 0x00, 0x80, 0x04, 0x40, 0x87, 0x80, 0x00, 0x08, 0x08, 0x88, 0x48, 0x30, 0x43, 0x00, 0x81, 0x1f, 0x09, 0x00, 0x81, 0x1f, 0x03, 0x00, 0x81, 0x1f, 0x06, 0x00, 0x8f, 0x1f, 0x00, 0x00, 0x07, 0x00, 0x1f, 0x00, 0x0f, 0x10, 0x08, 0x0f, 0x10, 0x0f, 0x00, 0x0e, 0x03, 0x11, 0x84, 0x09, 0x1f, 0x00, 0x1f, 0x06, 0x00, 0x81, 0x0f, 0x04, 0x12, 0x84, 0x13, 0x00, 0x00, 0x1b, 0x7f, 0x00, 0x26, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char screen_logout[187] = {
|
||||
0x7f, 0x00, 0x38, 0x00, 0x07, 0x80, 0x77, 0x00, 0x82, 0xfe, 0xff, 0x03, 0x01, 0x03, 0xf1, 0x04, 0xf0, 0x89, 0xff, 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x6b, 0x00, 0x82, 0x3f, 0x7f, 0x03, 0xc0, 0x03, 0xc7, 0x81, 0x87, 0x03, 0x07, 0x88, 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x7f, 0x00, 0x52, 0x00, 0x81, 0xf8, 0x06, 0x00, 0x81, 0x80, 0x04, 0x40, 0x83, 0x80, 0x00, 0x80, 0x03, 0x40, 0x84, 0x80, 0xc0, 0x00, 0x80, 0x04, 0x40, 0x83, 0x80, 0x00, 0xc0, 0x04, 0x00, 0x84, 0xc0, 0x00, 0x40, 0xf0, 0x03, 0x40, 0x09, 0x00, 0x81, 0xf8, 0x03, 0x08, 0x84, 0x10, 0xe0, 0x00, 0x80, 0x04, 0x40, 0x84, 0x80, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x83, 0x80, 0x00, 0x80, 0x04, 0x40, 0x81, 0x80, 0x34, 0x00, 0x81, 0x1f, 0x05, 0x10, 0x82, 0x00, 0x0f, 0x04, 0x10, 0x83, 0x0f, 0x00, 0x47, 0x03, 0x88, 0x84, 0x84, 0x7f, 0x00, 0x0f, 0x04, 0x10, 0x83, 0x0f, 0x00, 0x0f, 0x03, 0x10, 0x85, 0x08, 0x1f, 0x00, 0x00, 0x0f, 0x03, 0x10, 0x81, 0x08, 0x08, 0x00, 0x81, 0x1f, 0x03, 0x10, 0x84, 0x08, 0x07, 0x00, 0x0f, 0x04, 0x10, 0x83, 0x0f, 0x00, 0x1f, 0x04, 0x00, 0x83, 0x1f, 0x00, 0x0f, 0x04, 0x12, 0x81, 0x13, 0x7f, 0x00, 0x1b, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char screen_devmode[303] = {
|
||||
0x7f, 0x00, 0x37, 0x00, 0x85, 0xb0, 0x90, 0x20, 0xf0, 0x40, 0x10, 0x00, 0x82, 0x80, 0xe0, 0x05, 0xf0, 0x84, 0xf8, 0xf0, 0xe0, 0x80, 0x4d, 0x00, 0x88, 0x80, 0xe0, 0x70, 0x18, 0x8c, 0xce, 0xe6, 0xf6, 0x05, 0xfe, 0x81, 0xfc, 0x04, 0xfe, 0x86, 0x9b, 0x01, 0x00, 0x00, 0x01, 0x01, 0x0e, 0x00, 0x8f, 0xb1, 0xf3, 0xff, 0xff, 0xf3, 0xe3, 0xf3, 0xff, 0xe3, 0xe3, 0xf3, 0xff, 0xf7, 0xf3, 0xb1, 0x4b, 0x00, 0x82, 0x1f, 0x7f, 0x0f, 0xff, 0x82, 0x7f, 0x1f, 0x11, 0x00, 0x81, 0xfc, 0x05, 0xff, 0x87, 0xfc, 0x80, 0xf1, 0xff, 0xe1, 0xc0, 0xfe, 0x05, 0xff, 0x81, 0xf8, 0x4c, 0x00, 0x82, 0x01, 0x03, 0x09, 0x07, 0x82, 0x03, 0x01, 0x15, 0x00, 0x81, 0x01, 0x0f, 0x03, 0x81, 0x01, 0x30, 0x00, 0x81, 0xf8, 0x03, 0x08, 0x84, 0x10, 0xe0, 0x00, 0x00, 0x04, 0x40, 0x84, 0x80, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x83, 0x80, 0x00, 0x80, 0x03, 0x40, 0x84, 0x80, 0xc0, 0x00, 0x80, 0x04, 0x40, 0x84, 0x80, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x81, 0x80, 0x03, 0x00, 0x81, 0xf8, 0x0b, 0x00, 0x81, 0xf0, 0x04, 0x08, 0x83, 0x30, 0x00, 0x00, 0x04, 0x40, 0x83, 0x80, 0x00, 0xc0, 0x04, 0x00, 0x84, 0xc0, 0x00, 0x40, 0xf0, 0x03, 0x40, 0x03, 0x00, 0x83, 0x40, 0x40, 0xd8, 0x03, 0x00, 0x81, 0x80, 0x04, 0x40, 0x84, 0x80, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x81, 0x80, 0x03, 0x00, 0x81, 0xf8, 0x14, 0x00, 0x81, 0x1f, 0x03, 0x10, 0x84, 0x08, 0x07, 0x00, 0x0e, 0x03, 0x11, 0x84, 0x09, 0x1f, 0x00, 0x1f, 0x04, 0x00, 0x83, 0x1f, 0x00, 0x47, 0x03, 0x88, 0x84, 0x84, 0x7f, 0x00, 0x0f, 0x04, 0x12, 0x83, 0x13, 0x00, 0x1f, 0x08, 0x00, 0x81, 0x1b, 0x0b, 0x00, 0x81, 0x0f, 0x04, 0x10, 0x83, 0x0c, 0x00, 0x0e, 0x03, 0x11, 0x84, 0x09, 0x1f, 0x00, 0x0f, 0x03, 0x10, 0x85, 0x08, 0x1f, 0x00, 0x00, 0x0f, 0x03, 0x10, 0x81, 0x08, 0x04, 0x00, 0x81, 0x1f, 0x03, 0x00, 0x81, 0x0f, 0x04, 0x10, 0x83, 0x0f, 0x00, 0x1f, 0x04, 0x00, 0x81, 0x1f, 0x03, 0x00, 0x81, 0x1b, 0x7f, 0x00, 0x0c, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char screen_upgrading[190] = {
|
||||
0x7f, 0x00, 0x33, 0x00, 0x82, 0xe0, 0xe0, 0x11, 0x00, 0x07, 0x80, 0x66, 0x00, 0x82, 0xff, 0xff, 0x06, 0x00, 0x92, 0x80, 0xc0, 0xe0, 0xf0, 0xe0, 0xc0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0x7f, 0x3f, 0x1f, 0x0f, 0x1f, 0x1f, 0x66, 0x00, 0x8d, 0xff, 0xff, 0x00, 0x10, 0x38, 0x7c, 0x3e, 0x1f, 0x0f, 0x07, 0x03, 0x03, 0x07, 0x03, 0x0f, 0x83, 0x07, 0x03, 0x01, 0x6d, 0x00, 0x1b, 0x03, 0x54, 0x00, 0x81, 0xf8, 0x04, 0x00, 0x84, 0xf8, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x83, 0x80, 0x00, 0x80, 0x03, 0x40, 0x85, 0x80, 0xc0, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x83, 0x80, 0x00, 0x00, 0x04, 0x40, 0x83, 0x80, 0x00, 0x80, 0x03, 0x40, 0x87, 0x80, 0xf8, 0x00, 0x00, 0x40, 0x40, 0xd8, 0x03, 0x00, 0x82, 0xc0, 0x80, 0x03, 0x40, 0x83, 0x80, 0x00, 0x80, 0x03, 0x40, 0x82, 0x80, 0xc0, 0x42, 0x00, 0x81, 0x0f, 0x04, 0x10, 0x84, 0x0f, 0x00, 0x7f, 0x04, 0x03, 0x08, 0x83, 0x07, 0x00, 0x47, 0x03, 0x88, 0x84, 0x84, 0x7f, 0x00, 0x1f, 0x06, 0x00, 0x81, 0x0e, 0x03, 0x11, 0x84, 0x09, 0x1f, 0x00, 0x0f, 0x03, 0x10, 0x82, 0x08, 0x1f, 0x04, 0x00, 0x81, 0x1f, 0x03, 0x00, 0x81, 0x1f, 0x04, 0x00, 0x83, 0x1f, 0x00, 0x47, 0x03, 0x88, 0x82, 0x84, 0x7f, 0x7f, 0x00, 0x22, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char screen_replug[97] = {
|
||||
0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x2f, 0x00, 0x81, 0xf8, 0x04, 0x08, 0x83, 0xf0, 0x00, 0x80, 0x04, 0x40, 0x84, 0x80, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x84, 0x80, 0x00, 0x00, 0xf8, 0x05, 0x00, 0x81, 0xc0, 0x04, 0x00, 0x83, 0xc0, 0x00, 0x80, 0x03, 0x40, 0x82, 0x80, 0xc0, 0x57, 0x00, 0x88, 0x1f, 0x01, 0x03, 0x05, 0x09, 0x10, 0x00, 0x0f, 0x04, 0x12, 0x84, 0x13, 0x00, 0x7f, 0x04, 0x03, 0x08, 0x84, 0x07, 0x00, 0x00, 0x0f, 0x03, 0x10, 0x83, 0x00, 0x00, 0x0f, 0x03, 0x10, 0x84, 0x08, 0x1f, 0x00, 0x47, 0x03, 0x88, 0x82, 0x84, 0x7f, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x2e, 0x00, 0x00
|
||||
};
|
||||
|
||||
38
stm32/mk4-bootloader/assets/screens.h
Normal file
38
stm32/mk4-bootloader/assets/screens.h
Normal file
@ -0,0 +1,38 @@
|
||||
// autogenerated by assets/convert.py
|
||||
|
||||
|
||||
extern const unsigned char screen_verify[];
|
||||
|
||||
|
||||
extern const unsigned char screen_blankish[];
|
||||
|
||||
|
||||
extern const unsigned char screen_fatal[];
|
||||
|
||||
|
||||
extern const unsigned char screen_mitm[];
|
||||
|
||||
|
||||
extern const unsigned char screen_brick[];
|
||||
|
||||
|
||||
extern const unsigned char screen_dfu[];
|
||||
|
||||
|
||||
extern const unsigned char screen_downgrade[];
|
||||
|
||||
|
||||
extern const unsigned char screen_corrupt[];
|
||||
|
||||
|
||||
extern const unsigned char screen_logout[];
|
||||
|
||||
|
||||
extern const unsigned char screen_devmode[];
|
||||
|
||||
|
||||
extern const unsigned char screen_upgrading[];
|
||||
|
||||
|
||||
extern const unsigned char screen_replug[];
|
||||
|
||||
1
stm32/mk4-bootloader/assets/zevv-peep-iso8859-15-07x14.bdf
Symbolic link
1
stm32/mk4-bootloader/assets/zevv-peep-iso8859-15-07x14.bdf
Symbolic link
@ -0,0 +1 @@
|
||||
../../bootloader/assets/zevv-peep-iso8859-15-07x14.bdf
|
||||
1
stm32/mk4-bootloader/assets/zevv-peep-iso8859-15-07x14.pbm
Symbolic link
1
stm32/mk4-bootloader/assets/zevv-peep-iso8859-15-07x14.pbm
Symbolic link
@ -0,0 +1 @@
|
||||
../../bootloader/assets/zevv-peep-iso8859-15-07x14.pbm
|
||||
1
stm32/mk4-bootloader/assets/zevv-peep-iso8859-15-07x14.pil
Symbolic link
1
stm32/mk4-bootloader/assets/zevv-peep-iso8859-15-07x14.pil
Symbolic link
@ -0,0 +1 @@
|
||||
../../bootloader/assets/zevv-peep-iso8859-15-07x14.pil
|
||||
1
stm32/mk4-bootloader/assets/zevv-peep-iso8859-15-10x20.bdf
Symbolic link
1
stm32/mk4-bootloader/assets/zevv-peep-iso8859-15-10x20.bdf
Symbolic link
@ -0,0 +1 @@
|
||||
../../bootloader/assets/zevv-peep-iso8859-15-10x20.bdf
|
||||
1
stm32/mk4-bootloader/assets/zevv-peep-iso8859-15-10x20.pbm
Symbolic link
1
stm32/mk4-bootloader/assets/zevv-peep-iso8859-15-10x20.pbm
Symbolic link
@ -0,0 +1 @@
|
||||
../../bootloader/assets/zevv-peep-iso8859-15-10x20.pbm
|
||||
1
stm32/mk4-bootloader/assets/zevv-peep-iso8859-15-10x20.pil
Symbolic link
1
stm32/mk4-bootloader/assets/zevv-peep-iso8859-15-10x20.pil
Symbolic link
@ -0,0 +1 @@
|
||||
../../bootloader/assets/zevv-peep-iso8859-15-10x20.pil
|
||||
71
stm32/mk4-bootloader/basics.h
Normal file
71
stm32/mk4-bootloader/basics.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
extern void fatal_error(const char *) __attribute__((noreturn));
|
||||
|
||||
// I don't like the ususal assert macro, so here is my replacement,
|
||||
// and BTW: "assert()" is actually a macro, so it should
|
||||
// always be capitalized.
|
||||
//
|
||||
#undef assert
|
||||
#undef ASSERT
|
||||
#ifndef NDEBUG
|
||||
|
||||
//#define ASSERT(x) do { if(!(x)) { asm("BKPT #0");} } while(0)
|
||||
#ifdef RELEASE
|
||||
# define ASSERT(x) do { if(!(x)) { fatal_error("assert");} } while(0)
|
||||
#else
|
||||
# define ASSERT(x) do { if(!(x)) { fatal_error(#x);} } while(0)
|
||||
#endif
|
||||
|
||||
#else
|
||||
#error "Asserts disabled; not allowed"
|
||||
#define ASSERT(x)
|
||||
#endif
|
||||
|
||||
// Use anywhere. Will just crash on production, but useful in dev.
|
||||
#ifndef RELEASE
|
||||
#define BREAKPOINT asm("BKPT #0")
|
||||
#else
|
||||
#define BREAKPOINT #error
|
||||
#endif
|
||||
|
||||
// An assertion that we will be checked at *compile* time. Useful GCC feature.
|
||||
#define STATIC_ASSERT(cond) _Static_assert(cond, #cond)
|
||||
|
||||
// Similarly: for those times when you want to write ASSERT(False),
|
||||
// use this instead to provide a msg and abort. Altho the msg isn't
|
||||
// in the binary, it's still helpful when looking at source via debugger.
|
||||
//
|
||||
// CAUTION: some security checks end up here, so we want to always crash
|
||||
// in those cases.
|
||||
//
|
||||
//#define INCONSISTENT(x) do { asm("BKPT #0"); while(1); } while(0)
|
||||
#define INCONSISTENT(x) fatal_error("incon")
|
||||
|
||||
// Wait for an interrupt which will never happen (ie. die)
|
||||
#define LOCKUP_FOREVER() while(1) { __WFI(); }
|
||||
|
||||
// Like "sizeof()" but works on arrays, and returns the "numberof" elements.
|
||||
//
|
||||
#define numberof(x) (sizeof(x)/sizeof((x)[0]))
|
||||
|
||||
// This is an old favourite with dangerous programers...
|
||||
//
|
||||
#ifndef offsetof
|
||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
|
||||
#define msizeof(TYPE, MEMBER) sizeof(((TYPE *)0)->MEMBER)
|
||||
|
||||
// Handy macros.
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
#define MAX(a,b) (((a)>(b))?(a):(b))
|
||||
#define CLAMP(x,mn,mx) (((x)>(mx))?(mx):( ((x)<(mn)) ? (mn) : (x)))
|
||||
#define SGN(x) (((x)<0)?-1:(((x)>0)?1:0))
|
||||
#define ABS(x) (((x)<0)?-(x):(x))
|
||||
|
||||
137
stm32/mk4-bootloader/clocks.c
Normal file
137
stm32/mk4-bootloader/clocks.c
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 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 "basics.h"
|
||||
#include "clocks.h"
|
||||
#include "stm32l4xx_hal.h"
|
||||
|
||||
// HSE is used and is 8MHz
|
||||
// - not doing a complete setup here, but enough
|
||||
// - expect mainline code to refine this more
|
||||
// - this chip is well made and can handle later changes to clocks
|
||||
|
||||
#define CKCC_CLK_PLLN (40)
|
||||
#define CKCC_CLK_PLLM (2)
|
||||
#define CKCC_CLK_PLLR (2)
|
||||
#define CKCC_CLK_PLLP (7)
|
||||
#define CKCC_CLK_PLLQ (4)
|
||||
|
||||
// expected value of HAL_RCC_GetHCLKFreq(): 80Mhz
|
||||
#define HCLK_FREQUENCY 80000000
|
||||
|
||||
// systick_setup()
|
||||
//
|
||||
void
|
||||
systick_setup(void)
|
||||
{
|
||||
const uint32_t ticks = HCLK_FREQUENCY/1000;
|
||||
|
||||
SysTick->LOAD = (ticks - 1);
|
||||
SysTick->VAL = 0;
|
||||
SysTick->CTRL = SYSTICK_CLKSOURCE_HCLK | SysTick_CTRL_ENABLE_Msk;
|
||||
}
|
||||
|
||||
// clocks_setup()
|
||||
//
|
||||
void
|
||||
clocks_setup(void)
|
||||
{
|
||||
RCC_ClkInitTypeDef RCC_ClkInitStruct;
|
||||
RCC_OscInitTypeDef RCC_OscInitStruct;
|
||||
|
||||
// Configure LSE Drive Capability
|
||||
__HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW);
|
||||
|
||||
// Enable HSE Oscillator and activate PLL with HSE as source
|
||||
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
|
||||
|
||||
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
|
||||
RCC_OscInitStruct.LSEState = RCC_LSE_OFF;
|
||||
RCC_OscInitStruct.MSIState = RCC_MSI_OFF;
|
||||
|
||||
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
|
||||
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
|
||||
|
||||
// Select PLL as system clock source and configure
|
||||
// the HCLK, PCLK1 and PCLK2 clocks dividers
|
||||
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK
|
||||
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
|
||||
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
|
||||
|
||||
RCC_OscInitStruct.PLL.PLLM = CKCC_CLK_PLLM;
|
||||
RCC_OscInitStruct.PLL.PLLN = CKCC_CLK_PLLN;
|
||||
RCC_OscInitStruct.PLL.PLLP = CKCC_CLK_PLLP;
|
||||
RCC_OscInitStruct.PLL.PLLQ = CKCC_CLK_PLLQ;
|
||||
RCC_OscInitStruct.PLL.PLLR = CKCC_CLK_PLLR;
|
||||
|
||||
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
|
||||
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
|
||||
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
|
||||
|
||||
HAL_RCC_OscConfig(&RCC_OscInitStruct);
|
||||
|
||||
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
|
||||
|
||||
// DIS-able MSI-Hardware auto calibration mode with LSE
|
||||
CLEAR_BIT(RCC->CR, RCC_CR_MSIPLLEN);
|
||||
|
||||
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
|
||||
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI1|RCC_PERIPHCLK_I2C1
|
||||
|RCC_PERIPHCLK_USB |RCC_PERIPHCLK_ADC
|
||||
|RCC_PERIPHCLK_RNG |RCC_PERIPHCLK_RTC;
|
||||
|
||||
PeriphClkInitStruct.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
|
||||
// PLLSAI is used to clock USB, ADC, I2C1 and RNG. The frequency is
|
||||
// HSE(8MHz)/PLLM(2)*PLLSAI1N(24)/PLLSAIQ(2) = 48MHz.
|
||||
//
|
||||
PeriphClkInitStruct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI1;
|
||||
PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1;
|
||||
PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_PLLSAI1;
|
||||
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
|
||||
PeriphClkInitStruct.RngClockSelection = RCC_RNGCLKSOURCE_PLLSAI1;
|
||||
|
||||
PeriphClkInitStruct.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_HSE;
|
||||
PeriphClkInitStruct.PLLSAI1.PLLSAI1M = 2;
|
||||
PeriphClkInitStruct.PLLSAI1.PLLSAI1N = 24;
|
||||
PeriphClkInitStruct.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7;
|
||||
PeriphClkInitStruct.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2;
|
||||
PeriphClkInitStruct.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2;
|
||||
PeriphClkInitStruct.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_SAI1CLK
|
||||
|RCC_PLLSAI1_48M2CLK
|
||||
|RCC_PLLSAI1_ADC1CLK;
|
||||
|
||||
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
|
||||
|
||||
__HAL_RCC_RTC_ENABLE();
|
||||
__HAL_RCC_HASH_CLK_ENABLE();
|
||||
|
||||
// setup SYSTICK, but we don't have the irq hooked up and not using HAL
|
||||
systick_setup();
|
||||
}
|
||||
|
||||
// EOF
|
||||
10
stm32/mk4-bootloader/clocks.h
Normal file
10
stm32/mk4-bootloader/clocks.h
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// call once at startup
|
||||
void clocks_setup(void);
|
||||
|
||||
// the 1ms systick value. call anytime
|
||||
void systick_setup(void);
|
||||
@ -5,17 +5,13 @@
|
||||
*
|
||||
*/
|
||||
#include "basics.h"
|
||||
#include "console.h"
|
||||
#include "stm32l4xx_hal.h"
|
||||
#include <string.h>
|
||||
|
||||
#undef putchar
|
||||
|
||||
// mk4 has USART1 on header pins: RGT = Rx Gnd Tx
|
||||
#define MY_UART USART1
|
||||
|
||||
// (ms) timeout for tx (disable code)
|
||||
#define TX_TIMEOUT HAL_MAX_DELAY
|
||||
|
||||
static const char hexmap[16] = "0123456789abcdef";
|
||||
static const char *CRLF = "\r\n";
|
||||
|
||||
@ -68,7 +64,7 @@ puthex4(uint16_t w)
|
||||
puts2(const char *msg)
|
||||
{
|
||||
// output string with NO newline.
|
||||
HAL_USART_Transmit(&con, (uint8_t *)msg, strlen(msg), TX_TIMEOUT);
|
||||
HAL_USART_Transmit(&con, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
|
||||
@ -96,23 +92,40 @@ putchar(int c)
|
||||
uint8_t cb = c;
|
||||
|
||||
if(cb != '\n') {
|
||||
HAL_USART_Transmit(&con, &cb, 1, TX_TIMEOUT);
|
||||
HAL_USART_Transmit(&con, &cb, 1, HAL_MAX_DELAY);
|
||||
} else {
|
||||
HAL_USART_Transmit(&con, (uint8_t *)CRLF, 2, TX_TIMEOUT);
|
||||
HAL_USART_Transmit(&con, (uint8_t *)CRLF, 2, HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
// puts()
|
||||
//
|
||||
int
|
||||
puts(const char *msg)
|
||||
{
|
||||
HAL_USART_Transmit(&con, (uint8_t *)msg, strlen(msg), TX_TIMEOUT);
|
||||
HAL_USART_Transmit(&con, (uint8_t *)CRLF, 2, TX_TIMEOUT);
|
||||
int ln = strlen(msg);
|
||||
if(ln) HAL_USART_Transmit(&con, (uint8_t *)msg, ln, HAL_MAX_DELAY);
|
||||
HAL_USART_Transmit(&con, (uint8_t *)CRLF, 2, HAL_MAX_DELAY);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// getchar()
|
||||
//
|
||||
int
|
||||
getchar(void)
|
||||
{
|
||||
uint8_t rv = 0;
|
||||
|
||||
HAL_USART_Receive(&con, &rv, 1, HAL_MAX_DELAY);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// is_print()
|
||||
//
|
||||
static inline bool
|
||||
is_print(uint8_t c)
|
||||
{
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
#include "basics.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void console_setup(void);
|
||||
|
||||
@ -13,10 +14,21 @@ void puthex4(uint16_t w);
|
||||
// put hex onto the end of a string. output length always 2*len + nul
|
||||
void strcat_hex(char *msg, const void *d, int len);
|
||||
|
||||
// my versions, being careful not to pull in FILE and other stdio.h parts
|
||||
#undef puts
|
||||
int puts(const char *msg);
|
||||
#undef putchar
|
||||
int putchar(int c);
|
||||
|
||||
// like puts() but without newline
|
||||
void puts2(const char *msg);
|
||||
|
||||
// Print out a standard hex dump, with relative offsets
|
||||
//
|
||||
void hex_dump(const void *data, int len);
|
||||
|
||||
// Blocking read.
|
||||
#undef getchar
|
||||
int getchar(void);
|
||||
|
||||
|
||||
// EOF
|
||||
|
||||
79
stm32/mk4-bootloader/constant_time.c
Normal file
79
stm32/mk4-bootloader/constant_time.c
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#include "constant_time.h"
|
||||
|
||||
// check_all_ones()
|
||||
//
|
||||
// Return T if all bytes are 0xFF
|
||||
//
|
||||
bool
|
||||
check_all_ones(const void *ptrV, int len)
|
||||
{
|
||||
uint8_t rv = 0xff;
|
||||
const uint8_t *ptr = (const uint8_t *)ptrV;
|
||||
|
||||
for(; len; len--, ptr++) {
|
||||
rv &= *ptr;
|
||||
}
|
||||
|
||||
return (rv == 0xff);
|
||||
}
|
||||
|
||||
// check_all_zeros()
|
||||
//
|
||||
// Return T if all bytes are 0x00
|
||||
//
|
||||
bool
|
||||
check_all_zeros(const void *ptrV, int len)
|
||||
{
|
||||
uint8_t rv = 0x0;
|
||||
const uint8_t *ptr = (const uint8_t *)ptrV;
|
||||
|
||||
for(; len; len--, ptr++) {
|
||||
rv |= *ptr;
|
||||
}
|
||||
|
||||
return (rv == 0x00);
|
||||
}
|
||||
|
||||
// check_equal()
|
||||
//
|
||||
// Equality check.
|
||||
//
|
||||
bool
|
||||
check_equal(const void *aV, const void *bV, int len)
|
||||
{
|
||||
const uint8_t *left = (const uint8_t *)aV;
|
||||
const uint8_t *right = (const uint8_t *)bV;
|
||||
uint8_t diff = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
diff |= (left[i] ^ right[i]);
|
||||
}
|
||||
|
||||
return (diff == 0);
|
||||
}
|
||||
|
||||
#ifdef TEST_CODE
|
||||
// compile with:
|
||||
//
|
||||
// gcc -g -DTEST_CODE constant_time.c -o test && gdb ./test
|
||||
//
|
||||
#include <assert.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const uint8_t a0[13] = { 0 };
|
||||
const uint8_t a1[3] = { 0xff, 0xff, 0xff };
|
||||
const uint8_t a2[13] = { 0, 0x1, 0 };
|
||||
|
||||
assert(check_all_zeros(a0, sizeof(a0)));
|
||||
assert(check_all_ones(a1, sizeof(a1)));
|
||||
assert(!check_equal(a0, a2, sizeof(a2)));
|
||||
assert(check_equal(a0, a0, sizeof(a0)));
|
||||
}
|
||||
|
||||
#endif
|
||||
28
stm32/mk4-bootloader/constant_time.h
Normal file
28
stm32/mk4-bootloader/constant_time.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
//
|
||||
// Constant-time functions, useful for crypto checking.
|
||||
//
|
||||
|
||||
// Return T if all bytes are 0xff
|
||||
bool check_all_ones(const void *ptrV, int len);
|
||||
|
||||
// Return T if all bytes are 0x00
|
||||
bool check_all_zeros(const void *ptrV, int len);
|
||||
|
||||
// Equality check.
|
||||
bool check_equal(const void *aV, const void *bV, int len);
|
||||
|
||||
// XOR-mixin more bytes; acc = acc XOR more for each byte
|
||||
void static inline xor_mixin(uint8_t *acc, const uint8_t *more, int len)
|
||||
{
|
||||
for(; len; len--, more++, acc++) {
|
||||
*(acc) ^= *(more);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
45
stm32/mk4-bootloader/delay.c
Normal file
45
stm32/mk4-bootloader/delay.c
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*
|
||||
* delay.c -- Software delay loops (we have no interrupts)
|
||||
*
|
||||
*/
|
||||
#include "basics.h"
|
||||
#include "delay.h"
|
||||
#include "stm32l4xx_hal.h"
|
||||
|
||||
// delay_ms()
|
||||
//
|
||||
void
|
||||
delay_ms(int ms)
|
||||
{
|
||||
// Clear the COUNTFLAG and reset value to zero
|
||||
SysTick->VAL = 0;
|
||||
//SysTick->CTRL;
|
||||
|
||||
// Wait for ticks to happen
|
||||
while(ms > 0) {
|
||||
if(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) {
|
||||
ms--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delay_us()
|
||||
//
|
||||
void
|
||||
delay_us(int us)
|
||||
{
|
||||
if(us > 1000) {
|
||||
// big round up
|
||||
delay_ms((us + 500) / 1000);
|
||||
|
||||
} else {
|
||||
// XXX calibrate this
|
||||
for(volatile int i=0; i<(10000*us); i++) {
|
||||
__NOP();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EOF
|
||||
9
stm32/mk4-bootloader/delay.h
Normal file
9
stm32/mk4-bootloader/delay.h
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// Software delay loops (we have no interrupts)
|
||||
|
||||
void delay_ms(int ms);
|
||||
void delay_us(int us);
|
||||
815
stm32/mk4-bootloader/dispatch.c
Normal file
815
stm32/mk4-bootloader/dispatch.c
Normal file
@ -0,0 +1,815 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*
|
||||
* dispatch.c
|
||||
*
|
||||
* This code runs in an area of flash protected from viewing. It has limited entry
|
||||
* point (via a special callgate) and checks state carefully before running other stuff.
|
||||
*
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "basics.h"
|
||||
#include "misc.h"
|
||||
#include "console.h"
|
||||
#include "faster_sha256.h"
|
||||
#include "version.h"
|
||||
#include "clocks.h"
|
||||
#include "oled.h"
|
||||
#include "delay.h"
|
||||
#include "rng.h"
|
||||
#include "gpio.h"
|
||||
#include "ae.h"
|
||||
#include "pins.h"
|
||||
#include "verify.h"
|
||||
#include "storage.h"
|
||||
#include "sflash.h"
|
||||
#include "psram.h"
|
||||
#include "dispatch.h"
|
||||
#include "constant_time.h"
|
||||
#include "assets/screens.h"
|
||||
#include "stm32l4xx_hal.h"
|
||||
|
||||
// This magic value indicates we should go direct into DFU on this reboot.
|
||||
// Arbitrary SRAM1 location, random magic values. Also what screen to show.
|
||||
static const char *REBOOT_TO_DFU = "Boot2DFU";
|
||||
typedef struct {
|
||||
char magic[8];
|
||||
const uint8_t *screen;
|
||||
} dfu_flag_t;
|
||||
#define dfu_flag ((dfu_flag_t *)0x20008000)
|
||||
|
||||
// reboot_seed_setup()
|
||||
//
|
||||
// We need to know when we are rebooted, so write some noise
|
||||
// into SRAM and lock its value. Not secrets. One page = 1k bytes here.
|
||||
//
|
||||
void
|
||||
reboot_seed_setup(void)
|
||||
{
|
||||
#warning "re-enable sram protection"
|
||||
#if 0
|
||||
extern uint8_t reboot_seed_base[1024]; // see link-script.ld
|
||||
|
||||
// lots of manual memory alloc here...
|
||||
uint8_t *reboot_seed = &reboot_seed_base[0]; // 32 bytes
|
||||
coldcardFirmwareHeader_t *hdr_copy = (void *)&reboot_seed_base[32];
|
||||
uint32_t *boot_flags = (uint32_t *)RAM_BOOT_FLAGS;
|
||||
|
||||
// can only do this once, and might be done already
|
||||
if(SYSCFG->SWPR != (1<<31)) {
|
||||
ASSERT(((uint32_t)reboot_seed) == 0x10007c00);
|
||||
ASSERT(((uint32_t)hdr_copy) == RAM_HEADER_BASE);
|
||||
|
||||
// populate seed w/ noise
|
||||
memset(reboot_seed, 0x55, 1024);
|
||||
rng_buffer(reboot_seed, 32);
|
||||
|
||||
// preserve a copy of the verified FW header
|
||||
memcpy(hdr_copy, FW_HDR, sizeof(coldcardFirmwareHeader_t));
|
||||
|
||||
// document how we booted.
|
||||
uint32_t fl = 0;
|
||||
if(!flash_is_security_level2()) {
|
||||
fl |= RBF_FACTORY_MODE;
|
||||
}
|
||||
if(sf_completed_upgrade == SF_COMPLETED_UPGRADE) {
|
||||
fl |= RBF_FRESH_VERSION;
|
||||
}
|
||||
*boot_flags = fl;
|
||||
|
||||
// lock it (top most page = 1k bytes)
|
||||
SYSCFG->SWPR = (1<<31);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// memset4()
|
||||
//
|
||||
static inline void
|
||||
memset4(uint32_t *dest, uint32_t value, uint32_t byte_len)
|
||||
{
|
||||
for(; byte_len; byte_len-=4, dest++) {
|
||||
*dest = value;
|
||||
}
|
||||
}
|
||||
|
||||
// wipe_all_sram()
|
||||
//
|
||||
static void
|
||||
wipe_all_sram(void)
|
||||
{
|
||||
const uint32_t noise = 0xdeadbeef;
|
||||
|
||||
// strange omission from HAL headers
|
||||
#define SRAM3_BASE ((uint32_t)0x20040000U)
|
||||
#define SRAM3_SIZE ((uint32_t)0x00060000U)
|
||||
|
||||
// wipe all of SRAM (except our own memory, which was already wiped)
|
||||
memset4((void *)(SRAM1_BASE+BL_SRAM_SIZE), noise, SRAM1_SIZE_MAX - BL_SRAM_SIZE);
|
||||
memset4((void *)SRAM2_BASE, noise, SRAM2_SIZE);
|
||||
memset4((void *)SRAM3_BASE, noise, SRAM3_SIZE);
|
||||
}
|
||||
|
||||
// system_startup()
|
||||
//
|
||||
// Called only on system boot.
|
||||
//
|
||||
void
|
||||
system_startup(void)
|
||||
{
|
||||
// configure clocks first
|
||||
clocks_setup();
|
||||
|
||||
|
||||
#if RELEASE
|
||||
// security check: should we be in protected mode? Was there some UV-C bitrot perhaps?
|
||||
if(!check_all_ones(rom_secrets->bag_number, sizeof(rom_secrets->bag_number))
|
||||
&& !flash_is_security_level2()
|
||||
) {
|
||||
// yikes. recovery: do lockdown... we should be/(thought we were) locked already
|
||||
flash_lockdown_hard(OB_RDP_LEVEL_2);
|
||||
}
|
||||
#else
|
||||
# warning "Built for debug."
|
||||
#endif
|
||||
|
||||
// config pins
|
||||
gpio_setup();
|
||||
|
||||
//while(1) HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_7); // SD Active
|
||||
|
||||
// debug output and banner
|
||||
console_setup();
|
||||
|
||||
|
||||
puts2("\r\n\nMk4 Bootloader: ");
|
||||
puts(version_string);
|
||||
|
||||
sha256_selftest();
|
||||
|
||||
#if 0
|
||||
// workaround to get into DFU from micropython
|
||||
// LATER: none of this is useful with RDP=2
|
||||
if(memcmp(dfu_flag->magic, REBOOT_TO_DFU, sizeof(dfu_flag->magic)) == 0) {
|
||||
dfu_flag->magic[0] = 0;
|
||||
|
||||
// still see a flash here, but that's proof it works.
|
||||
oled_setup();
|
||||
oled_show(dfu_flag->screen);
|
||||
|
||||
enter_dfu();
|
||||
// NOT-REACHED
|
||||
}
|
||||
#endif
|
||||
|
||||
// clear and setup OLED display
|
||||
oled_setup();
|
||||
oled_show_progress(screen_verify, 0);
|
||||
|
||||
// won't always need it, but enable RNG anyway
|
||||
rng_setup();
|
||||
|
||||
puts2("RNG setup done: ");
|
||||
puthex4(rng_sample());
|
||||
putchar('\n');
|
||||
|
||||
// wipe all of SRAM (except our own memory, which was already wiped)
|
||||
wipe_all_sram();
|
||||
|
||||
puts("AE setup start");
|
||||
// secure element setup
|
||||
ae_setup();
|
||||
ae_set_gpio(0); // not checking return on purpose
|
||||
|
||||
puts("AE setup done");
|
||||
|
||||
// protect our flash, and/or check it's protected
|
||||
// - and pick pairing secret if we don't already have one
|
||||
// - may also do one-time setup of 508a
|
||||
// - note: ae_setup must already be called, since it can talk to that
|
||||
flash_setup();
|
||||
|
||||
puts("flash setup done");
|
||||
// escape into DFU
|
||||
if(dfu_button_pressed()) dfu_by_request();
|
||||
|
||||
// maybe upgrade to a firmware image found in sflash
|
||||
sf_firmware_upgrade();
|
||||
|
||||
puts("psram setup");
|
||||
psram_setup();
|
||||
|
||||
puts("verify");
|
||||
// SLOW part: check firmware is legit; else enter DFU
|
||||
// - may die due to downgrade attack or unsigned/badly signed image
|
||||
verify_firmware();
|
||||
|
||||
// .. for slow people, check again; last chance
|
||||
if(dfu_button_pressed()) dfu_by_request();
|
||||
|
||||
// track reboots, capture firmware hdr used
|
||||
// - must be near end of boot process, ie: here.
|
||||
reboot_seed_setup();
|
||||
|
||||
// load a blank screen, so that if the firmware crashes, we are showing
|
||||
// something reasonable and not misleading.
|
||||
oled_show(screen_blankish);
|
||||
}
|
||||
|
||||
// fatal_error(const char *msg)
|
||||
//
|
||||
void
|
||||
fatal_error(const char *msgvoid)
|
||||
{
|
||||
oled_setup();
|
||||
oled_show(screen_fatal);
|
||||
|
||||
#ifndef RELEASE
|
||||
puts2("\r\n\nAssert fail: ");
|
||||
puts(msgvoid);
|
||||
BREAKPOINT;
|
||||
#endif
|
||||
|
||||
// Maybe should do a reset after a delay, like with
|
||||
// the watchdog timer or something.
|
||||
LOCKUP_FOREVER();
|
||||
}
|
||||
|
||||
// fatal_mitm()
|
||||
//
|
||||
void
|
||||
fatal_mitm(void)
|
||||
{
|
||||
oled_setup();
|
||||
oled_show(screen_mitm);
|
||||
|
||||
#ifdef RELEASE
|
||||
wipe_all_sram();
|
||||
#endif
|
||||
|
||||
LOCKUP_FOREVER();
|
||||
}
|
||||
|
||||
// dfu_by_request()
|
||||
//
|
||||
void
|
||||
dfu_by_request(void)
|
||||
{
|
||||
if(flash_is_security_level2()) {
|
||||
// cannot get into DFU when secure
|
||||
// so do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
oled_show(screen_dfu);
|
||||
enter_dfu();
|
||||
}
|
||||
|
||||
// enter_dfu()
|
||||
//
|
||||
void __attribute__((noreturn))
|
||||
enter_dfu(void)
|
||||
{
|
||||
const uint32_t noise = 0xDeadBeef;
|
||||
|
||||
puts("enter_dfu");
|
||||
|
||||
// clear the green light, if set
|
||||
ae_setup();
|
||||
ae_set_gpio(0);
|
||||
|
||||
// Reset huge parts of the chip
|
||||
__HAL_RCC_APB1_FORCE_RESET();
|
||||
__HAL_RCC_APB1_RELEASE_RESET();
|
||||
|
||||
__HAL_RCC_APB2_FORCE_RESET();
|
||||
__HAL_RCC_APB2_RELEASE_RESET();
|
||||
|
||||
__HAL_RCC_AHB1_FORCE_RESET();
|
||||
__HAL_RCC_AHB1_RELEASE_RESET();
|
||||
|
||||
#if 0
|
||||
// But not this; it borks things.
|
||||
__HAL_RCC_AHB2_FORCE_RESET();
|
||||
__HAL_RCC_AHB2_RELEASE_RESET();
|
||||
#endif
|
||||
|
||||
__HAL_RCC_AHB3_FORCE_RESET();
|
||||
__HAL_RCC_AHB3_RELEASE_RESET();
|
||||
|
||||
__HAL_FIREWALL_PREARM_ENABLE();
|
||||
|
||||
// Wipe all of memory SRAM, just in case
|
||||
// there is some way to trick us into DFU
|
||||
// after sensitive content in place.
|
||||
memset4((void *)SRAM1_BASE, noise, SRAM1_SIZE_MAX);
|
||||
memset4((void *)SRAM2_BASE, noise, SRAM2_SIZE - 1024); // avoid seed area
|
||||
|
||||
if(flash_is_security_level2()) {
|
||||
// cannot do DFU in RDP=2, so just die. Helps to preserve screen
|
||||
LOCKUP_FOREVER();
|
||||
}
|
||||
|
||||
// Reset clocks.
|
||||
HAL_RCC_DeInit();
|
||||
|
||||
// move system ROM into 0x0
|
||||
__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
|
||||
|
||||
// simulate a reset vector
|
||||
__ASM volatile ("movs r0, #0\n"
|
||||
"ldr r3, [r0, #0]\n"
|
||||
"msr msp, r3\n"
|
||||
"ldr r3, [r0, #4]\n"
|
||||
"blx r3"
|
||||
: : : "r0", "r3", "sp");
|
||||
|
||||
// NOT-REACHED.
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
// good_addr()
|
||||
//
|
||||
static int
|
||||
good_addr(const uint8_t *b, int minlen, int len, bool readonly)
|
||||
{
|
||||
uint32_t x = (uint32_t)b;
|
||||
|
||||
if(minlen) {
|
||||
if(!b) return EFAULT; // gave no buffer
|
||||
if(len < minlen) return ERANGE; // too small
|
||||
}
|
||||
|
||||
|
||||
if((x >= SRAM1_BASE) && ((x-SRAM1_BASE) < SRAM1_SIZE_MAX)) {
|
||||
// inside SRAM1, okay
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!readonly) {
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
if((x >= FIRMWARE_START) && (x - FIRMWARE_START) < FW_MAX_LENGTH) {
|
||||
// inside flash of main firmware (happens for QSTR's)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return EACCES;
|
||||
}
|
||||
|
||||
/*
|
||||
The callgate into the firewall...
|
||||
|
||||
From the reference manual:
|
||||
The “call gate” is composed of 3 words located on the first three
|
||||
32-bit addresses of the base address of the code segment and of the
|
||||
Volatile data segment if it is declared as not shared (VDS = 0) and
|
||||
executable (VDE = 1).
|
||||
|
||||
– 1st word: Dummy 32-bit words always closed in order to protect
|
||||
the “call gate” opening from an access due to a prefetch buffer.
|
||||
|
||||
– 2nd and 3rd words: 2 specific 32-bit words called “call gate” and always opened.
|
||||
|
||||
We are assuming the caller gives us a working C runtime: stack, arguments in
|
||||
registers and so on.
|
||||
*/
|
||||
|
||||
// firewall_dispatch()
|
||||
//
|
||||
// A C-runtime compatible env. is running, so do some work.
|
||||
//
|
||||
__attribute__ ((used))
|
||||
int
|
||||
firewall_dispatch(int method_num, uint8_t *buf_io, int len_in,
|
||||
uint32_t arg2, uint32_t incoming_sp, uint32_t incoming_lr)
|
||||
{
|
||||
|
||||
// from linker, offset of firewall entry
|
||||
extern uint32_t firewall_starts;
|
||||
|
||||
int rv = 0;
|
||||
|
||||
#if 0
|
||||
// TODO: re-enable this; causing crash now.
|
||||
// in case the caller didn't already, but would just lead to a crash anyway
|
||||
__disable_irq();
|
||||
#endif
|
||||
|
||||
// "1=any code executed outside the protected segment will close the Firewall"
|
||||
// "0=.. will reset the processor"
|
||||
__HAL_FIREWALL_PREARM_DISABLE();
|
||||
|
||||
// Important:
|
||||
// - range check pointers so we aren't tricked into revealing our secrets
|
||||
// - check buf_io points to main SRAM, and not into us!
|
||||
// - range check len_in tightly
|
||||
// - calling convention only gives me enough for 4 args to this function, so
|
||||
// using read/write in place.
|
||||
// - use arg2 use when a simple number is needed; never a pointer!
|
||||
// - mpy may provide a pointer to flash if we give it a qstr or small value, and if
|
||||
// we're reading only, that's fine.
|
||||
|
||||
if(len_in > 1024) { // arbitrary max, increase as needed
|
||||
rv = ERANGE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Use these macros
|
||||
#define REQUIRE_IN_ONLY(x) if((rv = good_addr(buf_io, (x), len_in, true))) { goto fail; }
|
||||
#define REQUIRE_OUT(x) if((rv = good_addr(buf_io, (x), len_in, false))) { goto fail; }
|
||||
|
||||
switch(method_num) {
|
||||
case 0: {
|
||||
REQUIRE_OUT(64);
|
||||
|
||||
// Return my version string
|
||||
memset(buf_io, 0, len_in);
|
||||
strlcpy((char *)buf_io, version_string, len_in);
|
||||
|
||||
rv = strlen(version_string);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: {
|
||||
// Perform SHA256 over ourselves, with 32-bits of salt, to imply we
|
||||
// haven't stored valid responses.
|
||||
REQUIRE_OUT(32);
|
||||
|
||||
SHA256_CTX ctx;
|
||||
sha256_init(&ctx);
|
||||
sha256_update(&ctx, (void *)&arg2, 4);
|
||||
sha256_update(&ctx, (void *)BL_FLASH_BASE, BL_FLASH_SIZE);
|
||||
sha256_final(&ctx, buf_io);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
const uint8_t *scr;
|
||||
bool secure = flash_is_security_level2();
|
||||
|
||||
// Go into DFU mode. It's a one-way trip.
|
||||
// Also used to show some "fatal" screens w/ memory wipe.
|
||||
|
||||
switch(arg2) {
|
||||
default:
|
||||
case 0:
|
||||
// enter DFU for firmware upgrades
|
||||
if(secure) {
|
||||
// we cannot support DFU in secure mode anymore
|
||||
rv = EPERM;
|
||||
goto fail;
|
||||
}
|
||||
scr = screen_dfu;
|
||||
break;
|
||||
case 1:
|
||||
// in case some way for Micropython to detect it.
|
||||
scr = screen_downgrade;
|
||||
break;
|
||||
case 2:
|
||||
scr = screen_blankish;
|
||||
break;
|
||||
case 3:
|
||||
scr = screen_brick;
|
||||
secure = true; // no point going into DFU, if even possible
|
||||
break;
|
||||
}
|
||||
|
||||
oled_setup();
|
||||
oled_show(scr);
|
||||
|
||||
wipe_all_sram();
|
||||
|
||||
if(secure) {
|
||||
// just die with that message shown; can't start DFU
|
||||
LOCKUP_FOREVER();
|
||||
} else {
|
||||
// Cannot just call enter_dfu() because it doesn't work well
|
||||
// once Micropython has configured so much stuff in the chip.
|
||||
|
||||
// Leave a reminder to ourselves
|
||||
memcpy(dfu_flag->magic, REBOOT_TO_DFU, sizeof(dfu_flag->magic));
|
||||
dfu_flag->screen = scr;
|
||||
|
||||
// reset system
|
||||
NVIC_SystemReset();
|
||||
|
||||
// NOT-REACHED
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
// logout: wipe all of memory and lock up. Must powercycle to recover.
|
||||
switch(arg2) {
|
||||
case 0:
|
||||
case 2:
|
||||
oled_show(screen_logout);
|
||||
break;
|
||||
case 1:
|
||||
// leave screen untouched
|
||||
break;
|
||||
}
|
||||
|
||||
wipe_all_sram();
|
||||
|
||||
if(arg2 == 2) {
|
||||
// need some time to show OLED contents
|
||||
delay_ms(100);
|
||||
|
||||
// reboot so we can "login" again
|
||||
NVIC_SystemReset();
|
||||
|
||||
// NOT-REACHED (but ok if it does)
|
||||
}
|
||||
|
||||
// wait for an interrupt which will never happen (ie. sleep)
|
||||
LOCKUP_FOREVER()
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// attempt to control the GPIO (won't work for 1)
|
||||
ae_setup();
|
||||
ae_keep_alive();
|
||||
switch(arg2) {
|
||||
default:
|
||||
case 0: // read state
|
||||
rv = ae_get_gpio();
|
||||
break;
|
||||
case 1: // clear it (can work anytime)
|
||||
rv = ae_set_gpio(0);
|
||||
break;
|
||||
case 2: // set it (will always fail)
|
||||
rv = ae_set_gpio(1);
|
||||
break;
|
||||
|
||||
case 3: { // do a verify and see if it maybe goes green
|
||||
uint8_t fw_digest[32], world_digest[32];
|
||||
|
||||
// takes time, shows progress bar
|
||||
checksum_flash(fw_digest, world_digest);
|
||||
|
||||
rv = ae_set_gpio_secure(world_digest);
|
||||
|
||||
oled_show(screen_blankish);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// Are we a brick?
|
||||
// if the pairing secret doesn't work anymore, that
|
||||
// means we've been bricked.
|
||||
// TODO: also report hardware issue, and non-configured states
|
||||
ae_setup();
|
||||
rv = (ae_pair_unlock() != 0);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
// Do we have a ATECC608a and all that implies?
|
||||
// NOTE: this number was unused in V1 bootroms, so return ENOENT
|
||||
#if FOR_608
|
||||
rv = 0;
|
||||
#else
|
||||
rv = ENOENT;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 12:
|
||||
// read the DFU button (used for selftest at least)
|
||||
REQUIRE_OUT(1);
|
||||
gpio_setup();
|
||||
buf_io[0] = dfu_button_pressed();
|
||||
break;
|
||||
|
||||
case 15: {
|
||||
// Read a dataslot directly. Will fail on
|
||||
// encrypted slots.
|
||||
if(len_in != 4 && len_in != 32 && len_in != 72) {
|
||||
rv = ERANGE;
|
||||
} else {
|
||||
REQUIRE_OUT(4);
|
||||
|
||||
ae_setup();
|
||||
if(ae_read_data_slot(arg2 & 0xf, buf_io, len_in)) {
|
||||
rv = EIO;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 16: {
|
||||
// Provide the 2 words for anti-phishing.
|
||||
REQUIRE_OUT(MAX_PIN_LEN);
|
||||
|
||||
// arg2: length of pin.
|
||||
if((arg2 < 1) || (arg2 > MAX_PIN_LEN)) {
|
||||
rv = ERANGE;
|
||||
} else {
|
||||
if(pin_prefix_words((char *)buf_io, arg2, (uint32_t *)buf_io)) {
|
||||
rv = EIO;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 17:
|
||||
// test rng
|
||||
REQUIRE_OUT(32);
|
||||
memset(buf_io, 0x55, 32); // to help show errors
|
||||
rng_buffer(buf_io, 32);
|
||||
break;
|
||||
|
||||
case 18: {
|
||||
// Try login w/ PIN.
|
||||
REQUIRE_OUT(PIN_ATTEMPT_SIZE_V2);
|
||||
pinAttempt_t *args = (pinAttempt_t *)buf_io;
|
||||
|
||||
switch(arg2) {
|
||||
case 0:
|
||||
rv = pin_setup_attempt(args);
|
||||
break;
|
||||
case 1:
|
||||
rv = pin_delay(args);
|
||||
break;
|
||||
case 2:
|
||||
rv = pin_login_attempt(args);
|
||||
break;
|
||||
case 3:
|
||||
rv = pin_change(args);
|
||||
break;
|
||||
case 4:
|
||||
rv = pin_fetch_secret(args);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
rv = pin_firmware_greenlight(args);
|
||||
break;
|
||||
|
||||
case 6: // new for v2
|
||||
rv = pin_long_secret(args);
|
||||
break;
|
||||
|
||||
default:
|
||||
rv = ENOENT;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case 19: { // bag number stuff
|
||||
switch(arg2) {
|
||||
case 0:
|
||||
// read out number
|
||||
REQUIRE_OUT(32);
|
||||
memcpy(buf_io, rom_secrets->bag_number, 32);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// set the bag number, and (should) do lock down
|
||||
REQUIRE_IN_ONLY(32);
|
||||
|
||||
flash_save_bag_number(buf_io);
|
||||
break;
|
||||
|
||||
case 100:
|
||||
flash_lockdown_hard(OB_RDP_LEVEL_0); // wipes contents of flash (1->0)
|
||||
break;
|
||||
case 101:
|
||||
flash_lockdown_hard(OB_RDP_LEVEL_1); // Can only do 0->1 (experiments)
|
||||
break;
|
||||
case 102:
|
||||
// production units will be:
|
||||
flash_lockdown_hard(OB_RDP_LEVEL_2); // No change possible after this.
|
||||
break;
|
||||
|
||||
default:
|
||||
rv = ENOENT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 20:
|
||||
// Read out entire config dataspace
|
||||
REQUIRE_OUT(128);
|
||||
|
||||
ae_setup();
|
||||
rv = ae_config_read(buf_io);
|
||||
if(rv) {
|
||||
rv = EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
case 21:
|
||||
// read OTP / downgrade protection
|
||||
switch(arg2) {
|
||||
case 0:
|
||||
REQUIRE_OUT(8);
|
||||
get_min_version(buf_io);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
REQUIRE_IN_ONLY(8);
|
||||
rv = check_is_downgrade(buf_io, NULL);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
REQUIRE_IN_ONLY(8);
|
||||
|
||||
if(buf_io[0] < 0x10 || buf_io[0] >= 0x40) {
|
||||
// bad data
|
||||
rv = ERANGE;
|
||||
} if(check_is_downgrade(buf_io, NULL)) {
|
||||
// already at a higher version?
|
||||
rv = EAGAIN;
|
||||
} else {
|
||||
uint8_t min[8];
|
||||
get_min_version(min);
|
||||
|
||||
if(memcmp(min, buf_io, 8) == 0) {
|
||||
// dupe
|
||||
rv = EAGAIN;
|
||||
} else {
|
||||
// save it, but might be "full" already
|
||||
if(record_highwater_version(buf_io)) {
|
||||
rv = ENOMEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// read raw counter0 value (max is 0x1fffff)
|
||||
REQUIRE_OUT(4);
|
||||
ae_setup();
|
||||
rv = ae_get_counter((uint32_t *)buf_io, 0) ? EIO: 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
rv = ENOENT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case -1:
|
||||
// System startup code. Cannot be reached by any code (that hopes to run
|
||||
// again) except our reset stub.
|
||||
if(incoming_lr <= BL_FLASH_BASE || incoming_lr >= (uint32_t)&firewall_starts) {
|
||||
fatal_error("LR");
|
||||
} else {
|
||||
system_startup();
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
rv = ENOENT;
|
||||
break;
|
||||
}
|
||||
#undef REQUIRE_IN_ONLY
|
||||
#undef REQUIRE_OUT
|
||||
|
||||
fail:
|
||||
|
||||
// Precaution: we don't want to leave ATECC508A authorized for any specific keys,
|
||||
// perhaps due to an error path we didn't see. Always reset the chip.
|
||||
ae_reset_chip();
|
||||
|
||||
// Unlikely it matters, but clear flash memory cache.
|
||||
__HAL_FLASH_DATA_CACHE_DISABLE();
|
||||
__HAL_FLASH_DATA_CACHE_RESET();
|
||||
__HAL_FLASH_DATA_CACHE_ENABLE();
|
||||
|
||||
// .. and instruction memory (flash cache too?)
|
||||
__HAL_FLASH_INSTRUCTION_CACHE_DISABLE();
|
||||
__HAL_FLASH_INSTRUCTION_CACHE_RESET();
|
||||
__HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
|
||||
|
||||
|
||||
// authorize return from firewall into user's code
|
||||
__HAL_FIREWALL_PREARM_ENABLE();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// HAL support garbage
|
||||
const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
|
||||
const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4};
|
||||
const uint32_t MSIRangeTable[12] = {100000, 200000, 400000, 800000, 1000000, 2000000, \
|
||||
4000000, 8000000, 16000000, 24000000, 32000000, 48000000};
|
||||
uint32_t SystemCoreClock;
|
||||
|
||||
// TODO: cleanup HAL stuff to not use this
|
||||
uint32_t HAL_GetTick(void) { return 53; }
|
||||
|
||||
// EOF
|
||||
10
stm32/mk4-bootloader/dispatch.h
Normal file
10
stm32/mk4-bootloader/dispatch.h
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// Go into DFU mode, and certainly clear things.
|
||||
void enter_dfu(void) __attribute__((noreturn));
|
||||
|
||||
// Start DFU, or return doing nothing if chip is secure (no DFU possible).
|
||||
void dfu_by_request(void);
|
||||
95
stm32/mk4-bootloader/enable.c
Normal file
95
stm32/mk4-bootloader/enable.c
Normal file
@ -0,0 +1,95 @@
|
||||
//
|
||||
// (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
//
|
||||
//
|
||||
// enable.c
|
||||
//
|
||||
// C-level code to enable the firewall feature. All **outside** the firewall.
|
||||
//
|
||||
#include <errno.h>
|
||||
#include "basics.h"
|
||||
#include "stm32l4xx_hal_firewall.h"
|
||||
#include "stm32l4xx_hal_rcc.h"
|
||||
#include "storage.h"
|
||||
#include "constant_time.h"
|
||||
|
||||
|
||||
// firewall_setup()
|
||||
//
|
||||
// It's best if this is outside the firewall. After we return, we'll
|
||||
// jump into setup code contained inside the firewall.
|
||||
//
|
||||
void
|
||||
firewall_setup(void)
|
||||
{
|
||||
// This is critical: without the clock enabled to "SYSCFG" we
|
||||
// can't tell the FW is enabled or not! Enabling it would also not work
|
||||
__HAL_RCC_SYSCFG_CLK_ENABLE();
|
||||
|
||||
if(__HAL_FIREWALL_IS_ENABLED()) {
|
||||
// After the first (POR) reset, the firewall may already be enabled, and if so
|
||||
// we can't change it anyway, so we're done.
|
||||
return;
|
||||
}
|
||||
|
||||
#if RELEASE
|
||||
// REMINDERS:
|
||||
// - cannot debug anything in boot loader w/ firewall enabled (no readback, no bkpt)
|
||||
// - when RDP=2, this protection still important or else python can read pairing secret
|
||||
// - in factory mode (RDP!=2), it's nice to have this disabled so we can debug still
|
||||
// - could look at RDP level here, but it would be harder to completely reset the bag number!
|
||||
if(check_all_ones(rom_secrets->bag_number, sizeof(rom_secrets->bag_number))) {
|
||||
// ok. still virgin unit -- run w/o security
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// for debug builds, never enable firewall
|
||||
return;
|
||||
#endif
|
||||
|
||||
extern int firewall_starts; // see startup.S ... aligned@256 (0x08000300)
|
||||
uint32_t start = (uint32_t)&firewall_starts;
|
||||
uint32_t len = BL_FLASH_SIZE - (start - BL_FLASH_BASE);
|
||||
|
||||
#if 0
|
||||
ASSERT(start);
|
||||
ASSERT(!(start & 0xff));
|
||||
ASSERT(len>256);
|
||||
ASSERT(!(len & 0xff));
|
||||
#endif
|
||||
|
||||
// NOTE: the "Non volatile data segment" is not executable, and so cannot
|
||||
// overlap the "Code Segment". Not clear why it's ever useful, but
|
||||
// maybe so you can prevent execution on that section, or have it somewhere
|
||||
// else I suppose.
|
||||
//
|
||||
// - many of the bits in these registers are not-implemented and are forced to zero
|
||||
// - that prevents the firewall being used for things like protecting OTP area
|
||||
// - volatile data is SRAM1 only, so doesn't help us, since we're using SRAM2
|
||||
// - on-chip DFU will erase up to start (0x300), which borks the reset vector
|
||||
// but sensitive stuff is still there (which would allow bypass)
|
||||
// - so it's important to enable option bytes to set write-protect on entire bootloader
|
||||
// - to disable debug and complete protection, must enable write-protect "level 2"
|
||||
//
|
||||
|
||||
FIREWALL_InitTypeDef init = {
|
||||
.CodeSegmentStartAddress = start,
|
||||
.CodeSegmentLength = len,
|
||||
.NonVDataSegmentStartAddress = BL_NVROM_BASE,
|
||||
.NonVDataSegmentLength = BL_NVROM_SIZE,
|
||||
.VDataSegmentStartAddress = 0,
|
||||
.VDataSegmentLength = 0,
|
||||
.VolatileDataExecution = 0,
|
||||
.VolatileDataShared = 0,
|
||||
};
|
||||
|
||||
int rv = HAL_FIREWALL_Config((FIREWALL_InitTypeDef *)&init);
|
||||
if(rv) {
|
||||
INCONSISTENT("fw");
|
||||
}
|
||||
|
||||
__HAL_FIREWALL_PREARM_DISABLE();
|
||||
HAL_FIREWALL_EnableFirewall();
|
||||
}
|
||||
|
||||
// EOF
|
||||
137
stm32/mk4-bootloader/faster_sha256.c
Normal file
137
stm32/mk4-bootloader/faster_sha256.c
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#include "basics.h"
|
||||
#include "console.h"
|
||||
#include "faster_sha256.h"
|
||||
#include "stm32l4xx_hal.h"
|
||||
#include <string.h>
|
||||
|
||||
void sha256_init(SHA256_CTX *ctx)
|
||||
{
|
||||
memset(ctx, 0, sizeof(SHA256_CTX));
|
||||
|
||||
ctx->num_pending = 0;
|
||||
ctx->hh.Init.DataType = HASH_DATATYPE_8B;
|
||||
|
||||
HAL_HASH_Init(&ctx->hh);
|
||||
}
|
||||
|
||||
void sha256_update(SHA256_CTX *ctx, const uint8_t data[], uint32_t len)
|
||||
{
|
||||
HAL_StatusTypeDef rv;
|
||||
|
||||
// clear out any pending bytes
|
||||
if(ctx->num_pending + len >= 4) {
|
||||
while(ctx->num_pending != 4) {
|
||||
ctx->pending[ctx->num_pending++] = *data;
|
||||
data += 1;
|
||||
len -= 1;
|
||||
if(!len) break;
|
||||
}
|
||||
if(ctx->num_pending == 4) {
|
||||
rv = HAL_HASHEx_SHA256_Accumulate(&ctx->hh, ctx->pending, 4);
|
||||
ASSERT(rv == HAL_OK);
|
||||
ctx->num_pending = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// write full blocks
|
||||
uint32_t blocks = len / 4;
|
||||
if(blocks) {
|
||||
rv = HAL_HASHEx_SHA256_Accumulate(&ctx->hh, (uint8_t *)data, blocks*4);
|
||||
ASSERT(rv == HAL_OK);
|
||||
len -= blocks*4;
|
||||
data += blocks*4;
|
||||
}
|
||||
|
||||
// save runt for later
|
||||
ASSERT(len <= 3);
|
||||
while(len) {
|
||||
ctx->pending[ctx->num_pending++] = *data;
|
||||
data++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
void sha256_final(SHA256_CTX *ctx, uint8_t digest[32])
|
||||
{
|
||||
// Do final 0-3 bytes, pad and return digest.
|
||||
HAL_StatusTypeDef rv = HAL_HASHEx_SHA256_Start(&ctx->hh,
|
||||
ctx->pending, ctx->num_pending, digest, HAL_MAX_DELAY);
|
||||
|
||||
ASSERT(rv == HAL_OK);
|
||||
}
|
||||
|
||||
// sha256_single()
|
||||
//
|
||||
// single-shot version (best)
|
||||
//
|
||||
void
|
||||
sha256_single(const uint8_t data[], uint32_t len, uint8_t digest[32])
|
||||
{
|
||||
HASH_HandleTypeDef hh = {0};
|
||||
|
||||
hh.Init.DataType = HASH_DATATYPE_8B;
|
||||
|
||||
HAL_HASH_Init(&hh);
|
||||
|
||||
// It's called "Start" but it handles the runt packet, so really can only
|
||||
// be used once at end of message, or for whole message.
|
||||
HAL_StatusTypeDef rv = HAL_HASHEx_SHA256_Start(&hh, (uint8_t *)data, len,
|
||||
digest, HAL_MAX_DELAY);
|
||||
ASSERT(rv == HAL_OK);
|
||||
}
|
||||
|
||||
|
||||
#ifndef RELEASE
|
||||
//#pragma GCC push_options
|
||||
//#pragma GCC optimize ("O0")
|
||||
|
||||
|
||||
void
|
||||
sha256_selftest(void)
|
||||
{
|
||||
SHA256_CTX ctx;
|
||||
uint8_t md[32], md2[32];
|
||||
|
||||
puts("sha256 selftest start");
|
||||
|
||||
sha256_single((uint8_t *)"a", 1, md);
|
||||
ASSERT(md[0] == 0xca);
|
||||
ASSERT(md[31] == 0xbb);
|
||||
|
||||
sha256_init(&ctx);
|
||||
sha256_update(&ctx, (uint8_t *)"a", 1);
|
||||
sha256_final(&ctx, md2);
|
||||
ASSERT(memcmp(md, md2, 32) == 0);
|
||||
|
||||
const uint8_t *pat = (const uint8_t *)BL_FLASH_BASE;
|
||||
|
||||
for(int len=1; len<96; len+=17) {
|
||||
sha256_single(pat, len, md);
|
||||
|
||||
for(int st=1; st<len; st++) {
|
||||
#if 0
|
||||
puts2("st = ");
|
||||
puthex2(st);
|
||||
puts2(" len = ");
|
||||
puthex2(len);
|
||||
#endif
|
||||
|
||||
sha256_init(&ctx);
|
||||
sha256_update(&ctx, pat, st);
|
||||
sha256_update(&ctx, pat+st, len-st);
|
||||
sha256_final(&ctx, md2);
|
||||
|
||||
ASSERT(memcmp(md, md2, 32) == 0);
|
||||
// puts(" ... PASS");
|
||||
}
|
||||
}
|
||||
|
||||
puts("sha256 selftest PASS");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// EOF
|
||||
28
stm32/mk4-bootloader/faster_sha256.h
Normal file
28
stm32/mk4-bootloader/faster_sha256.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include "stm32l4xx_hal.h"
|
||||
|
||||
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
|
||||
|
||||
// hardware state is the context, altho singleton
|
||||
typedef struct {
|
||||
HASH_HandleTypeDef hh;
|
||||
uint8_t pending[4], num_pending; // up to 3 bytes might be waiting from last call
|
||||
} SHA256_CTX;
|
||||
|
||||
// compatible API
|
||||
void sha256_init(SHA256_CTX *ctx);
|
||||
void sha256_update(SHA256_CTX *ctx, const uint8_t data[], uint32_t len);
|
||||
void sha256_final(SHA256_CTX *ctx, uint8_t digest[32]);
|
||||
|
||||
// single-shot version (best)
|
||||
void sha256_single( const uint8_t data[], uint32_t len, uint8_t digest[32]);
|
||||
|
||||
#ifndef RELEASE
|
||||
void sha256_selftest(void);
|
||||
#endif
|
||||
|
||||
// EOF
|
||||
62
stm32/mk4-bootloader/firmware-keys.h
Normal file
62
stm32/mk4-bootloader/firmware-keys.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
|
||||
#define NUM_KNOWN_PUBKEYS 6
|
||||
|
||||
// Public keys used to verify firmware is from our factory, or internet rando's
|
||||
static const uint8_t approved_pubkeys[NUM_KNOWN_PUBKEYS][64] = {
|
||||
{ // 0: for Internet Rando's -- completely shared
|
||||
0xb4, 0xcb, 0x41, 0x26, 0xf7, 0xe1, 0x6c, 0xf3, 0x8f, 0xf2, 0xb4,
|
||||
0x71, 0x1d, 0xfb, 0x23, 0x01, 0x0d, 0x76, 0xd6, 0x66, 0xa7, 0x8a,
|
||||
0xa3, 0x6c, 0x9b, 0x53, 0xf9, 0xf6, 0x7b, 0x58, 0x18, 0x05, 0x58,
|
||||
0x0b, 0x3b, 0xe9, 0x31, 0xc4, 0x9f, 0xb8, 0x44, 0x04, 0x3c, 0x11,
|
||||
0x96, 0x08, 0x0f, 0x47, 0x81, 0x25, 0xed, 0x37, 0x7a, 0x23, 0x9e,
|
||||
0x4a, 0xaf, 0xb7, 0x18, 0x38, 0xba, 0x38, 0x04, 0xda
|
||||
},
|
||||
{ // 1: first production key
|
||||
0xd6, 0xa2, 0xc8, 0x1d, 0x1c, 0x81, 0x5e, 0xdf, 0xa6, 0x0c, 0x29,
|
||||
0x6d, 0xb8, 0x57, 0x8f, 0x8d, 0x5e, 0x29, 0x69, 0x92, 0xce, 0xd1,
|
||||
0x78, 0xc1, 0x7b, 0x20, 0xd7, 0x31, 0x7b, 0xa1, 0x96, 0xb5, 0x3d,
|
||||
0xef, 0x1b, 0x0c, 0xaa, 0x79, 0x1a, 0xc3, 0x45, 0x58, 0xc4, 0xc8,
|
||||
0x8a, 0x2d, 0xeb, 0xff, 0xfe, 0x9b, 0x82, 0x01, 0x87, 0x5f, 0x5e,
|
||||
0xbc, 0x96, 0xa5, 0xe5, 0x4f, 0xc7, 0x68, 0xfe, 0x9f
|
||||
},
|
||||
{ // 2: future
|
||||
0x42, 0xef, 0x66, 0x01, 0x56, 0xc4, 0xcf, 0x95, 0xf4, 0xb5, 0xf0,
|
||||
0x38, 0x64, 0x11, 0x26, 0xc5, 0x99, 0x39, 0xc1, 0x66, 0x32, 0x06,
|
||||
0x12, 0x14, 0x4c, 0x25, 0x9c, 0x68, 0x35, 0x8c, 0xd3, 0xba, 0x24,
|
||||
0x78, 0xde, 0x8c, 0x52, 0xab, 0xdf, 0x6c, 0xb8, 0xbf, 0x09, 0x78,
|
||||
0x03, 0xbb, 0x63, 0x3a, 0x11, 0x01, 0xd9, 0x0e, 0xa4, 0x7a, 0x73,
|
||||
0x8f, 0xbf, 0x18, 0x3b, 0x7f, 0xf0, 0x0a, 0x7b, 0xc8
|
||||
},
|
||||
{ // 3: future
|
||||
0x67, 0x60, 0x54, 0x56, 0x82, 0x0c, 0xec, 0xc5, 0x1d, 0xbc, 0x82,
|
||||
0x08, 0x16, 0xc1, 0x39, 0xef, 0xf5, 0xbf, 0xba, 0x32, 0x7c, 0xce,
|
||||
0x5f, 0xe3, 0x74, 0x1e, 0x62, 0xd7, 0xe9, 0xfc, 0xc5, 0x4c, 0x8a,
|
||||
0xe8, 0x11, 0x8d, 0xc3, 0xad, 0xc2, 0x13, 0x92, 0x29, 0x4f, 0x2a,
|
||||
0xea, 0xd2, 0xf8, 0xa4, 0xc4, 0xd5, 0x7c, 0xfe, 0x12, 0x05, 0x45,
|
||||
0x3b, 0x54, 0x89, 0x59, 0x07, 0xda, 0xd6, 0xd7, 0x88
|
||||
},
|
||||
{ // 4: future
|
||||
0x43, 0xb1, 0xcf, 0x37, 0xd2, 0x7c, 0x89, 0x1f, 0x5b, 0xfe, 0xac,
|
||||
0xf3, 0xba, 0x33, 0xfc, 0x95, 0x81, 0xd9, 0xe7, 0xdd, 0x25, 0x95,
|
||||
0xef, 0x14, 0xdd, 0xef, 0x97, 0xbb, 0x33, 0xf3, 0xd8, 0xa7, 0x34,
|
||||
0x2b, 0x7a, 0x97, 0xba, 0xb3, 0xaa, 0x73, 0xe7, 0x9d, 0x41, 0x32,
|
||||
0xd8, 0xfc, 0xa1, 0x17, 0x66, 0xb5, 0x0b, 0xfe, 0x63, 0x40, 0x21,
|
||||
0x89, 0xc9, 0x92, 0x7b, 0x8e, 0x72, 0xdf, 0x0b, 0x59
|
||||
},
|
||||
#if 0
|
||||
{ // 5: future
|
||||
0xd0, 0x5c, 0xdc, 0x76, 0x16, 0x30, 0xdd, 0x30, 0xc2, 0x80, 0xf1,
|
||||
0x56, 0x26, 0x5c, 0xa8, 0x61, 0xd7, 0x4f, 0x69, 0x69, 0xe5, 0xb8,
|
||||
0x57, 0x3d, 0x35, 0xe2, 0x2a, 0x58, 0xdd, 0xce, 0x9a, 0xc6, 0x45,
|
||||
0xa9, 0x1c, 0x2b, 0x0c, 0x01, 0xfc, 0x8e, 0xbf, 0x3f, 0x51, 0x13,
|
||||
0x80, 0x7e, 0x7c, 0x13, 0xd5, 0x4f, 0x4b, 0x5e, 0x9b, 0x4c, 0x9b,
|
||||
0xd5, 0x9e, 0x1d, 0xd8, 0xe0, 0xad, 0xc0, 0x46, 0x22
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
#define IS_FACTORY_KEY(kn) (kn != 0)
|
||||
|
||||
34
stm32/mk4-bootloader/gogo.gdb
Normal file
34
stm32/mk4-bootloader/gogo.gdb
Normal file
@ -0,0 +1,34 @@
|
||||
#exec-file l-port/build-COLDCARD/firmware.elf
|
||||
exec-file bootloader.elf
|
||||
#add-symbol-file l-port/build-COLDCARD/firmware.elf 0x8000000
|
||||
#add-symbol-file bootloader/bootloader.elf 0x8000000
|
||||
|
||||
# hex for all numbers
|
||||
set output-radix 16
|
||||
|
||||
# kill X repeats N times, which interfer w/ cut-n-paste into python of dumps
|
||||
set print repeats 128
|
||||
|
||||
# Use ST-Link (st-utils)
|
||||
#target extended-remote :4242
|
||||
|
||||
# Connect to the OpenOCD gdb server (needs to be already connected)
|
||||
#
|
||||
# openocd -f openocd_stm32l4x6.cfg
|
||||
#
|
||||
target extended-remote :3333
|
||||
|
||||
# NOTE: other types of reset don't work (OpenOCD)
|
||||
define reset
|
||||
mon reset init
|
||||
end
|
||||
|
||||
# Complete chip wipe, even DFU area?
|
||||
# TODO: see README for sequence, but needs timing gaps, etc.
|
||||
define CHIP_WIPE
|
||||
mon halt
|
||||
mon stm32l4x unlock 0
|
||||
mon reset halt
|
||||
mon halt
|
||||
mon stm32l4x mass_erase 0
|
||||
end
|
||||
97
stm32/mk4-bootloader/gpio.c
Normal file
97
stm32/mk4-bootloader/gpio.c
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*
|
||||
* gpio.c -- setup and control GPIO pins (and one button)
|
||||
*
|
||||
*/
|
||||
#include "basics.h"
|
||||
#include "gpio.h"
|
||||
#include "stm32l4xx_hal.h"
|
||||
|
||||
// PA0 - onewire bus for 508a
|
||||
// PA2 - onewire bus for 508a - second SE
|
||||
#define ONEWIRE_PIN GPIO_PIN_0
|
||||
#define ONEWIRE2_PIN GPIO_PIN_2
|
||||
#define ONEWIRE_PORT GPIOA
|
||||
|
||||
// gpio_setup()
|
||||
//
|
||||
// set directions, lock critical ones, etc.
|
||||
//
|
||||
void
|
||||
gpio_setup(void)
|
||||
{
|
||||
// NOTES:
|
||||
// - try not to limit PCB changes for future revs; leave unused unchanged.
|
||||
// - oled_setup() uses pins on PA4 thru PA8
|
||||
|
||||
// enable clock to that part of chip
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOC_CLK_ENABLE();
|
||||
|
||||
{ // Onewire bus pins used for ATECC508A comms
|
||||
GPIO_InitTypeDef setup = {
|
||||
.Pin = ONEWIRE_PIN,
|
||||
.Mode = GPIO_MODE_AF_OD,
|
||||
.Pull = GPIO_NOPULL,
|
||||
.Speed = GPIO_SPEED_FREQ_MEDIUM,
|
||||
.Alternate = GPIO_AF8_UART4,
|
||||
};
|
||||
HAL_GPIO_Init(ONEWIRE_PORT, &setup);
|
||||
|
||||
// second SE
|
||||
setup.Pin = ONEWIRE2_PIN;
|
||||
setup.Alternate = GPIO_AF7_USART2;
|
||||
HAL_GPIO_Init(ONEWIRE_PORT, &setup);
|
||||
}
|
||||
|
||||
// debug console: USART1 = PA9=Tx & PA10=Rx
|
||||
{ GPIO_InitTypeDef setup = {
|
||||
.Pin = GPIO_PIN_9,
|
||||
.Mode = GPIO_MODE_AF_PP,
|
||||
.Pull = GPIO_NOPULL,
|
||||
.Speed = GPIO_SPEED_FREQ_MEDIUM,
|
||||
.Alternate = GPIO_AF7_USART1,
|
||||
};
|
||||
HAL_GPIO_Init(GPIOA, &setup);
|
||||
|
||||
setup.Pin = GPIO_PIN_10;
|
||||
setup.Mode = GPIO_MODE_INPUT;
|
||||
setup.Pull = GPIO_PULLUP;
|
||||
HAL_GPIO_Init(GPIOA, &setup);
|
||||
}
|
||||
|
||||
// SD active LED: PC7
|
||||
{ GPIO_InitTypeDef setup = {
|
||||
.Pin = GPIO_PIN_7,
|
||||
.Mode = GPIO_MODE_OUTPUT_PP,
|
||||
.Pull = GPIO_NOPULL,
|
||||
.Speed = GPIO_SPEED_FREQ_LOW,
|
||||
};
|
||||
HAL_GPIO_Init(GPIOC, &setup);
|
||||
|
||||
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, 1); // turn on
|
||||
}
|
||||
|
||||
#if 0
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
|
||||
{ // DEBUG only: Row1 = PB13
|
||||
GPIO_InitTypeDef setup = {
|
||||
.Pin = GPIO_PIN_13,
|
||||
.Mode = GPIO_MODE_OUTPUT_PP,
|
||||
.Pull = GPIO_NOPULL,
|
||||
.Speed = GPIO_SPEED_FREQ_HIGH,
|
||||
.Alternate = 0,
|
||||
};
|
||||
|
||||
HAL_GPIO_Init(GPIOB, &setup);
|
||||
}
|
||||
|
||||
// elsewhere...
|
||||
//HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, 1);
|
||||
//HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
// EOF
|
||||
12
stm32/mk4-bootloader/gpio.h
Normal file
12
stm32/mk4-bootloader/gpio.h
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// set directions, lock critical ones, etc.
|
||||
void gpio_setup(void);
|
||||
|
||||
// sample the DFU button
|
||||
inline bool dfu_button_pressed(void) { return false; }
|
||||
|
||||
|
||||
296
stm32/mk4-bootloader/keylayout.py
Executable file
296
stm32/mk4-bootloader/keylayout.py
Executable file
@ -0,0 +1,296 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Determine bits needed to configure ATECC508A to our purposes.
|
||||
#
|
||||
# Resulting data travels in the bootloader and is used once during
|
||||
# factory setup.
|
||||
#
|
||||
import sys
|
||||
from secel_config import *
|
||||
from textwrap import TextWrapper
|
||||
from contextlib import contextmanager
|
||||
|
||||
# Specific slots (aka key numbers) are reserved for specific purposes.
|
||||
class KEYNUM_508: # mark 1, 2
|
||||
# reserve 0: it's weird
|
||||
pairing = 1 # pairing hash key (picked by bootloader)
|
||||
words = 2 # secret used just for generated 2-phase protection words (random, forgotten)
|
||||
pin_1 = 3 # user-defined PIN to protect the cryptocoins (primary)
|
||||
pin_2 = 4 # user-defined PIN to protect cryptocoins (secondary)
|
||||
lastgood_1 = 5 # publically readable, PIN required to update: last successful PIN entry (1)
|
||||
lastgood_2 = 6 # publically readable, PIN required to update: last successful PIN entry (2)
|
||||
pin_3 = 7 # Duress wallet 1 (no PIN failure counts)
|
||||
pin_4 = 8 # Duress wallet 2 (no PIN failure counts)
|
||||
secret_1 = 9 # arbitrary bytes protected by corresponding pin
|
||||
secret_2 = 10 # arbitrary bytes protected by corresponding pin
|
||||
secret_3 = 11 # bytes protected by corresponding pin
|
||||
secret_4 = 12 # bytes protected by corresponding pin
|
||||
brickme = 13 # "Brick Me" PIN holder (no associated secret, but can roll the pairing secret)
|
||||
firmware = 14 # hash of flash areas, stored as an unreadable secret, controls GPIO+light
|
||||
# reserve 15: special limited use key
|
||||
|
||||
class KEYNUM_608: # mark 3+
|
||||
# reserve 0: it's weird
|
||||
pairing = 1 # pairing hash key (picked by bootloader)
|
||||
pin_stretch = 2 # secret used to stretch pin (random, forgotten)
|
||||
main_pin = 3 # user-defined PIN to protect the cryptocoins (primary)
|
||||
pin_attempt = 4 # secret mixed into pin generation (rate limited, random, forgotten)
|
||||
lastgood = 5 # publically readable, PIN required to update: last successful PIN entry (1)
|
||||
match_count = 6 # match counter, updated if they get the PIN right
|
||||
duress_pin = 7 # duress wallet (no PIN failure counts)
|
||||
long_secret = 8 # 416 bytes protected by main pin (must be #8 - special longer slot)
|
||||
secret = 9 # 72 arbitrary bytes protected by main pin (normal case)
|
||||
duress_secret = 10 # 72 arbitrary bytes protected by duress pin
|
||||
duress_lastgood = 11 # counter value when duress last worked (so we can fake num_fails)
|
||||
# available: 12
|
||||
brickme = 13 # "Brick Me" PIN holder (no associated secret, but can roll the pairing secret)
|
||||
firmware = 14 # hash of flash areas, stored as an unreadable secret, controls GPIO+light
|
||||
# reserve 15: non-special, but some fields have all ones and so point to it.
|
||||
|
||||
|
||||
class AEConfig:
|
||||
def __init__(self):
|
||||
# typical data from a specific virgin chip; serial number and hardware rev will vary!
|
||||
self.data = bytearray(a2b_hex('01233b7e00005000e9f5342beec05400c0005500832087208f20c48f8f8f8f8f9f8faf8f0000000000000000000000000000af8fffffffff00000000ffffffff00000000ffffffffffffffffffffffffffffffff00005555ffff0000000000003300330033001c001c001c001c001c003c003c003c003c003c003c003c001c00'))
|
||||
assert len(self.data) == 4*32 == 128
|
||||
self.d_slot = [None]*16
|
||||
|
||||
def set_slot(self, n, slot_conf, key_conf):
|
||||
assert 0 <= n <= 15, n
|
||||
assert isinstance(slot_conf, SlotConfig)
|
||||
assert 'KeyConfig' in str(type(key_conf))
|
||||
|
||||
self.data[20+(n*2) : 22+(n*2)] = slot_conf.pack()
|
||||
self.data[96+(n*2) : 98+(n*2)] = key_conf.pack()
|
||||
|
||||
def set_combo(self, n, combo):
|
||||
self.set_slot(n, combo.sc, combo.kc)
|
||||
|
||||
def get_combo(self, n):
|
||||
rv = ComboConfig()
|
||||
blk = self.data
|
||||
rv.kc = KeyConfig.unpack(blk[96+(2*n):2+96+(2*n)])
|
||||
rv.sc = SlotConfig.unpack(blk[20+(2*n):2+20+(2*n)])
|
||||
return rv
|
||||
|
||||
def set_otp_mode(self, read_only):
|
||||
# set OTPmode for consumption or read only
|
||||
# default is consumption.
|
||||
self.data[18] = 0xAA if read_only else 0x55
|
||||
|
||||
def dump(self):
|
||||
secel_dump(self.data)
|
||||
|
||||
def set_gpio_config(self, kn):
|
||||
# GPIO is active-high output, controlled by indicated key number
|
||||
assert 0 <= kn <= 15
|
||||
assert self.data[14] & 1 == 0, "can only work on chip w/ SWI not I2C"
|
||||
self.data[16] = 0x1 | (kn << 4) # "Auth0" mode in table 7-1
|
||||
|
||||
def disable_KdfIvLoc(self):
|
||||
# prevent use of weird AES KDF init vector junk
|
||||
self.data[72] = 0xf0
|
||||
|
||||
def checks(self):
|
||||
# reserved areas / known values
|
||||
c = self.data
|
||||
assert c[17] == 0 # reserved
|
||||
if self.partno == 5:
|
||||
assert c[18] in (0xaa, 0x55) # OTPmode
|
||||
assert c[86] in (0x00, 0x55) # LockValue
|
||||
if self.partno == 5:
|
||||
assert set(c[90:96]) == set([0]) # RFU, X509Format
|
||||
if self.partno == 6:
|
||||
assert set(c[92:96]) == set([0]) # RFU, X509Format
|
||||
|
||||
|
||||
|
||||
class AEConfig508(AEConfig):
|
||||
def __init__(self):
|
||||
# typical data from a specific virgin chip; serial number and hardware rev will vary!
|
||||
self.data = bytearray(a2b_hex('01233b7e00005000e9f5342beec05400c0005500832087208f20c48f8f8f8f8f9f8faf8f0000000000000000000000000000af8fffffffff00000000ffffffff00000000ffffffffffffffffffffffffffffffff00005555ffff0000000000003300330033001c001c001c001c001c003c003c003c003c003c003c003c001c00'))
|
||||
assert len(self.data) == 4*32 == 128
|
||||
self.d_slot = [None]*16
|
||||
self.partno = 5
|
||||
|
||||
class AEConfig608(AEConfig):
|
||||
def __init__(self):
|
||||
# typical data from a specific virgin chip; serial number and hardware rev will vary!
|
||||
self.data = bytearray(a2b_hex('01236c4100006002bbe66928ee015400c0000000832087208f20c48f8f8f8f8f9f8faf8f0000000000000000000000000000af8fffffffff00000000ffffffff000000000000000000000000000000000000000000005555ffff0000000000003300330033001c001c001c001c001c003c003c003c003c003c003c003c001c00'))
|
||||
assert len(self.data) == 4*32 == 128
|
||||
self.d_slot = [None]*16
|
||||
self.partno = 6
|
||||
|
||||
|
||||
def counter_match(self, kn):
|
||||
assert 0 <= kn <= 15
|
||||
self.data[18] = (kn << 4) | 0x1
|
||||
|
||||
@contextmanager
|
||||
def chip_options(self):
|
||||
co = ChipOptions.unpack(self.data[90:92])
|
||||
yield co
|
||||
self.data[90:92] = co.pack()
|
||||
|
||||
|
||||
def cpp_dump_hex(buf):
|
||||
# format for CPP macro
|
||||
txt = ', '.join('0x%02x' %i for i in buf)
|
||||
tw = TextWrapper(width=60)
|
||||
return '\n'.join('\t%s \\' % i for i in tw.wrap(txt))
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
with open('ae_config.h', 'wt') as fp:
|
||||
print('// autogenerated; see bootloader/keylayout.py\n', file=fp)
|
||||
|
||||
for partno, ae, KEYNUM in [ (6, AEConfig608(), KEYNUM_608),
|
||||
(5, AEConfig508(), KEYNUM_508)]:
|
||||
doit(partno, ae, KEYNUM, fp)
|
||||
|
||||
def doit(partno, ae, KEYNUM, fp):
|
||||
# default all slots to storage
|
||||
cc = [ComboConfig() for i in range(16)]
|
||||
for j in range(16):
|
||||
cc[j].for_storage()
|
||||
|
||||
# unique keys per-device
|
||||
# - pairing key for linking AE and main micro together
|
||||
# - critical!
|
||||
cc[KEYNUM.pairing].hash_key(roll_kn=KEYNUM.brickme).lockable(False)
|
||||
|
||||
|
||||
if partno == 5:
|
||||
# mark 1/2: most keyslots require knowledge of a PIN
|
||||
secure_map = [
|
||||
(KEYNUM.pin_1, KEYNUM.secret_1, KEYNUM.lastgood_1),
|
||||
(KEYNUM.pin_2, KEYNUM.secret_2, KEYNUM.lastgood_2),
|
||||
(KEYNUM.pin_3, KEYNUM.secret_3, None),
|
||||
(KEYNUM.pin_4, KEYNUM.secret_4, None) ]
|
||||
|
||||
# - "words" HMAC-key used for for 2-phase PIN words (only)
|
||||
cc[KEYNUM.words].hash_key().require_auth(KEYNUM.pairing).deterministic()
|
||||
|
||||
main_pin = KEYNUM.pin_1
|
||||
unused_slots = [0, 15]
|
||||
|
||||
if partno == 6:
|
||||
# mark 3+: no more secondary pin, some renaming, plus KDF
|
||||
secure_map = [
|
||||
(KEYNUM.main_pin, KEYNUM.secret, KEYNUM.lastgood),
|
||||
(KEYNUM.main_pin, KEYNUM.long_secret, None),
|
||||
(KEYNUM.duress_pin, KEYNUM.duress_secret, KEYNUM.duress_lastgood),
|
||||
]
|
||||
main_pin = KEYNUM.main_pin
|
||||
unused_slots = [0, 12, 15]
|
||||
|
||||
# new slots related to pin attempt- and rate-limiting
|
||||
# - both hold random, unknown contents, can't be changed
|
||||
# - use of the first one will cost a counter incr
|
||||
# - actual PIN to be used is rv=HMAC(pin_stretch, rv) many times
|
||||
cc[KEYNUM.pin_attempt].hash_key().require_auth(KEYNUM.pairing).deterministic().limited_use()
|
||||
|
||||
# to rate-limit PIN attempts (also used for prefix words) we require
|
||||
# many HMAC cycles using this random+unknown value.
|
||||
cc[KEYNUM.pin_stretch].hash_key().require_auth(KEYNUM.pairing).deterministic()
|
||||
|
||||
# chip-enforced pin attempts: link keynum and enable "match count" feature
|
||||
cc[KEYNUM.match_count].writeable_storage(main_pin).require_auth(KEYNUM.pairing)
|
||||
ae.counter_match(KEYNUM.match_count)
|
||||
|
||||
# turn off selftest feature (performance problem), and enforce encryption
|
||||
# (io protection) for verify, etc.
|
||||
with ae.chip_options() as opt:
|
||||
opt.POSTEnable = 0
|
||||
opt.IOProtKeyEnable = 1
|
||||
opt.ECDHProt = 0x1 # allow encrypted output
|
||||
opt.KDFProt = 0x1 # allow encrypted output
|
||||
opt.IOProtKey = KEYNUM.pairing
|
||||
|
||||
# don't want
|
||||
ae.disable_KdfIvLoc()
|
||||
|
||||
# PIN and corresponding protected secrets
|
||||
# - if you know old value of PIN, you can write it (to change to new PIN)
|
||||
for kn, sec_num, lg_num in secure_map:
|
||||
cc[kn].hash_key(write_kn=kn).require_auth(KEYNUM.pairing)
|
||||
cc[sec_num].secret_storage(kn).require_auth(kn)
|
||||
if lg_num is not None:
|
||||
# used to hold counter0] value when we last successfully got that PIN
|
||||
cc[lg_num].writeable_storage(kn).require_auth(KEYNUM.pairing)
|
||||
|
||||
# "Brick Me" PIN holder: enables Roll of pairing secret + device destruction
|
||||
cc[KEYNUM.brickme].hash_key(write_kn=KEYNUM.brickme).require_auth(KEYNUM.pairing)
|
||||
|
||||
# field updateable secret, hopefully based on hash of flash contents
|
||||
# - if you know this value, then you can enable the green light
|
||||
# - to change it, you need the primary pin
|
||||
cc[KEYNUM.firmware].secret_storage(main_pin).no_read().require_auth(KEYNUM.pairing)
|
||||
|
||||
# Slot 8 is special because its data area is larger and could hold a
|
||||
# certificate in DER format. All ther others are 36/72 bytes only
|
||||
# BTW: on the 508a, an errata limits this to just 224 bytes, which is not enough anyway
|
||||
assert cc[8].kc.KeyType == 7
|
||||
|
||||
# Slot 0 has baggage because a zero value for ReadKey has special meaning,
|
||||
# so avoid using it. But had to put something in ReadKey, so it's 15 sometimes.
|
||||
assert cc[0].sc.IsSecret == 0
|
||||
assert cc[15].sc.IsSecret == 0
|
||||
|
||||
assert len(cc) == 16
|
||||
for idx, x in enumerate(cc):
|
||||
# no EC keys on this project
|
||||
assert cc[idx].kc.KeyType in [6,7], idx
|
||||
|
||||
if idx == KEYNUM.pairing:
|
||||
assert cc[idx].kc.KeyType == 7
|
||||
elif idx in unused_slots:
|
||||
# check not used
|
||||
assert cc[idx].sc.as_int() == 0x0000, (partno, idx)
|
||||
assert cc[idx].kc.as_int() == 0x003c, (partno, idx)
|
||||
else:
|
||||
# Use of **any** key require knowledge of pairing secret
|
||||
# except PIN-protected slots, which require PIN (which requires pairing secret)
|
||||
assert cc[idx].kc.ReqAuth, idx
|
||||
assert (cc[idx].kc.AuthKey == KEYNUM.pairing) or \
|
||||
(cc[cc[idx].kc.AuthKey].kc.AuthKey == KEYNUM.pairing), idx
|
||||
|
||||
ae.set_combo(idx, cc[idx])
|
||||
|
||||
# require CheckMac on indicated key to turn on GPIO
|
||||
ae.set_gpio_config(KEYNUM.firmware)
|
||||
|
||||
ae.checks()
|
||||
|
||||
#ae.dump()
|
||||
|
||||
# generate a single header file we will need
|
||||
if fp:
|
||||
print("#ifdef FOR_%d08\n" % partno, file=fp)
|
||||
|
||||
print('// bytes [16..84) of chip config area', file=fp)
|
||||
print('#define AE_CHIP_CONFIG_1 { \\', file=fp)
|
||||
print(cpp_dump_hex(ae.data[16:84]), file=fp)
|
||||
print('}\n\n', file=fp)
|
||||
|
||||
print('// bytes [90..128) of chip config area', file=fp)
|
||||
print('#define AE_CHIP_CONFIG_2 { \\', file=fp)
|
||||
print(cpp_dump_hex(ae.data[90:]), file=fp)
|
||||
print('}\n\n', file=fp)
|
||||
|
||||
print('// key/slot usage and names', file=fp)
|
||||
names = [nm for nm in dir(KEYNUM) if nm[0] != '_']
|
||||
for v,nm in sorted((getattr(KEYNUM, nm), nm) for nm in names):
|
||||
print('#define KEYNUM_%-20s\t%d' % (nm.lower(), v), file=fp)
|
||||
|
||||
if partno == 6:
|
||||
print('\n/*\n', file=fp)
|
||||
sys.stdout = fp
|
||||
ae.dump()
|
||||
print('\n*/', file=fp)
|
||||
|
||||
print("#endif /* FOR_%d08 */\n\n" % partno, file=fp)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
131
stm32/mk4-bootloader/link-script.ld
Normal file
131
stm32/mk4-bootloader/link-script.ld
Normal file
@ -0,0 +1,131 @@
|
||||
/**
|
||||
*
|
||||
* a specialized linker script:
|
||||
* - limits itself to just part of the device
|
||||
* - assumes only a small amount of ram is available.
|
||||
* - reference: <https://sourceware.org/binutils/docs/ld/>
|
||||
* - spaces actually matter in expression in this file
|
||||
*
|
||||
*/
|
||||
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
SEARCH_DIR(.)
|
||||
|
||||
/* Memory Spaces Definitions */
|
||||
MEMORY
|
||||
{
|
||||
rom (rx) : ORIGIN = BL_FLASH_BASE, LENGTH = BL_FLASH_SIZE
|
||||
ram (rwx) : ORIGIN = BL_SRAM_BASE, LENGTH = BL_SRAM_SIZE
|
||||
}
|
||||
|
||||
/* The stack size used by the bootloader. */
|
||||
STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x800;
|
||||
|
||||
/* Section Definitions */
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sfixed = .;
|
||||
KEEP(*(.entry_code))
|
||||
KEEP(*(.outside_fw))
|
||||
enable.o(.text*)
|
||||
stm32l4xx_hal_firewall.o(.text*)
|
||||
|
||||
. = ALIGN(256);
|
||||
_firewall_start = .;
|
||||
|
||||
KEEP(*(.firewall_code))
|
||||
dispatch.o(.text*)
|
||||
oled.o(.text*)
|
||||
stm32l4xx_hal_gpio.o(.text*)
|
||||
stm32l4xx_hal_spi.o(.text*)
|
||||
|
||||
/* important: this pulls in library (libgcc) stuff here */
|
||||
KEEP(*(.text .text.* .gnu.linkonce.t.*))
|
||||
*(.rodata .rodata* .gnu.linkonce.r.*)
|
||||
|
||||
/*
|
||||
*(.glue_7t) *(.glue_7)
|
||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||
*/
|
||||
|
||||
. = ALIGN(4);
|
||||
_efixed = .; /* End of text section */
|
||||
|
||||
} > rom
|
||||
|
||||
/* .ARM.exidx is sorted, so has to go in its own output section. */
|
||||
PROVIDE_HIDDEN (__exidx_start = .);
|
||||
.ARM.exidx :
|
||||
{
|
||||
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
|
||||
} > rom
|
||||
PROVIDE_HIDDEN (__exidx_end = .);
|
||||
|
||||
. = ALIGN(4);
|
||||
_etext = .;
|
||||
|
||||
.relocate : AT (_etext)
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_srelocate = .;
|
||||
*(.ramfunc .ramfunc.*);
|
||||
_pdg_hack = .;
|
||||
*(.data .data.*);
|
||||
. = ALIGN(4);
|
||||
_erelocate = .;
|
||||
|
||||
} > ram
|
||||
|
||||
/*
|
||||
.text : {
|
||||
. = (BL_FLASH_SIZE - BL_NVROM_SIZE);
|
||||
|
||||
KEEP(*(.pairing_secret))
|
||||
} > rom
|
||||
*/
|
||||
|
||||
/* Some very manual linking! I've tried doing it right, and couldn't get it to work well */
|
||||
addr_rom_secrets = BL_NVROM_BASE;
|
||||
|
||||
reboot_seed_base = BL_SRAM_BASE + BL_SRAM_SIZE;
|
||||
|
||||
/* if you initialize a global var to some non-zero value, then that data ends up
|
||||
in .relocate as read-only data and used briefly at startup (when copied to RAM).
|
||||
We don't want to support that, so we're checking here that hasn't happened. */
|
||||
ASSERT(_pdg_hack == _erelocate,
|
||||
"Sorry, no initialized data support! Set to zero or remove.")
|
||||
|
||||
/* ensure binary fits */
|
||||
ASSERT(_erelocate - BL_SRAM_BASE + _etext <= BL_FLASH_BASE + BL_FLASH_SIZE,
|
||||
"Binary is too big to fit!!!")
|
||||
|
||||
/* .bss section which is used for uninitialized data */
|
||||
.bss (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sbss = . ;
|
||||
_szero = .;
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_ebss = . ;
|
||||
_ezero = .;
|
||||
} > ram
|
||||
|
||||
/* stack section */
|
||||
.stack (NOLOAD):
|
||||
{
|
||||
. = ALIGN(8);
|
||||
_sstack = .;
|
||||
. = . + STACK_SIZE;
|
||||
. = ALIGN(8);
|
||||
_estack = .;
|
||||
} > ram
|
||||
|
||||
. = ALIGN(4);
|
||||
_end = . ;
|
||||
}
|
||||
119
stm32/mk4-bootloader/mathcheck.py
Normal file
119
stm32/mk4-bootloader/mathcheck.py
Normal file
@ -0,0 +1,119 @@
|
||||
#
|
||||
#
|
||||
# Test code to check crypto on 608a is doing what we think it is.
|
||||
#
|
||||
# Requires:
|
||||
# - various fields to be known quantities, which doesn't happen in normal cases
|
||||
# - JTAG (Cortex) debugger
|
||||
# - non-release firmware of bootloader, and system in "factory mode"
|
||||
# - code compiled to put 0x41 in secret spots of 608
|
||||
#
|
||||
from keylayout import KEYNUM_608 as KEYNUM
|
||||
from binascii import b2a_hex, a2b_hex
|
||||
from hashlib import sha256
|
||||
from hmac import HMAC
|
||||
|
||||
keys = {}
|
||||
|
||||
# mdb 0x08007800 32
|
||||
keys[KEYNUM.pairing] = a2b_hex('2744eaab79b539cc72fc032b7516875ae0a63e8ab22f4cbf529d21f5ea665aa7')
|
||||
|
||||
# fixed values from instrumented version of code
|
||||
for kn in [KEYNUM.pin_stretch, KEYNUM.pin_attempt]:
|
||||
keys[kn] = bytes((0x41+kn) for i in range(32))
|
||||
|
||||
assert all(len(i) == 32 for i in keys.values()), repr(keys)
|
||||
|
||||
PURPOSE_NORMAL = a2b_hex('58184d33')
|
||||
PURPOSE_WORDS = a2b_hex('73676d2e')
|
||||
|
||||
KDF_ITER_WORDS = 12
|
||||
KDF_ITER_PIN = 8
|
||||
|
||||
def show(lab, val):
|
||||
print('%s => \n %s' % (lab, b2a_hex(val).decode('ascii')))
|
||||
|
||||
# see pin_hash in pins.c
|
||||
def pin_hash(pin, purpose):
|
||||
assert len(purpose) == 4
|
||||
|
||||
if len(pin) == 0:
|
||||
return bytes(32)
|
||||
|
||||
md = sha256()
|
||||
md.update(keys[KEYNUM.pairing])
|
||||
md.update(purpose)
|
||||
md.update(pin)
|
||||
|
||||
return sha256(md.digest()).digest()
|
||||
|
||||
# see ae_stretch_iter in ae.c
|
||||
def ae_stretch_iter(start, iterations):
|
||||
end = bytes(start)
|
||||
|
||||
for i in range(iterations):
|
||||
hs = HMAC(keys[KEYNUM.pin_stretch], msg=end, digestmod=sha256)
|
||||
end = hs.digest()
|
||||
|
||||
return end
|
||||
|
||||
# see ae.c
|
||||
def ae_mixin_key(keynum, start):
|
||||
|
||||
if keynum:
|
||||
hm = HMAC(keys[keynum], msg=start, digestmod=sha256)
|
||||
end = hm.digest()
|
||||
else:
|
||||
end = bytes(32)
|
||||
|
||||
md = sha256()
|
||||
md.update(keys[KEYNUM.pairing])
|
||||
md.update(start)
|
||||
md.update(bytes([keynum]))
|
||||
md.update(end)
|
||||
|
||||
return md.digest()
|
||||
|
||||
|
||||
if 1:
|
||||
# for "WORDS"
|
||||
print("-- for words --")
|
||||
|
||||
# on target, do this:
|
||||
# import ckcc; b = bytearray(32); b[0:2] = b'12'; ckcc.gate(16, b, 2)
|
||||
# from ubinascii import hexlify as b2a_hex; b2a_hex(b[:4])
|
||||
prefix = b'12'
|
||||
|
||||
start = pin_hash(prefix, PURPOSE_WORDS)
|
||||
show('pin_hash(%r, WORDS)' % prefix, start)
|
||||
|
||||
end = ae_stretch_iter(start, KDF_ITER_WORDS)
|
||||
|
||||
show('full hash', end)
|
||||
|
||||
show('words value', end[0:4])
|
||||
|
||||
if 1:
|
||||
# for PIN codes
|
||||
print("\n-- for PIN code --")
|
||||
# on target, do this:
|
||||
#
|
||||
# >>> from pincodes import PinAttempt; pa = PinAttempt(); pa.setup(b'12-12')
|
||||
# 32
|
||||
# >>> pa.login()
|
||||
# True
|
||||
# >>> from ubinascii import hexlify as b2a_hex
|
||||
# >>> b2a_hex(pa.cached_main_pin)
|
||||
# b'75feafe5b8bfc8c255d816c6d6919f3f1b59769fcdee57174ab78044a839911f'
|
||||
#
|
||||
# - have to xor with key from pin_cache_get_key() as well (debugger)
|
||||
|
||||
pin = b'12-12'
|
||||
start = pin_hash(pin, PURPOSE_NORMAL)
|
||||
mid_result = ae_stretch_iter(start, KDF_ITER_PIN);
|
||||
show('mid-result', mid_result)
|
||||
duress = ae_mixin_key(0, mid_result)
|
||||
show('duress pin', duress)
|
||||
result = ae_mixin_key(KEYNUM.pin_attempt, mid_result)
|
||||
show('main pin', result)
|
||||
|
||||
1
stm32/mk4-bootloader/micro-ecc
Symbolic link
1
stm32/mk4-bootloader/micro-ecc
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/micro-ecc
|
||||
12
stm32/mk4-bootloader/misc.h
Normal file
12
stm32/mk4-bootloader/misc.h
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// no screen
|
||||
void enter_dfu(void);
|
||||
|
||||
// shows "send update" screen first
|
||||
void dfu_by_request(void);
|
||||
|
||||
|
||||
32
stm32/mk4-bootloader/mk-sigheader.py
Executable file
32
stm32/mk4-bootloader/mk-sigheader.py
Executable file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# parse out some values from C header... and include them into globals
|
||||
def doit(c_fname, py_file):
|
||||
lines = []
|
||||
for ln in open(c_fname, 'rt').readlines():
|
||||
if ln.startswith('#define'):
|
||||
lines.append(ln.split(' ', 2)[1:])
|
||||
if ln.startswith('// '):
|
||||
lines.append(ln[3:])
|
||||
if not ln.strip():
|
||||
lines.append(None)
|
||||
|
||||
with open(py_file, 'wt') as o:
|
||||
print("# Autogen'ed file, don't edit. See bootloader/sigheader.h for original\n",file=o)
|
||||
|
||||
for ln in lines:
|
||||
if ln is None:
|
||||
print('', file=o)
|
||||
elif len(ln) == 2:
|
||||
k,v = ln
|
||||
k = k.strip()
|
||||
v = v.strip()
|
||||
print('%s = %s' % (k, v), file=o)
|
||||
else:
|
||||
print('# '+ln.strip(), file=o)
|
||||
|
||||
print("\n# EOF", file=o)
|
||||
|
||||
if __name__ == '__main__':
|
||||
doit('sigheader.h', 'sigheader.py')
|
||||
|
||||
444
stm32/mk4-bootloader/oled.c
Normal file
444
stm32/mk4-bootloader/oled.c
Normal file
@ -0,0 +1,444 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#include "oled.h"
|
||||
#include "delay.h"
|
||||
#include "stm32l4xx_hal_gpio.h"
|
||||
#include "stm32l4xx_hal_rcc.h"
|
||||
#include "stm32l4xx_hal_dma.h"
|
||||
#include "stm32l4xx_hal_spi.h"
|
||||
#include <string.h>
|
||||
|
||||
// Reset and config sequence.
|
||||
//
|
||||
// As measured! No attempt to understand them here.
|
||||
static const uint8_t reset_commands[] = {
|
||||
0xae, // display off
|
||||
0x20, 0x00, // horz addr-ing mode
|
||||
0x40, // ram display start line: 0
|
||||
0xa1, // cold addr 127 mapped to seg0
|
||||
0xa8, 0x3f, // set multiplex ratio: 64
|
||||
0xc8, // remapped mode: scan from COMn to COM0
|
||||
0xd3, 0x00, // display offset (vertical shift): 0
|
||||
0xda, 0x12, // seq com pin conf: alt com pin
|
||||
0xd5, 0x80, // set display clock divide ratio
|
||||
0xd9, 0xf1, // set pre-change period
|
||||
0xdb, 0x30, // Cvomh deselect level
|
||||
0x81, 0xff, // Contrast: max
|
||||
0xa4, // display ram contents (not all on)
|
||||
0xa6, // normal not inverted
|
||||
0x8d, 0x14, // enable charge pump
|
||||
0xaf // display on
|
||||
};
|
||||
|
||||
// Bytes to send before sending the 1024 bytes of pixel data.
|
||||
//
|
||||
static const uint8_t before_show[] = {
|
||||
0x21, 0x00, 0x7f, // setup column address range (start, end): 0-127
|
||||
0x22, 0x00, 0x07 // setup page start/end address: 0 - 7
|
||||
};
|
||||
|
||||
// OLED connections:
|
||||
// - all on port A
|
||||
// - all push/pull outputs
|
||||
//
|
||||
#define RESET_PIN GPIO_PIN_6
|
||||
#define DC_PIN GPIO_PIN_8
|
||||
#define CS_PIN GPIO_PIN_4
|
||||
#define SPI_SCK GPIO_PIN_5
|
||||
#define SPI_MOSI GPIO_PIN_7
|
||||
|
||||
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
|
||||
|
||||
static SPI_HandleTypeDef spi_port;
|
||||
|
||||
// write_bytes()
|
||||
//
|
||||
static inline void
|
||||
write_bytes(int len, const uint8_t *buf)
|
||||
{
|
||||
// send via SPI(1)
|
||||
HAL_SPI_Transmit(&spi_port, (uint8_t *)buf, len, HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
// oled_write_cmd()
|
||||
//
|
||||
void
|
||||
oled_write_cmd(uint8_t cmd)
|
||||
{
|
||||
HAL_GPIO_WritePin(GPIOA, CS_PIN, 1);
|
||||
HAL_GPIO_WritePin(GPIOA, DC_PIN, 0);
|
||||
HAL_GPIO_WritePin(GPIOA, CS_PIN, 0);
|
||||
|
||||
write_bytes(1, &cmd);
|
||||
|
||||
HAL_GPIO_WritePin(GPIOA, CS_PIN, 1);
|
||||
}
|
||||
|
||||
// oled_write_cmd_sequence()
|
||||
//
|
||||
void
|
||||
oled_write_cmd_sequence(int len, const uint8_t *cmds)
|
||||
{
|
||||
for(int i=0; i<len; i++) {
|
||||
oled_write_cmd(cmds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// oled_write_data()
|
||||
//
|
||||
void
|
||||
oled_write_data(int len, const uint8_t *pixels)
|
||||
{
|
||||
HAL_GPIO_WritePin(GPIOA, CS_PIN, 1);
|
||||
HAL_GPIO_WritePin(GPIOA, DC_PIN, 1);
|
||||
HAL_GPIO_WritePin(GPIOA, CS_PIN, 0);
|
||||
|
||||
write_bytes(len, pixels);
|
||||
|
||||
HAL_GPIO_WritePin(GPIOA, CS_PIN, 1);
|
||||
}
|
||||
|
||||
// oled_spi_setup()
|
||||
//
|
||||
// Just setup SPI, do not reset display, etc.
|
||||
//
|
||||
void
|
||||
oled_spi_setup(void)
|
||||
{
|
||||
// might already be setup
|
||||
if(spi_port.Instance == SPI1) return;
|
||||
|
||||
memset(&spi_port, 0, sizeof(spi_port));
|
||||
|
||||
spi_port.Instance = SPI1;
|
||||
|
||||
// see SPI_InitTypeDef
|
||||
spi_port.Init.Mode = SPI_MODE_MASTER;
|
||||
spi_port.Init.Direction = SPI_DIRECTION_2LINES;
|
||||
spi_port.Init.DataSize = SPI_DATASIZE_8BIT;
|
||||
spi_port.Init.CLKPolarity = SPI_POLARITY_LOW;
|
||||
spi_port.Init.CLKPhase = SPI_PHASE_1EDGE;
|
||||
spi_port.Init.NSS = SPI_NSS_SOFT;
|
||||
spi_port.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; // conservative
|
||||
spi_port.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
||||
spi_port.Init.TIMode = SPI_TIMODE_DISABLED;
|
||||
spi_port.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;
|
||||
|
||||
HAL_SPI_Init(&spi_port);
|
||||
}
|
||||
|
||||
// oled_setup()
|
||||
//
|
||||
// Ok to call this lots.
|
||||
//
|
||||
void
|
||||
oled_setup(void)
|
||||
{
|
||||
static uint32_t inited;
|
||||
|
||||
if(inited == 0x238a572F) {
|
||||
return;
|
||||
}
|
||||
inited = 0x238a572F;
|
||||
|
||||
// enable some internal clocks
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
__HAL_RCC_SPI1_CLK_ENABLE();
|
||||
|
||||
// simple pins
|
||||
GPIO_InitTypeDef setup = {
|
||||
.Pin = RESET_PIN | DC_PIN | CS_PIN,
|
||||
.Mode = GPIO_MODE_OUTPUT_PP,
|
||||
.Pull = GPIO_NOPULL,
|
||||
.Speed = GPIO_SPEED_FREQ_MEDIUM,
|
||||
.Alternate = 0,
|
||||
};
|
||||
HAL_GPIO_Init(GPIOA, &setup);
|
||||
|
||||
// starting values
|
||||
HAL_GPIO_WritePin(GPIOA, RESET_PIN | CS_PIN | DC_PIN, 1);
|
||||
|
||||
// SPI pins
|
||||
setup.Pin = SPI_SCK | SPI_MOSI;
|
||||
setup.Mode = GPIO_MODE_AF_PP;
|
||||
setup.Alternate = GPIO_AF5_SPI1;
|
||||
HAL_GPIO_Init(GPIOA, &setup);
|
||||
|
||||
// lock the RESET pin so that St's DFU code doesn't clear screen
|
||||
// it might be trying to use it a MISO signal for SPI loading
|
||||
HAL_GPIO_LockPin(GPIOA, RESET_PIN | CS_PIN | DC_PIN);
|
||||
|
||||
// 10ms low-going pulse on reset pin
|
||||
delay_ms(1);
|
||||
HAL_GPIO_WritePin(GPIOA, RESET_PIN, 0);
|
||||
delay_ms(10);
|
||||
HAL_GPIO_WritePin(GPIOA, RESET_PIN, 1);
|
||||
|
||||
oled_spi_setup();
|
||||
|
||||
// where: SPI1->CR1, CR2, SR
|
||||
// mpy settings:
|
||||
// '0x354', '0x1700', '0x002'
|
||||
// this code:
|
||||
// '0x37c', '0x1700', '0x603'
|
||||
//SPI1->CR1 = 0x354;
|
||||
|
||||
// write a sequence to reset things
|
||||
oled_write_cmd_sequence(sizeof(reset_commands), reset_commands);
|
||||
}
|
||||
|
||||
// oled_show_raw()
|
||||
//
|
||||
// No decompression.
|
||||
//
|
||||
void
|
||||
oled_show_raw(uint32_t len, const uint8_t *pixels)
|
||||
{
|
||||
oled_setup();
|
||||
|
||||
oled_write_cmd_sequence(sizeof(before_show), before_show);
|
||||
|
||||
HAL_GPIO_WritePin(GPIOA, CS_PIN, 1);
|
||||
HAL_GPIO_WritePin(GPIOA, DC_PIN, 1);
|
||||
HAL_GPIO_WritePin(GPIOA, CS_PIN, 0);
|
||||
|
||||
write_bytes(len, pixels);
|
||||
|
||||
HAL_GPIO_WritePin(GPIOA, CS_PIN, 1);
|
||||
}
|
||||
|
||||
// oled_show()
|
||||
//
|
||||
// Perform simple RLE decompression.
|
||||
//
|
||||
void
|
||||
oled_show(const uint8_t *pixels)
|
||||
{
|
||||
oled_setup();
|
||||
|
||||
oled_write_cmd_sequence(sizeof(before_show), before_show);
|
||||
|
||||
HAL_GPIO_WritePin(GPIOA, CS_PIN, 1);
|
||||
HAL_GPIO_WritePin(GPIOA, DC_PIN, 1);
|
||||
HAL_GPIO_WritePin(GPIOA, CS_PIN, 0);
|
||||
|
||||
uint8_t buf[127];
|
||||
const uint8_t *p = pixels;
|
||||
|
||||
// NOTE: must also update code in oled_show_progress, which dups this heavily.
|
||||
while(1) {
|
||||
uint8_t hdr = *(p++);
|
||||
if(!hdr) break;
|
||||
|
||||
uint8_t len = hdr & 0x7f;
|
||||
if(hdr & 0x80) {
|
||||
// random bytes follow
|
||||
memcpy(buf, p, len);
|
||||
p += len;
|
||||
} else {
|
||||
// repeat same byte
|
||||
memset(buf, *p, len);
|
||||
p++;
|
||||
}
|
||||
|
||||
write_bytes(len, buf);
|
||||
}
|
||||
|
||||
HAL_GPIO_WritePin(GPIOA, CS_PIN, 1);
|
||||
}
|
||||
|
||||
// oled_show_progress()
|
||||
//
|
||||
// Perform simple RLE decompression, and add a bar on final screen line.
|
||||
//
|
||||
void
|
||||
oled_show_progress(const uint8_t *pixels, int progress)
|
||||
{
|
||||
oled_setup();
|
||||
|
||||
oled_write_cmd_sequence(sizeof(before_show), before_show);
|
||||
|
||||
HAL_GPIO_WritePin(GPIOA, CS_PIN, 1);
|
||||
HAL_GPIO_WritePin(GPIOA, DC_PIN, 1);
|
||||
HAL_GPIO_WritePin(GPIOA, CS_PIN, 0);
|
||||
|
||||
uint8_t buf[127];
|
||||
const uint8_t *p = pixels;
|
||||
|
||||
const uint16_t p_start = 896;
|
||||
uint32_t p_count = 1280 * progress / 1000;
|
||||
|
||||
if(p_count > 128) p_count = 128;
|
||||
if(p_count < 0) p_count = 0;
|
||||
|
||||
bool last_line = false;
|
||||
|
||||
uint16_t offset = 0;
|
||||
while(1) {
|
||||
uint8_t hdr = *(p++);
|
||||
if(hdr == 0) break;
|
||||
|
||||
uint8_t len = hdr & 0x7f;
|
||||
if(hdr & 0x80) {
|
||||
// random bytes follow
|
||||
memcpy(buf, p, len);
|
||||
p += len;
|
||||
} else {
|
||||
// repeat same byte
|
||||
memset(buf, *p, len);
|
||||
p++;
|
||||
}
|
||||
|
||||
if(!last_line && (offset+len) >= p_start) {
|
||||
last_line = true;
|
||||
|
||||
// adjust so we're aligned w/ last line
|
||||
int h = p_start - offset;
|
||||
if(h) {
|
||||
write_bytes(h, buf);
|
||||
memmove(buf, buf+h, len-h);
|
||||
len -= h;
|
||||
offset += h;
|
||||
}
|
||||
}
|
||||
|
||||
if(last_line) {
|
||||
for(int j=0; (p_count > 0) && (j<len); j++, p_count--) {
|
||||
buf[j] |= 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
write_bytes(len, buf);
|
||||
offset += len;
|
||||
}
|
||||
|
||||
HAL_GPIO_WritePin(GPIOA, CS_PIN, 1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// oled_busy_bar()
|
||||
//
|
||||
void
|
||||
oled_busy_bar(bool en)
|
||||
{
|
||||
// Render a continuous activity (not progress) bar in lower 8 lines of display
|
||||
// - using OLED itself to do the animation, so smooth and CPU free
|
||||
// - cannot preserve bottom 8 lines, since we have to destructively write there
|
||||
oled_spi_setup();
|
||||
|
||||
static const uint8_t setup[] = {
|
||||
//0x20, 0x00, // horz addr-ing mode (normal)
|
||||
0x21, 0x00, 0x7f, // setup column address range (start, end): 0-127
|
||||
0x22, 7, 7, // setup page start/end address: page 7=last 8 lines
|
||||
};
|
||||
static const uint8_t animate[] = {
|
||||
0x2e, // stop animations in progress
|
||||
0x26, // scroll leftwards (stock ticker mode)
|
||||
0, // placeholder
|
||||
7, // start 'page' (vertical)
|
||||
7, // scroll speed: 7=fastest,
|
||||
7, // end 'page'
|
||||
0, 0xff, // placeholders
|
||||
0x2f // start
|
||||
};
|
||||
static const uint8_t cleanup[] = {
|
||||
0x2e, // stop animation
|
||||
0x20, 0x00, // horz addr-ing mode
|
||||
0x21, 0x00, 0x7f, // setup column address range (start, end): 0-127
|
||||
0x22, 7, 7, // setup page start/end address: page 7=last 8 lines
|
||||
};
|
||||
|
||||
uint8_t data[128];
|
||||
|
||||
if(!en) {
|
||||
// clear it, stop animation
|
||||
memset(data, 0, sizeof(data));
|
||||
oled_write_cmd_sequence(sizeof(cleanup), cleanup);
|
||||
oled_write_data(sizeof(data), data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// some diagonal lines
|
||||
for(int x=0; x<128; x++) {
|
||||
// each byte here is a vertical column, 8 pixels tall, MSB at bottom
|
||||
switch(x % 4) {
|
||||
default:
|
||||
data[x] = 0x0;
|
||||
break;
|
||||
case 0 ... 1:
|
||||
data[x] = 0x80;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
oled_write_cmd_sequence(sizeof(setup), setup);
|
||||
oled_write_data(sizeof(data), data);
|
||||
oled_write_cmd_sequence(sizeof(animate), animate);
|
||||
}
|
||||
|
||||
// oled_draw_bar()
|
||||
//
|
||||
void
|
||||
oled_draw_bar(int percent)
|
||||
{
|
||||
// Render a continuous activity (progress) bar in lower 8 lines of display
|
||||
// - cannot preserve bottom 8 lines, since we have to destructively write there
|
||||
// - requires OLED and GPIO's already setup by other code.
|
||||
oled_spi_setup();
|
||||
|
||||
static const uint8_t setup[] = {
|
||||
0x21, 0x00, 0x7f, // setup column address range (start, end): 0-127
|
||||
0x22, 7, 7, // setup page start/end address: page 7=last 8 lines
|
||||
};
|
||||
|
||||
uint8_t data[128];
|
||||
int cut = percent * 128 / 100;
|
||||
|
||||
// each byte here is a vertical column, 8 pixels tall, MSB at bottom
|
||||
memset(data, 0x80, cut);
|
||||
memset(data+cut, 0x0, 128-cut);
|
||||
|
||||
oled_write_cmd_sequence(sizeof(setup), setup);
|
||||
oled_write_data(sizeof(data), data);
|
||||
}
|
||||
#endif
|
||||
|
||||
// oled_factory_busy()
|
||||
//
|
||||
void
|
||||
oled_factory_busy(void)
|
||||
{
|
||||
// Render a continuous activity (not progress) bar in lower 8 lines of display
|
||||
// - using OLED itself to do the animation, so smooth and CPU free
|
||||
// - cannot preserve bottom 8 lines, since we have to destructively write there
|
||||
//oled_spi_setup();
|
||||
|
||||
static const uint8_t setup[] = {
|
||||
0x21, 0x00, 0x7f, // setup column address range (start, end): 0-127
|
||||
0x22, 7, 7, // setup page start/end address: page 7=last 8 lines
|
||||
};
|
||||
static const uint8_t animate[] = {
|
||||
0x2e, // stop animations in progress
|
||||
0x26, // scroll leftwards (stock ticker mode)
|
||||
0, // placeholder
|
||||
7, // start 'page' (vertical)
|
||||
7, // scroll speed: 7=fastest,
|
||||
7, // end 'page'
|
||||
0, 0xff, // placeholders
|
||||
0x2f // start
|
||||
};
|
||||
uint8_t data[128];
|
||||
|
||||
for(int x=0; x<128; x++) {
|
||||
// each byte here is a vertical column, 8 pixels tall, MSB at bottom
|
||||
data[x] = (1<<(7 - (x%8)));
|
||||
}
|
||||
|
||||
oled_write_cmd_sequence(sizeof(setup), setup);
|
||||
oled_write_data(sizeof(data), data);
|
||||
oled_write_cmd_sequence(sizeof(animate), animate);
|
||||
}
|
||||
|
||||
// EOF
|
||||
33
stm32/mk4-bootloader/oled.h
Normal file
33
stm32/mk4-bootloader/oled.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#include "basics.h"
|
||||
|
||||
// need this many bytes for any update
|
||||
#define OLED_DRAW_SIZE 1024
|
||||
|
||||
extern void oled_setup(void);
|
||||
|
||||
// send a pre-compressed image to screen (complete)
|
||||
extern void oled_show(const uint8_t *pixels);
|
||||
|
||||
// .. same but add a progress bar
|
||||
extern void oled_show_progress(const uint8_t *pixels, int percent);
|
||||
|
||||
// send some bytes to screen
|
||||
extern void oled_show_raw(uint32_t len, const uint8_t *pixels);
|
||||
|
||||
// delay loop
|
||||
void sleep_ms(int n);
|
||||
|
||||
// show animated busy bar
|
||||
void oled_busy_bar(bool en);
|
||||
|
||||
// show just a progress bar in bottom 8 rows (destructive)
|
||||
void oled_draw_bar(int percent);
|
||||
|
||||
// just fun display in factory case
|
||||
void oled_factory_busy(void);
|
||||
|
||||
// EOF
|
||||
1222
stm32/mk4-bootloader/pins.c
Normal file
1222
stm32/mk4-bootloader/pins.c
Normal file
File diff suppressed because it is too large
Load Diff
118
stm32/mk4-bootloader/pins.h
Normal file
118
stm32/mk4-bootloader/pins.h
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*
|
||||
* pins.h -- everything to do with PIN's and their policies
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#include "basics.h"
|
||||
#include "ae.h"
|
||||
|
||||
// We hash it like we don't care, but PIN code is expected to be
|
||||
// just digits, no punctuation, and up to this many chars long.
|
||||
// Using pin+len rather than c-strings. Use zero-length for "blank" or "undefined" pins.
|
||||
//
|
||||
#define MAX_PIN_LEN 32
|
||||
|
||||
// Number of bytes (per pin) we are keeping secret.
|
||||
// ATECC[56]08A limitation/feature caps this at weird 72 byte value.
|
||||
#define AE_SECRET_LEN 72
|
||||
|
||||
// .. but on 608a, we can use this one weird data slot with more space
|
||||
#define AE_LONG_SECRET_LEN 416
|
||||
|
||||
// For change_flags field: choose one secret and/or one PIN only.
|
||||
#define CHANGE_WALLET_PIN 0x001
|
||||
#define CHANGE_DURESS_PIN 0x002
|
||||
#define CHANGE_BRICKME_PIN 0x004
|
||||
#define CHANGE_SECRET 0x008
|
||||
#define CHANGE_DURESS_SECRET 0x010
|
||||
#define CHANGE_SECONDARY_WALLET_PIN 0x020 // when used from main wallet only (obsolete)
|
||||
#define CHANGE_LS_OFFSET 0xf00 // v2: which 32-byte part of long-secret to affect
|
||||
#define CHANGE__MASK 0xf3f
|
||||
|
||||
// Magic value and/or version number.
|
||||
#define PA_MAGIC_V1 0x2eaf6311 // before v3.0.0 of main firmware (508a, mk1/2)
|
||||
#define PA_MAGIC_V2 0x2eaf6312
|
||||
|
||||
// For state_flags field: report only covers current wallet (primary vs. secondary)
|
||||
#define PA_SUCCESSFUL 0x01
|
||||
#define PA_IS_BLANK 0x02
|
||||
#define PA_HAS_DURESS 0x04
|
||||
#define PA_HAS_BRICKME 0x08
|
||||
#define PA_ZERO_SECRET 0x10
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic_value; // = PA_MAGIC
|
||||
int is_secondary; // (bool) primary or secondary [obsolete]
|
||||
char pin[MAX_PIN_LEN]; // value being attempted
|
||||
int pin_len; // valid length of pin
|
||||
uint32_t delay_achieved; // so far, how much time wasted? [obsolete]
|
||||
uint32_t delay_required; // how much will be needed? [obsolete]
|
||||
uint32_t num_fails; // for UI: number of fails PINs
|
||||
uint32_t attempts_left; // trys left until bricking
|
||||
uint32_t state_flags; // what things have been setup/enabled already
|
||||
uint32_t private_state; // some internal (encrypted) state [actually a nonce]
|
||||
uint8_t hmac[32]; // my hmac over above, or zeros
|
||||
// remaining fields are return values, or optional args;
|
||||
int change_flags; // bitmask of what to do
|
||||
char old_pin[MAX_PIN_LEN]; // (optional) old PIN value
|
||||
int old_pin_len; // (optional) valid length of old_pin, can be zero
|
||||
char new_pin[MAX_PIN_LEN]; // (optional) new PIN value
|
||||
int new_pin_len; // (optional) valid length of new_pin, can be zero
|
||||
uint8_t secret[AE_SECRET_LEN]; // secret to be changed / return value
|
||||
// may grow from here in future versions.
|
||||
uint8_t cached_main_pin[32]; // iff they provided right pin already
|
||||
} pinAttempt_t;
|
||||
|
||||
// For binary compatibility with Mark1/2 bootroms, the cached_main_pin is optional
|
||||
#define PIN_ATTEMPT_SIZE_V1 (176+AE_SECRET_LEN)
|
||||
#define PIN_ATTEMPT_SIZE_V2 (176+AE_SECRET_LEN+32)
|
||||
|
||||
// Errors codes
|
||||
enum {
|
||||
EPIN_HMAC_FAIL = -100,
|
||||
EPIN_HMAC_REQUIRED = -101,
|
||||
EPIN_BAD_MAGIC = -102,
|
||||
EPIN_RANGE_ERR = -103,
|
||||
EPIN_BAD_REQUEST = -104, // bad change flags
|
||||
EPIN_I_AM_BRICK = -105, // chip has been bricked
|
||||
EPIN_AE_FAIL = -106, // low-level fails; retry ok
|
||||
EPIN_MUST_WAIT = -107, // you haven't waited long enough
|
||||
EPIN_PIN_REQUIRED = -108, // must be non-zero length
|
||||
EPIN_WRONG_SUCCESS = -109, // success field is not what is needs to be
|
||||
EPIN_OLD_ATTEMPT = -110, // tried to recycle older attempt
|
||||
EPIN_AUTH_MISMATCH = -111, // need pin1 to change duress1, etc.
|
||||
EPIN_AUTH_FAIL = -112, // pin is wrong
|
||||
EPIN_OLD_AUTH_FAIL = -113, // existing pin is wrong (during change attempt)
|
||||
EPIN_PRIMARY_ONLY = -114, // only primary pin can change brickme
|
||||
};
|
||||
|
||||
// Get number of failed attempts on a PIN, since last success. Calculate
|
||||
// required delay, and setup initial struct for later attempts. Does not
|
||||
// attempt the PIN or return secrets if right.
|
||||
int pin_setup_attempt(pinAttempt_t *args);
|
||||
|
||||
// Delay for one time unit, and prove it. Doesn't check PIN value itself.
|
||||
int pin_delay(pinAttempt_t *args);
|
||||
|
||||
// Do the PIN check, and return a value. Or fail.
|
||||
int pin_login_attempt(pinAttempt_t *args);
|
||||
|
||||
// Change the PIN and/or secrets (must also know the value, or it must be blank)
|
||||
int pin_change(pinAttempt_t *args);
|
||||
|
||||
// To encourage not keeping the secret in memory, a way to fetch it after already prove you
|
||||
// know the PIN right.
|
||||
int pin_fetch_secret(pinAttempt_t *args);
|
||||
|
||||
// Record current flash checksum and make green light go on.
|
||||
int pin_firmware_greenlight(pinAttempt_t *args);
|
||||
|
||||
// Return 32 bits of bits which are presistently mapped from pin code; for anti-phishing feature.
|
||||
int pin_prefix_words(const char *pin_prefix, int prefix_len, uint32_t *result);
|
||||
|
||||
// Read/write the long secret. 32 bytes at a time.
|
||||
int pin_long_secret(pinAttempt_t *args);
|
||||
|
||||
// EOF
|
||||
103
stm32/mk4-bootloader/psram.c
Normal file
103
stm32/mk4-bootloader/psram.c
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*
|
||||
* Setup and talk with the ESP-PSRAM64H chip, new on Mk4.
|
||||
* See stm32l4xx_hal_qspi.h
|
||||
*
|
||||
*/
|
||||
#include "psram.h"
|
||||
#include <string.h>
|
||||
#include "delay.h"
|
||||
#include "stm32l4xx_hal.h"
|
||||
#include "console.h"
|
||||
|
||||
static QSPI_HandleTypeDef qh;
|
||||
|
||||
void
|
||||
psram_setup(void)
|
||||
{
|
||||
// enable clocks
|
||||
__HAL_RCC_QSPI_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOE_CLK_ENABLE();
|
||||
|
||||
// reset module
|
||||
__HAL_RCC_QSPI_FORCE_RESET();
|
||||
__HAL_RCC_QSPI_RELEASE_RESET();
|
||||
|
||||
// configure pins: Port E PE10-PE15
|
||||
GPIO_InitTypeDef setup = {
|
||||
.Pin = GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15,
|
||||
.Mode = GPIO_MODE_AF_PP, // not sure
|
||||
.Pull = GPIO_NOPULL, // not sure
|
||||
.Speed = GPIO_SPEED_FREQ_VERY_HIGH,
|
||||
.Alternate = GPIO_AF10_QUADSPI,
|
||||
};
|
||||
HAL_GPIO_Init(GPIOE, &setup);
|
||||
|
||||
#if 0
|
||||
QUADSPI_TypeDef *Instance; /* QSPI registers base address */
|
||||
QSPI_InitTypeDef Init; /* QSPI communication parameters */
|
||||
uint8_t *pTxBuffPtr; /* Pointer to QSPI Tx transfer Buffer */
|
||||
__IO uint32_t TxXferSize; /* QSPI Tx Transfer size */
|
||||
__IO uint32_t TxXferCount; /* QSPI Tx Transfer Counter */
|
||||
uint8_t *pRxBuffPtr; /* Pointer to QSPI Rx transfer Buffer */
|
||||
__IO uint32_t RxXferSize; /* QSPI Rx Transfer size */
|
||||
__IO uint32_t RxXferCount; /* QSPI Rx Transfer Counter */
|
||||
DMA_HandleTypeDef *hdma; /* QSPI Rx/Tx DMA Handle parameters */
|
||||
__IO HAL_LockTypeDef Lock; /* Locking object */
|
||||
__IO HAL_QSPI_StateTypeDef State; /* QSPI communication state */
|
||||
__IO uint32_t ErrorCode; /* QSPI Error code */
|
||||
uint32_t Timeout; /* Timeout for the QSPI memory access */
|
||||
#endif
|
||||
|
||||
memset(&qh, 0, sizeof(qh));
|
||||
|
||||
qh.Instance = QUADSPI;
|
||||
qh.Init.ClockPrescaler = 64; // conservative starting value
|
||||
qh.Init.FifoThreshold = 4; // indirect mode only
|
||||
qh.Init.FlashSize = 23;
|
||||
qh.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_8_CYCLE; // maxed for now
|
||||
qh.Init.ClockMode = QSPI_CLOCK_MODE_0; // low clock between ops
|
||||
qh.Init.FlashID = QSPI_FLASH_ID_1;
|
||||
qh.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
|
||||
|
||||
// module init
|
||||
HAL_StatusTypeDef rv = HAL_QSPI_Init(&qh);
|
||||
ASSERT(rv == HAL_OK);
|
||||
|
||||
// do some SPI commands first
|
||||
|
||||
QSPI_CommandTypeDef cmd = {
|
||||
.Instruction = 0x9f, // "read ID" command
|
||||
.InstructionMode = QSPI_INSTRUCTION_1_LINE,
|
||||
.Address = 0, // dont care
|
||||
.AddressSize = QSPI_ADDRESS_24_BITS,
|
||||
.AddressMode = QSPI_ADDRESS_1_LINE,
|
||||
.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE,
|
||||
.DummyCycles = 0,
|
||||
.DataMode = QSPI_DATA_1_LINE,
|
||||
.NbData = 8, // how much to read in bytes
|
||||
.DdrMode = QSPI_DDR_MODE_DISABLE, // normal
|
||||
.SIOOMode = QSPI_SIOO_INST_EVERY_CMD, // normal
|
||||
};
|
||||
|
||||
// Start "Indirection functional mode"
|
||||
uint8_t buf[8];
|
||||
rv = HAL_QSPI_Command(&qh, &cmd, HAL_MAX_DELAY);
|
||||
ASSERT(rv == HAL_OK);
|
||||
|
||||
rv = HAL_QSPI_Receive(&qh, buf, HAL_MAX_DELAY);
|
||||
|
||||
puts2("HAL_QSPI_Receive "); puthex2(rv); putchar('\n');
|
||||
hex_dump(buf, sizeof(buf));
|
||||
|
||||
#if 0
|
||||
QSPI_MemoryMappedTypeDef mmap = {
|
||||
};
|
||||
|
||||
rv = HAL_QSPI_MemoryMapped(&qh, &cmd, &mmap);
|
||||
ASSERT(rv == HAL_OK);
|
||||
#endif
|
||||
}
|
||||
|
||||
// EOF
|
||||
9
stm32/mk4-bootloader/psram.h
Normal file
9
stm32/mk4-bootloader/psram.h
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
#include "basics.h"
|
||||
|
||||
extern void psram_setup(void);
|
||||
|
||||
// EOF
|
||||
17
stm32/mk4-bootloader/releases/README.md
Normal file
17
stm32/mk4-bootloader/releases/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
# Releases (Bootloader)
|
||||
|
||||
In this directory we will capture public releases of the bootloader.
|
||||
|
||||
Github is nearly free, so why not capture all the actual bits!
|
||||
|
||||
|
||||
# Change log
|
||||
|
||||
- V1.2.1 - more bugfixes for Mark 2 boards
|
||||
- V1.2.0 - bugfix needed for Mark 2 boards (do not use this release)
|
||||
- V1.1.0 - security improvements against active MitM on our bus
|
||||
- V1.0.2 - bugfix: pin delay cleared systick interrupt enable
|
||||
- V1.0.1 - check signature over firmware before changing main flash
|
||||
- V1.0.0 - first public version
|
||||
|
||||
84
stm32/mk4-bootloader/rng.c
Normal file
84
stm32/mk4-bootloader/rng.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "rng.h"
|
||||
#include "basics.h"
|
||||
#include "stm32l4xx_hal.h"
|
||||
|
||||
// rng_setup()
|
||||
//
|
||||
void
|
||||
rng_setup(void)
|
||||
{
|
||||
if(RNG->CR & RNG_CR_RNGEN) {
|
||||
// already setup
|
||||
return;
|
||||
}
|
||||
|
||||
// Enable the Peripheral
|
||||
__HAL_RCC_RNG_CLK_ENABLE();
|
||||
|
||||
// Turn on feature.
|
||||
RNG->CR |= RNG_CR_RNGEN;
|
||||
|
||||
// Sample twice to be sure that we have a
|
||||
// valid RNG result.
|
||||
uint32_t chk = rng_sample();
|
||||
uint32_t chk2 = rng_sample();
|
||||
|
||||
// die if we are clearly not getting random values
|
||||
if(chk == 0 || chk == ~0
|
||||
|| chk2 == 0 || chk2 == ~0
|
||||
|| chk == chk2
|
||||
) {
|
||||
INCONSISTENT("bad rng");
|
||||
|
||||
while(1) ;
|
||||
}
|
||||
}
|
||||
|
||||
// rng_sample()
|
||||
//
|
||||
uint32_t
|
||||
rng_sample(void)
|
||||
{
|
||||
static uint32_t last_rng_result;
|
||||
|
||||
while(1) {
|
||||
// Check if data register contains valid random data
|
||||
while(!(RNG->SR & RNG_FLAG_DRDY)) {
|
||||
// busy wait; okay to get stuck here... better than failing.
|
||||
}
|
||||
|
||||
// Get the new number
|
||||
uint32_t rv = RNG->DR;
|
||||
|
||||
if(rv != last_rng_result && rv) {
|
||||
last_rng_result = rv;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// keep trying if not a new number
|
||||
}
|
||||
|
||||
// NOT-REACHED
|
||||
}
|
||||
|
||||
// rng_buffer()
|
||||
//
|
||||
void
|
||||
rng_buffer(uint8_t *result, int len)
|
||||
{
|
||||
while(len > 0) {
|
||||
uint32_t t = rng_sample();
|
||||
|
||||
memcpy(result, &t, MIN(4, len));
|
||||
|
||||
len -= 4;
|
||||
result += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// EOF
|
||||
10
stm32/mk4-bootloader/rng.h
Normal file
10
stm32/mk4-bootloader/rng.h
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
#include "basics.h"
|
||||
|
||||
void rng_setup(void);
|
||||
uint32_t rng_sample(void);
|
||||
void rng_buffer(uint8_t *result, int len);
|
||||
|
||||
411
stm32/mk4-bootloader/secel_config.py
Normal file
411
stm32/mk4-bootloader/secel_config.py
Normal file
@ -0,0 +1,411 @@
|
||||
# (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
# Secure Element Config Area.
|
||||
#
|
||||
# Bitwise details about the the ATECC608a and 508a "config" area, which determines what
|
||||
# you can and (mostly) cannot do with each private key in device.
|
||||
#
|
||||
# - you must contemplate the full datasheet at length
|
||||
# - as of Jul/2019 the 608a datasheet is under NDA, sorry.
|
||||
# - this file can be useful both in Micropython and CPython3
|
||||
#
|
||||
try:
|
||||
MPY=True
|
||||
from ubinascii import hexlify as b2a_hex
|
||||
from ubinascii import unhexlify as a2b_hex
|
||||
from uhashlib import sha256
|
||||
from ucollections import namedtuple
|
||||
import ustruct
|
||||
except ImportError:
|
||||
MPY=False
|
||||
from binascii import b2a_hex, a2b_hex
|
||||
from hashlib import sha256
|
||||
import struct
|
||||
|
||||
def secel_dump(blk, rnd=None, which_nums=range(16)):
|
||||
ASC = lambda b: str(b, 'ascii')
|
||||
|
||||
def hexdump(label, x):
|
||||
print(label + ASC(b2a_hex(x)) + (' len=%d'%len(x)))
|
||||
|
||||
#hexdump('SN: ', blk[0:4]+blk[8:13])
|
||||
hexdump('RevNum: ', blk[4:8])
|
||||
|
||||
# guessing this nibble in RevNum corresponds to chip 508a vs 608a
|
||||
print("Chip type: atecc%x08a" % ((blk[6]>>4)&0xf))
|
||||
partno = ((blk[6]>>4)&0xf)
|
||||
assert partno in [5, 6]
|
||||
|
||||
if partno == 6:
|
||||
print('AES_Enable = 0x%x' % (blk[13] & 0x1))
|
||||
|
||||
print('I2C_Enable = 0x%x' % (blk[14] & 0x1))
|
||||
if blk[14] & 0x01 == 0x01:
|
||||
print('I2C_Address = 0x%x' % (blk[16] >> 1))
|
||||
else:
|
||||
print('GPIO Mode = 0x%x' % (blk[16] & 0x3))
|
||||
print('GPIO Default = 0x%x' % ((blk[16]>>2) & 0x1))
|
||||
print('GPIO Detect (vs authout) = 0x%x' % ((blk[16]>>3) & 0x1))
|
||||
print('GPIO SignalKey/KeyId = 0x%x' % ((blk[16]>>4) & 0xf))
|
||||
print('I2C_Address(sic) = 0x%x' % blk[16])
|
||||
|
||||
if partno == 5:
|
||||
print('OTPmode = 0x%x' % blk[18])
|
||||
if partno == 6:
|
||||
print('CountMatchKey = 0x%x' % ((blk[18] >> 4)&0xf))
|
||||
print('CounterMatch enable = %d' % (blk[18] &0x1))
|
||||
|
||||
print('ChipMode = 0x%x' % blk[19])
|
||||
|
||||
print()
|
||||
|
||||
for i in which_nums:
|
||||
slot_conf = blk[20+(2*i):22+(2*i)]
|
||||
conf = SlotConfig.unpack(slot_conf)
|
||||
print(' Slot[%d] = 0x%s = %r' % (i, ASC(b2a_hex(slot_conf)), conf))
|
||||
|
||||
key_conf = blk[96+(2*i):2+96+(2*i)]
|
||||
|
||||
cls = KeyConfig_508 if partno == 5 else KeyConfig_608
|
||||
|
||||
print('KeyConfig[%d] = 0x%s = %r' % (i, ASC(b2a_hex(key_conf)),
|
||||
cls.unpack(key_conf)))
|
||||
|
||||
print()
|
||||
|
||||
hexdump('Counter[0]: ', blk[52:60])
|
||||
hexdump('Counter[1]: ', blk[60:68])
|
||||
if partno == 5:
|
||||
hexdump('LastKeyUse: ', blk[68:84])
|
||||
if partno == 6:
|
||||
print('UseLock = 0x%x' % blk[68])
|
||||
print('VolatileKeyPermission = 0x%x' % blk[69])
|
||||
hexdump('SecureBoot: ', blk[70:72])
|
||||
|
||||
print('KldfvLoc = 0x%x' % blk[72])
|
||||
hexdump('KdflvStr: ', blk[73:75])
|
||||
|
||||
# 75->83 reserved
|
||||
print('UserExtra = 0x%x' % blk[84])
|
||||
if partno == 5:
|
||||
print('Selector = 0x%x' % blk[85])
|
||||
if partno == 6:
|
||||
print('UserExtraAdd = 0x%x' % blk[85])
|
||||
|
||||
print('LockValue = 0x%x' % blk[86])
|
||||
print('LockConfig = 0x%x' % blk[87])
|
||||
hexdump('SlotLocked: ', blk[88:90])
|
||||
|
||||
if partno == 6:
|
||||
hexdump('ChipOptions: ', blk[90:92])
|
||||
print('ChipOptions = %r' % ChipOptions.unpack(blk[90:92]))
|
||||
|
||||
hexdump('X509format: ', blk[92:96])
|
||||
|
||||
if rnd is not None:
|
||||
hexdump('Random: ', rnd)
|
||||
|
||||
|
||||
if MPY:
|
||||
# XXX readonly version for micropython
|
||||
|
||||
def make_bitmask(name, defs):
|
||||
'''
|
||||
Take a list of bit widths and field names, and convert into a useful class.
|
||||
'''
|
||||
custom_t = namedtuple(name, [n for w,n in defs])
|
||||
|
||||
assert sum(w for w,n in defs) == 16
|
||||
|
||||
|
||||
class wrapper:
|
||||
def __init__(self, *a, **kws):
|
||||
if not a:
|
||||
a = [0] * len(defs)
|
||||
for idx, (_, nm) in enumerate(defs):
|
||||
if nm in kws:
|
||||
a[idx] = kws[nm]
|
||||
self.x = custom_t(*a)
|
||||
|
||||
@classmethod
|
||||
def unpack(cls, ss):
|
||||
v = ustruct.unpack('<H', ss)[0]
|
||||
pos = 0
|
||||
rv = []
|
||||
for w,n in defs:
|
||||
rv.append( (v >> pos) & ((1<<w)-1) )
|
||||
pos += w
|
||||
assert pos == 16
|
||||
return cls(*rv)
|
||||
|
||||
def pack(self):
|
||||
ss = 0
|
||||
pos = 0
|
||||
for w,n in defs:
|
||||
v = getattr(self.x, n)
|
||||
assert v < (1<<w), n+" is too big"
|
||||
ss |= (v << pos)
|
||||
pos += w
|
||||
assert pos == 16
|
||||
return ustruct.pack("<H", ss)
|
||||
|
||||
def as_int(self):
|
||||
return ustruct.unpack("<H", self.pack())[0]
|
||||
|
||||
def as_hex(self):
|
||||
return hex(self.as_int())
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self.x) + ('=0x%04x' % self.as_int())
|
||||
|
||||
return wrapper
|
||||
else:
|
||||
# full version for desktop... uses "namedlist" module to good effect
|
||||
from namedlist import namedlist
|
||||
|
||||
def make_bitmask(name, defs):
|
||||
'''
|
||||
Name a list of bit widths and names, and convert into a class.
|
||||
'''
|
||||
rv = namedlist(name, (n for w,n in defs), default=0)
|
||||
|
||||
assert sum(w for w,n in defs) == 16
|
||||
|
||||
@classmethod
|
||||
def unpack(cls, ss):
|
||||
v = struct.unpack('<H', ss)[0]
|
||||
pos = 0
|
||||
rv = {}
|
||||
for w,n in defs:
|
||||
rv[n] = (v >> pos) & ((1<<w)-1)
|
||||
pos += w
|
||||
assert pos == 16
|
||||
return cls(**rv)
|
||||
|
||||
def pack(self):
|
||||
ss = 0
|
||||
pos = 0
|
||||
for w,n in defs:
|
||||
v = getattr(self, n)
|
||||
assert v < (1<<w), n+" is too big"
|
||||
ss |= (v << pos)
|
||||
pos += w
|
||||
assert pos == 16
|
||||
return struct.pack("<H", ss)
|
||||
|
||||
|
||||
rv.unpack = unpack
|
||||
rv.pack = pack
|
||||
rv.as_int = lambda self: struct.unpack("<H", self.pack())[0]
|
||||
rv.as_hex = lambda self: hex(self.as_int())
|
||||
old_repr = rv.__repr__
|
||||
rv.__repr__ = lambda self: old_repr(self) + ('=0x%04x' % self.as_int())
|
||||
|
||||
return rv
|
||||
|
||||
# Section 2.2.5, Table 2-11: KeyConfig (Bytes 96 thru 127)
|
||||
KeyConfig_508 = make_bitmask('KeyConfig', [
|
||||
(1, 'Private'),
|
||||
(1, 'PubInfo'),
|
||||
(3, 'KeyType'),
|
||||
(1, 'Lockable'),
|
||||
(1, 'ReqRandom'),
|
||||
(1, 'ReqAuth'),
|
||||
(4, 'AuthKey'),
|
||||
(1, 'IntrusionDisable'),
|
||||
(1, 'RFU'),
|
||||
(2, 'X509id')])
|
||||
|
||||
# 608a: Section 2.2.13, Table 2-11: KeyConfig (Bytes 96 thru 127)
|
||||
KeyConfig_608 = make_bitmask('KeyConfig', [
|
||||
(1, 'Private'),
|
||||
(1, 'PubInfo'),
|
||||
(3, 'KeyType'),
|
||||
(1, 'Lockable'),
|
||||
(1, 'ReqRandom'),
|
||||
(1, 'ReqAuth'),
|
||||
(4, 'AuthKey'),
|
||||
(1, 'PersistentDisable'),
|
||||
(1, 'RFU'),
|
||||
(2, 'X509id')])
|
||||
|
||||
# Section 2.2.12, Table 2-5: SlotConfig (Bytes 20 to 51)
|
||||
SlotConfig = make_bitmask('SlotConfig', [
|
||||
(4, 'ReadKey'),
|
||||
(1, 'NoMac'),
|
||||
(1, 'LimitedUse'),
|
||||
(1, 'EncryptRead'),
|
||||
(1, 'IsSecret'),
|
||||
(4, 'WriteKey'),
|
||||
(4, 'WriteConfig')])
|
||||
|
||||
# 508a: Section 9.9, for the Info command (mode=State)
|
||||
InfoState_508 = make_bitmask('InfoState', [
|
||||
(4, 'TK_KeyId'),
|
||||
(1, 'TK_SourceFlag'),
|
||||
(1, 'TK_GenDigData'),
|
||||
(1, 'TK_GenKeyData'),
|
||||
(1, 'TK_NoMacFlag'),
|
||||
|
||||
(1, 'EEPROM_RNG'),
|
||||
(1, 'SRAM_RNG'),
|
||||
(1, 'AuthValid'),
|
||||
(4, 'AuthKey'),
|
||||
(1, 'TK_Valid') ])
|
||||
|
||||
# 608a: Section 11.8, for the Info command (mode=State)
|
||||
InfoState_608 = make_bitmask('InfoState', [
|
||||
(4, 'TK_KeyId'),
|
||||
(1, 'TK_SourceFlag'),
|
||||
(1, 'TK_GenDigData'),
|
||||
(1, 'TK_GenKeyData'),
|
||||
(1, 'TK_NoMacFlag'),
|
||||
|
||||
(2, 'zeros'),
|
||||
(1, 'AuthValid'),
|
||||
(4, 'AuthKey'),
|
||||
(1, 'TK_Valid') ])
|
||||
|
||||
# 608a: ChipOptions, offset 90 in EEPROM data
|
||||
# - the datasheet, in this one spot, lists the bits in MSB->LSB order, but elsewhere LSB->MSB
|
||||
# - bit numbers are right, and register isn't other endian, just the text backwards
|
||||
# - section 2.2.10 has in right order, but skips various bits in the register
|
||||
ChipOptions = make_bitmask('ChipOptions', [
|
||||
(1, 'POSTEnable'),
|
||||
(1, 'IOProtKeyEnable'),
|
||||
(1, 'KDFAESEnable'),
|
||||
(5, 'mustbezero'),
|
||||
(2, 'ECDHProt'),
|
||||
(2, 'KDFProt'),
|
||||
(4, 'IOProtKey'), ])
|
||||
|
||||
class ComboConfig(object):
|
||||
__slots__ = ['kc', 'sc', 'partno'] # block spelling mistakes
|
||||
|
||||
def __init__(self, partno=5):
|
||||
self.partno = partno
|
||||
self.kc = KeyConfig_508() if partno == 5 else KeyConfig_608()
|
||||
self.sc = SlotConfig(WriteConfig=0x8) # most restrictive
|
||||
|
||||
@property
|
||||
def is_ec_key(self):
|
||||
return (self.kc.KeyType == 4) # secp256r1
|
||||
|
||||
def ec_key(self, limited_sign=False):
|
||||
# basics for an EC key
|
||||
self.kc.KeyType = 4 # secp256r1
|
||||
self.kc.Private = 1 # is a EC private key
|
||||
self.kc.Lockable = 0 # normally set in stone
|
||||
self.sc.IsSecret = 1 # because is a private key
|
||||
self.sc.ReadKey = 0x2 if limited_sign else 0xf # allow CheckMac only, or all usages
|
||||
self.sc.WriteConfig = 0x2 # enable GenKey (not PrivWrite), no mac for key roll
|
||||
return self
|
||||
|
||||
def hash_key(self, write_kn=None, roll_kn=None):
|
||||
# basics for a hashing key (32-bytes of noise)
|
||||
self.kc.KeyType = 7 # not EC
|
||||
self.kc.Private = 0 # not an EC private key
|
||||
self.kc.ReqRandom = 1
|
||||
self.sc.NoMac = 0
|
||||
self.sc.IsSecret = 1 # because is a secret key
|
||||
self.sc.EncryptRead = 0 # no readback supported at all, even encrypted
|
||||
self.sc.ReadKey = 0xf # don't allow checkMac? Do allow other uses? IDK
|
||||
if write_kn is not None:
|
||||
assert 0 <= write_kn <= 15
|
||||
self.sc.WriteKey = write_kn
|
||||
self.sc.WriteConfig = 0x4 # encrypted writes allowed
|
||||
else:
|
||||
# 8="Never" - value must be written before data locked
|
||||
self.sc.WriteConfig = 0x8
|
||||
|
||||
if roll_kn is not None:
|
||||
assert write_kn is None
|
||||
self.sc.WriteKey = roll_kn
|
||||
self.sc.WriteConfig = 0x2 # see Table 2-0: enable Roll w/o MAC, still never write
|
||||
|
||||
return self
|
||||
|
||||
def for_storage(self, lockable=True):
|
||||
# public data storage, not secrets
|
||||
self.kc.KeyType = 7 # not EC
|
||||
self.kc.Private = 0 # not an EC private key
|
||||
self.kc.Lockable = int(lockable) # can delay slot locking
|
||||
self.sc.IsSecret = 0 # not a secret
|
||||
self.sc.ReadKey = 0x0 # allow checkMac
|
||||
self.sc.WriteConfig = 0 # permissive
|
||||
return self
|
||||
|
||||
def writeable_storage(self, write_kn, lockable=False):
|
||||
# public data storage but require key to update
|
||||
self.kc.KeyType = 7 # not EC
|
||||
self.kc.Private = 0 # not an EC private key
|
||||
self.kc.Lockable = int(lockable) # can delay slot locking
|
||||
self.sc.IsSecret = 0 # not a secret
|
||||
self.sc.ReadKey = 0x0 # allow checkMac
|
||||
# allow authenticated updates
|
||||
self.sc.WriteKey = write_kn
|
||||
self.sc.WriteConfig = 0x4 # encrypted writes allowed
|
||||
return self
|
||||
|
||||
def no_read(self):
|
||||
self.sc.IsSecret = 1 # because is a secret key
|
||||
self.sc.ReadKey = 0xf
|
||||
self.sc.EncryptRead = 0 # no readback supported at all, even encrypted
|
||||
return self
|
||||
|
||||
def secret_storage(self, rw_kn):
|
||||
# secret data storage, which can be updated repeatedly in the field
|
||||
assert 0 <= rw_kn <= 15
|
||||
self.kc.KeyType = 7 # not EC
|
||||
self.kc.Private = 0 # not an EC private key
|
||||
self.kc.Lockable = 0 # cannot lock the slot (would be DoS attack)
|
||||
self.kc.ReqRandom = 1 # rng must be part of nonce
|
||||
self.sc.IsSecret = 1 # shh.. secret
|
||||
self.sc.EncryptRead = 1 # always encrypted read required
|
||||
self.sc.ReadKey = rw_kn
|
||||
self.sc.WriteKey = rw_kn
|
||||
self.sc.WriteConfig = 0x4 # encrypted write, no DeriveKey support
|
||||
return self
|
||||
|
||||
def deterministic(self):
|
||||
# most keyslots should have ReqRandom=1 but if we're using it to hash up
|
||||
# a known value, like a PIN, then it can't be based on a nonce.
|
||||
self.kc.ReqRandom = 0
|
||||
return self
|
||||
|
||||
def require_auth(self, kn):
|
||||
# knowledge of another key will be required
|
||||
assert 0 <= kn <= 15
|
||||
self.kc.ReqAuth = 1
|
||||
self.kc.AuthKey = kn
|
||||
return self
|
||||
|
||||
def lockable(self, lockable):
|
||||
self.kc.Lockable = int(lockable) # can delay slot locking
|
||||
return self
|
||||
|
||||
def limited_use(self):
|
||||
self.sc.LimitedUse = 1 # counter0 will inc by one each use
|
||||
return self
|
||||
|
||||
def read_encrypted(self, kn):
|
||||
# readout allowed, but it's encrypted by key kn
|
||||
# "Reads from this slot are encrypted using the encryption algorithm
|
||||
# documented in Section 9.16, Read Command"
|
||||
assert 0 <= kn <= 15
|
||||
self.kc.EncryptRead = 1
|
||||
self.kc.ReadKey = kn
|
||||
self.kc.IsSecret = 1
|
||||
return self
|
||||
|
||||
def persistent_disable(self):
|
||||
assert self.partno == 6, '608a only'
|
||||
self.kc.PersistentDisable = 1
|
||||
return self
|
||||
|
||||
def is_aes_key(self):
|
||||
assert self.partno == 6, '608a only'
|
||||
self.kc.KeyType = 6 # for use with AES
|
||||
return self
|
||||
|
||||
# EOF
|
||||
60
stm32/mk4-bootloader/secel_debug.py
Normal file
60
stm32/mk4-bootloader/secel_debug.py
Normal file
@ -0,0 +1,60 @@
|
||||
# (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
# Secure Element Config debugging tools.
|
||||
#
|
||||
|
||||
from secel_config import secel_dump
|
||||
from binascii import b2a_hex, a2b_hex
|
||||
|
||||
# copy from ae_config.h
|
||||
# bytes [16..84) of chip config area
|
||||
AE_CHIP_CONFIG_1 = bytes([
|
||||
0xe1, 0x00, 0x61, 0x00, 0x00, 0x00, 0x87, 0x20, 0x8f, 0x80, \
|
||||
0x8f, 0x43, 0xaf, 0x80, 0x00, 0x43, 0x00, 0x43, 0x8f, 0x47, \
|
||||
0xc3, 0x43, 0xc3, 0x43, 0xc7, 0x47, 0x8f, 0x80, 0x00, 0x00, \
|
||||
0x8f, 0x4d, 0x8f, 0x43, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, \
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 \
|
||||
])
|
||||
|
||||
|
||||
# bytes [90..128) of chip config area
|
||||
AE_CHIP_CONFIG_2 = bytes([ \
|
||||
0x03, 0x15, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x33, 0x00, \
|
||||
0xbc, 0x01, 0xfc, 0x01, 0xfc, 0x01, 0x9c, 0x01, 0x9c, 0x01, \
|
||||
0xfc, 0x01, 0xdc, 0x03, 0xdc, 0x03, 0xdc, 0x07, 0xfc, 0x04, \
|
||||
0x3c, 0x00, 0xfc, 0x01, 0xdc, 0x01, 0x3c, 0x00 \
|
||||
])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
# observed values from unprogrammed devices
|
||||
ex_608 = a2b_hex('01236c4100006002bbe66928ee015400c0000000832087208f20c48f8f8f8f8f9f8faf8f0000000000000000000000000000af8fffffffff00000000ffffffff000000000000000000000000000000000000000000005555ffff0000000000003300330033001c001c001c001c001c003c003c003c003c003c003c003c001c00')
|
||||
ex_508 = a2b_hex('01233b7e00005000e9f5342beec05400c0005500832087208f20c48f8f8f8f8f9f8faf8f0000000000000000000000000000af8fffffffff00000000ffffffff00000000ffffffffffffffffffffffffffffffff00005555ffff0000000000003300330033001c001c001c001c001c003c003c003c003c003c003c003c001c00')
|
||||
|
||||
prob = bytes([
|
||||
0x1, 0x23, 0x97, 0x42, 0x0, 0x0, 0x60, 0x2, 0x78, 0x79, 0x3, 0x6c, 0xee,
|
||||
0x1, 0x54, 0x0, 0xe1, 0x0, 0x61, 0x0, 0x0, 0x0, 0x87, 0x20, 0x8f, 0x80,
|
||||
0x8f, 0x43, 0xaf, 0x80, 0x0, 0x43, 0x0, 0x43, 0x8f, 0x47, 0xc3, 0x43, 0xc3,
|
||||
0x43, 0xc7, 0x47, 0x8f, 0x80, 0x0, 0x0, 0x8f, 0x4d, 0x8f, 0x43, 0x0, 0x0,
|
||||
0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0x0, 0xfe, 0xff, 0x3, 0x15, 0x0, 0x0,
|
||||
0x0, 0x0, 0x3c, 0x0, 0x33, 0x0, 0xbc, 0x1, 0xfc, 0x1, 0xfc, 0x1, 0x9c, 0x1,
|
||||
0x9c, 0x1, 0xfc, 0x1, 0xdc, 0x3, 0xdc, 0x3, 0xdc, 0x7, 0xfc, 0x4, 0x3c, 0x0,
|
||||
0xfc, 0x1, 0xdc, 0x1, 0x3c, 0x0
|
||||
])
|
||||
assert len(prob) == 128
|
||||
|
||||
dev = ex_508
|
||||
|
||||
secel_dump(dev)
|
||||
|
||||
if 0:
|
||||
assert dev[16:84] == AE_CHIP_CONFIG_1
|
||||
assert dev[90:128] == AE_CHIP_CONFIG_2
|
||||
print("Bits are where they need to be")
|
||||
|
||||
# EOF
|
||||
435
stm32/mk4-bootloader/sflash.c
Normal file
435
stm32/mk4-bootloader/sflash.c
Normal file
@ -0,0 +1,435 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*
|
||||
* sflash.c -- talk to the serial flash
|
||||
*
|
||||
*/
|
||||
#include "sflash.h"
|
||||
#include <string.h>
|
||||
#include "delay.h"
|
||||
#include "stm32l4xx_hal.h"
|
||||
#include "sigheader.h"
|
||||
#include "verify.h"
|
||||
#include "faster_sha256.h"
|
||||
#include "oled.h"
|
||||
#include "dispatch.h"
|
||||
#include "storage.h"
|
||||
#include "gpio.h"
|
||||
#include "assets/screens.h"
|
||||
|
||||
// Connections:
|
||||
// - SPI2 port
|
||||
// - all port B
|
||||
//
|
||||
// SF_CS => PB9
|
||||
// SF_SCLK => PB10
|
||||
// SF_MISO => PC2
|
||||
// SF_MOSI => PC3
|
||||
|
||||
#define SF_CS_PIN GPIO_PIN_9 // port B
|
||||
#define SF_SPI_SCK GPIO_PIN_10 // port B
|
||||
#define SF_SPI_MISO GPIO_PIN_2 // port C
|
||||
#define SF_SPI_MOSI GPIO_PIN_3 // port C
|
||||
|
||||
#define CMD_WRSR 0x01
|
||||
#define CMD_WRITE 0x02
|
||||
#define CMD_READ 0x03
|
||||
#define CMD_FAST_READ 0x0b
|
||||
#define CMD_RDSR 0x05
|
||||
#define CMD_WREN 0x06
|
||||
#define CMD_SEC_ERASE 0x20
|
||||
#define CMD_RDCR 0x35
|
||||
#define CMD_RD_DEVID 0x9f
|
||||
#define CMD_CHIP_ERASE 0xc7
|
||||
|
||||
// active-low chip-select line
|
||||
#define CS_LOW() HAL_GPIO_WritePin(GPIOB, SF_CS_PIN, 0)
|
||||
#define CS_HIGH() HAL_GPIO_WritePin(GPIOB, SF_CS_PIN, 1)
|
||||
|
||||
static SPI_HandleTypeDef sf_spi_port;
|
||||
|
||||
uint32_t sf_completed_upgrade;
|
||||
|
||||
// sf_read_bytes()
|
||||
//
|
||||
static HAL_StatusTypeDef
|
||||
sf_read(uint32_t addr, int len, uint8_t *buf)
|
||||
{
|
||||
// send via SPI(1)
|
||||
uint8_t pkt[5] = { CMD_FAST_READ,
|
||||
(addr>>16) & 0xff, (addr >> 8) & 0xff, addr & 0xff,
|
||||
0x0 }; // for fast-read case
|
||||
|
||||
CS_LOW();
|
||||
|
||||
HAL_StatusTypeDef rv = HAL_SPI_Transmit(&sf_spi_port, pkt, sizeof(pkt), HAL_MAX_DELAY);
|
||||
if(rv == HAL_OK) {
|
||||
rv = HAL_SPI_Receive(&sf_spi_port, buf, len, HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
CS_HIGH();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
// sf_wait_wip_done()
|
||||
//
|
||||
static HAL_StatusTypeDef
|
||||
sf_wait_wip_done()
|
||||
{
|
||||
// read RDSR (status register) and busy-wait until
|
||||
// the write operation is done
|
||||
while(1) {
|
||||
uint8_t pkt = CMD_RDSR, stat = 0;
|
||||
|
||||
CS_LOW();
|
||||
|
||||
HAL_StatusTypeDef rv = HAL_SPI_Transmit(&sf_spi_port, &pkt, 1, HAL_MAX_DELAY);
|
||||
|
||||
if(rv == HAL_OK) {
|
||||
rv = HAL_SPI_Receive(&sf_spi_port, &stat, 1, HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
CS_HIGH();
|
||||
|
||||
if(rv != HAL_OK) return rv;
|
||||
|
||||
if(stat & 0x01) continue;
|
||||
|
||||
return HAL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// sf_write_enable()
|
||||
//
|
||||
static HAL_StatusTypeDef
|
||||
sf_write_enable(void)
|
||||
{
|
||||
uint8_t pkt = CMD_WREN;
|
||||
|
||||
CS_LOW();
|
||||
|
||||
HAL_StatusTypeDef rv = HAL_SPI_Transmit(&sf_spi_port, &pkt, 1, HAL_MAX_DELAY);
|
||||
|
||||
CS_HIGH();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// sf_write()
|
||||
//
|
||||
static HAL_StatusTypeDef
|
||||
sf_write(uint32_t addr, int len, const uint8_t *buf)
|
||||
{
|
||||
// enable writing
|
||||
HAL_StatusTypeDef rv = sf_write_enable();
|
||||
if(rv) return rv;
|
||||
|
||||
// do a "PAGE Program" aka. write
|
||||
uint8_t pkt[4] = { CMD_WRITE,
|
||||
(addr>>16) & 0xff, (addr >> 8) & 0xff, addr & 0xff
|
||||
};
|
||||
|
||||
CS_LOW();
|
||||
|
||||
rv = HAL_SPI_Transmit(&sf_spi_port, pkt, sizeof(pkt), HAL_MAX_DELAY);
|
||||
if(rv == HAL_OK) {
|
||||
rv = HAL_SPI_Transmit(&sf_spi_port, (uint8_t *)buf, len, HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
CS_HIGH();
|
||||
|
||||
if(rv == HAL_OK) {
|
||||
rv = sf_wait_wip_done();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// sf_sector_erase()
|
||||
//
|
||||
// Erase 4k of data (smallest possible amount).
|
||||
//
|
||||
static HAL_StatusTypeDef
|
||||
sf_sector_erase(uint32_t addr)
|
||||
{
|
||||
sf_write_enable();
|
||||
/*
|
||||
self.cmd(CMD_SEC_ERASE, address)
|
||||
def is_busy(self):
|
||||
# return status of WIP = Write In Progress bit
|
||||
r = self.read_reg(CMD_RDSR, 1)
|
||||
return bool(r[0] & 0x01)
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
|
||||
// sf_setup()
|
||||
//
|
||||
// Ok to call this lots.
|
||||
//
|
||||
void
|
||||
sf_setup(void)
|
||||
{
|
||||
HAL_StatusTypeDef rv;
|
||||
|
||||
// enable some internal clocks
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOC_CLK_ENABLE();
|
||||
__HAL_RCC_SPI2_CLK_ENABLE();
|
||||
|
||||
// simple pins
|
||||
GPIO_InitTypeDef setup = {
|
||||
.Pin = SF_CS_PIN,
|
||||
.Mode = GPIO_MODE_OUTPUT_PP,
|
||||
.Pull = GPIO_NOPULL,
|
||||
.Speed = GPIO_SPEED_FREQ_MEDIUM,
|
||||
.Alternate = 0,
|
||||
};
|
||||
HAL_GPIO_Init(GPIOB, &setup);
|
||||
|
||||
// starting value: high
|
||||
HAL_GPIO_WritePin(GPIOB, SF_CS_PIN, 1);
|
||||
|
||||
// SPI pins, on various ports
|
||||
setup.Pin = SF_SPI_SCK;
|
||||
setup.Mode = GPIO_MODE_AF_PP;
|
||||
setup.Alternate = GPIO_AF5_SPI2;
|
||||
HAL_GPIO_Init(GPIOB, &setup);
|
||||
|
||||
setup.Pin = SF_SPI_MOSI | SF_SPI_MISO;
|
||||
HAL_GPIO_Init(GPIOC, &setup);
|
||||
|
||||
memset(&sf_spi_port, 0, sizeof(sf_spi_port));
|
||||
|
||||
sf_spi_port.Instance = SPI2;
|
||||
|
||||
// see SPI_InitTypeDef
|
||||
sf_spi_port.Init.Mode = SPI_MODE_MASTER;
|
||||
sf_spi_port.Init.Direction = SPI_DIRECTION_2LINES;
|
||||
sf_spi_port.Init.DataSize = SPI_DATASIZE_8BIT;
|
||||
sf_spi_port.Init.CLKPolarity = SPI_POLARITY_LOW;
|
||||
sf_spi_port.Init.CLKPhase = SPI_PHASE_1EDGE;
|
||||
sf_spi_port.Init.NSS = SPI_NSS_SOFT;
|
||||
sf_spi_port.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; // conservative
|
||||
sf_spi_port.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
||||
sf_spi_port.Init.TIMode = SPI_TIMODE_DISABLED;
|
||||
sf_spi_port.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;
|
||||
|
||||
rv = HAL_SPI_Init(&sf_spi_port);
|
||||
ASSERT(!rv);
|
||||
}
|
||||
|
||||
// sf_do_upgrade()
|
||||
//
|
||||
// Copy from SPI flash to real flash, at final executable location.
|
||||
//
|
||||
static void
|
||||
sf_do_upgrade(uint32_t size)
|
||||
{
|
||||
ASSERT(size >= FW_MIN_LENGTH);
|
||||
|
||||
flash_setup0();
|
||||
flash_unlock();
|
||||
|
||||
uint8_t tmp[256] __attribute__((aligned(8)));
|
||||
|
||||
for(uint32_t pos=0; pos<size; pos += sizeof(tmp)) {
|
||||
// show some progress
|
||||
if((pos % 4096) == 0) {
|
||||
oled_show_progress(screen_upgrading, pos*100/size);
|
||||
}
|
||||
|
||||
if(sf_read(pos, sizeof(tmp), tmp) != HAL_OK) {
|
||||
INCONSISTENT();
|
||||
}
|
||||
|
||||
uint32_t addr = FIRMWARE_START + pos;
|
||||
uint64_t *b = (uint64_t *)tmp;
|
||||
|
||||
for(int i=0; i<sizeof(tmp)/sizeof(uint64_t); i++) {
|
||||
int rv;
|
||||
|
||||
if(addr % FLASH_PAGE_SIZE == 0) {
|
||||
rv = flash_page_erase(addr);
|
||||
ASSERT(rv == 0);
|
||||
}
|
||||
|
||||
rv = flash_burn(addr, *(b++));
|
||||
ASSERT(rv == 0);
|
||||
addr += sizeof(uint64_t);
|
||||
}
|
||||
|
||||
if(dfu_button_pressed() && !flash_is_security_level2()) {
|
||||
flash_lock();
|
||||
|
||||
dfu_by_request();
|
||||
// NOT-REACHED
|
||||
}
|
||||
}
|
||||
|
||||
flash_lock();
|
||||
}
|
||||
|
||||
// sf_calc_checksum()
|
||||
//
|
||||
// Do double-sha256 of the contents of the firmware upgrade, presently
|
||||
// in SPI flash. Similar to checksum_flash() in verify.c except only
|
||||
// concerned with firmware, not the rest of flash.
|
||||
//
|
||||
void
|
||||
sf_calc_checksum(const coldcardFirmwareHeader_t *hdr, uint8_t fw_digest[32])
|
||||
{
|
||||
|
||||
SHA256_CTX ctx;
|
||||
uint32_t total_len = hdr->firmware_length;
|
||||
|
||||
sha256_init(&ctx);
|
||||
|
||||
uint32_t pos = 0;
|
||||
uint8_t buf[128];
|
||||
STATIC_ASSERT(FW_HEADER_OFFSET % sizeof(buf) == 0);
|
||||
|
||||
oled_show_progress(screen_verify, 1);
|
||||
|
||||
// do part up to header.
|
||||
for(; pos < FW_HEADER_OFFSET; pos += sizeof(buf)) {
|
||||
if(sf_read(pos, sizeof(buf), buf) != HAL_OK) {
|
||||
fail:
|
||||
// fail for sure with bad signature; user can try again
|
||||
memset(fw_digest, 0, 32);
|
||||
return;
|
||||
}
|
||||
|
||||
sha256_update(&ctx, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
// include file header (but not the signature)
|
||||
ASSERT(pos == FW_HEADER_OFFSET);
|
||||
sha256_update(&ctx, (const uint8_t *)hdr, FW_HEADER_SIZE - 64);
|
||||
|
||||
// then the rest after the 'header' ... the useful firmware
|
||||
pos += FW_HEADER_SIZE;
|
||||
|
||||
for(int count=0; pos < total_len; pos += sizeof(buf), count++) {
|
||||
if(sf_read(pos, sizeof(buf), buf) != HAL_OK) {
|
||||
goto fail;
|
||||
}
|
||||
sha256_update(&ctx, buf, sizeof(buf));
|
||||
|
||||
if((count % 16) == 0) {
|
||||
int percent = (pos * 100) / total_len;
|
||||
oled_show_progress(screen_verify, percent);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(pos == hdr->firmware_length);
|
||||
|
||||
sha256_final(&ctx, fw_digest);
|
||||
|
||||
// double SHA256
|
||||
sha256_init(&ctx);
|
||||
sha256_update(&ctx, fw_digest, 32);
|
||||
sha256_final(&ctx, fw_digest);
|
||||
}
|
||||
|
||||
// sf_firmware_upgrade()
|
||||
//
|
||||
// maybe upgrade to a firmware image found in sflash
|
||||
//
|
||||
void
|
||||
sf_firmware_upgrade(void)
|
||||
{
|
||||
coldcardFirmwareHeader_t hdr = {};
|
||||
|
||||
// simple: just read in right spot to see header.
|
||||
sf_setup();
|
||||
|
||||
if(sf_read(FW_HEADER_OFFSET, sizeof(hdr), (void *)&hdr) != HAL_OK) {
|
||||
// hardware issues, keep going
|
||||
return;
|
||||
}
|
||||
|
||||
if(!verify_header(&hdr)) {
|
||||
// something wrong with it. might be noise, blank or otherwise. Not an error.
|
||||
puts("SPI flash: nope");
|
||||
return;
|
||||
}
|
||||
|
||||
// We have a good header so we can assume whole file there properly, right? (We could
|
||||
// check the signature first, but would be super slow.) And yet,
|
||||
// if you unpluged during the 'upload' process, after first part written, but before
|
||||
// you get to the end, we'd be bricked. Plus that seems really likely to happen.
|
||||
//
|
||||
// Solution: Look for a duplicated header at end of file. Will always write that last,
|
||||
// and even do a checksum over the data uploaded into the sflash before writing final
|
||||
// header out.
|
||||
//
|
||||
uint32_t off = hdr.firmware_length;
|
||||
|
||||
coldcardFirmwareHeader_t hdr2 = {};
|
||||
if(sf_read(off, sizeof(hdr2), (void *)&hdr2) != HAL_OK) {
|
||||
// Huh??? Hardware issue?
|
||||
return;
|
||||
}
|
||||
|
||||
if(memcmp(&hdr, &hdr2, sizeof(hdr)) != 0) {
|
||||
// mismatch? -- erase stuff to recover? Or just leave it?
|
||||
return;
|
||||
}
|
||||
|
||||
// We might upgrade now ... but only want to try once, so wipe the
|
||||
// second header to assure that we won't get stuck in an upgrade loop.
|
||||
//
|
||||
// LATER: if they unplug power part way thru, they land in fully-bricked mode,
|
||||
// even tho we have enough data (from SPI) to complete upgrade successfully.
|
||||
// So only clear flash once we've comlpeted successfully, or determined it
|
||||
// cannot work (bad signature, etc).
|
||||
|
||||
// Check for downgrade attack: show warning and stop.
|
||||
if(check_is_downgrade(hdr.timestamp, (const char *)hdr.version_string)) {
|
||||
oled_show(screen_downgrade);
|
||||
|
||||
fail:{
|
||||
// prevent second attempts. pointless
|
||||
uint8_t zeros[128] = { 0 };
|
||||
sf_write(off, sizeof(zeros), zeros);
|
||||
}
|
||||
|
||||
LOCKUP_FOREVER();
|
||||
}
|
||||
|
||||
// Check the firmware signature before changing main flash at all.
|
||||
uint8_t fw_digest[32];
|
||||
sf_calc_checksum(&hdr, fw_digest);
|
||||
|
||||
bool ok = verify_signature(&hdr, fw_digest);
|
||||
if(!ok) {
|
||||
// Bad signature over SPI contents; might be corruption or bad signature
|
||||
// We would not run the resulting firmware in main flash, so don't erase
|
||||
// what we have there now and abort.
|
||||
oled_show(screen_corrupt);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Start the upgrade ... takes about a minute.
|
||||
sf_do_upgrade(hdr.firmware_length);
|
||||
|
||||
if(hdr.install_flags & FWHIF_HIGH_WATER) {
|
||||
// Maybe set a new high-waterlevel for future versions.
|
||||
// Ignore failures, since we can't recover anyway.
|
||||
record_highwater_version(hdr.timestamp);
|
||||
}
|
||||
|
||||
// We're done, so clear header
|
||||
uint8_t zeros[128] = { 0 };
|
||||
sf_write(off, sizeof(zeros), zeros);
|
||||
|
||||
// Tell python, ultimately, that it worked.
|
||||
sf_completed_upgrade = SF_COMPLETED_UPGRADE;
|
||||
}
|
||||
|
||||
// EOF
|
||||
13
stm32/mk4-bootloader/sflash.h
Normal file
13
stm32/mk4-bootloader/sflash.h
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#include "basics.h"
|
||||
|
||||
#define SF_COMPLETED_UPGRADE 0xb50d5c24
|
||||
extern uint32_t sf_completed_upgrade;
|
||||
|
||||
extern void sf_setup(void);
|
||||
|
||||
// maybe upgrade to a firmware image found in sflash
|
||||
void sf_firmware_upgrade(void);
|
||||
|
||||
84
stm32/mk4-bootloader/sigheader.h
Normal file
84
stm32/mk4-bootloader/sigheader.h
Normal file
@ -0,0 +1,84 @@
|
||||
// (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
//
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
// Our simple firmware header.
|
||||
//
|
||||
// Although called a header, this data is placed into the middle of the binary.
|
||||
// It is located at start of firmware + 16k - sizeof(heaer). This is a gap unused in normal
|
||||
// micropython layout. Exactly the last 64 bytes (signature) should be left out of
|
||||
// the checksum. We do checksum areas beyond the end of the last byte of firmware (up to length)
|
||||
// and expect those regions to be unprogrammed flash (ones).
|
||||
//
|
||||
// - timestamp must increase with each upgrade (downgrade protection)
|
||||
// - version_string is for humans only
|
||||
// - pubkey_num indicates which pubkey was used for signature
|
||||
// - firmware_length, must be:
|
||||
// - bigger than minimum length, less than max
|
||||
// - 512-byte aligned
|
||||
// - bootloader assumes the flash filesystem (FAT FS) follows the firmware.
|
||||
// - this C header file is somewhat parsed and used by python signature-adding code
|
||||
// - timestamp is YYMMDDHHMMSS0000 in BCD
|
||||
//
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic_value; // fixed magic value
|
||||
uint8_t timestamp[8]; // for downgrade protection, this must increase
|
||||
uint8_t version_string[8]; // zero-terminated string: "1.0.0ab7" for humans
|
||||
uint32_t pubkey_num; // which pubkey was used to sign binary
|
||||
uint32_t firmware_length; // must be 512-aligned, and marks start of flash filesystem
|
||||
uint32_t install_flags; // flags about this release
|
||||
uint32_t hw_compat; // which hardware can run this release
|
||||
uint32_t future[7]; // reserved words
|
||||
uint8_t signature[64]; // signature over secp256k1
|
||||
} coldcardFirmwareHeader_t;
|
||||
|
||||
#define FW_HEADER_SIZE 128
|
||||
#define FW_HEADER_OFFSET (0x4000-FW_HEADER_SIZE)
|
||||
|
||||
#define FW_HEADER_MAGIC 0xCC001234
|
||||
|
||||
// Firmware Image Size
|
||||
|
||||
// arbitrary min size
|
||||
#define FW_MIN_LENGTH (256*1024)
|
||||
|
||||
// absolute max size: 1MB flash - 32k for bootloader
|
||||
// practical limit for our-protocol USB upgrades: 786432 (or else settings damaged)
|
||||
#define FW_MAX_LENGTH (0x100000 - 0x8000)
|
||||
|
||||
// Arguments to be used w/ python's struct module.
|
||||
#define FWH_PY_FORMAT "<I8s8sIIII28s64s"
|
||||
#define FWH_PY_VALUES "magic_value timestamp version_string pubkey_num firmware_length install_flags hw_compat future signature"
|
||||
#define FWH_NUM_FUTURE 7
|
||||
|
||||
// offset of pubkey number
|
||||
#define FWH_PK_NUM_OFFSET 20
|
||||
|
||||
// Bits in install_flags
|
||||
#define FWHIF_HIGH_WATER 0x01
|
||||
|
||||
// Bits in hw_compat
|
||||
#define MK_1_OK 0x01
|
||||
#define MK_2_OK 0x02
|
||||
#define MK_3_OK 0x04
|
||||
// RFU:
|
||||
#define MK_4_OK 0x08
|
||||
#define MK_5_OK 0x10
|
||||
|
||||
// There is a copy of the header at this location in RAM, copied by bootloader
|
||||
// **after** it has been verified. If you write to this memory area, you will be reset!
|
||||
#define RAM_HEADER_BASE 0x10007c20
|
||||
|
||||
// Original copy of header, as recorded in flash/firmware file.
|
||||
#define FLASH_HEADER_BASE 0x0800bf80
|
||||
|
||||
// One 32-bit word of flags from bootloader about how we got here (in protected RAM)
|
||||
#define RAM_BOOT_FLAGS (RAM_HEADER_BASE+FW_HEADER_SIZE)
|
||||
|
||||
// Bitmask for RAM_BOOT_FLAGS
|
||||
// - just did the SPI->flash copy on this bootup?
|
||||
#define RBF_FRESH_VERSION 0x01
|
||||
// - factory mode: flash not yet locked-down
|
||||
#define RBF_FACTORY_MODE 0x02
|
||||
71
stm32/mk4-bootloader/sigheader.py
Normal file
71
stm32/mk4-bootloader/sigheader.py
Normal file
@ -0,0 +1,71 @@
|
||||
# Autogen'ed file, don't edit. See bootloader/sigheader.h for original
|
||||
|
||||
# (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
|
||||
# Our simple firmware header.
|
||||
# Although called a header, this data is placed into the middle of the binary.
|
||||
# It is located at start of firmware + 16k - sizeof(heaer). This is a gap unused in normal
|
||||
# micropython layout. Exactly the last 64 bytes (signature) should be left out of
|
||||
# the checksum. We do checksum areas beyond the end of the last byte of firmware (up to length)
|
||||
# and expect those regions to be unprogrammed flash (ones).
|
||||
# - timestamp must increase with each upgrade (downgrade protection)
|
||||
# - version_string is for humans only
|
||||
# - pubkey_num indicates which pubkey was used for signature
|
||||
# - firmware_length, must be:
|
||||
# - bigger than minimum length, less than max
|
||||
# - 512-byte aligned
|
||||
# - bootloader assumes the flash filesystem (FAT FS) follows the firmware.
|
||||
# - this C header file is somewhat parsed and used by python signature-adding code
|
||||
# - timestamp is YYMMDDHHMMSS0000 in BCD
|
||||
|
||||
|
||||
FW_HEADER_SIZE = 128
|
||||
FW_HEADER_OFFSET = (0x4000-FW_HEADER_SIZE)
|
||||
|
||||
FW_HEADER_MAGIC = 0xCC001234
|
||||
|
||||
# Firmware Image Size
|
||||
|
||||
# arbitrary min size
|
||||
FW_MIN_LENGTH = (256*1024)
|
||||
|
||||
# absolute max size: 1MB flash - 32k for bootloader
|
||||
# practical limit for our-protocol USB upgrades: 786432 (or else settings damaged)
|
||||
FW_MAX_LENGTH = (0x100000 - 0x8000)
|
||||
|
||||
# Arguments to be used w/ python's struct module.
|
||||
FWH_PY_FORMAT = "<I8s8sIIII28s64s"
|
||||
FWH_PY_VALUES = "magic_value timestamp version_string pubkey_num firmware_length install_flags hw_compat future signature"
|
||||
FWH_NUM_FUTURE = 7
|
||||
|
||||
# offset of pubkey number
|
||||
FWH_PK_NUM_OFFSET = 20
|
||||
|
||||
# Bits in install_flags
|
||||
FWHIF_HIGH_WATER = 0x01
|
||||
|
||||
# Bits in hw_compat
|
||||
MK_1_OK = 0x01
|
||||
MK_2_OK = 0x02
|
||||
MK_3_OK = 0x04
|
||||
# RFU:
|
||||
MK_4_OK = 0x08
|
||||
MK_5_OK = 0x10
|
||||
|
||||
# There is a copy of the header at this location in RAM, copied by bootloader
|
||||
# **after** it has been verified. If you write to this memory area, you will be reset!
|
||||
RAM_HEADER_BASE = 0x10007c20
|
||||
|
||||
# Original copy of header, as recorded in flash/firmware file.
|
||||
FLASH_HEADER_BASE = 0x0800bf80
|
||||
|
||||
# One 32-bit word of flags from bootloader about how we got here (in protected RAM)
|
||||
RAM_BOOT_FLAGS = (RAM_HEADER_BASE+FW_HEADER_SIZE)
|
||||
|
||||
# Bitmask for RAM_BOOT_FLAGS
|
||||
# - just did the SPI->flash copy on this bootup?
|
||||
RBF_FRESH_VERSION = 0x01
|
||||
# - factory mode: flash not yet locked-down
|
||||
RBF_FACTORY_MODE = 0x02
|
||||
|
||||
# EOF
|
||||
153
stm32/mk4-bootloader/startup.S
Normal file
153
stm32/mk4-bootloader/startup.S
Normal file
@ -0,0 +1,153 @@
|
||||
// starting value for the top of our stack.
|
||||
#define OUR_STACK (BL_SRAM_BASE+BL_SRAM_SIZE)
|
||||
|
||||
.thumb
|
||||
.syntax unified
|
||||
|
||||
.text
|
||||
.section .entry_code
|
||||
|
||||
.global reset_entry
|
||||
.global vector_table
|
||||
.global firewall_starts
|
||||
.global bootloader_info
|
||||
.global my_version_code
|
||||
|
||||
// NOTE: Little attempt to support anything but reset vector here.
|
||||
//
|
||||
vector_table:
|
||||
.word OUR_STACK // initial stack value: near top of SRAM2
|
||||
.word reset_entry // verify: must be odd, to indicate Thumb mode
|
||||
.word NMI_Handler // placeholder / debug aid
|
||||
.word HardFault_Handler
|
||||
.word MemManage_Handler
|
||||
.word BusFault_Handler
|
||||
.word UsageFault_Handler
|
||||
|
||||
|
||||
// Debug aids: just die but in a way a debugger can maybe see why.
|
||||
|
||||
.type NMI_Handler, %function
|
||||
NMI_Handler:
|
||||
bkpt 1
|
||||
|
||||
.type HardFault_Handler, %function
|
||||
HardFault_Handler:
|
||||
bkpt 2
|
||||
|
||||
.type MemManage_Handler, %function
|
||||
MemManage_Handler:
|
||||
bkpt 3
|
||||
|
||||
.type BusFault_Handler, %function
|
||||
BusFault_Handler:
|
||||
bkpt 4
|
||||
|
||||
.type UsageFault_Handler, %function
|
||||
UsageFault_Handler:
|
||||
bkpt 5
|
||||
|
||||
b .
|
||||
|
||||
// NOTES:
|
||||
// - fixed at 0x8000040
|
||||
// - these ptrs are used by Micropython code
|
||||
bootloader_info:
|
||||
.align 6
|
||||
|
||||
.word callgate_entry0 // start of callgate (expect 08000x05)
|
||||
my_version_code:
|
||||
.word 0x100 // callgate protcol version, in BDC, unused
|
||||
.word 0 // reserved words
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
|
||||
|
||||
.align 4
|
||||
.ascii "(c) Copyright 2018-2022 by Coinkite Inc. \n"
|
||||
.ascii " \n"
|
||||
.ascii "This space for rent! Just 1BTC/year. \n"
|
||||
.ascii " \n"
|
||||
|
||||
//
|
||||
// Remainder is flexible for location
|
||||
//
|
||||
|
||||
.align 2
|
||||
.type reset_entry, %function // critical to have this, marks thumb entry pt
|
||||
reset_entry:
|
||||
|
||||
// do the critical one-time setup of firewall
|
||||
bl firewall_setup
|
||||
|
||||
// init some other things, maybe the screen
|
||||
mov r0, -1
|
||||
mov r1, 0
|
||||
mov r2, 0
|
||||
mov r3, 0
|
||||
bl callgate_entry0
|
||||
|
||||
// get a ptr to real code
|
||||
// load R1 with 0x08008000 value: start of firmware's area
|
||||
movw r1, (0x08008000 >> 12)
|
||||
lsl r1, 12
|
||||
|
||||
// set stack pointer to their preference
|
||||
ldr r0, [r1]
|
||||
mov sp, r0
|
||||
|
||||
// Read reset vector, and jump to it.
|
||||
mov r0, 1 // set reset_mode arg: 1=normal?
|
||||
ldr lr, [r1, 4]
|
||||
bx lr
|
||||
|
||||
|
||||
//
|
||||
// Firewalled region starts here, must be 0x100 aligned.
|
||||
//
|
||||
.section .firewall_code
|
||||
.align 8
|
||||
firewall_starts:
|
||||
.word 0x0f193a11 // my elite-speak is terrible
|
||||
.type callgate_entry0, %function // critical to have this, marks thumb entry pt
|
||||
callgate_entry0:
|
||||
|
||||
// Wipe our sram completely
|
||||
// CONCERN: damages r9, r10
|
||||
movw r9, BL_SRAM_BASE & 0xffff
|
||||
movt r9, BL_SRAM_BASE >> 16
|
||||
mov r10, BL_SRAM_SIZE
|
||||
add r10, r9, r10
|
||||
|
||||
wipe_loop1:
|
||||
str r10, [r9], +4 // will write 0x10008000
|
||||
cmp r9, r10
|
||||
bne wipe_loop1
|
||||
|
||||
// switch to our own stack (but save caller's stack ptr)
|
||||
mov r10, sp
|
||||
mov sp, r9
|
||||
|
||||
push {r10, lr}
|
||||
// do the real work
|
||||
dispatcher: // just for debuger view
|
||||
bl firewall_dispatch
|
||||
|
||||
pop {r10, lr}
|
||||
mov sp, r10
|
||||
|
||||
// clear our sram completely
|
||||
movw r9, BL_SRAM_BASE & 0xffff
|
||||
movt r9, BL_SRAM_BASE >> 16
|
||||
mov r10, BL_SRAM_SIZE
|
||||
add r10, r9, r10
|
||||
|
||||
wipe_loop2:
|
||||
str r0, [r9], +4
|
||||
cmp r9, r10
|
||||
bne wipe_loop2
|
||||
|
||||
bx lr
|
||||
|
||||
.end
|
||||
379
stm32/mk4-bootloader/stm32l4xx_hal_conf.h
Normal file
379
stm32/mk4-bootloader/stm32l4xx_hal_conf.h
Normal file
@ -0,0 +1,379 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file stm32l4xx_hal_conf.h
|
||||
* @author MCD Application Team
|
||||
* @version V1.2.0
|
||||
* @date 25-November-2015
|
||||
* @brief HAL configuration template file.
|
||||
* This file should be copied to the application folder and renamed
|
||||
* to stm32l4xx_hal_conf.h.
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
|
||||
*
|
||||
* 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 STMicroelectronics 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/* Define to prevent recursive inclusion -------------------------------------*/
|
||||
#ifndef __STM32L4xx_HAL_CONF_H
|
||||
#define __STM32L4xx_HAL_CONF_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define USE_USB_FS
|
||||
/* Exported types ------------------------------------------------------------*/
|
||||
/* Exported constants --------------------------------------------------------*/
|
||||
|
||||
/* ########################## Module Selection ############################## */
|
||||
/**
|
||||
* @brief This is the list of modules to be used in the HAL driver
|
||||
*/
|
||||
#define HAL_MODULE_ENABLED
|
||||
//#define HAL_ADC_MODULE_ENABLED
|
||||
//#define HAL_CAN_MODULE_ENABLED
|
||||
/* #define HAL_COMP_MODULE_ENABLED */
|
||||
#define HAL_CORTEX_MODULE_ENABLED
|
||||
/* #define HAL_CRC_MODULE_ENABLED */
|
||||
/* #define HAL_CRYP_MODULE_ENABLED */
|
||||
//#define HAL_DAC_MODULE_ENABLED
|
||||
/* #define HAL_DFSDM_MODULE_ENABLED */
|
||||
#define HAL_DMA_MODULE_ENABLED
|
||||
#define HAL_FIREWALL_MODULE_ENABLED
|
||||
#define HAL_FLASH_MODULE_ENABLED
|
||||
/* #define HAL_HCD_MODULE_ENABLED */
|
||||
/* #define HAL_NAND_MODULE_ENABLED */
|
||||
/* #define HAL_NOR_MODULE_ENABLED */
|
||||
/* #define HAL_SRAM_MODULE_ENABLED */
|
||||
#define HAL_GPIO_MODULE_ENABLED
|
||||
//#define HAL_I2C_MODULE_ENABLED
|
||||
/* #define HAL_IRDA_MODULE_ENABLED */
|
||||
/* #define HAL_IWDG_MODULE_ENABLED */
|
||||
/* #define HAL_LCD_MODULE_ENABLED */
|
||||
/* #define HAL_LPTIM_MODULE_ENABLED */
|
||||
/* #define HAL_OPAMP_MODULE_ENABLED */
|
||||
//#define HAL_PCD_MODULE_ENABLED
|
||||
#define HAL_PWR_MODULE_ENABLED
|
||||
#define HAL_QSPI_MODULE_ENABLED
|
||||
#define HAL_RCC_MODULE_ENABLED
|
||||
#define HAL_RNG_MODULE_ENABLED
|
||||
//#define HAL_RTC_MODULE_ENABLED
|
||||
/* #define HAL_SAI_MODULE_ENABLED */
|
||||
//#define HAL_SD_MODULE_ENABLED
|
||||
/* #define HAL_SMARTCARD_MODULE_ENABLED */
|
||||
/* #define HAL_SMBUS_MODULE_ENABLED */
|
||||
#define HAL_SPI_MODULE_ENABLED
|
||||
/* #define HAL_SWPMI_MODULE_ENABLED */
|
||||
//#define HAL_TIM_MODULE_ENABLED
|
||||
//#define HAL_TSC_MODULE_ENABLED
|
||||
#define HAL_UART_MODULE_ENABLED
|
||||
#define HAL_USART_MODULE_ENABLED
|
||||
/* #define HAL_WWDG_MODULE_ENABLED */
|
||||
|
||||
#define HAL_HASH_MODULE_ENABLED
|
||||
|
||||
|
||||
/* ########################## Oscillator Values adaptation ####################*/
|
||||
/**
|
||||
* @brief Adjust the value of External High Speed oscillator (HSE) used in your application.
|
||||
* This value is used by the RCC HAL module to compute the system frequency
|
||||
* (when HSE is used as system clock source, directly or through the PLL).
|
||||
*/
|
||||
#if !defined (HSE_VALUE)
|
||||
#define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
|
||||
#endif /* HSE_VALUE */
|
||||
|
||||
#if !defined (HSE_STARTUP_TIMEOUT)
|
||||
#define HSE_STARTUP_TIMEOUT ((uint32_t)100) /*!< Time out for HSE start up, in ms */
|
||||
#endif /* HSE_STARTUP_TIMEOUT */
|
||||
|
||||
/**
|
||||
* @brief Internal Multiple Speed oscillator (MSI) default value.
|
||||
* This value is the default MSI range value after Reset.
|
||||
*/
|
||||
#if !defined (MSI_VALUE)
|
||||
#define MSI_VALUE ((uint32_t)4000000) /*!< Value of the Internal oscillator in Hz*/
|
||||
#endif /* MSI_VALUE */
|
||||
|
||||
/**
|
||||
* @brief Internal High Speed oscillator (HSI) value.
|
||||
* This value is used by the RCC HAL module to compute the system frequency
|
||||
* (when HSI is used as system clock source, directly or through the PLL).
|
||||
*/
|
||||
#if !defined (HSI_VALUE)
|
||||
#define HSI_VALUE ((uint32_t)16000000) /*!< Value of the Internal oscillator in Hz*/
|
||||
#endif /* HSI_VALUE */
|
||||
|
||||
/**
|
||||
* @brief Internal Low Speed oscillator (LSI) value.
|
||||
*/
|
||||
#if !defined (LSI_VALUE)
|
||||
#define LSI_VALUE ((uint32_t)32000) /*!< LSI Typical Value in Hz*/
|
||||
#endif /* LSI_VALUE */ /*!< Value of the Internal Low Speed oscillator in Hz
|
||||
The real value may vary depending on the variations
|
||||
in voltage and temperature. */
|
||||
/**
|
||||
* @brief External Low Speed oscillator (LSE) value.
|
||||
* This value is used by the UART, RTC HAL module to compute the system frequency
|
||||
*/
|
||||
#if !defined (LSE_VALUE)
|
||||
#define LSE_VALUE ((uint32_t)32768) /*!< Value of the External oscillator in Hz*/
|
||||
#endif /* LSE_VALUE */
|
||||
|
||||
#if !defined (LSE_STARTUP_TIMEOUT)
|
||||
#define LSE_STARTUP_TIMEOUT ((uint32_t)5000) /*!< Time out for LSE start up, in ms */
|
||||
#endif /* HSE_STARTUP_TIMEOUT */
|
||||
|
||||
/**
|
||||
* @brief External clock source for SAI1 peripheral
|
||||
* This value is used by the RCC HAL module to compute the SAI1 & SAI2 clock source
|
||||
* frequency.
|
||||
*/
|
||||
#if !defined (EXTERNAL_SAI1_CLOCK_VALUE)
|
||||
#define EXTERNAL_SAI1_CLOCK_VALUE ((uint32_t)48000) /*!< Value of the SAI1 External clock source in Hz*/
|
||||
#endif /* EXTERNAL_SAI1_CLOCK_VALUE */
|
||||
|
||||
/**
|
||||
* @brief External clock source for SAI2 peripheral
|
||||
* This value is used by the RCC HAL module to compute the SAI1 & SAI2 clock source
|
||||
* frequency.
|
||||
*/
|
||||
#if !defined (EXTERNAL_SAI2_CLOCK_VALUE)
|
||||
#define EXTERNAL_SAI2_CLOCK_VALUE ((uint32_t)48000) /*!< Value of the SAI2 External clock source in Hz*/
|
||||
#endif /* EXTERNAL_SAI2_CLOCK_VALUE */
|
||||
|
||||
/* Tip: To avoid modifying this file each time you need to use different HSE,
|
||||
=== you can define the HSE value in your toolchain compiler preprocessor. */
|
||||
|
||||
/* ########################### System Configuration ######################### */
|
||||
/**
|
||||
* @brief This is the HAL system configuration section
|
||||
*/
|
||||
#define VDD_VALUE ((uint32_t)3300) /*!< Value of VDD in mv */
|
||||
#define TICK_INT_PRIORITY ((uint32_t)0x00) /*!< tick interrupt priority */
|
||||
#define USE_RTOS 0
|
||||
#define PREFETCH_ENABLE 1
|
||||
#define INSTRUCTION_CACHE_ENABLE 1
|
||||
#define DATA_CACHE_ENABLE 1
|
||||
|
||||
/* ########################## Assert Selection ############################## */
|
||||
/**
|
||||
* @brief Uncomment the line below to expanse the "assert_param" macro in the
|
||||
* HAL drivers code
|
||||
*/
|
||||
/* #define USE_FULL_ASSERT 1 */
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief Include module's header file
|
||||
*/
|
||||
|
||||
#ifdef HAL_RCC_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_rcc.h"
|
||||
#endif /* HAL_RCC_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_GPIO_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_gpio.h"
|
||||
#endif /* HAL_GPIO_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_DMA_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_dma.h"
|
||||
#endif /* HAL_DMA_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_DFSDM_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_dfsdm.h"
|
||||
#endif /* HAL_DFSDM_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_CORTEX_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_cortex.h"
|
||||
#endif /* HAL_CORTEX_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_ADC_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_adc.h"
|
||||
#endif /* HAL_ADC_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_CAN_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_can.h"
|
||||
#endif /* HAL_CAN_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_COMP_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_comp.h"
|
||||
#endif /* HAL_COMP_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_CRC_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_crc.h"
|
||||
#endif /* HAL_CRC_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_CRYP_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_cryp.h"
|
||||
#endif /* HAL_CRYP_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_DAC_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_dac.h"
|
||||
#endif /* HAL_DAC_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_FIREWALL_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_firewall.h"
|
||||
#endif /* HAL_FIREWALL_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_FLASH_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_flash.h"
|
||||
#endif /* HAL_FLASH_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_SRAM_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_sram.h"
|
||||
#endif /* HAL_SRAM_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_NOR_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_nor.h"
|
||||
#endif /* HAL_NOR_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_NAND_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_nand.h"
|
||||
#endif /* HAL_NAND_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_I2C_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_i2c.h"
|
||||
#endif /* HAL_I2C_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_IWDG_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_iwdg.h"
|
||||
#endif /* HAL_IWDG_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_LCD_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_lcd.h"
|
||||
#endif /* HAL_LCD_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_LPTIM_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_lptim.h"
|
||||
#endif /* HAL_LPTIM_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_OPAMP_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_opamp.h"
|
||||
#endif /* HAL_OPAMP_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_PWR_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_pwr.h"
|
||||
#endif /* HAL_PWR_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_QSPI_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_qspi.h"
|
||||
#endif /* HAL_QSPI_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_RNG_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_rng.h"
|
||||
#endif /* HAL_RNG_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_RTC_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_rtc.h"
|
||||
#endif /* HAL_RTC_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_SAI_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_sai.h"
|
||||
#endif /* HAL_SAI_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_SD_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_sd.h"
|
||||
#endif /* HAL_SD_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_SMBUS_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_smbus.h"
|
||||
#endif /* HAL_SMBUS_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_SPI_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_spi.h"
|
||||
#endif /* HAL_SPI_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_SWPMI_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_swpmi.h"
|
||||
#endif /* HAL_SWPMI_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_TIM_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_tim.h"
|
||||
#endif /* HAL_TIM_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_TSC_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_tsc.h"
|
||||
#endif /* HAL_TSC_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_UART_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_uart.h"
|
||||
#endif /* HAL_UART_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_USART_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_usart.h"
|
||||
#endif /* HAL_USART_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_IRDA_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_irda.h"
|
||||
#endif /* HAL_IRDA_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_SMARTCARD_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_smartcard.h"
|
||||
#endif /* HAL_SMARTCARD_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_WWDG_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_wwdg.h"
|
||||
#endif /* HAL_WWDG_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_PCD_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_pcd.h"
|
||||
#endif /* HAL_PCD_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_HCD_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_hcd.h"
|
||||
#endif /* HAL_HCD_MODULE_ENABLED */
|
||||
|
||||
#ifdef HAL_HASH_MODULE_ENABLED
|
||||
#include "stm32l4xx_hal_hash.h"
|
||||
#endif /* HAL_HASH_MODULE_ENABLED */
|
||||
|
||||
/* Exported macro ------------------------------------------------------------*/
|
||||
#ifdef USE_FULL_ASSERT
|
||||
/**
|
||||
* @brief The assert_param macro is used for function's parameters check.
|
||||
* @param expr: If expr is false, it calls assert_failed function
|
||||
* which reports the name of the source file and the source
|
||||
* line number of the call that failed.
|
||||
* If expr is true, it returns no value.
|
||||
* @retval None
|
||||
*/
|
||||
#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
void assert_failed(uint8_t* file, uint32_t line);
|
||||
#else
|
||||
#define assert_param(expr) ((void)0)
|
||||
#endif /* USE_FULL_ASSERT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __STM32L4xx_HAL_CONF_H */
|
||||
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
307
stm32/mk4-bootloader/stm32l4xx_hal_firewall.c
Normal file
307
stm32/mk4-bootloader/stm32l4xx_hal_firewall.c
Normal file
@ -0,0 +1,307 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file stm32l4xx_hal_firewall.c
|
||||
* @author MCD Application Team
|
||||
* @version V1.7.2
|
||||
* @date 16-June-2017
|
||||
* @brief FIREWALL HAL module driver.
|
||||
* This file provides firmware functions to manage the Firewall
|
||||
* Peripheral initialization and enabling.
|
||||
*
|
||||
*
|
||||
@verbatim
|
||||
===============================================================================
|
||||
##### How to use this driver #####
|
||||
===============================================================================
|
||||
[..]
|
||||
The FIREWALL HAL driver can be used as follows:
|
||||
|
||||
(#) Declare a FIREWALL_InitTypeDef initialization structure.
|
||||
|
||||
(#) Resort to HAL_FIREWALL_Config() API to initialize the Firewall
|
||||
|
||||
(#) Enable the FIREWALL in calling HAL_FIREWALL_EnableFirewall() API
|
||||
|
||||
(#) To ensure that any code executed outside the protected segment closes the
|
||||
FIREWALL, the user must set the flag FIREWALL_PRE_ARM_SET in calling
|
||||
__HAL_FIREWALL_PREARM_ENABLE() macro if called within a protected code segment
|
||||
or
|
||||
HAL_FIREWALL_EnablePreArmFlag() API if called outside of protected code segment
|
||||
after HAL_FIREWALL_Config() call.
|
||||
|
||||
|
||||
@endverbatim
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT(c) 2017 STMicroelectronics</center></h2>
|
||||
*
|
||||
* 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 STMicroelectronics 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "stm32l4xx_hal.h"
|
||||
|
||||
/** @addtogroup STM32L4xx_HAL_Driver
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @defgroup FIREWALL FIREWALL
|
||||
* @brief HAL FIREWALL module driver
|
||||
* @{
|
||||
*/
|
||||
#ifdef HAL_FIREWALL_MODULE_ENABLED
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
/* Private macro -------------------------------------------------------------*/
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
/* Private functions ---------------------------------------------------------*/
|
||||
|
||||
|
||||
/** @defgroup FIREWALL_Exported_Functions FIREWALL Exported Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @defgroup FIREWALL_Exported_Functions_Group1 Initialization Functions
|
||||
* @brief Initialization and Configuration Functions
|
||||
*
|
||||
@verbatim
|
||||
===============================================================================
|
||||
##### Initialization and Configuration functions #####
|
||||
===============================================================================
|
||||
[..]
|
||||
This subsection provides the functions allowing to initialize the Firewall.
|
||||
Initialization is done by HAL_FIREWALL_Config():
|
||||
|
||||
(+) Enable the Firewall clock thru __HAL_RCC_FIREWALL_CLK_ENABLE() macro.
|
||||
|
||||
(+) Set the protected code segment address start and length.
|
||||
|
||||
(+) Set the protected non-volatile and/or volatile data segments
|
||||
address starts and lengths if applicable.
|
||||
|
||||
(+) Set the volatile data segment execution and sharing status.
|
||||
|
||||
(+) Length must be set to 0 for an unprotected segment.
|
||||
|
||||
@endverbatim
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialize the Firewall according to the FIREWALL_InitTypeDef structure parameters.
|
||||
* @param fw_init: Firewall initialization structure
|
||||
* @note The API returns HAL_ERROR if the Firewall is already enabled.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_FIREWALL_Config(FIREWALL_InitTypeDef * fw_init)
|
||||
{
|
||||
/* Check the Firewall initialization structure allocation */
|
||||
if(fw_init == NULL)
|
||||
{
|
||||
return HAL_ERROR;
|
||||
}
|
||||
|
||||
/* Enable Firewall clock */
|
||||
__HAL_RCC_FIREWALL_CLK_ENABLE();
|
||||
|
||||
/* Make sure that Firewall is not enabled already */
|
||||
if (__HAL_FIREWALL_IS_ENABLED() != RESET)
|
||||
{
|
||||
return HAL_ERROR;
|
||||
}
|
||||
|
||||
/* Check Firewall configuration addresses and lengths when segment is protected */
|
||||
/* Code segment */
|
||||
if (fw_init->CodeSegmentLength != 0)
|
||||
{
|
||||
assert_param(IS_FIREWALL_CODE_SEGMENT_ADDRESS(fw_init->CodeSegmentStartAddress));
|
||||
assert_param(IS_FIREWALL_CODE_SEGMENT_LENGTH(fw_init->CodeSegmentStartAddress, fw_init->CodeSegmentLength));
|
||||
}
|
||||
/* Non volatile data segment */
|
||||
if (fw_init->NonVDataSegmentLength != 0)
|
||||
{
|
||||
assert_param(IS_FIREWALL_NONVOLATILEDATA_SEGMENT_ADDRESS(fw_init->NonVDataSegmentStartAddress));
|
||||
assert_param(IS_FIREWALL_NONVOLATILEDATA_SEGMENT_LENGTH(fw_init->NonVDataSegmentStartAddress, fw_init->NonVDataSegmentLength));
|
||||
}
|
||||
/* Volatile data segment */
|
||||
if (fw_init->VDataSegmentLength != 0)
|
||||
{
|
||||
assert_param(IS_FIREWALL_VOLATILEDATA_SEGMENT_ADDRESS(fw_init->VDataSegmentStartAddress));
|
||||
assert_param(IS_FIREWALL_VOLATILEDATA_SEGMENT_LENGTH(fw_init->VDataSegmentStartAddress, fw_init->VDataSegmentLength));
|
||||
}
|
||||
|
||||
/* Check Firewall Configuration Register parameters */
|
||||
assert_param(IS_FIREWALL_VOLATILEDATA_EXECUTE(fw_init->VolatileDataExecution));
|
||||
assert_param(IS_FIREWALL_VOLATILEDATA_SHARE(fw_init->VolatileDataShared));
|
||||
|
||||
/* Configuration */
|
||||
|
||||
/* Protected code segment start address configuration */
|
||||
WRITE_REG(FIREWALL->CSSA, (FW_CSSA_ADD & fw_init->CodeSegmentStartAddress));
|
||||
/* Protected code segment length configuration */
|
||||
WRITE_REG(FIREWALL->CSL, (FW_CSL_LENG & fw_init->CodeSegmentLength));
|
||||
|
||||
/* Protected non volatile data segment start address configuration */
|
||||
WRITE_REG(FIREWALL->NVDSSA, (FW_NVDSSA_ADD & fw_init->NonVDataSegmentStartAddress));
|
||||
/* Protected non volatile data segment length configuration */
|
||||
WRITE_REG(FIREWALL->NVDSL, (FW_NVDSL_LENG & fw_init->NonVDataSegmentLength));
|
||||
|
||||
/* Protected volatile data segment start address configuration */
|
||||
WRITE_REG(FIREWALL->VDSSA, (FW_VDSSA_ADD & fw_init->VDataSegmentStartAddress));
|
||||
/* Protected volatile data segment length configuration */
|
||||
WRITE_REG(FIREWALL->VDSL, (FW_VDSL_LENG & fw_init->VDataSegmentLength));
|
||||
|
||||
/* Set Firewall Configuration Register VDE and VDS bits
|
||||
(volatile data execution and shared configuration) */
|
||||
MODIFY_REG(FIREWALL->CR, FW_CR_VDS|FW_CR_VDE, fw_init->VolatileDataExecution|fw_init->VolatileDataShared);
|
||||
|
||||
return HAL_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the Firewall configuration.
|
||||
* @param fw_config: Firewall configuration, type is same as initialization structure
|
||||
* @note This API can't be executed inside a code area protected by the Firewall
|
||||
* when the Firewall is enabled
|
||||
* @note If NVDSL register is different from 0, that is, if the non volatile data segment
|
||||
* is defined, this API can't be executed when the Firewall is enabled.
|
||||
* @note User should resort to __HAL_FIREWALL_GET_PREARM() macro to retrieve FPA bit status
|
||||
* @retval None
|
||||
*/
|
||||
void HAL_FIREWALL_GetConfig(FIREWALL_InitTypeDef * fw_config)
|
||||
{
|
||||
|
||||
/* Enable Firewall clock, in case no Firewall configuration has been carried
|
||||
out up to this point */
|
||||
__HAL_RCC_FIREWALL_CLK_ENABLE();
|
||||
|
||||
/* Retrieve code segment protection setting */
|
||||
fw_config->CodeSegmentStartAddress = (READ_REG(FIREWALL->CSSA) & FW_CSSA_ADD);
|
||||
fw_config->CodeSegmentLength = (READ_REG(FIREWALL->CSL) & FW_CSL_LENG);
|
||||
|
||||
/* Retrieve non volatile data segment protection setting */
|
||||
fw_config->NonVDataSegmentStartAddress = (READ_REG(FIREWALL->NVDSSA) & FW_NVDSSA_ADD);
|
||||
fw_config->NonVDataSegmentLength = (READ_REG(FIREWALL->NVDSL) & FW_NVDSL_LENG);
|
||||
|
||||
/* Retrieve volatile data segment protection setting */
|
||||
fw_config->VDataSegmentStartAddress = (READ_REG(FIREWALL->VDSSA) & FW_VDSSA_ADD);
|
||||
fw_config->VDataSegmentLength = (READ_REG(FIREWALL->VDSL) & FW_VDSL_LENG);
|
||||
|
||||
/* Retrieve volatile data execution setting */
|
||||
fw_config->VolatileDataExecution = (READ_REG(FIREWALL->CR) & FW_CR_VDE);
|
||||
|
||||
/* Retrieve volatile data shared setting */
|
||||
fw_config->VolatileDataShared = (READ_REG(FIREWALL->CR) & FW_CR_VDS);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Enable FIREWALL.
|
||||
* @note Firewall is enabled in clearing FWDIS bit of SYSCFG CFGR1 register.
|
||||
* Once enabled, the Firewall cannot be disabled by software. Only a
|
||||
* system reset can set again FWDIS bit.
|
||||
* @retval None
|
||||
*/
|
||||
void HAL_FIREWALL_EnableFirewall(void)
|
||||
{
|
||||
/* Clears FWDIS bit of SYSCFG CFGR1 register */
|
||||
CLEAR_BIT(SYSCFG->CFGR1, SYSCFG_CFGR1_FWDIS);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable FIREWALL pre arm.
|
||||
* @note When FPA bit is set, any code executed outside the protected segment
|
||||
* will close the Firewall.
|
||||
* @note This API provides the same service as __HAL_FIREWALL_PREARM_ENABLE() macro
|
||||
* but can't be executed inside a code area protected by the Firewall.
|
||||
* @note When the Firewall is disabled, user can resort to HAL_FIREWALL_EnablePreArmFlag() API any time.
|
||||
* @note When the Firewall is enabled and NVDSL register is equal to 0 (that is,
|
||||
* when the non volatile data segment is not defined),
|
||||
* ** this API can be executed when the Firewall is closed
|
||||
* ** when the Firewall is opened, user should resort to
|
||||
* __HAL_FIREWALL_PREARM_ENABLE() macro instead
|
||||
* @note When the Firewall is enabled and NVDSL register is different from 0
|
||||
* (that is, when the non volatile data segment is defined)
|
||||
* ** FW_CR register can be accessed only when the Firewall is opened:
|
||||
* user should resort to __HAL_FIREWALL_PREARM_ENABLE() macro instead.
|
||||
* @retval None
|
||||
*/
|
||||
void HAL_FIREWALL_EnablePreArmFlag(void)
|
||||
{
|
||||
/* Set FPA bit */
|
||||
SET_BIT(FIREWALL->CR, FW_CR_FPA);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Disable FIREWALL pre arm.
|
||||
* @note When FPA bit is reset, any code executed outside the protected segment
|
||||
* when the Firewall is opened will generate a system reset.
|
||||
* @note This API provides the same service as __HAL_FIREWALL_PREARM_DISABLE() macro
|
||||
* but can't be executed inside a code area protected by the Firewall.
|
||||
* @note When the Firewall is disabled, user can resort to HAL_FIREWALL_EnablePreArmFlag() API any time.
|
||||
* @note When the Firewall is enabled and NVDSL register is equal to 0 (that is,
|
||||
* when the non volatile data segment is not defined),
|
||||
* ** this API can be executed when the Firewall is closed
|
||||
* ** when the Firewall is opened, user should resort to
|
||||
* __HAL_FIREWALL_PREARM_DISABLE() macro instead
|
||||
* @note When the Firewall is enabled and NVDSL register is different from 0
|
||||
* (that is, when the non volatile data segment is defined)
|
||||
* ** FW_CR register can be accessed only when the Firewall is opened:
|
||||
* user should resort to __HAL_FIREWALL_PREARM_DISABLE() macro instead.
|
||||
|
||||
* @retval None
|
||||
*/
|
||||
void HAL_FIREWALL_DisablePreArmFlag(void)
|
||||
{
|
||||
/* Clear FPA bit */
|
||||
CLEAR_BIT(FIREWALL->CR, FW_CR_FPA);
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#endif /* HAL_FIREWALL_MODULE_ENABLED */
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
570
stm32/mk4-bootloader/stm32l4xx_hal_gpio.c
Normal file
570
stm32/mk4-bootloader/stm32l4xx_hal_gpio.c
Normal file
@ -0,0 +1,570 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file stm32l4xx_hal_gpio.c
|
||||
* @author MCD Application Team
|
||||
* @version V1.7.2
|
||||
* @date 16-June-2017
|
||||
* @brief GPIO HAL module driver.
|
||||
* This file provides firmware functions to manage the following
|
||||
* functionalities of the General Purpose Input/Output (GPIO) peripheral:
|
||||
* + Initialization and de-initialization functions
|
||||
* + IO operation functions
|
||||
*
|
||||
@verbatim
|
||||
==============================================================================
|
||||
##### GPIO Peripheral features #####
|
||||
==============================================================================
|
||||
[..]
|
||||
(+) Each port bit of the general-purpose I/O (GPIO) ports can be individually
|
||||
configured by software in several modes:
|
||||
(++) Input mode
|
||||
(++) Analog mode
|
||||
(++) Output mode
|
||||
(++) Alternate function mode
|
||||
(++) External interrupt/event lines
|
||||
|
||||
(+) During and just after reset, the alternate functions and external interrupt
|
||||
lines are not active and the I/O ports are configured in input floating mode.
|
||||
|
||||
(+) All GPIO pins have weak internal pull-up and pull-down resistors, which can be
|
||||
activated or not.
|
||||
|
||||
(+) In Output or Alternate mode, each IO can be configured on open-drain or push-pull
|
||||
type and the IO speed can be selected depending on the VDD value.
|
||||
|
||||
(+) The microcontroller IO pins are connected to onboard peripherals/modules through a
|
||||
multiplexer that allows only one peripheral alternate function (AF) connected
|
||||
to an IO pin at a time. In this way, there can be no conflict between peripherals
|
||||
sharing the same IO pin.
|
||||
|
||||
(+) All ports have external interrupt/event capability. To use external interrupt
|
||||
lines, the port must be configured in input mode. All available GPIO pins are
|
||||
connected to the 16 external interrupt/event lines from EXTI0 to EXTI15.
|
||||
|
||||
(+) The external interrupt/event controller consists of up to 39 edge detectors
|
||||
(16 lines are connected to GPIO) for generating event/interrupt requests (each
|
||||
input line can be independently configured to select the type (interrupt or event)
|
||||
and the corresponding trigger event (rising or falling or both). Each line can
|
||||
also be masked independently.
|
||||
|
||||
##### How to use this driver #####
|
||||
==============================================================================
|
||||
[..]
|
||||
(#) Enable the GPIO AHB clock using the following function: __HAL_RCC_GPIOx_CLK_ENABLE().
|
||||
|
||||
(#) Configure the GPIO pin(s) using HAL_GPIO_Init().
|
||||
(++) Configure the IO mode using "Mode" member from GPIO_InitTypeDef structure
|
||||
(++) Activate Pull-up, Pull-down resistor using "Pull" member from GPIO_InitTypeDef
|
||||
structure.
|
||||
(++) In case of Output or alternate function mode selection: the speed is
|
||||
configured through "Speed" member from GPIO_InitTypeDef structure.
|
||||
(++) In alternate mode is selection, the alternate function connected to the IO
|
||||
is configured through "Alternate" member from GPIO_InitTypeDef structure.
|
||||
(++) Analog mode is required when a pin is to be used as ADC channel
|
||||
or DAC output.
|
||||
(++) In case of external interrupt/event selection the "Mode" member from
|
||||
GPIO_InitTypeDef structure select the type (interrupt or event) and
|
||||
the corresponding trigger event (rising or falling or both).
|
||||
|
||||
(#) In case of external interrupt/event mode selection, configure NVIC IRQ priority
|
||||
mapped to the EXTI line using HAL_NVIC_SetPriority() and enable it using
|
||||
HAL_NVIC_EnableIRQ().
|
||||
|
||||
(#) To get the level of a pin configured in input mode use HAL_GPIO_ReadPin().
|
||||
|
||||
(#) To set/reset the level of a pin configured in output mode use
|
||||
HAL_GPIO_WritePin()/HAL_GPIO_TogglePin().
|
||||
|
||||
(#) To lock pin configuration until next reset use HAL_GPIO_LockPin().
|
||||
|
||||
(#) During and just after reset, the alternate functions are not
|
||||
active and the GPIO pins are configured in input floating mode (except JTAG
|
||||
pins).
|
||||
|
||||
(#) The LSE oscillator pins OSC32_IN and OSC32_OUT can be used as general purpose
|
||||
(PC14 and PC15, respectively) when the LSE oscillator is off. The LSE has
|
||||
priority over the GPIO function.
|
||||
|
||||
(#) The HSE oscillator pins OSC_IN/OSC_OUT can be used as
|
||||
general purpose PH0 and PH1, respectively, when the HSE oscillator is off.
|
||||
The HSE has priority over the GPIO function.
|
||||
|
||||
@endverbatim
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT(c) 2017 STMicroelectronics</center></h2>
|
||||
*
|
||||
* 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 STMicroelectronics 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "stm32l4xx_hal.h"
|
||||
|
||||
/** @addtogroup STM32L4xx_HAL_Driver
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @defgroup GPIO GPIO
|
||||
* @brief GPIO HAL module driver
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifdef HAL_GPIO_MODULE_ENABLED
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
/* Private defines -----------------------------------------------------------*/
|
||||
/** @defgroup GPIO_Private_Defines GPIO Private Defines
|
||||
* @{
|
||||
*/
|
||||
#define GPIO_MODE ((uint32_t)0x00000003)
|
||||
#define ANALOG_MODE ((uint32_t)0x00000008)
|
||||
#define EXTI_MODE ((uint32_t)0x10000000)
|
||||
#define GPIO_MODE_IT ((uint32_t)0x00010000)
|
||||
#define GPIO_MODE_EVT ((uint32_t)0x00020000)
|
||||
#define RISING_EDGE ((uint32_t)0x00100000)
|
||||
#define FALLING_EDGE ((uint32_t)0x00200000)
|
||||
#define GPIO_OUTPUT_TYPE ((uint32_t)0x00000010)
|
||||
|
||||
#define GPIO_NUMBER ((uint32_t)16)
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/* Private macros ------------------------------------------------------------*/
|
||||
/* Private macros ------------------------------------------------------------*/
|
||||
/** @defgroup GPIO_Private_Macros GPIO Private Macros
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
/* Exported functions --------------------------------------------------------*/
|
||||
|
||||
/** @defgroup GPIO_Exported_Functions GPIO Exported Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @defgroup GPIO_Exported_Functions_Group1 Initialization/de-initialization functions
|
||||
* @brief Initialization and Configuration functions
|
||||
*
|
||||
@verbatim
|
||||
===============================================================================
|
||||
##### Initialization and de-initialization functions #####
|
||||
===============================================================================
|
||||
|
||||
@endverbatim
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialize the GPIOx peripheral according to the specified parameters in the GPIO_Init.
|
||||
* @param GPIOx: where x can be (A..H) to select the GPIO peripheral for STM32L4 family
|
||||
* @param GPIO_Init: pointer to a GPIO_InitTypeDef structure that contains
|
||||
* the configuration information for the specified GPIO peripheral.
|
||||
* @retval None
|
||||
*/
|
||||
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
|
||||
{
|
||||
uint32_t position = 0x00;
|
||||
uint32_t iocurrent = 0x00;
|
||||
uint32_t temp = 0x00;
|
||||
|
||||
/* Check the parameters */
|
||||
assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
|
||||
assert_param(IS_GPIO_PIN(GPIO_Init->Pin));
|
||||
assert_param(IS_GPIO_MODE(GPIO_Init->Mode));
|
||||
assert_param(IS_GPIO_PULL(GPIO_Init->Pull));
|
||||
|
||||
/* Configure the port pins */
|
||||
while (((GPIO_Init->Pin) >> position) != RESET)
|
||||
{
|
||||
/* Get current io position */
|
||||
iocurrent = (GPIO_Init->Pin) & (1U << position);
|
||||
|
||||
if(iocurrent)
|
||||
{
|
||||
/*--------------------- GPIO Mode Configuration ------------------------*/
|
||||
/* In case of Alternate function mode selection */
|
||||
if((GPIO_Init->Mode == GPIO_MODE_AF_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_OD))
|
||||
{
|
||||
/* Check the Alternate function parameters */
|
||||
assert_param(IS_GPIO_AF_INSTANCE(GPIOx));
|
||||
assert_param(IS_GPIO_AF(GPIO_Init->Alternate));
|
||||
|
||||
/* Configure Alternate function mapped with the current IO */
|
||||
temp = GPIOx->AFR[position >> 3];
|
||||
temp &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ;
|
||||
temp |= ((uint32_t)(GPIO_Init->Alternate) << (((uint32_t)position & (uint32_t)0x07) * 4));
|
||||
GPIOx->AFR[position >> 3] = temp;
|
||||
}
|
||||
|
||||
/* Configure IO Direction mode (Input, Output, Alternate or Analog) */
|
||||
temp = GPIOx->MODER;
|
||||
temp &= ~(GPIO_MODER_MODE0 << (position * 2));
|
||||
temp |= ((GPIO_Init->Mode & GPIO_MODE) << (position * 2));
|
||||
GPIOx->MODER = temp;
|
||||
|
||||
/* In case of Output or Alternate function mode selection */
|
||||
if((GPIO_Init->Mode == GPIO_MODE_OUTPUT_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_PP) ||
|
||||
(GPIO_Init->Mode == GPIO_MODE_OUTPUT_OD) || (GPIO_Init->Mode == GPIO_MODE_AF_OD))
|
||||
{
|
||||
/* Check the Speed parameter */
|
||||
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
|
||||
/* Configure the IO Speed */
|
||||
temp = GPIOx->OSPEEDR;
|
||||
temp &= ~(GPIO_OSPEEDR_OSPEED0 << (position * 2));
|
||||
temp |= (GPIO_Init->Speed << (position * 2));
|
||||
GPIOx->OSPEEDR = temp;
|
||||
|
||||
/* Configure the IO Output Type */
|
||||
temp = GPIOx->OTYPER;
|
||||
temp &= ~(GPIO_OTYPER_OT0 << position) ;
|
||||
temp |= (((GPIO_Init->Mode & GPIO_OUTPUT_TYPE) >> 4) << position);
|
||||
GPIOx->OTYPER = temp;
|
||||
}
|
||||
|
||||
#if defined(STM32L471xx) || defined(STM32L475xx) || defined(STM32L476xx) || defined(STM32L485xx) || defined(STM32L486xx)
|
||||
|
||||
/* In case of Analog mode, check if ADC control mode is selected */
|
||||
if((GPIO_Init->Mode & GPIO_MODE_ANALOG) == GPIO_MODE_ANALOG)
|
||||
{
|
||||
/* Configure the IO Output Type */
|
||||
temp = GPIOx->ASCR;
|
||||
temp &= ~(GPIO_ASCR_ASC0 << position) ;
|
||||
temp |= (((GPIO_Init->Mode & ANALOG_MODE) >> 3) << position);
|
||||
GPIOx->ASCR = temp;
|
||||
}
|
||||
|
||||
#endif /* STM32L471xx || STM32L475xx || STM32L476xx || STM32L485xx || STM32L486xx */
|
||||
|
||||
/* Activate the Pull-up or Pull down resistor for the current IO */
|
||||
temp = GPIOx->PUPDR;
|
||||
temp &= ~(GPIO_PUPDR_PUPD0 << (position * 2));
|
||||
temp |= ((GPIO_Init->Pull) << (position * 2));
|
||||
GPIOx->PUPDR = temp;
|
||||
|
||||
/*--------------------- EXTI Mode Configuration ------------------------*/
|
||||
/* Configure the External Interrupt or event for the current IO */
|
||||
if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
|
||||
{
|
||||
/* Enable SYSCFG Clock */
|
||||
__HAL_RCC_SYSCFG_CLK_ENABLE();
|
||||
|
||||
temp = SYSCFG->EXTICR[position >> 2];
|
||||
temp &= ~(((uint32_t)0x0F) << (4 * (position & 0x03)));
|
||||
temp |= (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03)));
|
||||
SYSCFG->EXTICR[position >> 2] = temp;
|
||||
|
||||
/* Clear EXTI line configuration */
|
||||
temp = EXTI->IMR1;
|
||||
temp &= ~((uint32_t)iocurrent);
|
||||
if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
|
||||
{
|
||||
temp |= iocurrent;
|
||||
}
|
||||
EXTI->IMR1 = temp;
|
||||
|
||||
temp = EXTI->EMR1;
|
||||
temp &= ~((uint32_t)iocurrent);
|
||||
if((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
|
||||
{
|
||||
temp |= iocurrent;
|
||||
}
|
||||
EXTI->EMR1 = temp;
|
||||
|
||||
/* Clear Rising Falling edge configuration */
|
||||
temp = EXTI->RTSR1;
|
||||
temp &= ~((uint32_t)iocurrent);
|
||||
if((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
|
||||
{
|
||||
temp |= iocurrent;
|
||||
}
|
||||
EXTI->RTSR1 = temp;
|
||||
|
||||
temp = EXTI->FTSR1;
|
||||
temp &= ~((uint32_t)iocurrent);
|
||||
if((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
|
||||
{
|
||||
temp |= iocurrent;
|
||||
}
|
||||
EXTI->FTSR1 = temp;
|
||||
}
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief De-initialize the GPIOx peripheral registers to their default reset values.
|
||||
* @param GPIOx: where x can be (A..H) to select the GPIO peripheral for STM32L4 family
|
||||
* @param GPIO_Pin: specifies the port bit to be written.
|
||||
* This parameter can be one of GPIO_PIN_x where x can be (0..15).
|
||||
* @retval None
|
||||
*/
|
||||
void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin)
|
||||
{
|
||||
uint32_t position = 0x00;
|
||||
uint32_t iocurrent = 0x00;
|
||||
uint32_t tmp = 0x00;
|
||||
|
||||
/* Check the parameters */
|
||||
assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
|
||||
assert_param(IS_GPIO_PIN(GPIO_Pin));
|
||||
|
||||
/* Configure the port pins */
|
||||
while ((GPIO_Pin >> position) != RESET)
|
||||
{
|
||||
/* Get current io position */
|
||||
iocurrent = (GPIO_Pin) & (1U << position);
|
||||
|
||||
if (iocurrent)
|
||||
{
|
||||
/*------------------------- GPIO Mode Configuration --------------------*/
|
||||
/* Configure IO in Analog Mode */
|
||||
GPIOx->MODER |= (GPIO_MODER_MODE0 << (position * 2));
|
||||
|
||||
/* Configure the default Alternate Function in current IO */
|
||||
GPIOx->AFR[position >> 3] &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ;
|
||||
|
||||
/* Configure the default value for IO Speed */
|
||||
GPIOx->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED0 << (position * 2));
|
||||
|
||||
/* Configure the default value IO Output Type */
|
||||
GPIOx->OTYPER &= ~(GPIO_OTYPER_OT0 << position) ;
|
||||
|
||||
/* Deactivate the Pull-up and Pull-down resistor for the current IO */
|
||||
GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPD0 << (position * 2));
|
||||
|
||||
#if defined(STM32L471xx) || defined(STM32L475xx) || defined(STM32L476xx) || defined(STM32L485xx) || defined(STM32L486xx)
|
||||
|
||||
/* Deactivate the Control bit of Analog mode for the current IO */
|
||||
GPIOx->ASCR &= ~(GPIO_ASCR_ASC0<< position);
|
||||
|
||||
#endif /* STM32L471xx || STM32L475xx || STM32L476xx || STM32L485xx || STM32L486xx */
|
||||
|
||||
/*------------------------- EXTI Mode Configuration --------------------*/
|
||||
/* Clear the External Interrupt or Event for the current IO */
|
||||
|
||||
tmp = SYSCFG->EXTICR[position >> 2];
|
||||
tmp &= (((uint32_t)0x0F) << (4 * (position & 0x03)));
|
||||
if(tmp == (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03))))
|
||||
{
|
||||
tmp = ((uint32_t)0x0F) << (4 * (position & 0x03));
|
||||
SYSCFG->EXTICR[position >> 2] &= ~tmp;
|
||||
|
||||
/* Clear EXTI line configuration */
|
||||
EXTI->IMR1 &= ~((uint32_t)iocurrent);
|
||||
EXTI->EMR1 &= ~((uint32_t)iocurrent);
|
||||
|
||||
/* Clear Rising Falling edge configuration */
|
||||
EXTI->RTSR1 &= ~((uint32_t)iocurrent);
|
||||
EXTI->FTSR1 &= ~((uint32_t)iocurrent);
|
||||
}
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @defgroup GPIO_Exported_Functions_Group2 IO operation functions
|
||||
* @brief GPIO Read, Write, Toggle, Lock and EXTI management functions.
|
||||
*
|
||||
@verbatim
|
||||
===============================================================================
|
||||
##### IO operation functions #####
|
||||
===============================================================================
|
||||
|
||||
@endverbatim
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Read the specified input port pin.
|
||||
* @param GPIOx: where x can be (A..H) to select the GPIO peripheral for STM32L4 family
|
||||
* @param GPIO_Pin: specifies the port bit to read.
|
||||
* This parameter can be GPIO_PIN_x where x can be (0..15).
|
||||
* @retval The input port pin value.
|
||||
*/
|
||||
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
|
||||
{
|
||||
GPIO_PinState bitstatus;
|
||||
|
||||
/* Check the parameters */
|
||||
assert_param(IS_GPIO_PIN(GPIO_Pin));
|
||||
|
||||
if((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET)
|
||||
{
|
||||
bitstatus = GPIO_PIN_SET;
|
||||
}
|
||||
else
|
||||
{
|
||||
bitstatus = GPIO_PIN_RESET;
|
||||
}
|
||||
return bitstatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set or clear the selected data port bit.
|
||||
*
|
||||
* @note This function uses GPIOx_BSRR and GPIOx_BRR registers to allow atomic read/modify
|
||||
* accesses. In this way, there is no risk of an IRQ occurring between
|
||||
* the read and the modify access.
|
||||
*
|
||||
* @param GPIOx: where x can be (A..H) to select the GPIO peripheral for STM32L4 family
|
||||
* @param GPIO_Pin: specifies the port bit to be written.
|
||||
* This parameter can be one of GPIO_PIN_x where x can be (0..15).
|
||||
* @param PinState: specifies the value to be written to the selected bit.
|
||||
* This parameter can be one of the GPIO_PinState enum values:
|
||||
* @arg GPIO_PIN_RESET: to clear the port pin
|
||||
* @arg GPIO_PIN_SET: to set the port pin
|
||||
* @retval None
|
||||
*/
|
||||
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
|
||||
{
|
||||
/* Check the parameters */
|
||||
assert_param(IS_GPIO_PIN(GPIO_Pin));
|
||||
assert_param(IS_GPIO_PIN_ACTION(PinState));
|
||||
|
||||
if(PinState != GPIO_PIN_RESET)
|
||||
{
|
||||
GPIOx->BSRR = (uint32_t)GPIO_Pin;
|
||||
}
|
||||
else
|
||||
{
|
||||
GPIOx->BRR = (uint32_t)GPIO_Pin;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Toggle the specified GPIO pin.
|
||||
* @param GPIOx: where x can be (A..H) to select the GPIO peripheral for STM32L4 family
|
||||
* @param GPIO_Pin: specifies the pin to be toggled.
|
||||
* @retval None
|
||||
*/
|
||||
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
|
||||
{
|
||||
/* Check the parameters */
|
||||
assert_param(IS_GPIO_PIN(GPIO_Pin));
|
||||
|
||||
GPIOx->ODR ^= GPIO_Pin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Lock GPIO Pins configuration registers.
|
||||
* @note The locked registers are GPIOx_MODER, GPIOx_OTYPER, GPIOx_OSPEEDR,
|
||||
* GPIOx_PUPDR, GPIOx_AFRL and GPIOx_AFRH.
|
||||
* @note The configuration of the locked GPIO pins can no longer be modified
|
||||
* until the next reset.
|
||||
* @param GPIOx: where x can be (A..H) to select the GPIO peripheral for STM32L4 family
|
||||
* @param GPIO_Pin: specifies the port bits to be locked.
|
||||
* This parameter can be any combination of GPIO_Pin_x where x can be (0..15).
|
||||
* @retval None
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
|
||||
{
|
||||
__IO uint32_t tmp = GPIO_LCKR_LCKK;
|
||||
|
||||
/* Check the parameters */
|
||||
assert_param(IS_GPIO_LOCK_INSTANCE(GPIOx));
|
||||
assert_param(IS_GPIO_PIN(GPIO_Pin));
|
||||
|
||||
/* Apply lock key write sequence */
|
||||
tmp |= GPIO_Pin;
|
||||
/* Set LCKx bit(s): LCKK='1' + LCK[15-0] */
|
||||
GPIOx->LCKR = tmp;
|
||||
/* Reset LCKx bit(s): LCKK='0' + LCK[15-0] */
|
||||
GPIOx->LCKR = GPIO_Pin;
|
||||
/* Set LCKx bit(s): LCKK='1' + LCK[15-0] */
|
||||
GPIOx->LCKR = tmp;
|
||||
/* Read LCKK bit*/
|
||||
tmp = GPIOx->LCKR;
|
||||
|
||||
if((GPIOx->LCKR & GPIO_LCKR_LCKK) != RESET)
|
||||
{
|
||||
return HAL_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return HAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle EXTI interrupt request.
|
||||
* @param GPIO_Pin: Specifies the port pin connected to corresponding EXTI line.
|
||||
* @retval None
|
||||
*/
|
||||
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
|
||||
{
|
||||
/* EXTI line interrupt detected */
|
||||
if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
|
||||
{
|
||||
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
|
||||
HAL_GPIO_EXTI_Callback(GPIO_Pin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief EXTI line detection callback.
|
||||
* @param GPIO_Pin: Specifies the port pin connected to corresponding EXTI line.
|
||||
* @retval None
|
||||
*/
|
||||
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
|
||||
{
|
||||
/* Prevent unused argument(s) compilation warning */
|
||||
UNUSED(GPIO_Pin);
|
||||
|
||||
/* NOTE: This function should not be modified, when the callback is needed,
|
||||
the HAL_GPIO_EXTI_Callback could be implemented in the user file
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#endif /* HAL_GPIO_MODULE_ENABLED */
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
2734
stm32/mk4-bootloader/stm32l4xx_hal_hash.c
Normal file
2734
stm32/mk4-bootloader/stm32l4xx_hal_hash.c
Normal file
File diff suppressed because it is too large
Load Diff
939
stm32/mk4-bootloader/stm32l4xx_hal_hash_ex.c
Normal file
939
stm32/mk4-bootloader/stm32l4xx_hal_hash_ex.c
Normal file
@ -0,0 +1,939 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file stm32l4xx_hal_hash_ex.c
|
||||
* @author MCD Application Team
|
||||
* @version V1.7.2
|
||||
* @date 16-June-2017
|
||||
* @brief Extended HASH HAL module driver.
|
||||
* This file provides firmware functions to manage the following
|
||||
* functionalities of the HASH peripheral for SHA-224 and SHA-256
|
||||
* alogrithms:
|
||||
* + HASH or HMAC processing in polling mode
|
||||
* + HASH or HMAC processing in interrupt mode
|
||||
* + HASH or HMAC processing in DMA mode
|
||||
* Additionally, this file provides functions to manage HMAC
|
||||
* multi-buffer DMA-based processing for MD-5, SHA-1, SHA-224
|
||||
* and SHA-256.
|
||||
*
|
||||
*
|
||||
@verbatim
|
||||
===============================================================================
|
||||
##### HASH peripheral extended features #####
|
||||
===============================================================================
|
||||
[..]
|
||||
The SHA-224 and SHA-256 HASH and HMAC processing can be carried out exactly
|
||||
the same way as for SHA-1 or MD-5 algorithms.
|
||||
(#) Three modes are available.
|
||||
(##) Polling mode: processing APIs are blocking functions
|
||||
i.e. they process the data and wait till the digest computation is finished,
|
||||
e.g. HAL_HASHEx_xxx_Start()
|
||||
(##) Interrupt mode: processing APIs are not blocking functions
|
||||
i.e. they process the data under interrupt,
|
||||
e.g. HAL_HASHEx_xxx_Start_IT()
|
||||
(##) DMA mode: processing APIs are not blocking functions and the CPU is
|
||||
not used for data transfer i.e. the data transfer is ensured by DMA,
|
||||
e.g. HAL_HASHEx_xxx_Start_DMA(). Note that in DMA mode, a call to
|
||||
HAL_HASHEx_xxx_Finish() is then required to retrieve the digest.
|
||||
|
||||
(#)Multi-buffer processing is possible in polling and DMA mode.
|
||||
(##) In polling mode, only multi-buffer HASH processing is possible.
|
||||
API HAL_HASHEx_xxx_Accumulate() must be called for each input buffer, except for the last one.
|
||||
User must resort to HAL_HASHEx_xxx_Start() to enter the last one and retrieve as
|
||||
well the computed digest.
|
||||
|
||||
(##) In DMA mode, multi-buffer HASH and HMAC processing are possible.
|
||||
|
||||
(+++) HASH processing: once initialization is done, MDMAT bit must be set thru __HAL_HASH_SET_MDMAT() macro.
|
||||
From that point, each buffer can be fed to the IP thru HAL_HASHEx_xxx_Start_DMA() API.
|
||||
Before entering the last buffer, reset the MDMAT bit with __HAL_HASH_RESET_MDMAT()
|
||||
macro then wrap-up the HASH processing in feeding the last input buffer thru the
|
||||
same API HAL_HASHEx_xxx_Start_DMA(). The digest can then be retrieved with a call to
|
||||
API HAL_HASHEx_xxx_Finish().
|
||||
|
||||
(+++) HMAC processing (MD-5, SHA-1, SHA-224 and SHA-256 must all resort to
|
||||
extended functions): after initialization, the key and the first input buffer are entered
|
||||
in the IP with the API HAL_HMACEx_xxx_Step1_2_DMA(). This carries out HMAC step 1 and
|
||||
starts step 2.
|
||||
The following buffers are next entered with the API HAL_HMACEx_xxx_Step2_DMA(). At this
|
||||
point, the HMAC processing is still carrying out step 2.
|
||||
Then, step 2 for the last input buffer and step 3 are carried out by a single call
|
||||
to HAL_HMACEx_xxx_Step2_3_DMA().
|
||||
|
||||
The digest can finally be retrieved with a call to API HAL_HASH_xxx_Finish() for
|
||||
MD-5 and SHA-1, to HAL_HASHEx_xxx_Finish() for SHA-224 and SHA-256.
|
||||
|
||||
|
||||
@endverbatim
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT(c) 2017 STMicroelectronics</center></h2>
|
||||
*
|
||||
* 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 STMicroelectronics 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "stm32l4xx_hal.h"
|
||||
|
||||
#ifdef HAL_HASH_MODULE_ENABLED
|
||||
|
||||
#if defined (STM32L4A6xx)
|
||||
|
||||
/** @addtogroup STM32L4xx_HAL_Driver
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @defgroup HASHEx HASHEx
|
||||
* @brief HASH HAL extended module driver.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
/* Private functions ---------------------------------------------------------*/
|
||||
|
||||
/** @defgroup HASHEx_Exported_Functions HASH Extended Exported Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/** @defgroup HASHEx_Exported_Functions_Group1 HASH extended processing functions in polling mode
|
||||
* @brief HASH extended processing functions using polling mode.
|
||||
*
|
||||
@verbatim
|
||||
===============================================================================
|
||||
##### Polling mode HASH extended processing functions #####
|
||||
===============================================================================
|
||||
[..] This section provides functions allowing to calculate in polling mode
|
||||
the hash value using one of the following algorithms:
|
||||
(+) SHA224
|
||||
(++) HAL_HASHEx_SHA224_Start()
|
||||
(++) HAL_HASHEx_SHA224_Accumulate()
|
||||
(+) SHA256
|
||||
(++) HAL_HASHEx_SHA256_Start()
|
||||
(++) HAL_HASHEx_SHA256_Accumulate()
|
||||
|
||||
[..] For a single buffer to be hashed, user can resort to HAL_HASH_xxx_Start().
|
||||
|
||||
[..] In case of multi-buffer HASH processing (a single digest is computed while
|
||||
several buffers are fed to the IP), the user can resort to successive calls
|
||||
to HAL_HASHEx_xxx_Accumulate() and wrap-up the digest computation by a call
|
||||
to HAL_HASHEx_xxx_Start().
|
||||
|
||||
@endverbatim
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the HASH peripheral in SHA224 mode, next process pInBuffer then
|
||||
* read the computed digest.
|
||||
* @note Digest is available in pOutBuffer.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (buffer to be hashed).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @param pOutBuffer: pointer to the computed digest. Digest size is 28 bytes.
|
||||
* @param Timeout: Timeout value
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HASHEx_SHA224_Start(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer, uint32_t Timeout)
|
||||
{
|
||||
return HASH_Start(hhash, pInBuffer, Size, pOutBuffer, Timeout, HASH_ALGOSELECTION_SHA224);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief If not already done, initialize the HASH peripheral in SHA224 mode then
|
||||
* processes pInBuffer.
|
||||
* @note Consecutive calls to HAL_HASHEx_SHA224_Accumulate() can be used to feed
|
||||
* several input buffers back-to-back to the IP that will yield a single
|
||||
* HASH signature once all buffers have been entered. Wrap-up of input
|
||||
* buffers feeding and retrieval of digest is done by a call to
|
||||
* HAL_HASHEx_SHA224_Start().
|
||||
* @note Field hhash->Phase of HASH handle is tested to check whether or not
|
||||
* the IP has already been initialized.
|
||||
* @note Digest is not retrieved by this API, user must resort to HAL_HASHEx_SHA224_Start()
|
||||
* to read it, feeding at the same time the last input buffer to the IP.
|
||||
* @note The input buffer size (in bytes) must be a multiple of 4 otherwise, the
|
||||
* HASH digest computation is corrupted. Only HAL_HASHEx_SHA224_Start() is able
|
||||
* to manage the ending buffer with a length in bytes not a multiple of 4.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (buffer to be hashed).
|
||||
* @param Size: length of the input buffer in bytes, must be a multiple of 4.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HASHEx_SHA224_Accumulate(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
return HASH_Accumulate(hhash, pInBuffer, Size,HASH_ALGOSELECTION_SHA224);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the HASH peripheral in SHA256 mode, next process pInBuffer then
|
||||
* read the computed digest.
|
||||
* @note Digest is available in pOutBuffer.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (buffer to be hashed).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @param pOutBuffer: pointer to the computed digest. Digest size is 32 bytes.
|
||||
* @param Timeout: Timeout value
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HASHEx_SHA256_Start(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer, uint32_t Timeout)
|
||||
{
|
||||
return HASH_Start(hhash, pInBuffer, Size, pOutBuffer, Timeout, HASH_ALGOSELECTION_SHA256);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief If not already done, initialize the HASH peripheral in SHA256 mode then
|
||||
* processes pInBuffer.
|
||||
* @note Consecutive calls to HAL_HASHEx_SHA256_Accumulate() can be used to feed
|
||||
* several input buffers back-to-back to the IP that will yield a single
|
||||
* HASH signature once all buffers have been entered. Wrap-up of input
|
||||
* buffers feeding and retrieval of digest is done by a call to
|
||||
* HAL_HASHEx_SHA256_Start().
|
||||
* @note Field hhash->Phase of HASH handle is tested to check whether or not
|
||||
* the IP has already been initialized.
|
||||
* @note Digest is not retrieved by this API, user must resort to HAL_HASHEx_SHA256_Start()
|
||||
* to read it, feeding at the same time the last input buffer to the IP.
|
||||
* @note The input buffer size (in bytes) must be a multiple of 4 otherwise, the
|
||||
* HASH digest computation is corrupted. Only HAL_HASHEx_SHA256_Start() is able
|
||||
* to manage the ending buffer with a length in bytes not a multiple of 4.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (buffer to be hashed).
|
||||
* @param Size: length of the input buffer in bytes, must be a multiple of 4.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HASHEx_SHA256_Accumulate(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
return HASH_Accumulate(hhash, pInBuffer, Size,HASH_ALGOSELECTION_SHA256);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @defgroup HASHEx_Exported_Functions_Group2 HASH extended processing functions in interrupt mode
|
||||
* @brief HASH extended processing functions using interrupt mode.
|
||||
*
|
||||
@verbatim
|
||||
===============================================================================
|
||||
##### Interruption mode HASH extended processing functions #####
|
||||
===============================================================================
|
||||
[..] This section provides functions allowing to calculate in interrupt mode
|
||||
the hash value using one of the following algorithms:
|
||||
(+) SHA224
|
||||
(++) HAL_HASHEx_SHA224_Start_IT()
|
||||
(+) SHA256
|
||||
(++) HAL_HASHEx_SHA256_Start_IT()
|
||||
|
||||
@endverbatim
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the HASH peripheral in SHA224 mode, next process pInBuffer then
|
||||
* read the computed digest in interruption mode.
|
||||
* @note Digest is available in pOutBuffer.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (buffer to be hashed).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @param pOutBuffer: pointer to the computed digest. Digest size is 28 bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HASHEx_SHA224_Start_IT(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer)
|
||||
{
|
||||
return HASH_Start_IT(hhash, pInBuffer, Size, pOutBuffer,HASH_ALGOSELECTION_SHA224);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the HASH peripheral in SHA256 mode, next process pInBuffer then
|
||||
* read the computed digest in interruption mode.
|
||||
* @note Digest is available in pOutBuffer.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (buffer to be hashed).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @param pOutBuffer: pointer to the computed digest. Digest size is 32 bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HASHEx_SHA256_Start_IT(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer)
|
||||
{
|
||||
return HASH_Start_IT(hhash, pInBuffer, Size, pOutBuffer,HASH_ALGOSELECTION_SHA256);
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#if 0
|
||||
/** @defgroup HASHEx_Exported_Functions_Group3 HASH extended processing functions in DMA mode
|
||||
* @brief HASH extended processing functions using DMA mode.
|
||||
*
|
||||
@verbatim
|
||||
===============================================================================
|
||||
##### DMA mode HASH extended processing functionss #####
|
||||
===============================================================================
|
||||
[..] This section provides functions allowing to calculate in DMA mode
|
||||
the hash value using one of the following algorithms:
|
||||
(+) SHA224
|
||||
(++) HAL_HASHEx_SHA224_Start_DMA()
|
||||
(++) HAL_HASHEx_SHA224_Finish()
|
||||
(+) SHA256
|
||||
(++) HAL_HASHEx_SHA256_Start_DMA()
|
||||
(++) HAL_HASHEx_SHA256_Finish()
|
||||
|
||||
[..] When resorting to DMA mode to enter the data in the IP, user must resort
|
||||
to HAL_HASHEx_xxx_Start_DMA() then read the resulting digest with
|
||||
HAL_HASHEx_xxx_Finish().
|
||||
|
||||
[..] In case of multi-buffer HASH processing, MDMAT bit must first be set before
|
||||
the successive calls to HAL_HASHEx_xxx_Start_DMA(). Then, MDMAT bit needs to be
|
||||
reset before the last call to HAL_HASHEx_xxx_Start_DMA(). Digest is finally
|
||||
retrieved thanks to HAL_HASHEx_xxx_Finish().
|
||||
|
||||
@endverbatim
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the HASH peripheral in SHA224 mode then initiate a DMA transfer
|
||||
* to feed the input buffer to the IP.
|
||||
* @note Once the DMA transfer is finished, HAL_HASHEx_SHA224_Finish() API must
|
||||
* be called to retrieve the computed digest.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (buffer to be hashed).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HASHEx_SHA224_Start_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
return HASH_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_SHA224);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Return the computed digest in SHA224 mode.
|
||||
* @note The API waits for DCIS to be set then reads the computed digest.
|
||||
* @note HAL_HASHEx_SHA224_Finish() can be used as well to retrieve the digest in
|
||||
* HMAC SHA224 mode.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pOutBuffer: pointer to the computed digest. Digest size is 28 bytes.
|
||||
* @param Timeout: Timeout value.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HASHEx_SHA224_Finish(HASH_HandleTypeDef *hhash, uint8_t* pOutBuffer, uint32_t Timeout)
|
||||
{
|
||||
return HASH_Finish(hhash, pOutBuffer, Timeout);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* @brief Initialize the HASH peripheral in SHA256 mode then initiate a DMA transfer
|
||||
* to feed the input buffer to the IP.
|
||||
* @note Once the DMA transfer is finished, HAL_HASHEx_SHA256_Finish() API must
|
||||
* be called to retrieve the computed digest.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (buffer to be hashed).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HASHEx_SHA256_Start_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
return HASH_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_SHA256);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Return the computed digest in SHA256 mode.
|
||||
* @note The API waits for DCIS to be set then reads the computed digest.
|
||||
* @note HAL_HASHEx_SHA256_Finish() can be used as well to retrieve the digest in
|
||||
* HMAC SHA256 mode.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pOutBuffer: pointer to the computed digest. Digest size is 32 bytes.
|
||||
* @param Timeout: Timeout value.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HASHEx_SHA256_Finish(HASH_HandleTypeDef *hhash, uint8_t* pOutBuffer, uint32_t Timeout)
|
||||
{
|
||||
return HASH_Finish(hhash, pOutBuffer, Timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @defgroup HASHEx_Exported_Functions_Group4 HMAC extended processing functions in polling mode
|
||||
* @brief HMAC extended processing functions using polling mode.
|
||||
*
|
||||
@verbatim
|
||||
===============================================================================
|
||||
##### Polling mode HMAC extended processing functions #####
|
||||
===============================================================================
|
||||
[..] This section provides functions allowing to calculate in polling mode
|
||||
the HMAC value using one of the following algorithms:
|
||||
(+) SHA224
|
||||
(++) HAL_HMACEx_SHA224_Start()
|
||||
(+) SHA256
|
||||
(++) HAL_HMACEx_SHA256_Start()
|
||||
|
||||
@endverbatim
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the HASH peripheral in HMAC SHA224 mode, next process pInBuffer then
|
||||
* read the computed digest.
|
||||
* @note Digest is available in pOutBuffer.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (buffer to be hashed).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @param pOutBuffer: pointer to the computed digest. Digest size is 28 bytes.
|
||||
* @param Timeout: Timeout value.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_SHA224_Start(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer, uint32_t Timeout)
|
||||
{
|
||||
return HMAC_Start(hhash, pInBuffer, Size, pOutBuffer, Timeout, HASH_ALGOSELECTION_SHA224);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the HASH peripheral in HMAC SHA256 mode, next process pInBuffer then
|
||||
* read the computed digest.
|
||||
* @note Digest is available in pOutBuffer.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (buffer to be hashed).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @param pOutBuffer: pointer to the computed digest. Digest size is 32 bytes.
|
||||
* @param Timeout: Timeout value.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_SHA256_Start(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer, uint32_t Timeout)
|
||||
{
|
||||
return HMAC_Start(hhash, pInBuffer, Size, pOutBuffer, Timeout, HASH_ALGOSELECTION_SHA256);
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
/** @defgroup HASHEx_Exported_Functions_Group5 HMAC extended processing functions in interrupt mode
|
||||
* @brief HMAC extended processing functions using interruption mode.
|
||||
*
|
||||
@verbatim
|
||||
===============================================================================
|
||||
##### Interrupt mode HMAC extended processing functions #####
|
||||
===============================================================================
|
||||
[..] This section provides functions allowing to calculate in interrupt mode
|
||||
the HMAC value using one of the following algorithms:
|
||||
(+) SHA224
|
||||
(++) HAL_HMACEx_SHA224_Start_IT()
|
||||
(+) SHA256
|
||||
(++) HAL_HMACEx_SHA256_Start_IT()
|
||||
|
||||
@endverbatim
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the HASH peripheral in HMAC SHA224 mode, next process pInBuffer then
|
||||
* read the computed digest in interrupt mode.
|
||||
* @note Digest is available in pOutBuffer.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (buffer to be hashed).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @param pOutBuffer: pointer to the computed digest. Digest size is 28 bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_SHA224_Start_IT(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer)
|
||||
{
|
||||
return HMAC_Start_IT(hhash, pInBuffer, Size, pOutBuffer, HASH_ALGOSELECTION_SHA224);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the HASH peripheral in HMAC SHA256 mode, next process pInBuffer then
|
||||
* read the computed digest in interrupt mode.
|
||||
* @note Digest is available in pOutBuffer.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (buffer to be hashed).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @param pOutBuffer: pointer to the computed digest. Digest size is 32 bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_SHA256_Start_IT(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer)
|
||||
{
|
||||
return HMAC_Start_IT(hhash, pInBuffer, Size, pOutBuffer, HASH_ALGOSELECTION_SHA256);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
/** @defgroup HASHEx_Exported_Functions_Group6 HMAC extended processing functions in DMA mode
|
||||
* @brief HMAC extended processing functions using DMA mode.
|
||||
*
|
||||
@verbatim
|
||||
===============================================================================
|
||||
##### DMA mode HMAC extended processing functions #####
|
||||
===============================================================================
|
||||
[..] This section provides functions allowing to calculate in DMA mode
|
||||
the HMAC value using one of the following algorithms:
|
||||
(+) SHA224
|
||||
(++) HAL_HMACEx_SHA224_Start_DMA()
|
||||
(+) SHA256
|
||||
(++) HAL_HMACEx_SHA256_Start_DMA()
|
||||
|
||||
[..] When resorting to DMA mode to enter the data in the IP for HMAC processing,
|
||||
user must resort to HAL_HMACEx_xxx_Start_DMA() then read the resulting digest
|
||||
with HAL_HASHEx_xxx_Finish().
|
||||
|
||||
|
||||
@endverbatim
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* @brief Initialize the HASH peripheral in HMAC SHA224 mode then initiate the required
|
||||
* DMA transfers to feed the key and the input buffer to the IP.
|
||||
* @note Once the DMA transfers are finished (indicated by hhash->State set back
|
||||
* to HAL_HASH_STATE_READY), HAL_HASHEx_SHA224_Finish() API must be called to retrieve
|
||||
* the computed digest.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @note If MDMAT bit is set before calling this function (multi-buffer
|
||||
* HASH processing case), the input buffer size (in bytes) must be
|
||||
* a multiple of 4 otherwise, the HASH digest computation is corrupted.
|
||||
* For the processing of the last buffer of the thread, MDMAT bit must
|
||||
* be reset and the buffer length (in bytes) doesn't have to be a
|
||||
* multiple of 4.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (buffer to be hashed).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_SHA224_Start_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
return HMAC_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_SHA224);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the HASH peripheral in HMAC SHA224 mode then initiate the required
|
||||
* DMA transfers to feed the key and the input buffer to the IP.
|
||||
* @note Once the DMA transfers are finished (indicated by hhash->State set back
|
||||
* to HAL_HASH_STATE_READY), HAL_HASHEx_SHA256_Finish() API must be called to retrieve
|
||||
* the computed digest.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @note If MDMAT bit is set before calling this function (multi-buffer
|
||||
* HASH processing case), the input buffer size (in bytes) must be
|
||||
* a multiple of 4 otherwise, the HASH digest computation is corrupted.
|
||||
* For the processing of the last buffer of the thread, MDMAT bit must
|
||||
* be reset and the buffer length (in bytes) doesn't have to be a
|
||||
* multiple of 4.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (buffer to be hashed).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_SHA256_Start_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
return HMAC_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_SHA256);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @defgroup HASHEx_Exported_Functions_Group7 Multi-buffer HMAC extended processing functions in DMA mode
|
||||
* @brief HMAC extended processing functions in multi-buffer DMA mode.
|
||||
*
|
||||
@verbatim
|
||||
===============================================================================
|
||||
##### Multi-buffer DMA mode HMAC extended processing functions #####
|
||||
===============================================================================
|
||||
[..] This section provides functions to manage HMAC multi-buffer
|
||||
DMA-based processing for MD5, SHA1, SHA224 and SHA256 algorithms.
|
||||
(+) MD5
|
||||
(++) HAL_HMACEx_MD5_Step1_2_DMA()
|
||||
(++) HAL_HMACEx_MD5_Step2_DMA()
|
||||
(++) HAL_HMACEx_MD5_Step2_3_DMA()
|
||||
(+) SHA1
|
||||
(++) HAL_HMACEx_SHA1_Step1_2_DMA()
|
||||
(++) HAL_HMACEx_SHA1_Step2_DMA()
|
||||
(++) HAL_HMACEx_SHA1_Step2_3_DMA()
|
||||
(+) SHA256
|
||||
(++) HAL_HMACEx_SHA224_Step1_2_DMA()
|
||||
(++) HAL_HMACEx_SHA224_Step2_DMA()
|
||||
(++) HAL_HMACEx_SHA224_Step2_3_DMA()
|
||||
(+) SHA256
|
||||
(++) HAL_HMACEx_SHA256_Step1_2_DMA()
|
||||
(++) HAL_HMACEx_SHA256_Step2_DMA()
|
||||
(++) HAL_HMACEx_SHA256_Step2_3_DMA()
|
||||
|
||||
[..] User must first start-up the multi-buffer DMA-based HMAC computation in
|
||||
calling HAL_HMACEx_xxx_Step1_2_DMA(). This carries out HMAC step 1 and
|
||||
intiates step 2 with the first input buffer.
|
||||
|
||||
[..] The following buffers are next fed to the IP with a call to the API
|
||||
HAL_HMACEx_xxx_Step2_DMA(). There may be several consecutive calls
|
||||
to this API.
|
||||
|
||||
[..] Multi-buffer DMA-based HMAC computation is wrapped up by a call to
|
||||
HAL_HMACEx_xxx_Step2_3_DMA(). This finishes step 2 in feeding the last input
|
||||
buffer to the IP then carries out step 3.
|
||||
|
||||
[..] Digest is retrieved by a call to HAL_HASH_xxx_Finish() for MD-5 or
|
||||
SHA-1, to HAL_HASHEx_xxx_Finish() for SHA-224 or SHA-256.
|
||||
|
||||
[..] If only two buffers need to be consecutively processed, a call to
|
||||
HAL_HMACEx_xxx_Step1_2_DMA() followed by a call to HAL_HMACEx_xxx_Step2_3_DMA()
|
||||
is sufficient.
|
||||
|
||||
@endverbatim
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief MD5 HMAC step 1 completion and step 2 start in multi-buffer DMA mode.
|
||||
* @note Step 1 consists in writing the inner hash function key in the IP,
|
||||
* step 2 consists in writing the message text.
|
||||
* @note The API carries out the HMAC step 1 then starts step 2 with
|
||||
* the first buffer entered to the IP. DCAL bit is not automatically set after
|
||||
* the message buffer feeding, allowing other messages DMA transfers to occur.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @note The input buffer size (in bytes) must be a multiple of 4 otherwise, the
|
||||
* HASH digest computation is corrupted.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (message buffer).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_MD5_Step1_2_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
hhash->DigestCalculationDisable = SET;
|
||||
return HMAC_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_MD5);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MD5 HMAC step 2 in multi-buffer DMA mode.
|
||||
* @note Step 2 consists in writing the message text in the IP.
|
||||
* @note The API carries on the HMAC step 2, applied to the buffer entered as input
|
||||
* parameter. DCAL bit is not automatically set after the message buffer feeding,
|
||||
* allowing other messages DMA transfers to occur.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @note The input buffer size (in bytes) must be a multiple of 4 otherwise, the
|
||||
* HASH digest computation is corrupted.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (message buffer).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_MD5_Step2_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
if (hhash->DigestCalculationDisable != SET)
|
||||
{
|
||||
return HAL_ERROR;
|
||||
}
|
||||
return HMAC_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_MD5);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MD5 HMAC step 2 wrap-up and step 3 completion in multi-buffer DMA mode.
|
||||
* @note Step 2 consists in writing the message text in the IP,
|
||||
* step 3 consists in writing the outer hash function key.
|
||||
* @note The API wraps up the HMAC step 2 in processing the buffer entered as input
|
||||
* parameter (the input buffer must be the last one of the multi-buffer thread)
|
||||
* then carries out HMAC step 3.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @note Once the DMA transfers are finished (indicated by hhash->State set back
|
||||
* to HAL_HASH_STATE_READY), HAL_HASHEx_SHA256_Finish() API must be called to retrieve
|
||||
* the computed digest.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (message buffer).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_MD5_Step2_3_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
hhash->DigestCalculationDisable = RESET;
|
||||
return HMAC_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_MD5);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief SHA1 HMAC step 1 completion and step 2 start in multi-buffer DMA mode.
|
||||
* @note Step 1 consists in writing the inner hash function key in the IP,
|
||||
* step 2 consists in writing the message text.
|
||||
* @note The API carries out the HMAC step 1 then starts step 2 with
|
||||
* the first buffer entered to the IP. DCAL bit is not automatically set after
|
||||
* the message buffer feeding, allowing other messages DMA transfers to occur.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @note The input buffer size (in bytes) must be a multiple of 4 otherwise, the
|
||||
* HASH digest computation is corrupted.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (message buffer).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_SHA1_Step1_2_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
hhash->DigestCalculationDisable = SET;
|
||||
return HMAC_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_SHA1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SHA1 HMAC step 2 in multi-buffer DMA mode.
|
||||
* @note Step 2 consists in writing the message text in the IP.
|
||||
* @note The API carries on the HMAC step 2, applied to the buffer entered as input
|
||||
* parameter. DCAL bit is not automatically set after the message buffer feeding,
|
||||
* allowing other messages DMA transfers to occur.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @note The input buffer size (in bytes) must be a multiple of 4 otherwise, the
|
||||
* HASH digest computation is corrupted.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (message buffer).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_SHA1_Step2_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
if (hhash->DigestCalculationDisable != SET)
|
||||
{
|
||||
return HAL_ERROR;
|
||||
}
|
||||
return HMAC_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_SHA1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SHA1 HMAC step 2 wrap-up and step 3 completion in multi-buffer DMA mode.
|
||||
* @note Step 2 consists in writing the message text in the IP,
|
||||
* step 3 consists in writing the outer hash function key.
|
||||
* @note The API wraps up the HMAC step 2 in processing the buffer entered as input
|
||||
* parameter (the input buffer must be the last one of the multi-buffer thread)
|
||||
* then carries out HMAC step 3.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @note Once the DMA transfers are finished (indicated by hhash->State set back
|
||||
* to HAL_HASH_STATE_READY), HAL_HASHEx_SHA256_Finish() API must be called to retrieve
|
||||
* the computed digest.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (message buffer).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_SHA1_Step2_3_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
hhash->DigestCalculationDisable = RESET;
|
||||
return HMAC_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_SHA1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SHA224 HMAC step 1 completion and step 2 start in multi-buffer DMA mode.
|
||||
* @note Step 1 consists in writing the inner hash function key in the IP,
|
||||
* step 2 consists in writing the message text.
|
||||
* @note The API carries out the HMAC step 1 then starts step 2 with
|
||||
* the first buffer entered to the IP. DCAL bit is not automatically set after
|
||||
* the message buffer feeding, allowing other messages DMA transfers to occur.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @note The input buffer size (in bytes) must be a multiple of 4 otherwise, the
|
||||
* HASH digest computation is corrupted.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (message buffer).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_SHA224_Step1_2_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
hhash->DigestCalculationDisable = SET;
|
||||
return HMAC_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_SHA224);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SHA224 HMAC step 2 in multi-buffer DMA mode.
|
||||
* @note Step 2 consists in writing the message text in the IP.
|
||||
* @note The API carries on the HMAC step 2, applied to the buffer entered as input
|
||||
* parameter. DCAL bit is not automatically set after the message buffer feeding,
|
||||
* allowing other messages DMA transfers to occur.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @note The input buffer size (in bytes) must be a multiple of 4 otherwise, the
|
||||
* HASH digest computation is corrupted.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (message buffer).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_SHA224_Step2_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
if (hhash->DigestCalculationDisable != SET)
|
||||
{
|
||||
return HAL_ERROR;
|
||||
}
|
||||
return HMAC_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_SHA224);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SHA224 HMAC step 2 wrap-up and step 3 completion in multi-buffer DMA mode.
|
||||
* @note Step 2 consists in writing the message text in the IP,
|
||||
* step 3 consists in writing the outer hash function key.
|
||||
* @note The API wraps up the HMAC step 2 in processing the buffer entered as input
|
||||
* parameter (the input buffer must be the last one of the multi-buffer thread)
|
||||
* then carries out HMAC step 3.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @note Once the DMA transfers are finished (indicated by hhash->State set back
|
||||
* to HAL_HASH_STATE_READY), HAL_HASHEx_SHA256_Finish() API must be called to retrieve
|
||||
* the computed digest.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (message buffer).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_SHA224_Step2_3_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
hhash->DigestCalculationDisable = RESET;
|
||||
return HMAC_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_SHA224);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SHA256 HMAC step 1 completion and step 2 start in multi-buffer DMA mode.
|
||||
* @note Step 1 consists in writing the inner hash function key in the IP,
|
||||
* step 2 consists in writing the message text.
|
||||
* @note The API carries out the HMAC step 1 then starts step 2 with
|
||||
* the first buffer entered to the IP. DCAL bit is not automatically set after
|
||||
* the message buffer feeding, allowing other messages DMA transfers to occur.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @note The input buffer size (in bytes) must be a multiple of 4 otherwise, the
|
||||
* HASH digest computation is corrupted.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (message buffer).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_SHA256_Step1_2_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
hhash->DigestCalculationDisable = SET;
|
||||
return HMAC_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_SHA256);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SHA256 HMAC step 2 in multi-buffer DMA mode.
|
||||
* @note Step 2 consists in writing the message text in the IP.
|
||||
* @note The API carries on the HMAC step 2, applied to the buffer entered as input
|
||||
* parameter. DCAL bit is not automatically set after the message buffer feeding,
|
||||
* allowing other messages DMA transfers to occur.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @note The input buffer size (in bytes) must be a multiple of 4 otherwise, the
|
||||
* HASH digest computation is corrupted.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (message buffer).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_SHA256_Step2_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
if (hhash->DigestCalculationDisable != SET)
|
||||
{
|
||||
return HAL_ERROR;
|
||||
}
|
||||
return HMAC_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_SHA256);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SHA256 HMAC step 2 wrap-up and step 3 completion in multi-buffer DMA mode.
|
||||
* @note Step 2 consists in writing the message text in the IP,
|
||||
* step 3 consists in writing the outer hash function key.
|
||||
* @note The API wraps up the HMAC step 2 in processing the buffer entered as input
|
||||
* parameter (the input buffer must be the last one of the multi-buffer thread)
|
||||
* then carries out HMAC step 3.
|
||||
* @note Same key is used for the inner and the outer hash functions; pointer to key and
|
||||
* key size are respectively stored in hhash->Init.pKey and hhash->Init.KeySize.
|
||||
* @note Once the DMA transfers are finished (indicated by hhash->State set back
|
||||
* to HAL_HASH_STATE_READY), HAL_HASHEx_SHA256_Finish() API must be called to retrieve
|
||||
* the computed digest.
|
||||
* @param hhash: HASH handle.
|
||||
* @param pInBuffer: pointer to the input buffer (message buffer).
|
||||
* @param Size: length of the input buffer in bytes.
|
||||
* @retval HAL status
|
||||
*/
|
||||
HAL_StatusTypeDef HAL_HMACEx_SHA256_Step2_3_DMA(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size)
|
||||
{
|
||||
hhash->DigestCalculationDisable = RESET;
|
||||
return HMAC_Start_DMA(hhash, pInBuffer, Size, HASH_ALGOSELECTION_SHA256);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#endif /* defined (STM32L4A6xx) */
|
||||
|
||||
#endif /* HAL_HASH_MODULE_ENABLED */
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
2356
stm32/mk4-bootloader/stm32l4xx_hal_qspi.c
Normal file
2356
stm32/mk4-bootloader/stm32l4xx_hal_qspi.c
Normal file
File diff suppressed because it is too large
Load Diff
1531
stm32/mk4-bootloader/stm32l4xx_hal_rcc.c
Normal file
1531
stm32/mk4-bootloader/stm32l4xx_hal_rcc.c
Normal file
File diff suppressed because it is too large
Load Diff
2835
stm32/mk4-bootloader/stm32l4xx_hal_rcc_ex.c
Normal file
2835
stm32/mk4-bootloader/stm32l4xx_hal_rcc_ex.c
Normal file
File diff suppressed because it is too large
Load Diff
3886
stm32/mk4-bootloader/stm32l4xx_hal_spi.c
Normal file
3886
stm32/mk4-bootloader/stm32l4xx_hal_spi.c
Normal file
File diff suppressed because it is too large
Load Diff
2292
stm32/mk4-bootloader/stm32l4xx_hal_usart.c
Normal file
2292
stm32/mk4-bootloader/stm32l4xx_hal_usart.c
Normal file
File diff suppressed because it is too large
Load Diff
588
stm32/mk4-bootloader/storage.c
Normal file
588
stm32/mk4-bootloader/storage.c
Normal file
@ -0,0 +1,588 @@
|
||||
// (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
//
|
||||
// storage.c -- manage flash and its sensitive contents.
|
||||
//
|
||||
// NOTE: ST's flash is different from others: it has ECC in effect, and can only
|
||||
// be programmed once. Writing ones is included in that. I'm used to flash that
|
||||
// can go from 1 to 0 anytime. One exception is you can write (64-bit) zero into flash
|
||||
// that is not erased. Consequence is if DFU file includes a page we want to write
|
||||
// later, we would need to erase it first. Not clear if DFU can do a bulk erase.
|
||||
//
|
||||
//
|
||||
#include "basics.h"
|
||||
#include "storage.h"
|
||||
#include "rng.h"
|
||||
#include "oled.h"
|
||||
#include "ae.h"
|
||||
#include "console.h"
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "assets/screens.h"
|
||||
#include "stm32l4xx_hal.h"
|
||||
#include "constant_time.h"
|
||||
|
||||
const uint32_t num_pages_locked = ((BL_FLASH_SIZE + BL_NVROM_SIZE) / 0x800)-1; // == 15
|
||||
|
||||
// flash_setup0()
|
||||
//
|
||||
void
|
||||
flash_setup0(void)
|
||||
{
|
||||
// PROBLEM: we are running in bank 1 (of 2) and want to program
|
||||
// bits in the same bank. Cannot read bank while programming it.
|
||||
// Therefore, must have our programming code running in RAM.
|
||||
|
||||
// put the ram-callable functions into place
|
||||
extern uint8_t _srelocate, _etext, _erelocate;
|
||||
memcpy(&_srelocate, &_etext, ((uint32_t)&_erelocate)-(uint32_t)&_srelocate);
|
||||
|
||||
// turn on clock to flash registers
|
||||
__HAL_RCC_FLASH_CLK_ENABLE();
|
||||
}
|
||||
|
||||
// _flash_wait_done()
|
||||
//
|
||||
// Like FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE)
|
||||
// Absolutely MUST be in RAM.
|
||||
//
|
||||
__attribute__((section(".ramfunc")))
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t
|
||||
_flash_wait_done(void)
|
||||
{
|
||||
while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) {
|
||||
// busy wait
|
||||
}
|
||||
|
||||
if((__HAL_FLASH_GET_FLAG(FLASH_FLAG_OPERR)) || (__HAL_FLASH_GET_FLAG(FLASH_FLAG_PROGERR)) ||
|
||||
(__HAL_FLASH_GET_FLAG(FLASH_FLAG_WRPERR)) || (__HAL_FLASH_GET_FLAG(FLASH_FLAG_PGAERR)) ||
|
||||
(__HAL_FLASH_GET_FLAG(FLASH_FLAG_SIZERR)) || (__HAL_FLASH_GET_FLAG(FLASH_FLAG_PGSERR)) ||
|
||||
(__HAL_FLASH_GET_FLAG(FLASH_FLAG_MISERR)) || (__HAL_FLASH_GET_FLAG(FLASH_FLAG_FASTERR)) ||
|
||||
(__HAL_FLASH_GET_FLAG(FLASH_FLAG_RDERR)) || (__HAL_FLASH_GET_FLAG(FLASH_FLAG_OPTVERR)) ||
|
||||
#if defined (STM32L431xx) || defined (STM32L432xx) || defined (STM32L433xx) || defined (STM32L442xx) || defined (STM32L443xx) || \
|
||||
defined (STM32L451xx) || defined (STM32L452xx) || defined (STM32L462xx) || defined (STM32L496xx) || defined (STM32L4A6xx)
|
||||
(__HAL_FLASH_GET_FLAG(FLASH_FLAG_ECCD)) || (__HAL_FLASH_GET_FLAG(FLASH_FLAG_PEMPTY))
|
||||
#else
|
||||
(__HAL_FLASH_GET_FLAG(FLASH_FLAG_ECCD))
|
||||
#endif
|
||||
) {
|
||||
// Save an error code; somewhat random
|
||||
return FLASH->SR;
|
||||
}
|
||||
|
||||
// Check FLASH End of Operation flag
|
||||
if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP)) {
|
||||
// Clear FLASH End of Operation pending bit
|
||||
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// flash_lock()
|
||||
//
|
||||
// Ok to run from flash.
|
||||
//
|
||||
void
|
||||
flash_lock(void)
|
||||
{
|
||||
// see HAL_FLASH_Lock();
|
||||
SET_BIT(FLASH->CR, FLASH_CR_LOCK);
|
||||
}
|
||||
|
||||
// flash_unlock()
|
||||
//
|
||||
// Ok to run from flash.
|
||||
//
|
||||
void
|
||||
flash_unlock(void)
|
||||
{
|
||||
// see HAL_FLASH_Unlock();
|
||||
if(READ_BIT(FLASH->CR, FLASH_CR_LOCK)) {
|
||||
// Authorize the FLASH Registers access
|
||||
WRITE_REG(FLASH->KEYR, FLASH_KEY1);
|
||||
WRITE_REG(FLASH->KEYR, FLASH_KEY2);
|
||||
|
||||
if(READ_BIT(FLASH->CR, FLASH_CR_LOCK)) {
|
||||
INCONSISTENT("failed to unlock");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flash_ob_lock()
|
||||
//
|
||||
// Enable write access to "option bytes".
|
||||
// - also does "launch" when done
|
||||
// - also locks/unlocks the main flash
|
||||
//
|
||||
void
|
||||
flash_ob_lock(bool lock)
|
||||
{
|
||||
if(!lock) {
|
||||
// unlock sequence
|
||||
if(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK)) {
|
||||
flash_unlock();
|
||||
|
||||
WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY1);
|
||||
WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY2);
|
||||
|
||||
if(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK)) {
|
||||
INCONSISTENT("failed to OB unlock");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// write changes to OB flash bytes
|
||||
|
||||
// Set OPTSTRT bit
|
||||
SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);
|
||||
|
||||
/// Wait for update to complete
|
||||
_flash_wait_done();
|
||||
|
||||
// lock OB again.
|
||||
SET_BIT(FLASH->CR, FLASH_CR_OPTLOCK);
|
||||
|
||||
// include "launch" to make them take effect NOW
|
||||
SET_BIT(FLASH->CR, FLASH_CR_OBL_LAUNCH);
|
||||
|
||||
_flash_wait_done();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// flash_burn()
|
||||
//
|
||||
// My simplified version of HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, ...)
|
||||
//
|
||||
// NOTES:
|
||||
// - this function **AND** everything it calls, must be in RAM
|
||||
// - interrupts are already off here (entire bootloader)
|
||||
// - return non-zero on failure; don't try to handle anything
|
||||
//
|
||||
__attribute__((section(".ramfunc")))
|
||||
__attribute__((noinline))
|
||||
int
|
||||
flash_burn(uint32_t address, uint64_t val)
|
||||
{
|
||||
uint32_t rv;
|
||||
|
||||
// just in case?
|
||||
_flash_wait_done();
|
||||
|
||||
// clear any and all errors
|
||||
FLASH->SR = FLASH->SR & 0xffff;
|
||||
|
||||
// disable data cache
|
||||
__HAL_FLASH_DATA_CACHE_DISABLE();
|
||||
|
||||
// Program double-word (64-bit) at a specified address
|
||||
// see FLASH_Program_DoubleWord(Address, Data);
|
||||
|
||||
// Set PG bit
|
||||
CLEAR_BIT(FLASH->CR, (FLASH_CR_PG | FLASH_CR_MER1 | FLASH_CR_PER | FLASH_CR_PNB)); // added
|
||||
SET_BIT(FLASH->CR, FLASH_CR_PG);
|
||||
|
||||
// Program a double word
|
||||
*(__IO uint32_t *)(address) = (uint32_t)val;
|
||||
*(__IO uint32_t *)(address+4) = (uint32_t)(val >> 32);
|
||||
|
||||
rv = _flash_wait_done();
|
||||
if(rv) return rv;
|
||||
|
||||
// If the program operation is completed, disable the PG or FSTPG Bit
|
||||
CLEAR_BIT(FLASH->CR, FLASH_CR_PG);
|
||||
|
||||
// Flush the caches to be sure of data consistency, and reenable.
|
||||
__HAL_FLASH_DATA_CACHE_RESET();
|
||||
__HAL_FLASH_DATA_CACHE_ENABLE();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// flash_page_erase()
|
||||
//
|
||||
// See HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError)
|
||||
//
|
||||
__attribute__((section(".ramfunc")))
|
||||
__attribute__((noinline))
|
||||
int
|
||||
flash_page_erase(uint32_t address)
|
||||
{
|
||||
uint32_t page_num = (address & 0x7ffffff) / FLASH_PAGE_SIZE; // 2k pages
|
||||
|
||||
// protect ourselves!
|
||||
if(page_num < ((BL_FLASH_SIZE + BL_NVROM_SIZE) / FLASH_PAGE_SIZE)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// always operate on both banks.
|
||||
bool bank2 = (page_num >= 256);
|
||||
page_num &= 0xff;
|
||||
|
||||
// just in case?
|
||||
_flash_wait_done();
|
||||
|
||||
// clear any and all errors
|
||||
FLASH->SR = FLASH->SR & 0xffff;
|
||||
|
||||
// disable data cache
|
||||
__HAL_FLASH_DATA_CACHE_DISABLE();
|
||||
|
||||
// choose appropriate bank to work on.
|
||||
if(bank2) {
|
||||
SET_BIT(FLASH->CR, FLASH_CR_BKER);
|
||||
} else {
|
||||
CLEAR_BIT(FLASH->CR, FLASH_CR_BKER);
|
||||
}
|
||||
|
||||
// Proceed to erase the page
|
||||
MODIFY_REG(FLASH->CR, FLASH_CR_PNB, (page_num << POSITION_VAL(FLASH_CR_PNB)));
|
||||
SET_BIT(FLASH->CR, FLASH_CR_PER);
|
||||
SET_BIT(FLASH->CR, FLASH_CR_STRT);
|
||||
|
||||
// Wait til done
|
||||
_flash_wait_done();
|
||||
|
||||
// If the erase operation is completed, disable the PER Bit
|
||||
CLEAR_BIT(FLASH->CR, (FLASH_CR_PER | FLASH_CR_PNB));
|
||||
|
||||
// Flush the caches to be sure of data consistency, and reenable.
|
||||
__HAL_FLASH_DATA_CACHE_RESET();
|
||||
__HAL_FLASH_DATA_CACHE_ENABLE();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// pick_pairing_secret()
|
||||
//
|
||||
static void
|
||||
pick_pairing_secret(void)
|
||||
{
|
||||
// important the RNG works here. ok to call setup multiple times.
|
||||
rng_setup();
|
||||
|
||||
// Demo to anyone watching that the RNG is working, but likely only
|
||||
// to be seen by production team during initial powerup.
|
||||
uint8_t tmp[1024];
|
||||
for(int i=0; i<1000; i++) {
|
||||
rng_buffer(tmp, sizeof(tmp));
|
||||
|
||||
oled_show_raw(sizeof(tmp), (void *)tmp);
|
||||
}
|
||||
|
||||
oled_factory_busy();
|
||||
|
||||
// .. but don't use those numbers, because those are semi-public now.
|
||||
uint32_t secret[8];
|
||||
for(int i=0; i<8; i++) {
|
||||
secret[i] = rng_sample();
|
||||
}
|
||||
|
||||
// enforce policy that first word is not all ones (so it never
|
||||
// looks like unprogrammed flash).
|
||||
while(secret[0] == ~0) {
|
||||
secret[0] = rng_sample();
|
||||
}
|
||||
|
||||
|
||||
// NOTE: if any of these 64-bit words have been programmed already once, this will
|
||||
// fail because we are not pre-erasing them. However, this area is expected
|
||||
// to be written exactly once in product's lifecycle so that should be okay.
|
||||
|
||||
// Write pairing secret into flash
|
||||
{
|
||||
uint32_t dest = (uint32_t)&rom_secrets->pairing_secret;
|
||||
|
||||
flash_unlock();
|
||||
for(int i=0; i<8; i+=2, dest += 8) {
|
||||
uint64_t val = (((uint64_t)secret[i]) << 32) | secret[i+1];
|
||||
|
||||
if(flash_burn(dest, val)) {
|
||||
INCONSISTENT("flash fail");
|
||||
}
|
||||
}
|
||||
flash_lock();
|
||||
}
|
||||
|
||||
// Also at this point, pick RNG noise to use as our one-time-pad
|
||||
// for encrypting the secrets held in the 608a.
|
||||
{
|
||||
uint32_t dest = (uint32_t)&rom_secrets->otp_key;
|
||||
const uint32_t blen = sizeof(rom_secrets->otp_key)
|
||||
+ sizeof(rom_secrets->otp_key_long)
|
||||
+ sizeof(rom_secrets->hash_cache_secret);
|
||||
|
||||
STATIC_ASSERT(blen % 8 == 0);
|
||||
STATIC_ASSERT(blen == (72+416+32));
|
||||
|
||||
flash_unlock();
|
||||
for(int i=0; i<blen; i+=8, dest += 8) {
|
||||
uint64_t val = ((uint64_t)rng_sample() << 32) | rng_sample();
|
||||
|
||||
if(flash_burn(dest, val)) {
|
||||
INCONSISTENT("flash fail");
|
||||
}
|
||||
}
|
||||
flash_lock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// confirm_pairing_secret()
|
||||
//
|
||||
static void
|
||||
confirm_pairing_secret(void)
|
||||
{
|
||||
// Concern: if the above is interrupted (by an evil user), then we might program only
|
||||
// the first 64-bits and the rest would be ones. Easy to brute-force from there.
|
||||
// Solution: write also the XOR of the right value, and check at boot time.
|
||||
// LATER: probably not a concern because flash is ECC-checked on this chip.
|
||||
// BUT: we are using to mark the 2nd half of a two-phase commit w.r.t AE setup
|
||||
|
||||
uint64_t *src = (uint64_t *)&rom_secrets->pairing_secret;
|
||||
uint32_t dest = (uint32_t)&rom_secrets->pairing_secret_xor;
|
||||
|
||||
flash_unlock();
|
||||
for(int i=0; i<(32/8); i++, dest+=8, src++) {
|
||||
uint64_t val = ~(*src);
|
||||
|
||||
if(flash_burn(dest, val)) {
|
||||
INCONSISTENT("flash xor fail");
|
||||
}
|
||||
}
|
||||
flash_lock();
|
||||
}
|
||||
|
||||
// flash_save_ae_serial()
|
||||
//
|
||||
// Write the serial number of ATECC508A into flash forever.
|
||||
//
|
||||
void
|
||||
flash_save_ae_serial(const uint8_t serial[9])
|
||||
{
|
||||
|
||||
uint64_t tmp[2];
|
||||
memset(&tmp, 0x0, sizeof(tmp));
|
||||
memcpy(&tmp, serial, 9);
|
||||
|
||||
flash_setup0();
|
||||
flash_unlock();
|
||||
|
||||
if(flash_burn((uint32_t)&rom_secrets->ae_serial_number[0], tmp[0])) {
|
||||
INCONSISTENT("fail1");
|
||||
}
|
||||
if(flash_burn((uint32_t)&rom_secrets->ae_serial_number[1], tmp[1])) {
|
||||
INCONSISTENT("fail2");
|
||||
}
|
||||
|
||||
flash_lock();
|
||||
}
|
||||
|
||||
// flash_save_bag_number()
|
||||
//
|
||||
// Write bag number (probably a string)
|
||||
//
|
||||
void
|
||||
flash_save_bag_number(const uint8_t new_number[32])
|
||||
{
|
||||
|
||||
uint32_t dest = (uint32_t)&rom_secrets->bag_number[0];
|
||||
uint64_t *src = (uint64_t *)new_number;
|
||||
|
||||
flash_setup0();
|
||||
flash_unlock();
|
||||
|
||||
// NOTE: can only write once! No provision for read/check, and write
|
||||
// when non-ones will fail.
|
||||
for(int i=0; i<(32/8); i++, dest+=8, src++) {
|
||||
if(flash_burn(dest, *src)) {
|
||||
INCONSISTENT("fail write");
|
||||
}
|
||||
}
|
||||
|
||||
flash_lock();
|
||||
}
|
||||
|
||||
// flash_setup()
|
||||
//
|
||||
// This is really a state-machine, to recover boards that are booted w/ missing AE chip.
|
||||
//
|
||||
void
|
||||
flash_setup(void)
|
||||
{
|
||||
flash_setup0();
|
||||
|
||||
STATIC_ASSERT(sizeof(rom_secrets_t) <= 2048);
|
||||
|
||||
// see if we have picked a pairing secret yet.
|
||||
bool blank_ps = check_all_ones(rom_secrets->pairing_secret, 32);
|
||||
bool blank_xor = check_all_ones(rom_secrets->pairing_secret_xor, 32);
|
||||
bool blank_ae = (~rom_secrets->ae_serial_number[0] == 0);
|
||||
|
||||
if(blank_ps) {
|
||||
// get some good entropy, save it.
|
||||
pick_pairing_secret();
|
||||
|
||||
blank_ps = false;
|
||||
}
|
||||
|
||||
if(blank_xor || blank_ae) {
|
||||
|
||||
// configure and lock-down the SE
|
||||
int rv = ae_setup_config();
|
||||
|
||||
if(rv) {
|
||||
// Hardware fail speaking to AE chip ... be careful not to brick here.
|
||||
// Do not continue!! We might fix the board, or add missing pullup, etc.
|
||||
oled_show(screen_brick);
|
||||
puts("SE config fail");
|
||||
|
||||
LOCKUP_FOREVER();
|
||||
}
|
||||
|
||||
if(blank_xor) {
|
||||
// write secret again, complemented, to indicate successful AE programming
|
||||
confirm_pairing_secret();
|
||||
}
|
||||
|
||||
// real power cycle required now.
|
||||
oled_show(screen_replug);
|
||||
puts("replug required");
|
||||
|
||||
LOCKUP_FOREVER();
|
||||
}
|
||||
|
||||
if(!blank_ps && !blank_xor) {
|
||||
// check the XOR value also written: 2 phase commit
|
||||
uint8_t tmp[32];
|
||||
memcpy(tmp, rom_secrets->pairing_secret, 32);
|
||||
xor_mixin(tmp, rom_secrets->pairing_secret_xor, 32);
|
||||
|
||||
if(!check_all_ones(tmp, 32)) {
|
||||
oled_show(screen_corrupt);
|
||||
puts("corrupt pair sec");
|
||||
|
||||
// dfu won't save them here, so just die
|
||||
LOCKUP_FOREVER();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: maybe check option bytes and protections
|
||||
// implied by that are in place. If wrong, do the
|
||||
// appropriate lockdown, which might be one-way.
|
||||
// That's fine if we intend to ship units locked already.
|
||||
|
||||
// Do NOT do write every boot, as it might wear-out
|
||||
// the flash bits in OB.
|
||||
|
||||
}
|
||||
|
||||
// flash_lockdown_hard()
|
||||
//
|
||||
// Configure the OB (option bytes) to values that:
|
||||
// - ensure bootloader isn't overwritten easily.
|
||||
// - enable level 2 flash protect
|
||||
// - once level 2 is set, no going back.
|
||||
//
|
||||
// This is a one-way trip. Might need power cycle to (fully?) take effect.
|
||||
//
|
||||
void
|
||||
flash_lockdown_hard(uint8_t rdp_level_code)
|
||||
{
|
||||
flash_setup0();
|
||||
|
||||
// see FLASH_OB_WRPConfig()
|
||||
|
||||
flash_ob_lock(false);
|
||||
// lock first 32k against any writes
|
||||
FLASH->WRP1AR = (num_pages_locked << 16);
|
||||
FLASH->WRP1BR = 0xff; // unused.
|
||||
FLASH->WRP2AR = 0xff; // unused.
|
||||
FLASH->WRP2BR = 0xff; // unused.
|
||||
|
||||
#if 0
|
||||
// PCRO = Proprietary Code Read-Out (protection)
|
||||
// - isn't useful to us (doesn't protect data, exec-only code)
|
||||
// - "In case the Level 1 is configured and no PCROP area is defined,
|
||||
// it is mandatory to set PCROP_RDP bit to 1 (full mass erase when
|
||||
// the RDP level is decreased from Level 1 to Level 0)."
|
||||
// - D-bus access blocked, even for code running inside the PCROP area! (AN4758)
|
||||
// So literal values and constant tables and such would need special linking.
|
||||
//
|
||||
FLASH->PCROP1ER = (1<<31); // set PCROP_RDP bit, since maybe we need to?
|
||||
FLASH->PCROP1SR = 0xffff;
|
||||
FLASH->PCROP2ER = (1<<31); // set PCROP_RDP bit, since maybe we need to?
|
||||
FLASH->PCROP2SR = 0xffff;
|
||||
#endif
|
||||
|
||||
// set protection level
|
||||
FLASH->OPTR = 0xffeff800 | rdp_level_code; // select level X, other values as observed
|
||||
|
||||
flash_ob_lock(true);
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
// backup_data_get()
|
||||
//
|
||||
uint32_t
|
||||
backup_data_get(int idx)
|
||||
{
|
||||
ASSERT(idx < 32);
|
||||
|
||||
return (&RTC->BKP0R)[idx];
|
||||
}
|
||||
|
||||
// backup_data_set()
|
||||
//
|
||||
void
|
||||
backup_data_set(int idx, uint32_t new_value)
|
||||
{
|
||||
ASSERT(idx < 32);
|
||||
|
||||
// unlock sequence.
|
||||
RTC->WPR = 0xCA;
|
||||
RTC->WPR = 0x53;
|
||||
|
||||
(&RTC->BKP0R)[idx] = new_value;
|
||||
|
||||
// relock (any value)
|
||||
// doesn't seem to work tho? stays unlocked
|
||||
RTC->WPR = 0xff;
|
||||
}
|
||||
#endif
|
||||
|
||||
// record_highwater_version()
|
||||
//
|
||||
int
|
||||
record_highwater_version(const uint8_t timestamp[8])
|
||||
{
|
||||
const uint8_t *otp = (const uint8_t *)OPT_FLASH_BASE;
|
||||
|
||||
ASSERT(timestamp[0] < 0x40);
|
||||
ASSERT(timestamp[0] >= 0x10);
|
||||
|
||||
// just write to first blank slot we can find.
|
||||
for(int i=0; i<NUM_OPT_SLOTS; i++, otp+=8) {
|
||||
if(check_all_ones(otp, 8)) {
|
||||
// here.
|
||||
uint64_t val = 0;
|
||||
memcpy(&val, timestamp, 8);
|
||||
|
||||
flash_setup0();
|
||||
|
||||
flash_unlock();
|
||||
flash_burn((uint32_t)otp, val);
|
||||
flash_lock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// no space.
|
||||
return 1;
|
||||
}
|
||||
|
||||
// EOF
|
||||
76
stm32/mk4-bootloader/storage.h
Normal file
76
stm32/mk4-bootloader/storage.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "basics.h"
|
||||
#include "stm32l4xx_hal.h"
|
||||
|
||||
// Details of the OTP area. 64-bit slots.
|
||||
#define OPT_FLASH_BASE 0x1FFF7000
|
||||
#define NUM_OPT_SLOTS 128
|
||||
|
||||
// This is what we're keeping secret... Kept in flash, written mostly once.
|
||||
// fields must be 64-bit aligned so they can be written independently
|
||||
typedef struct {
|
||||
// Pairing secret: picked once at factory when turned on
|
||||
// for the first time. Tied into all the secrets of the SE
|
||||
// on the same board.
|
||||
//
|
||||
uint8_t pairing_secret[32];
|
||||
uint8_t pairing_secret_xor[32];
|
||||
uint64_t ae_serial_number[2]; // 9 bytes active
|
||||
uint8_t bag_number[32]; // 32 bytes max, zero padded string
|
||||
uint8_t otp_key[72]; // key for secret encryption (seed storage)
|
||||
uint8_t otp_key_long[416]; // same, but for longer secret area
|
||||
uint8_t hash_cache_secret[32]; // encryption for cached pin hash value
|
||||
|
||||
// ... plus lots more space ...
|
||||
} rom_secrets_t;
|
||||
|
||||
// This area is defined in linker script as last page of boot loader flash.
|
||||
#define rom_secrets ((rom_secrets_t *)BL_NVROM_BASE)
|
||||
|
||||
// Call at boot time. Picks pairing secret and/or verifies it.
|
||||
void flash_setup(void);
|
||||
|
||||
// Set option-bytes region to appropriate values
|
||||
void flash_lockdown_hard(uint8_t rdp_level_code);
|
||||
|
||||
// Save a serial number from secure element
|
||||
void flash_save_ae_serial(const uint8_t serial[9]);
|
||||
|
||||
// Write bag number (probably a string)
|
||||
void flash_save_bag_number(const uint8_t new_number[32]);
|
||||
|
||||
// Are we operating in level2?
|
||||
static inline bool flash_is_security_level2(void) {
|
||||
return ((FLASH->OPTR & FLASH_OPTR_RDP_Msk) == 0xCC);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// We store some values in the RTC "backup" registers
|
||||
// - these are protected against accidental writes
|
||||
// - not cleared by system reset, full power cycle required
|
||||
// - mpy code could still change, so not secure.
|
||||
// - kinda pointless, but I have no SRAM that isn't wiped at boot
|
||||
// - XXX not working! no clock maybe? Reads as zero.
|
||||
#define IDX_WORD_LOOKUPS_USED 0x0
|
||||
#define IDX_DURESS_USED 0x1
|
||||
|
||||
uint32_t backup_data_get(int idx);
|
||||
void backup_data_set(int idx, uint32_t new_value);
|
||||
#endif
|
||||
|
||||
|
||||
// generial purpose flash functions
|
||||
void flash_setup0(void);
|
||||
void flash_lock(void);
|
||||
void flash_unlock(void);
|
||||
int flash_burn(uint32_t address, uint64_t val);
|
||||
int flash_page_erase(uint32_t address);
|
||||
|
||||
// write to OTP
|
||||
int record_highwater_version(const uint8_t timestamp[8]);
|
||||
|
||||
// EOF
|
||||
264
stm32/mk4-bootloader/verify.c
Normal file
264
stm32/mk4-bootloader/verify.c
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*
|
||||
* verify.c -- Check signatures on firmware images in flash.
|
||||
*
|
||||
*/
|
||||
#include "basics.h"
|
||||
#include "verify.h"
|
||||
#include "faster_sha256.h"
|
||||
#include "assets/screens.h"
|
||||
#include "oled.h"
|
||||
#include "console.h"
|
||||
#include "misc.h"
|
||||
#include "ae.h"
|
||||
#include "gpio.h"
|
||||
#include "delay.h"
|
||||
#include "storage.h"
|
||||
#include <string.h>
|
||||
#include "micro-ecc/uECC.h"
|
||||
#include "firmware-keys.h"
|
||||
|
||||
// 2 megabyte on mk4
|
||||
#define MAIN_FLASH_SIZE (2<<20)
|
||||
|
||||
// actual qty of bytes we check
|
||||
// - minus 64 because the firmware signature is skipped
|
||||
// - 0x400 of OTP area
|
||||
// - plus (0x28) twice for option bytes
|
||||
// - plus complete "system meory" area (boot rom with DFU)
|
||||
// - 12 bytes of device serial number
|
||||
#define TOTAL_CHECKSUM_LEN (MAIN_FLASH_SIZE - 64 + 0x400 + (0x28 *2) + 0x7000 + 12)
|
||||
|
||||
|
||||
// checksum_more()
|
||||
//
|
||||
static void
|
||||
checksum_more(SHA256_CTX *ctx, uint32_t *total, const uint8_t *addr, int len)
|
||||
{
|
||||
// mk4 has hardware hash engine, and no DFU button
|
||||
int percent = ((*total) * 100) / TOTAL_CHECKSUM_LEN;
|
||||
|
||||
#if 0
|
||||
puts2("Verify %0x");
|
||||
puthex2(percent);
|
||||
putchar('\n');
|
||||
#endif
|
||||
|
||||
oled_show_progress(screen_verify, percent);
|
||||
|
||||
sha256_update(ctx, addr, len);
|
||||
*total += len;
|
||||
}
|
||||
|
||||
|
||||
// checksum_flash()
|
||||
//
|
||||
void
|
||||
checksum_flash(uint8_t fw_digest[32], uint8_t world_digest[32])
|
||||
{
|
||||
const uint8_t *start = (const uint8_t *)FIRMWARE_START;
|
||||
|
||||
SHA256_CTX ctx;
|
||||
uint32_t total_len = 0;
|
||||
uint8_t first[32];
|
||||
|
||||
sha256_init(&ctx);
|
||||
|
||||
// start of firmware (just after we end) to header
|
||||
checksum_more(&ctx, &total_len, start, FW_HEADER_OFFSET + FW_HEADER_SIZE - 64);
|
||||
|
||||
// from after header to end
|
||||
checksum_more(&ctx, &total_len, start + FW_HEADER_OFFSET + FW_HEADER_SIZE,
|
||||
FW_HDR->firmware_length - (FW_HEADER_OFFSET + FW_HEADER_SIZE));
|
||||
|
||||
sha256_final(&ctx, first);
|
||||
|
||||
// double SHA256
|
||||
sha256_single(first, sizeof(first), fw_digest);
|
||||
|
||||
// start over, and get the rest of flash. All of it.
|
||||
sha256_init(&ctx);
|
||||
|
||||
// .. and chain in what we have so far
|
||||
sha256_update(&ctx, fw_digest, 32);
|
||||
|
||||
// bootloader, including pairing secret area.
|
||||
const uint8_t *base = (const uint8_t *)BL_FLASH_BASE;
|
||||
checksum_more(&ctx, &total_len, base, start-base);
|
||||
|
||||
// probably-blank area after firmware, and filesystem area
|
||||
const uint8_t *fs = start + FW_HDR->firmware_length;
|
||||
const uint8_t *last = base + MAIN_FLASH_SIZE;
|
||||
checksum_more(&ctx, &total_len, fs, last-fs);
|
||||
|
||||
// OTP area
|
||||
checksum_more(&ctx, &total_len, (void *)0x1fff7000, 0x400);
|
||||
|
||||
// "just in case" ... the option bytes (2 banks)
|
||||
checksum_more(&ctx, &total_len, (void *)0x1fff7800, 0x28);
|
||||
checksum_more(&ctx, &total_len, (void *)0x1ffff800, 0x28);
|
||||
|
||||
// System ROM (they say it can't change, but clearly
|
||||
// implemented as flash cells)
|
||||
checksum_more(&ctx, &total_len, (void *)0x1fff0000, 0x7000);
|
||||
|
||||
// device serial number, just for kicks
|
||||
checksum_more(&ctx, &total_len, (void *)0x1fff7590, 12);
|
||||
|
||||
ASSERT(total_len == TOTAL_CHECKSUM_LEN);
|
||||
|
||||
sha256_final(&ctx, world_digest);
|
||||
|
||||
// double SHA256 (a bitcoin fetish)
|
||||
sha256_single(world_digest, 32, world_digest);
|
||||
}
|
||||
|
||||
// get_min_version()
|
||||
//
|
||||
// Scan the OTP area and determine what the current min-version (timestamp)
|
||||
// we can allow. All zeros if any if okay.
|
||||
//
|
||||
void
|
||||
get_min_version(uint8_t min_version[8])
|
||||
{
|
||||
const uint8_t *otp = (const uint8_t *)OPT_FLASH_BASE;
|
||||
|
||||
memset(min_version, 0, 8);
|
||||
|
||||
for(int i=0; i<NUM_OPT_SLOTS; i++, otp+=8) {
|
||||
// is it programmed?
|
||||
if(otp[0] == 0xff) continue;
|
||||
|
||||
// is it a timestamp value?
|
||||
if(otp[0] >= 0x40) continue;
|
||||
if(otp[0] < 0x10) continue;
|
||||
|
||||
if(memcmp(otp, min_version, 8) > 0) {
|
||||
memcpy(min_version, otp, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check_is_downgrade()
|
||||
//
|
||||
bool
|
||||
check_is_downgrade(const uint8_t timestamp[8], const char *version)
|
||||
{
|
||||
if(version) {
|
||||
int major = (version[1] == '.') ? (version[0]-'0') : 10;
|
||||
if(major < 3) {
|
||||
// we require major version 3.0.0 or later (for mark3 hardware)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// look at FW_HDR->timestamp and compare to a growing list in main flash OTP
|
||||
uint8_t min[8];
|
||||
get_min_version(min);
|
||||
|
||||
return (memcmp(timestamp, min, 8) < 0);
|
||||
}
|
||||
|
||||
// check_factory_key()
|
||||
//
|
||||
void
|
||||
check_factory_key(uint32_t pubkey_num)
|
||||
{
|
||||
if(IS_FACTORY_KEY(pubkey_num)) return;
|
||||
|
||||
// warn the victim/altcoin user/developer
|
||||
#if RELEASE
|
||||
const int wait = 100;
|
||||
#else
|
||||
const int wait = 1;
|
||||
#endif
|
||||
|
||||
for(int i=0; i < wait; i++) {
|
||||
oled_show_progress(screen_devmode, (i*100)/wait);
|
||||
|
||||
delay_ms(250);
|
||||
}
|
||||
}
|
||||
|
||||
// verify_header()
|
||||
//
|
||||
bool
|
||||
verify_header(const coldcardFirmwareHeader_t *hdr)
|
||||
{
|
||||
if(hdr->magic_value != FW_HEADER_MAGIC) goto fail;
|
||||
if(hdr->version_string[0] == 0x0) goto fail;
|
||||
if(hdr->timestamp[0] >= 0x40) goto fail; // 22 yr product lifetime
|
||||
if(hdr->firmware_length < FW_MIN_LENGTH) goto fail;
|
||||
if(hdr->firmware_length > FW_MAX_LENGTH) goto fail;
|
||||
if(hdr->pubkey_num >= NUM_KNOWN_PUBKEYS) goto fail;
|
||||
|
||||
return true;
|
||||
fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
// verify_signature()
|
||||
//
|
||||
// Given double-sha256 over the firmware bytes, check the signature.
|
||||
//
|
||||
bool
|
||||
verify_signature(const coldcardFirmwareHeader_t *hdr, const uint8_t fw_check[32])
|
||||
{
|
||||
// this takes a few ms at least, not fast.
|
||||
int ok = uECC_verify(approved_pubkeys[hdr->pubkey_num], fw_check, 32,
|
||||
hdr->signature, uECC_secp256k1());
|
||||
|
||||
puts(ok ? "Sig ok" : "Sig fail");
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
// verify_firmware()
|
||||
//
|
||||
void
|
||||
verify_firmware(void)
|
||||
{
|
||||
STATIC_ASSERT(sizeof(coldcardFirmwareHeader_t) == FW_HEADER_SIZE);
|
||||
|
||||
// watch for unprogrammed header. and some
|
||||
if(FW_HDR->version_string[0] == 0xff) goto blank;
|
||||
if(!verify_header(FW_HDR)) goto fail;
|
||||
|
||||
// measure checksum
|
||||
uint8_t fw_check[32], world_check[32];
|
||||
checksum_flash(fw_check, world_check);
|
||||
|
||||
// Verify the signature
|
||||
// - use pubkey_num to pick a specific key
|
||||
if(!verify_signature(FW_HDR, fw_check)) goto fail;
|
||||
|
||||
// Push the hash to the SE which might make the Genuine light green,
|
||||
// but only if we arrived at same hash before. It decides.
|
||||
int not_green = ae_set_gpio_secure(world_check);
|
||||
|
||||
// XXX change this, no more dev key
|
||||
// maybe show big warning if not an "approved" key
|
||||
if(not_green) {
|
||||
check_factory_key(FW_HDR->pubkey_num);
|
||||
}
|
||||
|
||||
puts("good firmware");
|
||||
oled_show_progress(screen_verify, 100);
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
puts("corrupt firmware");
|
||||
oled_show(screen_corrupt);
|
||||
enter_dfu();
|
||||
return;
|
||||
|
||||
blank:
|
||||
puts("no firmware");
|
||||
oled_show(screen_dfu);
|
||||
enter_dfu();
|
||||
return;
|
||||
}
|
||||
|
||||
// EOF
|
||||
34
stm32/mk4-bootloader/verify.h
Normal file
34
stm32/mk4-bootloader/verify.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
#include "basics.h"
|
||||
#include "sigheader.h"
|
||||
|
||||
// Where the firmware starts in flash (right after us).
|
||||
#define FIRMWARE_START (BL_FLASH_BASE + BL_FLASH_SIZE + BL_NVROM_SIZE)
|
||||
|
||||
// The in-flash header.
|
||||
#define FW_HDR ((coldcardFirmwareHeader_t *)(FIRMWARE_START + FW_HEADER_OFFSET))
|
||||
|
||||
// check we have something valid, and signed, in memory
|
||||
extern void verify_firmware(void);
|
||||
|
||||
// read and checksum over **all** of flash memory
|
||||
void checksum_flash(uint8_t fw_digest[32], uint8_t world_digest[32]);
|
||||
|
||||
// do some range/sanity checking on a signed header
|
||||
bool verify_header(const coldcardFirmwareHeader_t *hdr);
|
||||
|
||||
// give digest over firmware, check the signature from header
|
||||
// - use only with a verified header (call verify_header first)
|
||||
// - return T if ok.
|
||||
bool verify_signature(const coldcardFirmwareHeader_t *hdr, const uint8_t fw_check[32]);
|
||||
|
||||
// check if proposed version is new enough (based on OTP values)
|
||||
bool check_is_downgrade(const uint8_t timestamp[8], const char *version);
|
||||
|
||||
// read what the watermark is, might be all zeros
|
||||
void get_min_version(uint8_t min_version[8]);
|
||||
|
||||
// EOF
|
||||
18
stm32/mk4-bootloader/version.c
Normal file
18
stm32/mk4-bootloader/version.c
Normal file
@ -0,0 +1,18 @@
|
||||
// (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
//
|
||||
// Version string. Careful with changes because parsed by python code and probably others.
|
||||
//
|
||||
#include "version.h"
|
||||
|
||||
// the Makefile will define BUILD and GIT values.
|
||||
const char version_string[] = RELEASE_VERSION
|
||||
#ifdef BUILD_TIME
|
||||
" time=" BUILD_TIME
|
||||
#endif
|
||||
#ifdef GIT_HASH
|
||||
" git=" GIT_HASH
|
||||
#endif
|
||||
#ifndef RELEASE
|
||||
" DEV=1"
|
||||
#endif
|
||||
;
|
||||
11
stm32/mk4-bootloader/version.h
Normal file
11
stm32/mk4-bootloader/version.h
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
// Public version number for humans. Lots more version data added by Makefile.
|
||||
#define RELEASE_VERSION "3.0.0"
|
||||
|
||||
extern const char version_string[];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user