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