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}