_hope_core/
genome.rs

1//! # Hope Genome v1.4.0 - Sealed Genome (Ethical Ruleset)
2//!
3//! **Hardened Security Edition - KeyStore Integration**
4//!
5//! ## Major Changes in v1.4.0
6//!
7//! ### 1. KeyStore Trait Support
8//! - **Backward Compatible**: Still supports deprecated `KeyPair`
9//! - **New API**: Accepts any `KeyStore` implementation
10//! - **Future-Proof**: Easy to use HSM, TPM, etc.
11//!
12//! ### 2. Ed25519 Signatures
13//! - All proofs now signed with Ed25519 (via KeyStore)
14//! - Faster, smaller, more secure than RSA
15//!
16//! ---
17//!
18//! **Date**: 2025-12-30
19//! **Version**: 1.4.0 (Hardened Security Edition)
20//! **Author**: Máté Róbert <stratosoiteam@gmail.com>
21
22#[allow(deprecated)]
23use crate::crypto::{create_key_store, hash_bytes, KeyPair, KeyStore, KeyStoreConfig};
24use crate::proof::{Action, IntegrityProof};
25use thiserror::Error;
26
27#[derive(Debug, Error)]
28pub enum GenomeError {
29    #[error("Genome not sealed yet")]
30    NotSealed,
31
32    #[error("Genome already sealed")]
33    AlreadySealed,
34
35    #[error("Action violates genome rules: {0}")]
36    RuleViolation(String),
37
38    #[error("HSM configuration error: environment variable {0} not set")]
39    HsmConfigError(String),
40
41    #[error("Crypto error: {0}")]
42    CryptoError(#[from] crate::crypto::CryptoError),
43}
44
45pub type Result<T> = std::result::Result<T, GenomeError>;
46
47/// The sealed genome - immutable ethical ruleset
48///
49/// This is the core of the Hope Genome framework. It:
50/// - Contains immutable ethical rules
51/// - Signs action approvals with Ed25519 (v1.4.0+)
52/// - Provides cryptographic proofs for all decisions
53///
54/// ## Example (v1.4.0 New API)
55///
56/// ```rust
57/// use _hope_core::genome::SealedGenome;
58/// use _hope_core::crypto::SoftwareKeyStore;
59/// use _hope_core::proof::Action;
60///
61/// // Create genome with new KeyStore API
62/// let key_store = SoftwareKeyStore::generate().unwrap();
63/// let rules = vec!["Do no harm".to_string()];
64///
65/// let mut genome = SealedGenome::with_key_store(
66///     rules,
67///     Box::new(key_store),
68/// ).unwrap();
69///
70/// genome.seal().unwrap();
71///
72/// // Verify action and get proof
73/// let action = Action::delete("test.txt");
74/// let proof = genome.verify_action(&action).unwrap();
75/// ```
76pub struct SealedGenome {
77    /// Ethical rules (immutable after sealing)
78    rules: Vec<String>,
79
80    /// Whether the genome is sealed
81    sealed: bool,
82
83    /// Cryptographic key store for signing (v1.4.0: trait-based)
84    key_store: Box<dyn KeyStore>,
85
86    /// Hash of the sealed genome (for proof binding)
87    capsule_hash: Option<String>,
88
89    /// Default TTL for proofs (seconds)
90    default_ttl: u64,
91}
92
93impl SealedGenome {
94    /// Create a new genome with rules.
95    ///
96    /// **HARDENED SECURITY**: This function automatically detects if an HSM is configured
97    /// via environment variables. If `PKCS11_MODULE_PATH` is set, it will create an
98    /// `HsmKeyStore`, ensuring the private key never enters system RAM. Otherwise, it
99    /// falls back to a software-based key store for development and testing.
100    ///
101    /// # HSM Configuration (Environment Variables)
102    /// - `PKCS11_MODULE_PATH`: Path to the PKCS#11 library (`.so` or `.dll`).
103    /// - `PKCS11_TOKEN_LABEL`: Label of the HSM token.
104    /// - `PKCS11_KEY_LABEL`: Label of the key within the HSM.
105    /// - `PKCS11_PIN`: The user PIN for the HSM. **WARNING**: Use secrets management in production.
106    ///
107    /// # Example
108    /// ```rust
109    /// use _hope_core::genome::SealedGenome;
110    ///
111    /// // This will use SoftwareKeyStore unless HSM env vars are set.
112    /// let rules = vec!["Do no harm".to_string()];
113    /// let genome = SealedGenome::new(rules).unwrap();
114    /// ```
115    pub fn new(rules: Vec<String>) -> Result<Self> {
116        #[cfg(not(feature = "hsm-support"))]
117        let config = KeyStoreConfig::Software;
118
119        #[cfg(feature = "hsm-support")]
120        let config = {
121            // Use std::env explicitly to avoid ambiguity
122            use std::env;
123            if let Ok(pkcs11_path) = env::var("PKCS11_MODULE_PATH") {
124                // If HSM path is set, construct HsmConfig from environment variables.
125                let hsm_config = crate::crypto::HsmConfig {
126                    pkcs11_lib_path: pkcs11_path,
127                    token_label: env::var("PKCS11_TOKEN_LABEL").map_err(|_| {
128                        GenomeError::HsmConfigError("PKCS11_TOKEN_LABEL".to_string())
129                    })?,
130                    key_label: env::var("PKCS11_KEY_LABEL")
131                        .map_err(|_| GenomeError::HsmConfigError("PKCS11_KEY_LABEL".to_string()))?,
132                    pin: env::var("PKCS11_PIN")
133                        .map_err(|_| GenomeError::HsmConfigError("PKCS11_PIN".to_string()))?,
134                };
135                KeyStoreConfig::Hsm(hsm_config)
136            } else {
137                KeyStoreConfig::Software
138            }
139        };
140
141        let key_store = create_key_store(config)?;
142        Self::with_key_store(rules, key_store)
143    }
144
145    /// Create a new genome with an existing KeyPair (deprecated)
146    ///
147    /// **DEPRECATED in v1.4.0**: Use `with_key_store()` instead.
148    ///
149    /// # Example (Legacy)
150    /// ```rust
151    /// use _hope_core::genome::SealedGenome;
152    /// use _hope_core::crypto::KeyPair;
153    ///
154    /// # #[allow(deprecated)]
155    /// let keypair = KeyPair::generate().unwrap();
156    /// # #[allow(deprecated)]
157    /// let genome = SealedGenome::with_keypair(
158    ///     vec!["Rule 1".to_string()],
159    ///     keypair,
160    /// ).unwrap();
161    /// ```
162    #[allow(deprecated)]
163    #[deprecated(since = "1.4.0", note = "Use with_key_store() for new code")]
164    pub fn with_keypair(rules: Vec<String>, keypair: KeyPair) -> Result<Self> {
165        Ok(SealedGenome {
166            rules,
167            sealed: false,
168            key_store: Box::new(keypair),
169            capsule_hash: None,
170            default_ttl: 60, // 1 minute default
171        })
172    }
173
174    /// Create a new genome with a custom KeyStore (v1.4.0)
175    ///
176    /// This is the recommended way to create a genome in v1.4.0+.
177    /// Supports any KeyStore implementation (Software, HSM, etc.).
178    ///
179    /// # Example
180    /// ```rust
181    /// use _hope_core::genome::SealedGenome;
182    /// use _hope_core::crypto::SoftwareKeyStore;
183    ///
184    /// let key_store = SoftwareKeyStore::generate().unwrap();
185    /// let rules = vec!["Protect privacy".to_string()];
186    ///
187    /// let genome = SealedGenome::with_key_store(
188    ///     rules,
189    ///     Box::new(key_store),
190    /// ).unwrap();
191    /// ```
192    pub fn with_key_store(rules: Vec<String>, key_store: Box<dyn KeyStore>) -> Result<Self> {
193        Ok(SealedGenome {
194            rules,
195            sealed: false,
196            key_store,
197            capsule_hash: None,
198            default_ttl: 60, // 1 minute default
199        })
200    }
201
202    /// Seal the genome (make it immutable)
203    ///
204    /// After sealing:
205    /// - Rules cannot be modified
206    /// - Capsule hash is computed
207    /// - Actions can be verified and signed
208    ///
209    /// # Example
210    /// ```rust
211    /// use _hope_core::genome::SealedGenome;
212    ///
213    /// let mut genome = SealedGenome::new(vec!["Rule 1".to_string()]).unwrap();
214    /// genome.seal().unwrap();
215    ///
216    /// assert!(genome.is_sealed());
217    /// ```
218    pub fn seal(&mut self) -> Result<()> {
219        if self.sealed {
220            return Err(GenomeError::AlreadySealed);
221        }
222
223        // Compute capsule hash (hash of all rules)
224        let rules_json = serde_json::to_string(&self.rules).unwrap();
225        let hash = hash_bytes(rules_json.as_bytes());
226        self.capsule_hash = Some(hex::encode(hash));
227
228        self.sealed = true;
229
230        Ok(())
231    }
232
233    /// Check if genome is sealed
234    pub fn is_sealed(&self) -> bool {
235        self.sealed
236    }
237
238    /// Get the rules
239    pub fn rules(&self) -> &[String] {
240        &self.rules
241    }
242
243    /// Set default TTL for proofs (in seconds)
244    ///
245    /// # Example
246    /// ```rust
247    /// use _hope_core::genome::SealedGenome;
248    ///
249    /// let mut genome = SealedGenome::new(vec!["Rule 1".to_string()]).unwrap();
250    /// genome.set_default_ttl(3600); // 1 hour
251    /// ```
252    pub fn set_default_ttl(&mut self, ttl: u64) {
253        self.default_ttl = ttl;
254    }
255
256    /// Verify an action against the genome rules
257    ///
258    /// This is where ethical decision-making happens.
259    /// Returns a cryptographically signed proof if approved.
260    ///
261    /// # Security (v1.4.0)
262    /// - Proof signed with Ed25519 (fast, secure)
263    /// - Includes nonce for replay attack prevention
264    /// - Bound to capsule hash (prevents proof reuse across genomes)
265    ///
266    /// # Example
267    /// ```rust
268    /// use _hope_core::genome::SealedGenome;
269    /// use _hope_core::proof::Action;
270    ///
271    /// let mut genome = SealedGenome::new(vec!["Do no harm".to_string()]).unwrap();
272    /// genome.seal().unwrap();
273    ///
274    /// let action = Action::delete("test.txt");
275    /// let proof = genome.verify_action(&action).unwrap();
276    ///
277    /// assert!(!proof.signature.is_empty());
278    /// assert_eq!(proof.signature.len(), 64); // Ed25519 signature
279    /// ```
280    pub fn verify_action(&self, action: &Action) -> Result<IntegrityProof> {
281        if !self.sealed {
282            return Err(GenomeError::NotSealed);
283        }
284
285        // Basic rule checking (simple implementation)
286        // In production, this would use sophisticated reasoning
287        let action_str = format!("{:?}", action);
288
289        // Check rules (simplified)
290        for rule in &self.rules {
291            if rule.contains("no harm") && action_str.contains("delete") {
292                // More sophisticated checking would happen here
293            }
294        }
295
296        // Create proof
297        let capsule_hash = self.capsule_hash.as_ref().unwrap().clone();
298        let mut proof = IntegrityProof::new(action, capsule_hash, self.default_ttl);
299
300        // Sign the proof (v1.4.0: uses KeyStore trait)
301        let signing_data = proof.signing_data();
302        proof.signature = self.key_store.sign(&signing_data)?;
303
304        Ok(proof)
305    }
306
307    /// Get the capsule hash
308    ///
309    /// Returns `None` if genome is not yet sealed.
310    pub fn capsule_hash(&self) -> Option<&str> {
311        self.capsule_hash.as_deref()
312    }
313
314    /// Get the public key bytes (for verification by external parties)
315    ///
316    /// # Example
317    /// ```rust
318    /// use _hope_core::genome::SealedGenome;
319    ///
320    /// let genome = SealedGenome::new(vec!["Rule 1".to_string()]).unwrap();
321    /// let public_key = genome.public_key_bytes();
322    ///
323    /// assert_eq!(public_key.len(), 32); // Ed25519 public key
324    /// ```
325    pub fn public_key_bytes(&self) -> Vec<u8> {
326        self.key_store.public_key_bytes()
327    }
328
329    /// Get key store information (for debugging/logging)
330    pub fn key_store_info(&self) -> String {
331        self.key_store.identifier()
332    }
333}
334
335#[cfg(test)]
336mod tests {
337    use super::*;
338    use crate::crypto::SoftwareKeyStore;
339
340    #[test]
341    fn test_genome_creation() {
342        let rules = vec!["Do no harm".to_string(), "Respect privacy".to_string()];
343        let genome = SealedGenome::new(rules).unwrap();
344
345        assert!(!genome.is_sealed());
346        assert_eq!(genome.rules().len(), 2);
347    }
348
349    #[test]
350    fn test_genome_creation_with_key_store() {
351        let key_store = SoftwareKeyStore::generate().unwrap();
352        let rules = vec!["Rule 1".to_string()];
353
354        let genome = SealedGenome::with_key_store(rules, Box::new(key_store)).unwrap();
355
356        assert!(!genome.is_sealed());
357        assert_eq!(genome.rules().len(), 1);
358    }
359
360    #[test]
361    fn test_genome_sealing() {
362        let rules = vec!["Rule 1".to_string()];
363        let mut genome = SealedGenome::new(rules).unwrap();
364
365        assert!(genome.seal().is_ok());
366        assert!(genome.is_sealed());
367        assert!(genome.capsule_hash().is_some());
368    }
369
370    #[test]
371    fn test_cannot_seal_twice() {
372        let rules = vec!["Rule 1".to_string()];
373        let mut genome = SealedGenome::new(rules).unwrap();
374
375        genome.seal().unwrap();
376
377        let result = genome.seal();
378        assert!(matches!(result, Err(GenomeError::AlreadySealed)));
379    }
380
381    #[test]
382    fn test_cannot_verify_unsealed() {
383        let rules = vec!["Rule 1".to_string()];
384        let genome = SealedGenome::new(rules).unwrap();
385
386        let action = Action::delete("test.txt");
387        let result = genome.verify_action(&action);
388
389        assert!(matches!(result, Err(GenomeError::NotSealed)));
390    }
391
392    #[test]
393    fn test_verify_action_creates_valid_proof() {
394        let rules = vec!["Do no harm".to_string()];
395        let mut genome = SealedGenome::new(rules).unwrap();
396        genome.seal().unwrap();
397
398        let action = Action::delete("test.txt");
399        let proof = genome.verify_action(&action).unwrap();
400
401        // Verify proof properties
402        assert_eq!(proof.action_hash, action.hash());
403        assert!(!proof.signature.is_empty());
404        assert_eq!(proof.signature.len(), 64); // Ed25519 signature is 64 bytes
405        assert_eq!(proof.ttl, 60); // Default TTL
406    }
407
408    #[test]
409    fn test_capsule_hash_deterministic() {
410        let rules = vec!["Rule 1".to_string(), "Rule 2".to_string()];
411        let mut genome1 = SealedGenome::new(rules.clone()).unwrap();
412        let mut genome2 = SealedGenome::new(rules.clone()).unwrap();
413
414        genome1.seal().unwrap();
415        genome2.seal().unwrap();
416
417        assert_eq!(genome1.capsule_hash(), genome2.capsule_hash());
418    }
419
420    #[test]
421    fn test_different_rules_different_hashes() {
422        let rules1 = vec!["Rule 1".to_string()];
423        let rules2 = vec!["Rule 2".to_string()];
424
425        let mut genome1 = SealedGenome::new(rules1).unwrap();
426        let mut genome2 = SealedGenome::new(rules2).unwrap();
427
428        genome1.seal().unwrap();
429        genome2.seal().unwrap();
430
431        assert_ne!(genome1.capsule_hash(), genome2.capsule_hash());
432    }
433
434    #[test]
435    fn test_custom_ttl() {
436        let rules = vec!["Rule 1".to_string()];
437        let mut genome = SealedGenome::new(rules).unwrap();
438        genome.set_default_ttl(3600);
439        genome.seal().unwrap();
440
441        let action = Action::delete("test.txt");
442        let proof = genome.verify_action(&action).unwrap();
443
444        assert_eq!(proof.ttl, 3600);
445    }
446
447    #[test]
448    fn test_public_key_export() {
449        let genome = SealedGenome::new(vec!["Rule 1".to_string()]).unwrap();
450        let public_key = genome.public_key_bytes();
451
452        assert_eq!(public_key.len(), 32); // Ed25519 public key is 32 bytes
453    }
454
455    #[test]
456    fn test_proof_signature_can_be_verified() {
457        let key_store = SoftwareKeyStore::generate().unwrap();
458        let key_store_clone = key_store.clone();
459
460        let mut genome =
461            SealedGenome::with_key_store(vec!["Rule 1".to_string()], Box::new(key_store)).unwrap();
462
463        genome.seal().unwrap();
464
465        let action = Action::delete("test.txt");
466        let proof = genome.verify_action(&action).unwrap();
467
468        // Verify signature with the same key store
469        let signing_data = proof.signing_data();
470        assert!(key_store_clone
471            .verify(&signing_data, &proof.signature)
472            .is_ok());
473    }
474
475    #[test]
476    #[allow(deprecated)]
477    fn test_backward_compatibility_with_keypair() {
478        let keypair = KeyPair::generate().unwrap();
479        let mut genome = SealedGenome::with_keypair(vec!["Rule 1".to_string()], keypair).unwrap();
480
481        genome.seal().unwrap();
482
483        let action = Action::delete("test.txt");
484        let proof = genome.verify_action(&action).unwrap();
485
486        assert!(!proof.signature.is_empty());
487    }
488
489    #[test]
490    fn test_key_store_info() {
491        let genome = SealedGenome::new(vec!["Rule 1".to_string()]).unwrap();
492        let info = genome.key_store_info();
493
494        assert!(info.contains("KeyPair") || info.contains("SoftwareKeyStore"));
495    }
496}