Skip to main content

cargo_mate/captain/
binary_encryptor.rs

1use anyhow::{Context, Result};
2use std::fs;
3use std::path::Path;
4use base64::{Engine as _, engine::general_purpose};
5pub struct BinaryEncryptor {
6    encryption_key: Vec<u8>,
7}
8impl BinaryEncryptor {
9    pub fn new(key: &str) -> Self {
10        use sha2::{Sha256, Digest};
11        let mut hasher = Sha256::new();
12        hasher.update(key.as_bytes());
13        let encryption_key = hasher.finalize().to_vec();
14        Self { encryption_key }
15    }
16    pub fn encrypt_binary(&self, input_path: &Path, output_path: &Path) -> Result<()> {
17        let binary_data = fs::read(input_path)
18            .with_context(|| {
19                format!("Failed to read binary: {}", input_path.display())
20            })?;
21        let encrypted_data = self.xor_encrypt(&binary_data);
22        fs::write(output_path, encrypted_data)
23            .with_context(|| {
24                format!("Failed to write encrypted binary: {}", output_path.display())
25            })?;
26        Ok(())
27    }
28    pub fn create_self_decrypting_binary(
29        &self,
30        input_path: &Path,
31        output_path: &Path,
32        platform: &str,
33    ) -> Result<()> {
34        let binary_data = fs::read(input_path)
35            .with_context(|| {
36                format!("Failed to read binary: {}", input_path.display())
37            })?;
38        let encrypted_data = self.xor_encrypt(&binary_data);
39        let _loader_code = self.generate_loader_code(&encrypted_data, platform);
40        let mut output_data = b"CARGO_MATE_ENCRYPTED_BINARY_V1\n".to_vec();
41        output_data.extend_from_slice(&self.encryption_key);
42        output_data.extend_from_slice(b"\n");
43        output_data.extend_from_slice(&encrypted_data);
44        fs::write(output_path, output_data)
45            .with_context(|| {
46                format!(
47                    "Failed to write self-decrypting binary: {}", output_path.display()
48                )
49            })?;
50        println!("🔐 Created self-decrypting binary: {}", output_path.display());
51        Ok(())
52    }
53    fn xor_encrypt(&self, data: &[u8]) -> Vec<u8> {
54        data.iter()
55            .enumerate()
56            .map(|(i, &byte)| byte ^ self.encryption_key[i % self.encryption_key.len()])
57            .collect()
58    }
59    fn xor_decrypt(&self, data: &[u8]) -> Vec<u8> {
60        self.xor_encrypt(data)
61    }
62    fn generate_loader_code(&self, encrypted_data: &[u8], platform: &str) -> String {
63        let encrypted_b64 = general_purpose::STANDARD.encode(encrypted_data);
64        let key_b64 = general_purpose::STANDARD.encode(&self.encryption_key);
65        format!(
66            r#"
67// Auto-generated loader for encrypted cargo-mate binary
68// Platform: {}
69
70use std::process;
71use std::io::Write;
72use base64::{{Engine as _, engine::general_purpose}};
73
74fn main() -> Result<(), Box<dyn std::error::Error>> {{
75    // Embedded encrypted binary and key
76    let encrypted_b64 = "{}";
77    let key_b64 = "{}";
78
79    // Decode the data
80    let encrypted_data = general_purpose::STANDARD.decode(encrypted_b64)?;
81    let key = general_purpose::STANDARD.decode(key_b64)?;
82
83    // Decrypt the binary
84    let decrypted_data = decrypt_binary(&encrypted_data, &key);
85
86    // Execute the decrypted binary in memory
87    execute_in_memory(&decrypted_data)?;
88
89    Ok(())
90}}
91
92fn decrypt_binary(data: &[u8], key: &[u8]) -> Vec<u8> {{
93    data.iter()
94        .enumerate()
95        .map(|(i, &byte)| byte ^ key[i % key.len()])
96        .collect()
97}}
98
99fn execute_in_memory(binary_data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {{
100    // Create a temporary file for the decrypted binary
101    let temp_path = std::env::temp_dir().join("cargo_mate_decrypted");
102
103    // Write decrypted binary to temp file
104    std::fs::write(&temp_path, binary_data)?;
105
106    // Make it executable
107    #[cfg(unix)]
108    {{
109        use std::os::unix::fs::PermissionsExt;
110        let mut perms = std::fs::metadata(&temp_path)?.permissions();
111        perms.set_mode(0o755);
112        std::fs::set_permissions(&temp_path, perms)?;
113    }}
114
115    // Execute the binary
116    let status = process::Command::new(&temp_path)
117        .args(std::env::args().skip(1))
118        .status()?;
119
120    // Clean up
121    let _ = std::fs::remove_file(&temp_path);
122
123    if !status.success() {{
124        process::exit(status.code().unwrap_or(1));
125    }}
126
127    Ok(())
128}}
129        "#,
130            platform, encrypted_b64, key_b64
131        )
132    }
133}
134pub fn encrypt_releases_directory(
135    releases_dir: &Path,
136    encryption_key: &str,
137) -> Result<()> {
138    let encryptor = BinaryEncryptor::new(encryption_key);
139    for entry in fs::read_dir(releases_dir)? {
140        let entry = entry?;
141        let path = entry.path();
142        if path.extension().map_or(false, |ext| ext == "exe")
143            || path
144                .file_name()
145                .map_or(false, |name| !name.to_string_lossy().contains("."))
146        {
147            let encrypted_path = path
148                .with_extension(
149                    format!(
150                        "{}.encrypted", path.extension().unwrap_or_default()
151                        .to_string_lossy()
152                    ),
153                );
154            encryptor.encrypt_binary(&path, &encrypted_path)?;
155        }
156    }
157    Ok(())
158}
159pub fn create_self_decrypting_releases(
160    releases_dir: &Path,
161    encryption_key: &str,
162) -> Result<()> {
163    let encryptor = BinaryEncryptor::new(encryption_key);
164    for entry in fs::read_dir(releases_dir)? {
165        let entry = entry?;
166        let path = entry.path();
167        if let Some(file_name) = path.file_name() {
168            let file_name_str = file_name.to_string_lossy();
169            if file_name_str.contains("linux") || file_name_str.contains("macos")
170                || file_name_str.contains("windows")
171            {
172                let platform = if file_name_str.contains("linux") {
173                    "linux"
174                } else if file_name_str.contains("macos") {
175                    "macos"
176                } else if file_name_str.contains("windows") {
177                    "windows"
178                } else {
179                    "unknown"
180                };
181                let self_decrypting_path = path
182                    .with_extension(
183                        format!(
184                            "{}.self", path.extension().unwrap_or_default()
185                            .to_string_lossy()
186                        ),
187                    );
188                encryptor
189                    .create_self_decrypting_binary(
190                        &path,
191                        &self_decrypting_path,
192                        platform,
193                    )?;
194            }
195        }
196    }
197    Ok(())
198}
199pub fn encrypt_binary(data: &[u8]) -> Result<Vec<u8>> {
200    let encryption_key = "default_encryption_key_32_chars_long";
201    let encryptor = BinaryEncryptor::new(encryption_key);
202    Ok(encryptor.xor_encrypt(data))
203}
204pub fn decrypt_binary(encrypted_data: &[u8]) -> Result<Vec<u8>> {
205    let encryption_key = "default_encryption_key_32_chars_long";
206    let encryptor = BinaryEncryptor::new(encryption_key);
207    Ok(encryptor.xor_decrypt(encrypted_data))
208}