从 PFR 到 DICE:安全启动链的工业级实现与 TPM2 远程证明体系深度实战

从 PFR 到 DICE:安全启动链的工业级实现与 TPM2 远程证明体系深度实战

引言:当软件安全的尽头是硬件信任根

现代计算安全的演进有一条清晰的主线——信任不断下沉。从应用层的 TLS/HTTPS,到操作系统层的 Secure Boot/DM-Verity,再到可信执行环境(TEE)的 Intel SGX/AMD SEV-SNP/ARM TrustZone,每一层的安全都依赖于下层提供的信任根基。

而这座信任大厦的最底层,是硬件信任根(Hardware Root of Trust, HRoT)。没有可靠的硬件信任根,上层所有的加密验证、远程证明、可信执行都成了空中楼阁。

然而,构建一套工业级的安全启动链远比"在 UEFI 里签个名"复杂。它需要解决一系列关键问题:

  • 初始信任如何注入? 如何在制造阶段建立不可篡改的设备身份?
  • 固件如何防降级攻击? 如何防止攻击者刷回带有已知漏洞的旧版本?
  • 多方供应链如何可信传递? OEM、ODM、芯片厂商的信任如何串联?
  • 运行时如何持续验证? 启动后的系统状态如何向远端证明?
  • 密钥如何安全轮换? 平台密钥泄露后如何恢复信任链?
  • 本文将从三个核心技术的深度工程实践出发,解析现代安全启动链的完整实现:PFR(Platform Firmware Resiliency,平台固件弹性)、DICE(Device Identifier Composition Engine,设备标识符组合引擎) 和 TPM2(Trusted Platform Module 2.0,可信平台模块)远程证明体系。我们将结合 TCG 规范、Linux 内核驱动代码和实际工程案例,呈现一条从硅片到云端的完整信任链。

    一、DICE:设备身份的密码学基石

    1.1 DICE 的核心思想

    DICE(Device Identifier Composition Engine)是 TCG(Trusted Computing Group)定义的一种标准化机制,用于在设备启动的最早阶段(通常为 ROM Bootloader)建立密码学身份。它的核心设计哲学可以用一句话概括:每一层软件的度量(Measurement)决定了下一层的唯一密钥。

    这种设计带来一个关键特性:如果固件被篡改,设备将自动推导出不同的密钥,从而无法向服务端证明自己的合法性。不需要预先烧录唯一密钥,不需要在 ROM 中存储秘密——"身份"是从代码本身涌现出来的。

    1.2 密码学推导链

    DICE 的核心操作是 Compound Device Identifier(CDI) 的推导。整个流程如下:

    ┌──────────────────────────────────────────────────┐
    │  阶段 0:UDS (Unique Device Secret)               │
    │  ─ 硬件熔丝/PUF 提供的唯一秘密,只在 DICE 层可见  │
    │  ─ 长度:256 bit (AES-256)                       │
    └─────────────────┬────────────────────────────────┘
                      │
                      ▼ HMAC-SHA256(UDS, Stage0_Firmware_Hash)
    ┌──────────────────────────────────────────────────┐
    │  阶段 1:CDI_0 (Compound Device Identifier 0)     │
    │  ─ Stage 0 的"复合设备标识符"                     │
    │  ─ 由 UDS 和 Stage 0 固件哈希推导而来             │
    │  ─ Stage 0 固件被篡改 → CDI_0 完全不同            │
    └─────────────────┬────────────────────────────────┘
                      │
                      ▼ HMAC-SHA256(CDI_0, Stage1_Firmware_Hash)
    ┌──────────────────────────────────────────────────┐
    │  阶段 2:CDI_1 (Compound Device Identifier 1)     │
    │  ─ Stage 1 的复合设备标识符                      │
    │  ─ 绑定了 Stage 0 和 Stage 1 的双重度量           │
    └─────────────────┬────────────────────────────────┘
                      │
                      ▼ ...继续传递直到 OS 内核
    ┌──────────────────────────────────────────────────┐
    │  阶段 N:CDI_N(操作系统的复合身份)              │
    │  ─ 完整启动链的密码学摘要                        │
    └──────────────────────────────────────────────────┘

    1.3 DICE 密钥推导的代码实现

    以下是一个简化的 DICE 密钥推导实现,展示了 CDI 的计算逻辑:

    /* DICE CDI derivation - simplified reference implementation */
    #include <openssl/hmac.h>
    #include <stdint.h>
    #include <string.h>
    
    #define DICE_DIGEST_SIZE 32   /* SHA-256 */
    #define DICE_MAX_COMPONENTS  16
    
    struct dice_config {
        uint8_t uds[DICE_DIGEST_SIZE];          /* 硬件唯一密钥 */
        uint8_t cdi[DICE_DIGEST_SIZE];           /* 当前 CDI */
        uint8_t cdi_private_key[32];            /* Ed25519/X25519 私钥种子 */
        int     stage_index;
    };
    
    /* 核心:用上一层的 CDI 推导下一层 */
    int dice_derive_next_cdi(struct dice_config *ctx,
                             const uint8_t *next_stage_hash, 
                             size_t hash_len)
    {
        unsigned char new_cdi[DICE_DIGEST_SIZE];
        unsigned int  new_cdi_len = DICE_DIGEST_SIZE;
        
        /* CDI_{n+1} = HMAC-SHA256(Key=CDI_n, Data=Hash(Firmware_{n+1})) */
        if (!HMAC(EVP_sha256(), ctx->cdi, DICE_DIGEST_SIZE,
                  next_stage_hash, hash_len,
                  new_cdi, &new_cdi_len)) {
            return -1;
        }
        
        /* 安全擦除旧 CDI——防止后续代码读取到以前的秘密 */
        explicit_bzero(ctx->cdi, DICE_DIGEST_SIZE);
        memcpy(ctx->cdi, new_cdi, DICE_DIGEST_SIZE);
        
        /* 同理擦除 HMAC 中间结果 */
        explicit_bzero(new_cdi, DICE_DIGEST_SIZE);
        
        ctx->stage_index++;
        return 0;
    }
    
    /* 从 CDI 派生设备密钥对(X25519 用于 ECDH 密钥交换) */
    int dice_derive_device_keypair(struct dice_config *ctx,
                                   uint8_t *public_key,
                                   uint8_t *private_key)
    {
        /*
         * 使用 HKDF 从 CDI 派生确定性密钥:
         *   PrivateKey = HKDF-Expand(HKDF-Extract(CDI, "DICE-KEY-V1"), 32)
         *   PublicKey = X25519(PrivateKey, basepoint)
         * 
         * 确定性派生的好处:只要固件不变,设备重启后推导出相同的密钥对
         */
        uint8_t seed[32];
        /* HKDF-Extract */
        HMAC(EVP_sha256(), ctx->cdi, DICE_DIGEST_SIZE,
             (const uint8_t *)"DICE-KEY-V1", 11,
             seed, NULL);
        
        /* HKDF-Expand 得到 X25519 私钥 */
        uint8_t okm[64]; /* X25519 需要扩展 */
        HMAC(EVP_sha256(), seed, 32,
             (const uint8_t *)"\x01" /* info = version byte */, 1,
             okm, NULL);
        
        memcpy(private_key, okm, 32);
        /* X25519 标量乘法生成公钥 */
        x25519_public_key(private_key, public_key);
        
        explicit_bzero(seed, 32);
        explicit_bzero(okm, 64);
        return 0;
    }

    1.4 DICE 的硬件实现要求

    DICE 的前提是有一个硬件秘密 UDS。工业级的 UDS 来源有三种:

    UDS 来源实现方式安全等级适用场景
    eFuse 编程制造时烧入 256-bit 随机数中(可能被物理攻击读取)IoT、消费级 MCU
    SRAM PUF利用 SRAM 上电随机态作为指纹高(电阻式,不可克隆)汽车 ECU、工业控制器
    专用安全芯片独立 IC(如 OptiTrust、MAX32520)最高(防侧信道、防篡改)服务器 BMS、金融终端

    关键安全约束:

  • UDS 只能在 DICE 硬件模块内部使用,绝不能通过任何寄存器、DMA 或调试接口泄漏。
  • ROM Bootloader 执行完毕后,必须通过硬件熔丝或 BIT 位永久锁定 UDS 访问通路。
  • 每个芯片的 UDS 必须唯一,制造过程中需验证随机性(NIST SP 800-90B 标准的 entropy assessment)。
  • 二、PFR:固件韧性的 NIST 工程框架

    2.1 从 NIST SP 1800-34 说起

    NIST(美国国家标准与技术研究院)发布的 SP 1800-34D 文档定义了平台固件弹性(Platform Firmware Resiliency, PFR)的最佳实践。其核心目标有三:

  • 保护(Protect):确保平台固件不被未经授权的修改所破坏。
  • 检测(Detect):实时监控固件镜像的完整性,发现篡改行为。
  • 恢复(Recover):在检测到攻击时,自动将固件恢复至已知安全状态。
  • PFR 为 DICE 提供了运行时的"免疫系统"——它不仅启动时验证,还在运行期间持续监控。

    2.2 PFR 的信任链架构

    典型的 PFR 架构以可信平台控制器(Platform Controller Hub, PCH/BMC)为信任锚点,监控主 CPU 的 SPI Flash:

    ┌─────────────────────────────────────────────────────┐
    │                   服务器主板                          │
    │                                                      │
    │  ┌──────────┐   SPI Bus   ┌──────────────────────┐  │
    │  │  BMC/PCH │◄──────────►│  主 SPI Flash         │  │
    │  │  (信任锚) │             │  (BIOS/UEFI 固件)     │  │
    │  └─────┬────┘             └──────────┬───────────┘  │
    │        │                              │              │
    │        │ 实时监控                      │              │
    │        │ (I2C/SPI/I3C)                │              │
    │  ┌─────▼────┐                  ┌──────▼───────────┐  │
    │  │ 备份 Flash │                  │  主 CPU           │  │
    │  │ (Recovery)│                  │  (运行 UEFI)       │  │
    │  └──────────┘                  └──────────────────┘  │
    │                                                      │
    │  关键特性:                                           │
    │  1. 双 Flash 镜像(Active + Recovery)                │
    │  2. 硬件级 SPI 总线监控(非软件检测)                  │
    │  3. 看门狗定时器 + 自动回滚                           │
    └─────────────────────────────────────────────────────┘

    2.3 PFR 的硬件监控机制

    PFR 的核心创新在于将固件保护下沉到硬件控制器(通常是一个专用的 CPLD 或集成在 BMC 中的模块)。该控制器:

  • 实时嗅探 SPI 总线上的读写操作,与白名单比对。
  • 维护固件镜像的 Golden Hash(黄金哈希值),通常存储在一次性可编程区域。
  • 通过 I2C/GPIO 总线连接TPM2 芯片,用于记录度量日志和远程证明。
  • /* PFR 固件监控的硬件状态机 */
    enum pfr_state {
        PFR_STATE_INIT,          /* 上电初始化 */
        PFR_STATE_MONITOR,       /* 正常运行监控 */
        PFR_STATE_DETECTED,      /* 检测到篡改 */
        PFR_STATE_RECOVERY,      /* 执行恢复 */
        PFR_STATE_LOCKDOWN       /* 锁定(需人工干预) */
    };
    
    struct pfr_context {
        enum pfr_state state;
        uint32_t       active_region_base;
        uint32_t       recovery_region_base;
        uint8_t        golden_hash[32];     /* SHA-256 of trusted firmware */
        uint32_t       watchdog_period_ms;
        bool           auto_recovery_enabled;
    };
    
    /* 硬件中断处理:检测到未授权 SPI 写入时触发 */
    void pfr_spi_violation_isr(struct pfr_context *ctx, 
                                uint32_t target_addr,
                                uint32_t len)
    {
        /* 步骤1:记录攻击日志到 BMC 的 EEPROM */
        pfr_log_attack(ctx, target_addr, len, PERR_TYPE_SPI_WRITE);
        
        /* 步骤2:如果攻击目标落在受保护区域 */
        if (pfr_is_protected_region(ctx, target_addr)) {
            ctx->state = PFR_STATE_DETECTED;
            
            /* 步骤3:触发自动恢复 */
            if (ctx->auto_recovery_enabled) {
                /* 硬件直接将 SPI Flash 切换至 Recovery 镜像 */
                pfr_switch_to_recovery(ctx);
                
                /* 步骤4:通知 BMC 发起系统复位 */
                bmc_request_reset("PFR: Firmware recovery initiated");
                
                ctx->state = PFR_STATE_RECOVERY;
            } else {
                /* 如果自动恢复被禁用,锁定平台 */
                ctx->state = PFR_STATE_LOCKDOWN;
                pfr_assert_platform_lock(ctx);
            }
        }
    }

    2.4 PFR 的回滚保护(Anti-Rollback)

    防降级攻击是 PFR 的另一大核心功能。攻击者可能利用旧版本固件中已知的漏洞(如 Logo Fail、BootHole 等),将固件回滚到易受攻击的版本。

    PFR 通过以下机制防护:

  • 单调计数器(Monotonic Counter):存储在 BMC 的安全 EEPROM 或 TPM2 的 NV 索引中,每次固件升级时递增。
  • 版本绑定签名:新固件的元数据中包含最小允许版本号,低于此版本的签名不被接受。
  • TPM2 NV 索引锁定:达到预设定数的版本后,通过 TPMA_NV_WRITELOCKED 锁定,无法回滚。
  • # 使用 TPM2 单调计数器实现防回滚
    # 1. 在 TPM2 上创建计数器
    tpm2_nvdefine -C o -s 8 -a "authwrite|authread|platformcreate" \
      -t "counter|no_da" 0x01C10100
    
    # 2. 读取当前计数器值(当前固件版本号)
    tpm2_nvread -C o -s 8 0x01C10100 | xxd -p
    # 输出: 0000000000000014  (版本 20)
    
    # 3. 升级固件时递增计数器
    tpm2_nvincrement -C o 0x01C10100
    
    # 4. 启动时验证:固件声明的版本 >= TPM2 计数器值
    # 如果固件版本 < 计数器值 → 回滚攻击,拒绝启动

    三、TPM2 远程证明体系

    3.1 远程证明的核心流程

    TPM2(Trusted Platform Module 2.0)是独立的安全芯片,提供密钥生成、加密操作、平台配置寄存器(PCR)和远程证明能力。远程证明的目标是:让远端验证服务确信本地平台运行的是未被篡改的固件和软件。

    完整的远程证明流程:

    ┌──────────┐                              ┌──────────────────┐
    │ 设备/节点 │                              │  远程验证服务      │
    │ (Attestor)│                              │  (Verifier/AAA)   │
    └─────┬────┘                              └────────┬─────────┘
          │                                             │
          │ 1. Verify 发送 Nonce + 公钥引用             │
          │◄──────────────────────────────────────────── │
          │                                             │
          │ 2. Attestor 执行 TPM2_Quote:               │
          │    - 获取 PCR[0..7] 的当前度量值            │
          │    - 用 AIK (Attestation Identity Key) 签名 │
          │    - 附带 Event Log (TPM2 event log)       │
          │────────────────────────────────────────────►│
          │    Quote = { PCR_values, Nonce, Signature }  │
          │                                             │
          │ 3. Verify 验证流程:                        │
    │                                             │
    │    (a) 验证 AIK 签名 (证明 Quote 来自真实 TPM)  │
    │    (b) 验证 Nonce 匹配 (新鲜性,防重放)         │
    │    (c) 重新计算 PCR 复合哈希                   │
    │    (d) 与 Event Log 中的预期度量值比对         │
    │    (e) 判断:系统状态是否可信?               │
    │                                             │
          │◄──────────────────────────────────────────── │
          │ 4. 返回验证结果 + 访问令牌/解密密钥         │
          │                                             │

    3.2 PCR 度量链详解

    TPM2 的 PCR(Platform Configuration Register)是整个度量体系的基石。不同的 PCR 索引度量启动链的不同阶段:

    PCR 索引度量阶段度量内容典型值来源
    PCR[0]CRTM/BIOSUEFI 固件主体代码SEC + PEI 阶段度量
    PCR[1]UEFI 配置UEFI 变量、启动选项UEFI 设置界面配置
    PCR[2]Option ROM外插卡固件GPU/RAID/NIC 的 UEFI Driver
    PCR[3]Option ROM 配置外插卡配置Option ROM 的配置项
    PCR[4]Boot LoaderGRUB/Shim 引导程序MOK 列表、grub.cfg
    PCR[5]Boot Loader 配置内核命令行参数/etc/default/grub
    PCR[6]Resume from S3休眠恢复状态S3 恢复固件度量
    PCR[7]Secure Boot 状态安全启动策略PK/KEK/db/dbx 数据库
    PCR[8-15]OS 内核空间内核、initramfs、模块IMA (Integrity Measurement Architecture)
    # 读取 TPM2 PCR 度量值(Linux 内核暴露的接口)
    cat /sys/class/tpm/tpm0/pcr-sha256/0
    # 输出: 0000000000000000000000000000000000000000000000000000000000000000
    # (如果 BIOS 未被度量,全零;正常启动后应呈现固件哈希)
    
    # 读取完整 PCR 寄存器集
    for i in $(seq 0 23); do
      echo -n "PCR[$i]: "
      cat /sys/class/tpm/tpm0/pcr-sha256/$i 2>/dev/null || echo "N/A"
    done
    
    # 使用 tpm2-tools 读取更详细的信息
    tpm2_pcrread sha256:0,1,2,3,4,5,6,7

    3.3 内核层的 IMA 度量

    Linux 的 IMA(Integrity Measurement Architecture)子系统将 TPM2 的度量能力延伸到运行时。它通过在文件被读取或执行时计算哈希值,并扩展到 PCR[10] 中,实现了运行时的持续完整性验证。

    # 启用 IMA 的内核命令行参数
    ima_policy=tcb ima_hash=sha256 ima_template=ima-ng
    
    # 查看 IMA 的运行时度量日志
    cat /sys/kernel/security/ima/ascii_runtime_measurements
    
    # 输出示例:
    # 10 5c5ec2e7... ima-ng sha256:7d9e3f1a... /usr/bin/nginx
    # 10 3a8f2b1c... ima-ng sha256:a1b2c3d4... /usr/lib/libssl.so.3
    # 10 9e7d6a5b... ima-ng sha256:e5f6a7b8... /etc/nginx/nginx.conf

    IMA 策略可以配置为多种模式:

    # ima_policy 的可用选项:
    #  tcb          - 度量所有运行的文件(最严格)
    #  appraise     - 拒绝不符合预期哈希的文件访问
    #  dont_log     - 关闭度量(仅用于调试)
    
    # 设置 IMA appraisal 规则(基于数字签名验证)
    echo "appraise fowner=0 func=FILE_CHECK mask=MAY_READ ima_sig" > \
      /sys/kernel/security/ima/policy

    3.4 远程证明的工程化实现

    以下是一个使用 TPM2 进行远程证明的简化 Python 实现,展示了完整的证明-验证流程:

    #!/usr/bin/env python3
    """
    基于 TPM2 的远程证明客户端实现
    依赖: tpm2-pytss, cryptography
    """
    
    import json
    import hashlib
    from tpm2_pytss import *
    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
    from cryptography.exceptions import InvalidSignature
    
    class TPMRemoteAttestor:
        def __init__(self, tpm_device="/dev/tpmrm0"):
            self.ectx = ESAPI(tpm_device)
            self._load_aik()
        
        def _load_aik(self):
            """加载 AIK (Attestation Identity Key)"""
            # AIK 是 TPM2 内部的持久化密钥句柄 
            # 典型值: 0x81010001 (配置在 TPM2 制造时写入)
            self.aik_handle = TPM2_RH.FROM_TPM(0x81010001)
        
        def quote(self, nonce: bytes, pcr_selection: list) -> dict:
            """
            执行 TPM2_Quote 操作
            
            Args:
                nonce: 验证方提供的随机数(通常 32 字节)
                pcr_selection: 需要引用的 PCR 索引列表,如 [0,1,2,7]
            
            Returns:
                Quote 结果对象,包含 PCR 值、签名和元数据
            """
            assert len(nonce) >= 16 and len(nonce) <= 64, \
                "Nonce must be 16-64 bytes"
            
            # 选择要引用的 PCR
            pcr_sess = TPML_PCSFParse(
                pcr_selection=[
                    TPMS_PcrSelection(
                        hash=TPM2_ALG.SHA256,
                        sizeofSelect=3,
                        pcrSelect=pcr_selection
                    )
                ]
            )
            
            # 执行 TPM2_Quote
            quote, signature = self.ectx.quote(
                self.aik_handle,
                pcr_sess,
                nonce
            )
            
            return {
                "quote": quote,          # 包含 PCR digest
                "signature": signature,  # AIK 签名
                "pcr_selection": pcr_selection,
                "nonce": nonce.hex()
            }
        
        def get_event_log(self) -> dict:
            """获取 TPM2 Event Log(包含所有历史度量记录)"""
            # Event Log 在 /sys/kernel/security/tpm0/binary_bios_measurements
            with open("/sys/kernel/security/tpm0/binary_bios_measurements", "rb") as f:
                return parse_tpm2_event_log(f.read())
    
    
    class RemoteVerifier:
        """远程验证服务(Verifier)"""
        
        def __init__(self, trusted_pcr_values: dict, aik_public_key: bytes):
            """
            Args:
                trusted_pcr_values: 预存的可信 PCR 值数据库
                    格式: {"firmware_v2.1": {"pcr0": "abc...", "pcr1": "def..."}, ...}
                aik_public_key: 经过 EK 证书链验证的 AIK 公钥
            """
            self.trusted_pcrs = trusted_pcr_values
            self.aik_pubkey = Ed25519PublicKey.from_public_bytes(aik_public_key)
        
        def verify_quote(self, attestation_response: dict) -> bool:
            """验证远程证明响应"""
            
            # 步骤1: 验证签名 (证明 Quote 来自真实 TPM)
            quote_data = attestation_response["quote"]
            signature = attestation_response["signature"]
            
            try:
                self.aik_pubkey.verify(signature, quote_data)
            except InvalidSignature:
                raise SecurityException("AIK 签名无效:可能来自伪造 TPM")
            
            # 步骤2: 验证 Nonce 新鲜性
            expected_nonce = self.get_pending_nonce(attestation_response["device_id"])
            if attestation_response["nonce"] != expected_nonce.hex():
                raise SecurityException("Nonce 不匹配:可能为重放攻击")
            
            # 步骤3: 验证 PCR 值是否在可信集合中
            observed_pcr_digest = extract_pcr_digest(quote_data)
            trusted = False
            
            for fw_version, expected_pcrs in self.trusted_pcrs.items():
                if pcr_digest_matches(observed_pcr_digest, expected_pcrs):
                    trusted = True
                    print(f"[+] PCR 匹配可信固件版本: {fw_version}")
                    break
            
            if not trusted:
                raise SecurityException(
                    f"PCR 值不在可信集合中,设备可能被篡改。"
                    f"Observed digest: {observed_pcr_digest.hex()[:32]}..."
                )
            
            return True
    
    
    # ============ 完整使用示例 ============
    
    def perform_attestation():
        """执行完整的远程证明流程"""
        
        # 设备侧
        attester = TPMRemoteAttestor("/dev/tpmrm0")
        
        # 接收来自验证方的 nonce(通常通过 TLS 通道)
        nonce = os.urandom(32)  # 实际从 verifier 获取
        
        # 执行 Quote(引用 PCR 0-7,覆盖完整启动链)
        quote_result = attester.quote(
            nonce=nonce,
            pcr_selection=[0, 1, 2, 3, 4, 5, 6, 7]
        )
        
        # 获取 Event Log 作为补充证据
        event_log = attester.get_event_log()
        
        # 打包发送到验证方
        attestation_packet = {
            "device_id": "server-node-prod-42",
            "quote": base64.b64encode(quote_result["quote"]).decode(),
            "signature": base64.b64encode(quote_result["signature"]).decode(),
            "event_log": event_log,
            "nonce": quote_result["nonce"],
            "timestamp": int(time.time())
        }
        
        # 发送到验证服务(通过 TLS/mTLS 通道)
        response = requests.post(
            "https://verifier.ybb.press/api/attest",
            json=attestation_packet,
            cert=("/certs/client.crt", "/certs/client.key"),
            verify="/certs/ca.crt"
        )
        
        if response.json().get("trusted"):
            # 获取解封的密钥(用于解密磁盘/数据)
            decryption_key = response.json()["unsealed_key"]
            return decryption_key
        else:
            raise SecurityException("远程证明失败,访问被拒绝")

    四、DICE + PFR + TPM2 三位一体:工业级工程实现

    4.1 三种技术的协同关系

    在实际的工业级系统中,DICE、PFR 和 TPM2 并非互相替代,而是各司其职、协同工作:

    ┌────────────────────────────────────────────────────────────────┐
    │                三位一体信任链架构                                 │
    ├────────────────────────────────────────────────────────────────┤
    │                                                                 │
    │  ┌──────────┐  ┌──────────────┐  ┌────────────┐               │
    │  │   DICE    │  │     PFR       │  │   TPM2     │               │
    │  │ (身份根基)│  │ (完整性保护)  │  │ (远程证明) │               │
    │  └─────┬────┘  └──────┬───────┘  └──────┬─────┘               │
    │        │               │                  │                     │
    │        ▼               ▼                  ▼                     │
    │  ┌──────────┐  ┌──────────────┐  ┌────────────┐               │
    │  │制造时注入  │  │运行时监控    │  │云端验证    │               │
    │  │UDS + 度量 │  │防篡改+回滚   │  │信任决策    │               │
    │  └──────────┘  └──────────────┘  └────────────┘               │
    │                                                                 │
    │  职责:            职责:              职责:                      │
    │  · 设备唯一身份    · SPI Flash 监控   · PCR 度量日志            │
    │  · 派生密钥链      · 双镜像自动恢复   · AIK 签名证明           │
    │  · 固件绑定身份    · 防版本回滚       · Event Log 审计          │
    │  · 早期信任锚点    · 攻击检测告警     · 密封数据解封            │
    │                                                                 │
    └────────────────────────────────────────────────────────────────┘

    4.2 典型的服务器启动流程

    在一个配备 DICE + PFR + TPM2 的现代服务器中,完整启动链如下:

    1. 电源上电
       │
       ▼
    2. DICE ROM 执行(Stage 0 - 不可变 ROM)
       │  · 从 eFuse 读取 UDS(唯一硬件密钥)
       │  · 计算下一阶段固件(BMC Stage1)的 SHA-256
       │  · CDI_0 = HMAC-SHA256(UDS, Hash(Stage1))
       │  · 通过 SPI 总线发送度量事件到 TPM2 → 扩展 PCR[0]
       │  · 锁定 UDS 访问通路
       │
       ▼
    3. BMC/PCH 固件启动(Stage 1)
       │  · DICE: 用 CDI_0 派生 BMC 固件密钥
       │  · PFR: 开始监控主 SPI Flash(此时主 CPU 被 Hold 在 RESET)
       │  · PFR: 通过 GPIO 通知 TPM2 初始化
       │
       ▼
    4. PFR 验证主 CPU 的 SPI Flash
       │  · 计算 BIOS Flash 的 SHA-256
       │  · 与 Golden Hash 比对
       │  · 若匹配 → 释放主 CPU RESET 信号
       │  · 若不匹配 → 从 Recovery 镜像启动,通知 BMC
       │
       ▼
    5. UEFI BIOS 启动(Stage 2)
       │  · DICE: CDI_1 = HMAC-SHA256(CDI_0, Hash(Stage2))
       │  · PCR[0] = Extend(PCR[0], Hash(BIOS Code))
       │  · PCR[1] = Extend(PCR[1], Hash(UEFI Configuration))
       │  · PCR[7] = Extend(PCR[7], Hash(Secure Boot State))
       │
       ▼
    6. UEFI 度量 Option ROM 和 Boot Loader
       │  · PCR[2] = Extend(PCR[2], Hash(GPU Option ROM))
       │  · PCR[4] = Extend(PCR[4], Hash(shim.efi + GRUB))
       │
       ▼
    7. Linux 内核启动
       │  · PCR[4] = Extend(PCR[4], Hash(vmlinuz))
       │  · PCR[5] = Extend(PCR[5], Hash(kernel cmdline))
       │  · PCR[9] = Extend(PCR[9], Hash(IMA policy))
       │  · 内核从 TPM2 获取 EK 证书链,验证 AIK 身份
       │
       ▼
    8. 运行时 IMA 持续度量
       │  · 每个被执行的 ELF/bin → PCR[10] Extend
       │  · 每个被读取的配置文件 → PCR[10] Extend
       │
       ▼
    9. 远程证明(按需触发)
       │  · 设备向验证方请求证明
       │  · TPM2_Quote(PCR[0-7,10], Nonce) → AIK 签名
       │  · 验证方验证签名 + PCR 值 + Event Log
       │  · 决策:允许访问 / 拒绝访问 / 降级服务
       │
       ▼
    10. 密封数据(Sealing)
          · 将解密密钥绑定到特定 PCR 状态
          · 只有 PCR 值匹配时 TPM2 才解封
          · 典型应用:LUKS 磁盘加密、Kubernetes Secret 解封

    4.3 Linux 内核中的 TPM2 驱动栈

    Linux 内核提供了完整的 TPM2 驱动栈,从硬件接口到用户态 API 一应俱全:

    用户态 API:
        /dev/tpm0          (字符设备,旧接口)
        /dev/trmrm0        (Resource Manager,推荐)
        /sys/class/tpm/    (sysfs 接口)
    
    内核态驱动层:
        ┌─────────────────────────────┐
        │  tpm_crb.c    (ACPI CRB 接口)│
        │  tpm_tis.c    (TIS 1.3 接口) │
        │  tpm_i2c_atmel│
        │  tpm_spi_st33 │
        │  tpm_ftpm_tee │
        └──────────┬──────────────────┘
                   │
        ┌──────────▼──────────────────┐
        │     tpm-chip.c (核心层)      │
        │   · 命令发送 / 接收          │
        │   · 时钟超时管理             │
        │   · TPM 2.0 协议封装        │
        └──────────┬──────────────────┘
                   │
        ┌──────────▼──────────────────┐
        │   tpm2-space.c / tpm2-cmd.c │
        │   · NV 索引读写             │
        │   · 密钥加载 / 操作          │
        │   · PCR Extend / Quote       │
        │   · HMAC / Policy 会话       │
        └──────────┬──────────────────┘
                   │
        ┌──────────▼──────────────────┐
        │   keyring / trusted keys    │
        │   · "trusted" key type      │
        │   · TPM 密封的数据加密密钥   │
        └─────────────────────────────┘

    4.4 密封数据的实战:TPM2 + LUKS 磁盘加密

    将 TPM2 用于磁盘加密(LUKS + Clevis + Tang/Nanos),可以在不存储明文密码的前提下实现自动解密:

    # 安装工具
    apt install clevis clevis-tpm2 clevis-luks tang
    
    # 配置 Tang 服务器(用于网络绑定密封)
    # 客户端首次绑定:
    clevis luks bind -d /dev/sda2 tpm2 '{"pcr_bank":"sha256","pcr_ids":"7"}'
    
    # 验证绑定
    clevis luks list -d /dev/sda2
    # 输出: 1: tpm2 '{"pcr_bank":"sha256","pcr_ids":"7"}'
    
    # 添加到 crypttab(开机自动解密)
    echo "sda2_crypt UUID=xxxx none luks,clevis" >> /etc/crypttab

    这种配置的精妙之处在于:如果攻击者将硬盘拆到另一台机器(PCR[7] 不同),TPM2 将拒绝解密,数据仍然安全。

    五、攻击面与防御深度

    5.1 现实中的硬件攻击向量

    任何安全启动链都不是绝对安全的。以下是一些实际可行的攻击向量及其对应防御:

    攻击类型攻击手段对应防御机制
    固件降级攻击刷回有漏洞的旧版 BIOSPFR 单调计数器 + TPM2 NV 锁定
    Glitch 攻击电压/时钟毛刺跳过安全检查DICE + 硬件毛刺检测电路
    SPI 嗅探逻辑总线分析仪读取固件PFR 实时监控 + SPI 加密
    侧信道分析功耗/电磁辐射恢复密钥TPM2 芯片内置抗侧信道防护
    冷启动攻击冷冻内存读取残留密钥DICE 密钥不驻留内存(仅在 TPM 内)
    恶意 BMC 固件带外管理后门PFR 硬件隔离 + BMC 远程证明
    供应链攻击制造阶段植入后门DICE UDS 多方生成 + 审计日志

    5.2 TPM2 侧信道防护的进阶工程

    对于高安全级别的场景(金融、政务),甚至需要考虑 TPM2 芯片本身的防护:

    /* TPM2 命令的侧信道恒定性验证 */
    /* 确保 cryptography 操作的时间与密钥无关 */
    
    /* 常量时间比较 — 防止 timing attack 猜测 PCR 值 */
    int secure_compare(const uint8_t *a, const uint8_t *b, size_t len) {
        volatile uint8_t result = 0;
        for (size_t i = 0; i < len; i++) {
            result |= a[i] ^ b[i];
        }
        return (result == 0) ? 0 : -1;
    }
    
    /* TPM2 NV 索引设置 - 配置写保护策略 */
    typedef struct {
        uint32_t attributes;
        uint16_t data_size;
        uint8_t  auth_policy[32];  /* 绑定到特定 PCR 状态 */
    } tpm2_nv_config_t;
    
    /* 配置一个"一旦写入,永远锁定"的 NV 索引(类似 eFuse */
    tpm2_nv_config_t write_once_config = {
        .attributes = TPMA_NV_PPWRITE     /* 仅 Platform 级可写 */
                    | TPMA_NV_WRITEALL    /* 必须写入全部数据 */
                    | TPMA_NV_WRITEDEFINE /* 定义后锁定 */
                    | TPMA_NV_POLICYWRITE,
        .data_size = 32,
        /* 策略:必须 PCR[7] = Secure Boot Enabled 状态才可写 */
        .auth_policy = { /* PolicyOR( PCR7=EfiSecureBootModeenabled, ... ) */ }
    };

    六、总结与工程建议

    6.1 技术选型指南

    安全启动链的设计没有 silver bullet,需要根据实际场景选择合适的技术组合:

  • 嵌入式 IoT 设备(成本敏感):DICE + 内部安全元件,远程证明使用 DTLS 证书链
  • 工业控制 / 汽车电子:DICE + 专用安全 MCU(HSM)+ SecOC(Secure Onboard Communication)
  • 数据中心服务器:DICE + PFR (BMC) + TPM2 + IMA + 远程证明服务
  • 高安全场景(金融/政务):上述全部 + 多国认证安全芯片(Common Criteria EAL4+)
  • 6.2 关键工程原则

    在实现安全启动链时,请始终牢记以下原则:

  • 硬件秘密永不离开芯片——UDS 和 CDI 不应出现在系统内存或寄存器中。
  • 度量先于执行——任何代码在运行前必须先被度量并扩展到 PCR。
  • 故障即安全——任何验证失败都应导致拒绝访问,而非降级运行。
  • 全链路可审计——所有度量事件都应记录到 Event Log,供事后取证。
  • 恢复与更新兼顾——支持安全固件更新的同时,绝不放开通往旧版本的回滚路径。
  • 深度防御——不要单一依赖某一层,DICE + PFR + TPM2 IMA叠加。
  • 6.3 展望未来

    随着机密计算(Confidential Computing)的兴起,安全启动链正在向 TEE 延伸:从 CPU TCB 为锚点开始度量,逐步扩展到 GPU TEE、DPU/智能网卡的 HRoT。NVIDIA Hopper 架构的 Confidential Computing、Intel TDX 的 TEE 可信启动、AMD SEV-SNP 的 Secure Nested Paging,都正在将 DICE 的理念延伸到加速器层面。

    在不远的未来,一台服务器的信任链可能从 CPU DICE 起始,经由 BMC PFR 验证,再延伸到 GPU 安全启动、NVMe 控制器的 TCG Opal 自加密,最终由服务端的统一证明平台(如 Keylime、VUnits)做全局的可信状态评估。


    本文基于 TCG DICE Architecture v1.1、TCM 2.0 Library Specification Rev 1.58、NIST SP 1800-34D 和 Linux 内核 v6.12 源码撰写。所有代码示例为教学用途的简化实现,生产环境需使用经过安全审计的库(如 tpm2-tss、wolfTPM)。

    点赞(0) 打赏

    评论列表 共有 0 条评论

    暂无评论
    立即
    投稿

    微信公众账号

    微信扫一扫加关注

    发表
    评论
    返回
    顶部