| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1907,6 +1907,44 @@ def test_unicode_password(self): | |||
| 1907 | 1907 | self.assertRaises(TypeError, self.zip.open, "test.txt", pwd="python") | |
| 1908 | 1908 | self.assertRaises(TypeError, self.zip.extract, "test.txt", pwd="python") | |
| 1909 | 1909 | ||
| 1910 | + def test_seek_tell(self): | ||
| 1911 | + self.zip.setpassword(b"python") | ||
| 1912 | + txt = self.plain | ||
| 1913 | + test_word = b'encryption' | ||
| 1914 | + bloc = txt.find(test_word) | ||
| 1915 | + bloc_len = len(test_word) | ||
| 1916 | + with self.zip.open("test.txt", "r") as fp: | ||
| 1917 | + fp.seek(bloc, os.SEEK_SET) | ||
| 1918 | + self.assertEqual(fp.tell(), bloc) | ||
| 1919 | + fp.seek(-bloc, os.SEEK_CUR) | ||
| 1920 | + self.assertEqual(fp.tell(), 0) | ||
| 1921 | + fp.seek(bloc, os.SEEK_CUR) | ||
| 1922 | + self.assertEqual(fp.tell(), bloc) | ||
| 1923 | + self.assertEqual(fp.read(bloc_len), txt[bloc:bloc+bloc_len]) | ||
| 1924 | + | ||
| 1925 | + # Make sure that the second read after seeking back beyond | ||
| 1926 | + # _readbuffer returns the same content (ie. rewind to the start of | ||
| 1927 | + # the file to read forward to the required position). | ||
| 1928 | + old_read_size = fp.MIN_READ_SIZE | ||
| 1929 | + fp.MIN_READ_SIZE = 1 | ||
| 1930 | + fp._readbuffer = b'' | ||
| 1931 | + fp._offset = 0 | ||
| 1932 | + fp.seek(0, os.SEEK_SET) | ||
| 1933 | + self.assertEqual(fp.tell(), 0) | ||
| 1934 | + fp.seek(bloc, os.SEEK_CUR) | ||
| 1935 | + self.assertEqual(fp.read(bloc_len), txt[bloc:bloc+bloc_len]) | ||
| 1936 | + fp.MIN_READ_SIZE = old_read_size | ||
| 1937 | + | ||
| 1938 | + fp.seek(0, os.SEEK_END) | ||
| 1939 | + self.assertEqual(fp.tell(), len(txt)) | ||
| 1940 | + fp.seek(0, os.SEEK_SET) | ||
| 1941 | + self.assertEqual(fp.tell(), 0) | ||
| 1942 | + | ||
| 1943 | + # Read the file completely to definitely call any eof integrity | ||
| 1944 | + # checks (crc) and make sure they still pass. | ||
| 1945 | + fp.read() | ||
| 1946 | + | ||
| 1947 | + | ||
| 1910 | 1948 | class AbstractTestsWithRandomBinaryFiles: | |
| 1911 | 1949 | @classmethod | |
| 1912 | 1950 | def setUpClass(cls): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -792,10 +792,10 @@ class ZipExtFile(io.BufferedIOBase): | |||
| 792 | 792 | # Chunk size to read during seek | |
| 793 | 793 | MAX_SEEK_READ = 1 << 24 | |
| 794 | 794 | ||
| 795 | - def __init__(self, fileobj, mode, zipinfo, decrypter=None, | ||
| 795 | + def __init__(self, fileobj, mode, zipinfo, pwd=None, | ||
| 796 | 796 | close_fileobj=False): | |
| 797 | 797 | self._fileobj = fileobj | |
| 798 | - self._decrypter = decrypter | ||
| 798 | + self._pwd = pwd | ||
| 799 | 799 | self._close_fileobj = close_fileobj | |
| 800 | 800 | ||
| 801 | 801 | self._compress_type = zipinfo.compress_type | |
@@ -810,11 +810,6 @@ def __init__(self, fileobj, mode, zipinfo, decrypter=None, | |||
| 810 | 810 | ||
| 811 | 811 | self.newlines = None | |
| 812 | 812 | ||
| 813 | - # Adjust read size for encrypted files since the first 12 bytes | ||
| 814 | - # are for the encryption/password information. | ||
| 815 | - if self._decrypter is not None: | ||
| 816 | - self._compress_left -= 12 | ||
| 817 | - | ||
| 818 | 813 | self.mode = mode | |
| 819 | 814 | self.name = zipinfo.filename | |
| 820 | 815 | ||
@@ -835,6 +830,30 @@ def __init__(self, fileobj, mode, zipinfo, decrypter=None, | |||
| 835 | 830 | except AttributeError: | |
| 836 | 831 | pass | |
| 837 | 832 | ||
| 833 | + self._decrypter = None | ||
| 834 | + if pwd: | ||
| 835 | + if zipinfo.flag_bits & 0x8: | ||
| 836 | + # compare against the file type from extended local headers | ||
| 837 | + check_byte = (zipinfo._raw_time >> 8) & 0xff | ||
| 838 | + else: | ||
| 839 | + # compare against the CRC otherwise | ||
| 840 | + check_byte = (zipinfo.CRC >> 24) & 0xff | ||
| 841 | + h = self._init_decrypter() | ||
| 842 | + if h != check_byte: | ||
| 843 | + raise RuntimeError("Bad password for file %r" % zipinfo.orig_filename) | ||
| 844 | + | ||
| 845 | + | ||
| 846 | + def _init_decrypter(self): | ||
| 847 | + self._decrypter = _ZipDecrypter(self._pwd) | ||
| 848 | + # The first 12 bytes in the cypher stream is an encryption header | ||
| 849 | + # used to strengthen the algorithm. The first 11 bytes are | ||
| 850 | + # completely random, while the 12th contains the MSB of the CRC, | ||
| 851 | + # or the MSB of the file time depending on the header type | ||
| 852 | + # and is used to check the correctness of the password. | ||
| 853 | + header = self._fileobj.read(12) | ||
| 854 | + self._compress_left -= 12 | ||
| 855 | + return self._decrypter(header)[11] | ||
| 856 | + | ||
| 838 | 857 | def __repr__(self): | |
| 839 | 858 | result = ['<%s.%s' % (self.__class__.__module__, | |
| 840 | 859 | self.__class__.__qualname__)] | |
@@ -1061,6 +1080,8 @@ def seek(self, offset, whence=0): | |||
| 1061 | 1080 | self._decompressor = _get_decompressor(self._compress_type) | |
| 1062 | 1081 | self._eof = False | |
| 1063 | 1082 | read_offset = new_pos | |
| 1083 | + if self._decrypter is not None: | ||
| 1084 | + self._init_decrypter() | ||
| 1064 | 1085 | ||
| 1065 | 1086 | while read_offset > 0: | |
| 1066 | 1087 | read_len = min(self.MAX_SEEK_READ, read_offset) | |
@@ -1524,32 +1545,16 @@ def open(self, name, mode="r", pwd=None, *, force_zip64=False): | |||
| 1524 | 1545 | ||
| 1525 | 1546 | # check for encrypted flag & handle password | |
| 1526 | 1547 | is_encrypted = zinfo.flag_bits & 0x1 | |
| 1527 | - zd = None | ||
| 1528 | 1548 | if is_encrypted: | |
| 1529 | 1549 | if not pwd: | |
| 1530 | 1550 | pwd = self.pwd | |
| 1531 | 1551 | if not pwd: | |
| 1532 | 1552 | raise RuntimeError("File %r is encrypted, password " | |
| 1533 | 1553 | "required for extraction" % name) | |
| 1554 | + else: | ||
| 1555 | + pwd = None | ||
| 1534 | 1556 | ||
| 1535 | - zd = _ZipDecrypter(pwd) | ||
| 1536 | - # The first 12 bytes in the cypher stream is an encryption header | ||
| 1537 | - # used to strengthen the algorithm. The first 11 bytes are | ||
| 1538 | - # completely random, while the 12th contains the MSB of the CRC, | ||
| 1539 | - # or the MSB of the file time depending on the header type | ||
| 1540 | - # and is used to check the correctness of the password. | ||
| 1541 | - header = zef_file.read(12) | ||
| 1542 | - h = zd(header[0:12]) | ||
| 1543 | - if zinfo.flag_bits & 0x8: | ||
| 1544 | - # compare against the file type from extended local headers | ||
| 1545 | - check_byte = (zinfo._raw_time >> 8) & 0xff | ||
| 1546 | - else: | ||
| 1547 | - # compare against the CRC otherwise | ||
| 1548 | - check_byte = (zinfo.CRC >> 24) & 0xff | ||
| 1549 | - if h[11] != check_byte: | ||
| 1550 | - raise RuntimeError("Bad password for file %r" % name) | ||
| 1551 | - | ||
| 1552 | - return ZipExtFile(zef_file, mode, zinfo, zd, True) | ||
| 1557 | + return ZipExtFile(zef_file, mode, zinfo, pwd, True) | ||
| 1553 | 1558 | except: | |
| 1554 | 1559 | zef_file.close() | |
| 1555 | 1560 | raise | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1 @@ | |||
| 1 | + Fixed seeking backward on an encrypted :class:`zipfile.ZipExtFile`. | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments