firmware/stm32/bootloader/verify.c
2020-11-18 14:19:14 -05:00

260 lines
6.6 KiB
C

/*
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
*
* verify.c -- Check signatures on firmware images in flash.
*
*/
#include "verify.h"
#include "sha256.h"
#include "assets/screens.h"
#include "oled.h"
#include "misc.h"
#include "ae.h"
#include "gpio.h"
#include "delay.h"
#include "storage.h"
#include <string.h>
#include "micro-ecc/uECC.h"
#include "firmware-keys.h"
// 1 megabyte
#define MAIN_FLASH_SIZE (1<<20)
// actual qty of bytes we check
// - minus 64 because the firmware signature is skipped
// - 0x400 of OTP area
// - plus (0x28) twice for option bytes
// - plus complete "system meory" area (boot rom with DFU)
// - 12 bytes of device serial number
#define TOTAL_CHECKSUM_LEN (MAIN_FLASH_SIZE - 64 + 0x400 + (0x28 *2) + 0x7000 + 12)
// checksum_more()
//
static void
checksum_more(SHA256_CTX *ctx, uint32_t *total, const uint8_t *addr, int len)
{
// do a nice progress bar as it works.
while(len) {
int here = MIN(len, 8196);
int percent = ((*total) * 100) / TOTAL_CHECKSUM_LEN;
oled_show_progress(screen_verify, percent);
sha256_update(ctx, addr, here);
*total += here;
addr += here;
len -= here;
if(dfu_button_pressed()) dfu_by_request();
}
}
// checksum_flash()
//
void
checksum_flash(uint8_t fw_digest[32], uint8_t world_digest[32])
{
const uint8_t *start = (const uint8_t *)FIRMWARE_START;
SHA256_CTX ctx;
uint32_t total_len = 0;
uint8_t first[32];
sha256_init(&ctx);
// start of firmware (just after we end) to header
checksum_more(&ctx, &total_len, start, FW_HEADER_OFFSET + FW_HEADER_SIZE - 64);
// from after header to end
checksum_more(&ctx, &total_len, start + FW_HEADER_OFFSET + FW_HEADER_SIZE,
FW_HDR->firmware_length - (FW_HEADER_OFFSET + FW_HEADER_SIZE));
sha256_final(&ctx, first);
// double SHA256
sha256_init(&ctx);
sha256_update(&ctx, first, sizeof(first));
sha256_final(&ctx, fw_digest);
// start over, and get the rest of flash. All of it.
sha256_init(&ctx);
// .. and chain in what we have so far
sha256_update(&ctx, fw_digest, 32);
// bootloader, including pairing secret area.
const uint8_t *base = (const uint8_t *)BL_FLASH_BASE;
checksum_more(&ctx, &total_len, base, start-base);
// probably-blank area after firmware, and filesystem area
const uint8_t *fs = start + FW_HDR->firmware_length;
const uint8_t *last = base + MAIN_FLASH_SIZE;
checksum_more(&ctx, &total_len, fs, last-fs);
// OTP area
checksum_more(&ctx, &total_len, (void *)0x1fff7000, 0x400);
// "just in case" ... the option bytes (2 banks)
checksum_more(&ctx, &total_len, (void *)0x1fff7800, 0x28);
checksum_more(&ctx, &total_len, (void *)0x1ffff800, 0x28);
// System ROM (they say it can't change, but clearly
// implemented as flash cells)
checksum_more(&ctx, &total_len, (void *)0x1fff0000, 0x7000);
// device serial number, just for kicks
checksum_more(&ctx, &total_len, (void *)0x1fff7590, 12);
ASSERT(total_len == TOTAL_CHECKSUM_LEN);
sha256_final(&ctx, world_digest);
// double SHA256 (a bitcoin fetish)
sha256_init(&ctx);
sha256_update(&ctx, world_digest, 32);
sha256_final(&ctx, world_digest);
}
// get_min_version()
//
// Scan the OTP area and determine what the current min-version (timestamp)
// we can allow. All zeros if any if okay.
//
void
get_min_version(uint8_t min_version[8])
{
const uint8_t *otp = (const uint8_t *)OPT_FLASH_BASE;
memset(min_version, 0, 8);
for(int i=0; i<NUM_OPT_SLOTS; i++, otp+=8) {
// is it programmed?
if(otp[0] == 0xff) continue;
// is it a timestamp value?
if(otp[0] >= 0x40) continue;
if(otp[0] < 0x10) continue;
if(memcmp(otp, min_version, 8) > 0) {
memcpy(min_version, otp, 8);
}
}
}
// check_is_downgrade()
//
bool
check_is_downgrade(const uint8_t timestamp[8], const char *version)
{
if(version) {
int major = (version[1] == '.') ? (version[0]-'0') : 10;
if(major < 3) {
// we require major version 3.0.0 or later (for mark3 hardware)
return true;
}
}
// look at FW_HDR->timestamp and compare to a growing list in main flash OTP
uint8_t min[8];
get_min_version(min);
return (memcmp(timestamp, min, 8) < 0);
}
// check_factory_key()
//
void
check_factory_key(uint32_t pubkey_num)
{
if(IS_FACTORY_KEY(pubkey_num)) return;
// warn the victim/altcoin user/developer
#if RELEASE
const int wait = 100;
#else
const int wait = 1;
#endif
for(int i=0; i < wait; i++) {
oled_show_progress(screen_devmode, (i*100)/wait);
delay_ms(250);
}
}
// verify_header()
//
bool
verify_header(const coldcardFirmwareHeader_t *hdr)
{
if(hdr->magic_value != FW_HEADER_MAGIC) goto fail;
if(hdr->version_string[0] == 0x0) goto fail;
if(hdr->timestamp[0] >= 0x40) goto fail; // 22 yr product lifetime
if(hdr->firmware_length < FW_MIN_LENGTH) goto fail;
if(hdr->firmware_length > FW_MAX_LENGTH) goto fail;
if(hdr->pubkey_num >= NUM_KNOWN_PUBKEYS) goto fail;
return true;
fail:
return false;
}
// verify_signature()
//
// Given double-sha256 over the firmware bytes, check the signature.
//
bool
verify_signature(const coldcardFirmwareHeader_t *hdr, const uint8_t fw_check[32])
{
int ok = uECC_verify(approved_pubkeys[hdr->pubkey_num], fw_check, 32,
hdr->signature, uECC_secp256k1());
return ok;
}
// verify_firmware()
//
void
verify_firmware(void)
{
STATIC_ASSERT(sizeof(coldcardFirmwareHeader_t) == FW_HEADER_SIZE);
// watch for unprogrammed header. and some
if(FW_HDR->version_string[0] == 0xff) goto blank;
if(!verify_header(FW_HDR)) goto fail;
// measure checksum
uint8_t fw_check[32], world_check[32];
checksum_flash(fw_check, world_check);
// Verify the signature
// - use pubkey_num to pick a specific key
if(!verify_signature(FW_HDR, fw_check)) goto fail;
// Push the hash to the 508a which might make the Genuine light green,
// but only if we arrived at same hash before. It decides.
int not_green = ae_set_gpio_secure(world_check);
// maybe show big warning if not an "approved" key
if(not_green) {
check_factory_key(FW_HDR->pubkey_num);
}
oled_show_progress(screen_verify, 100);
return;
fail:
oled_show(screen_corrupt);
enter_dfu();
return;
blank:
oled_show(screen_dfu);
enter_dfu();
return;
}
// EOF