Skip to content

Commit 0c63fc2

Browse files
committed
Fix zipfile decryption. The check for validity only worked on one
type of encrypted zip files. Files using extended local headers needed to compare the check byte against different values. (according to reading the infozip unzip crypt.c source code) Fixes issue1003.
1 parent 88fbcf8 commit 0c63fc2

File tree

2 files changed

+32
-2
lines changed

2 files changed

+32
-2
lines changed

Lib/test/test_zipfile.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,31 +698,52 @@ class DecryptionTests(unittest.TestCase):
698698
'\x1a\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00 \x00\xb6\x81'
699699
'\x00\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00'
700700
'\x00\x00L\x00\x00\x00\x00\x00' )
701+
data2 = (
702+
'PK\x03\x04\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02'
703+
'\x00\x00\x04\x00\x15\x00zeroUT\t\x00\x03\xd6\x8b\x92G\xda\x8b\x92GUx\x04'
704+
'\x00\xe8\x03\xe8\x03\xc7<M\xb5a\xceX\xa3Y&\x8b{oE\xd7\x9d\x8c\x98\x02\xc0'
705+
'PK\x07\x08xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00PK\x01\x02\x17\x03'
706+
'\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00'
707+
'\x04\x00\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00ze'
708+
'roUT\x05\x00\x03\xd6\x8b\x92GUx\x00\x00PK\x05\x06\x00\x00\x00\x00\x01'
709+
'\x00\x01\x00?\x00\x00\x00[\x00\x00\x00\x00\x00' )
701710

702711
plain = 'zipfile.py encryption test'
712+
plain2 = '\x00'*512
703713

704714
def setUp(self):
705715
fp = open(TESTFN, "wb")
706716
fp.write(self.data)
707717
fp.close()
708718
self.zip = zipfile.ZipFile(TESTFN, "r")
719+
fp = open(TESTFN2, "wb")
720+
fp.write(self.data2)
721+
fp.close()
722+
self.zip2 = zipfile.ZipFile(TESTFN2, "r")
709723

710724
def tearDown(self):
711725
self.zip.close()
712726
os.unlink(TESTFN)
727+
self.zip2.close()
728+
os.unlink(TESTFN2)
713729

714730
def testNoPassword(self):
715731
# Reading the encrypted file without password
716732
# must generate a RunTime exception
717733
self.assertRaises(RuntimeError, self.zip.read, "test.txt")
734+
self.assertRaises(RuntimeError, self.zip2.read, "zero")
718735

719736
def testBadPassword(self):
720737
self.zip.setpassword("perl")
721738
self.assertRaises(RuntimeError, self.zip.read, "test.txt")
739+
self.zip2.setpassword("perl")
740+
self.assertRaises(RuntimeError, self.zip2.read, "zero")
722741

723742
def testGoodPassword(self):
724743
self.zip.setpassword("python")
725744
self.assertEquals(self.zip.read("test.txt"), self.plain)
745+
self.zip2.setpassword("12345")
746+
self.assertEquals(self.zip2.read("zero"), self.plain2)
726747

727748

728749
class TestsWithRandomBinaryFiles(unittest.TestCase):

Lib/zipfile.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ class ZipInfo (object):
186186
'CRC',
187187
'compress_size',
188188
'file_size',
189+
'_raw_time',
189190
)
190191

191192
def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
@@ -683,6 +684,7 @@ def _RealGetContents(self):
683684
x.CRC, x.compress_size, x.file_size) = centdir[1:12]
684685
x.volume, x.internal_attr, x.external_attr = centdir[15:18]
685686
# Convert date/time code to (year, month, day, hour, min, sec)
687+
x._raw_time = t
686688
x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F,
687689
t>>11, (t>>5)&0x3F, (t&0x1F) * 2 )
688690

@@ -790,11 +792,18 @@ def open(self, name, mode="r", pwd=None):
790792
# The first 12 bytes in the cypher stream is an encryption header
791793
# used to strengthen the algorithm. The first 11 bytes are
792794
# completely random, while the 12th contains the MSB of the CRC,
795+
# or the MSB of the file time depending on the header type
793796
# and is used to check the correctness of the password.
794797
bytes = zef_file.read(12)
795798
h = map(zd, bytes[0:12])
796-
if ord(h[11]) != ((zinfo.CRC>>24)&255):
797-
raise RuntimeError, "Bad password for file %s" % name
799+
if zinfo.flag_bits & 0x8:
800+
# compare against the file type from extended local headers
801+
check_byte = (zinfo._raw_time >> 8) & 0xff
802+
else:
803+
# compare against the CRC otherwise
804+
check_byte = (zinfo.CRC >> 24) & 0xff
805+
if ord(h[11]) != check_byte:
806+
raise RuntimeError("Bad password for file", name)
798807

799808
# build and return a ZipExtFile
800809
if zd is None:

0 commit comments

Comments
 (0)