1use std::io::{Read, Write};
2
3#[derive(Debug)]
5pub struct CryptoEngine {
6 recipients: Vec<crate::crypto::keys::RecipientKey>,
7 identity: Option<crate::crypto::keys::IdentityKey>,
8}
9
10impl CryptoEngine {
11 pub fn new(recipients: Vec<crate::crypto::keys::RecipientKey>) -> Self {
13 Self {
14 recipients,
15 identity: None,
16 }
17 }
18
19 pub fn encrypt(&self, plaintext: &[u8]) -> crate::error::Result<Vec<u8>> {
21 if self.recipients.is_empty() {
22 return Err(crate::error::MnemeError::NoRecipientsConfigured);
23 }
24
25 let mut age_recipients: Vec<Box<dyn age::Recipient + Send>> = Vec::new();
26
27 for key in &self.recipients {
28 match key {
29 crate::crypto::keys::RecipientKey::Age(s) => {
30 let recipient: age::x25519::Recipient = s.parse().map_err(|e: &str| {
31 crate::error::MnemeError::Config(format!("invalid age key: {}", e))
32 })?;
33 age_recipients.push(Box::new(recipient));
34 }
35 crate::crypto::keys::RecipientKey::Ssh(s) => {
36 let recipient: age::ssh::Recipient =
37 s.parse().map_err(|e: age::ssh::ParseRecipientKeyError| {
38 crate::error::MnemeError::Config(format!("invalid ssh key: {:?}", e))
39 })?;
40 age_recipients.push(Box::new(recipient));
41 }
42 }
43 }
44
45 let encryptor = age::Encryptor::with_recipients(age_recipients)
46 .ok_or(crate::error::MnemeError::NoRecipientsConfigured)?;
47
48 let mut ciphertext = vec![];
49 let mut writer = encryptor
50 .wrap_output(&mut ciphertext)
51 .map_err(|e| crate::error::MnemeError::Config(format!("wrap output error: {}", e)))?;
52 writer.write_all(plaintext)?;
53 writer
54 .finish()
55 .map_err(|e| crate::error::MnemeError::Config(format!("finish error: {}", e)))?;
56
57 Ok(ciphertext)
58 }
59
60 pub fn encrypt_str(&self, plaintext: &str) -> crate::error::Result<String> {
62 let ciphertext = self.encrypt(plaintext.as_bytes())?;
63 Ok(hex::encode(ciphertext))
64 }
65
66 pub fn decrypt_str(&mut self, ciphertext_hex: &str) -> crate::error::Result<String> {
68 let ciphertext = hex::decode(ciphertext_hex)
69 .map_err(|e| crate::error::MnemeError::Config(format!("hex decode error: {}", e)))?;
70
71 let identity = self
72 .identity
73 .as_ref()
74 .ok_or(crate::error::MnemeError::IdentityNotLoaded)?;
75
76 let decrypted = match identity {
77 crate::crypto::keys::IdentityKey::Ssh(path) => {
78 let key_content = std::fs::read_to_string(path)?;
79 let identity = age::ssh::Identity::from_buffer(
80 std::io::BufReader::new(key_content.as_bytes()),
81 Some(path.display().to_string()),
82 )
83 .map_err(|_e| crate::error::MnemeError::DecryptionFailed)?;
84
85 let decryptor = match age::Decryptor::new(ciphertext.as_slice())
86 .map_err(|_| crate::error::MnemeError::DecryptionFailed)?
87 {
88 age::Decryptor::Recipients(d) => d,
89 _ => return Err(crate::error::MnemeError::DecryptionFailed),
90 };
91
92 let mut reader = decryptor
93 .decrypt(std::iter::once(&identity as &dyn age::Identity))
94 .map_err(|_| crate::error::MnemeError::DecryptionFailed)?;
95 let mut output = String::new();
96 reader.read_to_string(&mut output)?;
97 output
98 }
99 crate::crypto::keys::IdentityKey::Age(path) => {
100 let key_content = std::fs::read_to_string(path)?;
101 let identity: age::x25519::Identity = key_content
102 .trim()
103 .parse()
104 .map_err(|_e: &str| crate::error::MnemeError::DecryptionFailed)?;
105
106 let decryptor = match age::Decryptor::new(ciphertext.as_slice())
107 .map_err(|_| crate::error::MnemeError::DecryptionFailed)?
108 {
109 age::Decryptor::Recipients(d) => d,
110 _ => return Err(crate::error::MnemeError::DecryptionFailed),
111 };
112
113 let mut reader = decryptor
114 .decrypt(std::iter::once(&identity as &dyn age::Identity))
115 .map_err(|_| crate::error::MnemeError::DecryptionFailed)?;
116 let mut output = String::new();
117 reader.read_to_string(&mut output)?;
118 output
119 }
120 };
121
122 Ok(decrypted)
123 }
124
125 pub fn can_decrypt(&self) -> bool {
127 self.identity.is_some()
128 }
129
130 pub fn load_identity(&mut self) -> crate::error::Result<()> {
132 let identity = crate::crypto::keys::IdentityKey::detect()?;
133 self.identity = Some(identity);
134 Ok(())
135 }
136
137 pub fn load_identity_from_path(
139 &mut self,
140 path: &std::path::PathBuf,
141 ) -> crate::error::Result<()> {
142 let identity = crate::crypto::keys::IdentityKey::from_path(path)?;
143 self.identity = Some(identity);
144 Ok(())
145 }
146
147 pub fn has_recipients(&self) -> bool {
149 !self.recipients.is_empty()
150 }
151
152 pub fn encrypted_for_label(&self) -> String {
154 self.recipients
155 .iter()
156 .map(|r| r.key_type().to_string())
157 .collect::<Vec<_>>()
158 .join(",")
159 }
160}