Add 0x0026a custom AES-128-CMAC secure bootloader, Week 11a notebook, and README links

This commit is contained in:
Kevin Thomas committed 2026-10-13 12:37:20 -04:00
1 parent b85aaf72d6
commit bec685d573
33 files changed
+3451

No files matched your search

@@ -0,0 +1,40 @@
/**
* @file aes_cmac.h
* @brief AES-128-CMAC authentication tag helpers backed by Mbed TLS.
* @author Kevin Thomas
* @date 2026
*
* MIT License
* Copyright (c) 2026 Kevin Thomas
*/
#ifndef AES_CMAC_H
#define AES_CMAC_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
/** @brief AES block size in bytes. */
#define AES_BLOCK_SIZE 16U
/**
* @brief Compute an AES-128-CMAC tag over a memory region.
* @param key Pointer to the 16-byte AES-128 key.
* @param data Pointer to the message bytes, which may live in XIP flash.
* @param len Message length in bytes.
* @param tag Output buffer that receives the 16-byte CMAC tag.
* @return int Zero on success, non-zero when the cipher is unavailable.
*/
int aes128_cmac(const uint8_t *key, const uint8_t *data, size_t len,
uint8_t *tag);
/**
* @brief Compare two CMAC tags without short-circuiting on the first byte.
* @param a Pointer to the first tag.
* @param b Pointer to the second tag.
* @return bool True when the tags are byte-for-byte identical.
*/
bool cmac_tags_equal(const uint8_t *a, const uint8_t *b);
#endif
@@ -0,0 +1,45 @@
/**
* @file boot.h
* @brief Secure boot console and application handoff helpers.
* @author Kevin Thomas
* @date 2026
*
* MIT License
* Copyright (c) 2026 Kevin Thomas
*/
#ifndef BOOT_H
#define BOOT_H
#include "manifest.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Bring up the debug console and print the boot banner.
* @param void No parameters.
* @return void No return value.
*/
void boot_init(void);
/**
* @brief Print a boot failure reason and idle forever.
* @param reason Null-terminated message describing the failure.
* @return void Does not return.
*/
void boot_halt(const char *reason);
/**
* @brief Announce the verified image and jump to its reset vector.
* @param manifest Pointer to the validated manifest.
* @return void Does not return.
*/
void boot_launch(const fw_manifest_t *manifest);
#ifdef __cplusplus
}
#endif
#endif // BOOT_H
@@ -0,0 +1,31 @@
/**
* @file flash_layout.h
* @brief Flash partition map shared by the bootloader and the host signer.
* @author Kevin Thomas
* @date 2026
*
* MIT License
* Copyright (c) 2026 Kevin Thomas
*/
#ifndef FLASH_LAYOUT_H
#define FLASH_LAYOUT_H
#include <stdint.h>
/** @brief Absolute base address of the bootloader image in XIP flash. */
#define BOOTLOADER_BASE 0x10000000U
/** @brief Absolute address of the one-sector firmware manifest. */
#define FW_MANIFEST_BASE 0x1001F000U
/** @brief Size in bytes of the flash sector holding the manifest. */
#define FW_MANIFEST_SECTOR_SIZE 4096U
/** @brief Absolute base address where the signed application runs. */
#define APP_BASE 0x10020000U
/** @brief Total XIP flash size in bytes on the RP2350 Pico 2 board. */
#define FLASH_TOTAL_SIZE 0x200000U
#endif
@@ -0,0 +1,23 @@
/**
* @file image.h
* @brief Control transfer into a freshly verified Cortex-M application.
* @author Kevin Thomas
* @date 2026
*
* MIT License
* Copyright (c) 2026 Kevin Thomas
*/
#ifndef IMAGE_H
#define IMAGE_H
#include <stdint.h>
/**
* @brief Jump to an application whose vector table starts at a base address.
* @param vector_base Base address of the application vector table.
* @return void Does not return to the caller.
*/
void image_start(uint32_t vector_base);
#endif
@@ -0,0 +1,51 @@
/**
* @file manifest.h
* @brief Firmware manifest stored in the flash sector before the application.
* @author Kevin Thomas
* @date 2026
*
* MIT License
* Copyright (c) 2026 Kevin Thomas
*/
#ifndef MANIFEST_H
#define MANIFEST_H
#include <stdint.h>
#include <stdbool.h>
/** @brief Little-endian magic at the head of a valid manifest ("WFLB"). */
#define FW_MANIFEST_MAGIC 0x424C4657U
/** @brief Current manifest structure revision. */
#define FW_MANIFEST_VERSION 1U
/** @brief Length in bytes of an AES-128-CMAC authentication tag. */
#define FW_TAG_SIZE 16U
#pragma pack(push, 1)
/** @brief Fixed-layout record describing the signed application image. */
typedef struct {
uint32_t magic;
uint32_t version;
uint32_t app_base;
uint32_t app_size;
uint8_t tag[FW_TAG_SIZE];
} fw_manifest_t;
#pragma pack(pop)
/**
* @brief Copy the manifest out of its fixed flash address.
* @param out Pointer to the manifest structure to populate.
* @return void No return value.
*/
void manifest_read(fw_manifest_t *out);
/**
* @brief Report whether a manifest carries the expected magic and version.
* @param manifest Pointer to the manifest to inspect.
* @return bool True when the magic and version both match.
*/
bool manifest_is_valid(const fw_manifest_t *manifest);
#endif
@@ -0,0 +1,32 @@
/**
* @file verify.h
* @brief Firmware authenticity verification for the secure bootloader.
* @author Kevin Thomas
* @date 2026
*
* MIT License
* Copyright (c) 2026 Kevin Thomas
*/
#ifndef VERIFY_H
#define VERIFY_H
#include <stdbool.h>
#include "manifest.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Recompute the CMAC over the application and compare it to the tag.
* @param manifest Pointer to the validated manifest.
* @return bool True when the tag matches, false otherwise.
*/
bool verify_manifest(const fw_manifest_t *manifest);
#ifdef __cplusplus
}
#endif
#endif // VERIFY_H
@@ -0,0 +1,29 @@
/**
* @file aes_cmac.c
* @brief AES-128-CMAC primitives implemented on top of the SDK Mbed TLS.
* @author Kevin Thomas
* @date 2026
*
* MIT License
* Copyright (c) 2026 Kevin Thomas
*/
#include "mbedtls/cmac.h"
#include "mbedtls/cipher.h"
#include "aes_cmac.h"
int aes128_cmac(const uint8_t *key, const uint8_t *data, size_t len,
uint8_t *tag) {
const mbedtls_cipher_info_t *info;
info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB);
if (info == NULL) return -1;
return mbedtls_cipher_cmac(info, key, 128, data, len, tag);
}
bool cmac_tags_equal(const uint8_t *a, const uint8_t *b) {
uint8_t diff = 0;
for (size_t i = 0; i < AES_BLOCK_SIZE; i++) {
diff |= (uint8_t)(a[i] ^ b[i]);
}
return diff == 0;
}
@@ -0,0 +1,33 @@
/**
* @file boot.c
* @brief Secure boot console and application handoff helpers.
* @author Kevin Thomas
* @date 2026
*
* MIT License
* Copyright (c) 2026 Kevin Thomas
*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "boot.h"
#include "image.h"
void boot_init(void) {
stdio_init_all();
sleep_ms(2000);
printf("\n=== 0x0026a secure bootloader ===\n");
}
void boot_halt(const char *reason) {
printf("BOOT FAILED: %s\n", reason);
while (true) {
sleep_ms(500);
}
}
void boot_launch(const fw_manifest_t *manifest) {
printf("signature valid, entering app at 0x%08lx\n",
(unsigned long)manifest->app_base);
image_start(manifest->app_base);
}
@@ -0,0 +1,33 @@
/**
* @file image.c
* @brief Loads the application stack pointer and branches to its reset vector.
* @author Kevin Thomas
* @date 2026
*
* MIT License
* Copyright (c) 2026 Kevin Thomas
*/
#include "image.h"
/** @brief Application reset handler signature. */
typedef void (*image_entry_t)(void);
/**
* @brief Mask interrupts and point VTOR at the incoming image.
* @param base Vector table base address written to the VTOR register.
* @return void No return value.
*/
static void halt_irq_and_rebase(uint32_t base) {
__asm volatile ("cpsid i" ::: "memory");
*(volatile uint32_t *)0xE000ED08U = base;
}
void image_start(uint32_t vector_base) {
uint32_t *vectors = (uint32_t *)vector_base;
image_entry_t entry = (image_entry_t)vectors[1];
halt_irq_and_rebase(vector_base);
__asm volatile ("msr msp, %0" :: "r" (vectors[0]) : "memory");
__asm volatile ("dsb\n\tisb" ::: "memory");
entry();
}
@@ -0,0 +1,34 @@
/**
* @file main.c
* @brief Secure bootloader entry point (verify AES-128-CMAC, then boot).
* @author Kevin Thomas
* @date 2026
*
* MIT License
* Copyright (c) 2026 Kevin Thomas
*
* -----------------------------------------------------------------------------
*
* This file is deliberately thin: it only sequences the secure boot steps.
* The console setup, halt reporting, and application handoff live in boot.c;
* the CMAC verification (and its key) live in verify.c; the manifest lives in
* manifest.c; and the raw reset-vector jump lives in image.c.
*
* Flash map (2 MiB, RP2350 Pico 2):
* 0x10000000 bootloader image (this program)
* 0x1001F000 one-sector firmware manifest (magic, size, CMAC tag)
* 0x10020000 signed application image
*/
#include "manifest.h"
#include "boot.h"
#include "verify.h"
int main(void) {
fw_manifest_t manifest;
boot_init();
manifest_read(&manifest);
if (!manifest_is_valid(&manifest)) boot_halt("manifest invalid");
if (!verify_manifest(&manifest)) boot_halt("firmware signature invalid");
boot_launch(&manifest);
}
@@ -0,0 +1,23 @@
/**
* @file manifest.c
* @brief Reads and validates the firmware manifest from XIP flash.
* @author Kevin Thomas
* @date 2026
*
* MIT License
* Copyright (c) 2026 Kevin Thomas
*/
#include "flash_layout.h"
#include "manifest.h"
void manifest_read(fw_manifest_t *out) {
const fw_manifest_t *src = (const fw_manifest_t *)FW_MANIFEST_BASE;
*out = *src;
}
bool manifest_is_valid(const fw_manifest_t *manifest) {
if (manifest->magic != FW_MANIFEST_MAGIC) return false;
if (manifest->version != FW_MANIFEST_VERSION) return false;
return true;
}
@@ -0,0 +1,28 @@
/**
* @file verify.c
* @brief Firmware authenticity verification for the secure bootloader.
* @author Kevin Thomas
* @date 2026
*
* MIT License
* Copyright (c) 2026 Kevin Thomas
*/
#include <stdint.h>
#include "flash_layout.h"
#include "verify.h"
#include "aes_cmac.h"
/** @brief AES-128 key that authenticates the application image. */
static const uint8_t fw_key[FW_TAG_SIZE] = {
0x49, 0x72, 0x6F, 0x6E, 0x4E, 0x65, 0x74, 0x42,
0x6F, 0x6F, 0x74, 0x4B, 0x65, 0x79, 0x21, 0x21
};
bool verify_manifest(const fw_manifest_t *manifest) {
const uint8_t *base = (const uint8_t *)manifest->app_base;
uint8_t computed[FW_TAG_SIZE];
int rc = aes128_cmac(fw_key, base, manifest->app_size, computed);
if (rc != 0) return false;
return cmac_tags_equal(computed, manifest->tag);
}