1 //! Implementation of the ZipCrypto algorithm
2 //!
3 //! The following paper was used to implement the ZipCrypto algorithm:
4 //! [https://courses.cs.ut.ee/MTAT.07.022/2015_fall/uploads/Main/dmitri-report-f15-16.pdf](https://courses.cs.ut.ee/MTAT.07.022/2015_fall/uploads/Main/dmitri-report-f15-16.pdf)
5 
6 use std::num::Wrapping;
7 
8 /// A container to hold the current key state
9 #[derive(Clone, Copy)]
10 pub(crate) struct ZipCryptoKeys {
11     key_0: Wrapping<u32>,
12     key_1: Wrapping<u32>,
13     key_2: Wrapping<u32>,
14 }
15 
16 impl ZipCryptoKeys {
new() -> ZipCryptoKeys17     fn new() -> ZipCryptoKeys {
18         ZipCryptoKeys {
19             key_0: Wrapping(0x12345678),
20             key_1: Wrapping(0x23456789),
21             key_2: Wrapping(0x34567890),
22         }
23     }
24 
update(&mut self, input: u8)25     fn update(&mut self, input: u8) {
26         self.key_0 = ZipCryptoKeys::crc32(self.key_0, input);
27         self.key_1 =
28             (self.key_1 + (self.key_0 & Wrapping(0xff))) * Wrapping(0x08088405) + Wrapping(1);
29         self.key_2 = ZipCryptoKeys::crc32(self.key_2, (self.key_1 >> 24).0 as u8);
30     }
31 
stream_byte(&mut self) -> u832     fn stream_byte(&mut self) -> u8 {
33         let temp: Wrapping<u16> = Wrapping(self.key_2.0 as u16) | Wrapping(3);
34         ((temp * (temp ^ Wrapping(1))) >> 8).0 as u8
35     }
36 
decrypt_byte(&mut self, cipher_byte: u8) -> u837     fn decrypt_byte(&mut self, cipher_byte: u8) -> u8 {
38         let plain_byte: u8 = self.stream_byte() ^ cipher_byte;
39         self.update(plain_byte);
40         plain_byte
41     }
42 
43     #[allow(dead_code)]
encrypt_byte(&mut self, plain_byte: u8) -> u844     fn encrypt_byte(&mut self, plain_byte: u8) -> u8 {
45         let cipher_byte: u8 = self.stream_byte() ^ plain_byte;
46         self.update(plain_byte);
47         cipher_byte
48     }
49 
crc32(crc: Wrapping<u32>, input: u8) -> Wrapping<u32>50     fn crc32(crc: Wrapping<u32>, input: u8) -> Wrapping<u32> {
51         (crc >> 8) ^ Wrapping(CRCTABLE[((crc & Wrapping(0xff)).0 as u8 ^ input) as usize])
52     }
derive(password: &[u8]) -> ZipCryptoKeys53     pub(crate) fn derive(password: &[u8]) -> ZipCryptoKeys {
54         let mut keys = ZipCryptoKeys::new();
55         for byte in password.iter() {
56             keys.update(*byte);
57         }
58         keys
59     }
60 }
61 
62 /// A ZipCrypto reader with unverified password
63 pub struct ZipCryptoReader<R> {
64     file: R,
65     keys: ZipCryptoKeys,
66 }
67 
68 pub enum ZipCryptoValidator {
69     PkzipCrc32(u32),
70     InfoZipMsdosTime(u16),
71 }
72 
73 impl<R: std::io::Read> ZipCryptoReader<R> {
74     /// Note: The password is `&[u8]` and not `&str` because the
75     /// [zip specification](https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.3.TXT)
76     /// does not specify password encoding (see function `update_keys` in the specification).
77     /// Therefore, if `&str` was used, the password would be UTF-8 and it
78     /// would be impossible to decrypt files that were encrypted with a
79     /// password byte sequence that is unrepresentable in UTF-8.
new(file: R, password: &[u8]) -> ZipCryptoReader<R>80     pub fn new(file: R, password: &[u8]) -> ZipCryptoReader<R> {
81         ZipCryptoReader {
82             file,
83             keys: ZipCryptoKeys::derive(password),
84         }
85     }
86 
87     /// Read the ZipCrypto header bytes and validate the password.
validate( mut self, validator: ZipCryptoValidator, ) -> Result<Option<ZipCryptoReaderValid<R>>, std::io::Error>88     pub fn validate(
89         mut self,
90         validator: ZipCryptoValidator,
91     ) -> Result<Option<ZipCryptoReaderValid<R>>, std::io::Error> {
92         // ZipCrypto prefixes a file with a 12 byte header
93         let mut header_buf = [0u8; 12];
94         self.file.read_exact(&mut header_buf)?;
95         for byte in header_buf.iter_mut() {
96             *byte = self.keys.decrypt_byte(*byte);
97         }
98 
99         match validator {
100             ZipCryptoValidator::PkzipCrc32(crc32_plaintext) => {
101                 // PKZIP before 2.0 used 2 byte CRC check.
102                 // PKZIP 2.0+ used 1 byte CRC check. It's more secure.
103                 // We also use 1 byte CRC.
104 
105                 if (crc32_plaintext >> 24) as u8 != header_buf[11] {
106                     return Ok(None); // Wrong password
107                 }
108             }
109             ZipCryptoValidator::InfoZipMsdosTime(last_mod_time) => {
110                 // Info-ZIP modification to ZipCrypto format:
111                 // If bit 3 of the general purpose bit flag is set
112                 // (indicates that the file uses a data-descriptor section),
113                 // it uses high byte of 16-bit File Time.
114                 // Info-ZIP code probably writes 2 bytes of File Time.
115                 // We check only 1 byte.
116 
117                 if (last_mod_time >> 8) as u8 != header_buf[11] {
118                     return Ok(None); // Wrong password
119                 }
120             }
121         }
122 
123         Ok(Some(ZipCryptoReaderValid { reader: self }))
124     }
125 }
126 pub(crate) struct ZipCryptoWriter<W> {
127     pub(crate) writer: W,
128     pub(crate) buffer: Vec<u8>,
129     pub(crate) keys: ZipCryptoKeys,
130 }
131 impl<W: std::io::Write> ZipCryptoWriter<W> {
finish(mut self, crc32: u32) -> std::io::Result<W>132     pub(crate) fn finish(mut self, crc32: u32) -> std::io::Result<W> {
133         self.buffer[11] = (crc32 >> 24) as u8;
134         for byte in self.buffer.iter_mut() {
135             *byte = self.keys.encrypt_byte(*byte);
136         }
137         self.writer.write_all(&self.buffer)?;
138         self.writer.flush()?;
139         Ok(self.writer)
140     }
141 }
142 impl<W: std::io::Write> std::io::Write for ZipCryptoWriter<W> {
write(&mut self, buf: &[u8]) -> std::io::Result<usize>143     fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
144         self.buffer.extend_from_slice(buf);
145         Ok(buf.len())
146     }
flush(&mut self) -> std::io::Result<()>147     fn flush(&mut self) -> std::io::Result<()> {
148         Ok(())
149     }
150 }
151 
152 /// A ZipCrypto reader with verified password
153 pub struct ZipCryptoReaderValid<R> {
154     reader: ZipCryptoReader<R>,
155 }
156 
157 impl<R: std::io::Read> std::io::Read for ZipCryptoReaderValid<R> {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>158     fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
159         // Note: There might be potential for optimization. Inspiration can be found at:
160         // https://github.com/kornelski/7z/blob/master/CPP/7zip/Crypto/ZipCrypto.cpp
161 
162         let result = self.reader.file.read(buf);
163         for byte in buf.iter_mut() {
164             *byte = self.reader.keys.decrypt_byte(*byte);
165         }
166         result
167     }
168 }
169 
170 impl<R: std::io::Read> ZipCryptoReaderValid<R> {
171     /// Consumes this decoder, returning the underlying reader.
into_inner(self) -> R172     pub fn into_inner(self) -> R {
173         self.reader.file
174     }
175 }
176 
177 static CRCTABLE: [u32; 256] = [
178     0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
179     0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
180     0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
181     0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
182     0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
183     0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
184     0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
185     0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
186     0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
187     0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
188     0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
189     0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
190     0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
191     0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
192     0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
193     0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
194     0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
195     0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
196     0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
197     0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
198     0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
199     0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
200     0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
201     0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
202     0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
203     0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
204     0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
205     0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
206     0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
207     0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
208     0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
209     0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
210 ];
211