ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Odin 密码学底层探秘:core/crypto/_fiat 有限域算术库的移植与设计解析

Odin 密码学底层探秘:core/crypto/_fiat 有限域算术库的移植与设计解析 Odin 密码学底层探秘core/crypto/_fiat 有限域算术库的移植与设计解析【免费下载链接】OdinOdin Programming Language项目地址: https://gitcode.com/GitHub_Trending/od/Odin本篇技术指南以 Odin 标准库core/crypto/_fiat包为主体深入剖析其从 MIT 形式化验证项目 fiat-crypto 移植而来的有限域field算术实现包括目录结构与 8 个字段模块的划分、公共辅助设施cmovznz、u1/i1、非饱和 limb 与 Montgomery 形式两种内部表示、恒定时间保证与许可证选择等关键设计决策并追踪它在x25519、x448、ed25519、ristretto255、poly1305以及 P-256/P-384 Weierstrass 曲线实现中的真实调用关系。读完本文你将掌握 Odin 密码学栈最底层的数学发动机是如何运转、如何被上层算法消费以及移植代码的可审计性设计思路。一、背景为什么需要_fiat这个下划线包在 core/crypto/_fiat/README.md 中作者开门见山地说明了这个包的定位This package contains low level arithmetic required to implement certain cryptographic primitives, ported from the fiat-crypto project along with some higher-level helpers.即该包提供实现特定密码学原语所需的低层算术移植自 [fiat-crypto 项目]并附带一些更高层的辅助函数。fiat-crypto 是一个通过 Coq 证明助手自动生成密码学算术代码的项目其生成的代码带有形式化验证formal verification保证。Odin 标准库并非直接把这些代码塞进来而是以 fiat-crypto 生成的Go 输出为蓝本用手工移植的方式适配到 Odin 语法。README 明确说明With the current Odin syntax, the Go output is trivially ported in most cases and was used as the basis of the port.也就是说Go 输出在大多数情况下可以轻而易举地移植到 Odin这是移植可行性的根本原因。作者同时坦言更理想的做法是在未来直接为 fiat-crypto 增加一个用 Coq 编写的 Odin 代码生成后端或者通过解析其 JSON 输出来自动生成 Odin 代码——目前的手工移植是折中方案。二、目录结构与模块划分8 个有限域覆盖主流曲线core/crypto/_fiat目录下除公共文件外包含 8 个字段模块每个模块负责一条曲线或标量域的有限域运算模块目录目标域内部表示文件服务对象field_curve25519/素数域Z/(2^255 − 19)field51.odin非饱和 51-bit limbsCurve25519 / X25519 / Ed25519field_curve448/素数域Z/(2^448 − 2^224 − 1)field51.odin非饱和 56-bit limbsCurve448 / X448field_p256r1/NIST P-256 素域Z/(2^256 − 2^224 2^192 2^96 − 1)field64.odinMontgomery 形式ECDSA / ECDH 的 P-256 实现field_p384r1/NIST P-384 素域field64.odinMontgomery 形式ECDSA / ECDH 的 P-384 实现field_poly1305/素数域Z/(2^130 − 5)field4344.odin非饱和 44-bit limbsPoly1305 消息认证码field_scalar25519/Ed25519 标量域field64.odinEd25519 标量运算field_scalarp256r1/P-256 标量域field64.odinP-256 标量运算field_scalarp384r1/P-384 标量域field64.odinP-384 标量运算每个模块目录下的field.odin是平台无关的字段元素辅助层而带数字后缀的文件field51.odin、field64.odin、field4344.odin才是从 fiat-crypto 直接移植的核心算术内核。文件名中的数字即内部 limb 位宽51、64、44、56直接暴露了不同域采用的不同表示策略详见第四节。从源码结构看所有 8 个模块的field.odin都通过import fiat core:crypto/_fiat引入公共设施并在文件头保留 fiat-crypto 的 BSD-1-Clause 许可证声明见 field_curve25519/field51.odin。三、公共设施fiat.odin 中的类型与恒定时间条件移动core/crypto/_fiat/fiat.odin 是所有字段模块共享的公共底座全文不足 30 行包含三样东西1. 二补数系统断言// This code only works on a twos complement system. #assert((-1 3) 3)这是编译期断言确保代码只在二补数twos complement表示的平台上编译——因为后续大量位运算技巧如~x1取反、(u64(arg1) * 0xffffffffffffffff)生成全 1 掩码都依赖二补数语义。2. 布尔进位类型u1 :: distinct u8 i1 :: distinct i8fiat-crypto 生成的代码大量使用 1-bit 的进位/借位/选择信号。这里用distinct声明独立的u1/i1类型避免与普通u8/i8混用并保持与 Go 输出中uint1/int1语义的对应。3. 恒定时间条件移动cmovznz(optimization_mode none) cmovznz_u64 :: proc contextless (arg1: u1, arg2, arg3: u64) - (out1: u64) { x1 : (u64(arg1) * 0xffffffffffffffff) x2 : ((x1 arg3) | ((~x1) arg2)) out1 x2 return }cmovznzconditional move if not zero是恒定时间编程的基础原语当arg1 ! 0时返回arg3否则返回arg2。实现不依赖任何分支而是通过全 1 掩码的位运算完成选择从而避免因数据值不同而泄露时序信息。同名cmovznz_u32提供 32 位版本。注意两个关键注解contextless声明该过程不依赖 Odin 运行时上下文可在无运行时环境下使用便于内联进热路径(optimization_mode none)关闭优化。这是刻意为之——防止编译器把看似恒定时间的位运算序列优化成条件分支如cmov指令或跳转从而破坏时序安全保证。四、两种内部表示非饱和 limb 与 Montgomery 形式这是_fiat包最核心的设计差异。从文件名即可看出分两条技术路线4.1 非饱和unsaturatedlimb 表示以 Curve25519 的field_curve25519/field51.odin为例文件头明确写出其目标域与策略见 field_curve25519/field51.odinThe file provides arithmetic on the field Z/(2^255-19) using unsaturated 64-bit integer arithmetic.域元素用[5]u64数组表示每个 64 位槽位只饱和使用 51 位故称 51-bit limbs255 5 × 51Loose_Field_Element :: distinct [5]u64 Tight_Field_Element :: distinct [5]u64非饱和表示的核心思想是留出进位余量。中间乘积的累积不会立即溢出 64 位从而可以推迟规范化carry步骤减少进位传播的次数。核心运算fe_carry_mul借助 core/math/bits 中的bits.mul_u64获取 128 位乘积的高低位并以0x13即 19域模数常数做约减。同样Curve448 的field51.odin使用 56-bit limbs448 8 × 56Poly1305 的field4344.odin则使用 44-bit limbs130 3 × 44 − 2含额外空间。4.2 Montgomery 形式field64以field_p256r1/field64.odin为例见 field_p256r1/field64.odinThe file provides arithmetic on the field Z/(2^256 - 2^224 2^192 2^96 - 1) using a 64-bit Montgomery form internal representation.这里每个 64 位槽位是饱和的saturated但元素以 Montgomery 域表示乘法的代价被 Montgomery 约减取代约减过程利用 P-256 特殊形式的模数常数如0xffffffff00000001代码中显式给出ELL :: [4]u64{...}表示饱和形式的域阶。文件头还特别警告WARNING: While big-endian is the common representation used for this curve, the fiat output uses least-significant-limb first.即虽然 P-256 惯例上使用大端表示但 fiat-crypto 的输出是低 limb 在前移植时保持了这个约定调用方需要留意。4.3 Loose 与 Tight推迟规范化的二元表示在非饱和实现中所有字段模块都定义了Loose_Field_Element与Tight_Field_Element两个 distinct 数组类型见 field_curve25519/field.odin并通过fe_relax_cast/fe_tighten_cast在两者间做零成本指针转换fe_relax_cast :: #force_inline proc contextless ( arg1: ^Tight_Field_Element, ) - ^Loose_Field_Element { return (^Loose_Field_Element)(arg1) }其语义是Tight规范元素各 limb 已完全规范化可直接序列化/比较也可安全参与输入运算Loose宽松元素允许 limb 存在未规整的脏进位用于承接乘法、加法等会产生中间膨胀的运算减少反复 carry 的开销。field.odin中的辅助函数围绕这一思想设计例如fe_carry_add先以宽松形式做加法、再fe_carry收紧fe_carry_mul完成乘法后输出 Tight 结果。此外还提供了一批上层便捷过程覆盖反序列化、比较与常用常数fe_from_bytes字节反序列化自动屏蔽 Curve25519 场元素最高位的未使用 bittmp1[31] 127并在结束后用crypto.zero_explicit擦除临时缓冲fe_to_bytes/fe_is_negative/fe_equal/fe_equal_bytes序列化、符号判定与恒定时间比较底层用crypto.compare_constant_timefe_zero/fe_one/fe_set/fe_carry_opp/fe_carry_abs常数、取反、绝对值fe_carry_pow2k计算元素的2^k次幂通过反复平方fe_carry_sqrt_ratio_m1实现 RFC 9496 §4.2 的SQRT_RATIO_M1(u, v)平方根比值运算基于 Monocypher 的逆平方根思路返回符号是否正确的标志是 Edwards 点压缩/解压缩的关键fe_carry_inv利用平方根比值与费马小定理组合实现求逆fe_cond_swap/fe_cond_select/fe_cond_negate恒定时间条件交换/选择/取反同样带(optimization_mode none)防止被优化为分支。五、恒定时间保证及其边界README 对这一安全性质给出了精确的边界条件The routines are intended to be timing-safe, as long as the underlying integer arithmetic is constant time. This is true on most systems commonly used today, with the notable exception of WASM.即只要底层整数运算在目标平台上恒定时间这些例程就具备时序安全。这在当今大多数常见系统上成立WASM 是明确例外WASM 的 64 位乘法/除法可能编译为运行时库调用无法保证恒定时间。恒定时间策略在代码中层层可见无分支数据选择cmovznz_u64、fe_cond_select全部使用掩码位运算无分支条件交换fe_cond_swap通过异或掩码实现 swap关闭优化所有依赖时序安全的敏感过程都标记(optimization_mode none)敏感临时值显式擦除所有包含中间状态的[32]byte缓冲与临时字段元素在退出前都调用zero_explicit清零防止敏感数据残留栈上。六、移植策略与许可可审计性优先README 记录了三个重要的工程决策1. 许可选择BSD-1-Clausefiat-crypto 为派生作品提供了 3 种许可证选项本包选择了 1-Clause BSD 许可因为它与 Odin 现有的许可证兼容见 README 的 Notes 一节。这一选择在 field_curve25519/field51.odin 等每个移植文件的头部都保留了完整的版权声明Copyright (c) 2015-2020 the fiat-crypto authors。2. 仅移植 64 位版本fiat-crypto 同时产出面向 32 位与 64 位架构的输出但本包只采用 64 位版本理由是32 位架构正变得越来越罕见且无关紧要。3. 最小化改动、保持可审计README 明确写道For the most part, alterations to the base fiat-crypto generated code was kept to a minimum, to aid auditability. This results in a somewhat idiosyncratic style, and in some cases minor performance penalties.即为便于审计对 fiat-crypto 生成代码的改动被控制在最小范围这导致代码风格有些特立独行并且在某些情况下带来轻微性能损失——这是可审计性与性能之间有意做出的权衡。同时 README 也坦诚了形式化验证的边界As this is a port rather than autogenerated output, none of fiat-cryptos formal verification guarantees apply, unless it is possible to prove binary equivalence.fiat-crypto 原始输出的正确性是有 Coq 证明的而本包是手工移植除非能证明二进制等价否则 fiat-crypto 的形式化验证保证不再适用。各 field64/field51 文件头也重复了这一声明如 field_p256r1/field64.odinWhile the base implementation is provably correct, this implementation makes no such claims as the port and optimizations were done by hand.。七、上层消费方谁在调用这些字段算术通过仓库内的导入关系可以确认_fiat的真实消费链路搜索import ... core:crypto/_fiat/...的命中结果core/crypto/x25519/x25519.odinimport field core:crypto/_fiat/field_curve25519。X25519 的标量乘核心_scalarmult直接操作field.Tight_Field_Element与field.Loose_Field_Element用fe_from_bytes反序列化基点、fe_cond_swap做恒定时间条件交换、fe_carry_mul/fe_carry_square驱动 Montgomery 阶梯ladder最终调用fe_invert完成投影坐标的除法恢复。core/crypto/_edwards25519/edwards25519.odinimport field core:crypto/_fiat/field_curve25519Ed25519 的点加、倍点、压缩解压全部建立在 Curve25519 素域之上其标量运算则使用field_scalar25519。core/crypto/ristretto255/ristretto255.odinRistretto255 的解码/编码与SQRT_RATIO_M1强相关fe_carry_sqrt_ratio_m1正是为此准备同样消费field_curve25519。core/crypto/x448/x448.odinX448 对称地消费field_curve448。core/crypto/poly1305/poly1305.odin使用field_poly1305实现Z/(2^130 − 5)上的累积运算。core/crypto/_weierstrass/fe.odin统一导入field_p256r1与field_p384r1为 P-256/P-384 的 ECDSA/ECDH经由core/crypto/ecdsa、core/crypto/ecdh提供 Montgomery 域字段元素_weierstrass/sc.odin则消费field_scalarp256r1与field_scalarp384r1处理标量。从这一消费图谱可以清晰看出设计意图_fiat是整棵 Odin 密码学树的树根——所有基于椭圆曲线与 Poly1305 的公开 API 都最终落到这 8 个字段模块的有限域运算上。因此理解_fiat就等于理解了 Odin 密码学实现的数学底层。八、正确性验证与后续演进虽然_fiat本身不带独立的测试文件但仓库 tests/core 中的密码学测试套件覆盖了其上层的全部公开 API包括 X25519/X448 的 RFC 7748 测试向量、Ed25519、Ristretto255、Poly1305 以及 P-256/P-384 的 ECDSA/ECDH 测试间接验证了底层字段算术的正确性。感兴趣的读者可以在 core/crypto/README.md 与 tests/core 中找到完整测试入口。关于未来演进源码注释还留下了两个 TODO见 field_curve25519/field51.odin 与 field_curve448/field51.odinWhen fiat-crypto supports it, using a saturated 64-bit limbs instead of 51-bit limbs will be faster, though the gains are minimal unless adcx/adox/mulx are used.即一旦 fiat-crypto 支持饱和 64-bit limbs 输出迁移过去会更快——但收益很小除非在目标平台启用adcx/adox/mulx指令。README 也展望了通过 Coq 后端或 JSON 解析自动生成 Odin 代码的长期目标。这表明该包正处在一个手工移植保证当下可用、自动生成保证长期演进的过渡阶段。结语core/crypto/_fiat是 Odin 标准库中一个刻意保持低调却至关重要的包它没有公开的面向用户的 API却承载了 X25519、X448、Ed25519、Ristretto255、Poly1305 与 NIST P-256/P-384 等全部主流密码学原语的数学根基。通过 fiat-crypto 的 Go 输出移植、BSD-1-Clause 许可、最小化改动以保可审计、非饱和 limb 与 Montgomery 形式两种表示、以及optimization_mode none与zero_explicit层层构筑的恒定时间纪律它为上层密码学栈提供了正确性可追溯、时序可预期、审计可执行的底层基础。理解这个包是深入 Odin 密码学实现的最佳切入点。【免费下载链接】OdinOdin Programming Language项目地址: https://gitcode.com/GitHub_Trending/od/Odin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进