From 469546ff339582c191f69daea45e15b83be2ff0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Thu, 3 Apr 2025 15:48:51 +0200 Subject: [PATCH] fw: Add checksum to partition table Refuse to boot if blake2s digest over the partition table does not match digest stored on flash --- hw/application_fpga/fw/tk1/main.c | 8 +- hw/application_fpga/fw/tk1/partition_table.c | 52 +++++-- hw/application_fpga/fw/tk1/partition_table.h | 11 +- hw/application_fpga/fw/tk1/preload_app.c | 12 +- hw/application_fpga/fw/tk1/preload_app.h | 9 +- hw/application_fpga/fw/tk1/storage.c | 12 +- hw/application_fpga/fw/tk1/storage.h | 4 +- hw/application_fpga/fw/tk1/syscall_handler.c | 18 +-- .../tools/partition_table/go.mod | 7 + .../tools/partition_table/go.sum | 4 + .../tools/partition_table/partition_table.go | 143 ++++++++++++++---- 11 files changed, 207 insertions(+), 73 deletions(-) create mode 100644 hw/application_fpga/tools/partition_table/go.mod create mode 100644 hw/application_fpga/tools/partition_table/go.sum diff --git a/hw/application_fpga/fw/tk1/main.c b/hw/application_fpga/fw/tk1/main.c index 534f2d8..ad38b54 100644 --- a/hw/application_fpga/fw/tk1/main.c +++ b/hw/application_fpga/fw/tk1/main.c @@ -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; diff --git a/hw/application_fpga/fw/tk1/partition_table.c b/hw/application_fpga/fw/tk1/partition_table.c index afcc54d..01c068a 100644 --- a/hw/application_fpga/fw/tk1/partition_table.c +++ b/hw/application_fpga/fw/tk1/partition_table.c @@ -2,43 +2,67 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include +#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; } diff --git a/hw/application_fpga/fw/tk1/partition_table.h b/hw/application_fpga/fw/tk1/partition_table.h index 1709d33..e570fac 100644 --- a/hw/application_fpga/fw/tk1/partition_table.h +++ b/hw/application_fpga/fw/tk1/partition_table.h @@ -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 diff --git a/hw/application_fpga/fw/tk1/preload_app.c b/hw/application_fpga/fw/tk1/preload_app.c index 938b751..3a145b8 100644 --- a/hw/application_fpga/fw/tk1/preload_app.c +++ b/hw/application_fpga/fw/tk1/preload_app.c @@ -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 diff --git a/hw/application_fpga/fw/tk1/preload_app.h b/hw/application_fpga/fw/tk1/preload_app.h index bdb3c2d..14ec50e 100644 --- a/hw/application_fpga/fw/tk1/preload_app.h +++ b/hw/application_fpga/fw/tk1/preload_app.h @@ -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); diff --git a/hw/application_fpga/fw/tk1/storage.c b/hw/application_fpga/fw/tk1/storage.c index c54ba85..b500dc3 100644 --- a/hw/application_fpga/fw/tk1/storage.c +++ b/hw/application_fpga/fw/tk1/storage.c @@ -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; } diff --git a/hw/application_fpga/fw/tk1/storage.h b/hw/application_fpga/fw/tk1/storage.h index f48cb0b..c61860f 100644 --- a/hw/application_fpga/fw/tk1/storage.h +++ b/hw/application_fpga/fw/tk1/storage.h @@ -10,8 +10,8 @@ #include #include -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, diff --git a/hw/application_fpga/fw/tk1/syscall_handler.c b/hw/application_fpga/fw/tk1/syscall_handler.c index 3bad1db..8a877b5 100644 --- a/hw/application_fpga/fw/tk1/syscall_handler.c +++ b/hw/application_fpga/fw/tk1/syscall_handler.c @@ -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); diff --git a/hw/application_fpga/tools/partition_table/go.mod b/hw/application_fpga/tools/partition_table/go.mod new file mode 100644 index 0000000..f14a95d --- /dev/null +++ b/hw/application_fpga/tools/partition_table/go.mod @@ -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 diff --git a/hw/application_fpga/tools/partition_table/go.sum b/hw/application_fpga/tools/partition_table/go.sum new file mode 100644 index 0000000..927b255 --- /dev/null +++ b/hw/application_fpga/tools/partition_table/go.sum @@ -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= diff --git a/hw/application_fpga/tools/partition_table/partition_table.go b/hw/application_fpga/tools/partition_table/partition_table.go index a34320e..aa3d9c8 100644 --- a/hw/application_fpga/tools/partition_table/partition_table.go +++ b/hw/application_fpga/tools/partition_table/partition_table.go @@ -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) + // } }