ARM TrustZone-M实战:在i.MX RT600上构建硬件级安全嵌入式系统
2026/6/8 15:17:04 网站建设 项目流程

1. 项目概述:从硬件隔离到可信执行环境

在嵌入式系统开发,尤其是涉及支付、身份认证或设备固件保护的场景里,我们常常面临一个核心矛盾:如何在资源受限的单片机(MCU)上,既运行功能丰富的应用程序,又能确保核心的安全算法、密钥等敏感资产不被恶意代码窃取或篡改?过去,我们可能依赖软件层面的权限管理,或者干脆使用两颗物理隔离的芯片,前者在漏洞面前不堪一击,后者则显著增加了成本和设计复杂度。

ARM TrustZone 技术的出现,为这个难题提供了一个优雅的硬件级解决方案。它不是在操作系统或应用层做文章,而是直接从 CPU 架构层面“动刀”,将一个物理 CPU 核心在逻辑上划分为两个隔离的世界:安全世界(Secure World)非安全世界(Non-secure World)。你可以把它想象成一栋大楼里的两个独立保险库,它们共用同一套地基和墙体(CPU硬件),但拥有完全独立的门禁系统(安全状态)、钥匙(内存访问权限)和监控(总线控制器)。非安全世界的代码,无论权限多高,都无法直接窥探或进入安全世界,除非通过一个严格受控的、唯一的“安检通道”。

我最近在基于 NXP 的 i.MX RT600 跨界 MCU(搭载 ARM Cortex-M33 内核)上,深入实践了 TrustZone 的配置与开发。RT600 不仅支持 ARMv8-M 架构的 TrustZone,还集成了 NXP 自家的安全子系统,如安全总线控制器、IDAU(实现定义属性单元)等,使得硬件隔离的实现更为灵活和强大。这次实践的目标很明确:不仅仅是让 LED 闪烁或串口打印,而是要亲手搭建起一个可运行的双世界工程,理解内存如何被划分为安全与非安全区域,掌握安全状态切换的“门道”,并学会处理那些令人头疼的安全违规异常。如果你正在为 IoT 设备、智能家居网关或任何需要硬件安全基础的嵌入式产品选型或开发,那么理解并掌握 TrustZone 的实战应用,将是提升你产品安全等级的关键一步。

2. TrustZone-M 核心机制深度解析

要玩转 TrustZone,不能只停留在“有两个世界”的概念上,必须深入到其硬件机制的核心。对于 Cortex-M33 这类微控制器,TrustZone for ARMv8-M 的实现与 Cortex-A 系列处理器有所不同,它更侧重于在资源受限环境下提供高效、确定性的隔离。

2.1 安全状态与内存属性:世界的边界

CPU 在任何时刻都处于两种状态之一:安全状态(S)或非安全状态(NS)。这个状态是 CPU 执行上下文的一部分,决定了它能访问哪些内存和外设。与之紧密关联的是内存的三种属性:

  • 安全(Secure):只能被处于安全状态的 CPU 访问。这里是存放核心密钥、加密算法、安全启动代码等“宝藏”的地方。
  • 非安全(Non-secure, NS):可以被安全和非安全状态的 CPU 访问。这里是常规应用程序的领地。
  • 非安全可调用(Non-secure Callable, NSC):这是一个特殊区域,它本身属于安全内存,但被标记为“可被非安全世界调用”。它是两个世界之间唯一合法的通信桥梁。非安全代码只能跳转到 NSC 区域内的特定地址(即安全网关指令所在处),从而触发状态切换。

这里有一个关键点:安全代码可以自由访问非安全数据(因为安全世界权限更高),但非安全代码绝对无法直接访问安全数据或跳转到安全代码。任何尝试都会触发硬件异常(SecureFault 或 HardFault)。这种单向访问权限是构建可信基(Trusted Computing Base, TCB)的基础,确保安全世界的代码体积小、可审计,从而降低被攻破的风险。

2.2 SAU 与 IDAU:内存属性的“绘图师”

内存的属性由谁来定义和检查?这主要由两个硬件单元负责:安全属性单元(SAU)实现定义属性单元(IDAU)

IDAU是芯片厂商(如 NXP)实现的固定硬件逻辑。在 RT600 上,IDAU 的规则极其简单粗暴:它仅查看地址的第 28 位(bit 28)

  • 如果 bit 28 = 0, 则该地址被 IDAU 判定为非安全
  • 如果 bit 28 = 1, 则该地址被 IDAU 判定为安全

这意味着,整个 4GB 的地址空间被对半映射。例如,地址0x0000 00000x1000 0000指向同一个物理内存位置,但前者被 IDAU 视为非安全,后者被视为安全。这种设计提供了极大的灵活性,但需要软件(链接脚本)正确地将代码和数据放置到对应的地址区域。

SAU则是软件可编程的单元,通常集成在 Cortex-M33 核心内。RT600 的 SAU 支持 8 个可配置的区域。开发者可以通过配置 SAU 来覆盖 IDAU 的默认判定,从而更精细地划分内存空间。SAU 的优先级高于 IDAU:只要 SAU 或 IDAU 中的任何一个将某地址范围标记为安全,那么该范围就是安全的。通常,我们会在系统启动时(安全世界初始化阶段)配置 SAU,将大部分 RAM 和 Flash 区域定义为非安全,只保留一小部分关键区域(如安全服务函数、密钥存储区)为安全。

注意:SAU 的配置必须在 CPU 处于安全特权模式下进行。一旦配置完成并可能被锁定,后续的非安全代码将无法修改。这是确保隔离策略不可篡改的重要机制。

2.3 安全状态切换:唯一的“安检门”

非安全代码如何请求安全世界的服务?这个过程必须通过一个严格的、受控的路径,即安全网关(Secure Gateway, SG)指令

  1. 入口:非安全代码调用一个函数,这个函数的地址位于NSC 内存区域内。
  2. 触发切换:当 CPU(处于 NS 状态)执行到 NSC 区域中一条特定的SG指令时,硬件会自动将 CPU 状态切换到安全状态(S)。
  3. 执行服务SG指令之后,紧接着是一条分支指令(通常是BBL),跳转到实际的安全服务函数地址(该地址在安全内存中)。
  4. 返回:安全函数执行完毕后,使用特殊的返回指令(如BXNS)返回到非安全世界,CPU 状态切换回 NS。

这个过程中,NSC 区域就像一个“门厅”或“跳板”,里面只存放了极其简短的跳转代码(称为“veneer”函数)。安全服务函数的真实地址对非安全世界是隐藏的,非安全代码只知道“门厅”的地址,这进一步增加了安全性。

2.4 安全总线矩阵与外围隔离

内存隔离只是故事的一半。外设(如 UART、GPIO、加密加速器)同样需要被保护,防止非安全代码恶意操作。RT600 通过一个安全总线控制器矩阵来实现这一点,它主要由以下几个组件构成:

  • 主设备安全包装器(MSW):包裹在每一个总线主设备(如 Cortex-M33 核心、DMA 控制器)上。它为每个主设备赋予一个安全属性(Secure 或 Non-secure)。当主设备发起访问时,MSW 会将其安全状态作为“侧带信号”传递给总线。
  • 外设保护检查器(PPC):位于每一个总线从设备(外设)的入口。它接收来自总线的访问请求和主设备的安全属性,然后根据预先配置的规则(例如,某个外设只允许安全主设备访问)决定是允许还是拒绝此次访问,如果拒绝则产生错误。
  • 内存保护检查器(MPC):功能与 PPC 类似,但用于控制对内存块(如 SRAM、Flash)的访问。它可以以更细的粒度(例如 4KB 扇区)设置访问规则。

这套机制确保了即使非安全代码通过某种漏洞获得了某个外设的地址,只要 PPC 规则禁止 NS 访问,该操作也会被硬件拦截。此外,RT600 还提供了安全 GPIO 控制器,可以将特定的 GPIO 引脚配置为“安全引脚”,只有安全世界的代码才能控制它们,非常适合用来连接硬件安全模块(如 SE)的使能信号。

3. RT600 TrustZone 工程实战:从零构建双世界应用

理论铺垫完毕,现在进入最关键的实战环节。我们将以 NXP SDK 中的hello_world_trustzone示例为基础,拆解一个完整 TrustZone 应用的构建过程。我使用的是 MCUXpresso IDE 和 RT685-EVK 开发板,但原理通用于 Keil 和 IAR。

3.1 工程结构与链接脚本剖析

一个典型的 TrustZone 工程包含两个独立的项目(或构建目标):

  • 安全项目(Secure Project):编译生成安全世界的代码和数据,链接到安全地址区域(如 Flash 的 0x10000000 起始段, RAM 的 0x30000000 起始段)。
  • 非安全项目(Non-secure Project):编译生成非安全世界的代码和数据,链接到非安全地址区域(如 Flash 的 0x00000000 起始段, RAM 的 0x20000000 起始段)。

链接脚本(.ld 文件)是这一切的蓝图。你必须为两个项目分别编写链接脚本,明确指定各段(如 .text, .data, .bss)的加载地址(Load Address)和运行地址(VMA),并确保它们落在正确的安全/非安全地址空间。

安全项目链接脚本关键点

  • 定义 NSC 区域:你需要创建一个专门的段(例如.gnu.sgstubs),将其 VMA 设置在安全 Flash 中,但将其 LMA(加载地址)属性标记为可被非安全调用。在 MCUXpresso 中,这通常通过KEEP(*(.gnu.sgstubs*))和特定的地址分配来实现。
  • 指定安全向量表:安全世界的向量表(__Vectors)地址必须正确设置,通常位于安全 Flash 起始位置。

非安全项目链接脚本关键点

  • 指定非安全向量表:非安全世界的向量表(__Vectors_NS)地址必须正确设置,通常位于非安全 Flash 起始位置。系统复位后,CPU 首先从安全世界启动,安全启动代码会初始化 SAU,然后通过跳转指令将 CPU 切换到非安全状态,并从非安全向量表开始执行。
  • 引用安全项目符号:非安全项目需要调用安全函数。这不是通过传统的函数声明实现的,而是通过一个由安全项目生成的“veneer 表”的地址。在链接脚本中,你需要声明一个外部符号(如__VENEER_TABLE_START__)并指向 NSC 区域的起始地址。

3.2 SAU 初始化配置详解

系统上电后,CPU 处于安全状态。在跳转到非安全世界之前,安全世界的初始化代码必须配置好 SAU,划定好两个世界的“疆域”。以下是一个典型的 SAU 配置流程,基于 CMSIS-Core 函数,但原理与直接操作寄存器一致:

void BOARD_InitTrustZone(void) { /* 1. 启用 SAU */ TZ_SAU_Enable(); /* 2. 配置区域 0:非安全 RAM(用于代码执行,如非安全项目的 .text) */ SAU->RNR = 0; // 选择区域寄存器 0 SAU->RBAR = (NS_RAM_CODE_START & SAU_RBAR_BADDR_Msk); // 起始地址,例如 0x20000000 SAU->RLAR = ((NS_RAM_CODE_START + NS_RAM_CODE_SIZE - 1) & SAU_RLAR_LADDR_Msk) | SAU_RLAR_ENABLE_Msk; // 结束地址,并启用区域。注意未设置 NSC 位,此为普通 NS 区域。 /* 3. 配置区域 1:非安全 RAM(用于数据,如非安全项目的 .data, .bss) */ SAU->RNR = 1; SAU->RBAR = (NS_RAM_DATA_START & SAU_RBAR_BADDR_Msk); // 例如 0x20010000 SAU->RLAR = ((NS_RAM_DATA_START + NS_RAM_DATA_SIZE - 1) & SAU_RLAR_LADDR_Msk) | SAU_RLAR_ENABLE_Msk; /* 4. 配置区域 2:非安全可调用 (NSC) RAM(用于存放跳转代码的 veneer 表)*/ SAU->RNR = 2; SAU->RBAR = (NSC_RAM_START & SAU_RBAR_BADDR_Msk); // 注意:此地址必须在安全 RAM 范围内(如 0x30000000 以上),但被标记为 NSC SAU->RLAR = ((NSC_RAM_START + NSC_RAM_SIZE - 1) & SAU_RLAR_LADDR_Msk) | SAU_RLAR_NSC_Msk | // 关键!设置 NSC 属性 SAU_RLAR_ENABLE_Msk; /* 5. 配置区域 3:非安全 Flash(用于非安全项目代码)*/ SAU->RNR = 3; SAU->RBAR = (NS_FLASH_START & SAU_RBAR_BADDR_Msk); // 例如 0x00000000 SAU->RLAR = ((NS_FLASH_START + NS_FLASH_SIZE - 1) & SAU_RLAR_LADDR_Msk) | SAU_RLAR_ENABLE_Msk; /* 6. 配置区域 4:非安全可调用 (NSC) Flash(用于存放跳转代码的 veneer 表)*/ SAU->RNR = 4; SAU->RBAR = (NSC_FLASH_START & SAU_RBAR_BADDR_Msk); // 注意:此地址必须在安全 Flash 范围内(如 0x10000000 以上),但被标记为 NSC SAU->RLAR = ((NSC_FLASH_START + NSC_FLASH_SIZE - 1) & SAU_RLAR_LADDR_Msk) | SAU_RLAR_NSC_Msk | // 关键!设置 NSC 属性 SAU_RLAR_ENABLE_Msk; /* 7. 强制内存隔离生效 */ __DSB(); __ISB(); TZ_SAU_Setup(); }

实操心得:SAU 区域的配置顺序和范围不能重叠。通常建议从非安全区域开始配置,并确保 NSC 区域完全包含在安全区域内。在调试时,如果非安全代码一运行就触发 HardFault,十有八九是 SAU 配置有误,导致非安全代码试图访问未明确声明为 NS 的内存,而 IDAU 默认将其视为安全区域,从而引发访问违规。

3.3 安全函数导出与非安全调用

安全世界提供一个函数给非安全世界使用,需要以下几个步骤:

1. 在安全项目中定义函数并声明为“可调用”:

// 在安全项目源文件中(例如 secure_service.c) #include “arm_cmse.h” // 包含 CMSIS 安全扩展头文件 // 这是一个安全函数,执行一些敏感操作,比如生成随机数 int32_t __attribute__((cmse_nonsecure_entry)) SECURE_GenerateRandomNumber(void) { // 访问硬件随机数生成器(这是一个安全外设) return SOME_RNG->VALUE; }

cmse_nonsecure_entry属性是关键。编译器会为这个函数生成一段特殊的“veneer”代码(包含SG指令),并将其放置在 NSC 内存区域。

2. 在非安全项目中声明并使用函数:

// 在非安全项目源文件中 // 声明函数,注意需要指定其位于“安全可调用”区域 extern int32_t SECURE_GenerateRandomNumber(void) __attribute__((cmse_nonsecure_call)); void non_secure_app_task(void) { int32_t random_value; // 直接调用,硬件会自动处理状态切换 random_value = SECURE_GenerateRandomNumber(); printf(“Random number from secure world: %ld\n”, random_value); }

cmse_nonsecure_call属性告诉编译器,这个函数调用需要生成特殊的跳转指令(BLXNS),并且会在调用前清除寄存器内容,防止安全数据通过寄存器泄露到非安全世界。

3. 处理 Veneer 表:链接器会自动收集所有标有cmse_nonsecure_entry的函数,将它们对应的 veneer 函数地址集中到一个表中。安全项目的启动代码需要将这个 veneer 表的起始地址(__VENEER_TABLE_START__)和结束地址(__VENEER_TABLE_END__)传递给非安全项目(通常通过一个预定义的、双方约定的内存位置或符号)。非安全项目的链接脚本需要引用这个地址,以确保对安全函数的调用能正确跳转到 NSC 区域。

3.4 安全世界调用非安全函数

有时,安全服务在执行过程中可能需要回调非安全世界的某个函数(例如,通知非安全应用某个安全事件已发生)。这通过cmse_nonsecure_call属性实现,但方向相反。

// 在安全项目源文件中 // 定义一个函数指针类型,用于指向非安全函数 typedef void (*ns_callback_t)(int event_id) __attribute__((cmse_nonsecure_call)); // 安全函数,它接收一个来自非安全的回调函数指针 void SECURE_ProcessRequest(int request, ns_callback_t ns_callback) { // ... 执行安全处理 ... if (ns_callback != NULL) { // 调用非安全回调函数 ns_callback(process_result); // 调用返回后,CPU 自动切换回安全状态 } // ... 后续安全操作 ... }

在非安全世界,你需要将一个普通的函数地址传递给这个安全函数。安全代码在调用前,需要使用 CMSIS 提供的cmse_nsfptr_create宏来验证并封装这个函数指针,确保跳转的安全性。

4. 安全故障(SecureFault)处理与调试技巧

在 TrustZone 开发中,安全故障是调试的“常客”。任何违反安全访问规则的行为都会触发 SecureFault 异常。熟练地分析和处理这些异常,是开发稳健 TrustZone 应用的必备技能。

4.1 常见 SecureFault 触发场景

  1. 非法状态切换:非安全代码试图通过非 NSC 区域的地址(即没有SG指令的地址)跳转到安全世界。或者,安全代码错误地使用普通BX指令跳转到非安全地址(应使用BXNS)。
  2. 非法内存访问:非安全代码试图读取或写入安全内存区域(SAU 或 IDAU 标记为安全)。或者,安全代码在非特权线程模式下试图访问只允许特权模式访问的安全外设(通过 MPU 配置)。
  3. 无效的 NSC 调用:非安全代码调用了一个 NSC 区域内的地址,但该地址处的指令不是SG。这通常是由于 veneer 表地址错乱或函数指针被篡改导致的。
  4. 安全总线控制器拦截:即使 SAU 允许访问(例如地址被标记为非安全),但如果安全总线控制器(如 PPC)的规则禁止当前主设备(如非安全状态的 DMA)访问该从设备,也会触发错误(通常是 BusFault)。

4.2 SecureFault 处理程序实现

你需要编写一个 SecureFault_Handler,用于捕获和分析这些错误。CMSIS 提供了SCB->CFSR(可配置故障状态寄存器)和SCB->SFSR(安全故障状态寄存器)来诊断问题。

void __attribute__((naked)) SecureFault_Handler(void) { __asm volatile( “tst lr, #4\n\t” // 检查 EXC_RETURN 的 bit2, 判断返回时使用 MSP 还是 PSP “ite eq\n\t” “mrseq r0, msp\n\t” // 如果使用 MSP, 将 MSP 值存入 R0 “mrsne r0, psp\n\t” // 如果使用 PSP, 将 PSP 值存入 R0 “b analyze_secure_fault\n\t” // 跳转到 C 函数进行分析 ); } void analyze_secure_fault(uint32_t* stack_frame) { uint32_t sfsr = SCB->SFSR; // 读取安全故障状态寄存器 uint32_t cfsr = SCB->CFSR; // 读取可配置故障状态寄存器 uint32_t mmfar = SCB->MMFAR; // 内存管理故障地址寄存器(如果 SFAR 有效) uint32_t bfar = SCB->BFAR; // 总线故障地址寄存器 uint32_t sfar = SCB->SFSR & SCB_SFSR_SFARVALID_Msk ? (uint32_t)(*((uint32_t*)(0xE000ED34))) : 0; // 安全故障地址寄存器(需查阅手册) printf(“!!! SecureFault !!!\n”); printf(“SFSR = 0x%08lX\n”, sfsr); printf(“CFSR = 0x%08lX\n”, cfsr); if (sfsr & SCB_SFSR_INVEP_Msk) { printf(“ - Invalid exception return.\n”); } if (sfsr & SCB_SFSR_INVIS_Msk) { printf(“ - Integrity signature error.\n”); } if (sfsr & SCB_SFSR_INVER_Msk) { printf(“ - Invalid entry point (SG instruction missing).\n”); } if (sfsr & SCB_SFSR_AUVIOL_Msk) { printf(“ - Attribution unit violation.\n”); printf(“ Fault Address (SFAR): 0x%08lX\n”, sfar); } if (cfsr & SCB_CFSR_MEMFAULT_Msk) { // 进一步解析内存管理故障 printf(“ - Memory Management Fault.\n”); printf(“ Fault Address (MMFAR): 0x%08lX\n”, mmfar); } if (cfsr & SCB_CFSR_BUSFAULT_Msk) { // 进一步解析总线故障 printf(“ - Bus Fault.\n”); printf(“ Fault Address (BFAR): 0x%08lX\n”, bfar); } // 打印出错的 PC 和 LR(来自堆栈帧) printf(“Faulting PC: 0x%08lX\n”, stack_frame[6]); printf(“Faulting LR: 0x%08lX\n”, stack_frame[5]); // 死循环,或根据策略进行系统复位 while(1) { __BKPT(0); // 触发调试器断点 } }

4.3 调试技巧与避坑指南

  1. 善用调试器视图:现代 IDE(如 MCUXpresso, Keil)的调试视图可以显示当前 CPU 的安全状态(S/NS),以及内存窗口会区分安全/非安全地址的访问。在单步调试时,观察状态切换是否发生在预期位置。
  2. 从简单示例开始:不要一开始就构建复杂应用。先让 SDK 的hello_world_trustzone示例跑起来,理解其工程结构、链接脚本和 SAU 配置。然后尝试修改,比如增加一个自己的安全函数并调用。
  3. 链接脚本是重中之重:80% 的 TrustZone 启动问题源于链接脚本错误。务必反复检查两个项目的内存区域划分是否与 SAU 配置严格对应,NSC 区域是否正确定义并包含了 veneer 表。
  4. 注意编译工具链:确保你使用的编译器版本支持 ARMv8-M 架构和 TrustZone 扩展(-mcmse编译选项)。在 MCUXpresso 中,创建 TrustZone 项目模板会自动配置这些选项。
  5. 安全世界的初始化顺序:安全世界的main()函数(或Reset_Handler)需要按正确顺序执行:初始化关键硬件(如时钟)、配置 MPU(如果需要)、配置 SAU、初始化安全服务,最后才跳转到非安全世界。跳转前必须确保非安全世界的栈指针和向量表地址已正确设置。
  6. 处理安全世界的中断:安全世界可以配置某些中断为安全中断(通过 NVIC 的ITNS寄存器)。安全中断服务程序(ISR)必须放置在安全内存中。非安全世界的中断发生时,如果 CPU 处于安全状态,会先完成当前安全任务,再处理非安全中断(除非中断被配置为抢占)。

5. 进阶话题:系统安全加固实践

当基本的多世界框架跑通后,我们需要思考如何构建一个真正坚固的安全系统。TrustZone 提供了硬件基础,但良好的软件架构和策略同样重要。

5.1 最小化可信计算基(TCB)

TCB 是指系统中所有安全关键软件和硬件的集合。一个核心原则是:TCB 越小,被攻击的面就越小,安全性就越容易验证和保证。在 TrustZone 设计中:

  • 将安全世界的代码限制在绝对必要的范围内:安全启动、加解密服务、密钥管理、安全存储、真随机数生成等。
  • 复杂的业务逻辑、网络协议栈、文件系统等,应尽量放在非安全世界。
  • 安全世界提供的 API(通过 NSC 调用)应设计得简洁、原子化,每次调用只完成一项明确的任务,避免复杂的上下文传递。

5.2 安全启动与固件验证

TrustZone 是运行时的保护,而安全启动是确保系统从第一行代码开始就可信的基石。RT600 的 EdgeLock 400A 安全子系统支持基于硬件信任根的安全启动流程:

  1. ROM Bootloader:芯片上电后,首先执行不可更改的 ROM 代码。它会验证最初加载的固件(通常是安全世界的 bootloader)的数字签名,使用存储在硬件中的公钥或证书。
  2. 安全 Bootloader:验证通过后,执行安全 bootloader。它负责初始化更复杂的硬件(包括 TrustZone 的 SAU、MPU),然后验证并加载安全世界的核心固件(Secure Firmware)。
  3. 安全固件:安全固件验证并加载非安全世界的应用程序。 这个链式验证过程,确保了在非安全应用代码执行之前,整个软件链的完整性和真实性都得到了校验。

5.3 外设与内存的细粒度隔离

除了 SAU 划分的大块内存区域,还可以利用安全 MPU非安全 MPU进行更细粒度的保护。

  • 安全 MPU:在安全世界内部,可以限制不同安全任务(如果运行安全 RTOS)对安全内存和外围的访问权限。例如,一个负责解密的任务可能只能访问密钥存储区和解密引擎,而不能访问安全日志区。
  • 非安全 MPU:在非安全世界,同样可以使用 MPU 来防止应用程序的不同模块相互干扰或攻击,这是传统嵌入式安全的重要手段,与 TrustZone 结合使用,形成纵深防御。
  • 外设 PPC 配置:仔细规划每个外设的安全属性。例如,连接着安全元件的 I2C 总线、用于安全日志的 UART、加解密加速器,都应配置为仅安全世界可访问。而用户按钮、普通显示屏的 SPI 等,可以配置为非安全世界可访问。

5.4 安全与非安全世界的通信协议

两个世界之间的通信不能是简单的函数调用加几个参数。需要设计一个健壮的、防篡改的通信协议。

  • 参数检查:安全函数在处理来自非安全世界的指针参数时,必须使用cmse_check_pointed_object等 CMSIS 函数来验证该指针指向的区域是否确实是非安全的,防止非安全世界传递一个指向安全内存的指针来进行“数据探针”攻击。
  • 序列化与反序列化:考虑使用简单的序列化格式传递复杂数据,并在安全世界进行严格的格式和范围校验。
  • 会话与状态管理:对于需要多次交互的服务,安全世界应维护会话状态,并验证每次调用是否属于有效的、未篡改的会话。

在我实际将 TrustZone 应用于一个智能门锁的固件升级方案时,就深刻体会到了这些原则的重要性。安全世界仅包含一个最小的 bootloader 和用于验证新固件签名的 RSA 算法。非安全世界则包含了完整的 TCP/IP 栈、HTTP 客户端和文件系统。升级时,非安全世界下载固件包后,通过 NSC 调用安全世界的验证函数。安全函数会严格检查传入的缓冲区地址(确保是 NS RAM),然后计算哈希、验证签名。只有验证通过,安全世界才会将一个“允许跳转到新固件”的标志写入受保护的 Flash 扇区,并触发系统复位。这个设计确保了即使非安全世界的网络栈被攻破,攻击者也无法植入未经签名的恶意固件。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询