1use ed25519_dalek::{Signature, Signer, Verifier};
23
24#[derive(Debug, Clone, PartialEq, Eq)]
26pub enum SignError {
27 Invalid(String),
29 Randomness(&'static str),
31}
32
33impl core::fmt::Display for SignError {
34 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
35 match self {
36 SignError::Invalid(msg) => write!(f, "审计签名: 非法输入: {msg}"),
37 SignError::Randomness(src) => write!(f, "审计签名: 随机种子失败: {src}"),
38 }
39 }
40}
41
42impl std::error::Error for SignError {}
43
44#[derive(Debug, Clone)]
48pub struct AuditSigner {
49 signing: ed25519_dalek::SigningKey,
50 verifying: ed25519_dalek::VerifyingKey,
51}
52
53impl AuditSigner {
54 pub fn from_bytes(seed: [u8; 32]) -> Self {
56 let signing = ed25519_dalek::SigningKey::from_bytes(&seed);
57 let verifying = signing.verifying_key();
58 Self { signing, verifying }
59 }
60
61 pub fn from_hex(seed_hex: &str) -> Result<Self, SignError> {
63 let bytes = hex_decode(seed_hex)?;
64 if bytes.len() != 32 {
65 return Err(SignError::Invalid(format!(
66 "私钥种子长度 != 32: {}",
67 bytes.len()
68 )));
69 }
70 let mut seed = [0u8; 32];
72 for (dst, src) in seed.iter_mut().zip(bytes.iter()) {
73 *dst = *src;
74 }
75 Ok(Self::from_bytes(seed))
76 }
77
78 pub fn generate_keys() -> Result<(String, String), SignError> {
83 let mut seed = [0u8; 32];
84 getrandom::getrandom(&mut seed)
85 .map_err(|_| SignError::Randomness("OS 熵源不可用"))?;
86 let signer = Self::from_bytes(seed);
87 Ok((hex_encode(&seed), hex_encode(&signer.verifying_bytes())))
88 }
89
90 pub fn verifying_bytes(&self) -> [u8; 32] {
92 self.verifying.to_bytes()
93 }
94
95 pub fn signature_bytes(&self, payload: &[u8]) -> [u8; 64] {
97 let sig: Signature = self.signing.sign(payload);
98 sig.to_bytes()
99 }
100}
101
102pub fn verify_signature(
107 verifying_bytes: [u8; 32],
108 payload: &[u8],
109 sig_bytes: &[u8; 64],
110) -> Result<bool, SignError> {
111 let verifying = ed25519_dalek::VerifyingKey::from_bytes(&verifying_bytes)
112 .map_err(|e| SignError::Invalid(format!("公钥非法: {e}")))?;
113 let sig = Signature::from_bytes(sig_bytes);
114 Ok(verifying.verify(payload, &sig).is_ok())
115}
116
117pub(crate) fn hex_encode(bytes: &[u8]) -> String {
119 let mut s = String::with_capacity(bytes.len() * 2);
120 for b in bytes {
121 s.push_str(&format!("{b:02x}"));
122 }
123 s
124}
125
126pub(crate) fn hex_decode(hex: &str) -> Result<Vec<u8>, SignError> {
128 if hex.len() % 2 != 0 {
129 return Err(SignError::Invalid(format!("hex 长度为奇数: {}", hex.len())));
130 }
131 let mut out = Vec::with_capacity(hex.len() / 2);
132 let bytes = hex.as_bytes();
133 for i in (0..bytes.len()).step_by(2) {
134 let hi = (bytes[i] as char).to_digit(16);
135 let lo = (bytes[i + 1] as char).to_digit(16);
136 match (hi, lo) {
137 (Some(h), Some(l)) => out.push(((h << 4) | l) as u8),
138 _ => return Err(SignError::Invalid("hex 含非十六进制字符".into())),
139 }
140 }
141 Ok(out)
142}
143
144#[cfg(test)]
145mod tests {
146 #![allow(clippy::unwrap_used)]
147 use super::*;
148
149 #[test]
150 fn test_signature_is_deterministic() {
151 let signer = AuditSigner::from_bytes([7u8; 32]);
152 let payload = b"audit-anchor-payload";
153 assert_eq!(signer.signature_bytes(payload), signer.signature_bytes(payload));
154 }
155
156 #[test]
157 fn test_sign_and_verify_ok() {
158 let signer = AuditSigner::from_bytes([7u8; 32]);
159 let pk = signer.verifying_bytes();
160 let payload = b"hello-audit";
161 let sig = signer.signature_bytes(payload);
162 assert!(verify_signature(pk, payload, &sig).unwrap());
163 }
164
165 #[test]
166 fn test_verify_rejects_tampered_payload() {
167 let signer = AuditSigner::from_bytes([7u8; 32]);
168 let pk = signer.verifying_bytes();
169 let sig = signer.signature_bytes(b"hello");
170 assert!(!verify_signature(pk, b"hellO", &sig).unwrap());
171 }
172
173 #[test]
174 fn test_hex_roundtrip() {
175 let bytes = [0u8, 1, 0xff, 0x10, 0xab, 0xcd];
176 let hex = hex_encode(&bytes);
177 assert_eq!(hex_decode(&hex).unwrap(), bytes);
178 assert!(hex_decode("zz").is_err());
179 assert!(hex_decode("abc").is_err());
180 }
181
182 #[test]
183 fn test_generate_keys_produces_hex() {
184 let (sk, pk) = AuditSigner::generate_keys().unwrap();
185 assert_eq!(sk.len(), 64);
186 assert_eq!(pk.len(), 64);
187 let signer = AuditSigner::from_hex(&sk).unwrap();
189 assert_eq!(signer.verifying_bytes(), hex_decode(&pk).unwrap().as_slice());
190 }
191}