Zero-OS 合规模块

源文件:kernel/compliance/lib.rs
Crate:compliance(v0.1.0)
环境:#![no_std]

目录

  1. 概述
  2. 架构与设计原则
  3. 加固配置
  4. FIPS 模式
  5. 密码算法策略
  6. FIPS 已知答案测试(KAT)
  7. 审计集成
  8. 与安全模块的集成
  9. 合规报告
  10. 关键数据结构
  11. 并发与安全性
  12. 关键函数逐行解析
  13. 启动流程集成
  14. 缺陷修复历史

概述

compliance 模块是 Zero-OS 内核安全态势的集中式策略引擎。它管理系统加固的两个正交维度:

  1. 加固配置 —— 在启动时选择安全性与性能之间的权衡方案(安全 / 均衡 / 性能)。每个配置映射到一个具体的 SecurityConfig,用于控制 W^X 强制执行、Spectre 缓解措施、内核指针混淆以及审计缓冲区大小。

  2. FIPS 模式 —— 一个可在运行时激活的、粘性的(单向的,重启前不可撤销)标志,用于强制执行 FIPS 140-2 / 140-3 合规性。启用后,仅允许使用经 FIPS 批准的密码算法,且所有密码操作均会被审计。激活前需通过一组 NIST 已知答案测试(KAT)。

该模块专为裸机 no_std 内核环境设计。它仅使用无锁原子操作管理全局状态,从不分配堆内存,并与 auditsecuritylivepatch crate 集成。

依赖项

Crate作用
security接收由当前加固配置生成的 SecurityConfig
audit提供 SHA-256 / HMAC-SHA256 密码原语及事件发送 API
livepatch提供 ECDSA P-256 验证器及其 KAT(run_kat_if_needed
spin自旋锁原语(列于 Cargo.toml 中,当前源码未直接使用)
x86_64VirtAddr 类型,用于物理内存偏移

架构与设计原则

合规模块遵循以下安全工程原则:

  • 失败即关闭(Fail-Closed):对原子值的每个 match 匹配都包含一个通配符分支(_),回退到最严格的状态。损坏的配置值默认为 Secure;损坏的 FIPS 值默认为 Failed。这确保内存损坏或位翻转永远不会削弱安全态势。

  • 粘性状态(Sticky State):FIPS 模式一旦启用便无法禁用。FIPS_MODE 原子变量只能从 Disabled -> EnabledDisabled -> Failed 转换,不存在反向路径。

  • 单一事实来源(Single Source of Truth):合规模块不重复实现密码算法,而是将 KAT 执行委托给所属子系统(audit::crypto 负责 SHA-256/HMAC,livepatch::ecdsa_p256 负责 ECDSA),以防止实现偏差。

  • 零分配(Zero Allocation):整个模块无需堆分配即可运行,因此在分配器初始化之前的早期启动阶段也可安全调用。

  • 无锁并发(Lock-Free Concurrency):所有全局状态均使用 core::sync::atomic 类型,并带有显式的 Ordering 标注。正常操作期间不持有任何互斥锁或自旋锁。


加固配置

枚举定义

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum HardeningProfile {
    Secure      = 0,
    Balanced    = 1,
    Performance = 2,
}

#[repr(u8)] 确保该枚举具有稳定的单字节表示,适合原子存储。

配置对比矩阵

特性安全(Secure)均衡(Balanced)性能(Performance)
W^X 强制执行严格(违规时 panic)仅警告禁用
NX 强制执行
Spectre 缓解措施(IBRS/IBPB/STIBP)全部启用全部启用禁用
内核指针混淆(kptr guard)禁用
恒等映射清理RemoveWritableRemoveWritable
RNG 初始化
安全自检
审计环容量256 条128 条64 条
推荐用途生产环境、高安全性开发环境、通用场景基准测试、低延迟

配置解析

from_str 方法接受不区分大小写的别名:

配置可接受的字符串
Securesecurestricthardened
Balancedbalanceddefaultnormal
Performanceperformanceperffast

解析使用固定的 16 字节栈缓冲区进行大小写转换,避免堆分配。超过 16 字节的字符串会被静默截断。

默认配置

Default 实现返回 Balanced,为开发环境提供合理的安全性/性能权衡。

SecurityConfig 生成

每个配置通过 security_config(&self, phys_offset) 方法生成完整的 security::SecurityConfig 结构体。phys_offset 参数(x86_64::VirtAddr 类型)必须由内存子系统提供,表示物理内存恒等映射的虚拟地址。

各配置在生成的 SecurityConfig 中的关键差异:

  • Securestrict_wxorx = true(W^X 违规时 panic),run_security_tests = true
  • Balancedstrict_wxorx = false(警告但继续运行),run_security_tests = false
  • Performancevalidate_wxorx = falseenable_kptr_guard = falseenable_spectre_mitigations = false

三种配置共享:cleanup_strategy = RemoveWritableenforce_nx = trueinitialize_rng = true


FIPS 模式

状态机

FIPS 模式建模为三状态状态机:

                  enable_fips_mode()
    Disabled --------------------------> Enabled
        |
        |   (自检失败)
        +--------------> Failed
  • 未知/损坏的原子值映射为 Failed(失败即关闭)。
  • EnabledFailed 是终态(重启前不可转出)。
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum FipsState {
    Disabled = 0,
    Enabled  = 1,
    Failed   = 2,
}

状态转换:

  • Disabled -> Enabled:所有 KAT 通过后成功调用 enable_fips_mode()
  • Disabled -> Failed:调用 enable_fips_mode() 时某个 KAT 失败。
  • Enabled -> (无转换):FIPS 模式具有粘性,无法禁用。
  • Failed -> (无转换):失败状态在重启前是永久的。

粘性语义

一旦 FIPS_MODE 被设置为 Enabled,就没有 API 可以将其恢复为 Disabled。退出 FIPS 模式的唯一方式是系统重启,届时 AtomicU8 会被重新初始化为 0Disabled)。

错误类型

pub enum FipsError {
    AlreadyEnabled,   // FIPS 模式已处于激活状态
    EnableFailed,     // FIPS 模式此前已失败(永久性)
    NotPermitted,     // 操作被 FIPS 策略阻止
    SelfTestFailed,   // 启用过程中 KAT 失败
    AccessDenied,     // 权限检查失败
}

密码算法策略

算法注册表

pub enum CryptoAlgorithm {
    // 哈希函数
    Sha256, Sha384, Sha512, Blake2b,
    // 消息认证码
    HmacSha256,
    // 签名
    EcdsaP256, EcdsaP384, Ed25519,
    // 加密
    Aes128Gcm, Aes256Gcm, ChaCha20, ChaCha20Poly1305,
}

FIPS 批准矩阵

算法FIPS 批准标准
SHA-256FIPS 180-4
SHA-384FIPS 180-4
SHA-512FIPS 180-4
Blake2bRFC 7693
HMAC-SHA256FIPS 198-1
ECDSA P-256FIPS 186-4
ECDSA P-384FIPS 186-4
Ed25519RFC 8032
AES-128-GCMFIPS 197 / SP 800-38D
AES-256-GCMFIPS 197 / SP 800-38D
ChaCha20RFC 8439
ChaCha20-Poly1305RFC 8439

策略执行逻辑

is_algorithm_permitted(algorithm) 函数实现三路判定:

  1. FIPS 已禁用:所有算法均被允许(立即返回 true)。
  2. FIPS 已启用:仅允许经 FIPS 批准的算法(通过匹配表检查)。
  3. FIPS 已失败:不允许任何算法(立即返回 false)。这就是失败即关闭行为——如果 FIPS 自检失败,所有密码操作均被阻止。

FIPS 已知答案测试(KAT)

fips_kat 子模块实现了 NIST 规定的已知答案测试,这些测试必须在 FIPS 模式激活前全部通过。

测试套件

测试标准测试向量实现
SHA-256FIPS 180-4 / CAVP空字符串、"abc"、100 万个 'a'audit::crypto::sha256_digest / StreamingSha256
HMAC-SHA256NIST CSRC / RFC 423164 字节密钥、32 字节密钥、100 字节密钥audit::crypto::hmac_sha256_digest
ECDSA P-256RFC 6979 A.2.5确定性签名验证livepatch::ecdsa_p256::run_kat_if_needed

SHA-256 KAT 详情

验证三个测试向量:

  1. 空消息SHA-256("") = e3b0c442... —— 测试无输入数据的基本情况。
  2. 短消息SHA-256("abc") = ba7816bf... —— 测试单块输入。
  3. 长消息(流式)SHA-256("a" * 1,000,000) = cdc76e5c... —— 通过以 64 字节块(与 SHA-256 内部块大小一致)逐步输入数据来测试流式哈希接口。这在不进行堆分配的情况下测试了多块处理和填充逻辑。

HMAC-SHA256 KAT 详情

按照 NIST CSRC 规范测试三种密钥长度场景:

  1. 密钥 = 块长度(64 字节):密钥 0x00..0x3f,消息 "Sample message for keylen=blocklen"。测试密钥恰好填满一个块的标准路径。
  2. 密钥 < 块长度(32 字节):密钥 0x00..0x1f,消息 "Sample message for keylen<blocklen"。测试零填充路径。
  3. 密钥 > 块长度(100 字节):密钥 0x00..0x63,消息 "Sample message for keylen=blocklen"。测试密钥哈希路径,即 HMAC 必须先对密钥进行哈希以将其缩减到块大小。

ECDSA P-256 KAT 详情

委托给 livepatch::ecdsa_p256::run_kat_if_needed() 执行,该函数运行 RFC 6979 附录 A.2.5 中的确定性 ECDSA 测试向量:

  • 私钥:d = 0xC9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721
  • 消息:"sample"(使用 SHA-256 预哈希)
  • 签名 (r, s) 根据从 d 派生的公钥进行验证。

常量时间比较

所有 KAT 结果比较均使用常量时间相等函数(ct_eq)以防止时序侧信道攻击:

fn ct_eq(a: &[u8], b: &[u8]) -> bool {
    if a.len() != b.len() {
        return false;
    }
    let mut diff = 0u8;
    for i in 0..a.len() {
        diff |= a[i] ^ b[i];
    }
    diff == 0
}

该函数对所有字节累积异或差异,仅在最后检查结果,确保比较时间与不匹配位置无关。


审计集成

FIPS 状态变更通过审计子系统记录。emit_fips_audit_event 函数构造的审计事件包含:

  • 类别(Kind)AuditKind::Security(类别 5)
  • 结果(Outcome)AuditOutcome::SuccessAuditOutcome::Error
  • 主体(Subject)AuditSubject::kernel()(内核本身是操作者)
  • 对象(Object)AuditObject::None(无目标资源)
  • 参数(Args)[event_type, success_flag],其中 event_type = 1(FIPS 启用事件),success_flag1(成功)或 0(失败)
  • 错误码(Errno)0(无错误码)
  • 时间戳(Timestamp)0(由审计子系统填充)

审计事件在两种情况下发送:

  1. FIPS 激活成功(success = true, message = "enabled"
  2. 因 KAT 失败导致 FIPS 激活失败(success = false, message = "self-test failed"

emit 调用的结果被显式丢弃(let _ = ...),因为审计失败不应阻止 FIPS 状态转换——FIPS 状态变更是主要操作,审计日志记录是尽力而为的。


与安全模块的集成

合规模块是高层策略(加固配置)与底层执行(security crate)之间的桥梁。集成流程如下:

启动命令行 --> compliance::HardeningProfile::from_str()
                        |
                        v
              compliance::set_profile(profile)
                        |
                        v
              profile.security_config(phys_offset)
                        |
                        v
              security::SecurityConfig { ... }
                        |
                        v
              security::init(config, &mut frame_allocator)
                        |
                        v
              compliance::lock_profile()  // 重启前不可变

SecurityConfig 结构体字段及其含义:

字段类型描述
phys_offsetVirtAddr物理内存映射的虚拟地址
cleanup_strategyIdentityCleanupStrategy如何处理引导加载程序的恒等映射(始终为 RemoveWritable
enforce_nxbool在数据页上设置 NX 位
validate_wxorxbool检查 W^X 违规
strict_wxorxboolW^X 违规时 panic(而非仅警告)
initialize_rngbool初始化 RDRAND/RDSEED + ChaCha20 CSPRNG
enable_kptr_guardbool在日志中混淆内核指针
enable_spectre_mitigationsbool启用 IBRS/IBPB/STIBP
run_security_testsbool运行运行时安全自检

合规报告

ComplianceStatus 结构体提供当前合规态势的快照:

pub struct ComplianceStatus {
    pub profile: HardeningProfile,
    pub profile_locked: bool,
    pub fips_state: FipsState,
}

通过 compliance::status() 获取,该结构体聚合了:

  • 当前活跃的加固配置(从 ACTIVE_PROFILE 原子变量读取)
  • 配置是否已锁定(从 PROFILE_LOCKED 原子变量读取)
  • 当前 FIPS 状态(从 FIPS_MODE 原子变量读取,采用失败即关闭语义)

这对于运行时自省、健康检查和合规仪表板非常有用。


关键数据结构

全局原子变量

静态变量类型初始值用途
ACTIVE_PROFILEAtomicU81(Balanced)存储当前活跃的加固配置
PROFILE_LOCKEDAtomicBoolfalse防止启动后修改配置
FIPS_MODEAtomicU80(Disabled)存储 FIPS 状态
FIPS_ENABLINGAtomicBoolfalse序列化并发的 FIPS 启用尝试

枚举

枚举表示变体用途
HardeningProfileu8Secure(0)、Balanced(1)、Performance(2)安全级别选择
FipsStateu8Disabled(0)、Enabled(1)、Failed(2)FIPS 模式状态机
FipsError--AlreadyEnabled、EnableFailed、NotPermitted、SelfTestFailed、AccessDenied错误报告
CryptoAlgorithm--12 个变体(哈希、MAC、签名、加密)算法标识

结构体

结构体字段用途
ComplianceStatusprofile、profile_locked、fips_state运行时合规快照

并发与安全性

内存排序

模块全程使用显式的原子排序:

  • Ordering::Acquire 用于所有加载操作:确保对原子值的读取能看到对应 Release 存储之前发生的所有写入。

  • Ordering::Release 用于所有存储操作:确保存储对其他核心上后续的 Acquire 加载可见。

  • Ordering::AcqRel 用于 compare_exchange:用于 FIPS 启用序列化锁(FIPS_ENABLING)。

FIPS 启用序列化

enable_fips_mode() 函数通过 FIPS_ENABLING 使用自旋锁模式来序列化并发的启用尝试:

线程 A:CAS(FIPS_ENABLING, false -> true)  --> 成功,运行 KAT
线程 B:CAS(FIPS_ENABLING, false -> true)  --> 失败,自旋等待
线程 A:KAT 通过,存储 FIPS_MODE = Enabled
线程 A:EnableGuard 析构,存储 FIPS_ENABLING = false
线程 B:CAS 成功,重新检查 FIPS_MODE --> AlreadyEnabled,返回错误

EnableGuard RAII 结构体确保 FIPS_ENABLING 始终被释放,即使在 KAT 执行期间发生 panic 也是如此。

TOCTOU 防护

enable_fips_mode() 函数两次检查 fips_state()

  1. 获取启用锁之前(快速路径拒绝)
  2. 获取启用锁之后(防止 TOCTOU 竞态条件)

配置锁定

配置锁是一个单向闩锁:PROFILE_LOCKEDfalse 转换为 true 后不再回退。这是安全的,因为 set_profile() 仅在早期单线程启动阶段调用,而 lock_profile() 紧随其后立即调用。


关键函数逐行解析

enable_fips_mode()

pub fn enable_fips_mode() -> Result<(), FipsError> {

公共入口点。FIPS 激活成功时返回 Ok(()),否则返回类型化错误。

    match fips_state() {
        FipsState::Enabled => return Err(FipsError::AlreadyEnabled),
        FipsState::Failed => return Err(FipsError::EnableFailed),
        FipsState::Disabled => {}
    }

快速路径检查(第 249-253 行):在尝试获取开销较大的自旋锁和执行 KAT 之前,如果 FIPS 已启用或永久失败,则立即拒绝。

    while FIPS_ENABLING
        .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
        .is_err()
    {
        core::hint::spin_loop();
    }

自旋锁获取(第 256-261 行):原子地尝试将 FIPS_ENABLINGfalse 设置为 true。如果另一个线程持有该锁,则通过 core::hint::spin_loop() 自旋等待(在 x86 上发出 PAUSE 指令)。

    struct EnableGuard;
    impl Drop for EnableGuard {
        fn drop(&mut self) {
            FIPS_ENABLING.store(false, Ordering::Release);
        }
    }
    let _guard = EnableGuard;

RAII 守卫(第 264-270 行):定义一个零大小类型,其 Drop 实现释放启用锁。这保证了在所有退出路径上锁都会被释放。

    match fips_state() {
        FipsState::Enabled => return Err(FipsError::AlreadyEnabled),
        FipsState::Failed => return Err(FipsError::EnableFailed),
        FipsState::Disabled => {}
    }

TOCTOU 重新检查(第 273-277 行):获取锁之后,重新读取 FIPS 状态以防止竞态条件。

    if !run_fips_self_tests() {
        FIPS_MODE.store(FipsState::Failed as u8, Ordering::Release);
        emit_fips_audit_event(false, "self-test failed");
        return Err(FipsError::SelfTestFailed);
    }

KAT 执行(第 280-284 行):运行所有 FIPS 已知答案测试。如果任何测试失败,FIPS 状态将被永久设置为 Failed

    FIPS_MODE.store(FipsState::Enabled as u8, Ordering::Release);
    emit_fips_audit_event(true, "enabled");
    Ok(())

激活(第 287-290 行):所有 KAT 均已通过。将 FIPS 模式设置为 Enabled(粘性),发送成功审计事件,并返回 Ok

current_profile()

pub fn current_profile() -> HardeningProfile {
    match ACTIVE_PROFILE.load(Ordering::Acquire) {
        0 => HardeningProfile::Secure,
        1 => HardeningProfile::Balanced,
        2 => HardeningProfile::Performance,
        _ => HardeningProfile::Secure,  // 失败即关闭
    }
}

通配符分支(_)实现了失败即关闭原则:如果原子值被损坏,系统默认回退到最严格的 Secure 配置。

fips_state()

pub fn fips_state() -> FipsState {
    match FIPS_MODE.load(Ordering::Acquire) {
        0 => FipsState::Disabled,
        1 => FipsState::Enabled,
        2 => FipsState::Failed,
        _ => FipsState::Failed,  // 失败即关闭
    }
}

失败即关闭的通配符分支将损坏的值映射为 Failed,这会导致 is_algorithm_permitted() 阻止所有算法。

is_algorithm_permitted()

两阶段判定:首先检查 FIPS 状态(对 Disabled/Failed 进行短路处理),然后将特定算法与 FIPS 批准列表进行比对。Failed 状态会阻止一切。

fips_kat::ct_eq()

常量时间字节比较。对每对字节进行异或运算,并将结果按位或累积到一个累加器中。无论不匹配出现在哪个位置,循环始终执行完整长度,从而防止时序侧信道攻击。


启动流程集成

合规模块按如下方式集成到 Zero-OS 启动流程中(来自 kernel/src/main.rs):

[2.6/3] 应用安全加固...
    1. 选择配置:      compliance::HardeningProfile::Balanced
    2. 设置配置:      compliance::set_profile(profile)
    3. 生成配置:      profile.security_config(phys_offset)
    4. 打印信息:      "Profile: Balanced (audit_capacity: 128)"
    5. 初始化安全模块:security::init(sec_config, &mut frame_allocator)
    6. 锁定配置:      compliance::lock_profile()
    7. 打印:          "Profile locked (immutable until reboot)"

调用 lock_profile() 之后,任何后续对 set_profile() 的调用都会返回 false 且不产生任何效果。

目前,内核主函数中配置被硬编码为 Balanced。生产部署时应解析启动命令行(例如 profile=secure),并使用 HardeningProfile::from_str() 选择相应的配置。


缺陷修复历史

修复编号描述影响
R93-1使用 FIPS_ENABLING 自旋锁和 TOCTOU 重新检查来序列化 FIPS 启用尝试防止并发 enable_fips_mode() 调用之间的竞态条件
R93-1is_algorithm_permitted() 中对 FIPS 自检失败实施失败即关闭策略FipsState::Failed 阻止所有算法,而非允许它们通过
R93-14实现真正的 FIPS 140-2/140-3 已知答案测试(SHA-256、HMAC-SHA256、ECDSA P-256)用 NIST 规定的测试向量替换占位自检
R94-3对 FIPS 状态损坏实施失败即关闭策略(_ => FipsState::Failed损坏的原子值不再回退到 Disabled
R94-4对配置损坏实施失败即关闭策略(_ => HardeningProfile::Secure损坏的原子值不再回退到 Balanced