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