mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-26 09:28:13 +00:00
Merge branch 'secfix'
This commit is contained in:
commit
358ea93e80
1
Makefile
1
Makefile
@ -21,6 +21,7 @@ ifneq ($(EMULATOR),1)
|
|||||||
OBJS += timer.o
|
OBJS += timer.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
OBJS += usb_standard.o
|
||||||
OBJS += usb21_standard.o
|
OBJS += usb21_standard.o
|
||||||
OBJS += webusb.o
|
OBJS += webusb.o
|
||||||
OBJS += winusb.o
|
OBJS += winusb.o
|
||||||
|
@ -134,8 +134,10 @@ endif
|
|||||||
|
|
||||||
ifeq ($(MEMORY_PROTECT), 0)
|
ifeq ($(MEMORY_PROTECT), 0)
|
||||||
CFLAGS += -DMEMORY_PROTECT=0
|
CFLAGS += -DMEMORY_PROTECT=0
|
||||||
|
$(info MEMORY_PROTECT=0)
|
||||||
else
|
else
|
||||||
CFLAGS += -DMEMORY_PROTECT=1
|
CFLAGS += -DMEMORY_PROTECT=1
|
||||||
|
$(info MEMORY_PROTECT=1)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(DEBUG_RNG), 1)
|
ifeq ($(DEBUG_RNG), 1)
|
||||||
@ -159,7 +161,7 @@ flash: $(NAME).bin
|
|||||||
$(OPENOCD) -c "init; reset halt; flash write_image erase $(NAME).bin 0x8000000; exit"
|
$(OPENOCD) -c "init; reset halt; flash write_image erase $(NAME).bin 0x8000000; exit"
|
||||||
|
|
||||||
upload: sign
|
upload: sign
|
||||||
trezorctl firmware_update -f $(NAME).bin
|
trezorctl firmware_update -f $(NAME).bin -s
|
||||||
|
|
||||||
sign: $(NAME).bin
|
sign: $(NAME).bin
|
||||||
$(PYTHON) ../bootloader/firmware_sign.py -f $(NAME).bin
|
$(PYTHON) ../bootloader/firmware_sign.py -f $(NAME).bin
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <libopencm3/cm3/scb.h>
|
#include <libopencm3/cm3/scb.h>
|
||||||
|
|
||||||
#include "bootloader.h"
|
#include "bootloader.h"
|
||||||
|
#include "signatures.h"
|
||||||
#include "buttons.h"
|
#include "buttons.h"
|
||||||
#include "setup.h"
|
#include "setup.h"
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
@ -32,9 +33,9 @@
|
|||||||
#include "signatures.h"
|
#include "signatures.h"
|
||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
#include "rng.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];
|
char str[4][17];
|
||||||
for (int i = 0; i < 4; i++) {
|
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);
|
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();
|
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);
|
layoutDialog(&bmp_icon_warning, "Abort", "I'll take the risk", NULL, "WARNING!", NULL, "Unofficial firmware", "detected.", NULL, NULL);
|
||||||
|
|
||||||
do {
|
bool but = get_button_response();
|
||||||
delay(100000);
|
if (!but) { // no button was pressed -> halt
|
||||||
buttonUpdate();
|
show_halt("Unofficial firmware", "aborted.");
|
||||||
} while (!button.YesUp && !button.NoUp);
|
|
||||||
|
|
||||||
if (button.NoUp) {
|
|
||||||
show_halt(); // no button was pressed -> halt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
layoutFirmwareHash(hash);
|
layoutFirmwareFingerprint(hash);
|
||||||
|
|
||||||
do {
|
but = get_button_response();
|
||||||
delay(100000);
|
if (!but) { // no button was pressed -> halt
|
||||||
buttonUpdate();
|
show_halt("Unofficial firmware", "aborted.");
|
||||||
} while (!button.YesUp && !button.NoUp);
|
|
||||||
|
|
||||||
if (button.NoUp) {
|
|
||||||
show_halt(); // no button was pressed -> halt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// everything is OK, user pressed 2x Continue -> continue program
|
// 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
|
// zero out SRAM
|
||||||
memset_reg(_ram_start, _ram_end, 0);
|
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);
|
jump_to_firmware((const vector_table_t *) FLASH_PTR(FLASH_APP_START), signed_firmware);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool firmware_present(void)
|
static void bootloader_loop(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)
|
|
||||||
{
|
{
|
||||||
oledClear();
|
oledClear();
|
||||||
oledDrawBitmap(0, 0, &bmp_logo64);
|
oledDrawBitmap(0, 0, &bmp_logo64);
|
||||||
if (firmware_present()) {
|
if (firmware_present_new()) {
|
||||||
oledDrawStringCenter(90, 10, "TREZOR", FONT_STANDARD);
|
oledDrawStringCenter(90, 10, "TREZOR", FONT_STANDARD);
|
||||||
oledDrawStringCenter(90, 30, "Bootloader", FONT_STANDARD);
|
oledDrawStringCenter(90, 30, "Bootloader", FONT_STANDARD);
|
||||||
oledDrawStringCenter(90, 50, VERSTR(VERSION_MAJOR) "." VERSTR(VERSION_MINOR) "." VERSTR(VERSION_PATCH), 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();
|
oledRefresh();
|
||||||
|
|
||||||
usbLoop(firmware_present());
|
usbLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
@ -129,24 +115,31 @@ int main(void)
|
|||||||
oledInit();
|
oledInit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef APPVER
|
mpu_config_bootloader();
|
||||||
// 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);
|
|
||||||
|
|
||||||
if (firmware_present() && unpressed) {
|
#ifndef APPVER
|
||||||
|
bool left_pressed = (buttonRead() & BTN_PIN_NO) == 0;
|
||||||
|
|
||||||
|
if (firmware_present_new() && !left_pressed) {
|
||||||
|
|
||||||
oledClear();
|
oledClear();
|
||||||
oledDrawBitmap(40, 0, &bmp_logo64_empty);
|
oledDrawBitmap(40, 0, &bmp_logo64_empty);
|
||||||
oledRefresh();
|
oledRefresh();
|
||||||
|
|
||||||
uint8_t hash[32];
|
const image_header *hdr = (const image_header *)FLASH_PTR(FLASH_FWHEADER_START);
|
||||||
int signed_firmware = signatures_ok(hash);
|
|
||||||
|
uint8_t fingerprint[32];
|
||||||
|
int signed_firmware = signatures_new_ok(hdr, fingerprint);
|
||||||
if (SIG_OK != signed_firmware) {
|
if (SIG_OK != signed_firmware) {
|
||||||
show_unofficial_warning(hash);
|
show_unofficial_warning(fingerprint);
|
||||||
timer_init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
load_app(signed_firmware);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -21,20 +21,21 @@
|
|||||||
#define __BOOTLOADER_H__
|
#define __BOOTLOADER_H__
|
||||||
|
|
||||||
#define VERSION_MAJOR 1
|
#define VERSION_MAJOR 1
|
||||||
#define VERSION_MINOR 6
|
#define VERSION_MINOR 8
|
||||||
#define VERSION_PATCH 1
|
#define VERSION_PATCH 0
|
||||||
|
|
||||||
#define STR(X) #X
|
#define STR(X) #X
|
||||||
#define VERSTR(X) STR(X)
|
#define VERSTR(X) STR(X)
|
||||||
|
|
||||||
#define VERSION_MAJOR_CHAR "\x01"
|
#define VERSION_MAJOR_CHAR "\x01"
|
||||||
#define VERSION_MINOR_CHAR "\x06"
|
#define VERSION_MINOR_CHAR "\x08"
|
||||||
#define VERSION_PATCH_CHAR "\x01"
|
#define VERSION_PATCH_CHAR "\x00"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "memory.h"
|
|
||||||
|
|
||||||
void layoutFirmwareHash(const uint8_t *hash);
|
void show_halt(const char *line1, const char *line2);
|
||||||
bool firmware_present(void);
|
void layoutFirmwareFingerprint(const uint8_t *hash);
|
||||||
|
bool get_button_response(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
TOTALSIZE = 32768
|
||||||
|
MAXSIZE = TOTALSIZE - 32
|
||||||
|
|
||||||
fn = sys.argv[1]
|
fn = sys.argv[1]
|
||||||
fs = os.stat(fn).st_size
|
fs = os.stat(fn).st_size
|
||||||
if fs > 32768:
|
if fs > MAXSIZE:
|
||||||
raise Exception('bootloader has to be smaller than 32768 bytes')
|
raise Exception('bootloader has to be smaller than %d bytes (current size is %d)' % (MAXSIZE, fs))
|
||||||
with open(fn, 'ab') as f:
|
with open(fn, 'ab') as f:
|
||||||
f.write(b'\x00' * (32768 - fs))
|
f.write(b'\x00' * (TOTALSIZE - fs))
|
||||||
f.close()
|
f.close()
|
||||||
|
@ -7,10 +7,6 @@ import struct
|
|||||||
|
|
||||||
import ecdsa
|
import ecdsa
|
||||||
|
|
||||||
try:
|
|
||||||
raw_input
|
|
||||||
except:
|
|
||||||
raw_input = input
|
|
||||||
|
|
||||||
SLOTS = 3
|
SLOTS = 3
|
||||||
|
|
||||||
@ -22,8 +18,9 @@ pubkeys = {
|
|||||||
5: "047384c51ae81add0a523adbb186c91b906ffb64c2c765802bf26dbd13bdf12c319e80c2213a136c8ee03d7874fd22b70d68e7dee469decfbbb510ee9a460cda45",
|
5: "047384c51ae81add0a523adbb186c91b906ffb64c2c765802bf26dbd13bdf12c319e80c2213a136c8ee03d7874fd22b70d68e7dee469decfbbb510ee9a460cda45",
|
||||||
}
|
}
|
||||||
|
|
||||||
INDEXES_START = len("TRZR") + struct.calcsize("<I")
|
FWHEADER_SIZE = 1024
|
||||||
SIG_START = INDEXES_START + SLOTS + 1 + 52
|
SIGNATURES_START = 6 * 4 + 8 + 512
|
||||||
|
INDEXES_START = SIGNATURES_START + 3 * 64
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
@ -52,47 +49,83 @@ def parse_args():
|
|||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
def prepare(data):
|
def pad_to_size(data, size):
|
||||||
# Takes raw OR signed firmware and clean out metadata structure
|
if len(data) > size:
|
||||||
# This produces 'clean' data for signing
|
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":
|
# see memory.h for details
|
||||||
meta += data[4 : 4 + struct.calcsize("<I")]
|
|
||||||
|
|
||||||
|
def prepare_hashes(data):
|
||||||
|
# process chunks
|
||||||
|
start = 0
|
||||||
|
end = (64 - 1) * 1024
|
||||||
|
hashes = []
|
||||||
|
for i in range(16):
|
||||||
|
sector = data[start:end]
|
||||||
|
if len(sector) > 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:
|
else:
|
||||||
meta += struct.pack("<I", len(data)) # length of the code
|
print("HASHES NOT OK")
|
||||||
meta += b"\x00" * SLOTS # signature index #1-#3
|
|
||||||
meta += b"\x01" # flags
|
|
||||||
meta += b"\x00" * 52 # reserved
|
|
||||||
meta += b"\x00" * 64 * SLOTS # signature #1-#3
|
|
||||||
|
|
||||||
if data[:4] == b"TRZR":
|
|
||||||
# Replace existing header
|
def update_hashes_in_header(data):
|
||||||
out = meta + data[len(meta) :]
|
# Store hashes in the firmware header
|
||||||
|
data = bytearray(data)
|
||||||
|
o = 0
|
||||||
|
for h in prepare_hashes(data[FWHEADER_SIZE:]):
|
||||||
|
data[0x20 + o:0x20 + o + 32] = h
|
||||||
|
o += 32
|
||||||
|
return bytes(data)
|
||||||
|
|
||||||
|
|
||||||
|
def get_header(data, zero_signatures=False):
|
||||||
|
if not zero_signatures:
|
||||||
|
return data[:FWHEADER_SIZE]
|
||||||
else:
|
else:
|
||||||
# create data from meta + code
|
data = bytearray(data[:FWHEADER_SIZE])
|
||||||
out = meta + data
|
data[SIGNATURES_START : SIGNATURES_START + 3 * 64 + 3] = b"\x00" * (3 * 64 + 3)
|
||||||
|
return bytes(data)
|
||||||
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
def check_size(data):
|
||||||
|
size = struct.unpack("<L", data[12:16])[0]
|
||||||
|
assert size == len(data) - 1024
|
||||||
|
|
||||||
def check_signatures(data):
|
def check_signatures(data):
|
||||||
# Analyses given firmware and prints out
|
# Analyses given firmware and prints out
|
||||||
# status of included signatures
|
# status of included signatures
|
||||||
|
|
||||||
try:
|
indexes = [x for x in data[INDEXES_START : INDEXES_START + SLOTS]]
|
||||||
indexes = [ord(x) for x in data[INDEXES_START : INDEXES_START + SLOTS]]
|
|
||||||
except:
|
|
||||||
indexes = [x for x in data[INDEXES_START : INDEXES_START + SLOTS]]
|
|
||||||
|
|
||||||
to_sign = prepare(data)[256:] # without meta
|
to_sign = get_header(data, zero_signatures=True)
|
||||||
fingerprint = hashlib.sha256(to_sign).hexdigest()
|
fingerprint = hashlib.sha256(to_sign).hexdigest()
|
||||||
|
|
||||||
print("Firmware fingerprint:", fingerprint)
|
print("Firmware fingerprint:", fingerprint)
|
||||||
|
|
||||||
used = []
|
used = []
|
||||||
for x in range(SLOTS):
|
for x in range(SLOTS):
|
||||||
signature = data[SIG_START + 64 * x : SIG_START + 64 * x + 64]
|
signature = data[SIGNATURES_START + 64 * x : SIGNATURES_START + 64 * x + 64]
|
||||||
|
|
||||||
if indexes[x] == 0:
|
if indexes[x] == 0:
|
||||||
print("Slot #%d" % (x + 1), "is empty")
|
print("Slot #%d" % (x + 1), "is empty")
|
||||||
@ -118,25 +151,18 @@ def check_signatures(data):
|
|||||||
|
|
||||||
|
|
||||||
def modify(data, slot, index, signature):
|
def modify(data, slot, index, signature):
|
||||||
# Replace signature in data
|
data = bytearray(data)
|
||||||
|
# put index to data
|
||||||
# Put index to data
|
data[INDEXES_START + slot - 1] = index
|
||||||
data = (
|
# put signature to data
|
||||||
data[: INDEXES_START + slot - 1] + bytes([index]) + data[INDEXES_START + slot :]
|
data[SIGNATURES_START + 64 * (slot - 1) : SIGNATURES_START + 64 * slot] = signature
|
||||||
)
|
return bytes(data)
|
||||||
|
|
||||||
# Put signature to data
|
|
||||||
data = (
|
|
||||||
data[: SIG_START + 64 * (slot - 1)] + signature + data[SIG_START + 64 * slot :]
|
|
||||||
)
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def sign(data, is_pem):
|
def sign(data, is_pem):
|
||||||
# Ask for index and private key and signs the firmware
|
# Ask for index and private key and signs the firmware
|
||||||
|
|
||||||
slot = int(raw_input("Enter signature slot (1-%d): " % SLOTS))
|
slot = int(input("Enter signature slot (1-%d): " % SLOTS))
|
||||||
if slot < 1 or slot > SLOTS:
|
if slot < 1 or slot > SLOTS:
|
||||||
raise Exception("Invalid slot")
|
raise Exception("Invalid slot")
|
||||||
|
|
||||||
@ -145,28 +171,28 @@ def sign(data, is_pem):
|
|||||||
print("(blank private key removes the signature on given index)")
|
print("(blank private key removes the signature on given index)")
|
||||||
pem_key = ""
|
pem_key = ""
|
||||||
while True:
|
while True:
|
||||||
key = raw_input()
|
key = input()
|
||||||
pem_key += key + "\n"
|
pem_key += key + "\n"
|
||||||
if key == "":
|
if key == "":
|
||||||
break
|
break
|
||||||
if pem_key.strip() == "":
|
if pem_key.strip() == "":
|
||||||
# Blank key,let's remove existing signature from slot
|
# 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)
|
key = ecdsa.SigningKey.from_pem(pem_key)
|
||||||
else:
|
else:
|
||||||
print("Paste SECEXP (in hex) and press Enter:")
|
print("Paste SECEXP (in hex) and press Enter:")
|
||||||
print("(blank private key removes the signature on given index)")
|
print("(blank private key removes the signature on given index)")
|
||||||
secexp = raw_input()
|
secexp = input()
|
||||||
if secexp.strip() == "":
|
if secexp.strip() == "":
|
||||||
# Blank key,let's remove existing signature from slot
|
# 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(
|
key = ecdsa.SigningKey.from_secret_exponent(
|
||||||
secexp=int(secexp, 16),
|
secexp=int(secexp, 16),
|
||||||
curve=ecdsa.curves.SECP256k1,
|
curve=ecdsa.curves.SECP256k1,
|
||||||
hashfunc=hashlib.sha256,
|
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
|
# Locate proper index of current signing key
|
||||||
pubkey = "04" + key.get_verifying_key().to_string().hex()
|
pubkey = "04" + key.get_verifying_key().to_string().hex()
|
||||||
@ -207,20 +233,21 @@ def main(args):
|
|||||||
data = open(args.path, "rb").read()
|
data = open(args.path, "rb").read()
|
||||||
assert len(data) % 4 == 0
|
assert len(data) % 4 == 0
|
||||||
|
|
||||||
if data[:4] != b"TRZR":
|
if data[:4] != b"TRZF":
|
||||||
print("Metadata has been added...")
|
|
||||||
data = prepare(data)
|
|
||||||
|
|
||||||
if data[:4] != b"TRZR":
|
|
||||||
raise Exception("Firmware header expected")
|
raise Exception("Firmware header expected")
|
||||||
|
|
||||||
|
data = update_hashes_in_header(data)
|
||||||
|
|
||||||
print("Firmware size %d bytes" % len(data))
|
print("Firmware size %d bytes" % len(data))
|
||||||
|
|
||||||
|
check_size(data)
|
||||||
check_signatures(data)
|
check_signatures(data)
|
||||||
|
check_hashes(data)
|
||||||
|
|
||||||
if args.sign:
|
if args.sign:
|
||||||
data = sign(data, args.pem)
|
data = sign(data, args.pem)
|
||||||
check_signatures(data)
|
check_signatures(data)
|
||||||
|
check_hashes(data)
|
||||||
|
|
||||||
fp = open(args.path, "wb")
|
fp = open(args.path, "wb")
|
||||||
fp.write(data)
|
fp.write(data)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
from __future__ import print_function
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -7,10 +6,7 @@ import ecdsa
|
|||||||
from binascii import hexlify, unhexlify
|
from binascii import hexlify, unhexlify
|
||||||
|
|
||||||
print('master secret:', end='')
|
print('master secret:', end='')
|
||||||
try:
|
h = input()
|
||||||
h = raw_input()
|
|
||||||
except:
|
|
||||||
h = input()
|
|
||||||
if h:
|
if h:
|
||||||
h = unhexlify(h).encode('ascii')
|
h = unhexlify(h).encode('ascii')
|
||||||
else:
|
else:
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "signatures.h"
|
#include "signatures.h"
|
||||||
@ -25,6 +24,11 @@
|
|||||||
#include "secp256k1.h"
|
#include "secp256k1.h"
|
||||||
#include "sha2.h"
|
#include "sha2.h"
|
||||||
#include "bootloader.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
|
#define PUBKEYS 5
|
||||||
|
|
||||||
@ -38,19 +42,45 @@ static const uint8_t * const pubkey[PUBKEYS] = {
|
|||||||
|
|
||||||
#define SIGNATURES 3
|
#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 uint32_t codelen = *((const uint32_t *)FLASH_META_CODELEN);
|
||||||
const uint8_t sigindex1 = *((const uint8_t *)FLASH_META_SIGINDEX1);
|
const uint8_t sigindex1 = *((const uint8_t *)FLASH_META_SIGINDEX1);
|
||||||
const uint8_t sigindex2 = *((const uint8_t *)FLASH_META_SIGINDEX2);
|
const uint8_t sigindex2 = *((const uint8_t *)FLASH_META_SIGINDEX2);
|
||||||
const uint8_t sigindex3 = *((const uint8_t *)FLASH_META_SIGINDEX3);
|
const uint8_t sigindex3 = *((const uint8_t *)FLASH_META_SIGINDEX3);
|
||||||
|
|
||||||
uint8_t hash[32];
|
if (codelen > FLASH_APP_LEN) {
|
||||||
sha256_Raw((const uint8_t *)FLASH_APP_START, codelen, hash);
|
return false;
|
||||||
if (store_hash) {
|
|
||||||
memcpy(store_hash, hash, 32);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (sigindex1 < 1 || sigindex1 > PUBKEYS) return SIG_FAIL; // invalid index
|
||||||
if (sigindex2 < 1 || sigindex2 > 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
|
if (sigindex3 < 1 || sigindex3 > PUBKEYS) return SIG_FAIL; // invalid index
|
||||||
@ -71,3 +101,93 @@ int signatures_ok(uint8_t *store_hash)
|
|||||||
|
|
||||||
return SIG_OK;
|
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;
|
||||||
|
}
|
||||||
|
@ -20,9 +20,50 @@
|
|||||||
#ifndef __SIGNATURES_H__
|
#ifndef __SIGNATURES_H__
|
||||||
#define __SIGNATURES_H__
|
#define __SIGNATURES_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern const uint32_t FIRMWARE_MAGIC_OLD; // TRZR
|
||||||
|
extern const uint32_t FIRMWARE_MAGIC_NEW; // TRZF
|
||||||
|
|
||||||
#define SIG_OK 0x5A3CA5C3
|
#define SIG_OK 0x5A3CA5C3
|
||||||
#define SIG_FAIL 0x00000000
|
#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
|
#endif
|
||||||
|
465
bootloader/usb.c
465
bootloader/usb.c
@ -34,91 +34,15 @@
|
|||||||
#include "ecdsa.h"
|
#include "ecdsa.h"
|
||||||
#include "secp256k1.h"
|
#include "secp256k1.h"
|
||||||
#include "memzero.h"
|
#include "memzero.h"
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
#include "usb21_standard.h"
|
#include "usb21_standard.h"
|
||||||
#include "webusb.h"
|
#include "webusb.h"
|
||||||
#include "winusb.h"
|
#include "winusb.h"
|
||||||
|
|
||||||
#define FIRMWARE_MAGIC "TRZR"
|
#include "usb_desc.h"
|
||||||
|
#include "usb_send.h"
|
||||||
#define USB_INTERFACE_INDEX_MAIN 0
|
#include "usb_erase.h"
|
||||||
|
|
||||||
#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",
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
STATE_READY,
|
STATE_READY,
|
||||||
@ -130,121 +54,75 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static uint32_t flash_pos = 0, flash_len = 0;
|
static uint32_t flash_pos = 0, flash_len = 0;
|
||||||
|
static uint32_t chunk_idx = 0;
|
||||||
static char flash_state = STATE_READY;
|
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];
|
uint32_t offset = (chunk_idx == 0) ? FLASH_FWHEADER_LEN : 0;
|
||||||
memzero(response, sizeof(response));
|
uint32_t chunk_pos = flash_pos % FW_CHUNK_SIZE;
|
||||||
// response: Success message (id 2), payload len 0
|
if (chunk_pos == 0) {
|
||||||
memcpy(response,
|
chunk_pos = FW_CHUNK_SIZE;
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
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();
|
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)
|
static void rx_callback(usbd_device *dev, uint8_t ep)
|
||||||
{
|
{
|
||||||
(void)ep;
|
(void)ep;
|
||||||
|
static uint16_t msg_id = 0xFFFF;
|
||||||
static uint8_t buf[64] __attribute__((aligned(4)));
|
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 wi;
|
||||||
|
static int old_was_signed;
|
||||||
|
|
||||||
if ( usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_OUT, buf, 64) != 64) return;
|
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
|
// struct.unpack(">HL") => msg, size
|
||||||
msg_id = (buf[3] << 8) + buf[4];
|
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) {
|
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)
|
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);
|
layoutDialog(&bmp_icon_question, "Cancel", "Confirm", NULL, "Do you really want to", "wipe the device?", NULL, "All data will be lost.", NULL, NULL);
|
||||||
do {
|
bool but = get_button_response();
|
||||||
delay(100000);
|
if (but) {
|
||||||
buttonUpdate();
|
erase_storage_code_progress();
|
||||||
} 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();
|
|
||||||
flash_state = STATE_END;
|
flash_state = STATE_END;
|
||||||
layoutDialog(&bmp_icon_ok, NULL, NULL, NULL, "Device", "successfully wiped.", NULL, "You may now", "unplug your TREZOR.", NULL);
|
layoutDialog(&bmp_icon_ok, NULL, NULL, NULL, "Device", "successfully wiped.", NULL, "You may now", "unplug your TREZOR.", NULL);
|
||||||
send_msg_success(dev);
|
send_msg_success(dev);
|
||||||
@ -311,59 +171,31 @@ static void rx_callback(usbd_device *dev, uint8_t ep)
|
|||||||
|
|
||||||
if (flash_state == STATE_OPEN) {
|
if (flash_state == STATE_OPEN) {
|
||||||
if (msg_id == 0x0006) { // FirmwareErase message (id 6)
|
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);
|
layoutDialog(&bmp_icon_question, "Abort", "Continue", NULL, "Install new", "firmware?", NULL, "Never do this without", "your recovery card!", NULL);
|
||||||
do {
|
proceed = get_button_response();
|
||||||
delay(100000);
|
} else {
|
||||||
buttonUpdate();
|
proceed = true;
|
||||||
} while (!button.YesUp && !button.NoUp);
|
|
||||||
}
|
}
|
||||||
if (brand_new_firmware || button.YesUp) {
|
if (proceed) {
|
||||||
// check whether current firmware is signed
|
// check whether the current firmware is signed (old or new method)
|
||||||
if (!brand_new_firmware && SIG_OK == signatures_ok(NULL)) {
|
if (firmware_present_new()) {
|
||||||
old_was_unsigned = false;
|
const image_header *hdr = (const image_header *)FLASH_PTR(FLASH_FWHEADER_START);
|
||||||
// backup metadata
|
old_was_signed = signatures_new_ok(hdr, NULL) & check_firmware_hashes(hdr);
|
||||||
backup_metadata(meta_backup);
|
} else if (firmware_present_old()) {
|
||||||
|
old_was_signed = signatures_old_ok();
|
||||||
} else {
|
} else {
|
||||||
old_was_unsigned = true;
|
old_was_signed = SIG_FAIL;
|
||||||
}
|
}
|
||||||
flash_wait_for_last_operation();
|
erase_code_progress();
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
send_msg_success(dev);
|
send_msg_success(dev);
|
||||||
flash_state = STATE_FLASHSTART;
|
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;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -374,42 +206,47 @@ static void rx_callback(usbd_device *dev, uint8_t ep)
|
|||||||
if (buf[9] != 0x0a) { // invalid contents
|
if (buf[9] != 0x0a) { // invalid contents
|
||||||
send_msg_failure(dev);
|
send_msg_failure(dev);
|
||||||
flash_state = STATE_END;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
// read payload length
|
// read payload length
|
||||||
const uint8_t *p = buf + 10;
|
const uint8_t *p = buf + 10;
|
||||||
flash_len = readprotobufint(&p);
|
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);
|
send_msg_failure(dev);
|
||||||
flash_state = STATE_END;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
// check firmware magic
|
// check firmware magic
|
||||||
if (memcmp(p, FIRMWARE_MAGIC, 4) != 0) {
|
if (memcmp(p, &FIRMWARE_MAGIC_NEW, 4) != 0) {
|
||||||
send_msg_failure(dev);
|
send_msg_failure(dev);
|
||||||
flash_state = STATE_END;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
memzero(FW_HEADER, sizeof(FW_HEADER));
|
||||||
|
memzero(FW_CHUNK, sizeof(FW_CHUNK));
|
||||||
flash_state = STATE_FLASHING;
|
flash_state = STATE_FLASHING;
|
||||||
p += 4; // Don't flash firmware header yet.
|
flash_pos = 0;
|
||||||
flash_pos = 4;
|
chunk_idx = 0;
|
||||||
wi = 0;
|
w = 0;
|
||||||
flash_unlock();
|
|
||||||
while (p < buf + 64) {
|
while (p < buf + 64) {
|
||||||
towrite[wi] = *p;
|
w = (w >> 8) | (*p << 24); // assign byte to first byte of uint32_t w
|
||||||
wi++;
|
wi++;
|
||||||
if (wi == 4) {
|
if (wi == 4) {
|
||||||
const uint32_t *w = (uint32_t *)towrite;
|
FW_HEADER[flash_pos / 4] = w;
|
||||||
flash_program_word(FLASH_META_START + flash_pos, *w);
|
|
||||||
flash_pos += 4;
|
flash_pos += 4;
|
||||||
wi = 0;
|
wi = 0;
|
||||||
}
|
}
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
flash_lock();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -419,35 +256,44 @@ static void rx_callback(usbd_device *dev, uint8_t ep)
|
|||||||
if (buf[0] != '?') { // invalid contents
|
if (buf[0] != '?') { // invalid contents
|
||||||
send_msg_failure(dev);
|
send_msg_failure(dev);
|
||||||
flash_state = STATE_END;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
const uint8_t *p = buf + 1;
|
|
||||||
|
static uint8_t flash_anim = 0;
|
||||||
if (flash_anim % 32 == 4) {
|
if (flash_anim % 32 == 4) {
|
||||||
layoutProgress("INSTALLING ... Please wait", 1000 * flash_pos / flash_len);
|
layoutProgress("INSTALLING ... Please wait", 1000 * flash_pos / flash_len);
|
||||||
}
|
}
|
||||||
flash_anim++;
|
flash_anim++;
|
||||||
flash_unlock();
|
|
||||||
|
const uint8_t *p = buf + 1;
|
||||||
while (p < buf + 64 && flash_pos < flash_len) {
|
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++;
|
wi++;
|
||||||
if (wi == 4) {
|
if (wi == 4) {
|
||||||
const uint32_t *w = (const uint32_t *)towrite;
|
if (flash_pos < FLASH_FWHEADER_LEN) {
|
||||||
if (flash_pos < FLASH_META_DESC_LEN) {
|
FW_HEADER[flash_pos / 4] = w;
|
||||||
flash_program_word(FLASH_META_START + flash_pos, *w); // the first 256 bytes of firmware is metadata descriptor
|
|
||||||
} else {
|
} 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;
|
flash_pos += 4;
|
||||||
wi = 0;
|
wi = 0;
|
||||||
|
// finished the whole chunk
|
||||||
|
if (flash_pos % FW_CHUNK_SIZE == 0) {
|
||||||
|
check_and_write_chunk();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
flash_lock();
|
|
||||||
// flashing done
|
// flashing done
|
||||||
if (flash_pos == flash_len) {
|
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;
|
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);
|
send_msg_buttonrequest_firmwarecheck(dev);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -458,55 +304,68 @@ static void rx_callback(usbd_device *dev, uint8_t ep)
|
|||||||
|
|
||||||
if (flash_state == STATE_CHECK) {
|
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)
|
if (msg_id != 0x001B) { // ButtonAck message (id 27)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint8_t hash[32];
|
uint8_t hash[32];
|
||||||
sha256_Raw(FLASH_PTR(FLASH_APP_START), flash_len - FLASH_META_DESC_LEN, hash);
|
compute_firmware_fingerprint(hdr, hash);
|
||||||
layoutFirmwareHash(hash);
|
layoutFirmwareFingerprint(hash);
|
||||||
do {
|
hash_check_ok = get_button_response();
|
||||||
delay(100000);
|
} else {
|
||||||
buttonUpdate();
|
hash_check_ok = true;
|
||||||
} while (!button.YesUp && !button.NoUp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hash_check_ok = brand_new_firmware || button.YesUp;
|
|
||||||
|
|
||||||
layoutProgress("INSTALLING ... Please wait", 1000);
|
layoutProgress("INSTALLING ... Please wait", 1000);
|
||||||
uint8_t flags = *FLASH_PTR(FLASH_META_FLAGS);
|
|
||||||
// wipe storage if:
|
// wipe storage if:
|
||||||
// 0) there was no firmware
|
// 1) old firmware was unsigned or not present
|
||||||
// 1) old firmware was unsigned
|
// 2) signatures are not OK
|
||||||
// 2) firmware restore flag isn't set
|
// 3) hashes are not OK
|
||||||
// 3) signatures are not ok
|
if (SIG_OK != old_was_signed || SIG_OK != signatures_new_ok(hdr, NULL) || SIG_OK != check_firmware_hashes(hdr)) {
|
||||||
if (brand_new_firmware || old_was_unsigned || (flags & 0x01) == 0 || SIG_OK != signatures_ok(NULL)) {
|
// erase storage
|
||||||
memzero(meta_backup, sizeof(meta_backup));
|
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
|
flash_wait_for_last_operation();
|
||||||
memcpy(meta_backup, (void *)FLASH_META_START, FLASH_META_DESC_LEN);
|
flash_clear_status_flags();
|
||||||
// write "TRZR" in header only when hash was confirmed
|
flash_unlock();
|
||||||
|
// write firmware header only when hash was confirmed
|
||||||
if (hash_check_ok) {
|
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 {
|
} 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
flash_wait_for_last_operation();
|
||||||
// no need to erase, because we are not changing any already flashed byte.
|
flash_lock();
|
||||||
restore_metadata(meta_backup);
|
|
||||||
memzero(meta_backup, sizeof(meta_backup));
|
|
||||||
|
|
||||||
flash_state = STATE_END;
|
flash_state = STATE_END;
|
||||||
if (hash_check_ok) {
|
if (hash_check_ok) {
|
||||||
layoutDialog(&bmp_icon_ok, NULL, NULL, NULL, "New firmware", "successfully installed.", NULL, "You may now", "unplug your TREZOR.", NULL);
|
layoutDialog(&bmp_icon_ok, NULL, NULL, NULL, "New firmware", "successfully installed.", NULL, "You may now", "unplug your TREZOR.", NULL);
|
||||||
send_msg_success(dev);
|
send_msg_success(dev);
|
||||||
|
shutdown();
|
||||||
} else {
|
} else {
|
||||||
layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, "Firmware installation", "aborted.", NULL, "You need to repeat", "the procedure with", "the correct firmware.");
|
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);
|
send_msg_failure(dev);
|
||||||
|
shutdown();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_config(usbd_device *dev, uint16_t wValue)
|
static void set_config(usbd_device *dev, uint16_t wValue)
|
||||||
@ -531,7 +390,7 @@ static const struct usb_bos_descriptor bos_descriptor = {
|
|||||||
.capabilities = capabilities
|
.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_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);
|
usbd_register_set_config_callback(usbd_dev, set_config);
|
||||||
@ -540,7 +399,7 @@ void usbInit(void)
|
|||||||
winusb_setup(usbd_dev, USB_INTERFACE_INDEX_MAIN);
|
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;
|
static bool btn_left = false, btn_right = false, btn_final = false;
|
||||||
if (btn_final) {
|
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();
|
usbInit();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
usbd_poll(usbd_dev);
|
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();
|
checkButtons();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,6 @@
|
|||||||
#ifndef __USB_H__
|
#ifndef __USB_H__
|
||||||
#define __USB_H__
|
#define __USB_H__
|
||||||
|
|
||||||
void usbLoop(bool firmware_present);
|
void usbLoop(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
75
bootloader/usb_desc.h
Normal file
75
bootloader/usb_desc.h
Normal file
@ -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",
|
||||||
|
};
|
44
bootloader/usb_erase.h
Normal file
44
bootloader/usb_erase.h
Normal file
@ -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();
|
||||||
|
}
|
86
bootloader/usb_send.h
Normal file
86
bootloader/usb_send.h
Normal file
@ -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) {}
|
||||||
|
}
|
@ -29,10 +29,9 @@ uint16_t buttonRead(void) {
|
|||||||
|
|
||||||
void buttonUpdate()
|
void buttonUpdate()
|
||||||
{
|
{
|
||||||
uint16_t state;
|
|
||||||
static uint16_t last_state = BTN_PIN_YES | BTN_PIN_NO;
|
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 ((state & BTN_PIN_YES) == 0) { // Yes button is down
|
||||||
if ((last_state & BTN_PIN_YES) == 0) { // last Yes was down
|
if ((last_state & BTN_PIN_YES) == 0) { // last Yes was down
|
||||||
|
@ -128,8 +128,7 @@ void svc_flash_program(uint32_t size) {
|
|||||||
}
|
}
|
||||||
void svc_flash_erase_sector(uint16_t sector) {
|
void svc_flash_erase_sector(uint16_t sector) {
|
||||||
assert (!flash_locked);
|
assert (!flash_locked);
|
||||||
assert (sector >= FLASH_META_SECTOR_FIRST &&
|
assert (sector >= FLASH_STORAGE_SECTOR_FIRST && sector <= FLASH_STORAGE_SECTOR_LAST);
|
||||||
sector <= FLASH_META_SECTOR_LAST);
|
|
||||||
flash_erase_sector(sector, 3);
|
flash_erase_sector(sector, 3);
|
||||||
}
|
}
|
||||||
uint32_t svc_flash_lock(void) {
|
uint32_t svc_flash_lock(void) {
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
Version 1.8.0
|
Version 1.8.0
|
||||||
* Stable release, optional update
|
* 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
|
Version 1.7.3
|
||||||
* Stable release, optional update
|
* Stable release, optional update
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
APPVER = 1.0.0
|
APPVER = 1.8.0
|
||||||
|
|
||||||
NAME = trezor
|
NAME = trezor
|
||||||
|
|
||||||
@ -8,6 +8,7 @@ else
|
|||||||
OBJS += usb.o
|
OBJS += usb.o
|
||||||
OBJS += bl_check.o
|
OBJS += bl_check.o
|
||||||
OBJS += otp.o
|
OBJS += otp.o
|
||||||
|
OBJS += header.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
OBJS += u2f.o
|
OBJS += u2f.o
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
#include "gettext.h"
|
#include "gettext.h"
|
||||||
#include "util.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 (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, "\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
|
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, "\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, "\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, "\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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,12 +197,15 @@ static secbool config_get_uint32(uint16_t key, uint32_t *value)
|
|||||||
return sectrue;
|
return sectrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define FLASH_META_START 0x08008000
|
||||||
|
#define FLASH_META_LEN 0x100
|
||||||
|
|
||||||
static secbool config_upgrade_v10(void)
|
static secbool config_upgrade_v10(void)
|
||||||
{
|
{
|
||||||
#define OLD_STORAGE_SIZE(last_member) (((offsetof(Storage, last_member) + pb_membersize(Storage, last_member)) + 3) & ~3)
|
#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 ||
|
if (memcmp(FLASH_PTR(FLASH_META_START), &META_MAGIC_V10, sizeof(META_MAGIC_V10)) != 0 ||
|
||||||
memcmp(FLASH_PTR(FLASH_STORAGE_START), &CONFIG_MAGIC_V10, sizeof(CONFIG_MAGIC_V10)) != 0) {
|
memcmp(FLASH_PTR(FLASH_META_START + FLASH_META_LEN), &CONFIG_MAGIC_V10, sizeof(CONFIG_MAGIC_V10)) != 0) {
|
||||||
// wrong magic
|
// wrong magic
|
||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
@ -210,8 +213,8 @@ static secbool config_upgrade_v10(void)
|
|||||||
Storage config __attribute__((aligned(4)));
|
Storage config __attribute__((aligned(4)));
|
||||||
_Static_assert((sizeof(config) & 3) == 0, "storage unaligned");
|
_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_uuid, FLASH_PTR(FLASH_META_START + FLASH_META_LEN + 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, FLASH_PTR(FLASH_META_START + FLASH_META_LEN + sizeof(CONFIG_MAGIC_V10) + sizeof(config_uuid)), sizeof(config));
|
||||||
|
|
||||||
// version 1: since 1.0.0
|
// version 1: since 1.0.0
|
||||||
// version 2: since 1.2.1
|
// version 2: since 1.2.1
|
||||||
@ -252,7 +255,7 @@ static secbool config_upgrade_v10(void)
|
|||||||
|
|
||||||
// Erase newly added fields.
|
// Erase newly added fields.
|
||||||
if (old_config_size != sizeof(Storage)) {
|
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;
|
const uint32_t FLASH_STORAGE_PINAREA = FLASH_META_START + 0x4000;
|
||||||
@ -294,6 +297,7 @@ static secbool config_upgrade_v10(void)
|
|||||||
if (config.has_pin) {
|
if (config.has_pin) {
|
||||||
storage_change_pin(PIN_EMPTY, pin_to_int(config.pin));
|
storage_change_pin(PIN_EMPTY, pin_to_int(config.pin));
|
||||||
}
|
}
|
||||||
|
|
||||||
while (pin_wait != 0) {
|
while (pin_wait != 0) {
|
||||||
storage_pin_fails_increase();
|
storage_pin_fails_increase();
|
||||||
pin_wait >>= 1;
|
pin_wait >>= 1;
|
||||||
@ -351,17 +355,28 @@ static secbool config_upgrade_v10(void)
|
|||||||
|
|
||||||
void config_init(void)
|
void config_init(void)
|
||||||
{
|
{
|
||||||
|
char oldTiny = usbTiny(1);
|
||||||
|
|
||||||
config_upgrade_v10();
|
config_upgrade_v10();
|
||||||
|
|
||||||
storage_init(&protectPinUiCallback, HW_ENTROPY_DATA, HW_ENTROPY_LEN);
|
storage_init(&protectPinUiCallback, HW_ENTROPY_DATA, HW_ENTROPY_LEN);
|
||||||
memzero(HW_ENTROPY_DATA, sizeof(HW_ENTROPY_DATA));
|
memzero(HW_ENTROPY_DATA, sizeof(HW_ENTROPY_DATA));
|
||||||
|
|
||||||
uint16_t len = 0;
|
// Auto-unlock storage if no PIN is set.
|
||||||
if (sectrue == storage_get(KEY_UUID, config_uuid, sizeof(config_uuid), &len) && len == sizeof(config_uuid)) {
|
if (storage_is_unlocked() == secfalse && storage_has_pin() == secfalse) {
|
||||||
data2hex(config_uuid, sizeof(config_uuid), config_uuid_str);
|
storage_unlock(PIN_EMPTY);
|
||||||
} else {
|
|
||||||
config_wipe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
void session_clear(bool lock)
|
||||||
@ -709,9 +724,12 @@ bool config_containsMnemonic(const char *mnemonic)
|
|||||||
/* Check whether pin matches storage. The pin must be
|
/* Check whether pin matches storage. The pin must be
|
||||||
* a null-terminated string with at most 9 characters.
|
* 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)
|
bool config_hasPin(void)
|
||||||
@ -726,7 +744,9 @@ bool config_changePin(const char *old_pin, const char *new_pin)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char oldTiny = usbTiny(1);
|
||||||
secbool ret = storage_change_pin(pin_to_int(old_pin), new_pin_int);
|
secbool ret = storage_change_pin(pin_to_int(old_pin), new_pin_int);
|
||||||
|
usbTiny(oldTiny);
|
||||||
|
|
||||||
#if DEBUG_LINK
|
#if DEBUG_LINK
|
||||||
if (sectrue == ret) {
|
if (sectrue == ret) {
|
||||||
@ -897,8 +917,12 @@ void config_setAutoLockDelayMs(uint32_t auto_lock_delay_ms)
|
|||||||
|
|
||||||
void config_wipe(void)
|
void config_wipe(void)
|
||||||
{
|
{
|
||||||
|
char oldTiny = usbTiny(1);
|
||||||
storage_wipe();
|
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));
|
random_buffer((uint8_t *)config_uuid, sizeof(config_uuid));
|
||||||
data2hex(config_uuid, sizeof(config_uuid), config_uuid_str);
|
data2hex(config_uuid, sizeof(config_uuid), config_uuid_str);
|
||||||
autoLockDelayMsCached = secfalse;
|
autoLockDelayMsCached = secfalse;
|
||||||
|
@ -120,9 +120,8 @@ bool config_dumpNode(HDNodeType *node);
|
|||||||
bool config_getPin(char *dest, uint16_t dest_size);
|
bool config_getPin(char *dest, uint16_t dest_size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool config_containsPin(const char *pin);
|
bool config_unlock(const char *pin);
|
||||||
bool config_hasPin(void);
|
bool config_hasPin(void);
|
||||||
void config_setPin(const char *pin);
|
|
||||||
bool config_changePin(const char *old_pin, const char *new_pin);
|
bool config_changePin(const char *old_pin, const char *new_pin);
|
||||||
bool session_isUnlocked(void);
|
bool session_isUnlocked(void);
|
||||||
|
|
||||||
|
@ -377,6 +377,9 @@ static void layoutEthereumFee(const uint8_t *value, uint32_t value_len,
|
|||||||
char tx_value[32];
|
char tx_value[32];
|
||||||
char gas_value[32];
|
char gas_value[32];
|
||||||
|
|
||||||
|
memzero(tx_value, sizeof(tx_value));
|
||||||
|
memzero(gas_value, sizeof(gas_value));
|
||||||
|
|
||||||
memzero(pad_val, sizeof(pad_val));
|
memzero(pad_val, sizeof(pad_val));
|
||||||
memcpy(pad_val + (32 - gas_price_len), gas_price, gas_price_len);
|
memcpy(pad_val + (32 - gas_price_len), gas_price, gas_price_len);
|
||||||
bn_read_be(pad_val, &val);
|
bn_read_be(pad_val, &val);
|
||||||
|
33
firmware/header.S
Normal file
33
firmware/header.S
Normal file
@ -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:
|
@ -128,8 +128,8 @@ static const char *address_n_str(const uint32_t *address_n, size_t address_n_cou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Path: m" / i '
|
// "Path: m" / i '
|
||||||
static char address_str[7 + 8 * (1 + 9 + 1) + 1];
|
static char address_str[7 + 8 * (1 + 10 + 1) + 1];
|
||||||
char *c = address_str + sizeof(address_str) - 1;
|
char *c = address_str + sizeof(address_str) - 1;
|
||||||
|
|
||||||
*c = 0; c--;
|
*c = 0; 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.
|
// Convert wait to secstr string.
|
||||||
char secstrbuf[] = _("________0 seconds");
|
char secstrbuf[] = _("________0 seconds");
|
||||||
@ -163,7 +163,7 @@ secbool protectPinUiCallback(uint32_t wait, uint32_t progress)
|
|||||||
secstrbuf[16] = 0;
|
secstrbuf[16] = 0;
|
||||||
}
|
}
|
||||||
oledClear();
|
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, 2 * 9, _("Please wait"), FONT_STANDARD);
|
||||||
oledDrawStringCenter(OLED_WIDTH / 2, 3 * 9, secstr, FONT_STANDARD);
|
oledDrawStringCenter(OLED_WIDTH / 2, 3 * 9, secstr, FONT_STANDARD);
|
||||||
oledDrawStringCenter(OLED_WIDTH / 2, 4 * 9, _("to continue ..."), 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_unlock(pin);
|
||||||
bool ret = config_containsPin(pin);
|
|
||||||
usbTiny(0);
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
fsm_sendFailure(FailureType_Failure_PinInvalid, NULL);
|
fsm_sendFailure(FailureType_Failure_PinInvalid, NULL);
|
||||||
}
|
}
|
||||||
@ -225,6 +223,18 @@ bool protectChangePin(bool removal)
|
|||||||
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
|
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
|
||||||
return false;
|
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));
|
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);
|
bool ret = config_changePin(old_pin, new_pin);
|
||||||
usbTiny(0);
|
|
||||||
memzero(old_pin, sizeof(old_pin));
|
memzero(old_pin, sizeof(old_pin));
|
||||||
memzero(new_pin, sizeof(new_pin));
|
memzero(new_pin, sizeof(new_pin));
|
||||||
if (ret == false) {
|
if (ret == false) {
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
#include "secbool.h"
|
#include "secbool.h"
|
||||||
|
|
||||||
bool protectButton(ButtonRequestType type, bool confirm_only);
|
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 protectPin(bool use_cached);
|
||||||
bool protectChangePin(bool removal);
|
bool protectChangePin(bool removal);
|
||||||
bool protectPassphrase(void);
|
bool protectPassphrase(void);
|
||||||
|
@ -21,10 +21,7 @@
|
|||||||
#define __TREZOR_H__
|
#define __TREZOR_H__
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "version.h"
|
||||||
#define VERSION_MAJOR 1
|
|
||||||
#define VERSION_MINOR 8
|
|
||||||
#define VERSION_PATCH 0
|
|
||||||
|
|
||||||
#define STR(X) #X
|
#define STR(X) #X
|
||||||
#define VERSTR(X) STR(X)
|
#define VERSTR(X) STR(X)
|
||||||
|
@ -175,6 +175,8 @@ void u2fhid_init_cmd(const U2FHID_FRAME *f) {
|
|||||||
|
|
||||||
void u2fhid_read_start(const U2FHID_FRAME *f) {
|
void u2fhid_read_start(const U2FHID_FRAME *f) {
|
||||||
U2F_ReadBuffer readbuffer;
|
U2F_ReadBuffer readbuffer;
|
||||||
|
memzero(&readbuffer, sizeof(readbuffer));
|
||||||
|
|
||||||
if (!(f->type & TYPE_INIT)) {
|
if (!(f->type & TYPE_INIT)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -259,6 +259,8 @@ static int hid_control_request(usbd_device *dev, struct usb_setup_data *req, uin
|
|||||||
(void)complete;
|
(void)complete;
|
||||||
(void)dev;
|
(void)dev;
|
||||||
|
|
||||||
|
wait_random();
|
||||||
|
|
||||||
if ((req->bmRequestType != 0x81) ||
|
if ((req->bmRequestType != 0x81) ||
|
||||||
(req->bRequest != USB_REQ_GET_DESCRIPTOR) ||
|
(req->bRequest != USB_REQ_GET_DESCRIPTOR) ||
|
||||||
(req->wValue != 0x2200))
|
(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");
|
debugLog(0, "", "hid_control_request u2f");
|
||||||
*buf = (uint8_t *)hid_report_descriptor_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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
7
firmware/version.h
Normal file
7
firmware/version.h
Normal file
@ -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
|
4
memory.c
4
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)) {
|
if (((FLASH_OPTION_BYTES_1 & 0xFFEC) == 0xCCEC) && ((FLASH_OPTION_BYTES_2 & 0xFFF) == 0xFFC) && (FLASH_OPTCR == 0x0FFCCCED)) {
|
||||||
return; // already set up correctly - bail out
|
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();
|
flash_unlock_option_bytes();
|
||||||
// Section 2.8.6 Flash option control register (FLASH_OPTCR)
|
// Section 2.8.6 Flash option control register (FLASH_OPTCR)
|
||||||
// Bits 31:28 Reserved, must be kept cleared.
|
// Bits 31:28 Reserved, must be kept cleared.
|
||||||
|
81
memory.h
81
memory.h
@ -24,43 +24,37 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
flash memory layout:
|
Flash memory layout:
|
||||||
|
|
||||||
name | range | size | function
|
name | range | size | function
|
||||||
-----------+-------------------------+---------+------------------
|
-----------+-------------------------+---------+------------------
|
||||||
Sector 0 | 0x08000000 - 0x08003FFF | 16 KiB | bootloader code
|
Sector 0 | 0x08000000 - 0x08003FFF | 16 KiB | bootloader
|
||||||
Sector 1 | 0x08004000 - 0x08007FFF | 16 KiB | bootloader code
|
Sector 1 | 0x08004000 - 0x08007FFF | 16 KiB | bootloader
|
||||||
-----------+-------------------------+---------+------------------
|
-----------+-------------------------+---------+------------------
|
||||||
Sector 2 | 0x08008000 - 0x0800BFFF | 16 KiB | metadata area
|
Sector 2 | 0x08008000 - 0x0800BFFF | 16 KiB | storage area
|
||||||
Sector 3 | 0x0800C000 - 0x0800FFFF | 16 KiB | metadata area
|
Sector 3 | 0x0800C000 - 0x0800FFFF | 16 KiB | storage area
|
||||||
-----------+-------------------------+---------+------------------
|
-----------+-------------------------+---------+------------------
|
||||||
Sector 4 | 0x08010000 - 0x0801FFFF | 64 KiB | application code
|
Sector 4 | 0x08010000 - 0x0801FFFF | 64 KiB | firmware
|
||||||
Sector 5 | 0x08020000 - 0x0803FFFF | 128 KiB | application code
|
Sector 5 | 0x08020000 - 0x0803FFFF | 128 KiB | firmware
|
||||||
Sector 6 | 0x08040000 - 0x0805FFFF | 128 KiB | application code
|
Sector 6 | 0x08040000 - 0x0805FFFF | 128 KiB | firmware
|
||||||
Sector 7 | 0x08060000 - 0x0807FFFF | 128 KiB | application code
|
Sector 7 | 0x08060000 - 0x0807FFFF | 128 KiB | firmware
|
||||||
===========+=========================+============================
|
Sector 8 | 0x08080000 - 0x0809FFFF | 128 KiB | firmware
|
||||||
Sector 8 | 0x08080000 - 0x0809FFFF | 128 KiB | application code
|
Sector 9 | 0x080A0000 - 0x080BFFFF | 128 KiB | firmware
|
||||||
Sector 9 | 0x080A0000 - 0x080BFFFF | 128 KiB | application code
|
Sector 10 | 0x080C0000 - 0x080DFFFF | 128 KiB | firmware
|
||||||
Sector 10 | 0x080C0000 - 0x080DFFFF | 128 KiB | application code
|
Sector 11 | 0x080E0000 - 0x080FFFFF | 128 KiB | firmware
|
||||||
Sector 11 | 0x080E0000 - 0x080FFFFF | 128 KiB | application code
|
|
||||||
|
|
||||||
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
|
We pad the firmware chunks with zeroes if they are shorted.
|
||||||
--------+-------------+-------------------------------
|
|
||||||
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)
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -78,31 +72,20 @@ extern uint8_t *emulator_flash_base;
|
|||||||
#define FLASH_BOOT_START (FLASH_ORIGIN)
|
#define FLASH_BOOT_START (FLASH_ORIGIN)
|
||||||
#define FLASH_BOOT_LEN (0x8000)
|
#define FLASH_BOOT_LEN (0x8000)
|
||||||
|
|
||||||
#define FLASH_META_START (FLASH_BOOT_START + FLASH_BOOT_LEN)
|
#define FLASH_STORAGE_START (FLASH_BOOT_START + FLASH_BOOT_LEN)
|
||||||
#define FLASH_META_LEN (0x8000)
|
#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_APP_START (FLASH_FWHEADER_START + FLASH_FWHEADER_LEN)
|
||||||
#define FLASH_META_CODELEN (FLASH_META_START + 0x0004)
|
#define FLASH_APP_LEN (FLASH_TOTAL_SIZE - (FLASH_APP_START - FLASH_ORIGIN))
|
||||||
#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_BOOT_SECTOR_FIRST 0
|
#define FLASH_BOOT_SECTOR_FIRST 0
|
||||||
#define FLASH_BOOT_SECTOR_LAST 1
|
#define FLASH_BOOT_SECTOR_LAST 1
|
||||||
|
|
||||||
#define FLASH_META_SECTOR_FIRST 2
|
#define FLASH_STORAGE_SECTOR_FIRST 2
|
||||||
#define FLASH_META_SECTOR_LAST 3
|
#define FLASH_STORAGE_SECTOR_LAST 3
|
||||||
|
|
||||||
#define FLASH_CODE_SECTOR_FIRST 4
|
#define FLASH_CODE_SECTOR_FIRST 4
|
||||||
#define FLASH_CODE_SECTOR_LAST 11
|
#define FLASH_CODE_SECTOR_LAST 11
|
||||||
|
31
memory_app_1.8.0.ld
Normal file
31
memory_app_1.8.0.ld
Normal file
@ -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);
|
@ -29,7 +29,7 @@
|
|||||||
/*
|
/*
|
||||||
* The length of the sector header in bytes. The header is preserved between sector erasures.
|
* 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.
|
* Current storage version.
|
||||||
|
@ -22,4 +22,8 @@ fi
|
|||||||
make -C vendor/nanopb/generator/proto
|
make -C vendor/nanopb/generator/proto
|
||||||
make -C firmware/protob
|
make -C firmware/protob
|
||||||
|
|
||||||
make -C firmware sign
|
make -C firmware
|
||||||
|
|
||||||
|
if [ "$EMULATOR" != 1 ]; then
|
||||||
|
make -C firmware sign
|
||||||
|
fi
|
||||||
|
54
setup.c
54
setup.c
@ -33,7 +33,7 @@ uint32_t __stack_chk_guard;
|
|||||||
|
|
||||||
static inline void __attribute__((noreturn)) fault_handler(const char *line1) {
|
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);
|
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) {
|
void __attribute__((noreturn)) __stack_chk_fail(void) {
|
||||||
@ -141,6 +141,7 @@ void setupApp(void)
|
|||||||
gpio_set_af(GPIOA, GPIO_AF10, GPIO10);
|
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_1KB (0x09UL << MPU_RASR_SIZE_LSB)
|
||||||
#define MPU_RASR_SIZE_4KB (0x0BUL << MPU_RASR_SIZE_LSB)
|
#define MPU_RASR_SIZE_4KB (0x0BUL << MPU_RASR_SIZE_LSB)
|
||||||
#define MPU_RASR_SIZE_8KB (0x0CUL << 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_512KB (0x12UL << MPU_RASR_SIZE_LSB)
|
||||||
#define MPU_RASR_SIZE_1MB (0x13UL << 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_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
|
// http://infocenter.arm.com/help/topic/com.arm.doc.dui0552a/BABDJJGF.html
|
||||||
#define MPU_RASR_ATTR_FLASH (MPU_RASR_ATTR_C)
|
#define MPU_RASR_ATTR_FLASH (MPU_RASR_ATTR_C)
|
||||||
@ -161,6 +163,55 @@ void setupApp(void)
|
|||||||
#define FLASH_BASE (0x08000000U)
|
#define FLASH_BASE (0x08000000U)
|
||||||
#define SRAM_BASE (0x20000000U)
|
#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)
|
// Never use in bootloader! Disables access to PPB (including MPU, NVIC, SCB)
|
||||||
void mpu_config_firmware(void)
|
void mpu_config_firmware(void)
|
||||||
{
|
{
|
||||||
@ -169,6 +220,7 @@ void mpu_config_firmware(void)
|
|||||||
MPU_CTRL = 0;
|
MPU_CTRL = 0;
|
||||||
|
|
||||||
// Note: later entries overwrite previous ones
|
// Note: later entries overwrite previous ones
|
||||||
|
|
||||||
// Flash (0x08000000 - 0x0807FFFF, 1 MiB, read-only)
|
// Flash (0x08000000 - 0x0807FFFF, 1 MiB, read-only)
|
||||||
MPU_RBAR = FLASH_BASE | MPU_RBAR_VALID | (0 << MPU_RBAR_REGION_LSB);
|
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;
|
MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_FLASH | MPU_RASR_SIZE_1MB | MPU_RASR_ATTR_AP_PRO_URO;
|
||||||
|
2
setup.h
2
setup.h
@ -27,6 +27,8 @@ extern uint32_t __stack_chk_guard;
|
|||||||
void setup(void);
|
void setup(void);
|
||||||
void setupApp(void);
|
void setupApp(void);
|
||||||
|
|
||||||
|
void mpu_config_off(void);
|
||||||
|
void mpu_config_bootloader(void);
|
||||||
void mpu_config_firmware(void);
|
void mpu_config_firmware(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
10
startup.s
10
startup.s
@ -19,6 +19,16 @@ memset_reg:
|
|||||||
.global reset_handler
|
.global reset_handler
|
||||||
.type reset_handler, STT_FUNC
|
.type reset_handler, STT_FUNC
|
||||||
reset_handler:
|
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 r0, =_ram_start // r0 - point to beginning of SRAM
|
||||||
ldr r1, =_ram_end // r1 - point to byte after the end 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
|
ldr r2, =0 // r2 - the byte-sized value to be written
|
||||||
|
@ -45,9 +45,9 @@ static void svhandler_flash_program(uint32_t psize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void svhandler_flash_erase_sector(uint16_t sector) {
|
static void svhandler_flash_erase_sector(uint16_t sector) {
|
||||||
/* we only allow erasing meta sectors 2 and 3. */
|
/* we only allow erasing storage sectors 2 and 3. */
|
||||||
if (sector < FLASH_META_SECTOR_FIRST ||
|
if (sector < FLASH_STORAGE_SECTOR_FIRST ||
|
||||||
sector > FLASH_META_SECTOR_LAST) {
|
sector > FLASH_STORAGE_SECTOR_LAST) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
flash_erase_sector(sector, FLASH_CR_PROGRAM_X32);
|
flash_erase_sector(sector, FLASH_CR_PROGRAM_X32);
|
||||||
|
@ -62,13 +62,15 @@ static int usb21_standard_get_descriptor(usbd_device* usbd_dev,
|
|||||||
(void)complete;
|
(void)complete;
|
||||||
(void)usbd_dev;
|
(void)usbd_dev;
|
||||||
|
|
||||||
|
wait_random();
|
||||||
|
|
||||||
if (req->bRequest == USB_REQ_GET_DESCRIPTOR) {
|
if (req->bRequest == USB_REQ_GET_DESCRIPTOR) {
|
||||||
int descr_type = req->wValue >> 8;
|
int descr_type = req->wValue >> 8;
|
||||||
if (descr_type == USB_DT_BOS) {
|
if (descr_type == USB_DT_BOS) {
|
||||||
if (!usb21_bos) {
|
if (!usb21_bos) {
|
||||||
return USBD_REQ_NOTSUPP;
|
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;
|
return USBD_REQ_HANDLED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
161
usb_private.h
Normal file
161
usb_private.h
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
/** @defgroup usb_private_defines USB Private Structures
|
||||||
|
|
||||||
|
@brief <b>Defined Constants and Types for the USB Private Structures</b>
|
||||||
|
|
||||||
|
@ingroup USB_defines
|
||||||
|
|
||||||
|
@version 1.0.0
|
||||||
|
|
||||||
|
@author @htmlonly © @endhtmlonly 2010
|
||||||
|
Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
|
||||||
|
@date 10 March 2013
|
||||||
|
|
||||||
|
LGPL License Terms @ref lgpl_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the libopencm3 project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**@{*/
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
604
usb_standard.c
Normal file
604
usb_standard.c
Normal file
@ -0,0 +1,604 @@
|
|||||||
|
/** @defgroup usb_standard_file Generic USB Standard Request Interface
|
||||||
|
|
||||||
|
@ingroup USB
|
||||||
|
|
||||||
|
@brief <b>Generic USB Standard Request Interface</b>
|
||||||
|
|
||||||
|
@version 1.0.0
|
||||||
|
|
||||||
|
@author @htmlonly © @endhtmlonly 2010
|
||||||
|
Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
|
||||||
|
@date 10 March 2013
|
||||||
|
|
||||||
|
LGPL License Terms @ref lgpl_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the libopencm3 project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**@{*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <libopencm3/usb/usbd.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
19
util.c
19
util.c
@ -17,6 +17,7 @@
|
|||||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "rng.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
inline void delay(uint32_t wait)
|
inline void delay(uint32_t wait)
|
||||||
@ -24,6 +25,24 @@ inline void delay(uint32_t wait)
|
|||||||
while (--wait > 0) __asm__("nop");
|
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";
|
static const char *hexdigits = "0123456789ABCDEF";
|
||||||
|
|
||||||
void uint32hex(uint32_t num, char *str)
|
void uint32hex(uint32_t num, char *str)
|
||||||
|
5
util.h
5
util.h
@ -27,14 +27,18 @@
|
|||||||
#if !EMULATOR
|
#if !EMULATOR
|
||||||
#include <libopencm3/cm3/scb.h>
|
#include <libopencm3/cm3/scb.h>
|
||||||
#include <libopencm3/cm3/vector.h>
|
#include <libopencm3/cm3/vector.h>
|
||||||
|
#include "timer.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Statement expressions make these macros side-effect safe
|
// 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 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; })
|
#define MAX(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _a : _b; })
|
||||||
|
|
||||||
void delay(uint32_t wait);
|
void delay(uint32_t wait);
|
||||||
|
|
||||||
|
void wait_random(void);
|
||||||
|
|
||||||
// converts uint32 to hexa (8 digits)
|
// converts uint32 to hexa (8 digits)
|
||||||
void uint32hex(uint32_t num, char *str);
|
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
|
// Set stack pointer
|
||||||
__asm__ volatile("msr msp, %0" :: "r" (vector_table->initial_sp_value));
|
__asm__ volatile("msr msp, %0" :: "r" (vector_table->initial_sp_value));
|
||||||
} else { // untrusted firmware
|
} else { // untrusted firmware
|
||||||
|
timer_init();
|
||||||
mpu_config_firmware(); // * configure MPU for the firmware
|
mpu_config_firmware(); // * configure MPU for the firmware
|
||||||
__asm__ volatile("msr msp, %0" :: "r" (_stack));
|
__asm__ volatile("msr msp, %0" :: "r" (_stack));
|
||||||
}
|
}
|
||||||
|
2
vendor/trezor-crypto
vendored
2
vendor/trezor-crypto
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 21391dc5be9917bc32a518cf98376f79103727af
|
Subproject commit 4211ce389f6795d844809b0ba66a84082038ca04
|
2
vendor/trezor-storage
vendored
2
vendor/trezor-storage
vendored
@ -1 +1 @@
|
|||||||
Subproject commit d715873ee62d776ddc0f85028f7622374d4e0fe7
|
Subproject commit 511fc205b284605651348512c5c5c2c95a642fa1
|
4
webusb.c
4
webusb.c
@ -53,6 +53,8 @@ static int webusb_control_vendor_request(usbd_device *usbd_dev,
|
|||||||
(void)complete;
|
(void)complete;
|
||||||
(void)usbd_dev;
|
(void)usbd_dev;
|
||||||
|
|
||||||
|
wait_random();
|
||||||
|
|
||||||
if (req->bRequest != WEBUSB_VENDOR_CODE) {
|
if (req->bRequest != WEBUSB_VENDOR_CODE) {
|
||||||
return USBD_REQ_NEXT_CALLBACK;
|
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->bDescriptorType = WEBUSB_DT_URL;
|
||||||
url->bScheme = WEBUSB_URL_SCHEME_HTTPS;
|
url->bScheme = WEBUSB_URL_SCHEME_HTTPS;
|
||||||
memcpy(&url->URL, webusb_https_url, url_len);
|
memcpy(&url->URL, webusb_https_url, url_len);
|
||||||
*len = MIN(*len, url->bLength);
|
*len = MIN_8bits(*len, url->bLength);
|
||||||
status = USBD_REQ_HANDLED;
|
status = USBD_REQ_HANDLED;
|
||||||
} else {
|
} else {
|
||||||
// TODO: stall instead?
|
// TODO: stall instead?
|
||||||
|
10
winusb.c
10
winusb.c
@ -82,6 +82,8 @@ static int winusb_descriptor_request(usbd_device *usbd_dev,
|
|||||||
(void)complete;
|
(void)complete;
|
||||||
(void)usbd_dev;
|
(void)usbd_dev;
|
||||||
|
|
||||||
|
wait_random();
|
||||||
|
|
||||||
if ((req->bmRequestType & USB_REQ_TYPE_TYPE) != USB_REQ_TYPE_STANDARD) {
|
if ((req->bmRequestType & USB_REQ_TYPE_TYPE) != USB_REQ_TYPE_STANDARD) {
|
||||||
return USBD_REQ_NEXT_CALLBACK;
|
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 (req->bRequest == USB_REQ_GET_DESCRIPTOR && usb_descriptor_type(req->wValue) == USB_DT_STRING) {
|
||||||
if (usb_descriptor_index(req->wValue) == WINUSB_EXTRA_STRING_INDEX) {
|
if (usb_descriptor_index(req->wValue) == WINUSB_EXTRA_STRING_INDEX) {
|
||||||
*buf = (uint8_t*)(&winusb_string_descriptor);
|
*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;
|
return USBD_REQ_HANDLED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,6 +105,8 @@ static int winusb_control_vendor_request(usbd_device *usbd_dev,
|
|||||||
(void)complete;
|
(void)complete;
|
||||||
(void)usbd_dev;
|
(void)usbd_dev;
|
||||||
|
|
||||||
|
wait_random();
|
||||||
|
|
||||||
if (req->bRequest != WINUSB_MS_VENDOR_CODE) {
|
if (req->bRequest != WINUSB_MS_VENDOR_CODE) {
|
||||||
return USBD_REQ_NEXT_CALLBACK;
|
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) &&
|
if (((req->bmRequestType & USB_REQ_TYPE_RECIPIENT) == USB_REQ_TYPE_DEVICE) &&
|
||||||
(req->wIndex == WINUSB_REQ_GET_COMPATIBLE_ID_FEATURE_DESCRIPTOR)) {
|
(req->wIndex == WINUSB_REQ_GET_COMPATIBLE_ID_FEATURE_DESCRIPTOR)) {
|
||||||
*buf = (uint8_t*)(&winusb_wcid);
|
*buf = (uint8_t*)(&winusb_wcid);
|
||||||
*len = MIN(*len, winusb_wcid.header.dwLength);
|
*len = MIN_8bits(*len, winusb_wcid.header.dwLength);
|
||||||
status = USBD_REQ_HANDLED;
|
status = USBD_REQ_HANDLED;
|
||||||
|
|
||||||
} else if (((req->bmRequestType & USB_REQ_TYPE_RECIPIENT) == USB_REQ_TYPE_INTERFACE) &&
|
} 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)) {
|
(usb_descriptor_index(req->wValue) == winusb_wcid.functions[0].bInterfaceNumber)) {
|
||||||
|
|
||||||
*buf = (uint8_t*)(&guid);
|
*buf = (uint8_t*)(&guid);
|
||||||
*len = MIN(*len, guid.header.dwLength);
|
*len = MIN_8bits(*len, guid.header.dwLength);
|
||||||
status = USBD_REQ_HANDLED;
|
status = USBD_REQ_HANDLED;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user