Ed25519:现代数字签名算法的原理与应用(附 Rust 实现)
请务必阅读此小章关于密码学
-
推荐链接 偏通识
-
不太推荐但是放上来的链接 需要丰富的密码学相关经验
1. 什么是 Ed25519?
Ed25519 是一种基于 Edwards-curve Digital Signature Algorithm (EdDSA) 的高效签名方案,使用与 Curve25519 双有理等价的 edwards25519 曲线。其特点包括:
- 128 位经典安全性(不抗量子计算)。
- 确定性签名:签名的临时标量由密钥材料和消息确定;密钥生成仍需要安全随机数。
- 64 字节签名:紧凑且易于处理。
- 高性能:比 RSA 和传统 ECDSA 更快。
2. 核心原理
密钥生成
- 私钥:32 字节随机种子(通常来自 CSPRNG)。
- 公钥:对种子做 SHA-512;按 RFC 8032 修剪前 32 字节并解释为标量
a,计算A = a * B。后 32 字节用作签名的秘密前缀prefix。
签名
- 计算
r = SHA-512(prefix || 消息) mod L。 - 生成临时点
R = r * B。 - 计算
s = (r + (SHA-512(R || A || 消息) mod L) * a) mod L。 - 输出
(R, s)(共 64 字节)。
验证
检查:s * B == R + (SHA-512(R || A || 消息) mod L) * A。
以上为群运算示意。正式验证还包含点与标量编码、范围和余因子等检查,应交给符合 RFC 8032 的库。
3. Rust 实现示例
以下示例固定使用 ed25519-dalek 2.1.1。将步骤 2、3、4 的 Rust 代码按顺序合并到 src/main.rs,依赖放入一个 edition 2021 的 Cargo 项目,再运行 cargo run。
步骤 1:添加依赖
# Cargo.toml
[dependencies]
ed25519-dalek = { version = "=2.1.1", features = ["rand_core"] }
rand_core = { version = "=0.6.4", features = ["getrandom"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
步骤 2:生成密钥对
#![allow(unused)]
fn main() {
use ed25519_dalek::{SigningKey, VerifyingKey, Signer, Verifier, Signature};
use rand_core::OsRng;
fn generate_keypair() -> SigningKey {
// 从安全随机源生成密钥对
let mut csprng = OsRng;
SigningKey::generate(&mut csprng)
}
}
步骤 3:签名与验证
#![allow(unused)]
fn main() {
fn sign_and_verify() {
// 1. 生成密钥对
let keypair = generate_keypair();
let public_key = keypair.verifying_key();
// 2. 签名一条消息(例如代币交易)
let message = b"Transfer 100 tokens to Alice";
let signature: Signature = keypair.sign(message);
// 3. 验证签名
match public_key.verify(message, &signature) {
Ok(_) => println!("Signature is valid!"),
Err(_) => println!("Signature is invalid!"),
}
}
}
步骤 4:完整代币交易示例
#[derive(serde::Serialize)]
struct TokenTransaction {
sender: String,
receiver: String,
amount: u64,
}
impl TokenTransaction {
fn sign(&self, keypair: &SigningKey) -> Signature {
let message = serde_json::to_vec(self).unwrap();
keypair.sign(&message)
}
fn verify(&self, signature: &Signature, public_key: &VerifyingKey) -> bool {
let message = serde_json::to_vec(self).unwrap();
public_key.verify(&message, signature).is_ok()
}
}
fn main() {
let keypair = generate_keypair();
let transaction = TokenTransaction {
sender: "Bob".to_string(),
receiver: "Alice".to_string(),
amount: 100,
};
sign_and_verify();
// 签名交易
let signature = transaction.sign(&keypair);
// 验证交易
let is_valid = transaction.verify(&signature, &keypair.verifying_key());
assert!(is_valid);
let mut changed = transaction;
changed.amount += 1;
assert!(!changed.verify(&signature, &keypair.verifying_key()));
println!("签名通过;修改金额后验证失败");
}
4. 为什么选择 Ed25519?
- 安全性
- 确定性签名减少对每次签名时随机数的依赖,不能消除密钥生成与保管风险。
- 抗侧信道攻击(恒定时间操作)。
- 性能
- 实际性能取决于平台、库、编译参数和任务;不采用无基准条件的固定倍数。
- 签名验证速度极快。
- 标准化
- 被 IETF (RFC 8032)、OpenSSH、Solana 等广泛采用。
5. 注意事项
- 私钥管理:必须安全存储种子(32 字节)。
- 库的选择:优先使用审计过的库(如
ed25519-dalek)。 - 不要自行实现密码学:直接使用标准库。
这个 TokenTransaction 仅用于本地消息签名练习,不是 Solana 或其他链的交易编码。正式支付协议需绑定发送者的公钥、网络、nonce 和有效期,并明确双方共享的序列化规则。