#ed25519-key #signature-verification #ed25519 #signature #curve25519 #ecc

无需std test-ed25519-dalek

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

3个版本

2.0.0-pre.32023年2月2日
2.0.0-pre.12023年1月5日

#12 in #ed25519-key

BSD-3-Clause

105KB
1K SLoC

ed25519-dalek Rust

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

使用

稳定

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

ed25519-dalek = "1"

测试版

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

ed25519-dalek = "2.0.0-pre.0"

功能标志

此包与#[no_std]兼容,默认情况下不启用功能default-features = false

功能 默认? 描述
alloc 当启用pkcs8时,为SigningKeyVerifyingKey分别实现EncodePrivateKeyEncodePublicKey
std SignatureError实现std::error::Error。同时启用alloc
zeroize SigningKey实现ZeroizeZeroizeOnDrop
rand_core 启用SigningKey::generate
batch 启用 verify_batch 以快速验证多个签名。同时启用 rand_core
摘要 启用 ContextSigningKey::{with_context, sign_prehashed}VerifyingKey::{with_context, verify_prehashed, verify_prehashed_strict} 以支持 Ed25519ph 预哈希签名
汇编 在 SHA-512 压缩函数中启用汇编优化
pkcs8 启用 SigningKeyVerifyingKeyPKCS#8 序列化/反序列化
pem 启用 PKCS#8 私钥和 SPKI 公钥的 PEM 序列化支持。同时启用 alloc
向后兼容 不安全: 禁用某些签名检查。详见 下面

主要变更

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

2.0.0 版本的破坏性变更

  • 将 MSRV 从 1.41 提升至 1.60.0
  • 提升 Rust 版本
  • signature 依赖项提升到 2.0
  • 使 curve25519-backend 选择 更自动化
  • digest 设置为可选依赖项
  • zeroize 设置为可选依赖项
  • rand_core 设置为可选依赖项
  • 使所有批量验证确定化,移除 batch_deterministic (#256)
  • 移除 ExpandedSecretKey API ((#205)https://github.com/dalek-cryptography/ed25519-dalek/pull/205)
  • Keypair 重命名为 SigningKey,将 PublicKey 重命名为 VerifyingKey

文档

文档可在 此处 获取。

兼容性策略

此库的所有默认启用功能均受 语义版本控制 (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 以避免已知问题。

此外,此crate没有任何——事实上禁止——不安全代码。您可以选择使用位于 curve25519-dalek 中的某些高度优化的不安全代码。有关后端选择的更多信息,请参阅 下面

性能

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

基准测试

基准测试使用criterion.rs运行。

cargo bench --features "batch"
# Uses avx2 or ifma only if compiled for an appropriate target.
export RUSTFLAGS='--cfg curve25519_dalek_backend="simd" -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 +14.188%
签名验证 40.144 µs 25.963 µs -35.603% 42.118 µs +62.758%
严格签名验证 41.334 µs 27.874 µs -32.660% 43.985 µs +57.763%
批签名验证/4 109.44 µs 81.778 µs -25.079% 117.80 µs +43.629%
批签名验证/8 182.75 µs 138.40 µs -23.871% 195.86 µs +40.665%
批签名验证/16 328.67 µs 251.39 µs -23.744% 351.55 µs +39.901%
批签名验证/32 619.49 µs 477.36 µs -23.053% 669.41 µs +39.966%
批签名验证/64 1.2136 ms 936.85 µs -22.543% 1.3028 ms +38.808%
批签名验证/96 1.8677 ms 1.2357 ms -33.936% 2.0552 ms +66.439%
批签名验证/128 2.3281 ms 1.5795 ms -31.996% 2.5596 ms +61.678%
批签名验证/256 4.1868 ms 2.8864 ms -31.061% 4.6494 ms +61.081%
密钥对生成 13.973 µs 13.108 µs -6.5062% 15.099 µs +15.407%

批处理性能

如果您的协议或应用能够批量签名进行验证,verify_batch函数将大幅提高性能。

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

特定于(微)架构的后端

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

后端选择细节和说明可以在curve25519-dalek文档中找到。

贡献

请参阅CONTRIBUTING.md

批签名验证

标准的批签名验证变体(即使用许多不同的公钥和许多不同的消息生成的许多签名)可以通过batch特性获得。它使用确定性随机数,即它对输入进行哈希处理(使用merlin,它处理转储项分离)并使用结果生成随机系数。批验证需要分配,因此在无堆栈环境中无法使用。

验证标准

签名方案的验证标准是签名和公钥必须满足的准则,以便被接受。不幸的是,Ed25519有一些未指明部分,导致在不同实现中存在不同的验证标准。关于这个问题的极好概述,请参阅Henry的帖子

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

可剪裁性和legacy_compatibility特性

如果被动攻击者知道公钥 A、消息 m 和有效签名 σ',并且能够生成一个不同的 σ',使得 σ' 是相对于 A 的有效签名,则认为签名方案会产生 可变签名。只有当攻击者能够在不知道与 A 对应的私钥的情况下做到这一点时,方案才是可变的。

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

尽管如此,一些其他 Ed25519 实现是可变的,例如libsodium 在启用 ED25519_COMPAT 的情况下ed25519-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–3MB
~62K SLoC