chaincraft_rust/crypto/
encrypt.rs1use crate::error::{ChaincraftError, CryptoError, Result};
4use fernet::Fernet;
5use std::str;
6
7#[derive(Clone)]
9pub struct SymmetricEncryption {
10 fernet: Fernet,
11 key: String,
12}
13
14impl SymmetricEncryption {
15 pub fn new(key: Option<&str>) -> Result<Self> {
17 let key = match key {
18 None => Fernet::generate_key(),
19 Some(k) => k.to_string(),
20 };
21 let fernet = Fernet::new(&key)
22 .ok_or_else(|| ChaincraftError::Crypto(CryptoError::EncryptionFailed {
23 reason: "Invalid Fernet key".to_string(),
24 }))?;
25 Ok(Self { fernet, key })
26 }
27
28 pub fn generate_key(&mut self) -> Result<String> {
30 self.key = Fernet::generate_key();
31 self.fernet = Fernet::new(&self.key).ok_or_else(|| {
32 ChaincraftError::Crypto(CryptoError::KeyGenerationFailed {
33 reason: "Fernet key generation failed".to_string(),
34 })
35 })?;
36 Ok(self.key.clone())
37 }
38
39 pub fn sign(&self, data: &[u8]) -> Result<Vec<u8>> {
41 let token = self.fernet.encrypt(data);
42 Ok(token.into_bytes())
43 }
44
45 pub fn verify(&self, data: &[u8], signature: &[u8]) -> Result<bool> {
47 let token = str::from_utf8(signature).map_err(|_| {
48 ChaincraftError::Crypto(CryptoError::DecryptionFailed {
49 reason: "Invalid UTF-8 token".to_string(),
50 })
51 })?;
52 let decrypted = self.fernet.decrypt(token).map_err(|_| {
53 ChaincraftError::Crypto(CryptoError::DecryptionFailed {
54 reason: "Decryption failed".to_string(),
55 })
56 })?;
57 Ok(decrypted == data)
58 }
59
60 pub fn encrypt(&self, plaintext: &str) -> Result<String> {
62 let token = self.fernet.encrypt(plaintext.as_bytes());
63 Ok(token)
64 }
65
66 pub fn decrypt(&self, ciphertext: &str) -> Result<String> {
68 let bytes = self.fernet.decrypt(ciphertext).map_err(|_| {
69 ChaincraftError::Crypto(CryptoError::DecryptionFailed {
70 reason: "Decryption failed".to_string(),
71 })
72 })?;
73 String::from_utf8(bytes).map_err(|e| {
74 ChaincraftError::Crypto(CryptoError::DecryptionFailed {
75 reason: format!("Invalid UTF-8: {}", e),
76 })
77 })
78 }
79
80 pub fn get_key(&self) -> &str {
82 &self.key
83 }
84}
85
86impl Default for SymmetricEncryption {
87 fn default() -> Self {
88 Self::new(None).expect("Fernet default keygen")
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn test_encrypt_decrypt_roundtrip() {
98 let enc = SymmetricEncryption::new(None).unwrap();
99 let msg = "Hello, Chaincraft!";
100 let ciphertext = enc.encrypt(msg).unwrap();
101 let decrypted = enc.decrypt(&ciphertext).unwrap();
102 assert_eq!(decrypted, msg);
103 }
104
105 #[test]
106 fn test_sign_verify() {
107 let enc = SymmetricEncryption::new(None).unwrap();
108 let data = b"secret bytes";
109 let sig = enc.sign(data).unwrap();
110 assert!(enc.verify(data, &sig).unwrap());
111 assert!(!enc.verify(b"wrong", &sig).unwrap());
112 }
113
114 #[test]
115 fn test_with_provided_key() {
116 let key = Fernet::generate_key();
117 let enc1 = SymmetricEncryption::new(Some(&key)).unwrap();
118 let enc2 = SymmetricEncryption::new(Some(&key)).unwrap();
119 let ct = enc1.encrypt("test").unwrap();
120 let pt = enc2.decrypt(&ct).unwrap();
121 assert_eq!(pt, "test");
122 }
123}