Skip to main content

encryption/asymmetric/
ed25519.rs

1// Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz
2
3//! Ed25519 签名 / 验签(RFC 8032)。
4//!
5//! 十六进制约定:
6//! - 私钥:32 字节 seed hex(64 位,即 ed25519-dalek `SigningKey` 的序列化形式);
7//! - 公钥:32 字节裸公钥 hex(64 位,压缩 Edwards-y 编码);
8//! - 签名:64 字节定长 hex(128 位)。
9//!
10//! 签名不消耗随机源(Ed25519 本身确定性);验签走 ed25519-dalek 的
11//! `verify_strict`:RFC 8032 判定之外,额外拒绝**小阶公钥、小阶 R** 与非规范
12//! R 编码——公钥若来自不可信来源,普通 `verify` 允许小阶公钥使任意消息
13//! 「验签通过」,本库按防御姿态选择严格模式(诚实签名方不受影响)。
14
15use ed25519_dalek::{Signature, Signer as _, SigningKey, VerifyingKey};
16use rand_core::OsRng;
17
18use crate::asymmetric::KeyPairHex;
19use crate::contract::{Identified, Signer};
20use crate::error::{Error, Result};
21use crate::internal::{hex_decode, hex_encode};
22
23const LABEL: &str = "Ed25519";
24
25/// Ed25519 签名器(实现 [`crate::contract::Signer`])。
26#[derive(Debug, Default, Clone, Copy)]
27pub struct Ed25519Signer;
28
29impl Ed25519Signer {
30    pub const IDENTIFIER: &'static str = "ed25519";
31
32    pub fn new() -> Self {
33        Self
34    }
35
36    /// 对消息签名,返回 64 字节定长签名 hex。
37    fn sign_hex(message: &[u8], private_key_hex: &str) -> Result<String> {
38        let seed =
39            parse_32_bytes(private_key_hex).map_err(|_| Error::InvalidKey { label: LABEL })?;
40        let signing_key = SigningKey::from_bytes(&seed);
41        Ok(hex_encode(&signing_key.sign(message).to_bytes()))
42    }
43
44    /// 验签;格式错误、密钥非法、签名不对一律 [`Error::VerificationFailed`]。
45    fn verify_hex(message: &[u8], signature_hex: &str, public_key_hex: &str) -> Result<()> {
46        let public_bytes = parse_32_bytes(public_key_hex)
47            .map_err(|_| Error::VerificationFailed { label: LABEL })?;
48        let verifying_key = VerifyingKey::from_bytes(&public_bytes)
49            .map_err(|_| Error::VerificationFailed { label: LABEL })?;
50        let signature_bytes =
51            hex_decode(signature_hex).map_err(|_| Error::VerificationFailed { label: LABEL })?;
52        let signature = Signature::from_slice(&signature_bytes)
53            .map_err(|_| Error::VerificationFailed { label: LABEL })?;
54        verifying_key
55            .verify_strict(message, &signature)
56            .map_err(|_| Error::VerificationFailed { label: LABEL })
57    }
58
59    /// 生成密钥对:私钥 = seed hex,公钥 = 裸公钥 hex;随机源为系统 CSPRNG。
60    pub fn generate_key_pair_hex() -> Result<KeyPairHex> {
61        let signing_key = SigningKey::generate(&mut OsRng);
62        Ok(KeyPairHex {
63            private_key_hex: hex_encode(&signing_key.to_bytes()),
64            public_key_hex: hex_encode(signing_key.verifying_key().as_bytes()),
65        })
66    }
67}
68
69/// 十六进制 → 定长 32 字节(seed / 公钥都是 32 字节)。
70fn parse_32_bytes(hex: &str) -> Result<[u8; 32]> {
71    hex_decode(hex)?.try_into().map_err(|_| Error::InvalidHex)
72}
73
74impl Identified for Ed25519Signer {
75    fn identifier(&self) -> &str {
76        Self::IDENTIFIER
77    }
78}
79
80impl Signer for Ed25519Signer {
81    fn sign(&self, message: &[u8], private_key_hex: &str) -> Result<String> {
82        Self::sign_hex(message, private_key_hex)
83    }
84
85    fn verify(&self, message: &[u8], signature_hex: &str, public_key_hex: &str) -> Result<()> {
86        Self::verify_hex(message, signature_hex, public_key_hex)
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    /// 小阶公钥(identity 压缩点 `01 00…00`)必须被拒绝:普通 `verify` 下它会令
95    /// 任意消息「验签通过」,`verify_strict` 拦住(本测试防回归回普通 verify)。
96    #[test]
97    fn small_order_public_key_is_rejected() {
98        const IDENTITY_PK: &str =
99            "0100000000000000000000000000000000000000000000000000000000000000";
100        let signer = Ed25519Signer::new();
101        let dummy_signature = "00".repeat(64);
102        assert_eq!(
103            signer
104                .verify(b"any message", &dummy_signature, IDENTITY_PK)
105                .unwrap_err(),
106            Error::VerificationFailed { label: LABEL }
107        );
108    }
109
110    /// KAT:RFC 8032 §7.1 TEST 1 —— 空消息。
111    /// 向量出处 RFC 8032 §7.1(seed / 公钥 / 签名均为 RFC 原文定值)。
112    #[test]
113    fn rfc8032_test1_empty_message() {
114        const SEED: &str = "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60";
115        const PUBLIC_KEY: &str = "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a";
116        const SIGNATURE: &str = concat!(
117            "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155",
118            "5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b",
119        );
120
121        let signer = Ed25519Signer::new();
122        let signature = signer.sign(b"", SEED).unwrap();
123        assert_eq!(signature, SIGNATURE);
124
125        signer.verify(b"", SIGNATURE, PUBLIC_KEY).unwrap();
126        // 篡改一个字节 → 必败。
127        let mut tampered = SIGNATURE.to_string();
128        let last = tampered.len() - 1;
129        let flipped = if tampered.as_bytes()[last] == b'0' {
130            '1'
131        } else {
132            '0'
133        };
134        tampered.replace_range(last.., &flipped.to_string());
135        assert!(signer.verify(b"", &tampered, PUBLIC_KEY).is_err());
136    }
137
138    /// KAT:RFC 8032 §7.1 TEST 3 —— 2 字节消息 `af82`。
139    #[test]
140    fn rfc8032_test3_two_byte_message() {
141        const SEED: &str = "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7";
142        const PUBLIC_KEY: &str = "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025";
143        const SIGNATURE: &str = concat!(
144            "6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac",
145            "18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a",
146        );
147        let message = hex_decode("af82").unwrap();
148
149        let signer = Ed25519Signer::new();
150        assert_eq!(signer.sign(&message, SEED).unwrap(), SIGNATURE);
151        signer.verify(&message, SIGNATURE, PUBLIC_KEY).unwrap();
152        // 换消息 → 必败。
153        assert!(signer.verify(b"af83", SIGNATURE, PUBLIC_KEY).is_err());
154    }
155
156    #[test]
157    fn keypair_roundtrip_and_hex_shapes() {
158        let pair = Ed25519Signer::generate_key_pair_hex().unwrap();
159        assert_eq!(pair.private_key_hex.len(), 64);
160        assert_eq!(pair.public_key_hex.len(), 64);
161
162        let signer = Ed25519Signer::new();
163        let message = "非 ASCII 消息 🎉".as_bytes();
164        let signature = signer.sign(message, &pair.private_key_hex).unwrap();
165        assert_eq!(signature.len(), 128);
166        signer
167            .verify(message, &signature, &pair.public_key_hex)
168            .unwrap();
169
170        // 换一把密钥 / 换消息 → 必败。
171        let other = Ed25519Signer::generate_key_pair_hex().unwrap();
172        assert!(
173            signer
174                .verify(message, &signature, &other.public_key_hex)
175                .is_err()
176        );
177        assert!(
178            signer
179                .verify(b"changed", &signature, &pair.public_key_hex)
180                .is_err()
181        );
182    }
183
184    #[test]
185    fn signatures_are_deterministic() {
186        let pair = Ed25519Signer::generate_key_pair_hex().unwrap();
187        let signer = Ed25519Signer::new();
188        assert_eq!(
189            signer.sign(b"same", &pair.private_key_hex).unwrap(),
190            signer.sign(b"same", &pair.private_key_hex).unwrap(),
191        );
192    }
193
194    #[test]
195    fn malformed_keys_are_rejected() {
196        let pair = Ed25519Signer::generate_key_pair_hex().unwrap();
197        let signer = Ed25519Signer::new();
198
199        assert_eq!(
200            signer.sign(b"x", "zz").unwrap_err(),
201            Error::InvalidKey { label: LABEL }
202        );
203        // 31 字节 seed:长度不对。
204        assert_eq!(
205            signer.sign(b"x", &pair.private_key_hex[..62]).unwrap_err(),
206            Error::InvalidKey { label: LABEL }
207        );
208        assert_eq!(
209            signer.verify(b"x", "00", &pair.public_key_hex).unwrap_err(),
210            Error::VerificationFailed { label: LABEL }
211        );
212        assert_eq!(
213            signer
214                .verify(b"x", "00", &pair.public_key_hex[..62])
215                .unwrap_err(),
216            Error::VerificationFailed { label: LABEL }
217        );
218    }
219
220    #[test]
221    fn identifier_is_stable() {
222        assert_eq!(Ed25519Signer::new().identifier(), "ed25519");
223    }
224}