2个稳定版本
1.0.2 | 2021年2月19日 |
---|---|
1.0.1 | 2021年2月18日 |
#2006 in 密码学
1,472 monthly downloads
100KB
856 lines
ed25519-dalek
在Rust中快速高效的实现ed25519密钥生成、签名和验证。
文档
文档可在此处找到。
安装
要安装,请将以下内容添加到项目中的Cargo.toml
[dependencies.ed25519-dalek]
version = "1"
基准测试
在运行于3.30 GHz的Intel Skylake i9-7900X处理器上,未开启TurboBoost,此代码实现了以下性能基准
∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ cargo bench
Compiling ed25519-dalek v0.7.0 (file:///home/isis/code/rust/ed25519-dalek)
Finished release [optimized] target(s) in 3.11s
Running target/release/deps/ed25519_benchmarks-721332beed423bce
Ed25519 signing time: [15.617 us 15.630 us 15.647 us]
Ed25519 signature verification time: [45.930 us 45.968 us 46.011 us]
Ed25519 keypair generation time: [15.440 us 15.465 us 15.492 us]
通过启用avx2后端(在兼容的微架构的机器上),签名验证的性能得到了显著提升
∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ export RUSTFLAGS=-Ctarget_cpu=native
∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ cargo bench --features=avx2_backend
Compiling ed25519-dalek v0.7.0 (file:///home/isis/code/rust/ed25519-dalek)
Finished release [optimized] target(s) in 4.28s
Running target/release/deps/ed25519_benchmarks-e4866664de39c84d
Ed25519 signing time: [15.923 us 15.945 us 15.967 us]
Ed25519 signature verification time: [33.382 us 33.411 us 33.445 us]
Ed25519 keypair generation time: [15.246 us 15.260 us 15.275 us]
与之相比,Golang中的等效包表现如下
∃!isisⒶmistakenot:(master *=)~/code/go/src/github.com/agl/ed25519 ∴ go test -bench .
BenchmarkKeyGeneration 30000 47007 ns/op
BenchmarkSigning 30000 48820 ns/op
BenchmarkVerification 10000 119701 ns/op
ok github.com/agl/ed25519 5.775s
使密钥生成和签名平均快2倍,验证快2.5-3倍,具体取决于avx2的可用性。当然,这只是我的机器,这些结果——远远不够严谨——应该以少量盐来衡量。
转换为大致的周期数:我们乘以3.3的系数,将纳秒转换为3300 MHz CPU上的每秒周期数,验证为110256周期,签名为52618周期,这与手工优化的汇编实现具有竞争力。
此外,如果您正在使用rand
包中的CSPRNG,nightly
功能将启用那里的u128
/i128
功能,从而可能导致性能更快。
如果您的协议或应用能够批量签名以进行验证,则verify_batch()
函数的性能得到了显著提升。在上述Intel Skylake i9-7900X上,验证96个签名的批处理需要1.7673毫秒。这是18.4094微秒,或大约每签名验证60750周期,是原始论文中给出的批处理验证速度的两倍以上(这很可能不是一个公平的比较,因为那是一台Nehalem机器)。测试名称中/
之后的数字指的是批处理的大小
∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ export RUSTFLAGS=-Ctarget_cpu=native
∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ cargo bench --features=avx2_backend batch
Compiling ed25519-dalek v0.8.0 (file:///home/isis/code/rust/ed25519-dalek)
Finished release [optimized] target(s) in 34.16s
Running target/release/deps/ed25519_benchmarks-cf0daf7d68fc71b6
Ed25519 batch signature verification/4 time: [105.20 us 106.04 us 106.99 us]
Ed25519 batch signature verification/8 time: [178.66 us 179.01 us 179.39 us]
Ed25519 batch signature verification/16 time: [325.65 us 326.67 us 327.90 us]
Ed25519 batch signature verification/32 time: [617.96 us 620.74 us 624.12 us]
Ed25519 batch signature verification/64 time: [1.1862 ms 1.1900 ms 1.1943 ms]
Ed25519 batch signature verification/96 time: [1.7611 ms 1.7673 ms 1.7742 ms]
Ed25519 batch signature verification/128 time: [2.3320 ms 2.3376 ms 2.3446 ms]
Ed25519 batch signature verification/256 time: [5.0124 ms 5.0290 ms 5.0491 ms]
如您所见,每台机器都有一个最佳批次大小,因此您可能想测试目标CPU上的基准测试来发现最佳大小。对于这台机器,每个批次大约100个签名是最佳选择
此外,由于Rust语言,此实现既具有类型安全性,又具有内存安全性。它也比那些能读懂qhasm的人更容易被更多人阅读,使其更容易进行审计。我们认为,最终,这些特性——结合速度——比单纯周期数更有价值。
关于签名可塑性的说明
本库产生的签名是可塑性的,这在原始论文中有讨论。
虽然我们Signature
结构体的标量组件在从字节反序列化Signature
时执行了严格的非可塑性检查,但对于本库中所有类型的签名,由于群元素组件,仍然存在潜在的因可塑性而导致的问题。
我们可以通过乘以曲线系数来消除这种可塑性特性,但这会使我们的实现与现有的每个实现的行为不匹配。截至本文撰写之时,RFC 8032,“Edwards曲线数字签名算法(EdDSA)”,建议进行更强的检查。虽然我们同意应该进行更强的检查,但我们认为,不应该在事隔十年之后改变“ed25519验证”的定义,从而与每个其他实现不兼容。
但是,如果您需要这样做,请参阅verify_strict()
函数的文档,该函数对群元素进行了全面检查。此功能默认可用。
如果出于某种原因——尽管我们强烈建议您不要——您需要符合上面摘录的ed25519签名的原始规范,您可以通过--features='legacy_compatibility'
禁用标量可塑性检查。**我们强烈反对这样做**。
legacy_compatibility
特性
默认情况下,此库在签名反序列化时对签名的标量组件进行严格的可塑性检查。这个更严格的检查,即s < \ell
,其中\ell
是基点的阶,是由RFC8032规定的。然而,该RFC是在原始论文十年后才标准化的,如上所述,(通常错误地)指出可塑性无关紧要。
因此,大多数ed25519实现只执行有限的、更复杂的三位最高位标量未设置位的检查。如果您需要与旧实现兼容,包括
- ed25519-donna
- Golang的/x/crypto ed25519
- libsodium(仅在用
-DED25519_COMPAT
构建时) - NaCl的“ref”实现
- 可能还有其他一些
然后启用ed25519-dalek
的legacy_compatibility
特性。请注意,并警告您,这样做会允许签名可塑性,这意味着对于相同的消息和相同的密钥,可能存在两个不同但“有效”的签名,这在许多情况下都是极其危险的,包括但不限于身份验证协议和加密货币交易。
verify_strict()
函数
签名的标量分量并不是签名的唯一可变来源。用于签名验证的公钥以及签名中的群元素分量也是可变的,因为它们可能包含一个小的扭分量,这是由于曲线25519群不是素数阶,但有一个小的系数8。
如果您希望消除这种签名可变性的来源,请参阅verify_strict()
函数的文档。
关于随机数生成的说明
原始论文的规范和RFC8032标准的制定并没有明确指定如何生成随机数,除了使用加密安全随机数生成器(CSPRNG)。特别是在签名验证的情况下,安全性证明依赖于遮蔽因子/随机数的唯一性,因此这些随机样本必须对攻击者来说是不可猜测的。因此,当前在密码学家中逐渐形成了一种共识,即使用合成随机数更安全。
为了解释合成随机数,我们首先应该解释ed25519-dalek
如何处理生成确定性随机数。这种模式默认是禁用的,因为这种模式可能会让用户面临故障攻击,攻击者可以控制批量验证的所有输入(即公钥、签名和消息),以特定的方式构造它们来诱导故障(例如,在RAM中错误地翻转位,使处理器过热等)。在确定性模式下,我们通过创建一个基于公共输入的Fiat-Shamir变换的PRNG(伪随机数生成器)来生成我们的遮蔽因子/随机数。这种模式对于需要强大可审计性保证的协议以及没有访问到安全系统/芯片提供随机数的协议可能是有用的。此功能可以通过--features='batch_deterministic'
启用。请注意,我们不支持确定性签名,因为这存在许多陷阱,包括意外重复的随机数可能泄露密钥。
在默认模式下,我们像在完全确定性模式下那样做,但我们在基于系统/芯片提供的随机数的底层keccak-f1600函数(用于基于转写的PRNG)上进一步向前滚动。这提供了合成随机数,即基于确定性和非确定性数据的随机数。这样做的原因是为了防止不良的种子系统RNG破坏签名验证方案的安全性。
功能
#![no_std]
这个库旨在符合#![no_std]
规范。如果需要批量验证(--features='batch'
),请启用std
或alloc
功能之一。
夜间编译器
要使您的应用程序以默认启用夜间功能的ed25519-dalek
构建,请执行以下操作
[dependencies.ed25519-dalek]
version = "1"
features = ["nightly"]
要使您的应用程序在有人使用cargo build --features="nightly"
构建时启用夜间功能,请在Cargo.toml
中添加以下内容
[features]
nightly = ["ed25519-dalek/nightly"]
Serde
要启用serde支持,请使用serde
功能构建ed25519-dalek
。
(微)架构特定后端
默认情况下,ed25519-dalek
会针对curve25519-dalek
的u64_backend
特性进行编译,该特性使用Rust的i128
特性,其速度大约是u32_backend
特性的两倍。然而,当针对32位系统进行编译时,您可能希望使用以下命令编译:cargo build --no-default-features --features="u32_backend"
。如果您正在为支持avx2指令的机器编译,还可以使用实验性的simd_backend
,目前包括avx2或avx512后端。要使用它们,请使用以下命令编译:RUSTFLAGS="-C target_cpu=native" cargo build --no-default-features --features="simd_backend"
批量签名验证
通过batch
特性提供了批量签名验证的标准变体(即使用可能许多不同的公钥和可能许多不同的消息生成许多签名)。它使用合成的随机性,如上所述。
确定性批量签名验证
通过batch_deterministic
特性可以启用与上述相同的批量签名验证概念,但使用纯粹的确定性随机性。
依赖项
~2.2–3MB
~66K SLoC