first commit

This commit is contained in:
Peter D. Gray 2021-05-11 14:03:13 -04:00
parent ed6f7d422a
commit 3a773d13c2
No known key found for this signature in database
GPG Key ID: F0E6CC6AFC16CF7B
77 changed files with 27407 additions and 11 deletions

11
stm32/mk4-bootloader/.gitignore vendored Normal file
View 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

View 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

File diff suppressed because it is too large Load Diff

188
stm32/mk4-bootloader/ae.h Normal file
View 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

View 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 */

View File

@ -0,0 +1,2 @@
# Buy the fine product and copy file here (minus spaces)
FontAwesome5Pro-Light-300.otf

Binary file not shown.

View File

@ -0,0 +1,4 @@
all screens.c screens.h: convert.py
python3 convert.py

View File

@ -0,0 +1,13 @@
# Boot Loader screens
![sampler images](sampler.png)
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/)

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

View 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')

View 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.**

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View 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
};

View 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[];

View File

@ -0,0 +1 @@
../../bootloader/assets/zevv-peep-iso8859-15-07x14.bdf

View File

@ -0,0 +1 @@
../../bootloader/assets/zevv-peep-iso8859-15-07x14.pbm

View File

@ -0,0 +1 @@
../../bootloader/assets/zevv-peep-iso8859-15-07x14.pil

View File

@ -0,0 +1 @@
../../bootloader/assets/zevv-peep-iso8859-15-10x20.bdf

View File

@ -0,0 +1 @@
../../bootloader/assets/zevv-peep-iso8859-15-10x20.pbm

View File

@ -0,0 +1 @@
../../bootloader/assets/zevv-peep-iso8859-15-10x20.pil

View 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))

View 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

View 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);

View File

@ -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)
{

View File

@ -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

View 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

View 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);
}
}

View 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

View 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);

View 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

View 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);

View 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

View 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

View 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

View 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)

View 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

View 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

View 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
View 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()

View 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 = . ;
}

View 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)

View File

@ -0,0 +1 @@
../bootloader/micro-ecc

View 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);

View 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
View 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

View 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

File diff suppressed because it is too large Load Diff

118
stm32/mk4-bootloader/pins.h Normal file
View 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

View 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

View 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

View 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

View 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

View 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);

View 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

View 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

View 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

View 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);

View 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

View 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

View 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

View 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>&copy; 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****/

View 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>&copy; 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****/

View 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>&copy; 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****/

File diff suppressed because it is too large Load Diff

View 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>&copy; 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****/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

View 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
;

View 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[];