· 正微光电· company-news· 约 59 分钟精读
Cortex-A7 后量子密码:指令级优化加速实践
正微光电面向物联网的后量子密码算法库,在 ARM Cortex-A7 平台以 854 行 NEON 汇编重写热循环,相对开源 liboqs 基线实现 6.7-8.9 倍运算加速:ML-KEM KeyGen 7,035 ops/s、ML-DSA KeyGen 2,480 ops/s。本文详细剖析向量化蝶形运算、双发射流水线调度、访存对齐与海量 IoT 终端 PQC 升级的工程路径。
后量子密码(Post-Quantum Cryptography, PQC)从标准走向终端,最大障碍从来不是算法数学,而是嵌入式算力。ML-KEM、ML-DSA 等格基方案以大量多项式运算换取抗量子安全性,其计算密度远高于 RSA 与 ECC,而电力采集、车联网、工业控制等领域数以亿计的存量 IoT 终端仍运行在 Cortex-A7 这一级的低功耗处理器上,既无更换硬件的现实条件,也无放任不管的安全余量。正微光电(量子安全基础设施供应商,母公司为正则量子(北京)技术有限公司,围绕量子安全主线已形成 13 项专利布局)在自有 PQC IoT 算法库中,以 854 行 NEON 汇编对 Cortex-A7 上的 ML-KEM/ML-DSA 热循环实施指令集级重写,实现相对开源 liboqs 基线 6.7–8.9 倍的运算加速,其中 ML-KEM-768 密钥生成实测 7,035 ops/s、ML-DSA-2 签名实测 2,480 ops/s,并经独立第三方实测验证。本文从威胁模型、算法热点、微架构映射、向量化实现、访存优化到侧信道与工程落地,完整还原这一优化路径。
1. IoT 终端的量子威胁与算力约束
1.1 海量 IoT 终端面临的 HNDL 威胁与长服役周期
量子计算对公钥密码的根本性威胁,业界已有共识:Shor 算法可将整数分解与离散对数问题的复杂度从超多项式压缩至多项式,一旦具备数千逻辑量子比特的容错量子计算机问世,RSA 与椭圆曲线密码将整体失效。但对 IoT 终端而言,第一性威胁并非”今天被解密”,而是 HNDL(Harvest Now, Decrypt Later,先存后解):攻击者当下截获并存储加密流量与数字证书,静候量子算力成熟后批量解密。这一攻击模式的杀伤力由终端资产的三个特征共同放大。
流量规模巨大且不可撤回。电力采集终端(用电信息采集、配电终端)通过公网或无线专网与主站高频交互,车联网终端(T-Box、OBU)持续上报位置与运行数据,工业控制终端(PLC、边缘网关、传感器汇聚节点)承载工艺参数与控制指令。这些链路一旦被嗅探,历史密文便永久留存,待量子解密能力出现时一次性兑现。服役周期远超密码换代周期。电网计量设备的设计寿命普遍在十年以上,工业控制器常服役十五至二十年,而一个部署于 2026 年的 RSA-2048/ECC-256 证书体系,其保密期要求大概率跨越 2030 年后的量子威胁窗口。供应链投毒路径隐蔽。攻击者无需解密全部会话,只需解出某张被缓存的根证书私钥,即可离线签发伪造的固件更新包,对存量终端实施批量投毒——这种”一次破解、永久渗透”的攻击面,使 HNDL 从理论模型直接变为终端安全的现实预算项。
后量子迁移的紧迫性并不依赖量子计算机的精确到货时间。只要”先存后解”在成本上成立,长周期资产就必须假设其密文将在某个未来时点被解密,这是密码工程中的时间维度风险,与当下算力无关。对于无法快速更换的亿级存量终端,唯一现实的对策是在现有硬件上尽早完成密码原语的量子安全化——这正是本文优化工作的出发点。
1.2 低功耗处理器的算力、内存与功耗三重约束
以 Cortex-A7 为代表的低功耗应用处理器,是电力采集、车联网、工业控制终端的绝对主力。典型平台为 1.0–1.2 GHz 双核/四核,集成 32 KB 指令与 32 KB 数据 L1 缓存、256 KB 共享 L2,外挂 128–512 MB DDR3,整板功耗预算常在数瓦以内,电池供电或能量采集场景下更是以毫瓦计。将未经优化的 PQC 参考实现直接移植到这类平台,会同时撞上三重约束。
算力约束最直接。开源参考实现的 ML-KEM-768 密钥生成在 Cortex-A7 上约需 110 万周期,折合 1.1 ms 以上;ML-DSA-2 签名约需 315 万周期,折合 3.1 ms。而终端侧的 TLS 握手、固件验签、密钥轮换往往叠加在数十毫秒的控制周期内,多会话并发时延迟余量被迅速吞噬。内存约束同样严峻:ML-KEM-768 密钥对、密文与共享密钥虽仅数百字节,但参考实现为求可读性,在栈上大量使用 32 位中间量与冗余拷贝,峰值栈占用可达数十 KB,在多任务 RTOS 环境中直接挤压任务栈预算;ML-DSA 的展开态签名与验签路径更长,代码体积与数据段合计轻易超过 100 KB,逼近片内 Flash 与紧凑镜像的预算线。功耗约束则具有乘数效应:公钥运算在 Cortex-A7 上以高占空比运行会显著推高动态功耗,对电池供电的采集终端意味着更换周期缩短,对能量采集终端则直接决定功能能否维持。
三重约束叠加的结论是:PQC 在低功耗终端落地,本质上是在固定硅片上挤出算法所需算力的工程问题。任何不触碰指令级效率的方案——无论是加大缓存、提高主频还是增加核数——都与终端成本、功耗与散热边界冲突。
1.3 软件级 PQC 优化为何是终端迁移的核心路径
终端后量子化的候选路径有三条:硬件更换、安全芯片外挂、软件优化。前两条路径在理论上有其价值,但在存量市场面前均不成立。硬件更换意味着重新设计主板、重新走电磁兼容与可靠性认证、重新组织供应链,单终端成本与周期以年计,对亿级存量设备而言是不可承受的;安全芯片外挂同样面临接口改造、认证重做与成本分摊问题,且外挂方案在带宽与延迟上并不天然优于片内算力。
软件级优化的核心前提是”无硬件更换”:不更换主控、不修改外设接口、不触碰既有认证结论,仅在现有 Cortex-A7 上通过指令集级重写释放算法性能。这条路径的可行性来自两个技术事实。格基密码的计算结构高度规则:ML-KEM/ML-DSA 的核心是环上的多项式运算,NTT 蝶形网络具有天然的层状并行性,与 SIMD 数据通路的结构完全同构,128 位 NEON 寄存器一次即可容纳 8 个 16 位系数。低功耗处理器的性能瓶颈主要来自访存模式、分支预测失效与标量指令密度,而非浮点吞吐,这些瓶颈恰好是手工汇编与数据布局重构最擅长消除的。正微光电 PQC IoT 算法库的工程实践表明:软件级优化可将 ML-KEM-768 密钥生成从约 912 ops/s 提升至 7,035 ops/s,足以支撑终端侧每秒数千次密钥协商的并发需求,且优化成果可通过 OTA 与固件升级直接下发至存量设备,形成”算法即升级”的迁移模式。这也是本文第 2 至第 7 章所展开的技术主线。
2. ML-KEM/ML-DSA 的计算热点分析
2.1 多项式乘法与 NTT 在算法中的占比
优化的前提是精确的热点剖析。我们在 1.0 GHz Cortex-A7 上以 GCC -O2 编译 liboqs 基线,借助性能监视单元(PMU)对 ML-KEM-768 与 ML-DSA-2 的全流程做函数级周期归因,得到如下分布:
| 算法操作 | NTT 族(正变换、逆变换、逐点乘) | 采样(CBD/拒绝采样) | 哈希、压缩与打包 |
|---|---|---|---|
| ML-KEM-768 密钥生成 | 74% | 9% | 17% |
| ML-KEM-768 封装 | 66% | 10% | 24% |
| ML-KEM-768 解封装 | 71% | 9% | 20% |
| ML-DSA-2 签名 | 47% | 28%(拒绝采样) | 25% |
| ML-DSA-2 验签 | 81% | — | 19% |
需要说明剖析口径:函数级归因以 PMU 的周期计数器(PMCCNTR)为基准,对每次调用做入栈-出栈包围计数,再按函数调用树逐层聚合;每个操作重复 10,000 次取中位数,剔除中断与调度抖动。该口径与第 6 章实测平台完全一致,保证热点结论可直接迁移到后续优化效果的归因。结论清晰:NTT 族运算(含多项式乘法所必需的两次正变换、一次逐点乘与一次逆变换)在全部热点操作中占据绝对主导,验签路径高达 81%,密钥生成与封装亦超过三分之二。即只要将 NTT 及多项式乘法做到接近硬件极限,整个算法的性能上限就会被大幅抬高;而采样与哈希环节虽然占比有限,但在 ML-DSA 签名路径中合计超过 50%,同样需要在向量化时一并覆盖。热点剖析的另一个产出是确定了优化投入的优先级:先攻 NTT,再攻采样,最后清理压缩与打包,这与我们第 4 章的工程顺序完全一致。
2.2 模数与环结构:q = 3329、ζ = 17 与二次因式分解
ML-KEM 与 ML-DSA 共享同一套核心代数结构,其参数选择的精妙之处值得先做交代。工作环为
模数 是精心挑选的 NTT 友好素数:,其 2-adic 度恰好覆盖 ,故 中存在 256 次本原单位根。标准实现取 ,直接验证可得
即 17 在模 3329 下的阶恰为 256,是 256 次本原单位根。由此得到环的因式分解:
其中 为 7 位比特逆序置换。需要特别指出的是, 但 ,因此这些二次因式在 上不可再分裂为一次因式——这正是 Kyber/Dilithium 系算法所谓”非完整 NTT”(incomplete NTT)的数学根源:变换将环分解为 128 个二次扩域分量,多项式乘法退化为 128 路”逐二次式”乘法与负循环折叠。工程上这带来两个直接后果:蝶形运算的基数为 2 且层数固定为 8( 加上最后的组合层),计算图完全静态,天然适合循环展开与 SIMD 化;所有旋转常数 可离线预计算为查表,将运行期的求幂开销整体消除。此外,Montgomery 约减所需的常数同样固定:(满足 ), 的模 剩余为 ,其逆为 。这些常数在汇编实现中全部以立即数或向量广播形式固化,运行期零计算。
2.3 中心二项分布采样与拒绝采样的开销
采样环节虽非最大热点,但其开销结构决定了向量化的具体打法。ML-KEM 使用中心二项分布(CBD)采样:从 SHAKE256 输出中每 2 个字节抽取 2 个 4 位半字节,以异或后做 popcount 相减的方式得到 分布的系数。其标量开销来自三处:位提取(每个系数 2 次移位与掩码)、异或与 popcount(每次 4 条整数指令)、以及结果写入。在标量实现中,每个系数平均消耗约 8–10 条指令,而 NEON 化后可用 vtbl 查表配合 vcnt 指令将 8 个系数并行产出,单次向量操作覆盖一整组采样。
ML-DSA 的拒绝采样开销结构不同:签名路径从扩展哈希输出中按 24 字节一组提取 3 个系数,若系数超出 界则拒绝并重试。其标量实现的代价难点在于算术,更在于分支——每个系数一次条件判断,签名全程期望重试次数约 1.39 次,分支预测失败在顺序执行的 Cortex-A7 上代价高昂。向量化的关键是消除数据相关分支:将拒绝条件改写为掩码运算,一次 vcgt.s16 生成全 1/全 0 掩码,配合 vbit/vbif 做选择,即可在无分支路径上完成同样的拒绝语义。实测表明,该改造将拒绝采样路径的单系数平均代价从约 9.8 周期降至约 1.4 周期,配合 8 路并行取得 7.0 倍加速。采样环节的优化同时具备安全意义:掩码式拒绝天然满足恒定时间要求,这一点在第 7.1 节还会展开。
3. Cortex-A7 微架构与 NEON 向量化基础
3.1 Cortex-A7 微架构:128-bit NEON SIMD、双发射、顺序执行
Cortex-A7 隶属 ARMv7-A 架构,是 ARM 面向低功耗市场的高效核心,其微架构特征对向量化策略具有决定性影响。顺序执行(in-order):整数流水线深度 8 级,无乱序执行窗口,指令的发射顺序即程序顺序,任何数据依赖造成的停顿都必须由编译器或汇编作者通过显式调度消除,不能指望硬件重命名窗口兜底。双发射:每周期最多发射 2 条指令,典型组合为 1 条 NEON/媒体指令与 1 条 Load/Store 或整数指令;即访存指令与向量算术在理想情况下可以完全重叠,为隐藏访存延迟提供了结构性基础。128 位 NEON 数据通路:NEON 单元独立于整数流水线,支持 8×16 位或 4×32 位定点并行,寄存器文件含 16 个 128 位 Q 寄存器(亦可视为 32 个 64 位 D 寄存器),全部数据通路对汇编完全可见。
访存层次同样需要量化。L1 数据缓存 32 KB,行大小 64 字节,命中延迟约 3–4 周期;L2 缓存 256 KB,未命中代价约 20–30 周期;顺序执行核心对访存停顿尤为敏感——一次 L1 未命中引发的流水线气泡无法被乱序窗口吸收,只能通过预取与软件流水主动规避。分支方面,Cortex-A7 的分支预测器对循环回边预测良好,但对数据相关分支(如拒绝采样的条件判断)在预测失败时付出约 13 周期的清洗代价。这些数字共同勾勒出优化空间:指令调度、访存模式、分支消除三者合计的潜在收益,远大于单纯”换用更快的乘法指令”所能带来的增益。理解这一点,是读懂本文全部优化手段的前提。
3.2 128-bit 寄存器同时容纳 8 个 16-bit 系数的并行度映射
向量化的第一性映射:一个 Q 寄存器 = 128 位 = 8 个 16 位系数。这一映射之所以成立,依赖第 2.2 节交代的系数动态范围管理。环 的系数以 16 位有符号整数承载:在”惰性约减”(lazy reduction)策略下,中间系数始终保持在 区间内,16 位表示绰绰有余;而乘法中间量 需要 32 位,故乘法指令一律使用 16×16→32 的加宽形式(vmull/vmlal),窄化回 16 位则在约减完成后进行。这样的宽度纪律保证了整个 NTT 过程中每个 Q 寄存器始终承载 8 个独立系数,8 路并行度不打任何折扣。
并行度映射的收益可以量化。一次标准 CT 蝶形(一次乘加、一次加减、一次条件约减)在标量实现中约需 25–35 条整数指令;NEON 化后,8 路蝶形共享同一条指令流,指令数摊薄至每路 3–4 条。以 256 点 NTT 的 1024 个蝶形计,标量路径约 3 万条指令,向量路径约 4 千条,指令密度提升直接转化为周期数的数量级改善。寄存器压力的测算同样重要。一次 8 路蝶形同时占用输入(3 个 Q)、中间量(6 个 Q)与结果(2 个 Q),加上常数向量与掩码暂存,峰值需求达 15 个 Q 寄存器,恰好落在 16 个 Q 寄存器的架构上限之内;一旦某个蝶形变体超出上限,编译器被迫溢出到栈,访存代价将直接吞噬向量化收益。因此汇编实现的寄存器分配以”上限余量最小化”为目标,任何新增的临时量都必须以复用已死亡寄存器的方式消化。需要强调,这种映射并非编译器的自动向量化所能稳定产出:GCC 对带 Montgomery 约减的 NTT 循环通常生成低效的标量-向量混排代码,原因在于约减过程的进位语义与窄化操作难以被自动识别——这正是手写汇编不可替代的根本理由。
3.3 NEON 指令集关键指令:vld1/vst1、vmull/vmlal、vshr、vmls
本文实现用到的 NEON 指令可以收敛为一个精炼子集,每条指令的语义与用途如下表:
| 指令 | 语义 | 在 NTT 实现中的用途 |
|---|---|---|
vld1.16 {q}, [r]! | 加载 8×16 位,可选后变址 | 一次取齐 8 个系数/旋转常数 |
vst1.16 {q}, [r]! | 存储 8×16 位 | 蝶形结果写回 |
vmull.s16 q, d, d | 16×16→32 有符号加宽乘法 | 计算 的 32 位积 |
vmlal.s16 q, d, d | 加宽乘累加 | 多组积合并(如常数表组合) |
vmull.u16 q, d, d | 16×16→32 无符号加宽乘法 | Montgomery 约减中的 |
vshr.s32 q, q, #16 | 32 位算术右移 | 约减末步 |
vmovn.i32 d, q | 32 位窄化至 16 位(取低半) | 提取 、窄化约减结果 |
vadd.s16 / vsub.s16 | 16 位向量加减 | 蝶形加减 |
vcgt.s16 / vclt.s16 | 向量比较生成全 1/全 0 掩码 | 恒定时间条件约减 |
vand.s16 / vbit | 按位与 / 位选择 | 掩码式选择 |
vdup.16 d, r | 立即数/寄存器广播 | 装载 、 常数向量 |
pld [r, #imm] | 预取至 L1 | 访存延迟隐藏(第 5.2 节) |
两个工程细节值得记录。Cortex-A7 的 NEON 执行延迟与操作数无关——vmull 的延迟固定(约 4–5 周期),不存在乱序核心中常见的”乘法耗时随数据变化”现象,这为恒定时间实现提供了硬件级保障。vshr.s32 对 的算术右移在 恒为 倍数时精确等于整除,无需额外的舍入修正,这是 Montgomery 约减与 ARM 右移语义的天然契合点,也是该约减路径仅需 5 条向量指令即可完成的原因。
4. NTT 蝶形运算的向量化实现
4.1 数据布局重构:四路交织(AoS→SoA)消除 stride
向量化的第一个拦路虎是数据布局。参考实现以”数组-数组”(Array of Structures, AoS)形式逐点存放系数,蝶形运算需要同时读取同一位置的 、 两个操作数,在 AoS 布局下表现为跨步访问:每取一个系数都要跳过相邻的其他系数,一次 128 位加载中有效数据仅占一半甚至更少,且两次蝶形之间的访存地址不连续,直接破坏 64 字节缓存行的高效利用。
解法是布局重构:将参与同一蝶形层的多条多项式从 AoS 转换为结构-数组(Structure of Arrays, SoA)形态,具体采用四路交织方案。以 4 条多项式 为一组,按 64 系数块交错存放:块 的布局为 ,即任意蝶形位置上的 4 个系数在内存中连续占据 8 字节,两个相邻蝶形位置恰好凑满 16 字节——一次 vld1.16 {q}, [r] 即可取齐 8 路蝶形所需的全部 8 个系数,加载带宽利用率从约 50% 提升至 100%:
布局重构的收益不止于加载效率。SoA 形态下蝶形输出的写回同样连续,vst1.16 一次写满 16 字节;旋转常数表也按”8 个连续常数一组”重排,与蝶形层的访存模式完全对齐;更重要的是,连续访问将 L1 缓存行(64 字节)的利用率推到极限——每个缓存行承载 32 个系数,恰好覆盖 4 个蝶形批次,为第 5 章的命中率优化奠定了布局基础。这一重构在算法语义上完全透明(只是索引重排),可在编译期由宏展开完成,运行期零开销。
4.2 8 路并行蝶形的 NEON 汇编实现
在 SoA 布局与 Montgomery 域常数表就绪后,8 路并行蝶形可以用一段结构规整的 NEON 汇编完整表达。以下代码为 CT(Cooley–Tukey)蝶形的核心例程:输入 8 个 系数、8 个 系数与 8 个旋转常数,输出 8 组 与 ,全程无分支、无除法、无 32 位宽度的数据相关:
@ ================================================================
@ ntt8_butterfly: 8 路并行 CT 蝶形 + Montgomery 约减
@ r0: &a[8] (SoA 布局, 8 个 a 系数连续存放)
@ r1: &b[8] (SoA 布局, 8 个 b 系数连续存放)
@ r2: &zeta_table[8] (Montgomery 域常数, 已预乘 R = 2^16)
@ q14: 8 x qinv (qinv = 62209 = -q^{-1} mod 2^16)
@ q15: 8 x q (q = 3329, 恒定时间条件约减常数)
@ 输出: a' = a + b*zeta mod q, b' = a - b*zeta mod q
@ 输入/输出均以 Montgomery 域表示, 常数表预乘 R 保证结果语义正确
@ ================================================================
ntt8_butterfly:
vld1.16 {q0}, [r0] @ q0 = a[0..7]
vld1.16 {q1}, [r1] @ q1 = b[0..7]
vld1.16 {q2}, [r2]! @ q2 = zeta[0..7]
@ ---- 阶段 1: t = b * zeta, 16x16 -> 32-bit ----
vmull.s16 q8, d2, d4 @ t[0..3] = b[0..3] * zeta[0..3]
vmull.s16 q9, d3, d5 @ t[4..7] = b[4..7] * zeta[4..7]
@ ---- 阶段 2: m = (t * qinv) mod 2^16 (取低 16 位) ----
vmovn.i32 d20, q8 @ t_low[0..3] = t[0..3] & 0xFFFF
vmovn.i32 d21, q9 @ t_low[4..7] = t[4..7] & 0xFFFF
vmull.u16 q11, d20, d28 @ p[0..3] = t_low[0..3] * qinv
vmull.u16 q12, d21, d29 @ p[4..7] = t_low[4..7] * qinv
vmovn.i32 d26, q11 @ m[0..3] = p[0..3] & 0xFFFF
vmovn.i32 d27, q12 @ m[4..7] = p[4..7] & 0xFFFF
@ ---- 阶段 3: t' = (t + m*q) >> 16 (Montgomery 约减) ----
vmull.u16 q10, d26, d30 @ mq[0..3] = m[0..3] * q
vmull.u16 q3, d27, d31 @ mq[4..7] = m[4..7] * q
vadd.i32 q8, q8, q10 @ t[0..3] + mq[0..3]
vadd.i32 q9, q9, q3 @ t[4..7] + mq[4..7]
vshr.s32 q8, q8, #16 @ t'[0..3] = (t + mq) >> 16
vshr.s32 q9, q9, #16
vmovn.i32 d16, q8 @ t'[0..3] 窄化回 16-bit
vmovn.i32 d17, q9 @ t'[4..7]
@ ---- 阶段 4: 蝶形加减 a' = a + t', b' = a - t' ----
vadd.s16 q4, q0, q8 @ a'[0..7] = a + t'
vsub.s16 q5, q0, q8 @ b'[0..7] = a - t'
@ ---- 阶段 5: 恒定时间条件约减至 [0, q) (掩码式, 无分支) ----
vcgt.s16 q6, q4, q15 @ mask = (a' > q) ? 0xFFFF : 0
vand.s16 q6, q6, q15 @ mask &= q
vsub.s16 q4, q4, q6 @ a' -= (a' > q) ? q : 0
vclt.s16 q6, q4, #0 @ mask = (a' < 0) ? 0xFFFF : 0
vand.s16 q6, q6, q15
vadd.s16 q4, q4, q6 @ a' += (a' < 0) ? q : 0
vcgt.s16 q6, q5, q15
vand.s16 q6, q6, q15
vsub.s16 q5, q5, q6
vclt.s16 q6, q5, #0
vand.s16 q6, q6, q15
vadd.s16 q5, q5, q6 @ b' 同样归约至 [0, q)
@ ---- 写回 (SoA 连续 16 字节, 无 stride) ----
vst1.16 {q4}, [r0]
vst1.16 {q5}, [r1]
bx lr该例程的关键设计点有三。8 路并行贯穿始终:从加载到约减到写回,所有向量指令均以 8 个 16 位 lane 为单位,无任何标量旁路;16 个 Q 寄存器被全部利用,寄存器压力处于架构上限但未溢出。宽度纪律:32 位中间量仅存在于 、、 三个阶段,且每个阶段结束立即窄化回 16 位,保证后续蝶形层的 lane 数不缩水。语义封闭:常数表以 Montgomery 域预乘(存储 ),约减输出的 恰好抵消预乘,蝶形结果在标准域与 Montgomery 域之间无缝衔接,各层之间无需额外的域转换开销。以 1.0 GHz 实测,该例程单次执行约 38 周期,256 点 NTT 共 1024 个蝶形、128 次例程调用,加循环与层间开销合计约 4,905 周期。
4.3 Montgomery 模约减的 NEON 实现:16-bit 溢出处理与 32-bit 展开
模约减是 NTT 向量化的数学枢纽。本文采用 Montgomery 约减:对 32 位积 ,计算
由于 ,,右移 16 位得到精确整数,且可证明 ,直接满足下一层蝶形的 16 位动态范围要求。该式在 NEON 上的实现难点有二,皆与 16 位数据通路的溢出语义有关。
难点一是低位截取的表示。 需要的是 的模 剩余,而 16 位 NEON 乘法的低位 16 位即模 结果,因此实现上先以 vmovn.i32 将 32 位 窄化为 16 位(截断即取模,算术上完全等价于 ,无需额外的掩码指令),再以无符号加宽乘法 vmull.u16 计算 的候选积。注意 为有符号数而截取的是无符号剩余,故 的乘法必须使用无符号形式(.u16),这是实现中容易出错却至关重要的细节。难点二是中间量宽度。 的幅度上界约为 ,远超 16 位表达范围,因此 必须展开为 32 位(vmull.u16 产出 32 位积),加法亦在 32 位域内完成(vadd.i32),最后以 vshr.s32 #16 算术右移并窄化回 16 位。整个约减在 8 路并行下仅消耗 8 条向量指令,其中 16 位窄化与 32 位展开的交替恰好构成了数据通路的自然节律,无需任何额外的溢出防护指令。
与 Barrett 约减的对比值得一提。Barrett 以预计算的乘数与右移近似完成除法,标量实现中指令序列略短,但每次约减需要两次 32 位乘法与一次移位,且其近似误差要求输入动态范围严格受控,否则需要修正分支——这在向量化语境下恰恰是劣势:两次乘法意味着两倍的 NEON 指令槽占用,动态范围失控则引入数据相关分支。Montgomery 以”预乘逆元 + 截断 + 右移”的固定序列完成约减,无分支、无修正,与 16 位数据通路的窄化语义天然契合,实测在同等工作量下比 Barrett 向量版本快约 9%。值得补充的还有约减强度的工程选择。Montgomery 约减保证输出落在 ,单次约减即可支撑一层蝶形;但实测表明,若允许系数在两层蝶形内保持 的”惰性”区间,可将约减频率减半,约减指令占比进一步下降。本文实现采用”每层一次完整约减 + 末层最终归约”的策略,在安全余量与指令密度之间取得平衡;该策略对侧信道的影响(恒定时间性)在第 7.1 节单独讨论。
4.4 双发射流水线调度:Load/Store 与向量算术交错隐藏延迟
顺序执行核心的软肋是访存延迟,而双发射结构给出了对策:让访存指令与向量算术在相邻周期配对发射,用算术的 4–5 周期执行时间掩护 vld1.16 的 3–4 周期 L1 命中延迟与更长的未命中延迟。实现上采用软件流水 + 双缓冲:外层循环在第 组蝶形执行算术的同时,预先发射第 组的 3 条 vld1.16,寄存器重命名由编译器与汇编手工共同完成。以下为外层循环的调度骨架(节选):
| 周期 | 端口 0(NEON 算术) | 端口 1(Load/Store/整数) |
|---|---|---|
| 1 | vmull.s16 q8, d2, d4 | vld1.16 {q0}, [r0, #16]! |
| 2 | vmull.s16 q9, d3, d5 | vld1.16 {q1}, [r1, #16]! |
| 3 | vmovn.i32 d20, q8 | vld1.16 {q2}, [r2, #16]! |
| 4 | vmovn.i32 d21, q9 | subs r3, r3, #1 |
| 5 | vmull.u16 q11, d20, d28 | bne ntt_loop |
| 6 | vmull.u16 q12, d21, d29 | (第 组加载已就绪) |
| … | (当前组算术继续执行) | (第 组数据在寄存器中等待) |
调度规则可以归纳为四条。配对原则:每周期优先将一条 vld1.16/vst1.16 与一条不依赖其结果的向量算术配对发射,使加载延迟被算术完全吸收。依赖距离:任何对刚写入寄存器的读取,其发射间隔至少 3 周期(覆盖 vmull 的 4–5 周期延迟),必要时通过交换两个 4-lane 组的处理顺序拉长依赖链。循环控制零开销化:subs/bne 作为整数指令与 NEON 指令双发射,回边预测良好,循环控制本身不占用额外的算术周期。展开倍数为 2:两层展开使预取窗口覆盖 32 字节,恰好跨入下一条缓存行边界之前,与第 5 章的预取策略衔接。实测中,软件流水将 8 路蝶形的平均周期从无调度的约 51 周期压至 38 周期,双发射重叠贡献约 25% 的收益——这是在指令数不变的前提下,纯粹由调度纪律换来的性能。
5. Cache 友好性与访存优化
5.1 64 字节 Cache Line 对齐
访存优化的第一层是地址对齐。Cortex-A7 的 L1 数据缓存行大小为 64 字节,而 NTT 的工作集(4 条多项式的 SoA 块、旋转常数表)以 16 字节为基本访问单位。若批次的起始地址未对齐到 64 字节,一个蝶形批次可能跨越两条缓存行,产生两次行填充(refill)开销,且跨行加载在顺序执行核心上会引入额外的地址计算与流水线气泡。对策是在 C 侧以显式属性强制对齐:
/* 64 字节缓存行对齐: 每个 8 路蝶形批次恰好落在一个缓存行内 */
__attribute__((aligned(64))) int16_t poly_a[4][256];
__attribute__((aligned(64))) int16_t poly_b[4][256];
__attribute__((aligned(64))) int16_t zeta_table[512];对齐的收益可定量描述:未对齐时,256 点 NTT 的 128 个蝶形批次中平均约 1.4 个批次跨行,每次跨行额外付出约 11 周期的 refill 停顿;对齐后跨行批次降为 0,且相邻批次共享缓存行(一行 64 字节恰好承载 4 个批次的输入),同一行的第二次访问命中变为零延迟。实测中,仅对齐一项即为完整 NTT 节省约 6% 周期。需要强调,aligned(64) 在数组声明层面即可保证,汇编侧无需额外的对齐检查指令——这是”布局即优化”原则的又一次体现。
5.2 预取指令(pld)与访存延迟隐藏
对齐解决的是行内效率,预取解决的是行间延迟。NTT 的访存模式在 SoA 布局下是完全规则的顺序流,这为软件预取提供了理想的预测性:下一批数据的地地址永远在当前地址 +16 字节处。利用 ARM 的 pld(Prefetch Load)指令,可在数据实际被使用之前 2–3 个批次提前发起取数,将 L1 未命中的 20–30 周期代价从关键路径上剥离:
/* 软件预取: 提前两个批次 (32 字节) 发起访存, 隐藏 L1 未命中延迟 */
__asm__ volatile("pld [%0, #32]" :: "r"(ptr_a + 16));
__asm__ volatile("pld [%0, #32]" :: "r"(ptr_b + 16));
__asm__ volatile("pld [%0, #32]" :: "r"(ptr_zeta + 16));预取距离的选择需要权衡:距离过近,预取请求尚未完成便已使用,无效;距离过远,则占用 L1 容量挤占工作集。实测对 1.0 GHz Cortex-A7 而言,提前 32 字节(两个批次)为最优,距离再远反而因 L2 带宽竞争而收益下降。还需注意 pld 是提示性指令,不产生异常且不影响架构状态,但其发射占用双发射的一个槽位——因此预取只应在访存指令与算术配对的空隙中插入,且仅对确定性顺序流使用,避免在分支密集的路径上浪费发射带宽。加入预取后,NTT 的 L1 未命中停顿从约 1,180 周期降至约 240 周期,成为第 5.3 节命中率跃升的直接来源。
5.3 L1 命中率提升实测:从约 72% 提升至 99%+
访存优化的总效果以 PMU 实测数据为证。利用 Cortex-A7 性能监视单元的标准事件——L1D_CACHE(0x04,L1 数据缓存访问)与 L1D_CACHE_REFILL(0x03,L1 行填充)——对 256 点 NTT 的完整执行做计数:
| 优化阶段 | L1 命中率 | 每蝶形平均 refill 次数 | NTT 周期数 |
|---|---|---|---|
| 基线(AoS,未对齐,无预取) | 约 72% | 0.42 | 7,124 |
| + SoA 布局重构 | 约 91% | 0.15 | 5,870 |
| + 64 字节对齐 | 约 95% | 0.07 | 5,520 |
| + pld 预取 | 99%+ | 0.02 | 4,905 |
命中率从约 72% 跃升至 99% 以上,其机理可分解为三层:布局重构消除了跨步访问,使每次 refill 的行填充都被 32 个系数充分消费;对齐消除了批次跨行;预取则将残余的强制性未命中(工作集首次加载)从关键路径剥离。还需要说明测量口径:命中率以 PMU 的 L1D_CACHE 与 L1D_CACHE_REFILL 事件之比计算,计数区间为完整 NTT 调用(含常数表遍历),排除了首次加载前的暖缓存过程,以反映稳态执行的真实访存行为。99%+ 意味着 256 点 NTT 的工作集(4 条多项式 8 KB + 常数表 1 KB,远小于 32 KB L1)在优化后几乎完全驻留于 L1,访存行为趋近于”寄存器文件”的确定性——这对于第 6 章的周期可预测性与第 7 章的恒定时间审计都是重要的工程基础。
6. 性能实测数据与对比分析
6.1 实测对比:参考 C vs GCC -O3 vs NEON 手写汇编
测试平台为 1.0 GHz Cortex-A7 双核 SoC(32 KB I-cache / 32 KB D-cache,256 KB L2),GCC 10.2 以 -O3 -mfpu=neon -mfloat-abi=hard 编译参考 C 与自动向量化版本,NEON 版本为本文第 4 章的手写汇编;周期数由 PMU 周期计数器(PMCCNTR)在热缓存条件下测得,吞吐以单核连续调用计。热点函数层面的对比:
| 热点函数 | 参考 C(周期) | GCC -O3(周期) | NEON 汇编(周期) | NEON vs 参考 C |
|---|---|---|---|---|
| 256 点 NTT | 38,412 | 21,870 | 4,905 | 7.8× |
| 多项式乘法(两次 NTT + 逐点乘 + 逆 NTT) | 118,640 | 66,240 | 13,320 | 8.9× |
| CBD 采样与多项式压缩 | 16,520 | 11,340 | 2,466 | 6.7× |
| ML-DSA 拒绝采样(签名路径) | 9,780 | 6,520 | 1,392 | 7.0× |
完整算法操作层面的吞吐对比(ops/s,单核):
| 操作 | 参考 C | GCC -O3 | NEON 汇编 | 加速比 |
|---|---|---|---|---|
| ML-KEM-768 密钥生成 | 912 | 1,608 | 7,035 | 7.7× |
| ML-KEM-768 封装 | 765 | 1,380 | 6,180 | 8.1× |
| ML-KEM-768 解封装 | 758 | 1,371 | 6,055 | 8.0× |
| ML-DSA-2 签名 | 318 | 578 | 2,480 | 7.8× |
| ML-DSA-2 验签 | 510 | 920 | 3,864 | 7.6× |
两组数据共同勾勒出 6.7–8.9 倍的加速区间:下限来自采样类短循环(6.7×),上限来自以多项式乘法为核心的长流程(8.9×),密钥协商与签名类完整操作集中在 7.6–8.1 倍。同时注意 GCC -O3 相对参考 C 已有 1.7–1.8 倍的提升,说明编译器对循环与常量传播的优化有效,但自动向量化未能突破约 2 倍的性能上限——其瓶颈在于 Montgomery 约减的窄化-展开交替无法被自动识别,这正是手写汇编的增量空间。
6.2 6.7-8.9 倍加速的来源分解
加速并非单一技术之功,而是四个正交维度的乘积。按贡献分解如下:
| 优化维度 | 贡献倍数 | 机理说明 |
|---|---|---|
| 8 路 SIMD 并行(NEON 蝶形) | 2.9× | 128 位寄存器同时处理 8 个系数,指令数摊薄至每路 3–4 条 |
| 访存优化(SoA 布局、对齐、预取) | 1.5× | L1 命中率 72% → 99%+,refill 停顿从关键路径剥离 |
| 分支消除与指令数缩减 | 1.7× | 掩码式约减、无数据相关分支、循环展开 |
| 双发射流水线调度 | 1.2× | Load/Store 与向量算术交错,隐藏访存延迟 |
| 合计 | 约 8.9× |
四个维度相互独立是分解成立的前提:SIMD 并行度由数据通路宽度决定,不依赖访存策略;访存优化作用于缓存层次,不改变指令流;分支消除改变控制流结构;调度则在不改变指令数的前提下压缩周期。正因如此,各贡献可以近似相乘而非相加——若以相加估计,四个维度合计仅约 7.3 倍,会显著低估真实收益。这一分解同时给出了可迁移性判断:在具备向量数据通路但缓存更小的平台(如 Cortex-M 系)上,访存维度的贡献会下降而并行度维度保持,优化重心的排序需要相应调整。
6.3 第三方实测验证声明
本文全部性能数据均经独立第三方实测验证。验证方式为:由独立第三方测试机构依据公开的测试方法(PMU 周期计数 + 单核吞吐基准)在同等 SoC 平台复测,周期数与吞吐量与本团队自测偏差小于 3%;测试脚本、编译器版本、SoC 型号、内核配置与复现步骤随验证报告一并公开,任何具备同等平台的团队均可复现。需要说明的测量约定包括:吞吐以单核、热缓存、L1 驻留工作集为条件;随机数输入来自经过验证的熵源(见第 7.2 节);报告不包含任何未公开的参数或环境调整。正微光电承诺上述数据口径与公开报告完全一致,并欢迎独立复现与交叉验证。
7. 侧信道防护与嵌入式落地
7.1 恒定时间实现:分支消除与掩码式条件选择
性能优化的收益不能以安全属性为代价。PQC 参考实现普遍以”正确性优先”编写,其中隐藏着两类时序侧信道:数据相关分支(拒绝采样、条件约减中的 if 语句)与数据相关的查表/运算路径。在 Cortex-A7 这类顺序执行核心上,分支预测失败约 13 周期的代价随数据分布变化,攻击者可通过统计解密/签名耗时反推系数取值——这正是 ML-KEM 解封装、ML-DSA 签名等私钥参与路径的经典攻击面。
本文实现的恒定时间策略有三条。分支消除:所有依赖数据的条件判断改写为向量比较+掩码选择。第 4.2 节汇编中的条件约减即范例——vcgt/vclt 生成全 1/全 0 掩码,vand 与 vadd/vsub 完成选择,控制流上不存在任何 bne 依赖数据的分支,执行时间与系数取值完全无关。运算路径统一:约减、窄化、乘加在每条 lane 上执行相同的指令序列,不因某个 lane 的值跳过任何操作;NEON 指令的延迟与操作数无关这一微架构事实(第 3.3 节)保证了指令级的时间均匀性。审计常态化:对私钥参与路径(密钥生成、解封装、签名)以 dudect 类时序检测框架做统计检验,实测各路径的时序方差低于检测阈值;同时以固定数量测试向量回归,防止后续编译器升级或代码变更引入隐蔽分支。采样环节的掩码式拒绝(第 2.3 节)与蝶形的掩码式约减共享同一套机制,使恒定时间属性在整个算法面而非单点成立。
7.2 与 QRNG 熵源的协同
恒定时间保证的是运算路径的均匀,随机数质量保证的则是密钥空间的均匀。PQC 算法的随机数敏感度极高:ML-KEM 的 CBD 采样直接由随机字节驱动,ML-DSA 的签名随机数(nonce 与展开采样)一旦可预测,攻击者即可从签名方程中恢复私钥——这与 ECDSA 的随机数事故同源。因此终端侧随机源必须满足密码学质量,且来源可审计。
正微光电的解决方案是量子随机数发生器(QRNG)与算法库的深度协同。终端或网关侧部署 QRNG 模组/板卡(量产产品以 1 Gbps 输出速率通过 NIST SP 800-90B 与 GM/T 0005 双标准的第三方实测验证),其输出经熵评估与调理后,作为 NIST SP 800-90A CTR_DRBG 的一次性种子注入,再由 DRBG 以流式方式供给算法库的全部采样需求:
这一架构的关键属性有三:种子一次性且不可预测,DRBG 的确定性输出不反哺熵源,杜绝”熵循环”;QRNG 的物理熵来自量子测量过程的本质随机性,不依赖系统噪声假设,熵源质量可度量、可审计;在无 QRNG 硬件的存量终端上,算法库提供降级路径——以 SoC 内置 TRNG 经 SP 800-90B 调理后供种,并在日志中标记熵源等级,供安全评估追溯。熵源与算法的协同,使第 6 章的吞吐数据在真实部署中具备可复现性,也使密钥轮换策略(如每日一次 ML-KEM 密钥对更新)在每秒数千次的协商能力下成为可行选项。
7.3 在正微光电 PQC IoT 算法库与 HyQrypt-Box 工控网关中的落地
以上优化并非实验室产物,而是正微光电 PQC IoT 算法库的正式组成。该算法库以模块化架构组织:NTT 内核、采样内核、哈希与打包模块分别以 NEON 汇编或经调优的 C 实现,对外提供与 liboqs 一致的 C ABI,应用层无需感知底层实现差异;针对不同算力档位提供编译期配置——完整 NEON 版(本文所述)、NEON 受限版(仅 NTT 向量化)与纯 C 回退版,使同一套 API 覆盖从 Cortex-M 到 Cortex-A 的全谱系终端。算法库已随固件通过 OTA 下发至存量终端,验证了”无硬件更换”的迁移路径的工程可行性。
在网关侧,算法库嵌入 HyQrypt-Box 工控网关。该网关作为工业现场的密码汇聚节点,承担 PLC 汇聚、边缘会话密钥协商与固件验签职责,其安全能力由算法库的实测数据直接支撑:ML-KEM-768 密钥生成 7,035 ops/s 意味着每秒可完成数千次密钥协商,满足多终端并发接入的握手吞吐;ML-DSA-2 签名 2,480 ops/s 支撑高频固件与配置包的签名发布;QRNG 熵源、恒定时间实现与第三方验证数据共同构成可交付的安全基线。从阿里巴巴量子安全团队延续而来的工程方法——威胁建模先行、热点剖析驱动、指令级实现、第三方验证闭环——在 HyQrypt-Box 的量产进程中经受住了现场部署的检验,也为更大规模的终端后量子迁移提供了可复制的技术底座。
8. 结论
8.1 指令集级优化的方法论总结
本文以 Cortex-A7 上的 ML-KEM/ML-DSA 优化为样本,完整走通了一条从威胁到实现的工程路径,其方法论可提炼为五个递进步骤。第一步,威胁与约束建模:以 HNDL 时间维度风险确立迁移必要性,以算力/内存/功耗三重约束划定优化边界,明确”无硬件更换”的软件前提。第二步,热点剖析驱动:以 PMU 数据而非直觉确定优化优先级——NTT 族 47%–81% 的占比决定了向量化的主攻方向,采样环节的分支结构决定了恒定时间改造的切入点。第三步,数学结构先于代码:在动手写汇编之前,先厘清 、、非完整 NTT 的二次因式分解与 Montgomery 常数,数学结构直接决定数据布局与指令选择。第四步,布局与调度并重:SoA 交织、64 字节对齐、软件预取解决访存,双发射配对与依赖距离管理解决时序,两者与指令选择同等重要——6.7–8.9 倍加速中,访存与调度合计贡献约 1.8 倍。第五步,安全属性内建:恒定时间与熵源质量不是优化完成后的补丁,而是与性能设计同步展开的约束条件,最终以第三方实测验证闭环。
这一方法论的价值在于可迁移性:将”热点剖析—数学结构—数据布局—指令调度—访存优化—侧信道内建—独立验证”的链条应用于其他平台,即可复现类似的收益——在具备 SIMD 数据通路但无缓存层次的平台(如 Cortex-M4 的 v7E-M DSP 扩展)上,并行度维度的贡献依旧成立;在 RISC-V 向量扩展(V 扩展)平台上,同一套 SoA 布局与约减策略可平移至 RVV 指令。后量子密码的落地难题,最终要靠这种”算法-微架构协同设计”的工程能力来破解。正微光电将持续以该能力服务电力采集、车联网与工业控制等长周期资产的后量子迁移,为海量 IoT 终端在量子时代保住最后一道密码防线。
9. 参考文献与延伸阅读
9.1 参考文献
- NIST FIPS 203, Module-Lattice-Based Key-Encapsulation Mechanism Standard (ML-KEM), 2024.
- NIST FIPS 204, Module-Lattice-Based Digital Signature Standard (ML-DSA), 2024.
- J. Bos, L. Ducas, E. Kiltz, T. Lepoint, V. Lyubashevsky, J. M. Schanck, P. Schwabe, G. Seiler, D. Stehlé, CRYSTALS-Kyber: a CCA-secure module-lattice-based KEM, IEEE S&P, 2018.(https://eprint.iacr.org/2017/634)
- L. Ducas, E. Kiltz, T. Lepoint, V. Lyubashevsky, P. Schwabe, G. Seiler, D. Stehlé, CRYSTALS-Dilithium: a lattice-based digital signature scheme, TCHES, 2018.(https://eprint.iacr.org/2017/633)
- P. L. Montgomery, Modular multiplication without trial division, Mathematics of Computation, 44(170), 1985.(https://www.ams.org/journals/mcom/1985-44-170/S0025-5718-1985-0777282-X/)
- ARM, ARM Architecture Reference Manual, ARMv7-A and ARMv7-R edition.
- ARM, NEON Programmer’s Guide, 2013.
- E. Alkim, P. Jakubeit, P. Schwabe, A new hope for ARM: NewHope on Cortex-M4, CHES, 2016.(https://eprint.iacr.org/2016/942)
- Open Quantum Safe project, liboqs: https://github.com/open-quantum-safe/liboqs
- pqm4: Post-quantum crypto library for the ARM Cortex-M4: https://github.com/mupq/pqm4
9.2 站内延伸阅读
- 格基密码与 NTT:后量子算法的心脏:从代数结构出发,系统讲解格基密码中 NTT 的数学原理与参数选择。
- 混合密钥交换的工程实践:经典-后量子混合密钥交换的协议设计与迁移策略。
- 量子随机数发生器(QRNG)技术全景:从量子光学原理到密码工程落地的 QRNG 完整技术脉络。