Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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。

签名

  1. 计算 r = SHA-512(prefix || 消息) mod L。
  2. 生成临时点 R = r * B。
  3. 计算 s = (r + (SHA-512(R || A || 消息) mod L) * a) mod L。
  4. 输出 (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?

  1. 安全性
    • 确定性签名减少对每次签名时随机数的依赖,不能消除密钥生成与保管风险。
    • 抗侧信道攻击(恒定时间操作)。
  2. 性能
    • 实际性能取决于平台、库、编译参数和任务;不采用无基准条件的固定倍数。
    • 签名验证速度极快。
  3. 标准化
    • 被 IETF (RFC 8032)、OpenSSH、Solana 等广泛采用。

5. 注意事项

  • 私钥管理:必须安全存储种子(32 字节)。
  • 库的选择:优先使用审计过的库(如 ed25519-dalek)。
  • 不要自行实现密码学:直接使用标准库。

这个 TokenTransaction 仅用于本地消息签名练习,不是 Solana 或其他链的交易编码。正式支付协议需绑定发送者的公钥、网络、nonce 和有效期,并明确双方共享的序列化规则。

参考:RFC 8032、ed25519-dalek 2.1.1 API。