]> granicus.if.org Git - python/commitdiff
Fix zipfile decryption. The check for validity only worked on one
authorGregory P. Smith <greg@mad-scientist.com>
Sun, 20 Jan 2008 01:21:03 +0000 (01:21 +0000)
committerGregory P. Smith <greg@mad-scientist.com>
Sun, 20 Jan 2008 01:21:03 +0000 (01:21 +0000)
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.

Lib/test/test_zipfile.py
Lib/zipfile.py

index 40003aa2f0c8014ccc0843b64d48a2830064936c..adc0dc179a54283f2662a0e39bcc5c07947112b0 100644 (file)
@@ -698,31 +698,52 @@ class DecryptionTests(unittest.TestCase):
     '\x1a\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00 \x00\xb6\x81'
     '\x00\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00'
     '\x00\x00L\x00\x00\x00\x00\x00' )
+    data2 = (
+    'PK\x03\x04\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02'
+    '\x00\x00\x04\x00\x15\x00zeroUT\t\x00\x03\xd6\x8b\x92G\xda\x8b\x92GUx\x04'
+    '\x00\xe8\x03\xe8\x03\xc7<M\xb5a\xceX\xa3Y&\x8b{oE\xd7\x9d\x8c\x98\x02\xc0'
+    'PK\x07\x08xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00PK\x01\x02\x17\x03'
+    '\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00'
+    '\x04\x00\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00ze'
+    'roUT\x05\x00\x03\xd6\x8b\x92GUx\x00\x00PK\x05\x06\x00\x00\x00\x00\x01'
+    '\x00\x01\x00?\x00\x00\x00[\x00\x00\x00\x00\x00' )
 
     plain = 'zipfile.py encryption test'
+    plain2 = '\x00'*512
 
     def setUp(self):
         fp = open(TESTFN, "wb")
         fp.write(self.data)
         fp.close()
         self.zip = zipfile.ZipFile(TESTFN, "r")
+        fp = open(TESTFN2, "wb")
+        fp.write(self.data2)
+        fp.close()
+        self.zip2 = zipfile.ZipFile(TESTFN2, "r")
 
     def tearDown(self):
         self.zip.close()
         os.unlink(TESTFN)
+        self.zip2.close()
+        os.unlink(TESTFN2)
 
     def testNoPassword(self):
         # Reading the encrypted file without password
         # must generate a RunTime exception
         self.assertRaises(RuntimeError, self.zip.read, "test.txt")
+        self.assertRaises(RuntimeError, self.zip2.read, "zero")
 
     def testBadPassword(self):
         self.zip.setpassword("perl")
         self.assertRaises(RuntimeError, self.zip.read, "test.txt")
+        self.zip2.setpassword("perl")
+        self.assertRaises(RuntimeError, self.zip2.read, "zero")
 
     def testGoodPassword(self):
         self.zip.setpassword("python")
         self.assertEquals(self.zip.read("test.txt"), self.plain)
+        self.zip2.setpassword("12345")
+        self.assertEquals(self.zip2.read("zero"), self.plain2)
 
 
 class TestsWithRandomBinaryFiles(unittest.TestCase):
index eb00b34daa4195318402687fe0f770fa2f8f5b82..6feabbf220ba48b444f52abc275e907b2dab1e2a 100644 (file)
@@ -186,6 +186,7 @@ class ZipInfo (object):
             'CRC',
             'compress_size',
             'file_size',
+            '_raw_time',
         )
 
     def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
@@ -683,6 +684,7 @@ class ZipFile:
                 x.CRC, x.compress_size, x.file_size) = centdir[1:12]
             x.volume, x.internal_attr, x.external_attr = centdir[15:18]
             # Convert date/time code to (year, month, day, hour, min, sec)
+            x._raw_time = t
             x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F,
                                      t>>11, (t>>5)&0x3F, (t&0x1F) * 2 )
 
@@ -790,11 +792,18 @@ class ZipFile:
             # The first 12 bytes in the cypher stream is an encryption header
             #  used to strengthen the algorithm. The first 11 bytes are
             #  completely random, while the 12th contains the MSB of the CRC,
+            #  or the MSB of the file time depending on the header type
             #  and is used to check the correctness of the password.
             bytes = zef_file.read(12)
             h = map(zd, bytes[0:12])
-            if ord(h[11]) != ((zinfo.CRC>>24)&255):
-                raise RuntimeError, "Bad password for file %s" % name
+            if zinfo.flag_bits & 0x8:
+                # compare against the file type from extended local headers
+                check_byte = (zinfo._raw_time >> 8) & 0xff
+            else:
+                # compare against the CRC otherwise
+                check_byte = (zinfo.CRC >> 24) & 0xff
+            if ord(h[11]) != check_byte:
+                raise RuntimeError("Bad password for file", name)
 
         # build and return a ZipExtFile
         if zd is None: