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