unarc_rs/
encryption.rs

1//! Encryption method detection for various archive formats.
2//!
3//! This module provides a unified way to represent encryption methods
4//! used by different archive formats.
5
6/// ZIP encryption methods
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum ZipEncryption {
9    /// Traditional PKWARE encryption (weak, easily cracked)
10    ZipCrypto,
11    /// WinZip AES-128 encryption
12    Aes128,
13    /// WinZip AES-192 encryption
14    Aes192,
15    /// WinZip AES-256 encryption
16    Aes256,
17    /// Encrypted but method unknown
18    Unknown,
19}
20
21impl std::fmt::Display for ZipEncryption {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        match self {
24            ZipEncryption::ZipCrypto => write!(f, "ZipCrypto"),
25            ZipEncryption::Aes128 => write!(f, "AES-128"),
26            ZipEncryption::Aes192 => write!(f, "AES-192"),
27            ZipEncryption::Aes256 => write!(f, "AES-256"),
28            ZipEncryption::Unknown => write!(f, "Unknown"),
29        }
30    }
31}
32
33/// RAR encryption methods
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35pub enum RarEncryption {
36    /// RAR 2.x proprietary encryption (weak)
37    Legacy,
38    /// RAR 3.x/4.x AES-128 encryption
39    Aes128,
40    /// RAR 5.x AES-256 with PBKDF2 key derivation
41    Aes256,
42    /// Encrypted but method unknown
43    Unknown,
44}
45
46impl std::fmt::Display for RarEncryption {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        match self {
49            RarEncryption::Legacy => write!(f, "RAR Legacy"),
50            RarEncryption::Aes128 => write!(f, "AES-128"),
51            RarEncryption::Aes256 => write!(f, "AES-256"),
52            RarEncryption::Unknown => write!(f, "Unknown"),
53        }
54    }
55}
56
57/// 7z encryption methods
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
59pub enum SevenZEncryption {
60    /// AES-256 with PBKDF2 key derivation
61    Aes256,
62    /// Encrypted but method unknown
63    Unknown,
64}
65
66impl std::fmt::Display for SevenZEncryption {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        match self {
69            SevenZEncryption::Aes256 => write!(f, "AES-256"),
70            SevenZEncryption::Unknown => write!(f, "Unknown"),
71        }
72    }
73}
74
75/// ARJ encryption methods
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
77pub enum ArjEncryption {
78    /// Standard "garble" encryption (XOR-based, very weak)
79    Garble,
80    /// GOST 28147-89 with 40-bit key (export-restricted, weak)
81    Gost40,
82    /// GOST 28147-89 with 256-bit key (requires ARJCRYPT module)
83    Gost256,
84    /// Encrypted but method unknown
85    Unknown,
86}
87
88impl std::fmt::Display for ArjEncryption {
89    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90        match self {
91            ArjEncryption::Garble => write!(f, "Garble (XOR)"),
92            ArjEncryption::Gost40 => write!(f, "GOST40"),
93            ArjEncryption::Gost256 => write!(f, "GOST-256"),
94            ArjEncryption::Unknown => write!(f, "Unknown"),
95        }
96    }
97}
98
99impl ArjEncryption {
100    /// Get encryption type from ARJ encryption version byte
101    pub fn from_version(version: u8, is_garbled: bool) -> Option<Self> {
102        if !is_garbled {
103            return None;
104        }
105        Some(match version {
106            0 | 1 => ArjEncryption::Garble,
107            2 => ArjEncryption::Gost40,
108            v if v >= 3 => ArjEncryption::Gost256,
109            _ => ArjEncryption::Unknown,
110        })
111    }
112}
113
114/// Unified encryption method enum
115///
116/// Represents the encryption method used by an archive entry.
117/// Each format has its own sub-enum for format-specific methods.
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
119pub enum EncryptionMethod {
120    /// Not encrypted
121    #[default]
122    None,
123    /// ZIP encryption
124    Zip(ZipEncryption),
125    /// RAR encryption
126    Rar(RarEncryption),
127    /// 7z encryption
128    SevenZ(SevenZEncryption),
129    /// ACE encryption (Blowfish-based)
130    Ace,
131    /// ARJ encryption
132    Arj(ArjEncryption),
133    /// Unknown encryption method (encrypted but method not determined)
134    Unknown,
135}
136
137impl EncryptionMethod {
138    /// Returns true if the entry is encrypted
139    pub fn is_encrypted(&self) -> bool {
140        !matches!(self, EncryptionMethod::None)
141    }
142
143    /// Returns true if the encryption is considered weak/insecure
144    pub fn is_weak(&self) -> bool {
145        matches!(
146            self,
147            EncryptionMethod::Zip(ZipEncryption::ZipCrypto)
148                | EncryptionMethod::Rar(RarEncryption::Legacy)
149                | EncryptionMethod::Arj(ArjEncryption::Garble)
150                | EncryptionMethod::Arj(ArjEncryption::Gost40)
151        )
152    }
153
154    /// Returns a human-readable description of the encryption method
155    pub fn description(&self) -> &'static str {
156        match self {
157            EncryptionMethod::None => "None",
158            EncryptionMethod::Zip(ZipEncryption::ZipCrypto) => "ZIP Traditional (weak)",
159            EncryptionMethod::Zip(ZipEncryption::Aes128) => "ZIP AES-128",
160            EncryptionMethod::Zip(ZipEncryption::Aes192) => "ZIP AES-192",
161            EncryptionMethod::Zip(ZipEncryption::Aes256) => "ZIP AES-256",
162            EncryptionMethod::Zip(ZipEncryption::Unknown) => "ZIP Encrypted",
163            EncryptionMethod::Rar(RarEncryption::Legacy) => "RAR Legacy (weak)",
164            EncryptionMethod::Rar(RarEncryption::Aes128) => "RAR AES-128",
165            EncryptionMethod::Rar(RarEncryption::Aes256) => "RAR AES-256",
166            EncryptionMethod::Rar(RarEncryption::Unknown) => "RAR Encrypted",
167            EncryptionMethod::SevenZ(SevenZEncryption::Aes256) => "7z AES-256",
168            EncryptionMethod::SevenZ(SevenZEncryption::Unknown) => "7z Encrypted",
169            EncryptionMethod::Ace => "ACE Blowfish",
170            EncryptionMethod::Arj(ArjEncryption::Garble) => "ARJ Garble (weak)",
171            EncryptionMethod::Arj(ArjEncryption::Gost40) => "ARJ GOST40 (weak)",
172            EncryptionMethod::Arj(ArjEncryption::Gost256) => "ARJ GOST-256",
173            EncryptionMethod::Arj(ArjEncryption::Unknown) => "ARJ Encrypted",
174            EncryptionMethod::Unknown => "Unknown",
175        }
176    }
177}
178
179impl std::fmt::Display for EncryptionMethod {
180    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181        write!(f, "{}", self.description())
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[test]
190    fn test_is_encrypted() {
191        assert!(!EncryptionMethod::None.is_encrypted());
192        assert!(EncryptionMethod::Zip(ZipEncryption::Aes256).is_encrypted());
193        assert!(EncryptionMethod::Ace.is_encrypted());
194        assert!(EncryptionMethod::Unknown.is_encrypted());
195    }
196
197    #[test]
198    fn test_is_weak() {
199        assert!(!EncryptionMethod::None.is_weak());
200        assert!(EncryptionMethod::Zip(ZipEncryption::ZipCrypto).is_weak());
201        assert!(!EncryptionMethod::Zip(ZipEncryption::Aes256).is_weak());
202        assert!(EncryptionMethod::Arj(ArjEncryption::Garble).is_weak());
203        assert!(EncryptionMethod::Arj(ArjEncryption::Gost40).is_weak());
204        assert!(!EncryptionMethod::Arj(ArjEncryption::Gost256).is_weak());
205        assert!(EncryptionMethod::Rar(RarEncryption::Legacy).is_weak());
206        assert!(!EncryptionMethod::Rar(RarEncryption::Aes256).is_weak());
207    }
208
209    #[test]
210    fn test_default() {
211        assert_eq!(EncryptionMethod::default(), EncryptionMethod::None);
212    }
213
214    #[test]
215    fn test_display() {
216        assert_eq!(
217            EncryptionMethod::Zip(ZipEncryption::Aes256).to_string(),
218            "ZIP AES-256"
219        );
220        assert_eq!(EncryptionMethod::Ace.to_string(), "ACE Blowfish");
221    }
222}