diff --git a/core/.changelog.d/2623.added b/core/.changelog.d/2623.added new file mode 100644 index 0000000000..3857a6bb9e --- /dev/null +++ b/core/.changelog.d/2623.added @@ -0,0 +1 @@ +Add model info to image and check when installing bootloader, prevent bootloader downgrade diff --git a/core/embed/boardloader/.changelog.d/2623.added b/core/embed/boardloader/.changelog.d/2623.added new file mode 100644 index 0000000000..fe8e5b8cb6 --- /dev/null +++ b/core/embed/boardloader/.changelog.d/2623.added @@ -0,0 +1 @@ +Check image model when replacing bootloader diff --git a/core/embed/boardloader/main.c b/core/embed/boardloader/main.c index e17896b1d3..93f85b9579 100644 --- a/core/embed/boardloader/main.c +++ b/core/embed/boardloader/main.c @@ -53,14 +53,8 @@ struct BoardCapabilities capablities __attribute__((section(".capabilities_section"))) = { .header = CAPABILITIES_HEADER, .model_tag = MODEL_NAME, - .model_length = MODEL_NAME_MAX_LENGTH, -#if defined TREZOR_MODEL_T - .model_name = "TREZORT", -#elif defined TREZOR_MODEL_R - .model_name = "TREZORR", -#else -#error Unknown model -#endif + .model_length = sizeof(uint32_t), + .model_name = HW_MODEL, .version_tag = BOARDLOADER_VERSION, .version_length = sizeof(struct BoardloaderVersion), .version = {.version_major = VERSION_MAJOR, @@ -93,17 +87,29 @@ static uint32_t check_sdcard(void) { sdcard_power_off(); - image_header hdr; + if (sectrue == read_status) { + const image_header *hdr = + read_image_header((const uint8_t *)sdcard_buf, BOOTLOADER_IMAGE_MAGIC, + BOOTLOADER_IMAGE_MAXSIZE); - if ((sectrue == read_status) && - (sectrue == - load_image_header((const uint8_t *)sdcard_buf, BOOTLOADER_IMAGE_MAGIC, - BOOTLOADER_IMAGE_MAXSIZE, BOARDLOADER_KEY_M, - BOARDLOADER_KEY_N, BOARDLOADER_KEYS, &hdr))) { - return hdr.codelen; - } else { - return 0; + if (hdr != (const image_header *)sdcard_buf) { + return 0; + } + + if (sectrue != check_image_model(hdr)) { + return 0; + } + + if (sectrue != check_image_header_sig(hdr, BOARDLOADER_KEY_M, + BOARDLOADER_KEY_N, + BOARDLOADER_KEYS)) { + return 0; + } + + return hdr->codelen; } + + return 0; } static void progress_callback(int pos, int len) { display_printf("."); } @@ -220,18 +226,21 @@ int main(void) { } #endif - image_header hdr; + const image_header *hdr = + read_image_header((const uint8_t *)BOOTLOADER_START, + BOOTLOADER_IMAGE_MAGIC, BOOTLOADER_IMAGE_MAXSIZE); - ensure(load_image_header((const uint8_t *)BOOTLOADER_START, - BOOTLOADER_IMAGE_MAGIC, BOOTLOADER_IMAGE_MAXSIZE, - BOARDLOADER_KEY_M, BOARDLOADER_KEY_N, - BOARDLOADER_KEYS, &hdr), + ensure(hdr == (const image_header *)BOOTLOADER_START ? sectrue : secfalse, "invalid bootloader header"); + ensure(check_image_header_sig(hdr, BOARDLOADER_KEY_M, BOARDLOADER_KEY_N, + BOARDLOADER_KEYS), + "invalid bootloader signature"); + const uint8_t sectors[] = { FLASH_SECTOR_BOOTLOADER, }; - ensure(check_image_contents(&hdr, IMAGE_HEADER_SIZE, sectors, 1), + ensure(check_image_contents(hdr, IMAGE_HEADER_SIZE, sectors, 1), "invalid bootloader hash"); ensure_compatible_settings(); diff --git a/core/embed/bootloader/.changelog.d/2623.added b/core/embed/bootloader/.changelog.d/2623.added new file mode 100644 index 0000000000..133dcc6d1c --- /dev/null +++ b/core/embed/bootloader/.changelog.d/2623.added @@ -0,0 +1 @@ +Add model info to image and check when installing/running firmware diff --git a/core/embed/bootloader/bootui.c b/core/embed/bootloader/bootui.c index 8c6618b935..de83047e3c 100644 --- a/core/embed/bootloader/bootui.c +++ b/core/embed/bootloader/bootui.c @@ -215,31 +215,6 @@ void ui_screen_firmware_info(const vendor_header *const vhdr, display_refresh(); } -void ui_screen_firmware_fingerprint(const image_header *const hdr) { - display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_BL_BG); - display_text(16, 32, "Firmware fingerprint", -1, FONT_NORMAL, COLOR_BL_FG, - COLOR_BL_BG); - display_bar(16, 44, DISPLAY_RESX - 14 * 2, 1, COLOR_BL_FG); - - static const char *hexdigits = "0123456789abcdef"; - char fingerprint_str[64]; - for (int i = 0; i < 32; i++) { - fingerprint_str[i * 2] = hexdigits[(hdr->fingerprint[i] >> 4) & 0xF]; - fingerprint_str[i * 2 + 1] = hexdigits[hdr->fingerprint[i] & 0xF]; - } - for (int i = 0; i < 4; i++) { - display_text_center(120, 70 + i * 25, fingerprint_str + i * 16, 16, - FONT_MONO, COLOR_BL_FG, COLOR_BL_BG); - } - - display_bar_radius(9, 184, 222, 50, COLOR_BL_DONE, COLOR_BL_BG, 4); - display_icon(9 + (222 - 19) / 2, 184 + (50 - 16) / 2, 20, 16, - toi_icon_confirm + 12, sizeof(toi_icon_confirm) - 12, - COLOR_BL_BG, COLOR_BL_DONE); - PIXELDATA_DIRTY(); - display_refresh(); -} - // install UI void ui_screen_install_confirm_upgrade(const vendor_header *const vhdr, diff --git a/core/embed/bootloader/bootui.h b/core/embed/bootloader/bootui.h index 0d74d316ec..73285e419e 100644 --- a/core/embed/bootloader/bootui.h +++ b/core/embed/bootloader/bootui.h @@ -34,7 +34,6 @@ void ui_screen_welcome_third(void); void ui_screen_firmware_info(const vendor_header* const vhdr, const image_header* const hdr); -void ui_screen_firmware_fingerprint(const image_header* const hdr); void ui_screen_install_confirm_upgrade(const vendor_header* const vhdr, const image_header* const hdr); diff --git a/core/embed/bootloader/header.S b/core/embed/bootloader/header.S index 3e3849a670..9014961835 100644 --- a/core/embed/bootloader/header.S +++ b/core/embed/bootloader/header.S @@ -10,7 +10,11 @@ g_header: .byte 'T','R','Z','B' // magic .word g_header_end - g_header // hdrlen +#ifdef TREZOR_MODEL_T .word 0 // expiry +#else + .word 1 // expiry +#endif .word _codelen // codelen .byte VERSION_MAJOR // vmajor .byte VERSION_MINOR // vminor @@ -20,7 +24,10 @@ g_header: .byte FIX_VERSION_MINOR // fix_vminor .byte FIX_VERSION_PATCH // fix_vpatch .byte FIX_VERSION_BUILD // fix_vbuild - . = . + 8 // reserved + .word HW_MODEL // type of the designated hardware + .byte HW_REVISION // revision of the designated hardware + .byte VERSION_MONOTONIC // monotonic version of the binary + . = . + 2 // reserved . = . + 512 // hash1 ... hash16 . = . + 415 // reserved .byte 0 // sigmask diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index 0e8e6701f0..c9f5c5ba69 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -159,7 +159,7 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr, break; case 7: // FirmwareUpload r = process_msg_FirmwareUpload(USB_IFACE_NUM, msg_size, buf); - if (r < 0 && r != -4) { // error, but not user abort (-4) + if (r < 0 && r != UPLOAD_ERR_USER_ABORT) { // error, but not user abort ui_fadeout(); ui_screen_fail(); ui_fadein(); @@ -193,10 +193,9 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr, } } -secbool load_vendor_header_keys(const uint8_t *const data, - vendor_header *const vhdr) { - return load_vendor_header(data, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N, - BOOTLOADER_KEYS, vhdr); +secbool check_vendor_header_keys(const vendor_header *const vhdr) { + return check_vendor_header_sig(vhdr, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N, + BOOTLOADER_KEYS); } static secbool check_vendor_header_lock(const vendor_header *const vhdr) { @@ -303,23 +302,40 @@ int main(void) { } #endif + const image_header *hdr = NULL; vendor_header vhdr; - image_header hdr; - // detect whether the devices contains a valid firmware + // detect whether the device contains a valid firmware + secbool firmware_present = sectrue; + + if (sectrue != read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr)) { + firmware_present = secfalse; + } + + if (sectrue == firmware_present) { + firmware_present = check_vendor_header_keys(&vhdr); + } - secbool firmware_present = - load_vendor_header_keys((const uint8_t *)FIRMWARE_START, &vhdr); if (sectrue == firmware_present) { firmware_present = check_vendor_header_lock(&vhdr); } + if (sectrue == firmware_present) { - firmware_present = load_image_header( - (const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), FIRMWARE_IMAGE_MAGIC, - FIRMWARE_IMAGE_MAXSIZE, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub, &hdr); + hdr = read_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), + FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE); + if (hdr != (const image_header *)(FIRMWARE_START + vhdr.hdrlen)) { + firmware_present = secfalse; + } + } + if (sectrue == firmware_present) { + firmware_present = check_image_model(hdr); } if (sectrue == firmware_present) { firmware_present = - check_image_contents(&hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, + check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub); + } + if (sectrue == firmware_present) { + firmware_present = + check_image_contents(hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, FIRMWARE_SECTORS, FIRMWARE_SECTORS_COUNT); } @@ -357,26 +373,35 @@ int main(void) { // ... or we have stay_in_bootloader flag to force it if (touched || stay_in_bootloader == sectrue) { // no ui_fadeout(); - we already start from black screen - ui_screen_firmware_info(&vhdr, &hdr); + ui_screen_firmware_info(&vhdr, hdr); ui_fadein(); // and start the usb loop - if (bootloader_usb_loop(&vhdr, &hdr) != sectrue) { + if (bootloader_usb_loop(&vhdr, hdr) != sectrue) { return 1; } } - ensure(load_vendor_header_keys((const uint8_t *)FIRMWARE_START, &vhdr), + ensure(read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr), "invalid vendor header"); + ensure(check_vendor_header_keys(&vhdr), "invalid vendor header signature"); + ensure(check_vendor_header_lock(&vhdr), "unauthorized vendor keys"); - ensure(load_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), - FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE, - vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub, &hdr), + hdr = read_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), + FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE); + + ensure(hdr == (const image_header *)(FIRMWARE_START + vhdr.hdrlen) ? sectrue + : secfalse, "invalid firmware header"); - ensure(check_image_contents(&hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, + ensure(check_image_model(hdr), "wrong firmware model"); + + ensure(check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub), + "invalid firmware signature"); + + ensure(check_image_contents(hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, FIRMWARE_SECTORS, FIRMWARE_SECTORS_COUNT), "invalid firmware hash"); @@ -384,7 +409,7 @@ int main(void) { if ((vhdr.vtrust & VTRUST_ALL) != VTRUST_ALL) { // ui_fadeout(); // no fadeout - we start from black screen - ui_screen_boot(&vhdr, &hdr); + ui_screen_boot(&vhdr, hdr); ui_fadein(); int delay = (vhdr.vtrust & VTRUST_WAIT) ^ VTRUST_WAIT; diff --git a/core/embed/bootloader/messages.c b/core/embed/bootloader/messages.c index 14474f1c1a..621564c10d 100644 --- a/core/embed/bootloader/messages.c +++ b/core/embed/bootloader/messages.c @@ -403,8 +403,7 @@ static bool _read_payload(pb_istream_t *stream, const pb_field_t *field, return true; } -secbool load_vendor_header_keys(const uint8_t *const data, - vendor_header *const vhdr); +secbool check_vendor_header_keys(const vendor_header *const vhdr); static int version_compare(uint32_t vera, uint32_t verb) { int a, b; @@ -422,8 +421,8 @@ static int version_compare(uint32_t vera, uint32_t verb) { return a - b; } -static void detect_installation(vendor_header *current_vhdr, - image_header *current_hdr, +static void detect_installation(const vendor_header *current_vhdr, + const image_header *current_hdr, const vendor_header *const new_vhdr, const image_header *const new_hdr, secbool *is_new, secbool *is_upgrade, @@ -431,16 +430,17 @@ static void detect_installation(vendor_header *current_vhdr, *is_new = secfalse; *is_upgrade = secfalse; *is_downgrade_wipe = secfalse; - if (sectrue != - load_vendor_header_keys((const uint8_t *)FIRMWARE_START, current_vhdr)) { + if (sectrue != check_vendor_header_keys(current_vhdr)) { *is_new = sectrue; return; } - if (sectrue != - load_image_header((const uint8_t *)FIRMWARE_START + current_vhdr->hdrlen, - FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE, - current_vhdr->vsig_m, current_vhdr->vsig_n, - current_vhdr->vpub, current_hdr)) { + if (sectrue != check_image_model(current_hdr)) { + *is_new = sectrue; + return; + } + if (sectrue != check_image_header_sig(current_hdr, current_vhdr->vsig_m, + current_vhdr->vsig_n, + current_vhdr->vpub)) { *is_new = sectrue; return; } @@ -472,7 +472,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid chunk size"); MSG_SEND(Failure); - return -1; + return UPLOAD_ERR_INVALID_CHUNK_SIZE; } static image_header hdr; @@ -483,29 +483,81 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, if (headers_offset == 0) { // first block and headers are not yet parsed vendor_header vhdr; - if (sectrue != load_vendor_header_keys(CHUNK_BUFFER_PTR, &vhdr)) { + + if (sectrue != read_vendor_header(CHUNK_BUFFER_PTR, &vhdr)) { MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header"); MSG_SEND(Failure); - return -2; + return UPLOAD_ERR_INVALID_VENDOR_HEADER; } - if (sectrue != load_image_header(CHUNK_BUFFER_PTR + vhdr.hdrlen, - FIRMWARE_IMAGE_MAGIC, - FIRMWARE_IMAGE_MAXSIZE, vhdr.vsig_m, - vhdr.vsig_n, vhdr.vpub, &hdr)) { + + if (sectrue != check_vendor_header_keys(&vhdr)) { + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); + MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header signature"); + MSG_SEND(Failure); + return UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG; + } + + const image_header *received_hdr = + read_image_header(CHUNK_BUFFER_PTR + vhdr.hdrlen, + FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE); + + if (received_hdr != + (const image_header *)(CHUNK_BUFFER_PTR + vhdr.hdrlen)) { MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid firmware header"); MSG_SEND(Failure); - return -3; + return UPLOAD_ERR_INVALID_IMAGE_HEADER; } + if (sectrue != check_image_model(received_hdr)) { + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); + MSG_SEND_ASSIGN_STRING(message, "Wrong firmware model"); + MSG_SEND(Failure); + return UPLOAD_ERR_INVALID_IMAGE_MODEL; + } + + if (sectrue != check_image_header_sig(received_hdr, vhdr.vsig_m, + vhdr.vsig_n, vhdr.vpub)) { + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); + MSG_SEND_ASSIGN_STRING(message, "Invalid firmware signature"); + MSG_SEND(Failure); + return UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG; + } + + memcpy(&hdr, received_hdr, sizeof(hdr)); + vendor_header current_vhdr; - image_header current_hdr; + secbool is_new = secfalse; - detect_installation(¤t_vhdr, ¤t_hdr, &vhdr, &hdr, &is_new, - &is_upgrade, &is_downgrade_wipe); + + if (sectrue != + read_vendor_header((const uint8_t *)FIRMWARE_START, ¤t_vhdr)) { + is_new = sectrue; + } + + const image_header *current_hdr = NULL; + + if (is_new == secfalse) { + current_hdr = read_image_header( + (const uint8_t *)FIRMWARE_START + current_vhdr.hdrlen, + FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE); + + if (current_hdr != + (const image_header *)FIRMWARE_START + current_vhdr.hdrlen) { + is_new = sectrue; + } + } + + if (is_new == secfalse) { + detect_installation(¤t_vhdr, current_hdr, &vhdr, &hdr, &is_new, + &is_upgrade, &is_downgrade_wipe); + } int response = INPUT_CANCEL; if (sectrue == is_new) { @@ -528,10 +580,10 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, if (INPUT_CANCEL == response) { ui_fadeout(); - ui_screen_firmware_info(¤t_vhdr, ¤t_hdr); + ui_screen_firmware_info(¤t_vhdr, current_hdr); ui_fadein(); send_user_abort(iface_num, "Firmware install cancelled"); - return -4; + return UPLOAD_ERR_USER_ABORT; } headers_offset = IMAGE_HEADER_SIZE + vhdr.hdrlen; @@ -572,7 +624,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Firmware too big"); MSG_SEND(Failure); - return -5; + return UPLOAD_ERR_FIRMWARE_TOO_BIG; } if (sectrue != check_single_hash(hdr.hashes + firmware_block * 32, @@ -591,7 +643,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid chunk hash"); MSG_SEND(Failure); - return -6; + return UPLOAD_ERR_INVALID_CHUNK_HASH; } ensure(flash_unlock_write(), NULL); @@ -654,11 +706,11 @@ int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Could not erase flash"); MSG_SEND(Failure); - return -1; + return WIPE_ERR_CANNOT_ERASE; } else { MSG_SEND_INIT(Success); MSG_SEND(Success); - return 0; + return WIPE_OK; } } diff --git a/core/embed/bootloader/messages.h b/core/embed/bootloader/messages.h index fd11b82ac7..0937f60627 100644 --- a/core/embed/bootloader/messages.h +++ b/core/embed/bootloader/messages.h @@ -29,6 +29,24 @@ #define FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT 2 +enum { + UPLOAD_OK = 0, + UPLOAD_ERR_INVALID_CHUNK_SIZE = -1, + UPLOAD_ERR_INVALID_VENDOR_HEADER = -2, + UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG = -3, + UPLOAD_ERR_INVALID_IMAGE_HEADER = -4, + UPLOAD_ERR_INVALID_IMAGE_MODEL = -5, + UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG = -6, + UPLOAD_ERR_USER_ABORT = -7, + UPLOAD_ERR_FIRMWARE_TOO_BIG = -8, + UPLOAD_ERR_INVALID_CHUNK_HASH = -9, +}; + +enum { + WIPE_OK = 0, + WIPE_ERR_CANNOT_ERASE = -1, +}; + secbool msg_parse_header(const uint8_t *buf, uint16_t *msg_id, uint32_t *msg_size); diff --git a/core/embed/bootloader_ci/.changelog.d/2623.added b/core/embed/bootloader_ci/.changelog.d/2623.added new file mode 100644 index 0000000000..133dcc6d1c --- /dev/null +++ b/core/embed/bootloader_ci/.changelog.d/2623.added @@ -0,0 +1 @@ +Add model info to image and check when installing/running firmware diff --git a/core/embed/bootloader_ci/header.S b/core/embed/bootloader_ci/header.S index 3e3849a670..bb386ef7fd 100644 --- a/core/embed/bootloader_ci/header.S +++ b/core/embed/bootloader_ci/header.S @@ -10,7 +10,11 @@ g_header: .byte 'T','R','Z','B' // magic .word g_header_end - g_header // hdrlen +#ifdef TREZOR_MODEL_T .word 0 // expiry +#else + .word 1 // expiry +#endif .word _codelen // codelen .byte VERSION_MAJOR // vmajor .byte VERSION_MINOR // vminor @@ -20,7 +24,10 @@ g_header: .byte FIX_VERSION_MINOR // fix_vminor .byte FIX_VERSION_PATCH // fix_vpatch .byte FIX_VERSION_BUILD // fix_vbuild - . = . + 8 // reserved + .word HW_MODEL // type of the designated hardware + .byte HW_REVISION // revision of the designated hardware + .byte VERSION_MONOTONIC // monotonic version + . = . + 2 // reserved . = . + 512 // hash1 ... hash16 . = . + 415 // reserved .byte 0 // sigmask diff --git a/core/embed/bootloader_ci/main.c b/core/embed/bootloader_ci/main.c index 4f10468f4e..32baff7de2 100644 --- a/core/embed/bootloader_ci/main.c +++ b/core/embed/bootloader_ci/main.c @@ -134,7 +134,7 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr, break; case 7: // FirmwareUpload r = process_msg_FirmwareUpload(USB_IFACE_NUM, msg_size, buf); - if (r < 0 && r != -4) { // error, but not user abort (-4) + if (r < 0 && r != UPLOAD_ERR_USER_ABORT) { // error, but not user abort ui_screen_fail(); usb_stop(); usb_deinit(); @@ -163,10 +163,9 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr, } } -secbool load_vendor_header_keys(const uint8_t *const data, - vendor_header *const vhdr) { - return load_vendor_header(data, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N, - BOOTLOADER_KEYS, vhdr); +secbool check_vendor_header_keys(vendor_header *const vhdr) { + return check_vendor_header_sig(vhdr, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N, + BOOTLOADER_KEYS); } static secbool check_vendor_header_lock(const vendor_header *const vhdr) { @@ -227,24 +226,40 @@ int main(void) { display_clear(); + const image_header *hdr = NULL; vendor_header vhdr; - image_header hdr; + // detect whether the device contains a valid firmware + secbool firmware_present = sectrue; - // detect whether the devices contains a valid firmware + if (sectrue != read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr)) { + firmware_present = secfalse; + } + + if (sectrue == firmware_present) { + firmware_present = check_vendor_header_keys(&vhdr); + } - secbool firmware_present = - load_vendor_header_keys((const uint8_t *)FIRMWARE_START, &vhdr); if (sectrue == firmware_present) { firmware_present = check_vendor_header_lock(&vhdr); } + if (sectrue == firmware_present) { - firmware_present = load_image_header( - (const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), FIRMWARE_IMAGE_MAGIC, - FIRMWARE_IMAGE_MAXSIZE, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub, &hdr); + hdr = read_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), + FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE); + if (hdr != (const image_header *)(FIRMWARE_START + vhdr.hdrlen)) { + firmware_present = secfalse; + } + } + if (sectrue == firmware_present) { + firmware_present = check_image_model(hdr); } if (sectrue == firmware_present) { firmware_present = - check_image_contents(&hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, + check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub); + } + if (sectrue == firmware_present) { + firmware_present = + check_image_contents(hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, FIRMWARE_SECTORS, FIRMWARE_SECTORS_COUNT); } @@ -260,17 +275,26 @@ int main(void) { return 1; } - ensure(load_vendor_header_keys((const uint8_t *)FIRMWARE_START, &vhdr), + ensure(read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr), "invalid vendor header"); + ensure(check_vendor_header_keys(&vhdr), "invalid vendor header signature"); + ensure(check_vendor_header_lock(&vhdr), "unauthorized vendor keys"); - ensure(load_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), - FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE, - vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub, &hdr), + hdr = read_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), + FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE); + + ensure(hdr == (const image_header *)(FIRMWARE_START + vhdr.hdrlen) ? sectrue + : secfalse, "invalid firmware header"); - ensure(check_image_contents(&hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, + ensure(check_image_model(hdr), "wrong firmware model"); + + ensure(check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub), + "invalid firmware signature"); + + ensure(check_image_contents(hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, FIRMWARE_SECTORS, FIRMWARE_SECTORS_COUNT), "invalid firmware hash"); diff --git a/core/embed/bootloader_ci/messages.c b/core/embed/bootloader_ci/messages.c index a024874045..8aee331717 100644 --- a/core/embed/bootloader_ci/messages.c +++ b/core/embed/bootloader_ci/messages.c @@ -401,8 +401,7 @@ static bool _read_payload(pb_istream_t *stream, const pb_field_t *field, return true; } -secbool load_vendor_header_keys(const uint8_t *const data, - vendor_header *const vhdr); +secbool check_vendor_header_keys(const vendor_header *const vhdr); static int version_compare(uint32_t vera, uint32_t verb) { int a, b; @@ -420,8 +419,8 @@ static int version_compare(uint32_t vera, uint32_t verb) { return a - b; } -static void detect_installation(vendor_header *current_vhdr, - image_header *current_hdr, +static void detect_installation(const vendor_header *current_vhdr, + const image_header *current_hdr, const vendor_header *const new_vhdr, const image_header *const new_hdr, secbool *is_new, secbool *is_upgrade, @@ -429,16 +428,17 @@ static void detect_installation(vendor_header *current_vhdr, *is_new = secfalse; *is_upgrade = secfalse; *is_downgrade_wipe = secfalse; - if (sectrue != - load_vendor_header_keys((const uint8_t *)FIRMWARE_START, current_vhdr)) { + if (sectrue != check_vendor_header_keys(current_vhdr)) { *is_new = sectrue; return; } - if (sectrue != - load_image_header((const uint8_t *)FIRMWARE_START + current_vhdr->hdrlen, - FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE, - current_vhdr->vsig_m, current_vhdr->vsig_n, - current_vhdr->vpub, current_hdr)) { + if (sectrue != check_image_model(current_hdr)) { + *is_new = sectrue; + return; + } + if (sectrue != check_image_header_sig(current_hdr, current_vhdr->vsig_m, + current_vhdr->vsig_n, + current_vhdr->vpub)) { *is_new = sectrue; return; } @@ -470,7 +470,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid chunk size"); MSG_SEND(Failure); - return -1; + return UPLOAD_ERR_INVALID_CHUNK_SIZE; } static image_header hdr; @@ -481,29 +481,80 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, if (headers_offset == 0) { // first block and headers are not yet parsed vendor_header vhdr; - if (sectrue != load_vendor_header_keys(chunk_buffer, &vhdr)) { + + if (sectrue != read_vendor_header(chunk_buffer, &vhdr)) { MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header"); MSG_SEND(Failure); - return -2; + return UPLOAD_ERR_INVALID_VENDOR_HEADER; } - if (sectrue != load_image_header(chunk_buffer + vhdr.hdrlen, - FIRMWARE_IMAGE_MAGIC, - FIRMWARE_IMAGE_MAXSIZE, vhdr.vsig_m, - vhdr.vsig_n, vhdr.vpub, &hdr)) { + + if (sectrue != check_vendor_header_keys(&vhdr)) { + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); + MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header signature"); + MSG_SEND(Failure); + return UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG; + } + + const image_header *received_hdr = + read_image_header(chunk_buffer + vhdr.hdrlen, FIRMWARE_IMAGE_MAGIC, + FIRMWARE_IMAGE_MAXSIZE); + + if (received_hdr != (const image_header *)chunk_buffer + vhdr.hdrlen) { MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid firmware header"); MSG_SEND(Failure); - return -3; + return UPLOAD_ERR_INVALID_IMAGE_HEADER; } + if (sectrue != check_image_model(received_hdr)) { + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); + MSG_SEND_ASSIGN_STRING(message, "Wrong firmware model"); + MSG_SEND(Failure); + return UPLOAD_ERR_INVALID_IMAGE_MODEL; + } + + if (sectrue != check_image_header_sig(received_hdr, vhdr.vsig_m, + vhdr.vsig_n, vhdr.vpub)) { + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); + MSG_SEND_ASSIGN_STRING(message, "Invalid firmware signature"); + MSG_SEND(Failure); + return UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG; + } + + memcpy(&hdr, received_hdr, sizeof(hdr)); + vendor_header current_vhdr; - image_header current_hdr; + secbool is_new = secfalse; - detect_installation(¤t_vhdr, ¤t_hdr, &vhdr, &hdr, &is_new, - &is_upgrade, &is_downgrade_wipe); + + if (sectrue != + read_vendor_header((const uint8_t *)FIRMWARE_START, ¤t_vhdr)) { + is_new = sectrue; + } + + const image_header *current_hdr = NULL; + + if (is_new == secfalse) { + current_hdr = read_image_header( + (const uint8_t *)FIRMWARE_START + current_vhdr.hdrlen, + FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE); + + if (current_hdr != + (const image_header *)FIRMWARE_START + current_vhdr.hdrlen) { + is_new = sectrue; + } + } + + if (is_new == secfalse) { + detect_installation(¤t_vhdr, current_hdr, &vhdr, &hdr, &is_new, + &is_upgrade, &is_downgrade_wipe); + } // no user confirmations, go directly to upload @@ -539,7 +590,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Firmware too big"); MSG_SEND(Failure); - return -5; + return UPLOAD_ERR_FIRMWARE_TOO_BIG; } if (sectrue != check_single_hash(hdr.hashes + firmware_block * 32, @@ -558,7 +609,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid chunk hash"); MSG_SEND(Failure); - return -6; + return UPLOAD_ERR_INVALID_CHUNK_HASH; } ensure(flash_unlock_write(), NULL); @@ -621,11 +672,11 @@ int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Could not erase flash"); MSG_SEND(Failure); - return -1; + return WIPE_ERR_CANNOT_ERASE; } else { MSG_SEND_INIT(Success); MSG_SEND(Success); - return 0; + return WIPE_OK; } } diff --git a/core/embed/bootloader_ci/messages.h b/core/embed/bootloader_ci/messages.h index fd11b82ac7..0937f60627 100644 --- a/core/embed/bootloader_ci/messages.h +++ b/core/embed/bootloader_ci/messages.h @@ -29,6 +29,24 @@ #define FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT 2 +enum { + UPLOAD_OK = 0, + UPLOAD_ERR_INVALID_CHUNK_SIZE = -1, + UPLOAD_ERR_INVALID_VENDOR_HEADER = -2, + UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG = -3, + UPLOAD_ERR_INVALID_IMAGE_HEADER = -4, + UPLOAD_ERR_INVALID_IMAGE_MODEL = -5, + UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG = -6, + UPLOAD_ERR_USER_ABORT = -7, + UPLOAD_ERR_FIRMWARE_TOO_BIG = -8, + UPLOAD_ERR_INVALID_CHUNK_HASH = -9, +}; + +enum { + WIPE_OK = 0, + WIPE_ERR_CANNOT_ERASE = -1, +}; + secbool msg_parse_header(const uint8_t *buf, uint16_t *msg_id, uint32_t *msg_size); diff --git a/core/embed/firmware/bl_check.c b/core/embed/firmware/bl_check.c index 444feb78c0..79279bc854 100644 --- a/core/embed/firmware/bl_check.c +++ b/core/embed/firmware/bl_check.c @@ -20,12 +20,14 @@ #include #include #include "blake2s.h" +#include "board_capabilities.h" #include "common.h" #include "flash.h" +#include "image.h" // symbols from bootloader.bin => bootloader.o -extern const uint32_t _binary_embed_firmware_bootloader_bin_start; -extern const uint32_t _binary_embed_firmware_bootloader_bin_size; +extern const void _binary_embed_firmware_bootloader_bin_start; +extern const void _binary_embed_firmware_bootloader_bin_size; /* static secbool known_bootloader(const uint8_t *hash, int len) { @@ -97,6 +99,48 @@ void check_and_replace_bootloader(void) { (const uint32_t *)&_binary_embed_firmware_bootloader_bin_start; const uint32_t len = (const uint32_t)&_binary_embed_firmware_bootloader_bin_size; + + const image_header *new_bld_hdr = read_image_header( + (uint8_t *)data, BOOTLOADER_IMAGE_MAGIC, BOOTLOADER_IMAGE_MAXSIZE); + + ensure(new_bld_hdr == (const image_header *)data ? sectrue : secfalse, + "Invalid embedded bootloader"); + + ensure(check_image_model(new_bld_hdr), "Incompatible embedded bootloader"); + + const image_header *current_bld_hdr = read_image_header( + bl_data, BOOTLOADER_IMAGE_MAGIC, BOOTLOADER_IMAGE_MAXSIZE); + + // cannot find valid header for current bootloader, something is wrong + ensure(current_bld_hdr == (const image_header *)bl_data ? sectrue : secfalse, + "Invalid bootloader header"); + + ensure(check_image_model(current_bld_hdr), "Incompatible bootloader found"); + + if (new_bld_hdr->monotonic < current_bld_hdr->monotonic) { + // reject downgrade + return; + } + + uint32_t board_name = get_board_name(); + if (board_name == 0 || strncmp((const char *)&board_name, "T2T1", 4) == 0) { + // no board capabilities, assume Model T + if ((strncmp((const char *)&new_bld_hdr->hw_model, "T2T1", 4) != 0) && + (new_bld_hdr->hw_model != 0)) { + // reject non-model T bootloader + // 0 represents pre-model check bootloader + ensure(secfalse, "Incompatible embedded bootloader"); + } + } + // at this point, due to the previous check_image_model call, we know that the + // new_bld_hdr is + // meant for the same model as this firmware, so we can check the board name + // against the firmware hw_model. + else if (board_name != HW_MODEL) { + // reject incompatible bootloader + ensure(secfalse, "Incompatible embedded bootloader"); + } + ensure(flash_erase(FLASH_SECTOR_BOOTLOADER), NULL); ensure(flash_unlock_write(), NULL); for (int i = 0; i < len / sizeof(uint32_t); i++) { diff --git a/core/embed/firmware/header.S b/core/embed/firmware/header.S index 9d48890f39..3531e23955 100644 --- a/core/embed/firmware/header.S +++ b/core/embed/firmware/header.S @@ -13,7 +13,11 @@ g_header: .byte 'T','R','Z','F' // magic .word g_header_end - g_header // hdrlen +#ifdef TREZOR_MODEL_T .word 0 // expiry +#else + .word 1 // expiry +#endif .word _codelen // codelen .byte VERSION_MAJOR // vmajor .byte VERSION_MINOR // vminor @@ -23,7 +27,10 @@ g_header: .byte FIX_VERSION_MINOR // fix_vminor .byte FIX_VERSION_PATCH // fix_vpatch .byte FIX_VERSION_BUILD // fix_vbuild - . = . + 8 // reserved + .word HW_MODEL // type of the designated hardware + .byte HW_REVISION // revision of the designated hardware + .byte VERSION_MONOTONIC // monotonic version of the binary + . = . + 2 // reserved . = . + 512 // hash1 ... hash16 #if !defined TREZOR_MODEL_1 diff --git a/core/embed/firmware/version.h b/core/embed/firmware/version.h index 4e21240996..908621e6fa 100644 --- a/core/embed/firmware/version.h +++ b/core/embed/firmware/version.h @@ -7,3 +7,5 @@ #define FIX_VERSION_MINOR 4 #define FIX_VERSION_PATCH 0 #define FIX_VERSION_BUILD 0 + +#define VERSION_MONOTONIC 1 diff --git a/core/embed/prodtest/header.S b/core/embed/prodtest/header.S index 18a2bdaa4f..ce84552baa 100644 --- a/core/embed/prodtest/header.S +++ b/core/embed/prodtest/header.S @@ -10,7 +10,11 @@ g_header: .byte 'T','R','Z','F' // magic .word g_header_end - g_header // hdrlen +#ifdef TREZOR_MODEL_T .word 0 // expiry +#else + .word 1 // expiry +#endif .word _codelen // codelen .byte VERSION_MAJOR // vmajor .byte VERSION_MINOR // vminor @@ -20,7 +24,10 @@ g_header: .byte FIX_VERSION_MINOR // fix_vminor .byte FIX_VERSION_PATCH // fix_vpatch .byte FIX_VERSION_BUILD // fix_vbuild - . = . + 8 // reserved + .word HW_MODEL // type of the designated hardware + .byte HW_REVISION // revision of the designated hardware + .byte VERSION_MONOTONIC // monotonic version of the binary + . = . + 2 // reserved . = . + 512 // hash1 ... hash16 . = . + 415 // reserved .byte 0 // sigmask diff --git a/core/embed/prodtest/version.h b/core/embed/prodtest/version.h index f5eeee876a..2279c0cc06 100644 --- a/core/embed/prodtest/version.h +++ b/core/embed/prodtest/version.h @@ -7,3 +7,5 @@ #define FIX_VERSION_MINOR 1 #define FIX_VERSION_PATCH 0 #define FIX_VERSION_BUILD 0 + +#define VERSION_MONOTONIC 1 diff --git a/core/embed/reflash/header.S b/core/embed/reflash/header.S index 18a2bdaa4f..ce84552baa 100644 --- a/core/embed/reflash/header.S +++ b/core/embed/reflash/header.S @@ -10,7 +10,11 @@ g_header: .byte 'T','R','Z','F' // magic .word g_header_end - g_header // hdrlen +#ifdef TREZOR_MODEL_T .word 0 // expiry +#else + .word 1 // expiry +#endif .word _codelen // codelen .byte VERSION_MAJOR // vmajor .byte VERSION_MINOR // vminor @@ -20,7 +24,10 @@ g_header: .byte FIX_VERSION_MINOR // fix_vminor .byte FIX_VERSION_PATCH // fix_vpatch .byte FIX_VERSION_BUILD // fix_vbuild - . = . + 8 // reserved + .word HW_MODEL // type of the designated hardware + .byte HW_REVISION // revision of the designated hardware + .byte VERSION_MONOTONIC // monotonic version of the binary + . = . + 2 // reserved . = . + 512 // hash1 ... hash16 . = . + 415 // reserved .byte 0 // sigmask diff --git a/core/embed/reflash/version.h b/core/embed/reflash/version.h index f5eeee876a..2279c0cc06 100644 --- a/core/embed/reflash/version.h +++ b/core/embed/reflash/version.h @@ -7,3 +7,5 @@ #define FIX_VERSION_MINOR 1 #define FIX_VERSION_PATCH 0 #define FIX_VERSION_BUILD 0 + +#define VERSION_MONOTONIC 1 diff --git a/core/embed/trezorhal/board_capabilities.c b/core/embed/trezorhal/board_capabilities.c index 235f4b1e58..a4095cb318 100644 --- a/core/embed/trezorhal/board_capabilities.c +++ b/core/embed/trezorhal/board_capabilities.c @@ -24,11 +24,11 @@ #define handle_fault(msg) \ (__fatal_error("Fault detected", msg, __FILE__, __LINE__, __func__)) -static uint8_t board_name[MODEL_NAME_MAX_LENGTH + 1] = {0}; +static uint32_t board_name = 0; static struct BoardloaderVersion boardloader_version; -const uint8_t *get_board_name() { return board_name; } +const uint32_t get_board_name() { return board_name; } const struct BoardloaderVersion *get_boardloader_version() { return &boardloader_version; @@ -49,7 +49,6 @@ void parse_boardloader_capabilities() { while (pos <= end - 2) { enum CapabilityTag tag = pos[0]; uint8_t length = pos[1]; - uint8_t used_length; pos += 2; if (pos + length > end) { @@ -61,9 +60,10 @@ void parse_boardloader_capabilities() { // not used yet, just advance pointer break; case MODEL_NAME: - used_length = MIN(MODEL_NAME_MAX_LENGTH, length); - memcpy(board_name, pos, used_length); - board_name[MODEL_NAME_MAX_LENGTH] = 0; + if (length != sizeof(uint32_t)) { + break; + } + memcpy((uint8_t *)&board_name, pos, sizeof(uint32_t)); break; case BOARDLOADER_VERSION: if (length != sizeof(boardloader_version)) { diff --git a/core/embed/trezorhal/board_capabilities.h b/core/embed/trezorhal/board_capabilities.h index ead351053a..807cd2e1a4 100644 --- a/core/embed/trezorhal/board_capabilities.h +++ b/core/embed/trezorhal/board_capabilities.h @@ -38,7 +38,6 @@ Last tag must be terminator or all space used. #define BOARD_CAPABILITIES_ADDR 0x0800BF00 #define BOARD_CAPABILITIES_SIZE 256 #define CAPABILITIES_HEADER "TRZC" -#define MODEL_NAME_MAX_LENGTH 16 enum CapabilityTag { TERMINATOR = 0x00, @@ -62,7 +61,7 @@ struct __attribute__((packed)) BoardCapabilities { uint8_t header[4]; uint8_t model_tag; uint8_t model_length; - uint8_t model_name[MODEL_NAME_MAX_LENGTH]; + uint32_t model_name; uint8_t version_tag; uint8_t version_length; struct BoardloaderVersion version; @@ -76,7 +75,7 @@ struct __attribute__((packed)) BoardCapabilities { */ void parse_boardloader_capabilities(); -const uint8_t* get_board_name(); +const uint32_t get_board_name(); const struct BoardloaderVersion* get_boardloader_version(); #endif diff --git a/core/embed/trezorhal/image.c b/core/embed/trezorhal/image.c index 97039891dc..44d536d46a 100644 --- a/core/embed/trezorhal/image.c +++ b/core/embed/trezorhal/image.c @@ -50,57 +50,84 @@ static secbool compute_pubkey(uint8_t sig_m, uint8_t sig_n, return sectrue * (0 == ed25519_cosi_combine_publickeys(res, keys, sig_m)); } -secbool load_image_header(const uint8_t *const data, const uint32_t magic, - const uint32_t maxsize, uint8_t key_m, uint8_t key_n, - const uint8_t *const *keys, image_header *const hdr) { - memcpy(&hdr->magic, data, 4); - if (hdr->magic != magic) return secfalse; +const image_header *read_image_header(const uint8_t *const data, + const uint32_t magic, + const uint32_t maxsize) { + const image_header *hdr = (const image_header *)data; - memcpy(&hdr->hdrlen, data + 4, 4); - if (hdr->hdrlen != IMAGE_HEADER_SIZE) return secfalse; + if (hdr->magic != magic) { + return NULL; + } + if (hdr->hdrlen != IMAGE_HEADER_SIZE) { + return NULL; + } - memcpy(&hdr->expiry, data + 8, 4); // TODO: expiry mechanism needs to be ironed out before production or those // devices won't accept expiring bootloaders (due to boardloader write // protection). - if (hdr->expiry != 0) return secfalse; + // lowest bit is used for breaking compatibility between old TT bootloaders + // and non TT images + // which is evaluated in check_image_model function + if ((hdr->expiry & 0xFFFFFFFE) != 0) return secfalse; - memcpy(&hdr->codelen, data + 12, 4); if (hdr->codelen > (maxsize - hdr->hdrlen)) return secfalse; if ((hdr->hdrlen + hdr->codelen) < 4 * 1024) return secfalse; if ((hdr->hdrlen + hdr->codelen) % 512 != 0) return secfalse; - memcpy(&hdr->version, data + 16, 4); - memcpy(&hdr->fix_version, data + 20, 4); + return hdr; +} - memcpy(hdr->hashes, data + 32, 512); +secbool check_image_model(const image_header *const hdr) { + // abusing expiry field to break compatibility of non-TT images with existing + // bootloaders/boardloaders +#ifdef TREZOR_MODEL_T + if (hdr->expiry == 0 && hdr->hw_model == 0 && hdr->hw_revision == 0) { + // images for model TT older than this check + return sectrue; + } +#else + if ((hdr->expiry & 0x01) == 0) { + // for models other than TT, expiry == 0 is unacceptable, as the image will + // run on bootloaders older that this check + return secfalse; + } +#endif - memcpy(&hdr->sigmask, data + IMAGE_HEADER_SIZE - IMAGE_SIG_SIZE, 1); + if (hdr->hw_model != HW_MODEL) { + return secfalse; + } + if (hdr->hw_revision != HW_REVISION) { + return secfalse; + } - memcpy(hdr->sig, data + IMAGE_HEADER_SIZE - IMAGE_SIG_SIZE + 1, - IMAGE_SIG_SIZE - 1); + return sectrue; +} +secbool check_image_header_sig(const image_header *const hdr, uint8_t key_m, + uint8_t key_n, const uint8_t *const *keys) { // check header signature - BLAKE2S_CTX ctx; + uint8_t fingerprint[32]; + blake2s_Init(&ctx, BLAKE2S_DIGEST_LENGTH); - blake2s_Update(&ctx, data, IMAGE_HEADER_SIZE - IMAGE_SIG_SIZE); + blake2s_Update(&ctx, hdr, IMAGE_HEADER_SIZE - IMAGE_SIG_SIZE); for (int i = 0; i < IMAGE_SIG_SIZE; i++) { blake2s_Update(&ctx, (const uint8_t *)"\x00", 1); } - blake2s_Final(&ctx, hdr->fingerprint, BLAKE2S_DIGEST_LENGTH); + + blake2s_Final(&ctx, fingerprint, BLAKE2S_DIGEST_LENGTH); ed25519_public_key pub; if (sectrue != compute_pubkey(key_m, key_n, keys, hdr->sigmask, pub)) return secfalse; return sectrue * - (0 == ed25519_sign_open(hdr->fingerprint, BLAKE2S_DIGEST_LENGTH, pub, + (0 == ed25519_sign_open(fingerprint, BLAKE2S_DIGEST_LENGTH, pub, *(const ed25519_signature *)hdr->sig)); } -secbool read_vendor_header(const uint8_t *const data, - vendor_header *const vhdr) { +secbool __wur read_vendor_header(const uint8_t *const data, + vendor_header *const vhdr) { memcpy(&vhdr->magic, data, 4); if (vhdr->magic != 0x565A5254) return secfalse; // TRZV @@ -110,6 +137,8 @@ secbool read_vendor_header(const uint8_t *const data, memcpy(&vhdr->expiry, data + 8, 4); if (vhdr->expiry != 0) return secfalse; + vhdr->origin = data; + memcpy(&vhdr->version, data + 12, 2); memcpy(&vhdr->vsig_m, data + 14, 1); @@ -143,10 +172,9 @@ secbool read_vendor_header(const uint8_t *const data, return sectrue; } -secbool load_vendor_header(const uint8_t *const data, uint8_t key_m, - uint8_t key_n, const uint8_t *const *keys, - vendor_header *const vhdr) { - if (sectrue != read_vendor_header(data, vhdr)) { +secbool check_vendor_header_sig(const vendor_header *const vhdr, uint8_t key_m, + uint8_t key_n, const uint8_t *const *keys) { + if (vhdr == NULL) { return secfalse; } @@ -155,7 +183,7 @@ secbool load_vendor_header(const uint8_t *const data, uint8_t key_m, uint8_t hash[BLAKE2S_DIGEST_LENGTH]; BLAKE2S_CTX ctx; blake2s_Init(&ctx, BLAKE2S_DIGEST_LENGTH); - blake2s_Update(&ctx, data, vhdr->hdrlen - IMAGE_SIG_SIZE); + blake2s_Update(&ctx, vhdr->origin, vhdr->hdrlen - IMAGE_SIG_SIZE); for (int i = 0; i < IMAGE_SIG_SIZE; i++) { blake2s_Update(&ctx, (const uint8_t *)"\x00", 1); } diff --git a/core/embed/trezorhal/image.h b/core/embed/trezorhal/image.h index 56d233efa8..21c1aa64b0 100644 --- a/core/embed/trezorhal/image.h +++ b/core/embed/trezorhal/image.h @@ -45,12 +45,14 @@ typedef struct { uint32_t codelen; uint32_t version; uint32_t fix_version; - // uint8_t reserved[8]; + uint32_t hw_model; + uint8_t hw_revision; + uint8_t monotonic; + uint8_t reserved_0[2]; uint8_t hashes[512]; - // uint8_t reserved[415]; + uint8_t reserved_1[415]; uint8_t sigmask; uint8_t sig[64]; - uint8_t fingerprint[32]; } image_header; #define MAX_VENDOR_PUBLIC_KEYS 8 @@ -76,20 +78,26 @@ typedef struct { const uint8_t *vimg; uint8_t sigmask; uint8_t sig[64]; + const uint8_t *origin; // pointer to the underlying data } vendor_header; -secbool __wur load_image_header(const uint8_t *const data, const uint32_t magic, - const uint32_t maxsize, uint8_t key_m, - uint8_t key_n, const uint8_t *const *keys, - image_header *const hdr); +const image_header *read_image_header(const uint8_t *const data, + const uint32_t magic, + const uint32_t maxsize); -secbool __wur load_vendor_header(const uint8_t *const data, uint8_t key_m, - uint8_t key_n, const uint8_t *const *keys, - vendor_header *const vhdr); +secbool __wur check_image_model(const image_header *const hdr); + +secbool __wur check_image_header_sig(const image_header *const hdr, + uint8_t key_m, uint8_t key_n, + const uint8_t *const *keys); secbool __wur read_vendor_header(const uint8_t *const data, vendor_header *const vhdr); +secbool __wur check_vendor_header_sig(const vendor_header *const vhdr, + uint8_t key_m, uint8_t key_n, + const uint8_t *const *keys); + void vendor_header_hash(const vendor_header *const vhdr, uint8_t *hash); secbool __wur check_single_hash(const uint8_t *const hash, diff --git a/core/site_scons/tools.py b/core/site_scons/tools.py index 96dc5d2e52..a9953d4190 100644 --- a/core/site_scons/tools.py +++ b/core/site_scons/tools.py @@ -9,16 +9,27 @@ def add_font(font_name, font, defines, sources): sources.append(sourcefile) +def get_hw_model_as_number(hw_model): + return int.from_bytes(hw_model.encode(), 'little') + + + def configure_board(model, env, defines, sources): model_r_version = 4 if model in ('1',): board = 'trezor_1.h' display = 'vg-2864ksweg01.c' + hw_model = get_hw_model_as_number('T1B1') + hw_revision = 0 elif model in ('T',): board = 'trezor_t.h' display = 'st7789v.c' + hw_model = get_hw_model_as_number('T2T1') + hw_revision = 0 elif model in ('R',): + hw_model = get_hw_model_as_number('T2B1') + hw_revision = model_r_version if model_r_version == 3: board = 'trezor_r_v3.h' display = "ug-2828tswig01.c" @@ -29,5 +40,7 @@ def configure_board(model, env, defines, sources): raise Exception("Unknown model") defines += [f'TREZOR_BOARD=\\"boards/{board}\\"', ] + defines += [f'HW_MODEL={hw_model}', ] + defines += [f'HW_REVISION={hw_revision}', ] sources += [f'embed/trezorhal/displays/{display}', ] env.get('ENV')['TREZOR_BOARD'] = board