fw: Add checksum to partition table

Refuse to boot if blake2s digest over the partition table does not match
digest stored on flash
This commit is contained in:
Mikael Ågren 2025-04-03 15:48:51 +02:00
parent 7d9aa7c647
commit 469546ff33
No known key found for this signature in database
GPG Key ID: E02DA3D397792C46
11 changed files with 207 additions and 73 deletions

View File

@ -41,7 +41,7 @@ static volatile uint32_t *ram_data_rand = (volatile uint32_t *)TK1_MMIO_TK1_R
static volatile struct reset *resetinfo = (volatile struct reset *)TK1_MMIO_RESETINFO_BASE;
// clang-format on
struct partition_table part_table;
struct partition_table_storage part_table_storage;
#define APP_SIZE_SLOT0 21684
// Context for the loading of a TKey program
@ -531,7 +531,7 @@ int main(void)
// TODO end of remove block
if (part_table_read(&part_table) != 0) {
if (part_table_read(&part_table_storage) != 0) {
// Couldn't read or create partition table
assert(1 == 2);
}
@ -541,7 +541,7 @@ int main(void)
#endif
// Hardocde size of slot 0
part_table.pre_app_data[0].size = APP_SIZE_SLOT0;
part_table_storage.table.pre_app_data[0].size = APP_SIZE_SLOT0;
// part_table.pre_app_data[1].size = 0x20000;
// TODO Just start something from flash without looking in
@ -583,7 +583,7 @@ int main(void)
break;
case FW_STATE_LOAD_FLASH:
if (load_flash_app(&part_table, ctx.digest, ctx.flash_slot) < 0) {
if (load_flash_app(&part_table_storage.table, ctx.digest, ctx.flash_slot) < 0) {
debug_puts("Couldn't load app from flash\n");
state = FW_STATE_FAIL;
break;

View File

@ -2,43 +2,67 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <stdint.h>
#include <tkey/assert.h>
#include <tkey/lib.h>
#include "blake2s/blake2s.h"
#include "flash.h"
#include "partition_table.h"
#include "proto.h"
int part_table_read(struct partition_table *part_table)
void part_digest(struct partition_table *part_table, uint8_t *out_digest, size_t out_len) {
blake2s_ctx b2s_ctx = {0};
int blake2err = 0;
uint8_t key[16] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
blake2err = blake2s(out_digest, out_len,
key, sizeof(key), part_table, sizeof(struct partition_table), &b2s_ctx);
assert(blake2err == 0);
}
int part_table_read(struct partition_table_storage *storage)
{
// Read from flash, if it exists, otherwise create a new one.
flash_release_powerdown();
memset(part_table, 0x00, sizeof(*part_table));
memset(storage, 0x00, sizeof(*storage));
flash_read_data(ADDR_PARTITION_TABLE, (uint8_t *)part_table,
sizeof(*part_table));
flash_read_data(ADDR_PARTITION_TABLE, (uint8_t *)storage,
sizeof(*storage));
// TODO: Implement redundancy and consistency check
// TODO: Implement redundancy
if (part_table->header.version != PART_TABLE_VERSION) {
// Partition table is not ours. Make a new one, and store it.
memset(part_table, 0x00, sizeof(*part_table));
uint8_t check_digest[PART_DIGEST_SIZE];
part_digest(&storage->table, check_digest, sizeof(check_digest));
part_table->header.version = PART_TABLE_VERSION;
part_table_write(part_table);
if (!memeq(check_digest, storage->check_digest, sizeof(check_digest))) {
return -1;
}
// if (part_table->header.version != PART_TABLE_VERSION) {
// // Partition table is not ours. Make a new one, and store it.
// memset(part_table, 0x00, sizeof(*part_table));
// part_table->header.version = PART_TABLE_VERSION;
// part_table_write(part_table);
// }
// Now the partition table is synced between flash and RAM.
return 0;
}
int part_table_write(struct partition_table *part_table)
int part_table_write(struct partition_table_storage *storage)
{
part_digest(&storage->table, storage->check_digest, sizeof(storage->check_digest));
flash_sector_erase(ADDR_PARTITION_TABLE);
flash_write_data(ADDR_PARTITION_TABLE, (uint8_t *)part_table,
sizeof(*part_table));
flash_write_data(ADDR_PARTITION_TABLE, (uint8_t *)storage,
sizeof(*storage));
return 0;
}

View File

@ -43,6 +43,8 @@
#define SIZE_STORAGE_AREA 0x20000UL // 128KiB
#define N_STORAGE_AREA 4
#define PART_DIGEST_SIZE 16
/* Partition Table */
/*- Table header */
/* - 1 bytes Version */
@ -88,7 +90,12 @@ struct partition_table {
struct app_storage_area app_storage[N_STORAGE_AREA];
} __attribute__((packed));
int part_table_read(struct partition_table *part_table);
int part_table_write(struct partition_table *part_table);
struct partition_table_storage {
struct partition_table table;
uint8_t check_digest[PART_DIGEST_SIZE];
} __attribute__((packed));
int part_table_read(struct partition_table_storage *storage);
int part_table_write(struct partition_table_storage *storage);
#endif

View File

@ -79,10 +79,12 @@ int preload_store(struct partition_table *part_table, uint32_t offset,
return flash_write_data(address, data, size);
}
int preload_store_finalize(struct partition_table *part_table, size_t app_size,
int preload_store_finalize(struct partition_table_storage *part_table_storage, size_t app_size,
uint8_t app_digest[32], uint8_t app_signature[64],
uint8_t to_slot)
{
struct partition_table *part_table = &part_table_storage->table;
if (to_slot >= N_PRELOADED_APP) {
return -4;
}
@ -112,13 +114,15 @@ int preload_store_finalize(struct partition_table *part_table, size_t app_size,
debug_putinthex(app_size);
debug_lf();
part_table_write(part_table);
part_table_write(part_table_storage);
return 0;
}
int preload_delete(struct partition_table *part_table, uint8_t slot)
int preload_delete(struct partition_table_storage *part_table_storage, uint8_t slot)
{
struct partition_table *part_table = &part_table_storage->table;
if (slot >= N_PRELOADED_APP) {
return -4;
}
@ -141,7 +145,7 @@ int preload_delete(struct partition_table *part_table, uint8_t slot)
memset(part_table->pre_app_data[slot].signature, 0,
sizeof(part_table->pre_app_data[slot].signature));
part_table_write(part_table);
part_table_write(part_table_storage);
/* Assumes the area is 64 KiB block aligned */
flash_block_64_erase(slot_to_start_address(slot)); // Erase first 64 KB block

View File

@ -14,10 +14,11 @@ bool preload_check_valid_app(struct partition_table *part_table,
int preload_load(struct partition_table *part_table, uint8_t from_slot);
int preload_store(struct partition_table *part_table, uint32_t offset,
uint8_t *data, size_t size, uint8_t to_slot);
int preload_store_finalize(struct partition_table *part_table, size_t app_size,
uint8_t app_digest[32], uint8_t app_signature[64],
uint8_t to_slot);
int preload_delete(struct partition_table *part_table, uint8_t slot);
int preload_store_finalize(struct partition_table_storage *part_table_storage,
size_t app_size, uint8_t app_digest[32],
uint8_t app_signature[64], uint8_t to_slot);
int preload_delete(struct partition_table_storage *part_table_storage,
uint8_t slot);
int preload_get_digsig(struct partition_table *part_table,
uint8_t app_digest[32], uint8_t app_signature[64],
uint8_t slot);

View File

@ -52,8 +52,10 @@ static int storage_get_area(struct partition_table *part_table)
/* Allocate a new area for an app. Returns zero if a new area is allocated, one
* if an area already was allocated, and negative values for errors. */
int storage_allocate_area(struct partition_table *part_table)
int storage_allocate_area(struct partition_table_storage *part_table_storage)
{
struct partition_table *part_table = &part_table_storage->table;
if (storage_get_area(part_table) != -1) {
/* Already has an area */
return 1;
@ -82,15 +84,17 @@ int storage_allocate_area(struct partition_table *part_table)
part_table->app_storage[index].status = 0x01;
auth_app_create(&part_table->app_storage[index].auth);
part_table_write(part_table);
part_table_write(part_table_storage);
return 0;
}
/* Dealloacate a previously allocated storage area. Returns zero on success, and
* non-zero on errors. */
int storage_deallocate_area(struct partition_table *part_table)
int storage_deallocate_area(struct partition_table_storage *part_table_storage)
{
struct partition_table *part_table = &part_table_storage->table;
int index = storage_get_area(part_table);
if (index == -1) {
/* No area to deallocate */
@ -119,7 +123,7 @@ int storage_deallocate_area(struct partition_table *part_table)
part_table->app_storage[index].auth.authentication_digest, 0x00,
sizeof(part_table->app_storage[index].auth.authentication_digest));
part_table_write(part_table);
part_table_write(part_table_storage);
return 0;
}

View File

@ -10,8 +10,8 @@
#include <stddef.h>
#include <stdint.h>
int storage_allocate_area(struct partition_table *part_table);
int storage_deallocate_area(struct partition_table *part_table);
int storage_deallocate_area(struct partition_table_storage *part_table_storage);
int storage_allocate_area(struct partition_table_storage *part_table_storage);
int storage_erase_sector(struct partition_table *part_table, uint32_t offset,
size_t size);
int storage_write_data(struct partition_table *part_table, uint32_t offset,

View File

@ -23,7 +23,7 @@ static volatile uint32_t *udi = (volatile uint32_t *)TK1_MMIO_TK1_UDI_F
static volatile uint8_t *resetinfo = (volatile uint8_t *) TK1_MMIO_RESETINFO_BASE;
// clang-format on
extern struct partition_table part_table;
extern struct partition_table_storage part_table_storage;
int32_t syscall_handler(uint32_t number, uint32_t arg1, uint32_t arg2,
uint32_t arg3)
@ -36,21 +36,21 @@ int32_t syscall_handler(uint32_t number, uint32_t arg1, uint32_t arg2,
return 0;
case TK1_SYSCALL_ALLOC_AREA:
if (storage_allocate_area(&part_table) < 0) {
if (storage_allocate_area(&part_table_storage) < 0) {
debug_puts("couldn't allocate storage area\n");
return -1;
}
return 0;
case TK1_SYSCALL_DEALLOC_AREA:
if (storage_deallocate_area(&part_table) < 0) {
if (storage_deallocate_area(&part_table_storage) < 0) {
debug_puts("couldn't deallocate storage area\n");
return -1;
}
return 0;
case TK1_SYSCALL_WRITE_DATA:
if (storage_write_data(&part_table, arg1, (uint8_t *)arg2,
if (storage_write_data(&part_table_storage.table, arg1, (uint8_t *)arg2,
arg3) < 0) {
debug_puts("couldn't write storage area\n");
return -1;
@ -58,7 +58,7 @@ int32_t syscall_handler(uint32_t number, uint32_t arg1, uint32_t arg2,
return 0;
case TK1_SYSCALL_READ_DATA:
if (storage_read_data(&part_table, arg1, (uint8_t *)arg2,
if (storage_read_data(&part_table_storage.table, arg1, (uint8_t *)arg2,
arg3) < 0) {
debug_puts("couldn't read storage area\n");
return -1;
@ -75,24 +75,24 @@ int32_t syscall_handler(uint32_t number, uint32_t arg1, uint32_t arg2,
return udi[0];
case TK1_SYSCALL_PRELOAD_DELETE:
return preload_delete(&part_table, 1);
return preload_delete(&part_table_storage, 1);
case TK1_SYSCALL_PRELOAD_STORE:
// arg1 offset
// arg2 data
// arg3 size
// always using slot 1
return preload_store(&part_table, arg1, (uint8_t *)arg2, arg3, 1);
return preload_store(&part_table_storage.table, arg1, (uint8_t *)arg2, arg3, 1);
case TK1_SYSCALL_PRELOAD_STORE_FIN:
// arg1 app_size
// arg2 app_digest
// arg3 app_signature
// always using slot 1
return preload_store_finalize(&part_table, arg1, (uint8_t *)arg2, (uint8_t *)arg3, 1);
return preload_store_finalize(&part_table_storage, arg1, (uint8_t *)arg2, (uint8_t *)arg3, 1);
case TK1_SYSCALL_PRELOAD_GET_DIGSIG:
return preload_get_digsig(&part_table, (uint8_t *)arg1, (uint8_t *)arg2, 1);
return preload_get_digsig(&part_table_storage.table, (uint8_t *)arg1, (uint8_t *)arg2, 1);
default:
assert(1 == 2);

View File

@ -0,0 +1,7 @@
module partition_table
go 1.23.0
require golang.org/x/crypto v0.36.0
require golang.org/x/sys v0.31.0 // indirect

View File

@ -0,0 +1,4 @@
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=

View File

@ -7,29 +7,40 @@ import (
"fmt"
"io"
"os"
"golang.org/x/crypto/blake2s"
)
type PreLoadedAppData struct {
Size uint32
Digest [32]uint8
Signature [64]uint8
}
type Auth struct {
Nonce [16]uint8
AuthDigest [16]uint8
}
type AppStorage struct {
Status uint8
Auth Auth
}
type PartTable struct {
Header struct {
Version uint8
}
PreLoadedAppData [2]struct {
Size uint32
Digest [32]uint8
Signature [64]uint8
}
AppStorage [4]struct {
Status uint8
Auth struct {
Nonce [16]uint8
AuthDigest [16]uint8
}
}
Version uint8
PreLoadedAppData [2]PreLoadedAppData
AppStorage [4]AppStorage
}
type PartTableStorage struct {
PartTable PartTable
Digest [16]uint8
}
type Flash struct {
Bitstream [0x20000]uint8
PartitionTable PartTable
PartitionTableStorage PartTableStorage
PartitionTablePadding [64*1024 - 365]uint8
PreLoadedApp0 [0x20000]uint8
PreLoadedApp1 [0x20000]uint8
@ -37,6 +48,24 @@ type Flash struct {
EndPadding [0x10000]uint8
}
func readPartTableStorage(filename string) PartTableStorage {
var storage PartTableStorage
tblFile, err := os.Open(filename)
if err != nil {
panic(err)
}
if err := binary.Read(tblFile, binary.LittleEndian, &storage); err != nil {
fmt.Println("binary.Read failed:", err)
}
tblStructLen, _ := tblFile.Seek(0, io.SeekCurrent)
fmt.Fprintf(os.Stderr, "INFO: Go partition table struct is %d byte long\n", tblStructLen)
return storage
}
func readPartTable(filename string) PartTable {
var tbl PartTable
@ -73,6 +102,14 @@ func readFlashDump(filename string) Flash {
return flash
}
func printPartTableStorageJson(storage PartTableStorage) {
json, err := json.MarshalIndent(storage, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("%s\n", string(json))
}
func printPartTableJson(tbl PartTable) {
partTableJSON, err := json.MarshalIndent(tbl, "", " ")
if err != nil {
@ -81,11 +118,11 @@ func printPartTableJson(tbl PartTable) {
fmt.Printf("%s\n", string(partTableJSON))
}
func printPartTableCondensed(tbl PartTable) {
func printPartTableCondensed(storage PartTableStorage) {
fmt.Printf("Header\n")
fmt.Printf(" Version : %d\n", tbl.Header.Version)
fmt.Printf(" Version : %d\n", storage.PartTable.Version)
for i, appData := range tbl.PreLoadedAppData {
for i, appData := range storage.PartTable.PreLoadedAppData {
fmt.Printf("Preloaded App %d\n", i)
fmt.Printf(" Size : %d\n", appData.Size)
fmt.Printf(" Digest : %x\n", appData.Digest[:16])
@ -97,6 +134,49 @@ func printPartTableCondensed(tbl PartTable) {
}
}
func printPartTableStorageCondensed(storage PartTableStorage) {
}
func calculateStorageDigest(data []byte) []byte {
key := [16]byte{}
hash, err := blake2s.New128(key[:])
if err != nil {
panic(err)
}
hash.Write(data)
digest := hash.Sum([]byte{})
return digest
}
func generatePartTableStorage(filename string) {
storage := PartTableStorage{
PartTable: PartTable{
Version: 1,
PreLoadedAppData: [2]PreLoadedAppData{},
AppStorage: [4]AppStorage{},
},
}
buf := make([]byte, 4096, 4096)
len, err := binary.Encode(buf, binary.LittleEndian, storage.PartTable)
if err != nil {
panic(err)
}
digest := calculateStorageDigest(buf[:len])
copy(storage.Digest[:], digest)
storageFile, err := os.Create(filename)
if err != nil {
panic(err)
}
if err := binary.Write(storageFile, binary.LittleEndian, storage); err != nil {
fmt.Println("binary.Write failed:", err)
}
}
func main() {
filename := ""
json := false
@ -112,17 +192,20 @@ func main() {
os.Exit(1)
}
var tbl PartTable
generatePartTableStorage(filename)
return
if flash {
tbl = readFlashDump(filename).PartitionTable
} else {
tbl = readPartTable(filename)
}
// var tbl PartTable
if json {
printPartTableJson(tbl)
} else {
printPartTableCondensed(tbl)
}
// if flash {
// tbl = readFlashDump(filename).PartitionTable
// } else {
// tbl = readPartTable(filename)
// }
// if json {
// printPartTableJson(tbl)
// } else {
// printPartTableCondensed(tbl)
// }
}