smart_locker/commands/
decrypt.rs

1use crate::commands::migrate::migrate_metadata;
2use crate::utils::config::EncryptionConfig;
3use crate::utils::metadata::{
4    has_this_secret_metadata, is_secret_expired, mark_secret_as_expired, read_metadata,
5};
6use crate::utils::toolbox::{get_locker_dir, is_this_secret};
7use crate::LockerResult;
8use crate::MetadataFile;
9use crate::SmartLockerError;
10use aes_gcm::aead::Aead;
11use aes_gcm::Nonce;
12use colored::Colorize;
13use flate2::read::GzDecoder;
14use std::fs;
15use std::io::{self, Read, Write};
16
17pub fn decrypt(name: &str) -> LockerResult<String> {
18    let config = EncryptionConfig::new();
19    let locker_dir = get_locker_dir()?;
20    let key_path = locker_dir.join("locker.key");
21    let secret_path = locker_dir.join(format!("{}.slock", name));
22
23    // Vérifier si le fichier est un secret valide
24    let (is_valid, _secret_name) = is_this_secret(&secret_path, false);
25    if !is_valid {
26        return Err(SmartLockerError::FileSystemError(format!(
27            "The file '{}' is not a valid secret file.",
28            secret_path.display()
29        )));
30    }
31
32    // Charger les métadonnées
33    let metadata_result = read_metadata();
34    let mut metadata = metadata_result.unwrap_or_else(|_| MetadataFile {
35        secrets: Default::default(),
36    });
37
38    // Vérifier les métadonnées
39    if !has_this_secret_metadata(name, &metadata) {
40        println!(
41            "{}",
42            format!(
43                "⚠️ Metadata for secret '{}' is missing or outdated. Do you want to migrate it? (yes/no): ",
44                name
45            )
46            .yellow()
47        );
48
49        // Lire la saisie utilisateur
50        let mut input = String::new();
51        io::stdout().flush().unwrap();
52        io::stdin().read_line(&mut input).unwrap();
53        let input = input.trim().to_lowercase();
54
55        if input == "yes" {
56            println!(
57                "{}",
58                format!("Migrating metadata for secret '{}'...", name).blue()
59            );
60            migrate_metadata(Some(name))?;
61            println!(
62                "{}",
63                "✅ Metadata migration completed successfully.".green()
64            );
65            metadata = read_metadata()?; // Relire les métadonnées après migration
66        } else {
67            return Err(SmartLockerError::DecryptionError(format!(
68                "Metadata for secret '{}' is invalid. Migration was skipped.",
69                name
70            )));
71        }
72    }
73
74    // Vérifier si le secret est présent dans les métadonnées
75    let secret_metadata = metadata.secrets.get(name).ok_or_else(|| {
76        SmartLockerError::DecryptionError(format!(
77            "Metadata for secret '{}' is missing after migration.",
78            name
79        ))
80    })?;
81
82    // Vérifier si le secret est expiré
83    if is_secret_expired(secret_metadata) {
84        mark_secret_as_expired(name, &mut metadata)?;
85        return Err(SmartLockerError::DecryptionError(format!(
86            "The secret '{}' has expired. Please renew it to use it again.",
87            name
88        )));
89    }
90
91    // Lire le fichier chiffré
92    let encrypted_data = fs::read(&secret_path).map_err(|_| {
93        SmartLockerError::FileSystemError("Unable to read the encrypted file".to_string())
94    })?;
95
96    // Vérifier la signature
97    if !encrypted_data.starts_with(config.signature) {
98        return Err(SmartLockerError::DecryptionError(format!(
99            "The secret '{}' is not in the current format. Please re-encrypt it using the latest version of smart-locker.",
100            name
101        )));
102    }
103
104    // Vérifier la version
105    let version = encrypted_data[config.signature.len()];
106    if version != config.format_version {
107        return Err(SmartLockerError::DecryptionError(format!(
108            "The secret '{}' uses an unsupported format version ({}). Please update smart-locker.",
109            name, version
110        )));
111    }
112
113    // Extraire le nonce et les données chiffrées
114    let data_without_header = &encrypted_data[config.signature.len() + 1..];
115    let (nonce, ciphertext) = data_without_header.split_at(config.nonce_size);
116    let nonce = Nonce::from_slice(nonce);
117
118    // Lire la clé symétrique
119    let key_data = fs::read(&key_path).map_err(|_| {
120        SmartLockerError::FileSystemError("Unable to read the symmetric key".to_string())
121    })?;
122    let cipher = config
123        .init_cipher(&key_data)
124        .map_err(SmartLockerError::DecryptionError)?;
125
126    // Déchiffrer les données
127    let decrypted_data = cipher
128        .decrypt(nonce, ciphertext)
129        .map_err(|_| SmartLockerError::DecryptionError("Decryption failed".to_string()))?;
130
131    // Décompresser les données
132    let mut decoder = GzDecoder::new(&decrypted_data[..]);
133    let mut decompressed_data = String::new();
134    decoder
135        .read_to_string(&mut decompressed_data)
136        .map_err(|_| {
137            SmartLockerError::FileSystemError("Failed to decompress the data".to_string())
138        })?;
139    Ok(decompressed_data)
140}