From c8c6ac83e1cdeea92fc368b16b237afd4f6b47f0 Mon Sep 17 00:00:00 2001 From: JackDandy Date: Fri, 7 Jun 2024 16:43:38 +0100 Subject: [PATCH] =?UTF-8?q?Update=20Rarfile=204.1a1=20(8a72967)=20?= =?UTF-8?q?=E2=86=92=204.2=20(db1df33).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 1 + lib/rarfile/rarfile.py | 59 ++++++++++++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 06091de0..ae36ee8d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ * Update imdbpie 5.6.4 (f695e87) to 5.6.5 (f8ed7a0) * Update profilehooks module 1.12.1 (c3fc078) to 1.13.0.dev0 (99f8a31) * Update pytz 2023.3/2023c (488d3eb) to 2024.1/2024a (3680953) +* Update Rarfile 4.1a1 (8a72967) to 4.2 (db1df33) * Update Requests library 2.31.0 (8812812) to 2.32.3 (0e322af) * Update Tornado Web Server 6.4 (b3f2a4b) to 6.4.1 (2a0e1d1) * Update urllib3 2.0.7 (56f01e0) to 2.2.1 (54d6edf) diff --git a/lib/rarfile/rarfile.py b/lib/rarfile/rarfile.py index 37c64992..fc95f7fd 100644 --- a/lib/rarfile/rarfile.py +++ b/lib/rarfile/rarfile.py @@ -1,6 +1,6 @@ # rarfile.py # -# Copyright (c) 2005-2020 Marko Kreen +# Copyright (c) 2005-2024 Marko Kreen # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -92,7 +92,7 @@ class AES_CBC_Decrypt: self.decrypt = ciph.decryptor().update -__version__ = "4.1" +__version__ = "4.2" # export only interesting items __all__ = ["get_rar_version", "is_rarfile", "is_rarfile_sfx", "RarInfo", "RarFile", "RarExtFile"] @@ -210,6 +210,9 @@ RAR_M3 = 0x33 #: Compression level `-m3`. RAR_M4 = 0x34 #: Compression level `-m4`. RAR_M5 = 0x35 #: Compression level `-m5` - Maximum compression. +RAR_MAX_PASSWORD = 127 #: Max number of utf-16 chars in passwords. +RAR_MAX_KDF_SHIFT = 24 #: Max power-of-2 for KDF count + # # RAR5 constants # @@ -739,6 +742,13 @@ class RarFile: """ return self._file_parser.needs_password() + def is_solid(self): + """Returns True if archive uses solid compression. + + .. versionadded:: 4.2 + """ + return self._file_parser.is_solid() + def namelist(self): """Return list of filenames in archive. """ @@ -1030,6 +1040,14 @@ class CommonParser: self._sfx_offset = sfx_offset self._part_only = part_only + def is_solid(self): + """Returns True if archive uses solid compression. + """ + if self._main: + if self._main.flags & RAR_MAIN_SOLID: + return True + return False + def has_header_encryption(self): """Returns True if headers are encrypted """ @@ -1167,7 +1185,9 @@ class CommonParser: if not self._password: break elif h.type == RAR_BLOCK_ENDARC: - more_vols = (h.flags & RAR_ENDARC_NEXT_VOLUME) > 0 + # use flag, but also allow RAR 2.x logic below to trigger + if h.flags & RAR_ENDARC_NEXT_VOLUME: + more_vols = True endarc = True if raise_need_first_vol and (h.flags & RAR_ENDARC_VOLNR) > 0: raise NeedFirstVolume( @@ -1810,10 +1830,10 @@ class RAR5Parser(CommonParser): def _gen_key(self, kdf_count, salt): if self._last_aes256_key[:2] == (kdf_count, salt): return self._last_aes256_key[2] - if kdf_count > 24: + if kdf_count > RAR_MAX_KDF_SHIFT: raise BadRarFile("Too large kdf_count") pwd = self._get_utf8_password() - key = pbkdf2_hmac("sha256", pwd, salt, 1 << kdf_count) + key = rar5_s2k(pwd, salt, 1 << kdf_count) self._last_aes256_key = (kdf_count, salt, key) return key @@ -1978,6 +1998,8 @@ class RAR5Parser(CommonParser): def _check_password(self, check_value, kdf_count_shift, salt): if len(check_value) != RAR5_PW_CHECK_SIZE + RAR5_PW_SUM_SIZE: return + if kdf_count_shift > RAR_MAX_KDF_SHIFT: + raise BadRarFile("Too large kdf_count") hdr_check = check_value[:RAR5_PW_CHECK_SIZE] hdr_sum = check_value[RAR5_PW_CHECK_SIZE:] @@ -1987,7 +2009,7 @@ class RAR5Parser(CommonParser): kdf_count = (1 << kdf_count_shift) + 32 pwd = self._get_utf8_password() - pwd_hash = pbkdf2_hmac("sha256", pwd, salt, kdf_count) + pwd_hash = rar5_s2k(pwd, salt, kdf_count) pwd_check = bytearray(RAR5_PW_CHECK_SIZE) len_mask = RAR5_PW_CHECK_SIZE - 1 @@ -2341,8 +2363,8 @@ class RarExtFile(io.RawIOBase): """Seek in data. On uncompressed files, the seeking works by actual - seeks so it's fast. On compresses files its slow - - forward seeking happends by reading ahead, + seeks so it's fast. On compressed files its slow + - forward seeking happens by reading ahead, backwards by re-opening and decompressing from the start. """ @@ -3044,12 +3066,23 @@ def is_filelike(obj): return True +def rar5_s2k(pwd, salt, kdf_count): + """String-to-key hash for RAR5. + """ + if not isinstance(pwd, str): + pwd = pwd.decode("utf8") + wstr = pwd.encode("utf-16le")[:RAR_MAX_PASSWORD*2] + ustr = wstr.decode("utf-16le").encode("utf8") + return pbkdf2_hmac("sha256", ustr, salt, kdf_count) + + def rar3_s2k(pwd, salt): """String-to-key hash for RAR3. """ if not isinstance(pwd, str): pwd = pwd.decode("utf8") - seed = bytearray(pwd.encode("utf-16le") + salt) + wstr = pwd.encode("utf-16le")[:RAR_MAX_PASSWORD*2] + seed = bytearray(wstr + salt) h = Rar3Sha1(rarbug=True) iv = b"" for i in range(16): @@ -3114,7 +3147,7 @@ def rar3_decompress(vers, meth, data, declen=0, flags=0, crc=0, pwd=None, salt=N def sanitize_filename(fname, pathsep, is_win32): - """Simulate unrar sanitization. + """Make filename safe for write access. """ if is_win32: if len(fname) > 1 and fname[1] == ":": @@ -3186,12 +3219,12 @@ def parse_dos_time(stamp): class nsdatetime(datetime): """Datetime that carries nanoseconds. - Arithmetic not supported, will lose nanoseconds. + Arithmetic operations will lose nanoseconds. .. versionadded:: 4.0 """ __slots__ = ("nanosecond",) - nanosecond: int #: Number of nanoseconds, 0 <= nanosecond < 999999999 + nanosecond: int #: Number of nanoseconds, 0 <= nanosecond <= 999999999 def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0, nanosecond=0): @@ -3393,7 +3426,7 @@ class ToolSetup: UNRAR_CONFIG = { "open_cmd": ("UNRAR_TOOL", "p", "-inul"), - "check_cmd": ("UNRAR_TOOL", "-inul"), + "check_cmd": ("UNRAR_TOOL", "-inul", "-?"), "password": "-p", "no_password": ("-p-",), # map return code to exception class, codes from rar.txt