1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-18 12:28:09 +00:00
trezor-firmware/core/embed/trezorhal/image.c
Pavol Rusnak 35d40cc164 fix(core): change logic of vendor header comparison
Previously we checked whether the current vendor header and
the new vendor header are the same by comparing the embedded keyset.

What originally looked like a good idea is not that good, because
this disallows us from ever changing the vendor header signing keys
without causing erasure of the storage during the version update.

This commit fixes that by changing the logic to comparing just the
vendor string.

Change of function names is purely cosmetic:
* vendor_keys_hash -> vendor_header_hash
* check_vendor_keys_lock -> check_vendor_header_lock
2022-01-03 14:23:58 +01:00

214 lines
6.8 KiB
C

/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "blake2s.h"
#include "ed25519-donna/ed25519.h"
#include "common.h"
#include "flash.h"
#include "image.h"
static secbool compute_pubkey(uint8_t sig_m, uint8_t sig_n,
const uint8_t *const *pub, uint8_t sigmask,
ed25519_public_key res) {
if (0 == sig_m || 0 == sig_n) return secfalse;
if (sig_m > sig_n) return secfalse;
// discard bits higher than sig_n
sigmask &= ((1 << sig_n) - 1);
// remove if number of set bits in sigmask is not equal to sig_m
if (__builtin_popcount(sigmask) != sig_m) return secfalse;
ed25519_public_key keys[sig_m];
int j = 0;
for (int i = 0; i < sig_n; i++) {
if ((1 << i) & sigmask) {
memcpy(keys[j], pub[i], 32);
j++;
}
}
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;
memcpy(&hdr->hdrlen, data + 4, 4);
if (hdr->hdrlen != IMAGE_HEADER_SIZE) return secfalse;
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;
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);
memcpy(hdr->hashes, data + 32, 512);
memcpy(&hdr->sigmask, data + IMAGE_HEADER_SIZE - IMAGE_SIG_SIZE, 1);
memcpy(hdr->sig, data + IMAGE_HEADER_SIZE - IMAGE_SIG_SIZE + 1,
IMAGE_SIG_SIZE - 1);
// check header signature
BLAKE2S_CTX ctx;
blake2s_Init(&ctx, BLAKE2S_DIGEST_LENGTH);
blake2s_Update(&ctx, data, 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);
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,
*(const ed25519_signature *)hdr->sig));
}
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) {
memcpy(&vhdr->magic, data, 4);
if (vhdr->magic != 0x565A5254) return secfalse; // TRZV
memcpy(&vhdr->hdrlen, data + 4, 4);
if (vhdr->hdrlen > 64 * 1024) return secfalse;
memcpy(&vhdr->expiry, data + 8, 4);
if (vhdr->expiry != 0) return secfalse;
memcpy(&vhdr->version, data + 12, 2);
memcpy(&vhdr->vsig_m, data + 14, 1);
memcpy(&vhdr->vsig_n, data + 15, 1);
memcpy(&vhdr->vtrust, data + 16, 2);
if (vhdr->vsig_n > MAX_VENDOR_PUBLIC_KEYS) {
return secfalse;
}
for (int i = 0; i < vhdr->vsig_n; i++) {
vhdr->vpub[i] = data + 32 + i * 32;
}
for (int i = vhdr->vsig_n; i < MAX_VENDOR_PUBLIC_KEYS; i++) {
vhdr->vpub[i] = 0;
}
memcpy(&vhdr->vstr_len, data + 32 + vhdr->vsig_n * 32, 1);
vhdr->vstr = (const char *)(data + 32 + vhdr->vsig_n * 32 + 1);
vhdr->vimg = data + 32 + vhdr->vsig_n * 32 + 1 + vhdr->vstr_len;
// align to 4 bytes
vhdr->vimg += (-(uintptr_t)vhdr->vimg) & 3;
memcpy(&vhdr->sigmask, data + vhdr->hdrlen - IMAGE_SIG_SIZE, 1);
memcpy(vhdr->sig, data + vhdr->hdrlen - IMAGE_SIG_SIZE + 1,
IMAGE_SIG_SIZE - 1);
// check header signature
uint8_t hash[BLAKE2S_DIGEST_LENGTH];
BLAKE2S_CTX ctx;
blake2s_Init(&ctx, BLAKE2S_DIGEST_LENGTH);
blake2s_Update(&ctx, data, vhdr->hdrlen - IMAGE_SIG_SIZE);
for (int i = 0; i < IMAGE_SIG_SIZE; i++) {
blake2s_Update(&ctx, (const uint8_t *)"\x00", 1);
}
blake2s_Final(&ctx, hash, BLAKE2S_DIGEST_LENGTH);
ed25519_public_key pub;
if (sectrue != compute_pubkey(key_m, key_n, keys, vhdr->sigmask, pub))
return secfalse;
return sectrue *
(0 == ed25519_sign_open(hash, BLAKE2S_DIGEST_LENGTH, pub,
*(const ed25519_signature *)vhdr->sig));
}
void vendor_header_hash(const vendor_header *const vhdr, uint8_t *hash) {
BLAKE2S_CTX ctx;
blake2s_Init(&ctx, BLAKE2S_DIGEST_LENGTH);
blake2s_Update(&ctx, vhdr->vstr, vhdr->vstr_len);
blake2s_Update(&ctx, "Trezor Vendor Header", 20);
blake2s_Final(&ctx, hash, BLAKE2S_DIGEST_LENGTH);
}
secbool check_single_hash(const uint8_t *const hash, const uint8_t *const data,
int len) {
uint8_t h[BLAKE2S_DIGEST_LENGTH];
blake2s(data, len, h, BLAKE2S_DIGEST_LENGTH);
return sectrue * (0 == memcmp(h, hash, BLAKE2S_DIGEST_LENGTH));
}
secbool check_image_contents(const image_header *const hdr, uint32_t firstskip,
const uint8_t *sectors, int blocks) {
if (0 == sectors || blocks < 1) {
return secfalse;
}
const void *data =
flash_get_address(sectors[0], firstskip, IMAGE_CHUNK_SIZE - firstskip);
if (!data) {
return secfalse;
}
int remaining = hdr->codelen;
if (sectrue !=
check_single_hash(hdr->hashes, data,
MIN(remaining, IMAGE_CHUNK_SIZE - firstskip))) {
return secfalse;
}
int block = 1;
remaining -= IMAGE_CHUNK_SIZE - firstskip;
while (remaining > 0) {
if (block >= blocks) {
return secfalse;
}
data = flash_get_address(sectors[block], 0, IMAGE_CHUNK_SIZE);
if (!data) {
return secfalse;
}
if (sectrue != check_single_hash(hdr->hashes + block * 32, data,
MIN(remaining, IMAGE_CHUNK_SIZE))) {
return secfalse;
}
block++;
remaining -= IMAGE_CHUNK_SIZE;
}
return sectrue;
}