""" Code for handling ZIP archives. Inspired by pyzipper. MIT licence """ import struct import zipfile import zlib import time from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives import hmac, hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC # Compression type. AES_COMPRESSION_TYPE = 99 # The id for the extra data. EXTRA_AES_HEADER_ID = 0x9901 AES_VENDOR_ID = b'AE' AES_V1 = b'\x01\x00' AES_V2 = b'\x02\x00' AES_128 = b'\x01' AES_192 = b'\x02' AES_256 = b'\x03' AES_HMAC_SIZE = 10 AES_SALT_LENGTHS = { AES_128: 8, # 128 bit AES_192: 12, # 192 bit AES_256: 16, # 256 bit } AES_KEY_LENGTHS = { AES_128: 16, # 128 bit AES_192: 24, # 192 bit AES_256: 32, # 256 bit } def _get_encryption_header_length(zinfo): # salt_length + pwd_verify_length salt_length = AES_SALT_LENGTHS[zinfo._aes_strength] return salt_length + 2 class ZipInfoWithAES(zipfile.ZipInfo): __slots__ = ( '_aes_version', '_aes_strength', '_aes_compression', ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._aes_strength = None self._aes_version = None self._aes_compression = None def _decodeExtra(self, filename_crc): """ Little endian encoding. Fragment has minimum 11 bytes: * 2 bytes - extra fragment type * 2 bytes - extra data length * 2 bytes - AES encryption version * 2 bytes - Vendor always b'AE" * 1 byte - AES strength * 2 bytes - compression type """ # Do the upstream decoding first. # This should validate each fragment. super()._decodeExtra(filename_crc) # Re-read the extra fragments to check for AES. extra = self.extra while len(extra) >= 4: tp, ln = struct.unpack('