def __init__(self, aes_properties: bytes, password: str, coders: List[Dict[str, Any]]) -> None: byte_password = password.encode('utf-16LE') firstbyte = aes_properties[0] numcyclespower = firstbyte & 0x3f if firstbyte & 0xc0 != 0: saltsize = (firstbyte >> 7) & 1 ivsize = (firstbyte >> 6) & 1 secondbyte = aes_properties[1] saltsize += (secondbyte >> 4) ivsize += (secondbyte & 0x0f) assert len(aes_properties) == 2 + saltsize + ivsize salt = aes_properties[2:2 + saltsize] iv = aes_properties[2 + saltsize:2 + saltsize + ivsize] assert len(salt) == saltsize assert len(iv) == ivsize assert numcyclespower <= 24 if ivsize < 16: iv += bytes('\x00' * (16 - ivsize), 'ascii') key = calculate_key(byte_password, numcyclespower, salt, 'sha256') self.lzma_decompressor = self._set_lzma_decompressor( coders) # type: lzma.LZMADecompressor self.cipher = AES.new(key, AES.MODE_CBC, iv) self.buf = Buffer(size=READ_BLOCKSIZE + 16) self.flushed = False else: raise UnsupportedCompressionMethodError
def __init__(self, aes_properties: bytes, password: str, blocksize: Optional[int] = None) -> None: firstbyte = aes_properties[0] numcyclespower = firstbyte & 0x3F if firstbyte & 0xC0 != 0: saltsize = (firstbyte >> 7) & 1 ivsize = (firstbyte >> 6) & 1 secondbyte = aes_properties[1] saltsize += secondbyte >> 4 ivsize += secondbyte & 0x0F assert len(aes_properties) == 2 + saltsize + ivsize salt = aes_properties[2:2 + saltsize] iv = aes_properties[2 + saltsize:2 + saltsize + ivsize] assert len(salt) == saltsize assert len(iv) == ivsize assert numcyclespower <= 24 if ivsize < 16: iv += bytes("\x00" * (16 - ivsize), "ascii") key = calculate_key(password.encode("utf-16LE"), numcyclespower, salt, "sha256") self.cipher = AES.new(key, AES.MODE_CBC, iv) if blocksize: self.buf = Buffer(size=blocksize + 16) else: self.buf = Buffer(size=get_default_blocksize() + 16) else: raise UnsupportedCompressionMethodError
def __init__(self, password: str) -> None: self.cycles = 19 # FIXME self.iv = get_random_bytes(16) self.salt = b'' self.method = CompressionMethod.CRYPT_AES256_SHA256 key = calculate_key(password.encode('utf-16LE'), self.cycles, self.salt, 'sha256') self.iv += bytes(self.AES_CBC_BLOCKSIZE - len(self.iv)) # zero padding if iv < AES_CBC_BLOCKSIZE self.cipher = AES.new(key, AES.MODE_CBC, self.iv) self.flushed = False self.buf = Buffer(size=READ_BLOCKSIZE + self.AES_CBC_BLOCKSIZE * 2)
def __init__(self, password: str, blocksize: Optional[int] = None) -> None: self.cycles = 19 # as same as p7zip self.iv = get_random_bytes(16) self.salt = b"" self.method = CompressionMethod.CRYPT_AES256_SHA256 key = calculate_key(password.encode("utf-16LE"), self.cycles, self.salt, "sha256") self.iv += bytes( self.AES_CBC_BLOCKSIZE - len(self.iv)) # zero padding if iv < AES_CBC_BLOCKSIZE self.cipher = AES.new(key, AES.MODE_CBC, self.iv) self.flushed = False if blocksize: self.buf = Buffer(size=blocksize + self.AES_CBC_BLOCKSIZE * 2) else: self.buf = Buffer(size=get_default_blocksize() + self.AES_CBC_BLOCKSIZE * 2)
def __init__(self, aes_properties: bytes, password: str) -> None: firstbyte = aes_properties[0] numcyclespower = firstbyte & 0x3f if firstbyte & 0xc0 != 0: saltsize = (firstbyte >> 7) & 1 ivsize = (firstbyte >> 6) & 1 secondbyte = aes_properties[1] saltsize += (secondbyte >> 4) ivsize += (secondbyte & 0x0f) assert len(aes_properties) == 2 + saltsize + ivsize salt = aes_properties[2:2 + saltsize] iv = aes_properties[2 + saltsize:2 + saltsize + ivsize] assert len(salt) == saltsize assert len(iv) == ivsize assert numcyclespower <= 24 if ivsize < 16: iv += bytes('\x00' * (16 - ivsize), 'ascii') key = calculate_key(password.encode('utf-16LE'), numcyclespower, salt, 'sha256') self.cipher = AES.new(key, AES.MODE_CBC, iv) self.buf = Buffer(size=READ_BLOCKSIZE + 16) else: raise UnsupportedCompressionMethodError
class AESCompressor(ISevenZipCompressor): '''AES Compression(Encryption) class. It accept pre-processing filter which may be a LZMA compression.''' AES_CBC_BLOCKSIZE = 16 def __init__(self, password: str) -> None: self.cycles = 19 # FIXME self.iv = get_random_bytes(16) self.salt = b'' self.method = CompressionMethod.CRYPT_AES256_SHA256 key = calculate_key(password.encode('utf-16LE'), self.cycles, self.salt, 'sha256') self.iv += bytes( self.AES_CBC_BLOCKSIZE - len(self.iv)) # zero padding if iv < AES_CBC_BLOCKSIZE self.cipher = AES.new(key, AES.MODE_CBC, self.iv) self.flushed = False self.buf = Buffer(size=READ_BLOCKSIZE + self.AES_CBC_BLOCKSIZE * 2) def encode_filter_properties(self): # cycles = secrets.SystemRandom().randint(1, 23) saltsize = len(self.salt) ivsize = len(self.iv) ivfirst = 1 # FIXME: it should always 1 saltfirst = 1 if len(self.salt) > 0 else 0 firstbyte = (self.cycles + (ivfirst << 6) + (saltfirst << 7)).to_bytes( 1, 'little') secondbyte = (((ivsize - 1) & 0x0f) + (((saltsize - saltfirst) << 4) & 0xf0)).to_bytes( 1, 'little') properties = firstbyte + secondbyte + self.salt + self.iv return properties def compress(self, data): '''Compression + AES encryption with 16byte alignment.''' # The size is < 16 which should be only last chunk. # From p7zip/CPP/7zip/common/FilterCoder.cpp # /* # AES filters need 16-bytes alignment for HARDWARE-AES instructions. # So we call IFilter::Filter(, size), where (size != 16 * N) only for last data block. # AES-CBC filters need data size aligned for 16-bytes. # So the encoder can add zeros to the end of original stream. # Some filters (BCJ and others) don't process data at the end of stream in some cases. # So the encoder and decoder write such last bytes without change. # */ currentlen = len(self.buf) + len(data) # hopefully aligned and larger than block size. if currentlen >= 16 and (currentlen & 0x0f) == 0: self.buf.add(data) res = self.cipher.encrypt(self.buf.view) self.buf.reset() elif currentlen > 16: # when not aligned # nextpos = (currentlen // self.AES_CBC_BLOCKSIZE) * self.AES_CBC_BLOCKSIZE nextpos = currentlen & ~0x0f buflen = len(self.buf) self.buf.add(data[:nextpos - buflen]) res = self.cipher.encrypt(self.buf.view) self.buf.set(data[nextpos - buflen:]) else: # pragma: no-cover # smaller than block size, it will processed when flush() self.buf.add(data) res = b'' return res def flush(self): if len(self.buf) > 0: padlen = -len( self.buf ) & 15 # padlen = 16 - currentlen % 16 if currentlen % 16 > 0 else 0 self.buf.add(bytes(padlen)) res = self.cipher.encrypt(self.buf.view) self.buf.reset() else: res = b'' return res
class AESDecompressor(ISevenZipDecompressor): def __init__(self, aes_properties: bytes, password: str) -> None: firstbyte = aes_properties[0] numcyclespower = firstbyte & 0x3f if firstbyte & 0xc0 != 0: saltsize = (firstbyte >> 7) & 1 ivsize = (firstbyte >> 6) & 1 secondbyte = aes_properties[1] saltsize += (secondbyte >> 4) ivsize += (secondbyte & 0x0f) assert len(aes_properties) == 2 + saltsize + ivsize salt = aes_properties[2:2 + saltsize] iv = aes_properties[2 + saltsize:2 + saltsize + ivsize] assert len(salt) == saltsize assert len(iv) == ivsize assert numcyclespower <= 24 if ivsize < 16: iv += bytes('\x00' * (16 - ivsize), 'ascii') key = calculate_key(password.encode('utf-16LE'), numcyclespower, salt, 'sha256') self.cipher = AES.new(key, AES.MODE_CBC, iv) self.buf = Buffer(size=READ_BLOCKSIZE + 16) else: raise UnsupportedCompressionMethodError def decompress(self, data: Union[bytes, bytearray, memoryview], max_length: int = -1) -> bytes: currentlen = len(self.buf) + len(data) # when aligned to 16 bytes(expected) if len(data) > 0 and (currentlen & 0x0f) == 0: self.buf.add(data) temp = self.cipher.decrypt(self.buf.view) self.buf.reset() return temp elif len(data) > 0: # pragma: no-cover # nextpos = (currentlen // 16) * 16 nextpos = currentlen & ~0x0f buflen = len(self.buf) temp2 = data[nextpos - buflen:] self.buf.add(data[:nextpos - buflen]) temp = self.cipher.decrypt(self.buf.view) self.buf.set(temp2) return temp elif len(self.buf) == 0: # pragma: no-cover # action flush return b'' else: # pragma: no-cover # action padding # align = 16 # padlen = (align - offset % align) % align # = (align - (offset & (align - 1))) & (align - 1) # = -offset & (align -1) # = -offset & (16 - 1) = -offset & 15 padlen = -len(self.buf) & 15 self.buf.add(bytes(padlen)) temp3 = self.cipher.decrypt(self.buf.view) # type: bytes self.buf.reset() return temp3
class AESDecompressor(ISevenZipDecompressor): """Decrypt data""" def __init__(self, aes_properties: bytes, password: str, blocksize: Optional[int] = None) -> None: firstbyte = aes_properties[0] numcyclespower = firstbyte & 0x3F if firstbyte & 0xC0 != 0: saltsize = (firstbyte >> 7) & 1 ivsize = (firstbyte >> 6) & 1 secondbyte = aes_properties[1] saltsize += secondbyte >> 4 ivsize += secondbyte & 0x0F assert len(aes_properties) == 2 + saltsize + ivsize salt = aes_properties[2:2 + saltsize] iv = aes_properties[2 + saltsize:2 + saltsize + ivsize] assert len(salt) == saltsize assert len(iv) == ivsize assert numcyclespower <= 24 if ivsize < 16: iv += bytes("\x00" * (16 - ivsize), "ascii") key = calculate_key(password.encode("utf-16LE"), numcyclespower, salt, "sha256") self.cipher = AES.new(key, AES.MODE_CBC, iv) if blocksize: self.buf = Buffer(size=blocksize + 16) else: self.buf = Buffer(size=get_default_blocksize() + 16) else: raise UnsupportedCompressionMethodError(firstbyte, "Wrong 7zAES properties") def decompress(self, data: Union[bytes, bytearray, memoryview], max_length: int = -1) -> bytes: currentlen = len(self.buf) + len(data) # when aligned to 16 bytes(expected) if len(data) > 0 and (currentlen & 0x0F) == 0: self.buf.add(data) temp = self.cipher.decrypt(self.buf.view) self.buf.reset() return temp elif len(data) > 0: # pragma: no-cover # nextpos = (currentlen // 16) * 16 nextpos = currentlen & ~0x0F buflen = len(self.buf) temp2 = data[nextpos - buflen:] self.buf.add(data[:nextpos - buflen]) temp = self.cipher.decrypt(self.buf.view) self.buf.set(temp2) return temp elif len(self.buf) == 0: # pragma: no-cover # action flush return b"" else: # pragma: no-cover # action padding # align = 16 # padlen = (align - offset % align) % align # = (align - (offset & (align - 1))) & (align - 1) # = -offset & (align -1) # = -offset & (16 - 1) = -offset & 15 padlen = -len(self.buf) & 15 self.buf.add(bytes(padlen)) temp3 = self.cipher.decrypt(self.buf.view) # type: bytes self.buf.reset() return temp3
class AESDecompressor: lzma_methods_map = { CompressionMethod.LZMA: lzma.FILTER_LZMA1, CompressionMethod.LZMA2: lzma.FILTER_LZMA2, CompressionMethod.DELTA: lzma.FILTER_DELTA, CompressionMethod.P7Z_BCJ: lzma.FILTER_X86, CompressionMethod.BCJ_ARM: lzma.FILTER_ARM, CompressionMethod.BCJ_ARMT: lzma.FILTER_ARMTHUMB, CompressionMethod.BCJ_IA64: lzma.FILTER_IA64, CompressionMethod.BCJ_PPC: lzma.FILTER_POWERPC, CompressionMethod.BCJ_SPARC: lzma.FILTER_SPARC, } def __init__(self, aes_properties: bytes, password: str, coders: List[Dict[str, Any]]) -> None: byte_password = password.encode('utf-16LE') firstbyte = aes_properties[0] numcyclespower = firstbyte & 0x3f if firstbyte & 0xc0 != 0: saltsize = (firstbyte >> 7) & 1 ivsize = (firstbyte >> 6) & 1 secondbyte = aes_properties[1] saltsize += (secondbyte >> 4) ivsize += (secondbyte & 0x0f) assert len(aes_properties) == 2 + saltsize + ivsize salt = aes_properties[2:2 + saltsize] iv = aes_properties[2 + saltsize:2 + saltsize + ivsize] assert len(salt) == saltsize assert len(iv) == ivsize assert numcyclespower <= 24 if ivsize < 16: iv += bytes('\x00' * (16 - ivsize), 'ascii') key = calculate_key(byte_password, numcyclespower, salt, 'sha256') self.lzma_decompressor = self._set_lzma_decompressor( coders) # type: lzma.LZMADecompressor self.cipher = AES.new(key, AES.MODE_CBC, iv) self.buf = Buffer(size=READ_BLOCKSIZE + 16) self.flushed = False else: raise UnsupportedCompressionMethodError # set pipeline decompressor def _set_lzma_decompressor( self, coders: List[Dict[str, Any]]) -> lzma.LZMADecompressor: filters = [] # type: List[Dict[str, Any]] for coder in coders: filter = self.lzma_methods_map.get(coder['method'], None) if filter is not None: properties = coder.get('properties', None) if properties is not None: filters[:0] = [ lzma._decode_filter_properties(filter, properties) ] # type: ignore else: filters[:0] = [{'id': filter}] else: raise UnsupportedCompressionMethodError return lzma.LZMADecompressor(format=lzma.FORMAT_RAW, filters=filters) def decompress(self, data: Union[bytes, bytearray, memoryview], max_length: int = -1) -> bytes: if len(data) == 0 and len(self.buf) == 0: # action flush return self.lzma_decompressor.decompress(b'', max_length) elif len(data) == 0: # action padding self.flushded = True # align = 16 # padlen = (align - offset % align) % align # = (align - (offset & (align - 1))) & (align - 1) # = -offset & (align -1) # = -offset & (16 - 1) = -offset & 15 padlen = -len(self.buf) & 15 self.buf.add(bytes(padlen)) temp = self.cipher.decrypt(self.buf.view) # type: bytes self.buf.reset() return self.lzma_decompressor.decompress(temp, max_length) else: currentlen = len(self.buf) + len(data) nextpos = (currentlen // 16) * 16 if currentlen == nextpos: self.buf.add(data) temp = self.cipher.decrypt(self.buf.view) self.buf.reset() return self.lzma_decompressor.decompress(temp, max_length) else: buflen = len(self.buf) temp2 = data[nextpos - buflen:] self.buf.add(data[:nextpos - buflen]) temp = self.cipher.decrypt(self.buf.view) self.buf.set(temp2) return self.lzma_decompressor.decompress(temp, max_length)