diff --git a/Makefile b/Makefile index fce0a317c5..7c270e621e 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ ifneq ($(EMULATOR),1) OBJS += timer.o endif +OBJS += usb_standard.o OBJS += usb21_standard.o OBJS += webusb.o OBJS += winusb.o diff --git a/Makefile.include b/Makefile.include index fdf6b5fca5..029dddae7d 100644 --- a/Makefile.include +++ b/Makefile.include @@ -134,8 +134,10 @@ endif ifeq ($(MEMORY_PROTECT), 0) CFLAGS += -DMEMORY_PROTECT=0 +$(info MEMORY_PROTECT=0) else CFLAGS += -DMEMORY_PROTECT=1 +$(info MEMORY_PROTECT=1) endif ifeq ($(DEBUG_RNG), 1) @@ -159,7 +161,7 @@ flash: $(NAME).bin $(OPENOCD) -c "init; reset halt; flash write_image erase $(NAME).bin 0x8000000; exit" upload: sign - trezorctl firmware_update -f $(NAME).bin + trezorctl firmware_update -f $(NAME).bin -s sign: $(NAME).bin $(PYTHON) ../bootloader/firmware_sign.py -f $(NAME).bin diff --git a/bootloader/bootloader.c b/bootloader/bootloader.c index b9f840cc94..6525ca283b 100644 --- a/bootloader/bootloader.c +++ b/bootloader/bootloader.c @@ -24,6 +24,7 @@ #include #include "bootloader.h" +#include "signatures.h" #include "buttons.h" #include "setup.h" #include "usb.h" @@ -32,9 +33,9 @@ #include "signatures.h" #include "layout.h" #include "rng.h" -#include "timer.h" +#include "memory.h" -void layoutFirmwareHash(const uint8_t *hash) +void layoutFirmwareFingerprint(const uint8_t *hash) { char str[4][17]; for (int i = 0; i < 4; i++) { @@ -43,40 +44,41 @@ void layoutFirmwareHash(const uint8_t *hash) layoutDialog(&bmp_icon_question, "Abort", "Continue", "Compare fingerprints", str[0], str[1], str[2], str[3], NULL, NULL); } -void show_halt(void) +bool get_button_response(void) { - layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Unofficial firmware", "aborted.", NULL, "Unplug your TREZOR", "contact our support.", NULL); + do { + delay(100000); + buttonUpdate(); + } while (!button.YesUp && !button.NoUp); + return button.YesUp; +} + +void show_halt(const char *line1, const char *line2) +{ + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, line1, line2, NULL, "Unplug your TREZOR,", "reinstall firmware.", NULL); shutdown(); } -void show_unofficial_warning(const uint8_t *hash) +static void show_unofficial_warning(const uint8_t *hash) { layoutDialog(&bmp_icon_warning, "Abort", "I'll take the risk", NULL, "WARNING!", NULL, "Unofficial firmware", "detected.", NULL, NULL); - do { - delay(100000); - buttonUpdate(); - } while (!button.YesUp && !button.NoUp); - - if (button.NoUp) { - show_halt(); // no button was pressed -> halt + bool but = get_button_response(); + if (!but) { // no button was pressed -> halt + show_halt("Unofficial firmware", "aborted."); } - layoutFirmwareHash(hash); + layoutFirmwareFingerprint(hash); - do { - delay(100000); - buttonUpdate(); - } while (!button.YesUp && !button.NoUp); - - if (button.NoUp) { - show_halt(); // no button was pressed -> halt + but = get_button_response(); + if (!but) { // no button was pressed -> halt + show_halt("Unofficial firmware", "aborted."); } // everything is OK, user pressed 2x Continue -> continue program } -void __attribute__((noreturn)) load_app(int signed_firmware) +static void __attribute__((noreturn)) load_app(int signed_firmware) { // zero out SRAM memset_reg(_ram_start, _ram_end, 0); @@ -84,27 +86,11 @@ void __attribute__((noreturn)) load_app(int signed_firmware) jump_to_firmware((const vector_table_t *) FLASH_PTR(FLASH_APP_START), signed_firmware); } -bool firmware_present(void) -{ -#ifndef APPVER - if (memcmp(FLASH_PTR(FLASH_META_MAGIC), "TRZR", 4)) { // magic does not match - return false; - } - if (*((const uint32_t *)FLASH_PTR(FLASH_META_CODELEN)) < 4096) { // firmware reports smaller size than 4kB - return false; - } - if (*((const uint32_t *)FLASH_PTR(FLASH_META_CODELEN)) > FLASH_TOTAL_SIZE - (FLASH_APP_START - FLASH_ORIGIN)) { // firmware reports bigger size than flash size - return false; - } -#endif - return true; -} - -void bootloader_loop(void) +static void bootloader_loop(void) { oledClear(); oledDrawBitmap(0, 0, &bmp_logo64); - if (firmware_present()) { + if (firmware_present_new()) { oledDrawStringCenter(90, 10, "TREZOR", FONT_STANDARD); oledDrawStringCenter(90, 30, "Bootloader", FONT_STANDARD); oledDrawStringCenter(90, 50, VERSTR(VERSION_MAJOR) "." VERSTR(VERSION_MINOR) "." VERSTR(VERSION_PATCH), FONT_STANDARD); @@ -115,7 +101,7 @@ void bootloader_loop(void) } oledRefresh(); - usbLoop(firmware_present()); + usbLoop(); } int main(void) @@ -129,24 +115,31 @@ int main(void) oledInit(); #endif -#ifndef APPVER - // at least one button is unpressed - uint16_t state = gpio_port_read(BTN_PORT); - int unpressed = ((state & BTN_PIN_YES) == BTN_PIN_YES || (state & BTN_PIN_NO) == BTN_PIN_NO); + mpu_config_bootloader(); - if (firmware_present() && unpressed) { +#ifndef APPVER + bool left_pressed = (buttonRead() & BTN_PIN_NO) == 0; + + if (firmware_present_new() && !left_pressed) { oledClear(); oledDrawBitmap(40, 0, &bmp_logo64_empty); oledRefresh(); - uint8_t hash[32]; - int signed_firmware = signatures_ok(hash); + const image_header *hdr = (const image_header *)FLASH_PTR(FLASH_FWHEADER_START); + + uint8_t fingerprint[32]; + int signed_firmware = signatures_new_ok(hdr, fingerprint); if (SIG_OK != signed_firmware) { - show_unofficial_warning(hash); - timer_init(); + show_unofficial_warning(fingerprint); } + if (SIG_OK != check_firmware_hashes(hdr)) { + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Broken firmware", "detected.", NULL, "Unplug your TREZOR,", "reinstall firmware.", NULL); + shutdown(); + } + + mpu_config_off(); load_app(signed_firmware); } #endif diff --git a/bootloader/bootloader.h b/bootloader/bootloader.h index c7f214f080..1217569b5a 100644 --- a/bootloader/bootloader.h +++ b/bootloader/bootloader.h @@ -21,20 +21,21 @@ #define __BOOTLOADER_H__ #define VERSION_MAJOR 1 -#define VERSION_MINOR 6 -#define VERSION_PATCH 1 +#define VERSION_MINOR 8 +#define VERSION_PATCH 0 #define STR(X) #X #define VERSTR(X) STR(X) #define VERSION_MAJOR_CHAR "\x01" -#define VERSION_MINOR_CHAR "\x06" -#define VERSION_PATCH_CHAR "\x01" +#define VERSION_MINOR_CHAR "\x08" +#define VERSION_PATCH_CHAR "\x00" +#include #include -#include "memory.h" -void layoutFirmwareHash(const uint8_t *hash); -bool firmware_present(void); +void show_halt(const char *line1, const char *line2); +void layoutFirmwareFingerprint(const uint8_t *hash); +bool get_button_response(void); #endif diff --git a/bootloader/firmware_align.py b/bootloader/firmware_align.py index 3d766862fc..3c1434987e 100755 --- a/bootloader/firmware_align.py +++ b/bootloader/firmware_align.py @@ -1,11 +1,14 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys import os +TOTALSIZE = 32768 +MAXSIZE = TOTALSIZE - 32 + fn = sys.argv[1] fs = os.stat(fn).st_size -if fs > 32768: - raise Exception('bootloader has to be smaller than 32768 bytes') +if fs > MAXSIZE: + raise Exception('bootloader has to be smaller than %d bytes (current size is %d)' % (MAXSIZE, fs)) with open(fn, 'ab') as f: - f.write(b'\x00' * (32768 - fs)) + f.write(b'\x00' * (TOTALSIZE - fs)) f.close() diff --git a/bootloader/firmware_sign.py b/bootloader/firmware_sign.py index 2b3a2c17ef..a7b8e13cf6 100755 --- a/bootloader/firmware_sign.py +++ b/bootloader/firmware_sign.py @@ -7,10 +7,6 @@ import struct import ecdsa -try: - raw_input -except: - raw_input = input SLOTS = 3 @@ -22,8 +18,9 @@ pubkeys = { 5: "047384c51ae81add0a523adbb186c91b906ffb64c2c765802bf26dbd13bdf12c319e80c2213a136c8ee03d7874fd22b70d68e7dee469decfbbb510ee9a460cda45", } -INDEXES_START = len("TRZR") + struct.calcsize(" size: + raise ValueError("Chunk too big already") + if len(data) == size: + return data + return data + b"\xFF" * (size - len(data)) - meta = b"TRZR" # magic - if data[:4] == b"TRZR": - meta += data[4 : 4 + struct.calcsize(" 0: + chunk = pad_to_size(sector, end - start) + hashes.append(hashlib.sha256(chunk).digest()) + else: + hashes.append(b"\x00" * 32) + start = end + end += 64 * 1024 + return hashes + + +def check_hashes(data): + expected_hashes = data[0x20 : 0x20 + 16 * 32] + hashes = b"" + for h in prepare_hashes(data[FWHEADER_SIZE:]): + hashes += h + + if expected_hashes == hashes: + print("HASHES OK") else: - meta += struct.pack(" SLOTS: raise Exception("Invalid slot") @@ -145,28 +171,28 @@ def sign(data, is_pem): print("(blank private key removes the signature on given index)") pem_key = "" while True: - key = raw_input() + key = input() pem_key += key + "\n" if key == "": break if pem_key.strip() == "": # Blank key,let's remove existing signature from slot - return modify(data, slot, 0, "\x00" * 64) + return modify(data, slot, 0, b"\x00" * 64) key = ecdsa.SigningKey.from_pem(pem_key) else: print("Paste SECEXP (in hex) and press Enter:") print("(blank private key removes the signature on given index)") - secexp = raw_input() + secexp = input() if secexp.strip() == "": # Blank key,let's remove existing signature from slot - return modify(data, slot, 0, "\x00" * 64) + return modify(data, slot, 0, b"\x00" * 64) key = ecdsa.SigningKey.from_secret_exponent( secexp=int(secexp, 16), curve=ecdsa.curves.SECP256k1, hashfunc=hashlib.sha256, ) - to_sign = prepare(data)[256:] # without meta + to_sign = get_header(data, zero_signatures=True) # Locate proper index of current signing key pubkey = "04" + key.get_verifying_key().to_string().hex() @@ -207,20 +233,21 @@ def main(args): data = open(args.path, "rb").read() assert len(data) % 4 == 0 - if data[:4] != b"TRZR": - print("Metadata has been added...") - data = prepare(data) - - if data[:4] != b"TRZR": + if data[:4] != b"TRZF": raise Exception("Firmware header expected") + data = update_hashes_in_header(data) + print("Firmware size %d bytes" % len(data)) + check_size(data) check_signatures(data) + check_hashes(data) if args.sign: data = sign(data, args.pem) check_signatures(data) + check_hashes(data) fp = open(args.path, "wb") fp.write(data) diff --git a/bootloader/firmware_sign_split.py b/bootloader/firmware_sign_split.py index 47b18e497a..25c4b341fd 100755 --- a/bootloader/firmware_sign_split.py +++ b/bootloader/firmware_sign_split.py @@ -1,5 +1,4 @@ -#!/usr/bin/env python -from __future__ import print_function +#!/usr/bin/env python3 import hashlib import os import subprocess @@ -7,10 +6,7 @@ import ecdsa from binascii import hexlify, unhexlify print('master secret:', end='') -try: - h = raw_input() -except: - h = input() +h = input() if h: h = unhexlify(h).encode('ascii') else: diff --git a/bootloader/signatures.c b/bootloader/signatures.c index e178ecec7a..ec6ca3af62 100644 --- a/bootloader/signatures.c +++ b/bootloader/signatures.c @@ -17,7 +17,6 @@ * along with this library. If not, see . */ -#include #include #include "signatures.h" @@ -25,6 +24,11 @@ #include "secp256k1.h" #include "sha2.h" #include "bootloader.h" +#include "memory.h" +#include "memzero.h" + +const uint32_t FIRMWARE_MAGIC_OLD = 0x525a5254; // TRZR +const uint32_t FIRMWARE_MAGIC_NEW = 0x465a5254; // TRZF #define PUBKEYS 5 @@ -38,19 +42,45 @@ static const uint8_t * const pubkey[PUBKEYS] = { #define SIGNATURES 3 -int signatures_ok(uint8_t *store_hash) +#define FLASH_META_START 0x08008000 +#define FLASH_META_CODELEN (FLASH_META_START + 0x0004) +#define FLASH_META_SIGINDEX1 (FLASH_META_START + 0x0008) +#define FLASH_META_SIGINDEX2 (FLASH_META_START + 0x0009) +#define FLASH_META_SIGINDEX3 (FLASH_META_START + 0x000A) +#define FLASH_OLD_APP_START 0x08010000 +#define FLASH_META_SIG1 (FLASH_META_START + 0x0040) +#define FLASH_META_SIG2 (FLASH_META_START + 0x0080) +#define FLASH_META_SIG3 (FLASH_META_START + 0x00C0) + +bool firmware_present_old(void) +{ + if (memcmp(FLASH_PTR(FLASH_META_START), &FIRMWARE_MAGIC_OLD, 4)) { // magic does not match + return false; + } + if (*((const uint32_t *)FLASH_PTR(FLASH_META_CODELEN)) < 8192) { // firmware reports smaller size than 8192 + return false; + } + if (*((const uint32_t *)FLASH_PTR(FLASH_META_CODELEN)) > FLASH_APP_LEN) { // firmware reports bigger size than flash size + return false; + } + + return true; +} + +int signatures_old_ok(void) { const uint32_t codelen = *((const uint32_t *)FLASH_META_CODELEN); const uint8_t sigindex1 = *((const uint8_t *)FLASH_META_SIGINDEX1); const uint8_t sigindex2 = *((const uint8_t *)FLASH_META_SIGINDEX2); const uint8_t sigindex3 = *((const uint8_t *)FLASH_META_SIGINDEX3); - uint8_t hash[32]; - sha256_Raw((const uint8_t *)FLASH_APP_START, codelen, hash); - if (store_hash) { - memcpy(store_hash, hash, 32); + if (codelen > FLASH_APP_LEN) { + return false; } + uint8_t hash[32]; + sha256_Raw(FLASH_PTR(FLASH_OLD_APP_START), codelen, hash); + if (sigindex1 < 1 || sigindex1 > PUBKEYS) return SIG_FAIL; // invalid index if (sigindex2 < 1 || sigindex2 > PUBKEYS) return SIG_FAIL; // invalid index if (sigindex3 < 1 || sigindex3 > PUBKEYS) return SIG_FAIL; // invalid index @@ -71,3 +101,93 @@ int signatures_ok(uint8_t *store_hash) return SIG_OK; } + +void compute_firmware_fingerprint(const image_header *hdr, uint8_t hash[32]) +{ + image_header copy; + memcpy(©, hdr, sizeof(image_header)); + memzero(copy.sig1, sizeof(copy.sig1)); + memzero(copy.sig2, sizeof(copy.sig2)); + memzero(copy.sig3, sizeof(copy.sig3)); + copy.sigindex1 = 0; + copy.sigindex2 = 0; + copy.sigindex3 = 0; + sha256_Raw((const uint8_t *)©, sizeof(image_header), hash); +} + +bool firmware_present_new(void) +{ + const image_header *hdr = (const image_header *)FLASH_PTR(FLASH_FWHEADER_START); + if (hdr->magic != FIRMWARE_MAGIC_NEW) return false; + // we need to ignore hdrlen for now + // because we keep reset_handler ptr there + // for compatibility with older bootloaders + // after this is no longer necessary, let's uncomment the line below: + // if (hdr->hdrlen != FLASH_FWHEADER_LEN) return false; + if (hdr->codelen > FLASH_APP_LEN) return false; + if (hdr->codelen < 4096) return false; + + return true; +} + +int signatures_new_ok(const image_header *hdr, uint8_t store_fingerprint[32]) +{ + uint8_t hash[32]; + compute_firmware_fingerprint(hdr, hash); + + if (store_fingerprint) { + memcpy(store_fingerprint, hash, 32); + } + + if (hdr->sigindex1 < 1 || hdr->sigindex1 > PUBKEYS) return SIG_FAIL; // invalid index + if (hdr->sigindex2 < 1 || hdr->sigindex2 > PUBKEYS) return SIG_FAIL; // invalid index + if (hdr->sigindex3 < 1 || hdr->sigindex3 > PUBKEYS) return SIG_FAIL; // invalid index + + if (hdr->sigindex1 == hdr->sigindex2) return SIG_FAIL; // duplicate use + if (hdr->sigindex1 == hdr->sigindex3) return SIG_FAIL; // duplicate use + if (hdr->sigindex2 == hdr->sigindex3) return SIG_FAIL; // duplicate use + + if (0 != ecdsa_verify_digest(&secp256k1, pubkey[hdr->sigindex1 - 1], hdr->sig1, hash)) { // failure + return SIG_FAIL; + } + if (0 != ecdsa_verify_digest(&secp256k1, pubkey[hdr->sigindex2 - 1], hdr->sig2, hash)) { // failure + return SIG_FAIL; + } + if (0 != ecdsa_verify_digest(&secp256k1, pubkey[hdr->sigindex3 - 1], hdr->sig3, hash)) { // failure + return SIG_FAIL; + } + + return SIG_OK; +} + +int mem_is_empty(const uint8_t *src, uint32_t len) +{ + for (uint32_t i = 0; i < len; i++) { + if (src[i]) return 0; + } + return 1; +} + +int check_firmware_hashes(const image_header *hdr) +{ + uint8_t hash[32]; + // check hash of the first code chunk + sha256_Raw(FLASH_PTR(FLASH_APP_START), (64 - 1) * 1024, hash); + if (0 != memcmp(hash, hdr->hashes, 32)) return SIG_FAIL; + // check remaining used chunks + uint32_t total_len = FLASH_FWHEADER_LEN + hdr->codelen; + int used_chunks = total_len / FW_CHUNK_SIZE; + if (total_len % FW_CHUNK_SIZE > 0) { + used_chunks++; + } + for (int i = 1; i < used_chunks; i++) { + sha256_Raw(FLASH_PTR(FLASH_FWHEADER_START + (64 * i) * 1024), 64 * 1024, hash); + if (0 != memcmp(hdr->hashes + 32 * i, hash, 32)) return SIG_FAIL; + } + // check unused chunks + for (int i = used_chunks; i < 16; i++) { + if (!mem_is_empty(hdr->hashes + 32 * i, 32)) return SIG_FAIL; + } + // all OK + return SIG_OK; +} diff --git a/bootloader/signatures.h b/bootloader/signatures.h index 01b5f308c1..6d3c00f969 100644 --- a/bootloader/signatures.h +++ b/bootloader/signatures.h @@ -20,9 +20,50 @@ #ifndef __SIGNATURES_H__ #define __SIGNATURES_H__ +#include +#include + +extern const uint32_t FIRMWARE_MAGIC_OLD; // TRZR +extern const uint32_t FIRMWARE_MAGIC_NEW; // TRZF + #define SIG_OK 0x5A3CA5C3 #define SIG_FAIL 0x00000000 -int signatures_ok(uint8_t *store_hash); +bool firmware_present_old(void); +int signatures_old_ok(void); + +// we use the same structure as T2 firmware header +// but we don't use the field sig +// and rather introduce fields sig1, sig2, sig3 +// immediately following the chunk hashes + +typedef struct { + uint32_t magic; + uint32_t hdrlen; + uint32_t expiry; + uint32_t codelen; + uint32_t version; + uint32_t fix_version; + uint8_t __reserved1[8]; + uint8_t hashes[512]; + uint8_t sig1[64]; + uint8_t sig2[64]; + uint8_t sig3[64]; + uint8_t sigindex1; + uint8_t sigindex2; + uint8_t sigindex3; + uint8_t __reserved2[220]; + uint8_t __sigmask; + uint8_t __sig[64]; +} __attribute__((packed)) image_header; + +#define FW_CHUNK_SIZE 65536 + +bool firmware_present_new(void); +void compute_firmware_fingerprint(const image_header *hdr, uint8_t hash[32]); +int signatures_new_ok(const image_header *hdr, uint8_t store_fingerprint[32]); +int check_firmware_hashes(const image_header *hdr); + +int mem_is_empty(const uint8_t *src, uint32_t len); #endif diff --git a/bootloader/usb.c b/bootloader/usb.c index 4532b6a1da..84885e8513 100644 --- a/bootloader/usb.c +++ b/bootloader/usb.c @@ -34,91 +34,15 @@ #include "ecdsa.h" #include "secp256k1.h" #include "memzero.h" +#include "memory.h" #include "usb21_standard.h" #include "webusb.h" #include "winusb.h" -#define FIRMWARE_MAGIC "TRZR" - -#define USB_INTERFACE_INDEX_MAIN 0 - -#define ENDPOINT_ADDRESS_IN (0x81) -#define ENDPOINT_ADDRESS_OUT (0x01) - -static bool brand_new_firmware; -static bool old_was_unsigned; - -static const struct usb_device_descriptor dev_descr = { - .bLength = USB_DT_DEVICE_SIZE, - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = 0x0210, - .bDeviceClass = 0, - .bDeviceSubClass = 0, - .bDeviceProtocol = 0, - .bMaxPacketSize0 = 64, - .idVendor = 0x1209, - .idProduct = 0x53c0, - .bcdDevice = 0x0100, - .iManufacturer = 1, - .iProduct = 2, - .iSerialNumber = 3, - .bNumConfigurations = 1, -}; - -static const struct usb_endpoint_descriptor endpoints[2] = {{ - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = ENDPOINT_ADDRESS_IN, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = 64, - .bInterval = 1, -}, { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = ENDPOINT_ADDRESS_OUT, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = 64, - .bInterval = 1, -}}; - -static const struct usb_interface_descriptor iface[] = {{ - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = USB_INTERFACE_INDEX_MAIN, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = 0, - .endpoint = endpoints, - .extra = NULL, - .extralen = 0, -}}; - -static const struct usb_interface ifaces[] = {{ - .num_altsetting = 1, - .altsetting = iface, -}}; - -static const struct usb_config_descriptor config = { - .bLength = USB_DT_CONFIGURATION_SIZE, - .bDescriptorType = USB_DT_CONFIGURATION, - .wTotalLength = 0, - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = 0, - .bmAttributes = 0x80, - .bMaxPower = 0x32, - .interface = ifaces, -}; - -static const char *usb_strings[] = { - "SatoshiLabs", - "TREZOR", - "000000000000000000000000", -}; +#include "usb_desc.h" +#include "usb_send.h" +#include "usb_erase.h" enum { STATE_READY, @@ -130,121 +54,75 @@ enum { }; static uint32_t flash_pos = 0, flash_len = 0; +static uint32_t chunk_idx = 0; static char flash_state = STATE_READY; -static uint8_t flash_anim = 0; -static uint16_t msg_id = 0xFFFF; -static uint32_t msg_size = 0; -static uint8_t meta_backup[FLASH_META_LEN]; +static uint32_t FW_HEADER[FLASH_FWHEADER_LEN/sizeof(uint32_t)]; +static uint32_t FW_CHUNK[FW_CHUNK_SIZE/sizeof(uint32_t)]; -static void send_msg_success(usbd_device *dev) +static void check_and_write_chunk(void) { - uint8_t response[64]; - memzero(response, sizeof(response)); - // response: Success message (id 2), payload len 0 - memcpy(response, - // header - "?##" - // msg_id - "\x00\x02" - // msg_size - "\x00\x00\x00\x00", - 9); - while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {} -} - -static void send_msg_failure(usbd_device *dev) -{ - uint8_t response[64]; - memzero(response, sizeof(response)); - // response: Failure message (id 3), payload len 2 - // - code = 99 (Failure_FirmwareError) - memcpy(response, - // header - "?##" - // msg_id - "\x00\x03" - // msg_size - "\x00\x00\x00\x02" - // data - "\x08" "\x63", - 11); - while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {} -} - -static void send_msg_features(usbd_device *dev) -{ - uint8_t response[64]; - memzero(response, sizeof(response)); - // response: Features message (id 17), payload len 25 - // - vendor = "trezor.io" - // - major_version = VERSION_MAJOR - // - minor_version = VERSION_MINOR - // - patch_version = VERSION_PATCH - // - bootloader_mode = True - // - firmware_present = True/False - // - model = "1" - memcpy(response, - // header - "?##" - // msg_id - "\x00\x11" - // msg_size - "\x00\x00\x00\x16" - // data - "\x0a" "\x09" "trezor.io" - "\x10" VERSION_MAJOR_CHAR - "\x18" VERSION_MINOR_CHAR - "\x20" VERSION_PATCH_CHAR - "\x28" "\x01" - "\x90\x01" "\x00" - "\xaa" "\x01" "1", - 34); - response[30] = brand_new_firmware ? 0x01 : 0x00; - while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {} -} - -static void send_msg_buttonrequest_firmwarecheck(usbd_device *dev) -{ - uint8_t response[64]; - memzero(response, sizeof(response)); - // response: ButtonRequest message (id 26), payload len 2 - // - code = ButtonRequest_FirmwareCheck (9) - memcpy(response, - // header - "?##" - // msg_id - "\x00\x1a" - // msg_size - "\x00\x00\x00\x02" - // data - "\x08" "\x09", - 11 - ); - while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {} -} - -static void backup_metadata(uint8_t *backup) -{ - memcpy(backup, FLASH_PTR(FLASH_META_START), FLASH_META_LEN); -} - -static void restore_metadata(const uint8_t *backup) -{ - flash_unlock(); - for (int i = 0; i < FLASH_META_LEN / 4; i++) { - const uint32_t *w = (const uint32_t *)(backup + i * 4); - flash_program_word(FLASH_META_START + i * 4, *w); + uint32_t offset = (chunk_idx == 0) ? FLASH_FWHEADER_LEN : 0; + uint32_t chunk_pos = flash_pos % FW_CHUNK_SIZE; + if (chunk_pos == 0) { + chunk_pos = FW_CHUNK_SIZE; } + uint8_t hash[32]; + SHA256_CTX ctx; + sha256_Init(&ctx); + sha256_Update(&ctx, (const uint8_t *)FW_CHUNK + offset, chunk_pos - offset); + if (chunk_pos < 64 * 1024) { + // pad with FF + for (uint32_t i = chunk_pos; i < 64 * 1024; i += 4) { + sha256_Update(&ctx, (const uint8_t *)"\xFF\xFF\xFF\xFF", 4); + } + } + sha256_Final(&ctx, hash); + + const image_header *hdr = (const image_header *)FW_HEADER; + // invalid chunk sent + if (0 != memcmp(hash, hdr->hashes + chunk_idx * 32, 32)) { + // erase storage + erase_storage(); + flash_state = STATE_END; + show_halt("Error installing", "firmware."); + return; + } + + flash_wait_for_last_operation(); + flash_clear_status_flags(); + flash_unlock(); + for (uint32_t i = offset/sizeof(uint32_t); i < chunk_pos/sizeof(uint32_t); i++) { + flash_program_word(FLASH_FWHEADER_START + chunk_idx * FW_CHUNK_SIZE + i * sizeof(uint32_t), FW_CHUNK[i]); + } + flash_wait_for_last_operation(); flash_lock(); + + // all done + if (flash_len == flash_pos) { + // check remaining chunks if any + for (uint32_t i = chunk_idx + 1; i < 16; i++) { + // hash should be empty if the chunk is unused + if (!mem_is_empty(hdr->hashes + 32 * i, 32)) { + flash_state = STATE_END; + show_halt("Error installing", "firmware."); + return; + } + } + } + + memzero(FW_CHUNK, sizeof(FW_CHUNK)); + chunk_idx++; } static void rx_callback(usbd_device *dev, uint8_t ep) { (void)ep; + static uint16_t msg_id = 0xFFFF; static uint8_t buf[64] __attribute__((aligned(4))); - static uint8_t towrite[4] __attribute__((aligned(4))); + static uint32_t w; static int wi; + static int old_was_signed; if ( usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_OUT, buf, 64) != 64) return; @@ -258,7 +136,6 @@ static void rx_callback(usbd_device *dev, uint8_t ep) } // struct.unpack(">HL") => msg, size msg_id = (buf[3] << 8) + buf[4]; - msg_size = ((uint32_t) buf[5] << 24) + (buf[6] << 16) + (buf[7] << 8) + buf[8]; } if (flash_state == STATE_READY || flash_state == STATE_OPEN) { @@ -277,26 +154,9 @@ static void rx_callback(usbd_device *dev, uint8_t ep) } if (msg_id == 0x0005) { // WipeDevice message (id 5) layoutDialog(&bmp_icon_question, "Cancel", "Confirm", NULL, "Do you really want to", "wipe the device?", NULL, "All data will be lost.", NULL, NULL); - do { - delay(100000); - buttonUpdate(); - } while (!button.YesUp && !button.NoUp); - if (button.YesUp) { - flash_wait_for_last_operation(); - flash_clear_status_flags(); - flash_unlock(); - // erase metadata area - for (int i = FLASH_META_SECTOR_FIRST; i <= FLASH_META_SECTOR_LAST; i++) { - layoutProgress("PREPARING ... Please wait", 1000 * (i - FLASH_META_SECTOR_FIRST) / (FLASH_CODE_SECTOR_LAST - FLASH_META_SECTOR_FIRST)); - flash_erase_sector(i, FLASH_CR_PROGRAM_X32); - } - // erase code area - for (int i = FLASH_CODE_SECTOR_FIRST; i <= FLASH_CODE_SECTOR_LAST; i++) { - layoutProgress("PREPARING ... Please wait", 1000 * (i - FLASH_META_SECTOR_FIRST) / (FLASH_CODE_SECTOR_LAST - FLASH_META_SECTOR_FIRST)); - flash_erase_sector(i, FLASH_CR_PROGRAM_X32); - } - flash_wait_for_last_operation(); - flash_lock(); + bool but = get_button_response(); + if (but) { + erase_storage_code_progress(); flash_state = STATE_END; layoutDialog(&bmp_icon_ok, NULL, NULL, NULL, "Device", "successfully wiped.", NULL, "You may now", "unplug your TREZOR.", NULL); send_msg_success(dev); @@ -311,59 +171,31 @@ static void rx_callback(usbd_device *dev, uint8_t ep) if (flash_state == STATE_OPEN) { if (msg_id == 0x0006) { // FirmwareErase message (id 6) - if (!brand_new_firmware) { + bool proceed = false; + if (firmware_present_new()) { layoutDialog(&bmp_icon_question, "Abort", "Continue", NULL, "Install new", "firmware?", NULL, "Never do this without", "your recovery card!", NULL); - do { - delay(100000); - buttonUpdate(); - } while (!button.YesUp && !button.NoUp); + proceed = get_button_response(); + } else { + proceed = true; } - if (brand_new_firmware || button.YesUp) { - // check whether current firmware is signed - if (!brand_new_firmware && SIG_OK == signatures_ok(NULL)) { - old_was_unsigned = false; - // backup metadata - backup_metadata(meta_backup); + if (proceed) { + // check whether the current firmware is signed (old or new method) + if (firmware_present_new()) { + const image_header *hdr = (const image_header *)FLASH_PTR(FLASH_FWHEADER_START); + old_was_signed = signatures_new_ok(hdr, NULL) & check_firmware_hashes(hdr); + } else if (firmware_present_old()) { + old_was_signed = signatures_old_ok(); } else { - old_was_unsigned = true; + old_was_signed = SIG_FAIL; } - flash_wait_for_last_operation(); - flash_clear_status_flags(); - flash_unlock(); - // erase metadata area - for (int i = FLASH_META_SECTOR_FIRST; i <= FLASH_META_SECTOR_LAST; i++) { - layoutProgress("PREPARING ... Please wait", 1000 * (i - FLASH_META_SECTOR_FIRST) / (FLASH_CODE_SECTOR_LAST - FLASH_META_SECTOR_FIRST)); - flash_erase_sector(i, FLASH_CR_PROGRAM_X32); - } - // erase code area - for (int i = FLASH_CODE_SECTOR_FIRST; i <= FLASH_CODE_SECTOR_LAST; i++) { - layoutProgress("PREPARING ... Please wait", 1000 * (i - FLASH_META_SECTOR_FIRST) / (FLASH_CODE_SECTOR_LAST - FLASH_META_SECTOR_FIRST)); - flash_erase_sector(i, FLASH_CR_PROGRAM_X32); - } - layoutProgress("INSTALLING ... Please wait", 0); - flash_wait_for_last_operation(); - flash_lock(); - - // check that metadata was succesfully erased - // flash status register should show now error and - // the config block should contain only \xff. - uint8_t hash[32]; - sha256_Raw(FLASH_PTR(FLASH_META_START), FLASH_META_LEN, hash); - if ((FLASH_SR & (FLASH_SR_PGAERR | FLASH_SR_PGPERR | FLASH_SR_PGSERR | FLASH_SR_WRPERR)) != 0 - || memcmp(hash, "\x2d\x86\x4c\x0b\x78\x9a\x43\x21\x4e\xee\x85\x24\xd3\x18\x20\x75\x12\x5e\x5c\xa2\xcd\x52\x7f\x35\x82\xec\x87\xff\xd9\x40\x76\xbc", 32) != 0) { - send_msg_failure(dev); - flash_state = STATE_END; - layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Error installing ", "firmware.", NULL, "Unplug your TREZOR", "and try again.", NULL); - return; - } - + erase_code_progress(); send_msg_success(dev); flash_state = STATE_FLASHSTART; - return; + } else { + send_msg_failure(dev); + flash_state = STATE_END; + layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, "Firmware installation", "aborted.", NULL, "You may now", "unplug your TREZOR.", NULL); } - send_msg_failure(dev); - flash_state = STATE_END; - layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, "Firmware installation", "aborted.", NULL, "You may now", "unplug your TREZOR.", NULL); return; } return; @@ -374,42 +206,47 @@ static void rx_callback(usbd_device *dev, uint8_t ep) if (buf[9] != 0x0a) { // invalid contents send_msg_failure(dev); flash_state = STATE_END; - layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Error installing ", "firmware.", NULL, "Unplug your TREZOR", "and try again.", NULL); + show_halt("Error installing", "firmware."); return; } // read payload length const uint8_t *p = buf + 10; flash_len = readprotobufint(&p); - if (flash_len > FLASH_TOTAL_SIZE + FLASH_META_DESC_LEN - (FLASH_APP_START - FLASH_ORIGIN)) { // firmware is too big + if (flash_len <= FLASH_FWHEADER_LEN) { // firmware is too small send_msg_failure(dev); flash_state = STATE_END; - layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Firmware is too big.", NULL, "Get official firmware", "from trezor.io/start", NULL, NULL); + show_halt("Firmware is too small.", NULL); + return; + } + if (flash_len > FLASH_FWHEADER_LEN + FLASH_APP_LEN) { // firmware is too big + send_msg_failure(dev); + flash_state = STATE_END; + show_halt("Firmware is too big.", NULL); return; } // check firmware magic - if (memcmp(p, FIRMWARE_MAGIC, 4) != 0) { + if (memcmp(p, &FIRMWARE_MAGIC_NEW, 4) != 0) { send_msg_failure(dev); flash_state = STATE_END; - layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Wrong firmware header.", NULL, "Get official firmware", "from trezor.io/start", NULL, NULL); + show_halt("Wrong firmware header.", NULL); return; } + memzero(FW_HEADER, sizeof(FW_HEADER)); + memzero(FW_CHUNK, sizeof(FW_CHUNK)); flash_state = STATE_FLASHING; - p += 4; // Don't flash firmware header yet. - flash_pos = 4; - wi = 0; - flash_unlock(); + flash_pos = 0; + chunk_idx = 0; + w = 0; while (p < buf + 64) { - towrite[wi] = *p; + w = (w >> 8) | (*p << 24); // assign byte to first byte of uint32_t w wi++; if (wi == 4) { - const uint32_t *w = (uint32_t *)towrite; - flash_program_word(FLASH_META_START + flash_pos, *w); + FW_HEADER[flash_pos / 4] = w; flash_pos += 4; wi = 0; } p++; } - flash_lock(); return; } return; @@ -419,35 +256,44 @@ static void rx_callback(usbd_device *dev, uint8_t ep) if (buf[0] != '?') { // invalid contents send_msg_failure(dev); flash_state = STATE_END; - layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Error installing ", "firmware.", NULL, "Unplug your TREZOR", "and try again.", NULL); + show_halt("Error installing", "firmware."); return; } - const uint8_t *p = buf + 1; + + static uint8_t flash_anim = 0; if (flash_anim % 32 == 4) { layoutProgress("INSTALLING ... Please wait", 1000 * flash_pos / flash_len); } flash_anim++; - flash_unlock(); + + const uint8_t *p = buf + 1; while (p < buf + 64 && flash_pos < flash_len) { - towrite[wi] = *p; + w = (w >> 8) | (*p << 24); // assign byte to first byte of uint32_t w wi++; if (wi == 4) { - const uint32_t *w = (const uint32_t *)towrite; - if (flash_pos < FLASH_META_DESC_LEN) { - flash_program_word(FLASH_META_START + flash_pos, *w); // the first 256 bytes of firmware is metadata descriptor + if (flash_pos < FLASH_FWHEADER_LEN) { + FW_HEADER[flash_pos / 4] = w; } else { - flash_program_word(FLASH_APP_START + (flash_pos - FLASH_META_DESC_LEN), *w); // the rest is code + FW_CHUNK[(flash_pos % FW_CHUNK_SIZE)/ 4] = w; } flash_pos += 4; wi = 0; + // finished the whole chunk + if (flash_pos % FW_CHUNK_SIZE == 0) { + check_and_write_chunk(); + } } p++; } - flash_lock(); // flashing done if (flash_pos == flash_len) { + // flush remaining data in the last chunk + if (flash_pos % FW_CHUNK_SIZE > 0) { + check_and_write_chunk(); + } flash_state = STATE_CHECK; - if (!brand_new_firmware) { + const image_header *hdr = (const image_header *)FW_HEADER; + if (SIG_OK != signatures_new_ok(hdr, NULL)) { send_msg_buttonrequest_firmwarecheck(dev); return; } @@ -458,55 +304,68 @@ static void rx_callback(usbd_device *dev, uint8_t ep) if (flash_state == STATE_CHECK) { - if (!brand_new_firmware) { + // use the firmware header from RAM + const image_header *hdr = (const image_header *)FW_HEADER; + + bool hash_check_ok; + // show fingerprint of unsigned firmware + if (SIG_OK != signatures_new_ok(hdr, NULL)) { if (msg_id != 0x001B) { // ButtonAck message (id 27) return; } uint8_t hash[32]; - sha256_Raw(FLASH_PTR(FLASH_APP_START), flash_len - FLASH_META_DESC_LEN, hash); - layoutFirmwareHash(hash); - do { - delay(100000); - buttonUpdate(); - } while (!button.YesUp && !button.NoUp); + compute_firmware_fingerprint(hdr, hash); + layoutFirmwareFingerprint(hash); + hash_check_ok = get_button_response(); + } else { + hash_check_ok = true; } - bool hash_check_ok = brand_new_firmware || button.YesUp; - layoutProgress("INSTALLING ... Please wait", 1000); - uint8_t flags = *FLASH_PTR(FLASH_META_FLAGS); // wipe storage if: - // 0) there was no firmware - // 1) old firmware was unsigned - // 2) firmware restore flag isn't set - // 3) signatures are not ok - if (brand_new_firmware || old_was_unsigned || (flags & 0x01) == 0 || SIG_OK != signatures_ok(NULL)) { - memzero(meta_backup, sizeof(meta_backup)); + // 1) old firmware was unsigned or not present + // 2) signatures are not OK + // 3) hashes are not OK + if (SIG_OK != old_was_signed || SIG_OK != signatures_new_ok(hdr, NULL) || SIG_OK != check_firmware_hashes(hdr)) { + // erase storage + erase_storage(); + // check erasure + uint8_t hash[32]; + sha256_Raw(FLASH_PTR(FLASH_STORAGE_START), FLASH_STORAGE_LEN, hash); + if (memcmp(hash, "\x2d\x86\x4c\x0b\x78\x9a\x43\x21\x4e\xee\x85\x24\xd3\x18\x20\x75\x12\x5e\x5c\xa2\xcd\x52\x7f\x35\x82\xec\x87\xff\xd9\x40\x76\xbc", 32) != 0) { + send_msg_failure(dev); + show_halt("Error installing", "firmware."); + return; + } } - // copy new firmware header - memcpy(meta_backup, (void *)FLASH_META_START, FLASH_META_DESC_LEN); - // write "TRZR" in header only when hash was confirmed + flash_wait_for_last_operation(); + flash_clear_status_flags(); + flash_unlock(); + // write firmware header only when hash was confirmed if (hash_check_ok) { - memcpy(meta_backup, FIRMWARE_MAGIC, 4); + for (size_t i = 0; i < FLASH_FWHEADER_LEN/sizeof(uint32_t); i++) { + flash_program_word(FLASH_FWHEADER_START + i * sizeof(uint32_t), FW_HEADER[i]); + } } else { - memzero(meta_backup, 4); + for (size_t i = 0; i < FLASH_FWHEADER_LEN/sizeof(uint32_t); i++) { + flash_program_word(FLASH_FWHEADER_START + i * sizeof(uint32_t), 0); + } } - - // no need to erase, because we are not changing any already flashed byte. - restore_metadata(meta_backup); - memzero(meta_backup, sizeof(meta_backup)); + flash_wait_for_last_operation(); + flash_lock(); flash_state = STATE_END; if (hash_check_ok) { layoutDialog(&bmp_icon_ok, NULL, NULL, NULL, "New firmware", "successfully installed.", NULL, "You may now", "unplug your TREZOR.", NULL); send_msg_success(dev); + shutdown(); } else { layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, "Firmware installation", "aborted.", NULL, "You need to repeat", "the procedure with", "the correct firmware."); send_msg_failure(dev); + shutdown(); } return; } - } static void set_config(usbd_device *dev, uint16_t wValue) @@ -531,7 +390,7 @@ static const struct usb_bos_descriptor bos_descriptor = { .capabilities = capabilities }; -void usbInit(void) +static void usbInit(void) { usbd_dev = usbd_init(&otgfs_usb_driver, &dev_descr, &config, usb_strings, sizeof(usb_strings)/sizeof(const char *), usbd_control_buffer, sizeof(usbd_control_buffer)); usbd_register_set_config_callback(usbd_dev, set_config); @@ -540,7 +399,7 @@ void usbInit(void) winusb_setup(usbd_dev, USB_INTERFACE_INDEX_MAIN); } -void checkButtons(void) +static void checkButtons(void) { static bool btn_left = false, btn_right = false, btn_final = false; if (btn_final) { @@ -569,13 +428,13 @@ void checkButtons(void) } } -void usbLoop(bool firmware_present) +void usbLoop(void) { - brand_new_firmware = !firmware_present; + bool firmware_present = firmware_present_new(); usbInit(); for (;;) { usbd_poll(usbd_dev); - if (brand_new_firmware && (flash_state == STATE_READY || flash_state == STATE_OPEN)) { + if (!firmware_present && (flash_state == STATE_READY || flash_state == STATE_OPEN)) { checkButtons(); } } diff --git a/bootloader/usb.h b/bootloader/usb.h index fea6e680e7..da506471ac 100644 --- a/bootloader/usb.h +++ b/bootloader/usb.h @@ -20,6 +20,6 @@ #ifndef __USB_H__ #define __USB_H__ -void usbLoop(bool firmware_present); +void usbLoop(void); #endif diff --git a/bootloader/usb_desc.h b/bootloader/usb_desc.h new file mode 100644 index 0000000000..d4044b9300 --- /dev/null +++ b/bootloader/usb_desc.h @@ -0,0 +1,75 @@ +#define USB_INTERFACE_INDEX_MAIN 0 + +#define ENDPOINT_ADDRESS_IN (0x81) +#define ENDPOINT_ADDRESS_OUT (0x01) + +static const struct usb_device_descriptor dev_descr = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0210, + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = 64, + .idVendor = 0x1209, + .idProduct = 0x53c0, + .bcdDevice = 0x0100, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .bNumConfigurations = 1, +}; + +static const struct usb_endpoint_descriptor endpoints[2] = {{ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = ENDPOINT_ADDRESS_IN, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 64, + .bInterval = 1, +}, { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = ENDPOINT_ADDRESS_OUT, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 64, + .bInterval = 1, +}}; + +static const struct usb_interface_descriptor iface[] = {{ + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = USB_INTERFACE_INDEX_MAIN, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, + .endpoint = endpoints, + .extra = NULL, + .extralen = 0, +}}; + +static const struct usb_interface ifaces[] = {{ + .num_altsetting = 1, + .altsetting = iface, +}}; + +static const struct usb_config_descriptor config = { + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = 0, + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0x80, + .bMaxPower = 0x32, + .interface = ifaces, +}; + +static const char *usb_strings[] = { + "SatoshiLabs", + "TREZOR", + "000000000000000000000000", +}; diff --git a/bootloader/usb_erase.h b/bootloader/usb_erase.h new file mode 100644 index 0000000000..cadcf45730 --- /dev/null +++ b/bootloader/usb_erase.h @@ -0,0 +1,44 @@ +static void erase_storage_code_progress(void) +{ + flash_wait_for_last_operation(); + flash_clear_status_flags(); + flash_unlock(); + // erase storage area + for (int i = FLASH_STORAGE_SECTOR_FIRST; i <= FLASH_STORAGE_SECTOR_LAST; i++) { + layoutProgress("WIPING ... Please wait", 1000 * (i - FLASH_STORAGE_SECTOR_FIRST) / (FLASH_CODE_SECTOR_LAST - FLASH_STORAGE_SECTOR_FIRST)); + flash_erase_sector(i, FLASH_CR_PROGRAM_X32); + } + // erase code area + for (int i = FLASH_CODE_SECTOR_FIRST; i <= FLASH_CODE_SECTOR_LAST; i++) { + layoutProgress("WIPING ... Please wait", 1000 * (i - FLASH_STORAGE_SECTOR_FIRST) / (FLASH_CODE_SECTOR_LAST - FLASH_STORAGE_SECTOR_FIRST)); + flash_erase_sector(i, FLASH_CR_PROGRAM_X32); + } + flash_wait_for_last_operation(); + flash_lock(); +} + +static void erase_code_progress(void) +{ + flash_wait_for_last_operation(); + flash_clear_status_flags(); + flash_unlock(); + for (int i = FLASH_CODE_SECTOR_FIRST; i <= FLASH_CODE_SECTOR_LAST; i++) { + layoutProgress("PREPARING ... Please wait", 1000 * (i - FLASH_CODE_SECTOR_FIRST) / (FLASH_CODE_SECTOR_LAST - FLASH_CODE_SECTOR_FIRST)); + flash_erase_sector(i, FLASH_CR_PROGRAM_X32); + } + layoutProgress("INSTALLING ... Please wait", 0); + flash_wait_for_last_operation(); + flash_lock(); +} + +static void erase_storage(void) +{ + flash_wait_for_last_operation(); + flash_clear_status_flags(); + flash_unlock(); + for (int i = FLASH_STORAGE_SECTOR_FIRST; i <= FLASH_STORAGE_SECTOR_LAST; i++) { + flash_erase_sector(i, FLASH_CR_PROGRAM_X32); + } + flash_wait_for_last_operation(); + flash_lock(); +} diff --git a/bootloader/usb_send.h b/bootloader/usb_send.h new file mode 100644 index 0000000000..ed73fbe08b --- /dev/null +++ b/bootloader/usb_send.h @@ -0,0 +1,86 @@ +static void send_msg_success(usbd_device *dev) +{ + uint8_t response[64]; + memzero(response, sizeof(response)); + // response: Success message (id 2), payload len 0 + memcpy(response, + // header + "?##" + // msg_id + "\x00\x02" + // msg_size + "\x00\x00\x00\x00", + 9); + while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {} +} + +static void send_msg_failure(usbd_device *dev) +{ + uint8_t response[64]; + memzero(response, sizeof(response)); + // response: Failure message (id 3), payload len 2 + // - code = 99 (Failure_FirmwareError) + memcpy(response, + // header + "?##" + // msg_id + "\x00\x03" + // msg_size + "\x00\x00\x00\x02" + // data + "\x08" "\x63", + 11); + while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {} +} + +static void send_msg_features(usbd_device *dev) +{ + uint8_t response[64]; + memzero(response, sizeof(response)); + // response: Features message (id 17), payload len 25 + // - vendor = "trezor.io" + // - major_version = VERSION_MAJOR + // - minor_version = VERSION_MINOR + // - patch_version = VERSION_PATCH + // - bootloader_mode = True + // - firmware_present = True/False + // - model = "1" + memcpy(response, + // header + "?##" + // msg_id + "\x00\x11" + // msg_size + "\x00\x00\x00\x16" + // data + "\x0a" "\x09" "trezor.io" + "\x10" VERSION_MAJOR_CHAR + "\x18" VERSION_MINOR_CHAR + "\x20" VERSION_PATCH_CHAR + "\x28" "\x01" + "\x90\x01" "\x00" + "\xaa" "\x01" "1", + 34); + response[30] = firmware_present_new() ? 0x01 : 0x00; + while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {} +} + +static void send_msg_buttonrequest_firmwarecheck(usbd_device *dev) +{ + uint8_t response[64]; + memzero(response, sizeof(response)); + // response: ButtonRequest message (id 26), payload len 2 + // - code = ButtonRequest_FirmwareCheck (9) + memcpy(response, + // header + "?##" + // msg_id + "\x00\x1a" + // msg_size + "\x00\x00\x00\x02" + // data + "\x08" "\x09", + 11 + ); + while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {} +} diff --git a/buttons.c b/buttons.c index a6e64b5ad6..f416736c38 100644 --- a/buttons.c +++ b/buttons.c @@ -29,10 +29,9 @@ uint16_t buttonRead(void) { void buttonUpdate() { - uint16_t state; static uint16_t last_state = BTN_PIN_YES | BTN_PIN_NO; - state = buttonRead(); + uint16_t state = buttonRead(); if ((state & BTN_PIN_YES) == 0) { // Yes button is down if ((last_state & BTN_PIN_YES) == 0) { // last Yes was down diff --git a/emulator/memory.c b/emulator/memory.c index 02b2a4ec71..4bcda38863 100644 --- a/emulator/memory.c +++ b/emulator/memory.c @@ -128,8 +128,7 @@ void svc_flash_program(uint32_t size) { } void svc_flash_erase_sector(uint16_t sector) { assert (!flash_locked); - assert (sector >= FLASH_META_SECTOR_FIRST && - sector <= FLASH_META_SECTOR_LAST); + assert (sector >= FLASH_STORAGE_SECTOR_FIRST && sector <= FLASH_STORAGE_SECTOR_LAST); flash_erase_sector(sector, 3); } uint32_t svc_flash_lock(void) { diff --git a/firmware/ChangeLog b/firmware/ChangeLog index 8f31e2f6d3..be72340c89 100644 --- a/firmware/ChangeLog +++ b/firmware/ChangeLog @@ -1,5 +1,10 @@ Version 1.8.0 * Stable release, optional update +* Security improvements +* Upgraded to new storage format +* Stellar and NEM fixes +* New coins: ATS, KMD, XPM, XSN, ZCL +* New ETH tokens Version 1.7.3 * Stable release, optional update diff --git a/firmware/Makefile b/firmware/Makefile index 91e84acadc..a21f0cd26b 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -1,4 +1,4 @@ -APPVER = 1.0.0 +APPVER = 1.8.0 NAME = trezor @@ -8,6 +8,7 @@ else OBJS += usb.o OBJS += bl_check.o OBJS += otp.o +OBJS += header.o endif OBJS += u2f.o diff --git a/firmware/bl_check.c b/firmware/bl_check.c index 5ca0a41c34..45238a5def 100644 --- a/firmware/bl_check.c +++ b/firmware/bl_check.c @@ -26,7 +26,7 @@ #include "gettext.h" #include "util.h" -int known_bootloader(int r, const uint8_t *hash) { +static int known_bootloader(int r, const uint8_t *hash) { if (r != 32) return 0; if (0 == memcmp(hash, "\xbf\x72\xe2\x5e\x2c\x2f\xc1\xba\x57\x04\x50\xfa\xdf\xb6\x6f\xaa\x5a\x71\x6d\xcd\xc0\x33\x35\x88\x55\x7b\x77\x54\x0a\xb8\x7e\x98", 32)) return 1; // 1.2.0a if (0 == memcmp(hash, "\x77\xb8\xe2\xf2\x5f\xaa\x8e\x8c\x7d\x9f\x5b\x32\x3b\x27\xce\x05\x6c\xa3\xdb\xc2\x3f\x56\xc3\x7e\xe3\x3f\x97\x7c\xa6\xeb\x4d\x3e", 32)) return 1; // 1.2.0b @@ -44,6 +44,7 @@ int known_bootloader(int r, const uint8_t *hash) { if (0 == memcmp(hash, "\x3e\xc4\xbd\xd5\x77\xea\x0c\x36\xc7\xba\xb7\xb9\xa3\x5b\x87\x17\xb3\xf1\xfc\x2f\x80\x9e\x69\x0c\x8a\xbe\x5b\x05\xfb\xc2\x43\xc6", 32)) return 1; // 1.6.0 shipped with fw 1.7.0 if (0 == memcmp(hash, "\x8e\x83\x02\x3f\x0d\x4f\x82\x4f\x64\x71\x20\x75\x2b\x6c\x71\x6f\x55\xd7\x95\x70\x66\x8f\xd4\x90\x65\xd5\xb7\x97\x6e\x7a\x6e\x19", 32)) return 1; // 1.6.0 shipped with fw 1.7.1 and 1.7.2 if (0 == memcmp(hash, "\xa2\x36\x6e\x77\xde\x8e\xfd\xfd\xc9\x99\xf4\x72\x20\xc0\x16\xe3\x3f\x6d\x24\x24\xe2\x45\x90\x79\x11\x7a\x90\xb3\xa8\x88\xba\xdd", 32)) return 1; // 1.6.1 shipped with fw 1.7.3 + if (0 == memcmp(hash, "\xf7\xfa\x16\x5b\xe6\xd7\x80\xf3\xe1\xaf\x00\xab\xc0\x7d\xf8\xb3\x07\x6b\xcd\xad\x72\xd7\x0d\xa2\x2a\x63\xd8\x89\x6b\x63\x91\xd8", 32)) return 1; // 1.8.0 shipped with fw 1.8.0 return 0; } diff --git a/firmware/config.c b/firmware/config.c index b2042acbfa..73bed8fac9 100644 --- a/firmware/config.c +++ b/firmware/config.c @@ -197,12 +197,15 @@ static secbool config_get_uint32(uint16_t key, uint32_t *value) return sectrue; } +#define FLASH_META_START 0x08008000 +#define FLASH_META_LEN 0x100 + static secbool config_upgrade_v10(void) { #define OLD_STORAGE_SIZE(last_member) (((offsetof(Storage, last_member) + pb_membersize(Storage, last_member)) + 3) & ~3) - if (memcmp(FLASH_PTR(FLASH_META_MAGIC), &META_MAGIC_V10, sizeof(META_MAGIC_V10)) != 0 || - memcmp(FLASH_PTR(FLASH_STORAGE_START), &CONFIG_MAGIC_V10, sizeof(CONFIG_MAGIC_V10)) != 0) { + if (memcmp(FLASH_PTR(FLASH_META_START), &META_MAGIC_V10, sizeof(META_MAGIC_V10)) != 0 || + memcmp(FLASH_PTR(FLASH_META_START + FLASH_META_LEN), &CONFIG_MAGIC_V10, sizeof(CONFIG_MAGIC_V10)) != 0) { // wrong magic return secfalse; } @@ -210,8 +213,8 @@ static secbool config_upgrade_v10(void) Storage config __attribute__((aligned(4))); _Static_assert((sizeof(config) & 3) == 0, "storage unaligned"); - memcpy(config_uuid, FLASH_PTR(FLASH_STORAGE_START + sizeof(CONFIG_MAGIC_V10)), sizeof(config_uuid)); - memcpy(&config, FLASH_PTR(FLASH_STORAGE_START + sizeof(CONFIG_MAGIC_V10) + sizeof(config_uuid)), sizeof(config)); + memcpy(config_uuid, FLASH_PTR(FLASH_META_START + FLASH_META_LEN + sizeof(CONFIG_MAGIC_V10)), sizeof(config_uuid)); + memcpy(&config, FLASH_PTR(FLASH_META_START + FLASH_META_LEN + sizeof(CONFIG_MAGIC_V10) + sizeof(config_uuid)), sizeof(config)); // version 1: since 1.0.0 // version 2: since 1.2.1 @@ -252,7 +255,7 @@ static secbool config_upgrade_v10(void) // Erase newly added fields. if (old_config_size != sizeof(Storage)) { - memzero(&config + old_config_size, sizeof(Storage) - old_config_size); + memzero((char*)&config + old_config_size, sizeof(Storage) - old_config_size); } const uint32_t FLASH_STORAGE_PINAREA = FLASH_META_START + 0x4000; @@ -294,6 +297,7 @@ static secbool config_upgrade_v10(void) if (config.has_pin) { storage_change_pin(PIN_EMPTY, pin_to_int(config.pin)); } + while (pin_wait != 0) { storage_pin_fails_increase(); pin_wait >>= 1; @@ -351,17 +355,28 @@ static secbool config_upgrade_v10(void) void config_init(void) { + char oldTiny = usbTiny(1); + config_upgrade_v10(); storage_init(&protectPinUiCallback, HW_ENTROPY_DATA, HW_ENTROPY_LEN); memzero(HW_ENTROPY_DATA, sizeof(HW_ENTROPY_DATA)); - uint16_t len = 0; - if (sectrue == storage_get(KEY_UUID, config_uuid, sizeof(config_uuid), &len) && len == sizeof(config_uuid)) { - data2hex(config_uuid, sizeof(config_uuid), config_uuid_str); - } else { - config_wipe(); + // Auto-unlock storage if no PIN is set. + if (storage_is_unlocked() == secfalse && storage_has_pin() == secfalse) { + storage_unlock(PIN_EMPTY); } + + uint16_t len = 0; + // If UUID is not set, then the config is uninitialized. + if (sectrue != storage_get(KEY_UUID, config_uuid, sizeof(config_uuid), &len) || len != sizeof(config_uuid)) { + random_buffer((uint8_t *)config_uuid, sizeof(config_uuid)); + storage_set(KEY_UUID, config_uuid, sizeof(config_uuid)); + storage_set(KEY_VERSION, &CONFIG_VERSION, sizeof(CONFIG_VERSION)); + } + data2hex(config_uuid, sizeof(config_uuid), config_uuid_str); + + usbTiny(oldTiny); } void session_clear(bool lock) @@ -709,9 +724,12 @@ bool config_containsMnemonic(const char *mnemonic) /* Check whether pin matches storage. The pin must be * a null-terminated string with at most 9 characters. */ -bool config_containsPin(const char *pin) +bool config_unlock(const char *pin) { - return sectrue == storage_unlock(pin_to_int(pin)); + char oldTiny = usbTiny(1); + secbool ret = storage_unlock(pin_to_int(pin)); + usbTiny(oldTiny); + return sectrue == ret; } bool config_hasPin(void) @@ -726,7 +744,9 @@ bool config_changePin(const char *old_pin, const char *new_pin) return false; } + char oldTiny = usbTiny(1); secbool ret = storage_change_pin(pin_to_int(old_pin), new_pin_int); + usbTiny(oldTiny); #if DEBUG_LINK if (sectrue == ret) { @@ -897,8 +917,12 @@ void config_setAutoLockDelayMs(uint32_t auto_lock_delay_ms) void config_wipe(void) { + char oldTiny = usbTiny(1); storage_wipe(); - storage_unlock(PIN_EMPTY); + if (storage_is_unlocked() != sectrue) { + storage_unlock(PIN_EMPTY); + } + usbTiny(oldTiny); random_buffer((uint8_t *)config_uuid, sizeof(config_uuid)); data2hex(config_uuid, sizeof(config_uuid), config_uuid_str); autoLockDelayMsCached = secfalse; diff --git a/firmware/config.h b/firmware/config.h index 636becec3c..8f8b1bfe32 100644 --- a/firmware/config.h +++ b/firmware/config.h @@ -120,9 +120,8 @@ bool config_dumpNode(HDNodeType *node); bool config_getPin(char *dest, uint16_t dest_size); #endif -bool config_containsPin(const char *pin); +bool config_unlock(const char *pin); bool config_hasPin(void); -void config_setPin(const char *pin); bool config_changePin(const char *old_pin, const char *new_pin); bool session_isUnlocked(void); diff --git a/firmware/ethereum.c b/firmware/ethereum.c index 500102c398..c9de7c9cca 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -377,6 +377,9 @@ static void layoutEthereumFee(const uint8_t *value, uint32_t value_len, char tx_value[32]; char gas_value[32]; + memzero(tx_value, sizeof(tx_value)); + memzero(gas_value, sizeof(gas_value)); + memzero(pad_val, sizeof(pad_val)); memcpy(pad_val + (32 - gas_price_len), gas_price, gas_price_len); bn_read_be(pad_val, &val); diff --git a/firmware/header.S b/firmware/header.S new file mode 100644 index 0000000000..46e4aa3751 --- /dev/null +++ b/firmware/header.S @@ -0,0 +1,33 @@ + .syntax unified + +#include "version.h" + + .section .header, "a" + + .type g_header, %object + .size g_header, .-g_header + +g_header: + .byte 'T','R','Z','F' // magic + .word reset_handler // reset handler, replace later with : .word g_header_end - g_header // hdrlen + .word 0 // expiry + .word _codelen // codelen + .byte VERSION_MAJOR // vmajor + .byte VERSION_MINOR // vminor + .byte VERSION_PATCH // vpatch + .byte 0 // vbuild + .byte FIX_VERSION_MAJOR // fix_vmajor + .byte FIX_VERSION_MINOR // fix_vminor + .byte FIX_VERSION_PATCH // fix_vpatch + .byte 0 // fix_vbuild + . = . + 8 // reserved + . = . + 512 // hash1 ... hash16 + . = . + 64 // sig1 + . = . + 64 // sig2 + . = . + 64 // sig3 + .byte 0 // sigindex1 + .byte 0 // sigindex2 + .byte 0 // sigindex3 + . = . + 220 // reserved + . = . + 65 // reserved +g_header_end: diff --git a/firmware/layout2.c b/firmware/layout2.c index 1a5b98004e..0ca71faf97 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -128,8 +128,8 @@ static const char *address_n_str(const uint32_t *address_n, size_t address_n_cou } } - // "Path: m" / i ' - static char address_str[7 + 8 * (1 + 9 + 1) + 1]; + // "Path: m" / i ' + static char address_str[7 + 8 * (1 + 10 + 1) + 1]; char *c = address_str + sizeof(address_str) - 1; *c = 0; c--; diff --git a/firmware/protect.c b/firmware/protect.c index 99223ae332..548504b8bd 100644 --- a/firmware/protect.c +++ b/firmware/protect.c @@ -147,7 +147,7 @@ const char *requestPin(PinMatrixRequestType type, const char *text) } } -secbool protectPinUiCallback(uint32_t wait, uint32_t progress) +secbool protectPinUiCallback(uint32_t wait, uint32_t progress, const char* message) { // Convert wait to secstr string. char secstrbuf[] = _("________0 seconds"); @@ -163,7 +163,7 @@ secbool protectPinUiCallback(uint32_t wait, uint32_t progress) secstrbuf[16] = 0; } oledClear(); - oledDrawStringCenter(OLED_WIDTH / 2, 0 * 9, _("Verifying PIN"), FONT_STANDARD); + oledDrawStringCenter(OLED_WIDTH / 2, 0 * 9, message, FONT_STANDARD); oledDrawStringCenter(OLED_WIDTH / 2, 2 * 9, _("Please wait"), FONT_STANDARD); oledDrawStringCenter(OLED_WIDTH / 2, 3 * 9, secstr, FONT_STANDARD); oledDrawStringCenter(OLED_WIDTH / 2, 4 * 9, _("to continue ..."), FONT_STANDARD); @@ -204,9 +204,7 @@ bool protectPin(bool use_cached) } } - usbTiny(1); - bool ret = config_containsPin(pin); - usbTiny(0); + bool ret = config_unlock(pin); if (!ret) { fsm_sendFailure(FailureType_Failure_PinInvalid, NULL); } @@ -225,6 +223,18 @@ bool protectChangePin(bool removal) fsm_sendFailure(FailureType_Failure_PinCancelled, NULL); return false; } + + // If removing, defer the check to config_changePin(). + if (!removal) { + usbTiny(1); + bool ret = config_unlock(pin); + usbTiny(0); + if (ret == false) { + fsm_sendFailure(FailureType_Failure_PinInvalid, NULL); + return false; + } + } + strlcpy(old_pin, pin, sizeof(old_pin)); } @@ -253,9 +263,7 @@ bool protectChangePin(bool removal) } } - usbTiny(1); bool ret = config_changePin(old_pin, new_pin); - usbTiny(0); memzero(old_pin, sizeof(old_pin)); memzero(new_pin, sizeof(new_pin)); if (ret == false) { diff --git a/firmware/protect.h b/firmware/protect.h index bcabdac674..fb269468fc 100644 --- a/firmware/protect.h +++ b/firmware/protect.h @@ -25,7 +25,7 @@ #include "secbool.h" bool protectButton(ButtonRequestType type, bool confirm_only); -secbool protectPinUiCallback(uint32_t wait, uint32_t progress); +secbool protectPinUiCallback(uint32_t wait, uint32_t progress, const char* message); bool protectPin(bool use_cached); bool protectChangePin(bool removal); bool protectPassphrase(void); diff --git a/firmware/trezor.h b/firmware/trezor.h index 65a5158904..9975a997b7 100644 --- a/firmware/trezor.h +++ b/firmware/trezor.h @@ -21,10 +21,7 @@ #define __TREZOR_H__ #include - -#define VERSION_MAJOR 1 -#define VERSION_MINOR 8 -#define VERSION_PATCH 0 +#include "version.h" #define STR(X) #X #define VERSTR(X) STR(X) diff --git a/firmware/u2f.c b/firmware/u2f.c index 048b52a15f..6cb05914db 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -175,6 +175,8 @@ void u2fhid_init_cmd(const U2FHID_FRAME *f) { void u2fhid_read_start(const U2FHID_FRAME *f) { U2F_ReadBuffer readbuffer; + memzero(&readbuffer, sizeof(readbuffer)); + if (!(f->type & TYPE_INIT)) { return; } diff --git a/firmware/usb.c b/firmware/usb.c index 9da00ea913..356bd03650 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -259,6 +259,8 @@ static int hid_control_request(usbd_device *dev, struct usb_setup_data *req, uin (void)complete; (void)dev; + wait_random(); + if ((req->bmRequestType != 0x81) || (req->bRequest != USB_REQ_GET_DESCRIPTOR) || (req->wValue != 0x2200)) @@ -266,7 +268,7 @@ static int hid_control_request(usbd_device *dev, struct usb_setup_data *req, uin debugLog(0, "", "hid_control_request u2f"); *buf = (uint8_t *)hid_report_descriptor_u2f; - *len = MIN(*len, sizeof(hid_report_descriptor_u2f)); + *len = MIN_8bits(*len, sizeof(hid_report_descriptor_u2f)); return 1; } diff --git a/firmware/version.h b/firmware/version.h new file mode 100644 index 0000000000..1202e1b44f --- /dev/null +++ b/firmware/version.h @@ -0,0 +1,7 @@ +#define VERSION_MAJOR 1 +#define VERSION_MINOR 8 +#define VERSION_PATCH 0 + +#define FIX_VERSION_MAJOR 1 +#define FIX_VERSION_MINOR 8 +#define FIX_VERSION_PATCH 0 diff --git a/memory.c b/memory.c index 11c550b6e6..b11e046b20 100644 --- a/memory.c +++ b/memory.c @@ -34,7 +34,9 @@ void memory_protect(void) if (((FLASH_OPTION_BYTES_1 & 0xFFEC) == 0xCCEC) && ((FLASH_OPTION_BYTES_2 & 0xFFF) == 0xFFC) && (FLASH_OPTCR == 0x0FFCCCED)) { return; // already set up correctly - bail out } - + for (int i = FLASH_STORAGE_SECTOR_FIRST; i <= FLASH_STORAGE_SECTOR_LAST; i++) { + flash_erase_sector(i, FLASH_CR_PROGRAM_X32); + } flash_unlock_option_bytes(); // Section 2.8.6 Flash option control register (FLASH_OPTCR) // Bits 31:28 Reserved, must be kept cleared. diff --git a/memory.h b/memory.h index eac09efa5f..e47fc29172 100644 --- a/memory.h +++ b/memory.h @@ -24,43 +24,37 @@ /* - flash memory layout: + Flash memory layout: name | range | size | function -----------+-------------------------+---------+------------------ - Sector 0 | 0x08000000 - 0x08003FFF | 16 KiB | bootloader code - Sector 1 | 0x08004000 - 0x08007FFF | 16 KiB | bootloader code + Sector 0 | 0x08000000 - 0x08003FFF | 16 KiB | bootloader + Sector 1 | 0x08004000 - 0x08007FFF | 16 KiB | bootloader -----------+-------------------------+---------+------------------ - Sector 2 | 0x08008000 - 0x0800BFFF | 16 KiB | metadata area - Sector 3 | 0x0800C000 - 0x0800FFFF | 16 KiB | metadata area + Sector 2 | 0x08008000 - 0x0800BFFF | 16 KiB | storage area + Sector 3 | 0x0800C000 - 0x0800FFFF | 16 KiB | storage area -----------+-------------------------+---------+------------------ - Sector 4 | 0x08010000 - 0x0801FFFF | 64 KiB | application code - Sector 5 | 0x08020000 - 0x0803FFFF | 128 KiB | application code - Sector 6 | 0x08040000 - 0x0805FFFF | 128 KiB | application code - Sector 7 | 0x08060000 - 0x0807FFFF | 128 KiB | application code -===========+=========================+============================ - Sector 8 | 0x08080000 - 0x0809FFFF | 128 KiB | application code - Sector 9 | 0x080A0000 - 0x080BFFFF | 128 KiB | application code - Sector 10 | 0x080C0000 - 0x080DFFFF | 128 KiB | application code - Sector 11 | 0x080E0000 - 0x080FFFFF | 128 KiB | application code + Sector 4 | 0x08010000 - 0x0801FFFF | 64 KiB | firmware + Sector 5 | 0x08020000 - 0x0803FFFF | 128 KiB | firmware + Sector 6 | 0x08040000 - 0x0805FFFF | 128 KiB | firmware + Sector 7 | 0x08060000 - 0x0807FFFF | 128 KiB | firmware + Sector 8 | 0x08080000 - 0x0809FFFF | 128 KiB | firmware + Sector 9 | 0x080A0000 - 0x080BFFFF | 128 KiB | firmware + Sector 10 | 0x080C0000 - 0x080DFFFF | 128 KiB | firmware + Sector 11 | 0x080E0000 - 0x080FFFFF | 128 KiB | firmware - metadata area: + firmware header (occupies first 1 KB of the firmware) + - very similar to trezor-core firmware header described in: + https://github.com/trezor/trezor-core/blob/master/docs/bootloader.md#firmware-header + - differences: + * we don't use sigmask or sig field (these are reserved and set to zero) + * we introduce new fields immediately following the hash16 field: + - sig1[64], sig2[64], sig3[64] + - sigindex1[1], sigindex2[1], sigindex3[1] + * reserved[415] area is reduced to reserved[220] + - see signatures.c for more details - offset | type/length | description ---------+-------------+------------------------------- - 0x0000 | 4 bytes | magic = 'TRZR' - 0x0004 | uint32 | length of the code (codelen) - 0x0008 | uint8 | signature index #1 - 0x0009 | uint8 | signature index #2 - 0x000A | uint8 | signature index #3 - 0x000B | uint8 | flags - 0x000C | 52 bytes | reserved - 0x0040 | 64 bytes | signature #1 - 0x0080 | 64 bytes | signature #2 - 0x00C0 | 64 bytes | signature #3 - 0x0100 | 32K-256 B | persistent storage - - flags & 0x01 -> restore storage after flashing (if signatures are ok) + We pad the firmware chunks with zeroes if they are shorted. */ @@ -78,31 +72,20 @@ extern uint8_t *emulator_flash_base; #define FLASH_BOOT_START (FLASH_ORIGIN) #define FLASH_BOOT_LEN (0x8000) -#define FLASH_META_START (FLASH_BOOT_START + FLASH_BOOT_LEN) -#define FLASH_META_LEN (0x8000) +#define FLASH_STORAGE_START (FLASH_BOOT_START + FLASH_BOOT_LEN) +#define FLASH_STORAGE_LEN (0x8000) -#define FLASH_APP_START (FLASH_META_START + FLASH_META_LEN) +#define FLASH_FWHEADER_START (FLASH_STORAGE_START + FLASH_STORAGE_LEN) +#define FLASH_FWHEADER_LEN (0x400) -#define FLASH_META_MAGIC (FLASH_META_START) -#define FLASH_META_CODELEN (FLASH_META_START + 0x0004) -#define FLASH_META_SIGINDEX1 (FLASH_META_START + 0x0008) -#define FLASH_META_SIGINDEX2 (FLASH_META_START + 0x0009) -#define FLASH_META_SIGINDEX3 (FLASH_META_START + 0x000A) -#define FLASH_META_FLAGS (FLASH_META_START + 0x000B) -#define FLASH_META_SIG1 (FLASH_META_START + 0x0040) -#define FLASH_META_SIG2 (FLASH_META_START + 0x0080) -#define FLASH_META_SIG3 (FLASH_META_START + 0x00C0) - -#define FLASH_META_DESC_LEN (0x100) - -#define FLASH_STORAGE_START (FLASH_META_START + FLASH_META_DESC_LEN) -#define FLASH_STORAGE_LEN (FLASH_APP_START - FLASH_STORAGE_START) +#define FLASH_APP_START (FLASH_FWHEADER_START + FLASH_FWHEADER_LEN) +#define FLASH_APP_LEN (FLASH_TOTAL_SIZE - (FLASH_APP_START - FLASH_ORIGIN)) #define FLASH_BOOT_SECTOR_FIRST 0 #define FLASH_BOOT_SECTOR_LAST 1 -#define FLASH_META_SECTOR_FIRST 2 -#define FLASH_META_SECTOR_LAST 3 +#define FLASH_STORAGE_SECTOR_FIRST 2 +#define FLASH_STORAGE_SECTOR_LAST 3 #define FLASH_CODE_SECTOR_FIRST 4 #define FLASH_CODE_SECTOR_LAST 11 diff --git a/memory_app_1.8.0.ld b/memory_app_1.8.0.ld new file mode 100644 index 0000000000..8767a33e29 --- /dev/null +++ b/memory_app_1.8.0.ld @@ -0,0 +1,31 @@ +/* STM32F205RG - 1024K Flash, 128K RAM */ +/* program starts at 0x08010400 */ +MEMORY +{ + rom (rx) : ORIGIN = 0x08010000, LENGTH = 1024K - 64K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K +} + +SECTIONS +{ + .confidential (NOLOAD) : { + *(confidential) + ASSERT ((SIZEOF(.confidential) <= 32K), "Error: Confidential section too big!"); + } >ram + + .header : ALIGN(4) { + KEEP(*(.header)); + } >rom AT>rom +} + +INCLUDE libopencm3_stm32f2.ld + +_codelen = SIZEOF(.text) + SIZEOF(.data) + SIZEOF(.ARM.exidx); + +_ram_start = ORIGIN(ram); +_ram_end = ORIGIN(ram) + LENGTH(ram); +_stack = _ram_end - 8; +__stack_chk_guard = _ram_end - 8; +system_millis = _ram_end - 4; + +_data_size = SIZEOF(.data); diff --git a/norcow_config.h b/norcow_config.h index ba57dfb4b2..1ea894c2b5 100644 --- a/norcow_config.h +++ b/norcow_config.h @@ -29,7 +29,7 @@ /* * The length of the sector header in bytes. The header is preserved between sector erasures. */ -#define NORCOW_HEADER_LEN (0x100) +#define NORCOW_HEADER_LEN (0) /* * Current storage version. diff --git a/script/cibuild b/script/cibuild index 2c2382a8e7..b02691b702 100755 --- a/script/cibuild +++ b/script/cibuild @@ -22,4 +22,8 @@ fi make -C vendor/nanopb/generator/proto make -C firmware/protob -make -C firmware sign +make -C firmware + +if [ "$EMULATOR" != 1 ]; then + make -C firmware sign +fi diff --git a/setup.c b/setup.c index e59682b28c..1353013007 100644 --- a/setup.c +++ b/setup.c @@ -33,7 +33,7 @@ uint32_t __stack_chk_guard; static inline void __attribute__((noreturn)) fault_handler(const char *line1) { layoutDialog(&bmp_icon_error, NULL, NULL, NULL, line1, "detected.", NULL, "Please unplug", "the device.", NULL); - for (;;) {} // loop forever + shutdown(); } void __attribute__((noreturn)) __stack_chk_fail(void) { @@ -141,6 +141,7 @@ void setupApp(void) gpio_set_af(GPIOA, GPIO_AF10, GPIO10); } +#define MPU_RASR_SIZE_32B (0x04UL << MPU_RASR_SIZE_LSB) #define MPU_RASR_SIZE_1KB (0x09UL << MPU_RASR_SIZE_LSB) #define MPU_RASR_SIZE_4KB (0x0BUL << MPU_RASR_SIZE_LSB) #define MPU_RASR_SIZE_8KB (0x0CUL << MPU_RASR_SIZE_LSB) @@ -152,6 +153,7 @@ void setupApp(void) #define MPU_RASR_SIZE_512KB (0x12UL << MPU_RASR_SIZE_LSB) #define MPU_RASR_SIZE_1MB (0x13UL << MPU_RASR_SIZE_LSB) #define MPU_RASR_SIZE_512MB (0x1CUL << MPU_RASR_SIZE_LSB) +#define MPU_RASR_SIZE_4GB (0x1FUL << MPU_RASR_SIZE_LSB) // http://infocenter.arm.com/help/topic/com.arm.doc.dui0552a/BABDJJGF.html #define MPU_RASR_ATTR_FLASH (MPU_RASR_ATTR_C) @@ -161,6 +163,55 @@ void setupApp(void) #define FLASH_BASE (0x08000000U) #define SRAM_BASE (0x20000000U) +void mpu_config_off(void) +{ + // Disable MPU + MPU_CTRL = 0; + + __asm__ volatile("dsb"); + __asm__ volatile("isb"); +} + +void mpu_config_bootloader(void) +{ + // Disable MPU + MPU_CTRL = 0; + + // Note: later entries overwrite previous ones + + // Everything (0x00000000 - 0xFFFFFFFF, 4 GiB, read-write) + MPU_RBAR = 0 | MPU_RBAR_VALID | (0 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_FLASH | MPU_RASR_SIZE_4GB | MPU_RASR_ATTR_AP_PRW_URW; + + // Flash (0x8007FE0 - 0x08007FFF, 32 B, no-access) + MPU_RBAR = (FLASH_BASE + 0x7FE0) | MPU_RBAR_VALID | (1 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_FLASH | MPU_RASR_SIZE_32B | MPU_RASR_ATTR_AP_PNO_UNO; + + // SRAM (0x20000000 - 0x2001FFFF, read-write, execute never) + MPU_RBAR = SRAM_BASE | MPU_RBAR_VALID | (2 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_SRAM | MPU_RASR_SIZE_128KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + + // Peripherals (0x40000000 - 0x4001FFFF, read-write, execute never) + MPU_RBAR = PERIPH_BASE | MPU_RBAR_VALID | (3 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_128KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + // Peripherals (0x40020000 - 0x40023FFF, read-write, execute never) + MPU_RBAR = 0x40020000 | MPU_RBAR_VALID | (4 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_16KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + // Don't enable DMA controller access + // Peripherals (0x50000000 - 0x5007ffff, read-write, execute never) + MPU_RBAR = 0x50000000 | MPU_RBAR_VALID | (5 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_512KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + + // Enable MPU + MPU_CTRL = MPU_CTRL_ENABLE | MPU_CTRL_HFNMIENA; + + // Enable memory fault handler + SCB_SHCSR |= SCB_SHCSR_MEMFAULTENA; + + __asm__ volatile("dsb"); + __asm__ volatile("isb"); +} + // Never use in bootloader! Disables access to PPB (including MPU, NVIC, SCB) void mpu_config_firmware(void) { @@ -169,6 +220,7 @@ void mpu_config_firmware(void) MPU_CTRL = 0; // Note: later entries overwrite previous ones + // Flash (0x08000000 - 0x0807FFFF, 1 MiB, read-only) MPU_RBAR = FLASH_BASE | MPU_RBAR_VALID | (0 << MPU_RBAR_REGION_LSB); MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_FLASH | MPU_RASR_SIZE_1MB | MPU_RASR_ATTR_AP_PRO_URO; diff --git a/setup.h b/setup.h index f6e3f91f77..503e418cad 100644 --- a/setup.h +++ b/setup.h @@ -27,6 +27,8 @@ extern uint32_t __stack_chk_guard; void setup(void); void setupApp(void); +void mpu_config_off(void); +void mpu_config_bootloader(void); void mpu_config_firmware(void); #endif diff --git a/startup.s b/startup.s index 59a87e1af0..ce75ba1a7a 100644 --- a/startup.s +++ b/startup.s @@ -19,6 +19,16 @@ memset_reg: .global reset_handler .type reset_handler, STT_FUNC reset_handler: +// we need to perform this in case an old bootloader +// is starting the new firmware, these will be set incorrectly + ldr r0, =0xE000ED08 // r0 = VTOR address + ldr r1, =0x08010400 // r1 = FLASH_APP_START + str r1, [r0] // assign + ldr r0, =_stack // r0 = stack pointer + msr msp, r0 // set stack pointer + dsb + isb + ldr r0, =_ram_start // r0 - point to beginning of SRAM ldr r1, =_ram_end // r1 - point to byte after the end of SRAM ldr r2, =0 // r2 - the byte-sized value to be written diff --git a/supervise.c b/supervise.c index 7669cc5ed0..5a86db884c 100644 --- a/supervise.c +++ b/supervise.c @@ -45,9 +45,9 @@ static void svhandler_flash_program(uint32_t psize) { } static void svhandler_flash_erase_sector(uint16_t sector) { - /* we only allow erasing meta sectors 2 and 3. */ - if (sector < FLASH_META_SECTOR_FIRST || - sector > FLASH_META_SECTOR_LAST) { + /* we only allow erasing storage sectors 2 and 3. */ + if (sector < FLASH_STORAGE_SECTOR_FIRST || + sector > FLASH_STORAGE_SECTOR_LAST) { return; } flash_erase_sector(sector, FLASH_CR_PROGRAM_X32); diff --git a/usb21_standard.c b/usb21_standard.c index 7ed1986cf4..d6aab2bfd9 100644 --- a/usb21_standard.c +++ b/usb21_standard.c @@ -62,13 +62,15 @@ static int usb21_standard_get_descriptor(usbd_device* usbd_dev, (void)complete; (void)usbd_dev; + wait_random(); + if (req->bRequest == USB_REQ_GET_DESCRIPTOR) { int descr_type = req->wValue >> 8; if (descr_type == USB_DT_BOS) { if (!usb21_bos) { return USBD_REQ_NOTSUPP; } - *len = MIN(*len, build_bos_descriptor(usb21_bos, *buf, *len)); + *len = MIN_8bits(*len, build_bos_descriptor(usb21_bos, *buf, *len)); return USBD_REQ_HANDLED; } } diff --git a/usb_private.h b/usb_private.h new file mode 100644 index 0000000000..bb553be32e --- /dev/null +++ b/usb_private.h @@ -0,0 +1,161 @@ +/** @defgroup usb_private_defines USB Private Structures + +@brief Defined Constants and Types for the USB Private Structures + +@ingroup USB_defines + +@version 1.0.0 + +@author @htmlonly © @endhtmlonly 2010 +Gareth McMullin + +@date 10 March 2013 + +LGPL License Terms @ref lgpl_license +*/ + +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2010 Gareth McMullin + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/**@{*/ + +#ifndef __USB_PRIVATE_H +#define __USB_PRIVATE_H + +#define MAX_USER_CONTROL_CALLBACK 4 +#define MAX_USER_SET_CONFIG_CALLBACK 4 + +/** Internal collection of device information. */ +struct _usbd_device { + const struct usb_device_descriptor *desc; + const struct usb_config_descriptor *config; + const char **strings; + int num_strings; + + uint8_t *ctrl_buf; /**< Internal buffer used for control transfers */ + uint16_t ctrl_buf_len; + + uint8_t current_address; + uint8_t current_config; + + uint16_t pm_top; /**< Top of allocated endpoint buffer memory */ + + /* User callback functions for various USB events */ + void (*user_callback_reset)(void); + void (*user_callback_suspend)(void); + void (*user_callback_resume)(void); + void (*user_callback_sof)(void); + + struct usb_control_state { + enum { + IDLE, STALLED, + DATA_IN, LAST_DATA_IN, STATUS_IN, + DATA_OUT, LAST_DATA_OUT, STATUS_OUT, + } state; + struct usb_setup_data req __attribute__((aligned(4))); + uint8_t *ctrl_buf; + uint16_t ctrl_len; + usbd_control_complete_callback complete; + bool needs_zlp; + } control_state; + + struct user_control_callback { + usbd_control_callback cb; + uint8_t type; + uint8_t type_mask; + } user_control_callback[MAX_USER_CONTROL_CALLBACK]; + + usbd_endpoint_callback user_callback_ctr[8][3]; + + /* User callback function for some standard USB function hooks */ + usbd_set_config_callback user_callback_set_config[MAX_USER_SET_CONFIG_CALLBACK]; + + usbd_set_altsetting_callback user_callback_set_altsetting; + + const struct _usbd_driver *driver; + + /* private driver data */ + + uint16_t fifo_mem_top; + uint16_t fifo_mem_top_ep0; + uint8_t force_nak[4]; + /* + * We keep a backup copy of the out endpoint size registers to restore + * them after a transaction. + */ + uint32_t doeptsiz[4]; + /* + * Received packet size for each endpoint. This is assigned in + * stm32f107_poll() which reads the packet status push register GRXSTSP + * for use in stm32f107_ep_read_packet(). + */ + uint16_t rxbcnt; +}; + +enum _usbd_transaction { + USB_TRANSACTION_IN, + USB_TRANSACTION_OUT, + USB_TRANSACTION_SETUP, +}; + +/* Do not appear to belong to the API, so are omitted from docs */ +/**@}*/ + +void _usbd_control_in(usbd_device *usbd_dev, uint8_t ea); +void _usbd_control_out(usbd_device *usbd_dev, uint8_t ea); +void _usbd_control_setup(usbd_device *usbd_dev, uint8_t ea); + +int _usbd_standard_request_device(usbd_device *usbd_dev, + struct usb_setup_data *req, uint8_t **buf, + uint16_t *len); +int _usbd_standard_request_interface(usbd_device *usbd_dev, + struct usb_setup_data *req, uint8_t **buf, + uint16_t *len); +int _usbd_standard_request_endpoint(usbd_device *usbd_dev, + struct usb_setup_data *req, uint8_t **buf, + uint16_t *len); +int _usbd_standard_request(usbd_device *usbd_dev, struct usb_setup_data *req, + uint8_t **buf, uint16_t *len); + +void _usbd_reset(usbd_device *usbd_dev); + +/* Functions provided by the hardware abstraction. */ +struct _usbd_driver { + usbd_device *(*init)(void); + void (*set_address)(usbd_device *usbd_dev, uint8_t addr); + void (*ep_setup)(usbd_device *usbd_dev, uint8_t addr, uint8_t type, + uint16_t max_size, usbd_endpoint_callback cb); + void (*ep_reset)(usbd_device *usbd_dev); + void (*ep_stall_set)(usbd_device *usbd_dev, uint8_t addr, + uint8_t stall); + void (*ep_nak_set)(usbd_device *usbd_dev, uint8_t addr, uint8_t nak); + uint8_t (*ep_stall_get)(usbd_device *usbd_dev, uint8_t addr); + uint16_t (*ep_write_packet)(usbd_device *usbd_dev, uint8_t addr, + const void *buf, uint16_t len); + uint16_t (*ep_read_packet)(usbd_device *usbd_dev, uint8_t addr, + void *buf, uint16_t len); + void (*poll)(usbd_device *usbd_dev); + void (*disconnect)(usbd_device *usbd_dev, bool disconnected); + uint32_t base_address; + bool set_address_before_status; + uint16_t rx_fifo_size; +}; + +#endif + diff --git a/usb_standard.c b/usb_standard.c new file mode 100644 index 0000000000..3576c289c0 --- /dev/null +++ b/usb_standard.c @@ -0,0 +1,604 @@ +/** @defgroup usb_standard_file Generic USB Standard Request Interface + +@ingroup USB + +@brief Generic USB Standard Request Interface + +@version 1.0.0 + +@author @htmlonly © @endhtmlonly 2010 +Gareth McMullin + +@date 10 March 2013 + +LGPL License Terms @ref lgpl_license +*/ + +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2010 Gareth McMullin + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/**@{*/ + +#include +#include +#include "usb_private.h" +#include "util.h" + +int usbd_register_set_config_callback(usbd_device *usbd_dev, + usbd_set_config_callback callback) +{ + int i; + + for (i = 0; i < MAX_USER_SET_CONFIG_CALLBACK; i++) { + if (usbd_dev->user_callback_set_config[i]) { + continue; + } + + usbd_dev->user_callback_set_config[i] = callback; + return 0; + } + + return -1; +} + +void usbd_register_set_altsetting_callback(usbd_device *usbd_dev, + usbd_set_altsetting_callback callback) +{ + usbd_dev->user_callback_set_altsetting = callback; +} + +static uint16_t build_config_descriptor(usbd_device *usbd_dev, + uint8_t index, uint8_t *buf, uint16_t len) +{ + uint8_t *tmpbuf = buf; + const struct usb_config_descriptor *cfg = &usbd_dev->config[index]; + uint16_t count, total = 0, totallen = 0; + uint16_t i, j, k; + + memcpy(buf, cfg, count = MIN(len, cfg->bLength)); + buf += count; + len -= count; + total += count; + totallen += cfg->bLength; + + /* For each interface... */ + for (i = 0; i < cfg->bNumInterfaces; i++) { + /* Interface Association Descriptor, if any */ + if (cfg->interface[i].iface_assoc) { + const struct usb_iface_assoc_descriptor *assoc = + cfg->interface[i].iface_assoc; + memcpy(buf, assoc, count = MIN(len, assoc->bLength)); + buf += count; + len -= count; + total += count; + totallen += assoc->bLength; + } + /* For each alternate setting... */ + for (j = 0; j < cfg->interface[i].num_altsetting; j++) { + const struct usb_interface_descriptor *iface = + &cfg->interface[i].altsetting[j]; + /* Copy interface descriptor. */ + memcpy(buf, iface, count = MIN(len, iface->bLength)); + buf += count; + len -= count; + total += count; + totallen += iface->bLength; + /* Copy extra bytes (function descriptors). */ + if (iface->extra) { + memcpy(buf, iface->extra, + count = MIN(len, iface->extralen)); + buf += count; + len -= count; + total += count; + totallen += iface->extralen; + } + /* For each endpoint... */ + for (k = 0; k < iface->bNumEndpoints; k++) { + const struct usb_endpoint_descriptor *ep = + &iface->endpoint[k]; + memcpy(buf, ep, count = MIN(len, ep->bLength)); + buf += count; + len -= count; + total += count; + totallen += ep->bLength; + /* Copy extra bytes (class specific). */ + if (ep->extra) { + memcpy(buf, ep->extra, + count = MIN(len, ep->extralen)); + buf += count; + len -= count; + total += count; + totallen += ep->extralen; + } + } + } + } + + /* Fill in wTotalLength. */ + *(uint16_t *)(tmpbuf + 2) = totallen; + + return total; +} + +static int usb_descriptor_type(uint16_t wValue) +{ + return wValue >> 8; +} + +static int usb_descriptor_index(uint16_t wValue) +{ + return wValue & 0xFF; +} + +static int usb_standard_get_descriptor(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) +{ + + wait_random(); + + int i, array_idx, descr_idx; + struct usb_string_descriptor *sd; + + descr_idx = usb_descriptor_index(req->wValue); + + switch (usb_descriptor_type(req->wValue)) { + case USB_DT_DEVICE: + *buf = (uint8_t *) usbd_dev->desc; + *len = MIN_8bits(*len, usbd_dev->desc->bLength); + return USBD_REQ_HANDLED; + case USB_DT_CONFIGURATION: + *buf = usbd_dev->ctrl_buf; + *len = build_config_descriptor(usbd_dev, descr_idx, *buf, *len); + return USBD_REQ_HANDLED; + case USB_DT_STRING: + sd = (struct usb_string_descriptor *)usbd_dev->ctrl_buf; + + if (descr_idx == 0) { + /* Send sane Language ID descriptor... */ + sd->wData[0] = USB_LANGID_ENGLISH_US; + sd->bLength = sizeof(sd->bLength) + + sizeof(sd->bDescriptorType) + + sizeof(sd->wData[0]); + + *len = MIN_8bits(*len, sd->bLength); + } else { + array_idx = descr_idx - 1; + + if (!usbd_dev->strings) { + /* Device doesn't support strings. */ + return USBD_REQ_NOTSUPP; + } + + /* Check that string index is in range. */ + if (array_idx >= usbd_dev->num_strings) { + return USBD_REQ_NOTSUPP; + } + + /* Strings with Language ID differnet from + * USB_LANGID_ENGLISH_US are not supported */ + if (req->wIndex != USB_LANGID_ENGLISH_US) { + return USBD_REQ_NOTSUPP; + } + + /* This string is returned as UTF16, hence the + * multiplication + */ + sd->bLength = strlen(usbd_dev->strings[array_idx]) * 2 + + sizeof(sd->bLength) + + sizeof(sd->bDescriptorType); + + *len = MIN_8bits(*len, sd->bLength); + + for (i = 0; i < (*len / 2) - 1; i++) { + sd->wData[i] = + usbd_dev->strings[array_idx][i]; + } + } + + sd->bDescriptorType = USB_DT_STRING; + *buf = (uint8_t *)sd; + + return USBD_REQ_HANDLED; + } + return USBD_REQ_NOTSUPP; +} + +static int usb_standard_set_address(usbd_device *usbd_dev, + struct usb_setup_data *req, uint8_t **buf, + uint16_t *len) +{ + (void)req; + (void)buf; + (void)len; + + /* The actual address is only latched at the STATUS IN stage. */ + if ((req->bmRequestType != 0) || (req->wValue >= 128)) { + return 0; + } + + usbd_dev->current_address = req->wValue; + + /* + * Special workaround for STM32F10[57] that require the address + * to be set here. This is undocumented! + */ + if (usbd_dev->driver->set_address_before_status) { + usbd_dev->driver->set_address(usbd_dev, req->wValue); + } + + return 1; +} + +static int usb_standard_set_configuration(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) +{ + unsigned i; + int found_index = -1; + const struct usb_config_descriptor *cfg; + + (void)req; + (void)buf; + (void)len; + + if (req->wValue > 0) { + for (i = 0; i < usbd_dev->desc->bNumConfigurations; i++) { + if (req->wValue + == usbd_dev->config[i].bConfigurationValue) { + found_index = i; + break; + } + } + if (found_index < 0) { + return USBD_REQ_NOTSUPP; + } + } + + usbd_dev->current_config = found_index + 1; + + if (usbd_dev->current_config > 0) { + cfg = &usbd_dev->config[usbd_dev->current_config - 1]; + + /* reset all alternate settings configuration */ + for (i = 0; i < cfg->bNumInterfaces; i++) { + if (cfg->interface[i].cur_altsetting) { + *cfg->interface[i].cur_altsetting = 0; + } + } + } + + /* Reset all endpoints. */ + usbd_dev->driver->ep_reset(usbd_dev); + + if (usbd_dev->user_callback_set_config[0]) { + /* + * Flush control callbacks. These will be reregistered + * by the user handler. + */ + for (i = 0; i < MAX_USER_CONTROL_CALLBACK; i++) { + usbd_dev->user_control_callback[i].cb = NULL; + } + + for (i = 0; i < MAX_USER_SET_CONFIG_CALLBACK; i++) { + if (usbd_dev->user_callback_set_config[i]) { + usbd_dev->user_callback_set_config[i](usbd_dev, + req->wValue); + } + } + } + + return 1; +} + +static int usb_standard_get_configuration(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) +{ + (void)req; + + if (*len > 1) { + *len = 1; + } + if (usbd_dev->current_config > 0) { + const struct usb_config_descriptor *cfg = + &usbd_dev->config[usbd_dev->current_config - 1]; + (*buf)[0] = cfg->bConfigurationValue; + } else { + (*buf)[0] = 0; + } + + return 1; +} + +static int usb_standard_set_interface(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) +{ + const struct usb_config_descriptor *cfx = + &usbd_dev->config[usbd_dev->current_config - 1]; + const struct usb_interface *iface; + + (void)buf; + + if (req->wIndex >= cfx->bNumInterfaces) { + return USBD_REQ_NOTSUPP; + } + + iface = &cfx->interface[req->wIndex]; + + if (req->wValue >= iface->num_altsetting) { + return USBD_REQ_NOTSUPP; + } + + if (iface->cur_altsetting) { + *iface->cur_altsetting = req->wValue; + } else if (req->wValue > 0) { + return USBD_REQ_NOTSUPP; + } + + if (usbd_dev->user_callback_set_altsetting) { + usbd_dev->user_callback_set_altsetting(usbd_dev, + req->wIndex, + req->wValue); + } + + *len = 0; + + return USBD_REQ_HANDLED; +} + +static int usb_standard_get_interface(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) +{ + uint8_t *cur_altsetting; + const struct usb_config_descriptor *cfx = + &usbd_dev->config[usbd_dev->current_config - 1]; + + if (req->wIndex >= cfx->bNumInterfaces) { + return USBD_REQ_NOTSUPP; + } + + *len = 1; + cur_altsetting = cfx->interface[req->wIndex].cur_altsetting; + (*buf)[0] = (cur_altsetting) ? *cur_altsetting : 0; + + return USBD_REQ_HANDLED; +} + +static int usb_standard_device_get_status(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) +{ + (void)usbd_dev; + (void)req; + + /* bit 0: self powered */ + /* bit 1: remote wakeup */ + if (*len > 2) { + *len = 2; + } + (*buf)[0] = 0; + (*buf)[1] = 0; + + return 1; +} + +static int usb_standard_interface_get_status(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) +{ + (void)usbd_dev; + (void)req; + /* not defined */ + + if (*len > 2) { + *len = 2; + } + (*buf)[0] = 0; + (*buf)[1] = 0; + + return 1; +} + +static int usb_standard_endpoint_get_status(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) +{ + (void)req; + + if (*len > 2) { + *len = 2; + } + (*buf)[0] = usbd_ep_stall_get(usbd_dev, req->wIndex) ? 1 : 0; + (*buf)[1] = 0; + + return 1; +} + +static int usb_standard_endpoint_stall(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) +{ + (void)buf; + (void)len; + + usbd_ep_stall_set(usbd_dev, req->wIndex, 1); + + return 1; +} + +static int usb_standard_endpoint_unstall(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) +{ + (void)buf; + (void)len; + + usbd_ep_stall_set(usbd_dev, req->wIndex, 0); + + return 1; +} + +/* Do not appear to belong to the API, so are omitted from docs */ +/**@}*/ + +int _usbd_standard_request_device(usbd_device *usbd_dev, + struct usb_setup_data *req, uint8_t **buf, + uint16_t *len) +{ + int (*command)(usbd_device *usbd_dev, struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) = NULL; + + switch (req->bRequest) { + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + if (req->wValue == USB_FEAT_DEVICE_REMOTE_WAKEUP) { + /* Device wakeup code goes here. */ + } + + if (req->wValue == USB_FEAT_TEST_MODE) { + /* Test mode code goes here. */ + } + + break; + case USB_REQ_SET_ADDRESS: + /* + * SET ADDRESS is an exception. + * It is only processed at STATUS stage. + */ + command = usb_standard_set_address; + break; + case USB_REQ_SET_CONFIGURATION: + command = usb_standard_set_configuration; + break; + case USB_REQ_GET_CONFIGURATION: + command = usb_standard_get_configuration; + break; + case USB_REQ_GET_DESCRIPTOR: + command = usb_standard_get_descriptor; + break; + case USB_REQ_GET_STATUS: + /* + * GET_STATUS always responds with zero reply. + * The application may override this behaviour. + */ + command = usb_standard_device_get_status; + break; + case USB_REQ_SET_DESCRIPTOR: + /* SET_DESCRIPTOR is optional and not implemented. */ + break; + } + + if (!command) { + return 0; + } + + return command(usbd_dev, req, buf, len); +} + +int _usbd_standard_request_interface(usbd_device *usbd_dev, + struct usb_setup_data *req, uint8_t **buf, + uint16_t *len) +{ + int (*command)(usbd_device *usbd_dev, struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) = NULL; + + switch (req->bRequest) { + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + /* not defined */ + break; + case USB_REQ_GET_INTERFACE: + command = usb_standard_get_interface; + break; + case USB_REQ_SET_INTERFACE: + command = usb_standard_set_interface; + break; + case USB_REQ_GET_STATUS: + command = usb_standard_interface_get_status; + break; + } + + if (!command) { + return 0; + } + + return command(usbd_dev, req, buf, len); +} + +int _usbd_standard_request_endpoint(usbd_device *usbd_dev, + struct usb_setup_data *req, uint8_t **buf, + uint16_t *len) +{ + int (*command) (usbd_device *usbd_dev, struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) = NULL; + + switch (req->bRequest) { + case USB_REQ_CLEAR_FEATURE: + if (req->wValue == USB_FEAT_ENDPOINT_HALT) { + command = usb_standard_endpoint_unstall; + } + break; + case USB_REQ_SET_FEATURE: + if (req->wValue == USB_FEAT_ENDPOINT_HALT) { + command = usb_standard_endpoint_stall; + } + break; + case USB_REQ_GET_STATUS: + command = usb_standard_endpoint_get_status; + break; + case USB_REQ_SET_SYNCH_FRAME: + /* FIXME: SYNCH_FRAME is not implemented. */ + /* + * SYNCH_FRAME is used for synchronization of isochronous + * endpoints which are not yet implemented. + */ + break; + } + + if (!command) { + return 0; + } + + return command(usbd_dev, req, buf, len); +} + +int _usbd_standard_request(usbd_device *usbd_dev, struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) +{ + /* FIXME: Have class/vendor requests as well. */ + if ((req->bmRequestType & USB_REQ_TYPE_TYPE) != USB_REQ_TYPE_STANDARD) { + return 0; + } + + switch (req->bmRequestType & USB_REQ_TYPE_RECIPIENT) { + case USB_REQ_TYPE_DEVICE: + return _usbd_standard_request_device(usbd_dev, req, buf, len); + case USB_REQ_TYPE_INTERFACE: + return _usbd_standard_request_interface(usbd_dev, req, + buf, len); + case USB_REQ_TYPE_ENDPOINT: + return _usbd_standard_request_endpoint(usbd_dev, req, buf, len); + default: + return 0; + } +} + diff --git a/util.c b/util.c index 575127421c..7d32092a8f 100644 --- a/util.c +++ b/util.c @@ -17,6 +17,7 @@ * along with this library. If not, see . */ +#include "rng.h" #include "util.h" inline void delay(uint32_t wait) @@ -24,6 +25,24 @@ inline void delay(uint32_t wait) while (--wait > 0) __asm__("nop"); } +void wait_random(void) +{ + int wait = random32() & 0xff; + volatile int i = 0; + volatile int j = wait; + while (i < wait) { + if (i + j != wait) { + shutdown(); + } + ++i; + --j; + } + // Double-check loop completion. + if (i != wait || j != 0) { + shutdown(); + } +} + static const char *hexdigits = "0123456789ABCDEF"; void uint32hex(uint32_t num, char *str) diff --git a/util.h b/util.h index bd95099082..c96a79b6e5 100644 --- a/util.h +++ b/util.h @@ -27,14 +27,18 @@ #if !EMULATOR #include #include +#include "timer.h" #endif // Statement expressions make these macros side-effect safe +#define MIN_8bits(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? (_a & 0xFF) : (_b & 0xFF); }) #define MIN(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _a : _b; }) #define MAX(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _a : _b; }) void delay(uint32_t wait); +void wait_random(void); + // converts uint32 to hexa (8 digits) void uint32hex(uint32_t num, char *str); @@ -65,6 +69,7 @@ static inline void __attribute__((noreturn)) jump_to_firmware(const vector_table // Set stack pointer __asm__ volatile("msr msp, %0" :: "r" (vector_table->initial_sp_value)); } else { // untrusted firmware + timer_init(); mpu_config_firmware(); // * configure MPU for the firmware __asm__ volatile("msr msp, %0" :: "r" (_stack)); } diff --git a/vendor/trezor-crypto b/vendor/trezor-crypto index 21391dc5be..4211ce389f 160000 --- a/vendor/trezor-crypto +++ b/vendor/trezor-crypto @@ -1 +1 @@ -Subproject commit 21391dc5be9917bc32a518cf98376f79103727af +Subproject commit 4211ce389f6795d844809b0ba66a84082038ca04 diff --git a/vendor/trezor-storage b/vendor/trezor-storage index d715873ee6..511fc205b2 160000 --- a/vendor/trezor-storage +++ b/vendor/trezor-storage @@ -1 +1 @@ -Subproject commit d715873ee62d776ddc0f85028f7622374d4e0fe7 +Subproject commit 511fc205b284605651348512c5c5c2c95a642fa1 diff --git a/webusb.c b/webusb.c index 7106187810..21c6d3b5e7 100644 --- a/webusb.c +++ b/webusb.c @@ -53,6 +53,8 @@ static int webusb_control_vendor_request(usbd_device *usbd_dev, (void)complete; (void)usbd_dev; + wait_random(); + if (req->bRequest != WEBUSB_VENDOR_CODE) { return USBD_REQ_NEXT_CALLBACK; } @@ -72,7 +74,7 @@ static int webusb_control_vendor_request(usbd_device *usbd_dev, url->bDescriptorType = WEBUSB_DT_URL; url->bScheme = WEBUSB_URL_SCHEME_HTTPS; memcpy(&url->URL, webusb_https_url, url_len); - *len = MIN(*len, url->bLength); + *len = MIN_8bits(*len, url->bLength); status = USBD_REQ_HANDLED; } else { // TODO: stall instead? diff --git a/winusb.c b/winusb.c index afdaf8b706..06321fd3dd 100644 --- a/winusb.c +++ b/winusb.c @@ -82,6 +82,8 @@ static int winusb_descriptor_request(usbd_device *usbd_dev, (void)complete; (void)usbd_dev; + wait_random(); + if ((req->bmRequestType & USB_REQ_TYPE_TYPE) != USB_REQ_TYPE_STANDARD) { return USBD_REQ_NEXT_CALLBACK; } @@ -89,7 +91,7 @@ static int winusb_descriptor_request(usbd_device *usbd_dev, if (req->bRequest == USB_REQ_GET_DESCRIPTOR && usb_descriptor_type(req->wValue) == USB_DT_STRING) { if (usb_descriptor_index(req->wValue) == WINUSB_EXTRA_STRING_INDEX) { *buf = (uint8_t*)(&winusb_string_descriptor); - *len = MIN(*len, winusb_string_descriptor.bLength); + *len = MIN_8bits(*len, winusb_string_descriptor.bLength); return USBD_REQ_HANDLED; } } @@ -103,6 +105,8 @@ static int winusb_control_vendor_request(usbd_device *usbd_dev, (void)complete; (void)usbd_dev; + wait_random(); + if (req->bRequest != WINUSB_MS_VENDOR_CODE) { return USBD_REQ_NEXT_CALLBACK; } @@ -111,7 +115,7 @@ static int winusb_control_vendor_request(usbd_device *usbd_dev, if (((req->bmRequestType & USB_REQ_TYPE_RECIPIENT) == USB_REQ_TYPE_DEVICE) && (req->wIndex == WINUSB_REQ_GET_COMPATIBLE_ID_FEATURE_DESCRIPTOR)) { *buf = (uint8_t*)(&winusb_wcid); - *len = MIN(*len, winusb_wcid.header.dwLength); + *len = MIN_8bits(*len, winusb_wcid.header.dwLength); status = USBD_REQ_HANDLED; } else if (((req->bmRequestType & USB_REQ_TYPE_RECIPIENT) == USB_REQ_TYPE_INTERFACE) && @@ -119,7 +123,7 @@ static int winusb_control_vendor_request(usbd_device *usbd_dev, (usb_descriptor_index(req->wValue) == winusb_wcid.functions[0].bInterfaceNumber)) { *buf = (uint8_t*)(&guid); - *len = MIN(*len, guid.header.dwLength); + *len = MIN_8bits(*len, guid.header.dwLength); status = USBD_REQ_HANDLED; } else {