Skip to content

Commit

Permalink
perf: faster ZipCrypto decompression
Browse files Browse the repository at this point in the history
Inspired by the report at #91,
found some performance improvements for the ZipCrypto function,
decrypt_weak_decompress. From some light testing of a password proteceted 100MB
file of pseudo-random data on my local filesystem, it seems to reduce
decryption+decompression time from ~55 seconds to ~45 seconds, which also makes
it a bit faster than Python's zipfile, at least in this circumstance.
  • Loading branch information
michalc committed Aug 24, 2024
1 parent 4e19403 commit 7075b7c
Showing 1 changed file with 20 additions and 11 deletions.
31 changes: 20 additions & 11 deletions stream_unzip.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,30 +209,39 @@ def get_extra_value(extra, if_true, signature, exception_if_missing, min_length,
return value

def decrypt_weak_decompress(chunks, decompress, is_done, num_unused):
# There are a few optimisations that make this code unusual:
# - There is code repetition (to avoid function calls inside loops)
# - We assign global variables to local (to avoid the dictionary lookups globals involve)
# - Use bytearray rather than bytes (to avoid allocating memory)
# - Avoids intermediate statements/variables (to minimise unnecessary operations)
# From some light tests these make it ~5%-10% faster than Python's zipfile (although it
# does use similar optimisations from what I can tell)
key_0 = 305419896
key_1 = 591751049
key_2 = 878082192
crc32 = zlib.crc32
bytes_c = bytes

def update_keys(byte):
nonlocal key_0, key_1, key_2
key_0 = ~crc32(bytes_c((byte,)), ~key_0) & 0xFFFFFFFF
key_1 = (key_1 + (key_0 & 0xFF)) & 0xFFFFFFFF
key_1 = ((key_1 * 134775813) + 1) & 0xFFFFFFFF
key_2 = ~crc32(bytes_c((key_1 >> 24,)), ~key_2) & 0xFFFFFFFF
bytearray_byte = bytearray(1)

def decrypt(chunk):
nonlocal key_0, key_1, key_2
chunk = bytearray(chunk)
for i, byte in enumerate(chunk):
temp = key_2 | 2
byte ^= ((temp * (temp ^ 1)) >> 8) & 0xFF
update_keys(byte)
bytearray_byte[0] = byte
key_0 = ~crc32(bytearray_byte, ~key_0) & 0xFFFFFFFF
key_1 = ((((key_1 + (key_0 & 0xFF)) & 0xFFFFFFFF) * 134775813) + 1) & 0xFFFFFFFF
bytearray_byte[0] = key_1 >> 24
key_2 = ~crc32(bytearray_byte, ~key_2) & 0xFFFFFFFF
chunk[i] = byte
return bytes(chunk)
return chunk

for byte in password:
update_keys(byte)
bytearray_byte[0] = byte
key_0 = ~crc32(bytearray_byte, ~key_0) & 0xFFFFFFFF
key_1 = ((((key_1 + (key_0 & 0xFF)) & 0xFFFFFFFF) * 134775813) + 1) & 0xFFFFFFFF
bytearray_byte[0] = key_1 >> 24
key_2 = ~crc32(bytearray_byte, ~key_2) & 0xFFFFFFFF

encryption_header = decrypt(get_num(12))
check_password_byte = \
Expand Down

0 comments on commit 7075b7c

Please sign in to comment.