aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Tolmachev <me@igorek.dev>2024-07-21 16:59:14 +0900
committerIgor Tolmachev <me@igorek.dev>2024-07-21 16:59:14 +0900
commit4c411b76cad9cc735687dc739d2e2db5d00e5eac (patch)
tree818168ca5726ad3f9d24089dba31a24ff6b1b1f4
parent7bcdc3b4ca460aec2b98fb2dca6165788c562b05 (diff)
downloadarchivator-4c411b76cad9cc735687dc739d2e2db5d00e5eac.tar.gz
archivator-4c411b76cad9cc735687dc739d2e2db5d00e5eac.zip
Add AES encryption
-rw-r--r--src/utils/cursor.rs7
-rw-r--r--src/zip/encryption/aes.rs49
-rw-r--r--src/zip/encryption/weak.rs15
-rw-r--r--src/zip/error.rs4
-rw-r--r--src/zip/file/read.rs71
-rw-r--r--tests/files/archive_aes.zipbin0 -> 3898 bytes
-rw-r--r--tests/files/archive_weak.zip (renamed from tests/files/archive_passwd.zip)bin1059 -> 1059 bytes
-rw-r--r--tests/zip.rs67
8 files changed, 150 insertions, 63 deletions
diff --git a/src/utils/cursor.rs b/src/utils/cursor.rs
index 91d44f8..ee80474 100644
--- a/src/utils/cursor.rs
+++ b/src/utils/cursor.rs
@@ -7,13 +7,12 @@ pub struct IoCursor<Io> {
7} 7}
8 8
9impl<Io: Seek> IoCursor<Io> { 9impl<Io: Seek> IoCursor<Io> {
10 pub fn new(mut io: Io, start: u64, end: u64) -> Result<Self> { 10 pub fn new(io: Io, cursor: u64, end: u64) -> Self {
11 let cursor = io.seek(SeekFrom::Start(start))?; 11 Self {
12 Ok(Self {
13 io, 12 io,
14 cursor, 13 cursor,
15 bounds: (cursor, end), 14 bounds: (cursor, end),
16 }) 15 }
17 } 16 }
18} 17}
19 18
diff --git a/src/zip/encryption/aes.rs b/src/zip/encryption/aes.rs
index 6f41aaa..b690482 100644
--- a/src/zip/encryption/aes.rs
+++ b/src/zip/encryption/aes.rs
@@ -1,4 +1,3 @@
1use crate::utils::ReadUtils;
2use aes::cipher::generic_array::GenericArray; 1use aes::cipher::generic_array::GenericArray;
3use aes::cipher::BlockEncrypt; 2use aes::cipher::BlockEncrypt;
4use std::io::{Read, Result as IoResult}; 3use std::io::{Read, Result as IoResult};
@@ -10,37 +9,63 @@ pub struct AesDecoder<Io: Read, Aes: BlockEncrypt> {
10 9
11 counter: u128, 10 counter: u128,
12 block: [u8; 16], 11 block: [u8; 16],
13 cursor: usize, 12 lower: usize,
13 upper: usize,
14} 14}
15 15
16impl<Io: Read, Aes: BlockEncrypt> AesDecoder<Io, Aes> { 16impl<Io: Read, Aes: BlockEncrypt> AesDecoder<Io, Aes> {
17 pub fn new(mut io: Io, aes: Aes) -> IoResult<Self> { 17 pub fn new(io: Io, aes: Aes) -> IoResult<Self> {
18 let block = io.read_arr::<16>()?;
19 let mut decoder = Self { 18 let mut decoder = Self {
20 io, 19 io,
21 aes, 20 aes,
22 counter: 1, 21
23 block, 22 counter: 0,
24 cursor: 0, 23 block: [0; 16],
24 lower: 0,
25 upper: 0,
25 }; 26 };
26 decoder.decrypt_block(); 27
28 decoder.update_block()?;
29
27 Ok(decoder) 30 Ok(decoder)
28 } 31 }
29 32
30 #[inline] 33 #[inline]
31 fn decrypt_block(&mut self) { 34 fn update_block(&mut self) -> IoResult<()> {
35 self.upper = self.io.read(&mut self.block)?;
36 self.lower = 0;
37
38 self.counter += 1;
32 let mut mask = self.counter.to_le_bytes(); 39 let mut mask = self.counter.to_le_bytes();
40
33 self.aes 41 self.aes
34 .encrypt_block(GenericArray::from_mut_slice(&mut mask)); 42 .encrypt_block(GenericArray::from_mut_slice(&mut mask));
43
35 for (b, m) in self.block.iter_mut().zip(mask) { 44 for (b, m) in self.block.iter_mut().zip(mask) {
36 *b ^= m 45 *b ^= m
37 } 46 }
38 self.counter += 1; 47
48 Ok(())
39 } 49 }
40} 50}
41 51
42impl<Io: Read, Aes: BlockEncrypt> Read for AesDecoder<Io, Aes> { 52impl<Io: Read, Aes: BlockEncrypt> Read for AesDecoder<Io, Aes> {
43 fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { 53 fn read(&mut self, mut buf: &mut [u8]) -> IoResult<usize> {
44 todo!() 54 let mut bytes = 0;
55 while !buf.is_empty() && self.lower != self.upper {
56 for (to, fr) in buf.iter_mut().zip(&self.block[self.lower..self.upper]) {
57 *to = *fr
58 }
59
60 let consumed = buf.len().min(self.upper - self.lower);
61 buf = &mut buf[consumed..];
62 self.lower += consumed;
63 bytes += consumed;
64
65 if self.lower == 16 {
66 self.update_block()?;
67 }
68 }
69 Ok(bytes)
45 } 70 }
46} 71}
diff --git a/src/zip/encryption/weak.rs b/src/zip/encryption/weak.rs
index 144cd53..ebddb2d 100644
--- a/src/zip/encryption/weak.rs
+++ b/src/zip/encryption/weak.rs
@@ -45,7 +45,7 @@ impl Keys {
45 } 45 }
46 } 46 }
47 47
48 fn update(&mut self, byte: u8) { 48 pub fn update(&mut self, byte: u8) {
49 self.key0 = crc32(byte, self.key0); 49 self.key0 = crc32(byte, self.key0);
50 self.key1 = self 50 self.key1 = self
51 .key1 51 .key1
@@ -55,19 +55,6 @@ impl Keys {
55 self.key2 = crc32((self.key1 >> 24) as u8, self.key2); 55 self.key2 = crc32((self.key1 >> 24) as u8, self.key2);
56 } 56 }
57 57
58 pub fn set_password(&mut self, passwd: &[u8]) {
59 for b in passwd {
60 self.update(*b)
61 }
62 }
63
64 pub fn set_header(&mut self, header: [u8; 12]) -> u8 {
65 for b in &header[..11] {
66 self.decode_byte(*b);
67 }
68 self.decode_byte(header[11])
69 }
70
71 #[allow(dead_code)] 58 #[allow(dead_code)]
72 pub fn encode_bytes(&mut self, byte: u8) -> u8 { 59 pub fn encode_bytes(&mut self, byte: u8) -> u8 {
73 let key = self.key2 | 2; 60 let key = self.key2 | 2;
diff --git a/src/zip/error.rs b/src/zip/error.rs
index 5508177..87cd9e9 100644
--- a/src/zip/error.rs
+++ b/src/zip/error.rs
@@ -23,7 +23,7 @@ pub enum ZipError {
23 InvalidFileComment, 23 InvalidFileComment,
24 24
25 FileNotFound, 25 FileNotFound,
26 IncorrectPassword, 26 WrongPassword,
27 PasswordIsNotSpecified, 27 PasswordIsNotSpecified,
28 CompressedDataIsUnseekable, 28 CompressedDataIsUnseekable,
29 EncryptedDataIsUnseekable, 29 EncryptedDataIsUnseekable,
@@ -81,7 +81,7 @@ impl Display for ZipError {
81 Self::InvalidFileComment => write!(f, "Invalid file comment"), 81 Self::InvalidFileComment => write!(f, "Invalid file comment"),
82 82
83 Self::FileNotFound => write!(f, "File not found"), 83 Self::FileNotFound => write!(f, "File not found"),
84 Self::IncorrectPassword => write!(f, "Incorrect password"), 84 Self::WrongPassword => write!(f, "Wrong password"),
85 Self::PasswordIsNotSpecified => write!(f, "Password is not specified"), 85 Self::PasswordIsNotSpecified => write!(f, "Password is not specified"),
86 Self::CompressedDataIsUnseekable => write!(f, "Compressed data is unseekable"), 86 Self::CompressedDataIsUnseekable => write!(f, "Compressed data is unseekable"),
87 Self::EncryptedDataIsUnseekable => write!(f, "Encrypted data is unseekable"), 87 Self::EncryptedDataIsUnseekable => write!(f, "Encrypted data is unseekable"),
diff --git a/src/zip/file/read.rs b/src/zip/file/read.rs
index 80bdcfb..d25655e 100644
--- a/src/zip/file/read.rs
+++ b/src/zip/file/read.rs
@@ -24,21 +24,34 @@ enum Encryption<Io: Read> {
24 Aes256(AesDecoder<Io, Aes256>), 24 Aes256(AesDecoder<Io, Aes256>),
25} 25}
26 26
27impl<Io: Read> Encryption<Io> { 27impl<Io: Read + Seek> Encryption<IoCursor<Io>> {
28 #[inline] 28 #[inline]
29 pub fn new(mut io: Io, info: &ZipFileInfo, password: Option<&[u8]>) -> ZipResult<Self> { 29 pub fn new(
30 mut io: Io,
31 cursor: u64,
32 end: u64,
33 info: &ZipFileInfo,
34 password: Option<&[u8]>,
35 ) -> ZipResult<Self> {
30 Ok(match info.encryption_method { 36 Ok(match info.encryption_method {
31 EncryptionMethod::None => Self::None(io), 37 EncryptionMethod::None => Self::None(IoCursor::new(io, cursor, end)),
32 EncryptionMethod::Weak => { 38 EncryptionMethod::Weak => {
33 let mut keys = Keys::new(); 39 let mut keys = Keys::new();
34 keys.set_password(password.ok_or(ZipError::PasswordIsNotSpecified)?);
35 40
36 let check = keys.set_header(io.read_arr()?); 41 for b in password.ok_or(ZipError::PasswordIsNotSpecified)? {
37 if check != info.password_check() { 42 keys.update(*b)
38 return Err(ZipError::IncorrectPassword); 43 }
44
45 let header = io.read_arr::<12>()?;
46 for b in &header[..11] {
47 keys.decode_byte(*b);
39 } 48 }
40 49
41 Self::Weak(WeakDecoder::new(io, keys)) 50 if keys.decode_byte(header[11]) != info.password_check() {
51 return Err(ZipError::WrongPassword);
52 }
53
54 Self::Weak(WeakDecoder::new(IoCursor::new(io, cursor + 12, end), keys))
42 } 55 }
43 EncryptionMethod::Aes128 => { 56 EncryptionMethod::Aes128 => {
44 let header = io.read_arr::<10>()?; 57 let header = io.read_arr::<10>()?;
@@ -53,10 +66,13 @@ impl<Io: Read> Encryption<Io> {
53 let key = &hash[..16]; 66 let key = &hash[..16];
54 67
55 if check != &hash[32..] { 68 if check != &hash[32..] {
56 return Err(ZipError::IncorrectPassword); 69 return Err(ZipError::WrongPassword);
57 } 70 }
58 71
59 Self::Aes128(AesDecoder::new(io, Aes128::new(key.into()))?) 72 Self::Aes128(AesDecoder::new(
73 IoCursor::new(io, cursor + 10, end - 10),
74 Aes128::new(key.into()),
75 )?)
60 } 76 }
61 EncryptionMethod::Aes192 => { 77 EncryptionMethod::Aes192 => {
62 let header = io.read_arr::<14>()?; 78 let header = io.read_arr::<14>()?;
@@ -71,10 +87,13 @@ impl<Io: Read> Encryption<Io> {
71 let key = &hash[..24]; 87 let key = &hash[..24];
72 88
73 if check != &hash[48..] { 89 if check != &hash[48..] {
74 return Err(ZipError::IncorrectPassword); 90 return Err(ZipError::WrongPassword);
75 } 91 }
76 92
77 Self::Aes192(AesDecoder::new(io, Aes192::new(key.into()))?) 93 Self::Aes192(AesDecoder::new(
94 IoCursor::new(io, cursor + 14, end - 10),
95 Aes192::new(key.into()),
96 )?)
78 } 97 }
79 EncryptionMethod::Aes256 => { 98 EncryptionMethod::Aes256 => {
80 let header = io.read_arr::<18>()?; 99 let header = io.read_arr::<18>()?;
@@ -89,10 +108,13 @@ impl<Io: Read> Encryption<Io> {
89 let key = &hash[..32]; 108 let key = &hash[..32];
90 109
91 if check != &hash[64..] { 110 if check != &hash[64..] {
92 return Err(ZipError::IncorrectPassword); 111 return Err(ZipError::WrongPassword);
93 } 112 }
94 113
95 Self::Aes256(AesDecoder::new(io, Aes256::new(key.into()))?) 114 Self::Aes256(AesDecoder::new(
115 IoCursor::new(io, cursor + 18, end - 10),
116 Aes256::new(key.into()),
117 )?)
96 } 118 }
97 EncryptionMethod::Unsupported => return Err(ZipError::UnsupportedEncryptionMethod), 119 EncryptionMethod::Unsupported => return Err(ZipError::UnsupportedEncryptionMethod),
98 }) 120 })
@@ -215,20 +237,17 @@ impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> {
215 if buf[..4] != FILE_HEADER_SIGNATURE { 237 if buf[..4] != FILE_HEADER_SIGNATURE {
216 return Err(ZipError::InvalidFileHeaderSignature); 238 return Err(ZipError::InvalidFileHeaderSignature);
217 } 239 }
218 let data_pointer = info.header_pointer 240
219 + 30 241 let cursor = io.seek(SeekFrom::Start(
220 + u16::from_le_bytes(buf[26..28].try_into().unwrap()) as u64 242 info.header_pointer
221 + u16::from_le_bytes(buf[28..30].try_into().unwrap()) as u64; 243 + 30
244 + u16::from_le_bytes(buf[26..28].try_into().unwrap()) as u64
245 + u16::from_le_bytes(buf[28..30].try_into().unwrap()) as u64,
246 ))?;
247 let end = cursor + info.compressed_size;
222 248
223 Ok(Self { 249 Ok(Self {
224 io: Compression::new( 250 io: Compression::new(Encryption::new(io, cursor, end, info, password)?, info)?,
225 Encryption::new(
226 IoCursor::new(io, data_pointer, data_pointer + info.compressed_size)?,
227 info,
228 password,
229 )?,
230 info,
231 )?,
232 info, 251 info,
233 }) 252 })
234 } 253 }
diff --git a/tests/files/archive_aes.zip b/tests/files/archive_aes.zip
new file mode 100644
index 0000000..a64415b
--- /dev/null
+++ b/tests/files/archive_aes.zip
Binary files differ
diff --git a/tests/files/archive_passwd.zip b/tests/files/archive_weak.zip
index 291bd12..291bd12 100644
--- a/tests/files/archive_passwd.zip
+++ b/tests/files/archive_weak.zip
Binary files differ
diff --git a/tests/zip.rs b/tests/zip.rs
index 2c8fc56..9283df3 100644
--- a/tests/zip.rs
+++ b/tests/zip.rs
@@ -3,8 +3,60 @@ use archivator::{Archive, Zip};
3use std::io::{Read, Seek, SeekFrom}; 3use std::io::{Read, Seek, SeekFrom};
4 4
5#[test] 5#[test]
6fn test_zip_passwd() { 6fn test_zip_aes() {
7 let mut archive = Archive::<Zip>::read_from_file("tests/files/archive_passwd.zip").unwrap(); 7 let mut archive = Archive::<Zip>::read_from_file("tests/files/archive_aes.zip").unwrap();
8
9 assert_eq!(archive.comment(), "archive comment");
10 assert_eq!(
11 archive
12 .files()
13 .iter()
14 .map(|f| &f.name)
15 .collect::<Vec<&String>>(),
16 vec![
17 "aes128/store",
18 "aes192/store",
19 "aes256/store",
20 "aes128/deflate",
21 "aes192/deflate",
22 "aes256/deflate",
23 "aes128/bzip",
24 "aes192/bzip",
25 "aes256/bzip",
26 "aes128/lzma",
27 "aes192/lzma",
28 "aes256/lzma",
29 ]
30 );
31
32 for encryption in ["aes128", "aes192", "aes256"] {
33 assert!(archive
34 .get_file_reader_by_name(&format!("{encryption}/store"))
35 .is_err_and(|e| e == ZipError::PasswordIsNotSpecified));
36 assert!(archive
37 .get_file_reader_by_name_with_password("aes128/store", b"wrong_passwd")
38 .is_err_and(|e| e == ZipError::WrongPassword));
39
40 for (name, check) in [
41 ("store", "98f64f03b3d168875ffa778f7fb4"),
42 ("deflate", "0230e7cadb76460e80cd9de611eb"),
43 ("bzip", "061c17646f025837e33e00425cca"),
44 ("lzma", "43ef5e8ed799eb7a0f25501824ff"),
45 ] {
46 let mut f = archive
47 .get_file_reader_by_name_with_password(&format!("{encryption}/{name}"), b"passwd")
48 .unwrap();
49 let mut data = String::new();
50 f.read_to_string(&mut data).unwrap();
51 assert_eq!(&data[..24], "test encrypted file data");
52 assert_eq!(&data[172..], check);
53 }
54 }
55}
56
57#[test]
58fn test_zip_weak() {
59 let mut archive = Archive::<Zip>::read_from_file("tests/files/archive_weak.zip").unwrap();
8 60
9 assert_eq!(archive.comment(), "archive comment"); 61 assert_eq!(archive.comment(), "archive comment");
10 assert_eq!( 62 assert_eq!(
@@ -21,9 +73,9 @@ fn test_zip_passwd() {
21 .is_err_and(|e| e == ZipError::PasswordIsNotSpecified)); 73 .is_err_and(|e| e == ZipError::PasswordIsNotSpecified));
22 assert!(archive 74 assert!(archive
23 .get_file_reader_by_name_with_password("store", b"wrong_passwd") 75 .get_file_reader_by_name_with_password("store", b"wrong_passwd")
24 .is_err_and(|e| e == ZipError::IncorrectPassword)); 76 .is_err_and(|e| e == ZipError::WrongPassword));
25 77
26 for (name, check_data) in [ 78 for (name, check) in [
27 ("store", "1e643774f40510e37c6f3c451d9d"), 79 ("store", "1e643774f40510e37c6f3c451d9d"),
28 ("deflate", "a70aff4b6b2754ad47852503236a"), 80 ("deflate", "a70aff4b6b2754ad47852503236a"),
29 ("bzip", "f7085f4f8ecc512a8c2c3cbe8227"), 81 ("bzip", "f7085f4f8ecc512a8c2c3cbe8227"),
@@ -34,7 +86,7 @@ fn test_zip_passwd() {
34 let mut data = String::new(); 86 let mut data = String::new();
35 f.read_to_string(&mut data).unwrap(); 87 f.read_to_string(&mut data).unwrap();
36 assert_eq!(&data[..24], "test encrypted file data"); 88 assert_eq!(&data[..24], "test encrypted file data");
37 assert_eq!(&data[172..], check_data); 89 assert_eq!(&data[172..], check);
38 } 90 }
39} 91}
40 92
@@ -52,6 +104,11 @@ fn test_zip() {
52 vec!["store", "deflate", "bzip", "lzma", "zstd", "xz"] 104 vec!["store", "deflate", "bzip", "lzma", "zstd", "xz"]
53 ); 105 );
54 106
107 assert_eq!(
108 archive.get_file_info_by_name("none").unwrap_err(),
109 ZipError::FileNotFound
110 );
111
55 let mut f = archive.get_file_reader_by_name("store").unwrap(); 112 let mut f = archive.get_file_reader_by_name("store").unwrap();
56 113
57 let mut data = String::new(); 114 let mut data = String::new();