ferritls_backend_x86_64/lib.rs
1//! # ferritls-backend-x86_64
2//!
3//! [`ferritls-core`] 的 x86_64 硬件执行核心(AES-NI + CLMUL GHASH +
4//! SHA-NI)。**FIPS 边界外** crate(docs/FIPS.md §1),仅 x86_64——
5//! 其余目标编译为空 crate。
6//!
7//! ## 用法
8//!
9//! ```no_run
10//! # fn main() -> Result<(), ferritls_core::Error> {
11//! #[cfg(target_arch = "x86_64")]
12//! {
13//! // 进程初始化阶段(构造任何密钥之前)一次性安装。本 crate 仅
14//! // x86_64 有内容,其他目标上此块被裁掉、示例仍然可编译。
15//! ferritls_backend_x86_64::install()?;
16//! }
17//! // 此后 ferritls_core::gcm::{Aes128Gcm, Aes256Gcm} 的新实例走
18//! // AES-NI/CLMUL 路径;已构造的实例与未安装的环境不受影响。
19//! # Ok(())
20//! # }
21//! ```
22//!
23//! 安装语义(详见 [`ferritls_core::ops`] 模块文档):
24//!
25//! - 探测 `aes` + `pclmulqdq`,任一缺失返回
26//! [`Unsupported`](ferritls_core::Error::Unsupported)(不降级、不安装);
27//! - 安装前在本 crate 的执行核心上跑 McGrew–Viega TC5/TC16 已知答案
28//! 自检,失败返回 [`SelfTestFailed`](ferritls_core::Error::SelfTestFailed)
29//! 并拒绝安装;
30//! - 批准模式(`ferritls-core` 以 `fips` feature 构建)下 core 拒绝一切
31//! 安装,软件路径固定;
32//! - 进程级一次,重复安装由 core 拒绝。
33//!
34//! ## unsafe 纪律(借鉴 fearless_simd 的模式,零依赖自建)
35//!
36//! crate 根 `#![deny(unsafe_code)]`;unsafe 只存在于唯一叶子模块
37//! [`raw`](self)(仅内存读写包装)与各 kernel 的进入点 trampoline
38//! (`new`/`seal`/`open` 与 SHA 的压缩函数注册点)。kernel 本体标注
39//! `#[target_feature(enable = ...)]` 并在上下文内直调 intrinsic(安全、
40//! 编译为裸指令)——从无 feature 上下文包装调用会因 feature 不匹配
41//! 被禁止内联,每个调用退化为真实函数调用(见 ARCHITECTURE.md §4)。
42//! CPU 能力以 [`AesNi`] token 进入类型系统:token 只能经运行时探测
43//! 构造(其 clone 同样源自一次成功探测),是进入 trampoline 的安全
44//! 论证。除 trampoline 外的全部代码均为安全代码。
45//!
46//! 常数时间:AES/GCM 指令为固定延迟、数据无关;kernel 内无以秘密为
47//! 条件的分支或访存(轮密钥按公开轮号索引);标签比较统一由 core 的
48//! 公开类型完成(本 crate 只计算、不比较)。
49//!
50//! [`ferritls-core`]: https://docs.rs/ferritls-core
51
52#![cfg(target_arch = "x86_64")]
53#![deny(unsafe_code)]
54
55mod gcm;
56mod raw;
57mod sha;
58mod token;
59
60use std::sync::OnceLock;
61
62use ferritls_core::ops;
63
64pub use token::{AesNi, ShaNi};
65
66/// 后端单例(唯一向 core 注册的对象)。
67struct Backend {
68 /// 探测成功后由 [`install`] 写入;注册引用只会在写入之后交给 core,
69 /// 工厂方法读取时的 [`None`] 属内部不变式破坏。
70 tok: OnceLock<AesNi>,
71}
72
73static BACKEND: Backend = Backend {
74 tok: OnceLock::new(),
75};
76
77impl ops::AeadOps for Backend {
78 fn name(&self) -> &'static str {
79 "aesni-clmul"
80 }
81
82 fn aes128_gcm(&self, key: &[u8; 16]) -> Box<dyn ops::AeadGcm> {
83 let tok = self
84 .tok
85 .get()
86 .expect("backend token set before registration");
87 Box::new(gcm::NiGcm::<11>::new(tok, key))
88 }
89
90 fn aes256_gcm(&self, key: &[u8; 32]) -> Box<dyn ops::AeadGcm> {
91 let tok = self
92 .tok
93 .get()
94 .expect("backend token set before registration");
95 Box::new(gcm::NiGcm::<15>::new(tok, key))
96 }
97}
98
99/// 探测 AES-NI + CLMUL(不安装)。
100///
101/// 供需要自定义装配/诊断的调用方使用;常规路径直接 [`install`]。
102pub fn detect() -> Option<AesNi> {
103 AesNi::detect()
104}
105
106/// SHA-256 后端单例(函数分发形态:唯一状态是能力证明)。
107struct ShaBackend {
108 tok: OnceLock<ShaNi>,
109}
110
111static SHA_BACKEND: ShaBackend = ShaBackend {
112 tok: OnceLock::new(),
113};
114
115impl ferritls_core::ops::HashOps for ShaBackend {
116 fn name(&self) -> &'static str {
117 "sha-ni"
118 }
119
120 fn sha256_compress(&self) -> ferritls_core::ops::Sha256Compress {
121 let tok = self
122 .tok
123 .get()
124 .expect("sha-ni token set before registration");
125 tok.sha256_compress()
126 }
127}
128
129/// 探测 SHA 扩展 → KAT 自检 → 安装 SHA-256 压缩后端(进程级一次)。
130///
131/// 语义同 [`install`]:CPU 不支持 / KAT 失败 / 批准模式 / 重复安装
132/// 分别以对应错误拒绝。与 [`install`] 相互独立(CPU 可能只具备其一)。
133pub fn install_hash() -> Result<(), ferritls_core::Error> {
134 let tok = ShaNi::detect().ok_or(ferritls_core::Error::Unsupported)?;
135 sha::power_up_kat(&tok)?;
136 let _ = SHA_BACKEND.tok.set(tok);
137 ferritls_core::ops::install_hash(&SHA_BACKEND)
138}
139
140/// 探测 → 上电 KAT 自检 → 安装到 ferritls-core(进程级一次)。
141///
142/// CPU 不支持返回 [`Unsupported`](ferritls_core::Error::Unsupported);
143/// KAT 失败返回 [`SelfTestFailed`](ferritls_core::Error::SelfTestFailed);
144/// 批准模式或重复安装由 core 拒绝(同样表现为
145/// [`Unsupported`](ferritls_core::Error::Unsupported))。
146pub fn install() -> Result<(), ferritls_core::Error> {
147 let tok = AesNi::detect().ok_or(ferritls_core::Error::Unsupported)?;
148 gcm::power_up_kat(&tok)?;
149 let _ = BACKEND.tok.set(tok);
150 ops::install(&BACKEND)
151}