atlas_cli/signing/
signable.rs

1//! # Signable Trait
2//!
3//! This module defines the `Signable` trait, which provides a common interface for
4//! cryptographically signing data structures using private keys and hash algorithms.
5//! The trait is designed to be implemented by any type that needs to support
6//! digital signatures for authentication and integrity verification.
7//!
8//! ## Supported Hash Algorithms
9//!
10//! The trait supports various hash algorithms through the `atlas_c2pa_lib::cose::HashAlgorithm`
11//! enum, including:
12//! - SHA-256
13//! - SHA-384
14//! - SHA-512
15//!
16//! ## Examples
17//!
18//! ### Implementing Signable for a Custom Type
19//!
20//! ```
21//! use atlas_cli::signing::signable::Signable;
22//! use atlas_cli::error::Result;
23//! use atlas_c2pa_lib::cose::HashAlgorithm;
24//! use std::path::PathBuf;
25//!
26//! struct MyDocument {
27//!     content: String,
28//!     signature: Option<Vec<u8>>,
29//! }
30//!
31//! impl Signable for MyDocument {
32//!     fn sign(&mut self, key_path: PathBuf, hash_alg: HashAlgorithm) -> Result<()> {
33//!         // Implementation would:
34//!         // 1. Load the private key from key_path
35//!         // 2. Hash the document content using hash_alg
36//!         // 3. Sign the hash with the private key
37//!         // 4. Store the signature in self.signature
38//!         Ok(())
39//!     }
40//! }
41//! ```
42//!
43//! ### Using a Signable Type
44//!
45//! ```no_run
46//! use atlas_cli::in_toto::dsse::Envelope;
47//! use atlas_cli::signing::signable::Signable;
48//! use atlas_c2pa_lib::cose::HashAlgorithm;
49//! use std::path::PathBuf;
50//!
51//! let mut envelope = Envelope::new(&b"data".to_vec(), "text/plain".to_string());
52//!
53//! // Sign with a private key using SHA-384
54//! envelope.sign(
55//!     PathBuf::from("private_key.pem"),
56//!     HashAlgorithm::Sha384
57//! ).unwrap();
58//! ```
59
60use crate::error::Result;
61
62use atlas_c2pa_lib::cose::HashAlgorithm;
63use std::path::PathBuf;
64
65/// A trait for types that can be cryptographically signed.
66///
67/// This module defines the `Signable` trait, which provides a common interface for
68/// cryptographically signing data structures using private keys and hash algorithms.
69/// The trait is designed to be implemented by any type that needs to support
70/// digital signatures for authentication and integrity verification.
71///
72/// ## Implementation Recommendations
73///
74/// Implementations should:
75/// 1. Load the private key from the provided path
76/// 2. Prepare the data to be signed (serialization, canonicalization, etc.)
77/// 3. Create a cryptographic signature using the specified hash algorithm
78/// 4. Store or attach the signature to the data structure
79/// 5. Return appropriate errors for any failure conditions
80///
81/// ## Error Handling
82///
83/// Implementations should return errors for common failure scenarios:
84/// - Invalid or missing private key files
85/// - Unsupported key formats or algorithms
86/// - Cryptographic operation failures
87/// - Data preparation or serialization errors
88///
89/// ## Examples
90///
91/// ### Basic Implementation Pattern
92///
93/// ```no_run
94/// use atlas_cli::signing::signable::Signable;
95/// use atlas_cli::error::Result;
96/// use atlas_c2pa_lib::cose::HashAlgorithm;
97/// use std::path::PathBuf;
98///
99/// struct SignableDocument {
100///     data: Vec<u8>,
101///     signatures: Vec<Vec<u8>>,
102/// }
103///
104/// impl Signable for SignableDocument {
105///     fn sign(&mut self, key_path: PathBuf, hash_alg: HashAlgorithm) -> Result<()> {
106///         // 1. Load private key (implementation-specific)
107///         // let private_key = load_private_key(&key_path)?;
108///         
109///         // 2. Prepare data for signing
110///         // let data_to_sign = prepare_signing_data(&self.data);
111///         
112///         // 3. Create signature
113///         // let signature = create_signature(&data_to_sign, &private_key, &hash_alg)?;
114///         
115///         // 4. Store signature
116///         // self.signatures.push(signature);
117///         
118///         Ok(())
119///     }
120/// }
121/// ```
122///
123/// ### Usage with Different Hash Algorithms
124///
125/// ```no_run
126/// use atlas_cli::signing::signable::Signable;
127/// use atlas_c2pa_lib::cose::HashAlgorithm;
128/// use std::path::PathBuf;
129/// # use atlas_cli::in_toto::dsse::Envelope;
130///
131/// let mut envelope = Envelope::new(&b"important data".to_vec(), "text/plain".to_string());
132/// let key_path = PathBuf::from("signing_key.pem");
133///
134/// // Sign with SHA-256
135/// envelope.sign(key_path.clone(), HashAlgorithm::Sha256).unwrap();
136///
137/// // Sign with SHA-384 for higher security
138/// envelope.sign(key_path.clone(), HashAlgorithm::Sha384).unwrap();
139/// ```
140pub trait Signable {
141    /// Signs the implementing type using a private key and hash algorithm.
142    ///
143    /// This method performs cryptographic signing of the data structure using
144    /// the specified private key and hash algorithm. The exact signing process
145    /// depends on the implementation.
146    ///
147    /// ## Arguments
148    ///
149    /// * `key_path` - Path to the private key file (PEM, PKCS#8, etc.)
150    /// * `hash_alg` - Hash algorithm to use for signature generation
151    ///
152    /// ## Returns
153    ///
154    /// Returns `Ok(())` on successful signing, or an error if signing fails.
155    ///
156    /// ## Errors
157    ///
158    /// This method may return errors in the following situations:
159    /// - **Key Loading**: Private key file doesn't exist, is corrupted, or has wrong format
160    /// - **Unsupported Algorithm**: The hash algorithm is not supported by the key type
161    /// - **Cryptographic Failure**: The signing operation fails due to invalid key or data
162    /// - **Storage Failure**: Unable to attach the signature to the data structure
163    ///
164    /// ## Examples
165    ///
166    /// ### Signing a DSSE Envelope
167    ///
168    /// ```no_run
169    /// use atlas_cli::in_toto::dsse::Envelope;
170    /// use atlas_cli::signing::signable::Signable;
171    /// use atlas_c2pa_lib::cose::HashAlgorithm;
172    /// use std::path::PathBuf;
173    ///
174    /// let mut envelope = Envelope::new(&b"payload data".to_vec(), "application/json".to_string());
175    ///
176    /// // Sign with an RSA private key using SHA-384
177    /// envelope.sign(
178    ///     PathBuf::from("rsa_private_key.pem"),
179    ///     HashAlgorithm::Sha384
180    /// ).unwrap();
181    ///
182    /// // Verify the envelope now has a signature
183    /// assert!(envelope.validate());
184    /// assert!(!envelope.signatures().is_empty());
185    /// ```
186    ///
187    /// ### Error Handling
188    ///
189    /// ```no_run
190    /// use atlas_cli::in_toto::dsse::Envelope;
191    /// use atlas_cli::signing::signable::Signable;
192    /// use atlas_c2pa_lib::cose::HashAlgorithm;
193    /// use std::path::PathBuf;
194    ///
195    /// let mut envelope = Envelope::new(&b"data".to_vec(), "text/plain".to_string());
196    ///
197    /// // Handle signing errors gracefully
198    /// match envelope.sign(PathBuf::from("nonexistent_key.pem"), HashAlgorithm::Sha256) {
199    ///     Ok(()) => println!("Signing successful"),
200    ///     Err(e) => eprintln!("Signing failed: {}", e),
201    /// }
202    /// ```
203    fn sign(&mut self, key_path: PathBuf, hash_alg: HashAlgorithm) -> Result<()>;
204}