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

无std ed25519-dalek-v2

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

2个版本 (1个稳定版)

2.1.1 2024年5月12日
2.0.0-rc.32024年5月11日

#843加密

每月50 次下载

BSD-3-Clause

125KB
1.5K SLoC

存档

此仓库已被 迁移。请将所有问题和拉取请求导向新仓库。

出于历史原因,此仓库将保持只读状态。


ed25519-dalek Rust

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

使用

稳定版

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

ed25519-dalek = "1"

测试版

要使用最新的预发布版本(见下方的更改 详情),请在项目的 Cargo.toml 中使用以下行

ed25519-dalek = "2.0.0-rc.3"

特性标志

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

特性 默认? 描述
alloc 当启用 pkcs8 时,分别实现了 EncodePrivateKey/EncodePublicKey 用于 SigningKey/VerifyingKey
std SignatureError 实现了 std::error::Error。同时启用了 alloc
zeroize SigningKey 实现了 ZeroizeZeroizeOnDrop
rand_core 启用了 SigningKey::generate
batch 启用了快速验证多个签名的 verify_batch。同时启用了 rand_core
digest 为 Ed25519ph 哈希签名启用了 ContextSigningKey::{with_context, sign_prehashed}VerifyingKey::{with_context, verify_prehashed, verify_prehashed_strict}
asm 在 SHA-512 压缩函数中启用了汇编优化。
pkcs8 SigningKeyVerifyingKey 启用了 PKCS#8 序列化和反序列化。
pem 启用了 PKCS#8 私钥和 SPKI 公钥的 PEM 序列化支持。同时启用了 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)https://github.com/dalek-cryptography/ed25519-dalek/pull/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 次要SemVer提升

安全性

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

此外,此crate没有也不允许使用不安全代码。您可以选择使用位于 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"

在运行在库存状态下的Intel 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和有效签名σ',则可以生成一个不同的σ',使得σ'是关于A的有效签名,则认为签名方案产生可塑签名。只有当攻击者可以在不知道与A对应的私钥的情况下做到这一点时,方案才是可塑的。

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

尽管如此,一些其他Ed25519实现是可塑的,例如libsodium(启用ED25519_COMPATed25519-donnaNaCl的ref10实现,以及可能还有很多其他。如果您需要与这些实现进行互操作并接受其他无效签名,可以启用legacy_compatibility标志。如果不需要,请不要启用legacy_compatibility,因为这会使您的签名变得可塑。

注意:CIRCL根本没有任何标量范围检查。我们没有为与CIRCL接受的较大集合的RFC不允许的签名进行互操作的功能标志。

弱密钥伪造和verify_strict()

签名伪造就像听起来一样:当攻击者,给定公钥A,创建一个签名σ和消息m,使得σ是关于A的有效签名时,就会发生签名伪造。由于这是任何签名方案的核心安全定义,因此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

依赖项

~1.7–2.8MB
~59K SLoC