1use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
7use rand::rngs::OsRng;
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11use crate::types::event::Event;
12use crate::types::ids::EventId;
13
14pub struct SigningKeyPair {
16 signing_key: SigningKey,
17}
18
19impl SigningKeyPair {
20 pub fn generate() -> Self {
22 let signing_key = SigningKey::generate(&mut OsRng);
23 Self { signing_key }
24 }
25
26 pub fn from_seed_hex(seed_hex: &str) -> Result<Self, SigningError> {
28 let seed_bytes =
29 hex::decode(seed_hex).map_err(|e| SigningError::KeyParseError(e.to_string()))?;
30
31 if seed_bytes.len() != 32 {
32 return Err(SigningError::KeyParseError(format!(
33 "Seed must be 32 bytes, got {}",
34 seed_bytes.len()
35 )));
36 }
37
38 let mut seed_array = [0u8; 32];
39 seed_array.copy_from_slice(&seed_bytes);
40
41 let signing_key = SigningKey::from_bytes(&seed_array);
42 Ok(Self { signing_key })
43 }
44
45 pub fn seed_hex(&self) -> String {
47 hex::encode(self.signing_key.to_bytes())
48 }
49
50 pub fn public_key_hex(&self) -> String {
52 hex::encode(self.signing_key.verifying_key().to_bytes())
53 }
54
55 pub fn verifying_key(&self) -> VerifyingKey {
57 self.signing_key.verifying_key()
58 }
59
60 pub fn sign(&self, event_id: &EventId) -> Vec<u8> {
62 let signature = self.signing_key.sign(event_id);
63 signature.to_bytes().to_vec()
64 }
65
66 pub fn sign_event(&self, event: &Event) -> Vec<u8> {
68 self.sign(&event.event_id)
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
74#[serde(rename_all = "lowercase")]
75pub enum VerificationPolicy {
76 #[default]
78 Off,
79 Warn,
81 Require,
83}
84
85impl VerificationPolicy {
86 #[allow(clippy::should_implement_trait)]
88 pub fn from_str(s: &str) -> Option<Self> {
89 match s.to_lowercase().as_str() {
90 "off" => Some(VerificationPolicy::Off),
91 "warn" => Some(VerificationPolicy::Warn),
92 "require" => Some(VerificationPolicy::Require),
93 _ => None,
94 }
95 }
96
97 pub fn as_str(&self) -> &'static str {
99 match self {
100 VerificationPolicy::Off => "off",
101 VerificationPolicy::Warn => "warn",
102 VerificationPolicy::Require => "require",
103 }
104 }
105}
106
107#[derive(Debug, Error)]
109pub enum SigningError {
110 #[error("signature missing")]
111 SignatureMissing,
112
113 #[error("invalid signature")]
114 InvalidSignature,
115
116 #[error("public key not found for actor {0}")]
117 PublicKeyNotFound(String),
118
119 #[error("key parse error: {0}")]
120 KeyParseError(String),
121
122 #[error("signature parse error: {0}")]
123 SignatureParseError(String),
124}
125
126pub fn verify_signature(event: &Event, public_key_hex: &str) -> Result<(), SigningError> {
128 let sig_bytes = event.sig.as_ref().ok_or(SigningError::SignatureMissing)?;
130
131 let pk_bytes =
133 hex::decode(public_key_hex).map_err(|e| SigningError::KeyParseError(e.to_string()))?;
134
135 if pk_bytes.len() != 32 {
136 return Err(SigningError::KeyParseError(format!(
137 "Public key must be 32 bytes, got {}",
138 pk_bytes.len()
139 )));
140 }
141
142 let mut pk_array = [0u8; 32];
143 pk_array.copy_from_slice(&pk_bytes);
144
145 let verifying_key = VerifyingKey::from_bytes(&pk_array)
146 .map_err(|e| SigningError::KeyParseError(e.to_string()))?;
147
148 if sig_bytes.len() != 64 {
150 return Err(SigningError::SignatureParseError(format!(
151 "Signature must be 64 bytes, got {}",
152 sig_bytes.len()
153 )));
154 }
155
156 let mut sig_array = [0u8; 64];
157 sig_array.copy_from_slice(sig_bytes);
158
159 let signature = Signature::from_bytes(&sig_array);
160
161 verifying_key
163 .verify(&event.event_id, &signature)
164 .map_err(|_| SigningError::InvalidSignature)
165}
166
167pub fn verify_raw(
169 event_id: &EventId,
170 signature: &[u8],
171 public_key_hex: &str,
172) -> Result<(), SigningError> {
173 let pk_bytes =
175 hex::decode(public_key_hex).map_err(|e| SigningError::KeyParseError(e.to_string()))?;
176
177 if pk_bytes.len() != 32 {
178 return Err(SigningError::KeyParseError(format!(
179 "Public key must be 32 bytes, got {}",
180 pk_bytes.len()
181 )));
182 }
183
184 let mut pk_array = [0u8; 32];
185 pk_array.copy_from_slice(&pk_bytes);
186
187 let verifying_key = VerifyingKey::from_bytes(&pk_array)
188 .map_err(|e| SigningError::KeyParseError(e.to_string()))?;
189
190 if signature.len() != 64 {
192 return Err(SigningError::SignatureParseError(format!(
193 "Signature must be 64 bytes, got {}",
194 signature.len()
195 )));
196 }
197
198 let mut sig_array = [0u8; 64];
199 sig_array.copy_from_slice(signature);
200
201 let sig = Signature::from_bytes(&sig_array);
202
203 verifying_key
205 .verify(event_id, &sig)
206 .map_err(|_| SigningError::InvalidSignature)
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212 use crate::types::event::EventKind;
213
214 #[test]
215 fn test_keypair_generation() {
216 let keypair = SigningKeyPair::generate();
217 let seed = keypair.seed_hex();
218 let pk = keypair.public_key_hex();
219
220 assert_eq!(seed.len(), 64);
222 assert_eq!(pk.len(), 64);
224 }
225
226 #[test]
227 fn test_keypair_from_seed() {
228 let keypair1 = SigningKeyPair::generate();
229 let seed = keypair1.seed_hex();
230
231 let keypair2 = SigningKeyPair::from_seed_hex(&seed).unwrap();
232
233 assert_eq!(keypair1.public_key_hex(), keypair2.public_key_hex());
235 }
236
237 #[test]
238 fn test_sign_and_verify() {
239 let keypair = SigningKeyPair::generate();
240 let event_id: EventId = [42u8; 32];
241
242 let signature = keypair.sign(&event_id);
243
244 assert_eq!(signature.len(), 64);
246
247 let result = verify_raw(&event_id, &signature, &keypair.public_key_hex());
249 assert!(result.is_ok());
250 }
251
252 #[test]
253 fn test_sign_event() {
254 let keypair = SigningKeyPair::generate();
255
256 let mut event = Event::new(
257 [1u8; 32],
258 [2u8; 16],
259 [3u8; 16],
260 1700000000000,
261 None,
262 EventKind::IssueCreated {
263 title: "Test".to_string(),
264 body: "Body".to_string(),
265 labels: vec![],
266 },
267 );
268
269 event.sig = Some(keypair.sign_event(&event));
270
271 let result = verify_signature(&event, &keypair.public_key_hex());
273 assert!(result.is_ok());
274 }
275
276 #[test]
277 fn test_verify_missing_signature() {
278 let keypair = SigningKeyPair::generate();
279
280 let event = Event::new(
281 [1u8; 32],
282 [2u8; 16],
283 [3u8; 16],
284 1700000000000,
285 None,
286 EventKind::CommentAdded {
287 body: "test".to_string(),
288 },
289 );
290
291 let result = verify_signature(&event, &keypair.public_key_hex());
292 assert!(matches!(result, Err(SigningError::SignatureMissing)));
293 }
294
295 #[test]
296 fn test_verify_invalid_signature() {
297 let keypair = SigningKeyPair::generate();
298
299 let mut event = Event::new(
300 [1u8; 32],
301 [2u8; 16],
302 [3u8; 16],
303 1700000000000,
304 None,
305 EventKind::CommentAdded {
306 body: "test".to_string(),
307 },
308 );
309
310 event.sig = Some(vec![0u8; 64]);
312
313 let result = verify_signature(&event, &keypair.public_key_hex());
314 assert!(matches!(result, Err(SigningError::InvalidSignature)));
315 }
316
317 #[test]
318 fn test_verify_wrong_public_key() {
319 let keypair1 = SigningKeyPair::generate();
320 let keypair2 = SigningKeyPair::generate();
321
322 let mut event = Event::new(
323 [1u8; 32],
324 [2u8; 16],
325 [3u8; 16],
326 1700000000000,
327 None,
328 EventKind::CommentAdded {
329 body: "test".to_string(),
330 },
331 );
332
333 event.sig = Some(keypair1.sign_event(&event));
335
336 let result = verify_signature(&event, &keypair2.public_key_hex());
338 assert!(matches!(result, Err(SigningError::InvalidSignature)));
339 }
340
341 #[test]
342 fn test_verification_policy_parse() {
343 assert_eq!(
344 VerificationPolicy::from_str("off"),
345 Some(VerificationPolicy::Off)
346 );
347 assert_eq!(
348 VerificationPolicy::from_str("warn"),
349 Some(VerificationPolicy::Warn)
350 );
351 assert_eq!(
352 VerificationPolicy::from_str("require"),
353 Some(VerificationPolicy::Require)
354 );
355 assert_eq!(
356 VerificationPolicy::from_str("REQUIRE"),
357 Some(VerificationPolicy::Require)
358 );
359 assert_eq!(VerificationPolicy::from_str("invalid"), None);
360 }
361}