本文我们介绍了 TrustZone 技术。通过CPU内置的硬件强制隔离,TrustZone 提供了一种高效的全系统安全设计。
我们介绍了如下功能:将 TrustZone 技术添加到处理器架构中,内存系统对于 TrustZone 的支持以及典型的软件架构。我们还介绍了 Arm 提供的资源,帮助使用 TrustZone 的系统和软件开发人员。
当阅读完本文时,你可以:
本文假定你熟悉Arm异常模型和内存管理。如果你不熟悉这些主题,请先阅读之前的异常模型和内存管理指南。另外如果你不熟悉安全概念,还建议你先阅读安全简介指南。
TrustZone 是 Arm A 系列中安全架构的名称。TrustZone 首次在 Armv6K 中引入,Armv7-A 和 Armv8-A 也是支持的。TrustZone 提供两种执行环境,它们之间是硬件强制隔离的,如下图所示。
普通世界运行着丰富的软件。这些软件栈通常包括许多应用程序,操作系统如Linux,可能还有 hypervisor,它们通常庞大且复杂。虽然可以尽力保护它们,但由于攻击面太大,它们也更容易受到攻击。
可信世界运行着一套小而简单的软件,通常称为可信执行环境(TEE)。TEE通常包括一些由轻量级内核托管的可信服务,这些服务提供类似密钥管理等功能。这些软件的攻击面比较窄,因此可以减少易受攻击的漏洞。
TrustZone 的目标是化圆为方。作为用户和开发人员,我们想要普通世界的丰富功能和灵活性。与此同时,我们希望在可信世界中通过更小、更受限制的软件来实现更高程度的信任。TrustZone 技术实现了以上要求,提供了两个硬件强制隔离的环境。
TrustZone 还用于表示 Armv8‑M 架构中的安全扩展。虽然 A 系列架构中和 M 系列架构中的 TrustZone 之间存在相似之处,但也有一些差异,本文仅针对 A 系列。
Armv9-A Realm Management Extension(RME) 延伸了 TrustZone 概念。本文不涉及 RME,但你可以在 Realm Management Extension 指南中找到更多信息。
本章我们讨论了处理器对 TrustZone 的支持,其他章节涵盖内存系统的支持,处理器和内存系统上的软件。
在Arm架构中,有两种安全状态:安全和非安全。这些安全状态对应到上一章提到的可信世界和普通世界。
处理器在 EL0,EL1和EL2异常等级,既可以处于安全状态,也可以处于非安全状态,这是由 SCR_EL3.NS
位控制的。你经常看到这样写:
无论SCR_EL3.NS
位的值是什么,EL3 始终处于安全状态。安全状态和异常等级如下图所示。
如果处理器位于 NS.EL1,软件想切换到 S.EL1,怎么实现?
要改变安全状态,无论朝哪个方向(安全到非安全或者非安全到安全),执行都必须通过EL3,如下图所示。
上图示例给出了在安全状态之间切换,需要的步骤顺序。
SCR_EL3.NS
位翻转。更改安全状态不仅仅是切换异常等级和修改SCR_EL3.NS
位,我们还必须考虑处理器状态。
向量寄存器、通用寄存器和大多数系统寄存器只有一份副本。当在安全状态之间切换时,保存和恢复寄存器状态是软件而不是硬件的职责。按照惯例,执行此操作的软件称为安全监控(Secure Monitor)。上面的示例更像下图所示:
Trusted Firmware 是 Arm 赞助的一个开源项目,提供了安全监控器的参考实现,我们将在后面讨论 Trusted Firmware。
少数寄存器与当前安全状态相关,这意味着寄存器有两个副本,并且内核自动使用属于当前安全状态的副本。处理器需要同时知道这些寄存器的两种配置。例如 ICC_BPR1_EL1
,用于控制中断抢占的GIC寄存器。寄存器存储是异常而不是规则,你可以在处理器的架构参考手册中找到。
当存储一个系统寄存器时,我们使用(S)和(NS)来标记使用哪一个寄存器副本,例如ICC_BPR1_EL1(S), ICC_BPR1_EL1(NS)
。
内存管理指南中介绍了多个虚拟地址空间及转换机制的概念。例如,EL0/1有一个转换进制,EL2有一个单独的转换机制,如下所示。
安全和非安全状态也有单独的转换机制。例如,有安全 EL0/1 转换机制和非安全 EL0/1 转换机制,如下所示:
在编写地址时,通常使用前缀来标识所引用的转换机制:
需要注意的是,S.EL1:0x8000
和NS.EL1:0x8000
是两个不同且独立的虚拟地址。处理器在安全状态下不使用 NS.EL1 转换机制,在非安全状态下不会使用 S.EL1 转换机制。
除了两种安全状态外,架构还提供了两种物理地址空间:安全和非安全。
在非安全状态下,虚拟地址始终转换为非安全物理地址。这意味着非安全状态下的软件只能看到非安全资源,而永远看不到安全资源,如下所示。
在安全状态下,软件可以访问安全和非安全物理地址空间。转换页表项中的 NS 位控制虚拟内存块或页转换到哪个物理地址空间,如下图所示:
与虚拟地址一样,通常使用前缀来标识所引用的地址空间。对于物理地址,这些前缀是 NP:
和 SP:
,例如:
重要的是要记住:安全和非安全是不同的地址空间,而不仅仅是可读或可写等属性。这意味着上面示例中的NP:0x8000
和SP:0x8000
是不同的内存区域,处理器将其视为不同的内存位置。
如果实施了Armv9-A的RME,物理地址空间的数量将增加到四个。额外的物理地址空间是 Root 和 Realm。安全状态下运行的软件仍然只能访问非安全和安全物理地址空间。
有关 RME 的更多信息,请参阅Realm Management Extension指南。
在Arm架构中,数据缓存是物理标记的。物理地址是下面每行的地址空间,如下。
查找NP:0x800000
上的缓存永远不会命中标有SP:0x800000
的缓存行,这是因为NP:0x800000
和SP:0x800000
是不同的地址。
这也会影响缓存的维护操作,考虑上图中的示例数据缓存。如果虚拟地址 va1 映射到物理地址 0x800000
,那么当软件从非安全状态发出 DC IVAC, va1
(数据或统一缓存行因虚拟地址无效)指令时会发生什么情况?
答案是,在非安全状态下,所有虚拟地址都会转换为非安全物理地址。因此,va1 映射到NP:0x800000
。缓存操作仅在指定地址行上,在本例中为NP:0x800000
,包含 SP:0x800000
的行不受影响。
如果我们在安全状态下执行相同的操作,且 va1 仍映射到NP:0x800000
,哪些缓存行会受到影响?
与前面的示例一样,缓存使包含指定物理地址NP:0x800000
的行无效,是否来自安全状态并不重要。
是否可以通过虚拟地址执行缓存操作,这个虚拟地址来自非安全,并映射到安全缓存行?
不可以。在非安全状态下,虚拟地址只能映射到非安全物理地址。根据定义,VA 从非安全状态进行的缓存操作只能针对非安全行。
对于设置操作,例如 DC ISW、Xt
,在非安全状态下发出的操作将仅影响非安全地址的行。从安全状态发出的设置操作会影响安全和非安全地址的行。
TLB缓存了最近使用的转换,处理器具有多个独立的转换机制。TLB 记录了哪些转换机制,包括安全状态的页表项。TLB 的结构是由实现定义的,下图只是一个示例:
当在 EL1 或 EL2 处发出 TLB 无效操作(即TLBI
指令)时,软件会针对当前的安全状态。因此,来自安全状态的TLBI ALLE1
会使 S.EL0/1 转换机制的所有缓存项无效。
EL3比较特殊。正如前面在安全状态中所述,当处于 EL0/1/2 时, SCR_EL3.NS
位控制处理器所处的安全状态。但是,无论SCR_EL3.NS
位如何,EL3 始终处于安全状态。当处于 EL3 时,SCR_EL3.NS
让软件控制TLBIs
操作的安全状态。
例如在EL3下执行TLBI ALLE1
指令(该指令使所有EL0/EL1虚拟地址空间的表项无效):
为了支持两种安全状态,Arm 架构定义了安全监控调用指令(SMC),执行SMC将会引起EL3下的SMC异常。
SMC 通常用于请求服务,这些服务驻留在 EL3 中或来自可信执行环境。SMC 最开始路由到 EL3,然后其中的 SMC 调度程序决定由谁进行处理。如下图所示:
为了标准化接口,Arm 提供了 SMC 调用约定(DEN0028
)和电源状态协调接口平台设计文档(DEN0022
)。这些规范说明了如何使用 SMC 来请求服务。
在EL1 上执行 SMC 可以陷入到 EL2,这对于 hypervisors 很有用,因为 hypervisors 可能需要模拟虚拟机所看到的固件接口。
当首次在Armv7-A引入虚拟化时,其仅支持在非安全状态下添加。在Armv8.3以前,安全支持如下。
如前面切换安全状态中所述, EL3 运行 Firmware 和 Secure Monitor。安全 EL0/1 运行可信执行环境 (TEE),它由可信服务和内核组成。
当时没有意识到需要多个处于安全状态的虚拟机,这意味着不需要支持安全的虚拟化。随着 TrustZone 使用增加,就出现一些其他需求:
解决方案是在安全状态下引入对 EL2 的支持,其随 Armv8.4‑A 一起提供,如下图所示:
S.EL2 通常运行安全分区管理器 (SPM),而不是完整的 hypervisor。SPM 允许创建隔离分区,这些分区无法看到其他分区的资源。系统可以有多个包含可信内核及其可信服务的分区。
分区还可以创建用于容纳平台固件,从而无需在EL3上运行这些代码。
启用安全EL2:当支持S.EL2
时,可以启用或者禁用它。S.EL2
是否使能由SCR_EL3.EEL2
位控制:
S.EL2
,其行为与不支持 S.EL2
的处理器相同S.EL2
安全状态下的第2阶段转换
在安全状态下,虚拟机 (VM) 的第一阶段转换可以输出安全和非安全地址,并由转换表描述符中的 NS 位控制。这会产生两个 IPA 空间(安全和非安全),每个空间都有自己的第 2 阶段转换表,如下图所示:
与第 1 阶段表不同,第 2 阶段页表项中没有 NS 位。对于给定的 IPA 空间,所有转换要么是安全物理地址,要么是非安全物理地址,其由一个寄存器位控制。通常,非安全 IPA 转换为非安全 PA,安全 IPA 转换为安全 PA。
到目前为止,我们主要是关注处理器,但 TrustZone 不只是一组处理器特性。为了利用 TrustZone 特性,我们还需要其他系统部分的支持。以下是系统启用 TrustZone 的示例。
下面将探讨该系统中的关键组件及其在 TrustZone 中的作用。
之前我们介绍了两种物理地址空间:安全和非安全。处理器将正在访问的地址空间导出到内存系统,内存系统使用这些信息进行强制隔离。
本节我们讨论安全总线和非安全总线。安全总线是指对安全物理地址空间的总线访问,非安全总线是指对非安全物理地址空间的总线访问。记住在安全状态下,软件可以访问两个物理地址空间。这意味着总线访问的安全性不一定与当前处理器的安全状态相同。
理论上,一个系统可以有两个完全独立的内存系统,使用访问的物理地址空间(AxPROT)选择。实际上这不太可能,相反,系统使用物理地址空间比如属性,控制对内存系统中不同设备的访问。
一般来说,我们讨论两种类型的存储器和外设以及总线完成器:
TrustZone aware
使用TrustZone技术构建的设备,并使用内部访问的安全特征。
比如通用中断控制器(GIC)。GIC可以被安全和非安全状态下的软件访问。非安全访问只能看到非安全中断,安全访问可以看到所有中断。GIC实现了使用总线事务的安全性来决定呈现哪种安全视图。
Non-TrustZone aware
这个是系统中的大多数设备形式,设备没有使用内部访问的安全特征。
例如定时器外设或者片上存储器。这些设备要么是安全,要么是非安全。
TrustZone有时被称为完成者强制保护系统。请求者发出安全/非安全访问信号,内存系统决定是否允许访问。那么内存系统是如何检查的呢?
在当今大多数系统中,基于内存系统的检查是通过互连(interconnect)完成的。例如,Arm NIC‑400 允许系统设计人员为每个连接上的完成者指定:
Secure
仅安全访问可以传递到设备。互连会将非安全访问标记为错误,不会将其传递给设备。
No-Secure
仅非安全访问可以传递到设备。互连会将安全访问标记为错误,不会将其传递给设备。
Boot time configurable
在系统启动初始化时,软件可以将设备配置为安全或非安全。
TrustZone aware
互连允许所有访问通过,连接的设备必须实现隔离。
例如:
这种方法适用于 TrustZone-aware 设备或完全位于一个地址空间内的设备。对于较大的存储器,例如片外 DDR,我们可能希望将内存划分为安全区域和非安全区域。TrustZone 地址空间控制器 (TZASC) 可以实现这一点,如下图所示:
TZASC 类似于内存保护单元 (MPU),并允许将设备的地址空间分为多个区域。将每个区域指定为安全或非安全。控制 TZASC 的寄存器只能安全访问,仅允许安全软件对内存进行分区。
TZASC 的一个示例实现是 Arm TZC‑400,它支持多达 9 个区域。
当实现了Armv9‑A 的 RME 扩展时,通过Granule Protection Table,内存可以动态的在不同的内存物理地址空间变化。更多信息请参考Introducing Arm’s Dynamic TrustZone technology。
接下来,我们看看系统中的总线请求者,如下图所示:
系统中的A系列处理器对 TrustZone 敏感,并在每次总线访问时发送正确的安全状态。然而,大多数现代SoC还有非处理器总线请求者,例如 GPU 和 DMA 控制器。
与完成者设备一样,我们可以将系统中的请求者设备大致分为几组:
TrustZone aware
一些请求者对 TrustZone 敏感,像处理器一样,为每次总线访问提供适当的安全信息。例如在 Arm SMMUv3 中构建的System MMUs(SMMUs)。
Non-TrustZone aware
并不是所有请求者都对 TrustZone 敏感,特别是一些旧的 IP。此类请求者通常不对总线访问提供安全信息,始终发送相同的值。
Design time tie-off
当请求者只需要访问单个物理地址空间,系统设计者可以捆绑适当的信号,将其访问的地址空间固定下来。这种方案虽然简单,但不够灵活。
Configurable logic
当请求者进行总线访问,提供了添加安全信息的逻辑。一些互连如 Arm NIC-400 提供了一些寄存器,安全软件可以在启动时配置这些寄存器,进而设置请求者访问的安全性。
SMMU
更灵活的选择是 SMMU。对于可信的请求者,SMMU 的行为类似于安全状态下的 MMU。这包括转换页表项中的 NS 位,控制访问哪个物理地址空间。
许多设计都集成了A、R、M系列处理器。例如手机包括:运行手机操作系统的 A 系列处理器,用于蜂窝调制解调器的 R 系列处理器,用于底层系统控制的 M 系列处理器。下面是手机中的不同处理器示意图。
R系列不像A系列那样支持两种安全状态,这意味着这些处理器上运行的软件无法控制物理地址空间。它们的行为与其他 non-TrustZone aware 总线请求者非常相似,这也适用于M系列处理器,其没有实现TrustZone。
通常这些处理器只需要访问单个物理地址空间。以手机为例,通常具有底层系统控制的 M 系列处理器,有时称为系统控制处理器SCP。在大多数系统中,SCP是纯安全设备。这意味着它只需要能够生成总线安全的访问。
接下来我们看看系统中的中断,如下图所示:
通用中断控制器 (GIC) 支持 TrustZone。每个中断源(在 GIC 规范中称为 INTID)被分配为三个组之一:
这是通过配置GIC[D|R]_IGROUPR
和GIC[D|R]_IGRPMODR
寄存器实现的,并且只能在安全状态下完成。分配操作并不是静态的,软件可以在运行时重新更新分配。
对于配置为安全的 INTID,只有安全总线访问才能修改状态和配置。对于安全中断对应的寄存器位,非安全总线读取的值为 0。
对于配置为非安全的 INTID,安全和非安全总线访问都可以修改状态和配置。
为什么有两个安全组?通常,Group 0 用于 EL3 固件处理的中断,这些涉及底层系统管理功能。Secure Group 1 用于其他安全中断源,通常由 S.EL1 或 S.EL2 软件处理。
处理器有两个中断异常:IRQ 和 FIQ。当中断变为挂起状态时,GIC 根据中断组和处理器当前的安全状态使用不同的中断信号:
Group 0 interrupt:始终发出FIQ异常信号
Secure Group 1
当前处理器处于安全状态:IRQ异常
当前处理器处于非安全状态:FIQ异常
Non-secure Group 1
当前处理器处于安全状态:FIQ异常
当前处理器处于非安全状态:IRQ异常
**请记住,Group 0 中断通常用于 EL3 固件。**这意味着:
下图示例展示了如何配置异常路由控制(图中SCR_EL3.FIQ=1表示FIQ中断均路由到EL3处理;SCR_EL3.IRQ=0表示如果当前运行在EL3,不处理IRQ中断,低于EL3等级则路由到当前运行的异常等级处理)。
上图只是一种可能的配置。另外比较常见的是在安全状态下,FIQ均路由到EL1。Trusted OS将FIQ视为向固件或者非安全状态的请求,这种方法使Trusted OS有机会从监控区退出。
接下来我们看看系统中的调试和跟踪组件,如下图所示。
现在Arm系统支持调试和分析的功能。当使用TrustZone时,我们必须保证这些功能不会降低系统的安全性。
当开发新的SoC时,系统每个部分只能信任相应的开发者。芯片公司的工程师需要信任所有组件,包括安全状态代码,因此需要启用所有调试功能。
当芯片交付给OEM厂商时,他们仍需要调试非安全状态的软件。但是OEM可能被禁止调试安全状态代码。
当芯片嵌入到产品后,应用开发者可能需要一些调试功能,但是不能向他们提供芯片原厂和OEM厂商的调试能力。
为了实现以上需求,可以启用不同的调试、跟踪和分析的功能。这包括用于控制安全状态和非安全状态功能的隔离标志,这些标志包括:
以下是如何使用这些标志示例:
芯片设计者进行早期开发
DBGEN==1, SPIDEN==1
:使能完整的外部调试功能
OEM进行产品开发
DBGEN==1
:启用非安全状态下的外部调试功能SPIDEN==0
:关闭安全状态下的调试功能量产产品
DBGEN==0, SPIDEN==0
:关闭安全和非安全状态下的外部调试功能
仍然允许应用级调试
由于我们在不同的开发阶段需要不同的调试标志,因此通常使用e-fuse或身份验证模块,下面是一个例子:
通过在制造过程中熔断fuse,可以永久禁用外部调试。使用fuse确实会使现场调试变得更加困难。当fuse被熔断后,无法重新恢复,因此采用认证模块更加灵活。
最后看一下系统中的其他设备,你可以在下图中找到:
上面启用TrustZone系统的示例还包括一些我们尚未涵盖的设备,但是在实际系统中确实需要构建。
一次性可编程存储器(OTP)或者fuses
这些存储器一旦写入数据就无法再更改。不像 boot ROM,每个芯片都是相同的镜像固件,OTP可以配置值为设备唯一和OEM唯一。
OTP的一个用处就是存储设备唯一私钥。当制造每个芯片时,一把随机的唯一密钥被写入OTP,这个密钥用于与该芯片进行绑定。
设备唯一密钥的优点是可以防止类攻击(class attack)。如果每个芯片都具有相同的密钥,那么当一台设备泄露时,其他所有类似的设备也将容易受到攻击。
OTP经常用于存储OEM公钥的哈希值。与其他存储器相比,OTP 相对昂贵。对于公钥来说,只存储哈希值而不存储完整密钥,可以节省成本。
非易失性计数器
非易失性 (NV) 计数器,实现更像熔丝fuses,这是一个只能递增而不能复位的计数器。
NV 计数器用于防回滚攻击。假定设备固件的版本3有一个已知漏洞,当前正在运行版本4,修复了该漏洞。攻击者可能利用这个漏洞,尝试将固件降级到版本 3。为了防止这种情况,每次更新固件时都会增加计数值。启动时,将根据 NV 计数器检查固件版本。如果不匹配,设备就知道它正在受到攻击。
可信RAM和ROM
这是片上只能安全访问的存储器。
可信 ROM 存放第一个启动代码,片上意味着攻击者无法替换它。作为 ROM 意味着攻击者无法对其进行重新编程,这表示我们有一个已知的、可信的执行起点,这将在本指南的软件架构部分中进行讨论。
可信 RAM 通常是几百 KB 的 SRAM,这是在安全状态下运行软件的使用内存。同样,由于位于芯片上,攻击者很难访问其内容。
可信基础系统架构(TBSA)是 Arm 为系统设计人员提供的一套指南。TBSA给出了不同情形下的资源需求建议,例如需要多少位OTP。
上面两章,我们探索了硬件方面的TrustZone支持,包括Arm处理器和内存系统。本章着眼于TrustZone系统中的软件架构。
下图是启用 TrustZone 系统的典型软件栈:
安全状态下的可信内核运行一些服务,如密钥管理或者DRM。在非安全状态下运行的软件需要对这些服务进行受控访问。
用户空间应用程序可能感觉不到TrustZone的存在。相反,它会使用用户空间库提供的高级 API,这个库与可信服务进行交互。 这类似于图像API提供对底层GPU的抽象。
服务库与可信服务之间通信通常使用内存中的消息队列或邮箱来处理。术语Word Shared Memory(WSM)有时用来描述这个通信的内存。这些队列必须位于两个世界的软件都可以看到的内存中,这意味着其是非安全内存,这是因为非安全状态只能看到非安全内存。
服务库将一个或多个请求放入邮箱中,然后调用内核空间中的驱动程序。驱动程序负责与可信执行环境 (TEE) 进行底层交互,其中可能包括为消息队列分配内存并向 TEE 注册。请记住,这两个世界在不同的虚拟地址空间中运行,因此它们不能使用虚拟地址进行通信。
驱动程序将调用安全状态,通常使用 SMC。这些控制将通过 EL3 安全监控器传递到 TEE 的可信内核中,然后内核从通信队列中读取调用服务请求。
在上面描述的流程中,请求队列位于非安全内存中,如果:
TEE 必须假设从非安全状态提供的任何请求或数据可能是恶意的,或者以其他方式无效。**这意味着需要在安全状态下对请求或请求者进行身份验证。**这取决于所提供的可信服务及其安全要求,没有一个统一的答案。
在 TrustZone 系统中,有两套软件栈,一个用于非安全状态,另一个用于安全状态。处理器核一次只能处于一种状态,那么谁去决定何时运行哪个世界呢?
显然是调用EL3固件,比如使用电源状态协调接口(PSCI)的电源管理请求,这些调用通常是阻塞的。这意味着只有当请求操作完成时,控制权才会返回到非安全状态。然而,这些调用往往比较短并且不频繁。
TEE 通常在非安全状态操作系统调度器的控制下运行。一个可能的设计是在操作系统下运行一个守护进程,用于承载 TEE。当其被操作系统调度时,守护进程通过 SMC 将控制权交给 TEE。然后 TEE 开始运行,处理未完成的请求,直到下一次滴答调度或者中断。最后控制权交回给非安全状态操作系统。
这可能看起来很奇怪,因为不受信任的软件能够控制可信软件何时执行,这可能会导致拒绝服务攻击。但是由于 TEE 是向未非安全状态提供服务,阻止 TEE 运行只会阻止这些服务可用。例如攻击者可能会阻止用户播放受保护的DRM视频,这种攻击并不会导致泄露任何信息。这种类型的设计可以保证机密性,但不能保证可用性。
我们可以设计软件栈来提高可用性。GIC 允许安全中断的优先级高于非安全中断,从而避免非安全状态阻止安全中断的触发。
市面上有许多可信内核,包括商业内核和开源内核。OP-TEE 就是其中一个,最初由 ST‑Ericsson 开发,但现在是由 Linaro 托管的开源项目。OP‑TEE 提供了功能完整的可信执行环境,您可以在 OP‑TEE 项目网站上找到详细说明。
OP-TEE 的结构如下图。
OP‑TEE Kernel 在 S.EL1 中运行,在 S.EL0 中托管 Trusted Application。Trusted Application 通过 TEE Internal API 与 OP‑TEE Kernel 进行通信。 TEE Kernel API 是 GlobalPlatform 小组开发的标准 API。GlobalPlatform 致力于开发标准 API,这些 API 受到许多不同 TEE 的支持,不仅仅是OP‑TEE。
在非安全状态下,内核空间中有一个底层 OP‑TEE Driver 驱动程序,它负责与 OP‑TEE Kernel 的进行通信。
在非安全用户空间 (EL0) 中,有一个用户空间库, 实现了其它的 GlobalPlatform API。应用程序通过 TEE Client API 访问 Trusted 应用或者服务。在大多数情况下,我们不希望应用程序直接使用 TEE Client API,相反提供一个更上层的服务库。
OP‑TEE 还包括一个称为 tee‑supplicant 的组件。 tee‑supplicant 处理 OP‑TEE 支持的服务,并且在某种程度上需要与 rich OS进行交互,一个例子就是安全存储。
到目前为止,我们忽略了在非安全状态下 hypervisor 可能存在的情况。当 hypervisor 存在时,虚拟机和安全状态之间的大部分通信将通过 hypervisor 进行。
例如,在虚拟化环境中,SMC 用于访问固件功能和可信服务。固件包括像电源管理功能等,hypervisor 通常不希望允许虚拟机直接访问这些功能。
hypervisor 可以捕获来自 EL1 的 SMC,并检查请求是针对固件服务还是针对可信服务。如果请求是针对固件服务,则 hypervisor 可以模拟接口而不是传递调用。hypervisor 也可以将可信服务请求转发到 EL3,如下:
启动是 TrustZone 系统的关键部分。只有当信任启动流程,我们才能信任之后运行的软件组件,这通常被称为信任链。下图是一套简化的信任链流程:
上图运行的第一段代码是 boot ROM。由于不存在更早的启动阶段,因此我们必须默认信任 boot ROM。由于存储在 ROM 中,这段初始启动代码不能再次写入。将初始启动代码保存在芯片上,可以防止被替换,因此我们可以默认地信任它。boot ROM 代码通常较小并且简单,其主要功能是从 flash 中加载和验证第二阶段启动代码。
第二阶段启动代码执行平台相关的系统初始化,例如初始化片外 DRAM 内存控制器(DDR)。这部分代码还负责加载和验证安全和非安全状态的镜像,如加载安全状态TEE 和非安全状态固件如 UEFI 。
之前我们介绍了系统控制处理器(SCP)。SCP是一种微控制器,在当前许多 SoC 中执行底层系统控制。当 SCP 或其它类似的处理器存在时,其也构成信任链的一部分。
在可信启动系统中,每个组件在加载之前都会验证下一个组件,从而形成信任链。现在来看看验证失败时会发生什么。
对于这种情况,没有一个统一答案。这取决于系统的安全需求以及故障发生在启动的哪个阶段。以移动设备中的 SoC 为例,如果验证失败发生在如下阶段:
第二阶段启动镜像
SoC 和处理器的初始化需要在第二阶段启动镜像中完成。如果在此阶段验证失败,我们可能无法确定设备是否可以启动并正常运行。因此,如果在此阶段验证失败,通常是致命的,并且设备无法启动。
TEE
TEE 提供像密钥管理相关的服务。在 TEE 不存在的情况下,设备仍可以运行,只是运行的异常等级有限。因此,我们可以选择不加载 TEE,但仍然允许加载非安全状态的软件。
非安全状态固件或者Rich OS镜像
非安全状态软件已经处于较低的信任等级。我们可以选择允许它启动,但会阻止其访问 TEE 提供的高级功能。例如,启用 TrustZone 的 DRM 可能无法用于非可信的操作系统。
这些只是示例,每个系统都需要根据其安全需求做出自己的决策。
之前我们介绍了可信基础系统架构(TBSA),它是系统设计者的指南。可信板级启动需求 (TBBR) 是针对软件开发人员的一套类似指南。TBBR 提供了如何在启用 TrustZone 的系统中构建可信启动流程。
Trusted Firmware(可信固件) 适用于 Armv8‑A 设备,是安全世界软件的开源参考实现。Trusted Firmware 为 SoC 开发人员和 OEM 提供了符合相关 Arm 规范的参考可信代码,包括 TBBR 和 SMCC。
下图是 Trusted Firmware 的结构:
SMC 调度程序处理传入的 SMC。SMC 调度程序识别哪些 SMC 应由可信固件在 EL3 处理,以及哪些 SMC 应转发至可信执行环境。
可信固件提供了处理 Arm 系统 IP 的代码,例如互连。芯片提供商需要提供处理自定义或第三方 IP 的代码,这包括 SoC 的电源管理。
上面我们介绍了硬件中的 TrustZone 功能,并讨论了使用这些功能的典型软件栈。接下来我们汇总这些知识并查看一些使用示例。
智能手机等移动设备包含大量个人数据。如果设备丢失或被盗,用户会关心该数据的机密性。这就是为什么大多数最新设备都支持文件系统加密。作为解决方案的一部分,TrustZone 可以用来保护这些数据。
存储在外部 flash 中的数据是加密的。启动时,设备对用户进行身份验证,然后提供密钥来解密文件系统。解密可以由加速器处理,也可以集成到 flash 控制器中。
文件系统的密钥也需要进行机密性保护。如果密钥被泄露,攻击者就可以解密文件系统。
认证后的处理流程如下图所示。
在安全状态下:
通过在安全状态下执行这些操作,TrustZone 保证不会将文件系统密钥暴露给非安全状态软件。这意味着非安全状态中的恶意代码无法获取这些密钥,进行后续攻击。讨论到目前为止,思考以下这个问题:
为什么文件系统密钥存储在片外?
与片外 flash 相比,片上存储器往往容量小且价格昂贵。将文件系统密钥保留在片外可以降低成本,对其进行加密意味着我们可以确保机密性。虽然还是存在恶意软件损坏密钥的风险,导致完整性被破坏,但不会暴露数据。
为什么上面示例中使用单独的文件系统密钥,而不是请求者设备唯一密钥?
理论上,我们可以使用设备唯一密钥。但这意味着我们永远无法更改密钥,因为请求者设备唯一的私钥存储在 OTP 中。例如,如果我们出售手机,这可能会成为一个问题。相反,我们生成一个新的随机文件系统密钥。如果您想格式化或重置设备,我们可以删除文件系统密钥并生成一把新的密钥。那么使用旧密钥加密的任何数据就无法被获取。
第二个示例就是更新启动固件,系统需求如下:
为了实现这些目标,OEM 使用其私钥对镜像进行签名。下载设备拥有公钥,可用于验证签名。当固件更新时,非易失性计数器会递增,可以检测版本回滚。
系统如下图所示。
镜像下载是在非安全状态下进行的。镜像本身不需要保密,因此我们不保护其机密性。下载的镜像放在内存中,并向安全状态发出安装请求。
安全状态软件负责身份验证。它使用 OEM 的公钥来完成此操作,该公钥通常存储在片外 flash 中。这个密钥是公开的,所以我们不需要保证机密性,但我们需要保证密钥的真实性,并检测密钥是否被替换。通过在芯片上保存密钥的哈希值来实现这一点,该哈希值用于校验密钥。由于片上存储器比较昂贵,哈希值通常占用字节较少,因此比较适合。
当公钥加载校验后时,其可以检查新的固件镜像。我们希望新的固件是真实的(签名值匹配),并且它比已安装运行的固件版本新。
假设这些检查通过,镜像也已安装,并且 NV 计数器也进行递增。递增 NV 计数器意味着,如果攻击者尝试安装较旧的固件,设备将能识别到这种攻击尝试。
在此示例中,TrustZone 保证用于验证固件镜像的密钥受到保护,并且固件镜像无法回滚。
Arm架构中安全状态和物理地址空间是什么?
Arm架构中的安全状态有安全状态和非安全状态。 Arm架构中的物理地址空间分为安全物理地址空间和非安全物理地址空间。
对于每个异常等级,什么决定了处理器处于安全状态还是非安全状态?
对于 EL0/1/2, 是SCR_EL3.NS
位。而 EL3 始终处于安全状态。
在非安全状态下,软件可以访问安全物理地址空间吗?
不能。在非安全状态下,虚拟地址始终映射到非安全物理地址。
对 SP:0x80000 的访问能否命中 NP:0x80000 的缓存行?
不会。SP:0x80000 和NP:0x80000 是不同的位置,因此不存在缓存命中。
可信基础系统架构 (TBSA) 和可信板级启动要求 (TBBR) 提供哪些指导?
TBBR 提供启动指导,TBSA 提供系统架构指导。
TrustZone 地址空间控制器 (TZASC) 的用途是什么?
TZASC 允许将内存划分为安全区域和非安全区域。
以下是与本文相关的一些资源:
其它一些相关的资源如下:
OP-TEE
SMC异常
以下规范描述了如何使用 SMC 来请求服务:
Trusted Board Boot Requirements
Trusted Firmware
本指南介绍了 TrustZone 安全架构,它提供两个世界或执行环境之间的隔离。Arm 架构还具有在指定环境中提供强大安全性的功能。要了解更多信息,请阅读安全指南—Providing protection for complex software。
在本指南中,我们提到了虚拟化和 GIC 主题,但没有充分探讨它们。
要了解有关这些主题的更多信息,请阅读以下指南: