4、安全需求分解:系统级 vs 子系统级,树形分解与分配矩阵

各位好,欢迎来到第四章。前面我们聊了怎么从业务场景里提炼出安全目标,但目标定得再漂亮,落不到硬件设计里也是白搭。这一章,我们就来解决一个核心问题:怎么把抽象的安全目标,一步步拆成硬件工程师能看懂、能实现的具体需求。

我个人习惯把安全需求分解比作「切蛋糕」。你手里有一整块蛋糕(系统级安全需求),得切成小块(子系统级需求),每块还得指定谁负责吃(需求分配矩阵)。切得不好,有人噎着,有人没吃着,最后流片回来才发现安全漏洞,那可就晚了。

4.1 系统级 vs 子系统级:到底差在哪?

先看个例子。假设我们有个安全目标:「防止攻击者通过JTAG接口读取芯片内部固件。」

这个目标本身是系统级的——它描述的是「整个芯片对外表现出的安全属性」。但JTAG接口涉及IO pad、调试控制器、总线矩阵、存储控制器等多个模块。你不可能只改一个地方就搞定。

所以,系统级安全需求的特点是:

  • 面向外部行为:描述「芯片应该做什么/不做什么」
  • 跨模块:通常需要多个硬件单元协同完成
  • 可验证:能在系统级测试中直接验证(比如上电后尝试JTAG读操作,看是否被拦截)

而子系统级安全需求呢?它更具体,更贴近RTL代码。比如:

  • 「JTAG控制器在安全模式下的TDO输出必须恒为0」
  • 「调试接口的状态机在收到非法序列后,必须在3个时钟周期内进入LOCK状态」
  • 「总线矩阵必须拒绝来自非安全域的JTAG读请求」

说白了,系统级需求是「what」,子系统级需求是「how」。我在一个IoT芯片项目里就吃过亏——系统级需求写得很漂亮,但分解到子系统时漏掉了「复位后默认状态」这个细节,结果攻击者利用复位瞬间的窗口期绕过了JTAG锁定。嗯,从那以后我每次做分解都会专门检查「默认状态」和「状态转换」。

维度 系统级安全需求 子系统级安全需求
抽象层级 芯片/SoC整体行为 模块/IP内部行为
描述对象 外部接口、安全属性 内部信号、状态机、寄存器
验证方式 系统级测试、渗透测试 模块级仿真、形式化验证
责任方 系统架构师、安全架构师 设计工程师、验证工程师
变更影响 大,可能影响多个模块 小,通常局限在单个模块

4.2 树形分解:把大目标拆成小零件

怎么拆?我推荐用树形分解法。这玩意儿不复杂,就是画一棵树:根节点是系统级安全需求,往下分到子系统,再往下分到具体的安全机制,最后到硬件实现要求。

举个例子,还是JTAG安全。树形分解大概长这样:

根节点(系统级):防止通过JTAG读取固件

├── 子系统1:JTAG控制器

│ ├── 安全机制1.1:认证握手

│ │ └── 硬件要求:支持128位挑战-响应协议,响应时间<1ms

│ ├── 安全机制1.2:状态锁定

│ │ └── 硬件要求:连续3次认证失败后锁定,仅POR可复位

│ └── 安全机制1.3:数据掩码

│ └── 硬件要求:TDO在非授权模式下输出固定值0x00

├── 子系统2:总线矩阵

│ ├── 安全机制2.1:访问权限过滤

│ │ └── 硬件要求:非安全域请求访问安全地址空间时返回错误

│ └── 安全机制2.2:事务追踪

│ └── 硬件要求:记录所有被拒绝的访问请求(时间戳+源ID)

└── 子系统3:存储控制器

└── 安全机制3.1:读保护

└── 硬件要求:安全区域读操作必须携带安全标签,否则返回全F

你想想看,这样一层层拆下来,每个硬件工程师拿到手的都是「可执行」的任务。而且,树形结构天然支持追溯——哪个子系统没实现好,顺着树往上查,就知道影响了哪个系统级需求。

我的小技巧:树形分解时,每个叶子节点(硬件要求)必须满足SMART原则——具体(Specific)、可衡量(Measurable)、可达成(Achievable)、相关(Relevant)、有时限(Time-bound)。特别是「可衡量」,比如「响应时间<1ms」就比「快速响应」好得多。

下面这张SVG图,展示了从系统级安全目标到硬件实现要求的完整树形分解路径:

安全需求树形分解示例:JTAG固件保护 系统级:防止通过JTAG读取固件 子系统1:JTAG控制器 子系统2:总线矩阵 子系统3:存储控制器 认证握手 状态锁定 数据掩码 访问权限过滤 事务追踪 读保护 128位挑战-响应,<1ms 3次失败锁定,仅POR复位 非授权TDO输出固定0x00 非安全域访问返回错误 记录拒绝访问(时间戳+源ID) 安全区域读操作需安全标签 系统级 子系统 安全机制 硬件要求

4.3 需求分配矩阵:谁负责什么?

树形分解把需求拆开了,但还没「分出去」。这时候就需要需求分配矩阵(Requirements Allocation Matrix, RAM)。说白了,就是一个表格,横轴是子系统/模块,纵轴是安全需求,交叉点打勾表示「这个模块负责实现这个需求」。

我曾经在一个项目里看到过这样的场景:系统级需求「防止侧信道攻击」写得很清楚,但分解到矩阵时,每个模块都觉得自己只负责一小块,结果没有模块对整体抗侧信道能力负责。最后流片回来,功耗分析攻击一打就穿。所以,矩阵里一定要有「整体责任」这一列,通常由系统架构师或安全架构师担任。

下面是一个简化的需求分配矩阵示例:

安全需求(系统级) JTAG控制器 总线矩阵 存储控制器 安全处理器 整体责任
防止JTAG读取固件 ✓ ✓ ✓ 安全架构师
防止调试接口绕过 ✓ ✓ 调试子系统负责人
安全启动完整性校验 ✓ ✓ 启动子系统负责人
内存隔离(安全/非安全域) ✓ ✓ 系统架构师
抗功耗分析攻击 ✓ 安全架构师

避坑指南:我曾经见过一个矩阵,所有需求都只分配给了一个模块。这很危险——如果那个模块的设计师离职了,或者模块本身有bug,整个安全防线就垮了。我建议每个关键需求至少分配两个模块,形成纵深防御。比如JTAG保护,JTAG控制器做第一道防线,总线矩阵做第二道,存储控制器做第三道。

4.4 分解过程中的常见陷阱

做了这么多年安全架构,我总结了几条常见的坑:

  1. 分解粒度太粗:系统级需求直接扔给设计工程师,没有拆到可执行粒度。结果设计师凭自己的理解去实现,跟架构师想的完全不一样。
  2. 遗漏边界条件:只分解了正常操作场景,忽略了复位、低功耗模式、测试模式等边界状态。攻击者最喜欢钻这种空子。
  3. 忽略交互影响:两个子系统各自实现了安全机制,但组合起来反而产生了新漏洞。比如JTAG锁定和总线矩阵的过滤策略冲突,导致锁定后还能通过特定序列绕过。
  4. 没有可验证性:分解出来的需求没法测试。比如「确保JTAG接口安全」——什么叫安全?怎么测?必须写成「在1000次随机攻击尝试中,成功读取固件的次数为0」这种可验证的表述。

我的习惯:每次做完树形分解和分配矩阵,我都会组织一次「安全需求评审会」。把系统架构师、设计工程师、验证工程师叫到一起,对着树和矩阵过一遍。重点检查:每个叶子节点是否可执行?每个交叉点是否有人认领?有没有遗漏的场景?这个会通常能发现30%以上的问题,比后期改RTL划算得多。