1use anyhow::{anyhow, Result};
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
18#[serde(rename_all = "lowercase")]
19pub enum SignatureAlgorithm {
20 Ed25519,
21 #[serde(rename = "rsa-2048")]
22 Rsa2048,
23 #[serde(rename = "rsa-4096")]
24 Rsa4096,
25}
26
27impl SignatureAlgorithm {
28 pub fn as_str(&self) -> &'static str {
29 match self {
30 SignatureAlgorithm::Ed25519 => "ed25519",
31 SignatureAlgorithm::Rsa2048 => "rsa-2048",
32 SignatureAlgorithm::Rsa4096 => "rsa-4096",
33 }
34 }
35
36 pub fn try_parse(s: &str) -> Option<Self> {
37 match s.to_lowercase().as_str() {
38 "ed25519" => Some(SignatureAlgorithm::Ed25519),
39 "rsa-2048" => Some(SignatureAlgorithm::Rsa2048),
40 "rsa-4096" => Some(SignatureAlgorithm::Rsa4096),
41 _ => None,
42 }
43 }
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct KeyInfo {
49 pub key_id: String,
50 pub algorithm: SignatureAlgorithm,
51 pub created_at: String,
52 pub expires_at: Option<String>,
53 pub key_material: Vec<u8>,
54 pub is_private: bool,
55 pub fingerprint: String,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct PluginSignature {
61 pub key_id: String,
62 pub algorithm: SignatureAlgorithm,
63 pub signature: String, pub signed_at: String,
65 pub payload_hash: String, }
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct VerificationResult {
71 pub is_valid: bool,
72 pub key_id: String,
73 pub algorithm: SignatureAlgorithm,
74 pub signed_at: String,
75 pub signer_fingerprint: String,
76 pub warning: Option<String>,
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
81#[serde(rename_all = "lowercase")]
82pub enum TrustLevel {
83 Unverified,
84 Unreviewed,
85 Reviewed,
86 Trusted,
87 Official,
88}
89
90impl TrustLevel {
91 pub fn description(&self) -> &'static str {
92 match self {
93 TrustLevel::Unverified => "No signature or signature not verified",
94 TrustLevel::Unreviewed => "Signed but not reviewed",
95 TrustLevel::Reviewed => "Signed and reviewed by community",
96 TrustLevel::Trusted => "Signed by trusted publisher",
97 TrustLevel::Official => "Official Skylet plugin",
98 }
99 }
100}
101
102pub struct SignatureManager {
104 keys: HashMap<String, KeyInfo>,
105 trusted_keys: Vec<String>,
106}
107
108impl SignatureManager {
109 pub fn new() -> Self {
111 Self {
112 keys: HashMap::new(),
113 trusted_keys: Vec::new(),
114 }
115 }
116
117 pub fn register_key(&mut self, key: KeyInfo) -> Result<()> {
119 if self.keys.contains_key(&key.key_id) {
120 return Err(anyhow!("Key {} already registered", key.key_id));
121 }
122 self.keys.insert(key.key_id.clone(), key);
123 Ok(())
124 }
125
126 pub fn trust_key(&mut self, key_id: &str) -> Result<()> {
128 if !self.keys.contains_key(key_id) {
129 return Err(anyhow!("Key {} not found", key_id));
130 }
131 if !self.trusted_keys.contains(&key_id.to_string()) {
132 self.trusted_keys.push(key_id.to_string());
133 }
134 Ok(())
135 }
136
137 pub fn is_trusted(&self, key_id: &str) -> bool {
139 self.trusted_keys.contains(&key_id.to_string())
140 }
141
142 pub fn get_key(&self, key_id: &str) -> Option<&KeyInfo> {
144 self.keys.get(key_id)
145 }
146
147 pub fn list_keys(&self) -> Vec<&KeyInfo> {
149 self.keys.values().collect()
150 }
151
152 pub fn verify_signature(
154 &self,
155 signature: &PluginSignature,
156 payload: &[u8],
157 ) -> Result<VerificationResult> {
158 let key = self
159 .get_key(&signature.key_id)
160 .ok_or_else(|| anyhow!("Signing key {} not found", signature.key_id))?;
161
162 if key.is_private {
163 return Err(anyhow!("Cannot verify with private key"));
164 }
165
166 let fingerprint = compute_fingerprint(&key.key_material);
168
169 use base64::Engine;
171 let engine = base64::engine::general_purpose::STANDARD;
172 let signature_bytes = engine
173 .decode(&signature.signature)
174 .map_err(|e| anyhow!("Failed to decode signature: {}", e))?;
175
176 let is_valid = match key.algorithm {
178 SignatureAlgorithm::Ed25519 => {
179 use ed25519_dalek::{Signature, Verifier, VerifyingKey};
180
181 if key.key_material.len() != 32 {
183 return Err(anyhow!("Ed25519 key must be 32 bytes"));
184 }
185 let mut key_bytes = [0u8; 32];
186 key_bytes.copy_from_slice(&key.key_material);
187
188 let public_key = VerifyingKey::from_bytes(&key_bytes)
189 .map_err(|e| anyhow!("Invalid Ed25519 public key: {}", e))?;
190
191 if signature_bytes.len() != 64 {
193 return Err(anyhow!("Ed25519 signature must be 64 bytes"));
194 }
195 let mut sig_bytes = [0u8; 64];
196 sig_bytes.copy_from_slice(&signature_bytes);
197 let signature = Signature::from_bytes(&sig_bytes);
198
199 public_key.verify(payload, &signature).is_ok()
201 }
202 SignatureAlgorithm::Rsa2048 | SignatureAlgorithm::Rsa4096 => {
203 if key.key_material.len() < 256 {
206 return Err(anyhow!("RSA key material too small"));
207 }
208 return Err(anyhow!("RSA signature verification not yet implemented"));
211 }
212 };
213
214 let warning = if !self.is_trusted(&signature.key_id) {
215 Some("Signature from untrusted key".to_string())
216 } else {
217 None
218 };
219
220 Ok(VerificationResult {
221 is_valid,
222 key_id: signature.key_id.clone(),
223 algorithm: signature.algorithm,
224 signed_at: signature.signed_at.clone(),
225 signer_fingerprint: fingerprint,
226 warning,
227 })
228 }
229
230 pub fn assess_trust_level(&self, signatures: &[PluginSignature]) -> TrustLevel {
232 if signatures.is_empty() {
233 return TrustLevel::Unverified;
234 }
235
236 let trusted_sigs = signatures
237 .iter()
238 .filter(|sig| self.is_trusted(&sig.key_id))
239 .count();
240
241 if trusted_sigs > 0 {
242 TrustLevel::Trusted
243 } else {
244 TrustLevel::Unreviewed
245 }
246 }
247
248 pub fn export_public_key(&self, key_id: &str) -> Result<String> {
250 let key = self
251 .get_key(key_id)
252 .ok_or_else(|| anyhow!("Key {} not found", key_id))?;
253
254 if key.is_private {
255 return Err(anyhow!("Cannot export private key as public"));
256 }
257
258 use base64::Engine;
260 let engine = base64::engine::general_purpose::STANDARD;
261 let encoded = engine.encode(&key.key_material);
262 Ok(format!(
263 "-----BEGIN {} PUBLIC KEY-----\n{}\n-----END {} PUBLIC KEY-----",
264 key.algorithm.as_str().to_uppercase(),
265 encoded,
266 key.algorithm.as_str().to_uppercase()
267 ))
268 }
269
270 pub fn import_public_key(&mut self, pem_data: &str, key_id: String) -> Result<()> {
272 let lines: Vec<&str> = pem_data.lines().collect();
274 if lines.len() < 3 {
275 return Err(anyhow!("Invalid PEM format"));
276 }
277
278 let header = lines[0];
280 let algorithm = if header.contains("ED25519") {
281 SignatureAlgorithm::Ed25519
282 } else if header.contains("RSA") {
283 if header.contains("RSA-4096") {
284 SignatureAlgorithm::Rsa4096
285 } else {
286 SignatureAlgorithm::Rsa2048
287 }
288 } else {
289 return Err(anyhow!("Unknown algorithm in PEM header"));
290 };
291
292 use base64::Engine;
294 let engine = base64::engine::general_purpose::STANDARD;
295 let key_data = lines[1..lines.len() - 1].join("");
296 let key_material = engine.decode(&key_data)?;
297
298 let fingerprint = compute_fingerprint(&key_material);
299
300 let key_info = KeyInfo {
301 key_id: key_id.clone(),
302 algorithm,
303 created_at: chrono::Utc::now().to_rfc3339(),
304 expires_at: None,
305 key_material,
306 is_private: false,
307 fingerprint,
308 };
309
310 self.register_key(key_info)
311 }
312}
313
314impl Default for SignatureManager {
315 fn default() -> Self {
316 Self::new()
317 }
318}
319
320fn compute_fingerprint(key_material: &[u8]) -> String {
322 use sha2::{Digest, Sha256};
323 let mut hasher = Sha256::new();
324 hasher.update(key_material);
325 let result = hasher.finalize();
326 hex::encode(&result[..16]) }
328
329#[derive(Debug, Clone, Serialize, Deserialize)]
331pub struct SignatureAuditLog {
332 pub plugin_id: String,
333 pub plugin_version: String,
334 pub signatures: Vec<PluginSignature>,
335 pub verified_at: String,
336 pub verification_results: Vec<VerificationResult>,
337 pub trust_level: TrustLevel,
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343
344 #[test]
345 fn test_signature_algorithm_to_str() {
346 assert_eq!(SignatureAlgorithm::Ed25519.as_str(), "ed25519");
347 assert_eq!(SignatureAlgorithm::Rsa2048.as_str(), "rsa-2048");
348 assert_eq!(SignatureAlgorithm::Rsa4096.as_str(), "rsa-4096");
349 }
350
351 #[test]
352 fn test_signature_algorithm_try_parse() {
353 assert_eq!(
354 SignatureAlgorithm::try_parse("ed25519"),
355 Some(SignatureAlgorithm::Ed25519)
356 );
357 assert_eq!(
358 SignatureAlgorithm::try_parse("rsa-2048"),
359 Some(SignatureAlgorithm::Rsa2048)
360 );
361 assert_eq!(SignatureAlgorithm::try_parse("invalid"), None);
362 }
363
364 #[test]
365 fn test_signature_manager_creation() {
366 let manager = SignatureManager::new();
367 assert_eq!(manager.list_keys().len(), 0);
368 }
369
370 #[test]
371 fn test_key_registration() -> Result<()> {
372 let mut manager = SignatureManager::new();
373 let key = KeyInfo {
374 key_id: "test-key-1".to_string(),
375 algorithm: SignatureAlgorithm::Ed25519,
376 created_at: "2026-02-10T00:00:00Z".to_string(),
377 expires_at: None,
378 key_material: vec![1, 2, 3, 4],
379 is_private: false,
380 fingerprint: "abcd1234".to_string(),
381 };
382
383 manager.register_key(key)?;
384 assert_eq!(manager.list_keys().len(), 1);
385 Ok(())
386 }
387
388 #[test]
389 fn test_duplicate_key_registration() -> Result<()> {
390 let mut manager = SignatureManager::new();
391 let key = KeyInfo {
392 key_id: "test-key-1".to_string(),
393 algorithm: SignatureAlgorithm::Ed25519,
394 created_at: "2026-02-10T00:00:00Z".to_string(),
395 expires_at: None,
396 key_material: vec![1, 2, 3, 4],
397 is_private: false,
398 fingerprint: "abcd1234".to_string(),
399 };
400
401 manager.register_key(key.clone())?;
402 assert!(manager.register_key(key).is_err());
403 Ok(())
404 }
405
406 #[test]
407 fn test_key_trust_management() -> Result<()> {
408 let mut manager = SignatureManager::new();
409 let key = KeyInfo {
410 key_id: "test-key-1".to_string(),
411 algorithm: SignatureAlgorithm::Ed25519,
412 created_at: "2026-02-10T00:00:00Z".to_string(),
413 expires_at: None,
414 key_material: vec![1, 2, 3, 4],
415 is_private: false,
416 fingerprint: "abcd1234".to_string(),
417 };
418
419 manager.register_key(key)?;
420 assert!(!manager.is_trusted("test-key-1"));
421
422 manager.trust_key("test-key-1")?;
423 assert!(manager.is_trusted("test-key-1"));
424
425 Ok(())
426 }
427
428 #[test]
429 fn test_trust_level_assessment() {
430 let manager = SignatureManager::new();
431
432 let trust = manager.assess_trust_level(&[]);
434 assert_eq!(trust, TrustLevel::Unverified);
435
436 let sig = PluginSignature {
438 key_id: "unknown-key".to_string(),
439 algorithm: SignatureAlgorithm::Ed25519,
440 signature: "signature".to_string(),
441 signed_at: "2026-02-10T00:00:00Z".to_string(),
442 payload_hash: "hash".to_string(),
443 };
444
445 let trust = manager.assess_trust_level(&[sig]);
446 assert_eq!(trust, TrustLevel::Unreviewed);
447 }
448
449 #[test]
450 fn test_trust_level_descriptions() {
451 assert!(!TrustLevel::Unverified.description().is_empty());
452 assert!(!TrustLevel::Trusted.description().is_empty());
453 assert!(!TrustLevel::Official.description().is_empty());
454 }
455
456 #[test]
457 fn test_trust_level_ordering() {
458 assert!(TrustLevel::Unverified < TrustLevel::Trusted);
459 assert!(TrustLevel::Trusted < TrustLevel::Official);
460 }
461
462 #[test]
463 fn test_key_info_serialization() -> Result<()> {
464 let key = KeyInfo {
465 key_id: "test-key".to_string(),
466 algorithm: SignatureAlgorithm::Ed25519,
467 created_at: "2026-02-10T00:00:00Z".to_string(),
468 expires_at: Some("2027-02-10T00:00:00Z".to_string()),
469 key_material: vec![1, 2, 3],
470 is_private: false,
471 fingerprint: "abc123".to_string(),
472 };
473
474 let json = serde_json::to_string(&key)?;
475 let deserialized: KeyInfo = serde_json::from_str(&json)?;
476 assert_eq!(key.key_id, deserialized.key_id);
477 Ok(())
478 }
479
480 #[test]
481 fn test_plugin_signature_serialization() -> Result<()> {
482 let sig = PluginSignature {
483 key_id: "key-1".to_string(),
484 algorithm: SignatureAlgorithm::Rsa2048,
485 signature: "sig-data".to_string(),
486 signed_at: "2026-02-10T00:00:00Z".to_string(),
487 payload_hash: "hash123".to_string(),
488 };
489
490 let json = serde_json::to_string(&sig)?;
491 let deserialized: PluginSignature = serde_json::from_str(&json)?;
492 assert_eq!(sig.key_id, deserialized.key_id);
493 Ok(())
494 }
495
496 #[test]
497 fn test_compute_fingerprint() {
498 let data1 = vec![1, 2, 3, 4];
499 let data2 = vec![1, 2, 3, 4];
500 let data3 = vec![1, 2, 3, 5];
501
502 let fp1 = compute_fingerprint(&data1);
503 let fp2 = compute_fingerprint(&data2);
504 let fp3 = compute_fingerprint(&data3);
505
506 assert_eq!(fp1, fp2);
507 assert_ne!(fp1, fp3);
508 }
509}