#签名验证 #ed25519密钥 #签名 #ed25519 #curve25519 #ecc

无std ed25519-dalek

纯Rust实现的快速高效ed25519 EdDSA密钥生成、签名和验证

32个版本 (5个稳定版)

2.1.1 2024年2月7日
2.1.0 2023年11月16日
2.0.0 2023年8月11日
2.0.0-rc.32023年6月24日
0.1.0 2016年12月9日

#42 in 加密学

Download history 334335/week @ 2024-04-21 293109/week @ 2024-04-28 296035/week @ 2024-05-05 348639/week @ 2024-05-12 337889/week @ 2024-05-19 343571/week @ 2024-05-26 350353/week @ 2024-06-02 322581/week @ 2024-06-09 306441/week @ 2024-06-16 303859/week @ 2024-06-23 257386/week @ 2024-06-30 304691/week @ 2024-07-07 318752/week @ 2024-07-14 337170/week @ 2024-07-21 335493/week @ 2024-07-28 347726/week @ 2024-08-04

1,357,099 每月下载量
用于 2,848 个crate(490个直接使用)

BSD-3-Clause

1.5MB
27K SLoC

ed25519-dalek CI

ed25519密钥生成、签名和验证的快速高效Rust实现。

使用

要导入ed25519-dalek,将以下内容添加到项目Cargo.toml的依赖关系部分:

ed25519-dalek = "2"

功能标志

此crate与#[no_std]兼容,默认default-features = false

功能 默认? 描述
alloc 当启用pkcs8时,为SigningKey/VerifyingKey分别实现EncodePrivateKey/EncodePublicKey
std SignatureError实现std::error::Error。同时启用alloc
zeroize SigningKey实现ZeroizeZeroizeOnDrop
rand_core 启用SigningKey::generate
batch 启用verify_batch,以便快速验证多个签名。同时启用rand_core
digest 启用 ContextSigningKey::{with_context, sign_prehashed}VerifyingKey::{with_context, verify_prehashed, verify_prehashed_strict} 以支持 Ed25519ph 预哈希签名
asm 在 SHA-512 压缩函数中启用汇编优化
pkcs8 启用 PKCS#8 序列化/反序列化以支持 SigningKeyVerifyingKey
pem 启用 PEM 序列化支持以支持 PKCS#8 私钥和 SPKI 公钥。同时也启用了 alloc
legacy_compatibility 不安全: 禁用某些签名检查。请参见以下内容
hazmat 不安全: 提供了 hazmat 模块以进行原始签名/验证。这些函数的误用将暴露私钥,如在签名预言机攻击中。

主要变更

有关过去版本中此crate所做的变更列表,请参阅CHANGELOG.md

2.0.0 中的重大变更

  • 将 MSRV 从 1.41 提升至 1.60.0
  • 提升 Rust 版本
  • signature 依赖项提升至 2.0
  • digest 设置为可选依赖项
  • zeroize 设置为可选依赖项
  • rand_core 设置为可选依赖项
  • 采用curve25519-backend 选择而不是功能
  • 使所有批验证确定性,删除 batch_deterministic (#256)
  • 删除 ExpandedSecretKey API (#205)
  • Keypair 重命名为 SigningKey,将 PublicKey 重命名为 VerifyingKey
  • hazmat 功能改为公开 ExpandedSecretKeyraw_sign()raw_sign_prehashed()raw_verify()raw_verify_prehashed()

文档

文档可在此处找到。

兼容性策略

此库的所有默认启用功能都受语义版本控制(SemVer)的保护。以下是关于 MSRV 和公共 API 的 SemVer 免疫条款的概述。

最低支持的 Rust 版本

版本 MSRV
2.x 1.60
1.x 1.41

从 2.x 开始,MSRV 的更改将伴随着小版本号的提升。

公共 API SemVer 免疫条款

影响公共 API 的 SemVer 免疫组件的破坏性更改将伴随着某些版本号的提升。

以下是具体策略

版本 公共 API 组件 策略
2.x digestpkcs8rand_core 依赖项 小版本号提升

安全性

ed25519-dalek 旨在防止滥用。签名操作是恒定时间的,所有签名密钥在超出作用域时都会被置零(除非禁用了 zeroize),脱离的公钥 不能 用于签名,并且提供了额外的功能,如 VerifyingKey::verify_strict,以避免已知的陷阱。

此外,这个包没有——实际上禁止——不安全代码。尽管如此,你可以选择使用位于 curve25519-dalek 中的某些高度优化的不安全代码。有关后端选择的更多信息,请参阅 下文

性能

性能是正确性、安全和清晰之后的次要目标,但我们的目标是与其他实现竞争。

基准测试

基准测试使用 criterion.rs 运行

cargo bench --features "batch"
# Uses avx2 or ifma only if compiled for an appropriate target.
export RUSTFLAGS='-C target_cpu=native'
cargo +nightly bench --features "batch"

在运行在库存状态下的英特尔 10700K 上,比较 curve25519-dalek 后端。

基准测试 u64 simd +avx2 fiat
签名 15.017 µs 13.906 µs -7.3967% 15.877 μs +5.7268%
签名验证 40.144 µs 25.963 µs -35.603% 42.118 μs +4.9173%
严格签名验证 41.334 µs 27.874 µs -32.660% 43.985 μs +6.4136%
批量签名验证/4 109.44 µs 81.778 µs -25.079% 117.80 μs +7.6389%
批量签名验证/8 182.75 µs 138.40 µs -23.871% 195.86 μs +7.1737%
批量签名验证/16 328.67 µs 251.39 µs -23.744% 351.55 μs +6.9614%
批量签名验证/32 619.49 µs 477.36 µs -23.053% 669.41 μs +8.0582%
批量签名验证/64 1.2136 ms 936.85 µs -22.543% 1.3028 ms +7.3500%
批量签名验证/96 1.8677 ms 1.2357 ms -33.936% 2.0552 ms +10.039%
批量签名验证/128 2.3281 ms 1.5795 ms -31.996% 2.5596 ms +9.9437%
批量签名验证/256 4.1868 ms 2.8864 ms -31.061% 4.6494 μs +11.049%
密钥对生成 13.973 µs 13.108 µs -6.5062% 15.099 μs +8.0584%

批量性能

如果你的协议或应用程序能够批量签名进行验证,那么 verify_batch 函数的性能将得到显著提高。

如你所见,每台机器都有一个最佳批量大小,因此你可能会想在自己的目标 CPU 上测试基准测试,以发现最佳大小。

(微)架构特定的后端

“后端”是指椭圆曲线和标量算术的实现。不同的后端有不同的用途。例如,如果你需要形式化验证的代码,你希望使用 fiat 后端(因为它是由 Fiat Crypto 生成的)。

有关后端选择详情和说明,请参阅 curve25519-dalek 文档

贡献

请参阅 CONTRIBUTING.md

批量签名验证

批签名验证的标准变体(即使用多个可能不同的公钥在多个可能不同的消息上制作的多个签名)可以通过 batch 功能获得。它使用确定性随机数,即,它使用 merlin(处理记录项分离)对输入进行哈希处理,并使用结果生成随机系数。批验证需要分配,因此在无堆栈设置中不会工作。

验证标准

签名方案的 验证标准 是签名和公钥必须满足的,以便被接受。不幸的是,Ed25519 有一些未定义的部分,导致不同的实现有不同的验证标准。有关这方面的详细介绍,请参阅 Henry 的帖子

在本节中,我们提到了一些关于我们的验证标准的具体细节以及如何处理它们。

可变性和 legacy_compatibility 功能

如果一个签名方案被认为会产生 可变签名,那么一个知道公钥 A、消息 m 和有效签名 σ' 的被动攻击者可以产生一个不同的 σ',使得 σ' 是关于 Am 的有效签名。一个方案只有在其攻击者可以 不知道A 对应的私钥的情况下做到这一点时才是可变的。

ed25519-dalek 不是一个可变签名方案。

尽管如此,一些其他的 Ed25519 实现是可变的,例如,启用 ED25519_COMPATlibsodiumed25519-donnaNaCl 的 ref10 实现,以及可能还有很多。如果您需要与这些实现交互并接受其他无效签名,您可以启用 legacy_compatibility 标志。如果您不必这样做,请不要启用 legacy_compatibility,因为它会使您的签名变得可变。

注意:CIRCL 完全没有标量范围检查。我们没有为与 CIRCL 接受的更大范围的 RFC 禁止的签名集进行交互的功能标志。

弱密钥伪造和 verify_strict()

签名伪造听起来就是:当攻击者,给定一个公钥 A,创建一个签名 σ 和消息 m,使得 σ 是关于 Am 的有效签名。由于这是任何签名方案的核心安全定义,因此 Ed25519 签名不能被伪造。

然而,Ed25519 允许一种更宽松的伪造方式,我们称之为 弱密钥伪造。攻击者可以产生一个特殊的公钥 A(我们称之为 公钥)和一个签名 σ,使得 σ 有很大概率是关于 A 的任何消息 m 的有效签名。这种攻击已在 Ed25519 论文 中得到确认,并导致了 Scuttlebutt 协议中的一个可利用的错误(论文,第 7.1 节)。VerifyingKey::verify() 函数允许弱密钥。

我们提供了 VerifyingKey::verify_strict(以及 verify_strict_prehashed)来帮助用户避免这些场景。这些函数对A进行额外的检查,确保它不是一个弱公钥。此外,我们还提供了VerifyingKey::is_weak,使用户在尝试签名验证之前能够执行此检查。

批量验证

如上所述,弱公钥可以以高概率生成未知消息的签名。这意味着有时一个伪造尝试会失败。实际上,它可能会失败高达7/8的时间。如果你对同一个失败的伪造调用两次verify(),它将两次返回错误,这是预期的。然而,如果你对两个不同但有效的批次分别调用两次verify_batch(),这两个批次都包含失败的伪造,那么其中一个失败而另一个成功的几率是21%。

为什么是这样?这是因为verify_batch()不执行verify_strict()的弱密钥测试,并且它将每个验证方程乘以某个随机系数。如果失败的伪造乘以8,那么弱密钥(这是一个低阶点)变为0,对尝试的伪造的验证方程将成功。

由于verify_batch()旨在具有高吞吐量,我们认为最好不要在其中放置弱密钥检查。如果您想防止批次中由于弱公钥引起的奇怪行为,您应该预先在输入上调用VerifyingKey::is_weak

依赖项

~0.5–1.6MB
~32K SLoC