Skip to main content

pulith_fetch/codec/
signature.rs

1//! Digital signature verification functionality.
2//!
3//! This module provides types and interfaces for verifying
4//! digital signatures of downloaded content. Currently provides
5//! type definitions and interfaces for future implementation.
6
7use crate::error::{Error, Result};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11/// Supported signature algorithms.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub enum SignatureAlgorithm {
14    /// RSA with PKCS#1 v1.5 padding
15    RsaPkcs1v15,
16    /// RSA with PSS padding
17    RsaPss,
18    /// Elliptic Curve Digital Signature Algorithm (ECDSA)
19    Ecdsa,
20    /// EdDSA (Ed25519/Ed448)
21    EdDsa,
22    /// Digital Signature Algorithm (DSA) - deprecated
23    Dsa,
24}
25
26impl SignatureAlgorithm {
27    /// Get the string representation of this algorithm.
28    pub fn as_str(&self) -> &'static str {
29        match self {
30            SignatureAlgorithm::RsaPkcs1v15 => "rsa-pkcs1v15",
31            SignatureAlgorithm::RsaPss => "rsa-pss",
32            SignatureAlgorithm::Ecdsa => "ecdsa",
33            SignatureAlgorithm::EdDsa => "eddsa",
34            SignatureAlgorithm::Dsa => "dsa",
35        }
36    }
37
38    /// Get the minimum key size in bits for this algorithm.
39    pub fn min_key_size(&self) -> usize {
40        match self {
41            SignatureAlgorithm::RsaPkcs1v15 => 2048,
42            SignatureAlgorithm::RsaPss => 2048,
43            SignatureAlgorithm::Ecdsa => 256,
44            SignatureAlgorithm::EdDsa => 256,
45            SignatureAlgorithm::Dsa => 2048,
46        }
47    }
48
49    /// Check if this algorithm is considered secure for current use.
50    pub fn is_secure(&self) -> bool {
51        match self {
52            SignatureAlgorithm::RsaPkcs1v15 => true,
53            SignatureAlgorithm::RsaPss => true,
54            SignatureAlgorithm::Ecdsa => true,
55            SignatureAlgorithm::EdDsa => true,
56            SignatureAlgorithm::Dsa => false, // DSA is deprecated
57        }
58    }
59}
60
61/// Format of the signature data.
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
63pub enum SignatureFormat {
64    /// Raw binary signature
65    Raw,
66    /// Base64-encoded signature
67    Base64,
68    /// Hexadecimal-encoded signature
69    Hex,
70    /// PEM format
71    Pem,
72    /// DER format
73    Der,
74}
75
76impl SignatureFormat {
77    /// Get the string representation of this format.
78    pub fn as_str(&self) -> &'static str {
79        match self {
80            SignatureFormat::Raw => "raw",
81            SignatureFormat::Base64 => "base64",
82            SignatureFormat::Hex => "hex",
83            SignatureFormat::Pem => "pem",
84            SignatureFormat::Der => "der",
85        }
86    }
87}
88
89/// Public key format.
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
91pub enum PublicKeyFormat {
92    /// PEM format
93    Pem,
94    /// DER format
95    Der,
96    /// Raw key bytes
97    Raw,
98    /// JWK (JSON Web Key) format
99    Jwk,
100    /// SSH public key format
101    Ssh,
102}
103
104impl PublicKeyFormat {
105    /// Get the string representation of this format.
106    pub fn as_str(&self) -> &'static str {
107        match self {
108            PublicKeyFormat::Pem => "pem",
109            PublicKeyFormat::Der => "der",
110            PublicKeyFormat::Raw => "raw",
111            PublicKeyFormat::Jwk => "jwk",
112            PublicKeyFormat::Ssh => "ssh",
113        }
114    }
115}
116
117/// A public key for signature verification.
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct PublicKey {
120    /// The key algorithm
121    pub algorithm: SignatureAlgorithm,
122    /// The key format
123    pub format: PublicKeyFormat,
124    /// The key data
125    pub data: Vec<u8>,
126    /// Optional key identifier
127    pub key_id: Option<String>,
128    /// Optional key usage constraints
129    pub usage: Option<KeyUsage>,
130}
131
132impl PublicKey {
133    /// Create a new public key.
134    pub fn new(algorithm: SignatureAlgorithm, format: PublicKeyFormat, data: Vec<u8>) -> Self {
135        Self {
136            algorithm,
137            format,
138            data,
139            key_id: None,
140            usage: None,
141        }
142    }
143
144    /// Set the key identifier.
145    pub fn with_key_id(mut self, key_id: String) -> Self {
146        self.key_id = Some(key_id);
147        self
148    }
149
150    /// Set the key usage constraints.
151    pub fn with_usage(mut self, usage: KeyUsage) -> Self {
152        self.usage = Some(usage);
153        self
154    }
155
156    /// Get the key size in bits.
157    pub fn key_size(&self) -> usize {
158        self.data.len() * 8
159    }
160
161    /// Validate the key parameters.
162    pub fn validate(&self) -> Result<()> {
163        if !self.algorithm.is_secure() {
164            return Err(Error::InvalidState(format!(
165                "Algorithm {:?} is not considered secure",
166                self.algorithm
167            )));
168        }
169
170        if self.key_size() < self.algorithm.min_key_size() {
171            return Err(Error::InvalidState(format!(
172                "Key size {} is below minimum {} for algorithm {:?}",
173                self.key_size(),
174                self.algorithm.min_key_size(),
175                self.algorithm
176            )));
177        }
178
179        Ok(())
180    }
181}
182
183/// Key usage constraints.
184#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct KeyUsage {
186    /// Allowed purposes for this key
187    pub purposes: Vec<KeyPurpose>,
188    /// Expiration time (Unix timestamp)
189    pub expires_at: Option<u64>,
190    /// Not valid before (Unix timestamp)
191    pub not_before: Option<u64>,
192}
193
194/// Key purpose types.
195#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
196pub enum KeyPurpose {
197    /// Code signing
198    CodeSigning,
199    /// Document signing
200    DocumentSigning,
201    /// Timestamp signing
202    TimestampSigning,
203    /// Certificate signing
204    CertificateSigning,
205    /// Custom purpose
206    Custom(String),
207}
208
209/// A digital signature.
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct Signature {
212    /// The signature algorithm
213    pub algorithm: SignatureAlgorithm,
214    /// The signature format
215    pub format: SignatureFormat,
216    /// The signature data
217    pub data: Vec<u8>,
218    /// Optional identifier of the key used
219    pub key_id: Option<String>,
220    /// Timestamp when signature was created (Unix timestamp)
221    pub created_at: Option<u64>,
222    /// Timestamp when signature expires (Unix timestamp)
223    pub expires_at: Option<u64>,
224}
225
226impl Signature {
227    /// Create a new signature.
228    pub fn new(algorithm: SignatureAlgorithm, format: SignatureFormat, data: Vec<u8>) -> Self {
229        Self {
230            algorithm,
231            format,
232            data,
233            key_id: None,
234            created_at: None,
235            expires_at: None,
236        }
237    }
238
239    /// Set the key identifier.
240    pub fn with_key_id(mut self, key_id: String) -> Self {
241        self.key_id = Some(key_id);
242        self
243    }
244
245    /// Set the creation timestamp.
246    pub fn with_created_at(mut self, created_at: u64) -> Self {
247        self.created_at = Some(created_at);
248        self
249    }
250
251    /// Set the expiration timestamp.
252    pub fn with_expires_at(mut self, expires_at: u64) -> Self {
253        self.expires_at = Some(expires_at);
254        self
255    }
256
257    /// Check if the signature has expired.
258    pub fn is_expired(&self) -> bool {
259        if let Some(expires_at) = self.expires_at {
260            // Use current time (placeholder - would use actual current time)
261            let now = 0; // Placeholder
262            now > expires_at
263        } else {
264            false
265        }
266    }
267
268    /// Decode the signature data based on its format.
269    pub fn decode_data(&self) -> Result<Vec<u8>> {
270        match self.format {
271            SignatureFormat::Raw => Ok(self.data.clone()),
272            SignatureFormat::Base64 => {
273                use base64::{Engine as _, engine::general_purpose};
274                general_purpose::STANDARD
275                    .decode(&self.data)
276                    .map_err(|e| Error::InvalidState(format!("Invalid base64 signature: {}", e)))
277            }
278            SignatureFormat::Hex => hex::decode(&self.data)
279                .map_err(|e| Error::InvalidState(format!("Invalid hex signature: {}", e))),
280            SignatureFormat::Pem | SignatureFormat::Der => {
281                // Would need proper PEM/DER parsing
282                Err(Error::InvalidState(
283                    "PEM/DER signature parsing not implemented".to_string(),
284                ))
285            }
286        }
287    }
288}
289
290/// Signature verification configuration.
291#[derive(Debug, Clone)]
292pub struct SignatureConfig {
293    /// The public key to verify with
294    pub public_key: PublicKey,
295    /// The expected signature
296    pub signature: Signature,
297    /// The data that was signed (if known)
298    pub signed_data: Option<Vec<u8>>,
299    /// Whether to ignore expired signatures
300    pub ignore_expired: bool,
301}
302
303impl SignatureConfig {
304    /// Create a new signature configuration.
305    pub fn new(public_key: PublicKey, signature: Signature) -> Self {
306        Self {
307            public_key,
308            signature,
309            signed_data: None,
310            ignore_expired: false,
311        }
312    }
313
314    /// Set the signed data.
315    pub fn with_signed_data(mut self, data: Vec<u8>) -> Self {
316        self.signed_data = Some(data);
317        self
318    }
319
320    /// Set whether to ignore expired signatures.
321    pub fn with_ignore_expired(mut self, ignore: bool) -> Self {
322        self.ignore_expired = ignore;
323        self
324    }
325
326    /// Validate the configuration.
327    pub fn validate(&self) -> Result<()> {
328        // Validate public key
329        self.public_key.validate()?;
330
331        // Check algorithm compatibility
332        if self.public_key.algorithm != self.signature.algorithm {
333            return Err(Error::InvalidState(format!(
334                "Algorithm mismatch: key is {:?}, signature is {:?}",
335                self.public_key.algorithm, self.signature.algorithm
336            )));
337        }
338
339        // Check expiration
340        if !self.ignore_expired && self.signature.is_expired() {
341            return Err(Error::InvalidState("Signature has expired".to_string()));
342        }
343
344        // Check key ID match if both present
345        if let (Some(key_key_id), Some(sig_key_id)) =
346            (&self.public_key.key_id, &self.signature.key_id)
347            && key_key_id != sig_key_id
348        {
349            return Err(Error::InvalidState("Key ID mismatch".to_string()));
350        }
351
352        Ok(())
353    }
354}
355
356/// Trait for signature verification implementations.
357pub trait SignatureVerifier: Send + Sync {
358    /// Verify a signature against the given data.
359    fn verify(&self, data: &[u8], config: &SignatureConfig) -> Result<bool>;
360
361    /// Get the supported algorithm.
362    fn algorithm(&self) -> SignatureAlgorithm;
363}
364
365/// Mock signature verifier for testing purposes.
366pub struct MockVerifier {
367    algorithm: SignatureAlgorithm,
368    should_verify: bool,
369}
370
371impl MockVerifier {
372    /// Create a new mock verifier.
373    pub fn new(algorithm: SignatureAlgorithm, should_verify: bool) -> Self {
374        Self {
375            algorithm,
376            should_verify,
377        }
378    }
379
380    /// Set whether verification should succeed.
381    pub fn set_should_verify(&mut self, should_verify: bool) {
382        self.should_verify = should_verify;
383    }
384}
385
386impl SignatureVerifier for MockVerifier {
387    fn verify(&self, _data: &[u8], config: &SignatureConfig) -> Result<bool> {
388        config.validate()?;
389        Ok(self.should_verify)
390    }
391
392    fn algorithm(&self) -> SignatureAlgorithm {
393        self.algorithm
394    }
395}
396
397/// Signature verification manager.
398pub struct SignatureManager {
399    verifiers: HashMap<SignatureAlgorithm, Box<dyn SignatureVerifier>>,
400}
401
402impl SignatureManager {
403    /// Create a new signature manager.
404    pub fn new() -> Self {
405        let mut manager = Self {
406            verifiers: HashMap::new(),
407        };
408
409        // Add mock verifiers for all algorithms
410        manager.add_mock_verifiers();
411        manager
412    }
413
414    /// Add a mock verifier for each algorithm.
415    fn add_mock_verifiers(&mut self) {
416        use SignatureAlgorithm::*;
417
418        // Add mock verifiers that always return true for testing
419        self.verifiers
420            .insert(RsaPkcs1v15, Box::new(MockVerifier::new(RsaPkcs1v15, true)));
421        self.verifiers
422            .insert(RsaPss, Box::new(MockVerifier::new(RsaPss, true)));
423        self.verifiers
424            .insert(Ecdsa, Box::new(MockVerifier::new(Ecdsa, true)));
425        self.verifiers
426            .insert(EdDsa, Box::new(MockVerifier::new(EdDsa, true)));
427        self.verifiers
428            .insert(Dsa, Box::new(MockVerifier::new(Dsa, true)));
429    }
430
431    /// Add a custom verifier for an algorithm.
432    pub fn add_verifier(&mut self, verifier: Box<dyn SignatureVerifier>) {
433        self.verifiers.insert(verifier.algorithm(), verifier);
434    }
435
436    /// Verify a signature.
437    pub fn verify(&self, data: &[u8], config: &SignatureConfig) -> Result<bool> {
438        if let Some(verifier) = self.verifiers.get(&config.signature.algorithm) {
439            verifier.verify(data, config)
440        } else {
441            Err(Error::InvalidState(format!(
442                "No verifier available for algorithm {:?}",
443                config.signature.algorithm
444            )))
445        }
446    }
447
448    /// Check if an algorithm is supported.
449    pub fn supports_algorithm(&self, algorithm: SignatureAlgorithm) -> bool {
450        self.verifiers.contains_key(&algorithm)
451    }
452
453    /// Get all supported algorithms.
454    pub fn supported_algorithms(&self) -> Vec<SignatureAlgorithm> {
455        self.verifiers.keys().copied().collect()
456    }
457}
458
459impl Default for SignatureManager {
460    fn default() -> Self {
461        Self::new()
462    }
463}
464
465/// Convenience function to verify a signature.
466pub fn verify_signature(data: &[u8], config: &SignatureConfig) -> Result<bool> {
467    let manager = SignatureManager::new();
468    manager.verify(data, config)
469}
470
471#[cfg(test)]
472mod tests {
473    use super::*;
474
475    #[test]
476    fn test_signature_algorithm() {
477        assert_eq!(SignatureAlgorithm::RsaPkcs1v15.as_str(), "rsa-pkcs1v15");
478        assert_eq!(SignatureAlgorithm::RsaPkcs1v15.min_key_size(), 2048);
479        assert!(SignatureAlgorithm::RsaPkcs1v15.is_secure());
480        assert!(!SignatureAlgorithm::Dsa.is_secure());
481    }
482
483    #[test]
484    fn test_signature_format() {
485        assert_eq!(SignatureFormat::Base64.as_str(), "base64");
486    }
487
488    #[test]
489    fn test_public_key_format() {
490        assert_eq!(PublicKeyFormat::Pem.as_str(), "pem");
491    }
492
493    #[test]
494    fn test_public_key_creation() {
495        let key = PublicKey::new(
496            SignatureAlgorithm::RsaPkcs1v15,
497            PublicKeyFormat::Pem,
498            b"mock_key_data".to_vec(),
499        )
500        .with_key_id("test-key".to_string());
501
502        assert_eq!(key.algorithm, SignatureAlgorithm::RsaPkcs1v15);
503        assert_eq!(key.format, PublicKeyFormat::Pem);
504        assert_eq!(key.data, b"mock_key_data");
505        assert_eq!(key.key_id, Some("test-key".to_string()));
506    }
507
508    #[test]
509    fn test_public_key_validation() {
510        let key = PublicKey::new(
511            SignatureAlgorithm::RsaPkcs1v15,
512            PublicKeyFormat::Pem,
513            vec![0u8; 256], // 2048 bits
514        );
515        assert!(key.validate().is_ok());
516
517        // Test insecure algorithm
518        let key = PublicKey::new(
519            SignatureAlgorithm::Dsa,
520            PublicKeyFormat::Pem,
521            vec![0u8; 256],
522        );
523        assert!(key.validate().is_err());
524
525        // Test key too small
526        let key = PublicKey::new(
527            SignatureAlgorithm::RsaPkcs1v15,
528            PublicKeyFormat::Pem,
529            vec![0u8; 128], // 1024 bits
530        );
531        assert!(key.validate().is_err());
532    }
533
534    #[test]
535    fn test_signature_creation() {
536        let sig = Signature::new(
537            SignatureAlgorithm::RsaPkcs1v15,
538            SignatureFormat::Base64,
539            b"mock_signature".to_vec(),
540        )
541        .with_key_id("test-key".to_string())
542        .with_created_at(1234567890)
543        .with_expires_at(1234567990);
544
545        assert_eq!(sig.algorithm, SignatureAlgorithm::RsaPkcs1v15);
546        assert_eq!(sig.format, SignatureFormat::Base64);
547        assert_eq!(sig.data, b"mock_signature");
548        assert_eq!(sig.key_id, Some("test-key".to_string()));
549        assert_eq!(sig.created_at, Some(1234567890));
550        assert_eq!(sig.expires_at, Some(1234567990));
551    }
552
553    #[test]
554    fn test_signature_decode_data() {
555        let sig = Signature::new(
556            SignatureAlgorithm::RsaPkcs1v15,
557            SignatureFormat::Raw,
558            b"raw_data".to_vec(),
559        );
560        assert_eq!(sig.decode_data().unwrap(), b"raw_data");
561
562        let sig = Signature::new(
563            SignatureAlgorithm::RsaPkcs1v15,
564            SignatureFormat::Hex,
565            b"48656c6c6f".to_vec(), // "Hello" in hex
566        );
567        assert_eq!(sig.decode_data().unwrap(), b"Hello");
568    }
569
570    #[test]
571    fn test_signature_config() {
572        let key = PublicKey::new(
573            SignatureAlgorithm::RsaPkcs1v15,
574            PublicKeyFormat::Pem,
575            vec![0u8; 256],
576        );
577        let sig = Signature::new(
578            SignatureAlgorithm::RsaPkcs1v15,
579            SignatureFormat::Raw,
580            b"signature".to_vec(),
581        );
582
583        let config = SignatureConfig::new(key, sig)
584            .with_signed_data(b"data".to_vec())
585            .with_ignore_expired(true);
586
587        assert!(config.validate().is_ok());
588    }
589
590    #[test]
591    fn test_signature_config_algorithm_mismatch() {
592        let key = PublicKey::new(
593            SignatureAlgorithm::RsaPkcs1v15,
594            PublicKeyFormat::Pem,
595            vec![0u8; 256],
596        );
597        let sig = Signature::new(
598            SignatureAlgorithm::Ecdsa,
599            SignatureFormat::Raw,
600            b"signature".to_vec(),
601        );
602
603        let config = SignatureConfig::new(key, sig);
604        assert!(config.validate().is_err());
605    }
606
607    #[test]
608    fn test_mock_verifier() {
609        let verifier = MockVerifier::new(SignatureAlgorithm::RsaPkcs1v15, true);
610        assert_eq!(verifier.algorithm(), SignatureAlgorithm::RsaPkcs1v15);
611
612        let key = PublicKey::new(
613            SignatureAlgorithm::RsaPkcs1v15,
614            PublicKeyFormat::Pem,
615            vec![0u8; 256],
616        );
617        let sig = Signature::new(
618            SignatureAlgorithm::RsaPkcs1v15,
619            SignatureFormat::Raw,
620            b"signature".to_vec(),
621        );
622        let config = SignatureConfig::new(key, sig);
623
624        assert!(verifier.verify(b"data", &config).unwrap());
625
626        let verifier = MockVerifier::new(SignatureAlgorithm::RsaPkcs1v15, false);
627        assert!(!verifier.verify(b"data", &config).unwrap());
628    }
629
630    #[test]
631    fn test_signature_manager() {
632        let manager = SignatureManager::new();
633
634        assert!(manager.supports_algorithm(SignatureAlgorithm::RsaPkcs1v15));
635        assert!(manager.supports_algorithm(SignatureAlgorithm::Ecdsa));
636
637        let algorithms = manager.supported_algorithms();
638        assert!(algorithms.contains(&SignatureAlgorithm::RsaPkcs1v15));
639        assert!(algorithms.contains(&SignatureAlgorithm::Ecdsa));
640
641        let key = PublicKey::new(
642            SignatureAlgorithm::RsaPkcs1v15,
643            PublicKeyFormat::Pem,
644            vec![0u8; 256],
645        );
646        let sig = Signature::new(
647            SignatureAlgorithm::RsaPkcs1v15,
648            SignatureFormat::Raw,
649            b"signature".to_vec(),
650        );
651        let config = SignatureConfig::new(key, sig);
652
653        // Mock verifier returns true
654        assert!(manager.verify(b"data", &config).unwrap());
655    }
656
657    #[test]
658    fn test_convenience_function() {
659        let key = PublicKey::new(
660            SignatureAlgorithm::RsaPkcs1v15,
661            PublicKeyFormat::Pem,
662            vec![0u8; 256],
663        );
664        let sig = Signature::new(
665            SignatureAlgorithm::RsaPkcs1v15,
666            SignatureFormat::Raw,
667            b"signature".to_vec(),
668        );
669        let config = SignatureConfig::new(key, sig);
670
671        // Mock verifier returns true
672        assert!(verify_signature(b"data", &config).unwrap());
673    }
674}