From 18e1d0d190c02a5dcede2e22d6ce0971cfa1f44e Mon Sep 17 00:00:00 2001 From: keepkeyjon Date: Wed, 21 Mar 2018 10:24:59 -0600 Subject: [PATCH] Disallow execution of unsigned firmware --- bootloader/local/baremetal/bootloader_main.c | 6 +- bootloader/local/baremetal/signatures.c | 10 +- bootloader/local/baremetal/usb_flash.c | 2 +- bootloader/public/signatures.h | 5 +- keepkey/local/baremetal/check_bootloader.c | 132 +++++++++++++++++++ keepkey/local/baremetal/keepkey_main.c | 4 + keepkey/local/baremetal/storage.c | 1 + keepkey/local/baremetal/storage_versions.inc | 3 +- keepkey/public/check_bootloader.h | 25 ++++ keepkey/public/storage.h | 2 +- keepkey_board/local/baremetal/memory.c | 11 ++ keepkey_board/public/memory.h | 2 + version.json | 2 +- 13 files changed, 194 insertions(+), 11 deletions(-) create mode 100644 keepkey/local/baremetal/check_bootloader.c create mode 100644 keepkey/public/check_bootloader.h diff --git a/bootloader/local/baremetal/bootloader_main.c b/bootloader/local/baremetal/bootloader_main.c index 569820721..837d61d47 100644 --- a/bootloader/local/baremetal/bootloader_main.c +++ b/bootloader/local/baremetal/bootloader_main.c @@ -187,18 +187,22 @@ static bool boot(void) { layout_home(); - if(signatures_ok() == 0) /* Signature check failed */ + if(signatures_ok() != SIG_OK) /* Signature check failed */ { delay_ms(500); +#if !MEMORY_PROTECT if(!confirm_without_button_request("Unofficial Firmware", "Do you want to continue booting?")) { +#endif layout_simple_message("Boot Aborted"); goto cancel_boot; +#if !MEMORY_PROTECT } layout_home(); +#endif } led_func(CLR_RED_LED); diff --git a/bootloader/local/baremetal/signatures.c b/bootloader/local/baremetal/signatures.c index 789dee039..f2bb18a7b 100644 --- a/bootloader/local/baremetal/signatures.c +++ b/bootloader/local/baremetal/signatures.c @@ -117,21 +117,21 @@ int signatures_ok(void) if(ecdsa_verify_digest(&secp256k1, pubkey[sigindex1 - 1], (uint8_t *)FLASH_META_SIG1, firmware_fingerprint) != 0) /* Failure */ { - return 0; + return SIG_FAIL; } if(ecdsa_verify_digest(&secp256k1, pubkey[sigindex2 - 1], (uint8_t *)FLASH_META_SIG2, firmware_fingerprint) != 0) /* Failure */ { - return 0; + return SIG_FAIL; } if(ecdsa_verify_digest(&secp256k1, pubkey[sigindex3 - 1], (uint8_t *)FLASH_META_SIG3, firmware_fingerprint) != 0) /* Failure */ { - return 0; + return SIG_FAIL; } #endif - return 1; -} \ No newline at end of file + return SIG_OK; +} diff --git a/bootloader/local/baremetal/usb_flash.c b/bootloader/local/baremetal/usb_flash.c index 23b79c129..5ed4ae943 100644 --- a/bootloader/local/baremetal/usb_flash.c +++ b/bootloader/local/baremetal/usb_flash.c @@ -223,7 +223,7 @@ bool usb_flash_firmware(void) case RAW_MESSAGE_COMPLETE: { /* Verify the image is from KeepKey */ - if((SIG_FLAG == 1) && (signatures_ok() == 1)) + if((SIG_FLAG == 1) && (signatures_ok() == SIG_OK)) { /* The image is from KeepKey. Restore storage data */ if(!storage_restore()) diff --git a/bootloader/public/signatures.h b/bootloader/public/signatures.h index 6edd3cea2..0c803a243 100644 --- a/bootloader/public/signatures.h +++ b/bootloader/public/signatures.h @@ -26,8 +26,11 @@ #define PUBKEY_LENGTH 65 #define SIGNATURES 3 +#define SIG_OK 0x5A3CA5C3 +#define SIG_FAIL 0x00000000 + /* === Functions =========================================================== */ int signatures_ok(void); -#endif \ No newline at end of file +#endif diff --git a/keepkey/local/baremetal/check_bootloader.c b/keepkey/local/baremetal/check_bootloader.c new file mode 100644 index 000000000..37e41c09a --- /dev/null +++ b/keepkey/local/baremetal/check_bootloader.c @@ -0,0 +1,132 @@ +/* + * This file is part of the KeepKey project. + * + * Copyright (C) 2018 keepkeyjon + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include "keepkey_board.h" +#include "layout.h" +#include "memory.h" +#include "sha2.h" + +#include + +#include +#include +#include + +enum BL_Kind { + BL_UNKNOWN = 0x0, + BL_HOTPATCHABLE = 0xa1f35c78, + BL_PATCH_APPLIED = 0x95c3a027, +}; + +static enum BL_Kind check_bootloader_kind(void) { + static uint8_t bl_hash[SHA256_DIGEST_LENGTH]; + if (32 != memory_bootloader_hash(bl_hash)) + return BL_UNKNOWN; + + // Fixed bootloaders + // --------------------- + if (0 == memcmp(bl_hash, "\xf1\x3c\xe2\x28\xc0\xbb\x2b\xdb\xc5\x6b\xdc\xb5\xf4\x56\x93\x67\xf8\xe3\x01\x10\x74\xcc\xc6\x33\x31\x34\x8d\xeb\x49\x8f\x2d\x8f", 32)) + return BL_PATCH_APPLIED; // v1.0.0, fixed + + if (0 == memcmp(bl_hash, "\xec\x61\x88\x36\xf8\x64\x23\xdb\xd3\x11\x4c\x37\xd6\xe3\xe4\xff\xdf\xb8\x7d\x9e\x4c\x61\x99\xcf\x3e\x16\x3a\x67\xb2\x74\x98\xa2", 32)) + return BL_PATCH_APPLIED; // v1.0.1, fixed + + if (0 == memcmp(bl_hash, "\x4f\x9c\x38\xc1\xcd\x06\xf5\x9e\x8d\x4d\xe8\xe0\xd3\x1c\xdd\x34\xc8\x31\x44\xd2\xdf\x55\x0c\x41\x2e\x00\x2b\x4b\x35\xbd\x4f\xb3", 32)) + return BL_PATCH_APPLIED; // v1.0.3, fixed + + if (0 == memcmp(bl_hash, "\x91\x7d\x19\x52\x26\x0c\x9b\x89\xf3\xa9\x6b\xea\x07\xee\xa4\x07\x4a\xfd\xcc\x0e\x8c\xdd\x5d\x06\x4e\x36\x86\x8b\xdd\x68\xba\x7d", 32)) + return BL_PATCH_APPLIED; // v1.0.3_signed, fixed + + if (0 == memcmp(bl_hash, "\xfc\x4e\x5c\x4d\xc2\xe5\x12\x7b\x68\x14\xa3\xf6\x94\x24\xc9\x36\xf1\xdc\x24\x1d\x1d\xaf\x2c\x5a\x2d\x8f\x07\x28\xeb\x69\xd2\x0d", 32)) + return BL_PATCH_APPLIED; // v1.0.4, fixed - SALT whitelabel + + // Unpatched bootloaders + // --------------------- + if (0 == memcmp(bl_hash, "\x63\x97\xc4\x46\xf6\xb9\x00\x2a\x8b\x15\x0b\xf4\xb9\xb4\xe0\xbb\x66\x80\x0e\xd0\x99\xb8\x81\xca\x49\x70\x01\x39\xb0\x55\x9f\x10", 32)) + return BL_HOTPATCHABLE; // v1.0.0, unpatched + + if (0 == memcmp(bl_hash, "\xd5\x44\xb5\xe0\x6b\x0c\x35\x5d\x68\xb8\x68\xac\x75\x80\xe9\xba\xb2\xd2\x24\xa1\xe2\x44\x08\x81\xcc\x1b\xca\x2b\x81\x67\x52\xd5", 32)) + return BL_HOTPATCHABLE; // v1.0.1, unpatched + + if (0 == memcmp(bl_hash, "\x5a\xa5\x5e\x69\xf1\xd9\xaa\x50\x4d\xe6\x0f\xaf\x22\xbe\x93\xcb\xd0\x3b\x13\x73\x2d\xcb\x07\xbb\xc0\xb7\xf9\x1d\x42\xe1\x4c\xcc", 32)) + return BL_HOTPATCHABLE; // v1.0.3, unpatched + + if (0 == memcmp(bl_hash, "\xcb\x22\x25\x48\xa3\x9f\xf6\xcb\xe2\xae\x2f\x02\xc8\xd4\x31\xc9\xae\x0d\xf8\x50\xf8\x14\x44\x49\x11\xf5\x21\xb9\x5a\xb0\x2f\x4c", 32)) + return BL_HOTPATCHABLE; // v1.0.3_signed, unpatched + + if (0 == memcmp(bl_hash, "\x77\x0b\x30\xaa\xa0\xbe\x88\x4e\xe8\x62\x18\x59\xf5\xd0\x55\x43\x7f\x89\x4a\x5c\x9c\x7c\xa2\x26\x35\xe7\x02\x4e\x05\x98\x57\xb7", 32)) + return BL_HOTPATCHABLE; // v1.0.4, unpatched - SALT whitelabel + + return BL_UNKNOWN; +} + +/* + * Hot-patch old bootloaders to disallow executing unsigned firmwares. + * + * \returns true iff this bootloader has been hotpatched + */ +static bool apply_hotpatch(void) +{ + const uintptr_t hotpatch_addr = 0x802026c; + + static const char hotpatch[18] = { + 0x00, 0x00, // movs r0, r0 + 0x00, 0x00, // movs r0, r0 + 0x00, 0x00, // movs r0, r0 + 0x00, 0x00, // movs r0, r0 + 0x00, 0x00, // movs r0, r0 + 0x00, 0x00, // movs r0, r0 + 0x00, 0x00, // movs r0, r0 + 0x00, 0x00, // movs r0, r0 + }; + + // Enable writing to the read-only sectors + memory_unlock(); + flash_unlock(); + + // Write into the sector. + flash_program((uint32_t)hotpatch_addr, (uint8_t*)hotpatch, sizeof(hotpatch)); + + // Disallow writing to flash. + flash_lock(); + + // Ignore the reported error. + flash_clear_status_flags(); + + // Check for the hotpatch sequence + return memcmp((void*)hotpatch_addr, hotpatch, sizeof(hotpatch)) == 0; +} + +void check_bootloader(void) { + enum BL_Kind kind = check_bootloader_kind(); + if (kind == BL_HOTPATCHABLE) { + if (!apply_hotpatch()) { + layout_warning_static("Hotpatch failed. Contact support."); + system_halt(); + } + } else if (kind == BL_UNKNOWN) { + layout_warning_static("Unknown bootloader. Contact support."); + system_halt(); + } else if (kind == BL_PATCH_APPLIED) { + // do nothing, bootloader is already safe + } else { + layout_warning_static("B/L check failed. Reboot Device!"); + system_halt(); + } +} diff --git a/keepkey/local/baremetal/keepkey_main.c b/keepkey/local/baremetal/keepkey_main.c index 4b1839278..69c5f7bce 100644 --- a/keepkey/local/baremetal/keepkey_main.c +++ b/keepkey/local/baremetal/keepkey_main.c @@ -36,6 +36,7 @@ #include "storage.h" #include "fsm.h" #include "app_layout.h" +#include "check_bootloader.h" /* === Defines ============================================================= */ #define APP_VERSIONS "VERSION" \ @@ -97,6 +98,9 @@ int main(void) /* Init board */ board_init(); + /* Bootloader hotpatching */ + check_bootloader(); + /* Init for safeguard against stack overflow (-fstack-protector-all) */ __stack_chk_guard = (uintptr_t)random32(); diff --git a/keepkey/local/baremetal/storage.c b/keepkey/local/baremetal/storage.c index 18bd16b87..fb92edc47 100644 --- a/keepkey/local/baremetal/storage.c +++ b/keepkey/local/baremetal/storage.c @@ -139,6 +139,7 @@ static bool storage_from_flash(ConfigFlash *stor_config) case StorageVersion_3: case StorageVersion_4: case StorageVersion_5: + case StorageVersion_6: memcpy(&shadow_config, stor_config, sizeof(shadow_config)); /* We have to do this for users with bootloaders <= v1.0.2. This diff --git a/keepkey/local/baremetal/storage_versions.inc b/keepkey/local/baremetal/storage_versions.inc index 8bc550713..e73ec2b56 100644 --- a/keepkey/local/baremetal/storage_versions.inc +++ b/keepkey/local/baremetal/storage_versions.inc @@ -10,7 +10,8 @@ STORAGE_VERSION_ENTRY(1) STORAGE_VERSION_ENTRY(2) STORAGE_VERSION_ENTRY(3) STORAGE_VERSION_ENTRY(4) -STORAGE_VERSION_LAST(5) +STORAGE_VERSION_ENTRY(5) +STORAGE_VERSION_LAST(6) #undef STORAGE_VERSION_ENTRY #undef STORAGE_VERSION_LAST diff --git a/keepkey/public/check_bootloader.h b/keepkey/public/check_bootloader.h new file mode 100644 index 000000000..4f5adad82 --- /dev/null +++ b/keepkey/public/check_bootloader.h @@ -0,0 +1,25 @@ +/* + * This file is part of the KeepKey project. + * + * Copyright (C) 2018 keepkeyjon + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#ifndef CHECK_BOOTLOADER_H +#define CHECK_BOOTLOADER_H + +void check_bootloader(void); + +#endif diff --git a/keepkey/public/storage.h b/keepkey/public/storage.h index ca8be56e3..dda1eb81a 100644 --- a/keepkey/public/storage.h +++ b/keepkey/public/storage.h @@ -30,7 +30,7 @@ /* === Defines ============================================================= */ -#define STORAGE_VERSION 5 /* Must add case fallthrough in storage_from_flash after increment*/ +#define STORAGE_VERSION 6 /* Must add case fallthrough in storage_from_flash after increment*/ #define STORAGE_RETRIES 3 /* === Functions =========================================================== */ diff --git a/keepkey_board/local/baremetal/memory.c b/keepkey_board/local/baremetal/memory.c index ef98f6988..c9a2f46a5 100644 --- a/keepkey_board/local/baremetal/memory.c +++ b/keepkey_board/local/baremetal/memory.c @@ -55,6 +55,16 @@ void memory_protect(void) flash_lock_option_bytes(); } +/* + * Enable writing. This exercises a bug in the STM32F2 that allows writing to + * read-only sectors of flash. + */ +void memory_unlock(void) { + flash_unlock_option_bytes(); + flash_program_option_bytes(0x0FFF0001); + flash_lock_option_bytes(); +} + /* * memory_bootloader_hash() - SHA256 hash of bootloader * @@ -157,3 +167,4 @@ bool find_active_storage(Allocation *storage_location) return(ret_stat); } + diff --git a/keepkey_board/public/memory.h b/keepkey_board/public/memory.h index cdd96e03f..2990de2d3 100644 --- a/keepkey_board/public/memory.h +++ b/keepkey_board/public/memory.h @@ -24,6 +24,7 @@ #include #include +#include /* @@ -204,6 +205,7 @@ static const FlashSector flash_sector_map[] = /* === Functions =========================================================== */ void memory_protect(void); +void memory_unlock(void); int memory_bootloader_hash(uint8_t *hash); int memory_firmware_hash(uint8_t *hash); int memory_storage_hash(uint8_t *hash, Allocation storage_location); diff --git a/version.json b/version.json index 25e229197..45024cb3c 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"BOOTLOADER_PATCH_VERSION": 4, "MAJOR_VERSION": 5, "MINOR_VERSION": 0, "PATCH_VERSION": 1, "BOOTLOADER_MAJOR_VERSION": 1, "BOOTLOADER_MINOR_VERSION": 0} +{"BOOTLOADER_PATCH_VERSION": 4, "MAJOR_VERSION": 5, "MINOR_VERSION": 1, "PATCH_VERSION": 0, "BOOTLOADER_MAJOR_VERSION": 1, "BOOTLOADER_MINOR_VERSION": 0}