mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-29 01:48:23 +00:00
feat(core): verify secmon signature in bootloader
[no changelog]
This commit is contained in:
parent
fcf2bd0d48
commit
8795cbcf5c
@ -338,9 +338,37 @@ void real_jump_to_firmware(void) {
|
|||||||
&FIRMWARE_AREA),
|
&FIRMWARE_AREA),
|
||||||
"Firmware is corrupted");
|
"Firmware is corrupted");
|
||||||
|
|
||||||
secret_prepare_fw(
|
size_t secmon_code_offset = 0;
|
||||||
((vhdr.vtrust & VTRUST_SECRET_MASK) == VTRUST_SECRET_ALLOW) * sectrue,
|
|
||||||
((vhdr.vtrust & VTRUST_NO_WARNING) == VTRUST_NO_WARNING) * sectrue);
|
#ifdef USE_SECMON_VERIFICATION
|
||||||
|
size_t secmon_start = (size_t)IMAGE_CODE_ALIGN(FIRMWARE_START + vhdr.hdrlen +
|
||||||
|
IMAGE_HEADER_SIZE);
|
||||||
|
const secmon_header_t *secmon_hdr =
|
||||||
|
read_secmon_header((const uint8_t *)secmon_start, FIRMWARE_MAXSIZE);
|
||||||
|
|
||||||
|
if (secmon_hdr != NULL) {
|
||||||
|
secmon_code_offset = IMAGE_CODE_ALIGN(SECMON_HEADER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure((secmon_hdr != NULL) * sectrue, "Secmon header not found");
|
||||||
|
|
||||||
|
ensure(check_secmon_model(secmon_hdr), "Wrong secmon model");
|
||||||
|
|
||||||
|
ensure(check_secmon_header_sig(secmon_hdr), "Invalid secmon signature");
|
||||||
|
|
||||||
|
ensure(check_secmon_contents(secmon_hdr, secmon_start - FIRMWARE_START,
|
||||||
|
&FIRMWARE_AREA),
|
||||||
|
"Secmon is corrupted");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
secbool provisioning_access =
|
||||||
|
((vhdr.vtrust & VTRUST_ALLOW_PROVISIONING) == VTRUST_ALLOW_PROVISIONING) *
|
||||||
|
sectrue;
|
||||||
|
|
||||||
|
secbool secret_run_access =
|
||||||
|
((vhdr.vtrust & VTRUST_SECRET_MASK) == VTRUST_SECRET_ALLOW) * sectrue;
|
||||||
|
|
||||||
|
secret_prepare_fw(secret_run_access, provisioning_access);
|
||||||
|
|
||||||
// if all warnings are disabled in VTRUST flags then skip the procedure
|
// if all warnings are disabled in VTRUST flags then skip the procedure
|
||||||
if ((vhdr.vtrust & VTRUST_NO_WARNING) != VTRUST_NO_WARNING) {
|
if ((vhdr.vtrust & VTRUST_NO_WARNING) != VTRUST_NO_WARNING) {
|
||||||
@ -381,7 +409,8 @@ void real_jump_to_firmware(void) {
|
|||||||
system_deinit();
|
system_deinit();
|
||||||
|
|
||||||
jump_to_next_stage(
|
jump_to_next_stage(
|
||||||
IMAGE_CODE_ALIGN(FIRMWARE_START + vhdr.hdrlen + IMAGE_HEADER_SIZE));
|
IMAGE_CODE_ALIGN(FIRMWARE_START + vhdr.hdrlen + IMAGE_HEADER_SIZE) +
|
||||||
|
secmon_code_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((noreturn)) void reboot_with_fade(void) {
|
__attribute__((noreturn)) void reboot_with_fade(void) {
|
||||||
@ -398,7 +427,11 @@ int bootloader_main(void) {
|
|||||||
|
|
||||||
system_init(&rsod_panic_handler);
|
system_init(&rsod_panic_handler);
|
||||||
|
|
||||||
secbool manufacturing_mode = is_manufacturing_mode();
|
vendor_header vhdr;
|
||||||
|
volatile secbool vhdr_present = secfalse;
|
||||||
|
vhdr_present = read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr);
|
||||||
|
|
||||||
|
secbool manufacturing_mode = is_manufacturing_mode(&vhdr);
|
||||||
|
|
||||||
secbool stay_in_bootloader = boot_sequence(manufacturing_mode);
|
secbool stay_in_bootloader = boot_sequence(manufacturing_mode);
|
||||||
|
|
||||||
@ -426,10 +459,8 @@ int bootloader_main(void) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
const image_header *hdr = NULL;
|
const image_header *hdr = NULL;
|
||||||
vendor_header vhdr;
|
|
||||||
|
|
||||||
// detect whether the device contains a valid firmware
|
// detect whether the device contains a valid firmware
|
||||||
volatile secbool vhdr_present = secfalse;
|
|
||||||
volatile secbool vhdr_keys_ok = secfalse;
|
volatile secbool vhdr_keys_ok = secfalse;
|
||||||
volatile secbool vhdr_lock_ok = secfalse;
|
volatile secbool vhdr_lock_ok = secfalse;
|
||||||
volatile secbool img_hdr_ok = secfalse;
|
volatile secbool img_hdr_ok = secfalse;
|
||||||
@ -440,8 +471,7 @@ int bootloader_main(void) {
|
|||||||
volatile secbool firmware_present = secfalse;
|
volatile secbool firmware_present = secfalse;
|
||||||
volatile secbool firmware_present_backup = secfalse;
|
volatile secbool firmware_present_backup = secfalse;
|
||||||
volatile secbool auto_upgrade = secfalse;
|
volatile secbool auto_upgrade = secfalse;
|
||||||
|
volatile secbool secmon_valid = secfalse;
|
||||||
vhdr_present = read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr);
|
|
||||||
|
|
||||||
if (sectrue == vhdr_present) {
|
if (sectrue == vhdr_present) {
|
||||||
vhdr_keys_ok = check_vendor_header_keys(&vhdr);
|
vhdr_keys_ok = check_vendor_header_keys(&vhdr);
|
||||||
@ -476,7 +506,46 @@ int bootloader_main(void) {
|
|||||||
header_present = version_ok;
|
header_present = version_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_SECMON_VERIFICATION
|
||||||
|
size_t secmon_start = (size_t)IMAGE_CODE_ALIGN(FIRMWARE_START + vhdr.hdrlen +
|
||||||
|
IMAGE_HEADER_SIZE);
|
||||||
|
|
||||||
|
const secmon_header_t *secmon_hdr =
|
||||||
|
read_secmon_header((const uint8_t *)secmon_start, FIRMWARE_MAXSIZE);
|
||||||
|
|
||||||
|
volatile secbool secmon_header_present = secfalse;
|
||||||
|
volatile secbool secmon_model_valid = secfalse;
|
||||||
|
volatile secbool secmon_header_sig_valid = secfalse;
|
||||||
|
volatile secbool secmon_contents_valid = secfalse;
|
||||||
|
|
||||||
if (sectrue == header_present) {
|
if (sectrue == header_present) {
|
||||||
|
secmon_header_present =
|
||||||
|
secbool_and(header_present, (secmon_hdr != NULL) * sectrue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectrue == secmon_header_present) {
|
||||||
|
secmon_model_valid =
|
||||||
|
secbool_and(secmon_header_present, check_secmon_model(secmon_hdr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectrue == secmon_model_valid) {
|
||||||
|
secmon_header_sig_valid =
|
||||||
|
secbool_and(secmon_model_valid, check_secmon_header_sig(secmon_hdr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectrue == secmon_header_sig_valid) {
|
||||||
|
secmon_contents_valid = secbool_and(
|
||||||
|
secmon_header_sig_valid,
|
||||||
|
check_secmon_contents(secmon_hdr, secmon_start - FIRMWARE_START,
|
||||||
|
&FIRMWARE_AREA));
|
||||||
|
secmon_valid = secmon_contents_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
secmon_valid = header_present;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (sectrue == secmon_valid) {
|
||||||
ensure_firmware_min_version(hdr->monotonic);
|
ensure_firmware_min_version(hdr->monotonic);
|
||||||
firmware_present = check_image_contents(
|
firmware_present = check_image_contents(
|
||||||
hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, &FIRMWARE_AREA);
|
hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, &FIRMWARE_AREA);
|
||||||
|
@ -62,6 +62,11 @@ typedef enum {
|
|||||||
UPLOAD_ERR_NOT_FULLTRUST_IMAGE = -13,
|
UPLOAD_ERR_NOT_FULLTRUST_IMAGE = -13,
|
||||||
UPLOAD_ERR_INVALID_CHUNK_PADDING = -14,
|
UPLOAD_ERR_INVALID_CHUNK_PADDING = -14,
|
||||||
UPLOAD_ERR_COMMUNICATION = -17,
|
UPLOAD_ERR_COMMUNICATION = -17,
|
||||||
|
UPLOAD_ERR_INVALID_SECMON_HEADER = -18,
|
||||||
|
UPLOAD_ERR_INVALID_SECMON_HEADER_SIG = -19,
|
||||||
|
UPLOAD_ERR_INVALID_SECMON_MODEL = -20,
|
||||||
|
UPLOAD_ERR_INVALID_SECMON_HASH = -21,
|
||||||
|
UPLOAD_ERR_SECMON_TOO_BIG = -22,
|
||||||
} upload_status_t;
|
} upload_status_t;
|
||||||
|
|
||||||
#define FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT 2
|
#define FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT 2
|
||||||
@ -80,6 +85,19 @@ typedef struct {
|
|||||||
size_t headers_offset; // offset of headers in the first block
|
size_t headers_offset; // offset of headers in the first block
|
||||||
size_t read_offset; // offset of the next read data in the chunk buffer
|
size_t read_offset; // offset of the next read data in the chunk buffer
|
||||||
uint32_t chunk_size; // size of already received chunk data
|
uint32_t chunk_size; // size of already received chunk data
|
||||||
|
#ifdef USE_SECMON_VERIFICATION
|
||||||
|
size_t secmon_code_offset; // offset of the secmon code in the first block
|
||||||
|
size_t secmon_code_size; // size of the secmon code
|
||||||
|
size_t secmon_code_processed; // size of the processed secmon code
|
||||||
|
uint8_t expected_secmon_hash[IMAGE_HASH_DIGEST_LENGTH]; // expected hash of
|
||||||
|
// the secmon code
|
||||||
|
// todo should be IMAGE_HASH_CTX, but due to limitations of the hash_processor
|
||||||
|
// driver implementation, we can't run two hash calculations in parallel so
|
||||||
|
// temporarily we use SW hashing for secmon code during update.
|
||||||
|
// As secmon is only used on U5 MCUs where also SHA256is used for image
|
||||||
|
// hashes, this works, but should be fixed by improving the hash_processor
|
||||||
|
SHA256_CTX secmon_hash_ctx;
|
||||||
|
#endif
|
||||||
} firmware_update_ctx_t;
|
} firmware_update_ctx_t;
|
||||||
|
|
||||||
static int version_compare(uint32_t vera, uint32_t verb) {
|
static int version_compare(uint32_t vera, uint32_t verb) {
|
||||||
@ -233,6 +251,43 @@ static upload_status_t process_msg_FirmwareUpload(protob_io_t *iface,
|
|||||||
return UPLOAD_ERR_INVALID_IMAGE_HEADER_VERSION;
|
return UPLOAD_ERR_INVALID_IMAGE_HEADER_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_SECMON_VERIFICATION
|
||||||
|
size_t secmon_start_offset =
|
||||||
|
(size_t)IMAGE_CODE_ALIGN(vhdr.hdrlen + IMAGE_HEADER_SIZE);
|
||||||
|
size_t secmon_start = (size_t)chunk_buffer + secmon_start_offset;
|
||||||
|
const secmon_header_t *secmon_hdr =
|
||||||
|
read_secmon_header((const uint8_t *)secmon_start, FIRMWARE_MAXSIZE);
|
||||||
|
|
||||||
|
if (secmon_hdr != NULL) {
|
||||||
|
ctx->secmon_code_offset =
|
||||||
|
IMAGE_CODE_ALIGN(vhdr.hdrlen + IMAGE_HEADER_SIZE) +
|
||||||
|
SECMON_HEADER_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secmon_hdr != (const secmon_header_t *)secmon_start) {
|
||||||
|
send_msg_failure(iface, FailureType_Failure_ProcessError,
|
||||||
|
"Invalid secmon header");
|
||||||
|
return UPLOAD_ERR_INVALID_SECMON_HEADER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectrue != check_secmon_model(secmon_hdr)) {
|
||||||
|
send_msg_failure(iface, FailureType_Failure_ProcessError,
|
||||||
|
"Wrong secmon model");
|
||||||
|
return UPLOAD_ERR_INVALID_SECMON_MODEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectrue != check_secmon_header_sig(secmon_hdr)) {
|
||||||
|
send_msg_failure(iface, FailureType_Failure_ProcessError,
|
||||||
|
"Invalid secmon signature");
|
||||||
|
return UPLOAD_ERR_INVALID_SECMON_HEADER_SIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->secmon_code_size = secmon_hdr->codelen;
|
||||||
|
|
||||||
|
memcpy(ctx->expected_secmon_hash, secmon_hdr->hash,
|
||||||
|
IMAGE_HASH_DIGEST_LENGTH);
|
||||||
|
#endif
|
||||||
|
|
||||||
memcpy(&hdr, received_hdr, sizeof(hdr));
|
memcpy(&hdr, received_hdr, sizeof(hdr));
|
||||||
|
|
||||||
vendor_header current_vhdr;
|
vendor_header current_vhdr;
|
||||||
@ -313,6 +368,16 @@ static upload_status_t process_msg_FirmwareUpload(protob_io_t *iface,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SECMON_VERIFICATION
|
||||||
|
if (ctx->secmon_code_size >
|
||||||
|
((hdr.codelen + IMAGE_HEADER_SIZE + vhdr.hdrlen) -
|
||||||
|
ctx->secmon_code_offset)) {
|
||||||
|
send_msg_failure(iface, FailureType_Failure_ProcessError,
|
||||||
|
"Secmon code too big");
|
||||||
|
return UPLOAD_ERR_SECMON_TOO_BIG;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
confirm_result_t response = CANCEL;
|
confirm_result_t response = CANCEL;
|
||||||
if (((vhdr.vtrust & VTRUST_NO_WARNING) == VTRUST_NO_WARNING) &&
|
if (((vhdr.vtrust & VTRUST_NO_WARNING) == VTRUST_NO_WARNING) &&
|
||||||
(sectrue == is_new || sectrue == is_ilu)) {
|
(sectrue == is_new || sectrue == is_ilu)) {
|
||||||
@ -408,6 +473,46 @@ static upload_status_t process_msg_FirmwareUpload(protob_io_t *iface,
|
|||||||
return UPLOAD_ERR_INVALID_CHUNK_HASH;
|
return UPLOAD_ERR_INVALID_CHUNK_HASH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_SECMON_VERIFICATION
|
||||||
|
// validate secmon code hash
|
||||||
|
if (ctx->secmon_code_size > 0) {
|
||||||
|
if (ctx->secmon_code_processed == 0) {
|
||||||
|
// todo SW SHA256, see comment in firmware_update_ctx_t
|
||||||
|
sha256_Init(&ctx->secmon_hash_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t secmon_code_remaining =
|
||||||
|
ctx->secmon_code_size - ctx->secmon_code_processed;
|
||||||
|
|
||||||
|
size_t secmon_code_to_process = IMAGE_CHUNK_SIZE - ctx->secmon_code_offset;
|
||||||
|
|
||||||
|
secmon_code_to_process = MIN(secmon_code_to_process, secmon_code_remaining);
|
||||||
|
|
||||||
|
sha256_Update(&ctx->secmon_hash_ctx,
|
||||||
|
(uint8_t *)chunk_buffer + ctx->secmon_code_offset,
|
||||||
|
secmon_code_to_process);
|
||||||
|
|
||||||
|
ctx->secmon_code_processed += secmon_code_to_process;
|
||||||
|
ctx->secmon_code_offset = 0;
|
||||||
|
|
||||||
|
if (ctx->secmon_code_processed >= ctx->secmon_code_size) {
|
||||||
|
// secmon code is fully processed
|
||||||
|
uint8_t secmon_hash[IMAGE_HASH_DIGEST_LENGTH];
|
||||||
|
sha256_Final(&ctx->secmon_hash_ctx, secmon_hash);
|
||||||
|
|
||||||
|
if (memcmp(secmon_hash, ctx->expected_secmon_hash,
|
||||||
|
IMAGE_HASH_DIGEST_LENGTH) != 0) {
|
||||||
|
send_msg_failure(iface, FailureType_Failure_ProcessError,
|
||||||
|
"Invalid secmon hash");
|
||||||
|
return UPLOAD_ERR_INVALID_SECMON_HASH;
|
||||||
|
}
|
||||||
|
ctx->secmon_code_size = 0; // reset secmon code size to prevent
|
||||||
|
// reprocessing in the next chunk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// buffer with the received data
|
// buffer with the received data
|
||||||
const uint32_t *src = (const uint32_t *)chunk_buffer;
|
const uint32_t *src = (const uint32_t *)chunk_buffer;
|
||||||
// number of received bytes
|
// number of received bytes
|
||||||
|
@ -54,6 +54,19 @@ static const uint8_t * const BOOTLOADER_KEYS[] = {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const uint8_t SECMON_KEY_M = 2;
|
||||||
|
const uint8_t SECMON_KEY_N = 3;
|
||||||
|
static const uint8_t * const SECMON_KEYS[] = {
|
||||||
|
#if !PRODUCTION
|
||||||
|
/*** DEVEL/QA KEYS ***/
|
||||||
|
(const uint8_t *)"\xdb\x99\x5f\xe2\x51\x69\xd1\x41\xca\xb9\xbb\xba\x92\xba\xa0\x1f\x9f\x2e\x1e\xce\x7d\xf4\xcb\x2a\xc0\x51\x90\xf3\x7f\xcc\x1f\x9d",
|
||||||
|
(const uint8_t *)"\x21\x52\xf8\xd1\x9b\x79\x1d\x24\x45\x32\x42\xe1\x5f\x2e\xab\x6c\xb7\xcf\xfa\x7b\x6a\x5e\xd3\x00\x97\x96\x0e\x06\x98\x81\xdb\x12",
|
||||||
|
(const uint8_t *)"\x22\xfc\x29\x77\x92\xf0\xb6\xff\xc0\xbf\xcf\xdb\x7e\xdb\x0c\x0a\xa1\x4e\x02\x5a\x36\x5e\xc0\xe3\x42\xe8\x6e\x38\x29\xcb\x74\xb6",
|
||||||
|
#else
|
||||||
|
MODEL_SCMON_KEYS
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
static secbool compute_pubkey(uint8_t sig_m, uint8_t sig_n,
|
static secbool compute_pubkey(uint8_t sig_m, uint8_t sig_n,
|
||||||
const uint8_t *const *pub, uint8_t sigmask,
|
const uint8_t *const *pub, uint8_t sigmask,
|
||||||
ed25519_public_key res) {
|
ed25519_public_key res) {
|
||||||
@ -159,6 +172,84 @@ secbool check_image_header_sig(const image_header *const hdr, uint8_t key_m,
|
|||||||
*(const ed25519_signature *)hdr->sig));
|
*(const ed25519_signature *)hdr->sig));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const secmon_header_t *read_secmon_header(const uint8_t *const data,
|
||||||
|
const uint32_t maxsize) {
|
||||||
|
const secmon_header_t *hdr = (const secmon_header_t *)data;
|
||||||
|
|
||||||
|
if (hdr->magic != SECMON_IMAGE_MAGIC) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (hdr->hdrlen != SECMON_HEADER_SIZE) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
return hdr;
|
||||||
|
}
|
||||||
|
|
||||||
|
secbool check_secmon_model(const secmon_header_t *const hdr) {
|
||||||
|
#ifndef TREZOR_EMULATOR
|
||||||
|
if (hdr->hw_model != HW_MODEL) {
|
||||||
|
return secfalse;
|
||||||
|
}
|
||||||
|
if (hdr->hw_revision != HW_REVISION) {
|
||||||
|
return secfalse;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return sectrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_secmon_fingerprint(const secmon_header_t *const hdr,
|
||||||
|
uint8_t *const out) {
|
||||||
|
IMAGE_HASH_CTX ctx;
|
||||||
|
IMAGE_HASH_INIT(&ctx);
|
||||||
|
IMAGE_HASH_UPDATE(&ctx, (uint8_t *)hdr, SECMON_HEADER_SIZE - IMAGE_SIG_SIZE);
|
||||||
|
for (int i = 0; i < IMAGE_SIG_SIZE; i++) {
|
||||||
|
IMAGE_HASH_UPDATE(&ctx, (const uint8_t *)"\x00", 1);
|
||||||
|
}
|
||||||
|
IMAGE_HASH_FINAL(&ctx, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
secbool check_secmon_header_sig(const secmon_header_t *const hdr) {
|
||||||
|
// check header signature
|
||||||
|
|
||||||
|
uint8_t fingerprint[32];
|
||||||
|
get_secmon_fingerprint(hdr, fingerprint);
|
||||||
|
|
||||||
|
ed25519_public_key pub;
|
||||||
|
if (sectrue != compute_pubkey(SECMON_KEY_M, SECMON_KEY_N, SECMON_KEYS,
|
||||||
|
hdr->sigmask, pub))
|
||||||
|
return secfalse;
|
||||||
|
|
||||||
|
return sectrue *
|
||||||
|
(0 == ed25519_sign_open(fingerprint, IMAGE_HASH_DIGEST_LENGTH, pub,
|
||||||
|
*(const ed25519_signature *)hdr->sig));
|
||||||
|
}
|
||||||
|
|
||||||
|
secbool check_secmon_contents(const secmon_header_t *const hdr,
|
||||||
|
size_t code_offset, const flash_area_t *area) {
|
||||||
|
if (0 == area) {
|
||||||
|
return secfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the secmon integrity, calculate and compare hash
|
||||||
|
const void *data = flash_area_get_address(
|
||||||
|
area, code_offset + SECMON_HEADER_SIZE, hdr->codelen);
|
||||||
|
if (!data) {
|
||||||
|
return secfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectrue != check_single_hash(hdr->hash, data, hdr->codelen)) {
|
||||||
|
return secfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sectrue;
|
||||||
|
}
|
||||||
|
|
||||||
secbool __wur read_vendor_header(const uint8_t *const data,
|
secbool __wur read_vendor_header(const uint8_t *const data,
|
||||||
vendor_header *const vhdr) {
|
vendor_header *const vhdr) {
|
||||||
memcpy(&vhdr->magic, data, 4);
|
memcpy(&vhdr->magic, data, 4);
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#define VENDOR_HEADER_MAX_SIZE (64 * 1024)
|
#define VENDOR_HEADER_MAX_SIZE (64 * 1024)
|
||||||
#define IMAGE_HEADER_SIZE 0x400 // size of the bootloader or firmware header
|
#define IMAGE_HEADER_SIZE 0x400 // size of the bootloader or firmware header
|
||||||
|
#define SECMON_HEADER_SIZE 0x200
|
||||||
#define IMAGE_SIG_SIZE 65
|
#define IMAGE_SIG_SIZE 65
|
||||||
#define IMAGE_INIT_CHUNK_SIZE (16 * 1024)
|
#define IMAGE_INIT_CHUNK_SIZE (16 * 1024)
|
||||||
|
|
||||||
@ -36,6 +37,8 @@
|
|||||||
|
|
||||||
#define FIRMWARE_IMAGE_MAGIC 0x465A5254 // TRZF
|
#define FIRMWARE_IMAGE_MAGIC 0x465A5254 // TRZF
|
||||||
|
|
||||||
|
#define SECMON_IMAGE_MAGIC 0x43455354 // TSEC
|
||||||
|
|
||||||
#define IMAGE_CODE_ALIGN(addr) \
|
#define IMAGE_CODE_ALIGN(addr) \
|
||||||
((((uint32_t)(uintptr_t)addr) + (CODE_ALIGNMENT - 1)) & ~(CODE_ALIGNMENT - 1))
|
((((uint32_t)(uintptr_t)addr) + (CODE_ALIGNMENT - 1)) & ~(CODE_ALIGNMENT - 1))
|
||||||
|
|
||||||
@ -120,6 +123,20 @@ typedef struct {
|
|||||||
uint8_t hash[IMAGE_HASH_DIGEST_LENGTH];
|
uint8_t hash[IMAGE_HASH_DIGEST_LENGTH];
|
||||||
} firmware_header_info_t;
|
} firmware_header_info_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t magic;
|
||||||
|
uint32_t hdrlen;
|
||||||
|
uint32_t codelen;
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t hw_model;
|
||||||
|
uint8_t hw_revision;
|
||||||
|
uint8_t reserved_0[3];
|
||||||
|
uint8_t hash[32];
|
||||||
|
uint8_t reserved_1[391];
|
||||||
|
uint8_t sigmask;
|
||||||
|
uint8_t sig[64];
|
||||||
|
} secmon_header_t;
|
||||||
|
|
||||||
const image_header *read_image_header(const uint8_t *const data,
|
const image_header *read_image_header(const uint8_t *const data,
|
||||||
const uint32_t magic,
|
const uint32_t magic,
|
||||||
const uint32_t maxsize);
|
const uint32_t maxsize);
|
||||||
@ -156,3 +173,14 @@ secbool check_firmware_header(const uint8_t *header, size_t header_size,
|
|||||||
firmware_header_info_t *info);
|
firmware_header_info_t *info);
|
||||||
|
|
||||||
secbool __wur check_bootloader_header_sig(const image_header *const hdr);
|
secbool __wur check_bootloader_header_sig(const image_header *const hdr);
|
||||||
|
|
||||||
|
const secmon_header_t *read_secmon_header(const uint8_t *const data,
|
||||||
|
const uint32_t maxsize);
|
||||||
|
|
||||||
|
secbool __wur check_secmon_model(const secmon_header_t *const hdr);
|
||||||
|
|
||||||
|
secbool __wur check_secmon_header_sig(const secmon_header_t *const hdr);
|
||||||
|
|
||||||
|
secbool __wur check_secmon_contents(const secmon_header_t *const hdr,
|
||||||
|
size_t code_offset,
|
||||||
|
const flash_area_t *area);
|
||||||
|
@ -47,6 +47,7 @@ def configure(
|
|||||||
("USE_HSE", "1"),
|
("USE_HSE", "1"),
|
||||||
("USE_BOOTARGS_RSOD", "1"),
|
("USE_BOOTARGS_RSOD", "1"),
|
||||||
("LOCKABLE_BOOTLOADER", "1"),
|
("LOCKABLE_BOOTLOADER", "1"),
|
||||||
|
("USE_SECMON_VERIFICATION", "1"),
|
||||||
]
|
]
|
||||||
|
|
||||||
if "display" in features_wanted:
|
if "display" in features_wanted:
|
||||||
|
@ -49,6 +49,7 @@ def configure(
|
|||||||
("FIXED_HW_DEINIT", "1"),
|
("FIXED_HW_DEINIT", "1"),
|
||||||
("LOCKABLE_BOOTLOADER", "1"),
|
("LOCKABLE_BOOTLOADER", "1"),
|
||||||
("LAZY_DISPLAY_INIT", "1"),
|
("LAZY_DISPLAY_INIT", "1"),
|
||||||
|
("USE_SECMON_VERIFICATION", "1"),
|
||||||
("USE_BOOTARGS_RSOD", "1"),
|
("USE_BOOTARGS_RSOD", "1"),
|
||||||
("TERMINAL_FONT_SCALE", "2"),
|
("TERMINAL_FONT_SCALE", "2"),
|
||||||
("TERMINAL_X_PADDING", "4"),
|
("TERMINAL_X_PADDING", "4"),
|
||||||
|
@ -49,6 +49,7 @@ def configure(
|
|||||||
("FIXED_HW_DEINIT", "1"),
|
("FIXED_HW_DEINIT", "1"),
|
||||||
("LOCKABLE_BOOTLOADER", "1"),
|
("LOCKABLE_BOOTLOADER", "1"),
|
||||||
("LAZY_DISPLAY_INIT", "1"),
|
("LAZY_DISPLAY_INIT", "1"),
|
||||||
|
("USE_SECMON_VERIFICATION", "1"),
|
||||||
("USE_BOOTARGS_RSOD", "1"),
|
("USE_BOOTARGS_RSOD", "1"),
|
||||||
("TERMINAL_FONT_SCALE", "2"),
|
("TERMINAL_FONT_SCALE", "2"),
|
||||||
("TERMINAL_X_PADDING", "4"),
|
("TERMINAL_X_PADDING", "4"),
|
||||||
|
@ -49,6 +49,7 @@ def configure(
|
|||||||
("FIXED_HW_DEINIT", "1"),
|
("FIXED_HW_DEINIT", "1"),
|
||||||
("LOCKABLE_BOOTLOADER", "1"),
|
("LOCKABLE_BOOTLOADER", "1"),
|
||||||
("LAZY_DISPLAY_INIT", "1"),
|
("LAZY_DISPLAY_INIT", "1"),
|
||||||
|
("USE_SECMON_VERIFICATION", "1"),
|
||||||
("USE_BOOTARGS_RSOD", "1"),
|
("USE_BOOTARGS_RSOD", "1"),
|
||||||
("TERMINAL_FONT_SCALE", "2"),
|
("TERMINAL_FONT_SCALE", "2"),
|
||||||
("TERMINAL_X_PADDING", "4"),
|
("TERMINAL_X_PADDING", "4"),
|
||||||
|
@ -183,6 +183,42 @@ def format_header(
|
|||||||
return "\n".join(output)
|
return "\n".join(output)
|
||||||
|
|
||||||
|
|
||||||
|
def format_secmon_header(
|
||||||
|
header: firmware.secmon.SecmonHeader,
|
||||||
|
code_hash: bytes,
|
||||||
|
digest: bytes,
|
||||||
|
sig_status: Status,
|
||||||
|
) -> str:
|
||||||
|
header_dict = asdict(header)
|
||||||
|
header_out = header_dict.copy()
|
||||||
|
|
||||||
|
for key, val in header_out.items():
|
||||||
|
if "version" in key:
|
||||||
|
header_out[key] = LiteralStr(_format_version(val))
|
||||||
|
|
||||||
|
# status = SYM_OK if header.hash == code_hash else SYM_FAIL
|
||||||
|
|
||||||
|
if all_zero(header.hash):
|
||||||
|
hash_status = Status.MISSING
|
||||||
|
elif header.hash != code_hash:
|
||||||
|
hash_status = Status.INVALID
|
||||||
|
else:
|
||||||
|
hash_status = Status.VALID
|
||||||
|
#
|
||||||
|
# header_out["hashes"] = hashes_out
|
||||||
|
|
||||||
|
all_ok = SYM_OK if hash_status.is_ok() and sig_status.is_ok() else SYM_FAIL
|
||||||
|
|
||||||
|
output = [
|
||||||
|
"SECMON Header " + _format_container(header_out),
|
||||||
|
"Code hash: " + click.style(code_hash.hex(), bold=True),
|
||||||
|
f"Fingerprint: {click.style(digest.hex(), bold=True)}",
|
||||||
|
f"{all_ok} Signature is {sig_status.value}, hash is {hash_status.value}",
|
||||||
|
]
|
||||||
|
|
||||||
|
return "\n".join(output)
|
||||||
|
|
||||||
|
|
||||||
# =========================== functionality implementations ===============
|
# =========================== functionality implementations ===============
|
||||||
|
|
||||||
|
|
||||||
@ -391,6 +427,39 @@ class BootloaderImage(firmware.FirmwareImage, CosiSignedMixin):
|
|||||||
return self.get_model_keys(dev_keys).boardloader_keys
|
return self.get_model_keys(dev_keys).boardloader_keys
|
||||||
|
|
||||||
|
|
||||||
|
class SecmonImage(firmware.SecmonImage, CosiSignedMixin):
|
||||||
|
NAME: t.ClassVar[str] = "secmon"
|
||||||
|
DEV_KEYS = _make_dev_keys(b"\x41", b"\x42")
|
||||||
|
|
||||||
|
def get_header(self) -> CosiSignatureHeaderProto:
|
||||||
|
return self.header
|
||||||
|
|
||||||
|
def format(self, verbose: bool = False) -> str:
|
||||||
|
return format_secmon_header(
|
||||||
|
self.header,
|
||||||
|
self.code_hash(),
|
||||||
|
self.digest(),
|
||||||
|
_check_signature_any(self),
|
||||||
|
)
|
||||||
|
|
||||||
|
def verify(self, dev_keys: bool = False) -> None:
|
||||||
|
self.validate_code_hash()
|
||||||
|
public_keys = self.public_keys(dev_keys)
|
||||||
|
try:
|
||||||
|
cosi.verify(
|
||||||
|
self.header.signature,
|
||||||
|
self.digest(),
|
||||||
|
self.get_model_keys(dev_keys).boardloader_sigs_needed,
|
||||||
|
public_keys,
|
||||||
|
self.header.sigmask,
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
raise firmware.InvalidSignatureError("Invalid bootloader signature")
|
||||||
|
|
||||||
|
def public_keys(self, dev_keys: bool = False) -> t.Sequence[bytes]:
|
||||||
|
return self.get_model_keys(dev_keys).boardloader_keys
|
||||||
|
|
||||||
|
|
||||||
class LegacyFirmware(firmware.LegacyFirmware):
|
class LegacyFirmware(firmware.LegacyFirmware):
|
||||||
NAME: t.ClassVar[str] = "legacy_firmware_v1"
|
NAME: t.ClassVar[str] = "legacy_firmware_v1"
|
||||||
|
|
||||||
@ -488,6 +557,11 @@ def parse_image(image: bytes) -> SignableImageProto:
|
|||||||
except c.ConstructError:
|
except c.ConstructError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
return SecmonImage.parse(image)
|
||||||
|
except c.ConstructError:
|
||||||
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
firmware_img = firmware.core.FirmwareImage.parse(image)
|
firmware_img = firmware.core.FirmwareImage.parse(image)
|
||||||
if firmware_img.header.magic == firmware.core.HeaderType.BOOTLOADER:
|
if firmware_img.header.magic == firmware.core.HeaderType.BOOTLOADER:
|
||||||
|
Loading…
Reference in New Issue
Block a user