Commit 0a250de3 authored by Gregory P. Smith's avatar Gregory P. Smith

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.
parent acd5ab93
...@@ -698,31 +698,52 @@ class DecryptionTests(unittest.TestCase): ...@@ -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' '\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\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00'
'\x00\x00L\x00\x00\x00\x00\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' plain = 'zipfile.py encryption test'
plain2 = '\x00'*512
def setUp(self): def setUp(self):
fp = open(TESTFN, "wb") fp = open(TESTFN, "wb")
fp.write(self.data) fp.write(self.data)
fp.close() fp.close()
self.zip = zipfile.ZipFile(TESTFN, "r") 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): def tearDown(self):
self.zip.close() self.zip.close()
os.unlink(TESTFN) os.unlink(TESTFN)
self.zip2.close()
os.unlink(TESTFN2)
def testNoPassword(self): def testNoPassword(self):
# Reading the encrypted file without password # Reading the encrypted file without password
# must generate a RunTime exception # must generate a RunTime exception
self.assertRaises(RuntimeError, self.zip.read, "test.txt") self.assertRaises(RuntimeError, self.zip.read, "test.txt")
self.assertRaises(RuntimeError, self.zip2.read, "zero")
def testBadPassword(self): def testBadPassword(self):
self.zip.setpassword("perl") self.zip.setpassword("perl")
self.assertRaises(RuntimeError, self.zip.read, "test.txt") self.assertRaises(RuntimeError, self.zip.read, "test.txt")
self.zip2.setpassword("perl")
self.assertRaises(RuntimeError, self.zip2.read, "zero")
def testGoodPassword(self): def testGoodPassword(self):
self.zip.setpassword("python") self.zip.setpassword("python")
self.assertEquals(self.zip.read("test.txt"), self.plain) 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): class TestsWithRandomBinaryFiles(unittest.TestCase):
......
...@@ -186,6 +186,7 @@ class ZipInfo (object): ...@@ -186,6 +186,7 @@ class ZipInfo (object):
'CRC', 'CRC',
'compress_size', 'compress_size',
'file_size', 'file_size',
'_raw_time',
) )
def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)): def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
...@@ -683,6 +684,7 @@ class ZipFile: ...@@ -683,6 +684,7 @@ class ZipFile:
x.CRC, x.compress_size, x.file_size) = centdir[1:12] x.CRC, x.compress_size, x.file_size) = centdir[1:12]
x.volume, x.internal_attr, x.external_attr = centdir[15:18] x.volume, x.internal_attr, x.external_attr = centdir[15:18]
# Convert date/time code to (year, month, day, hour, min, sec) # 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, x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F,
t>>11, (t>>5)&0x3F, (t&0x1F) * 2 ) t>>11, (t>>5)&0x3F, (t&0x1F) * 2 )
...@@ -790,11 +792,18 @@ class ZipFile: ...@@ -790,11 +792,18 @@ class ZipFile:
# The first 12 bytes in the cypher stream is an encryption header # The first 12 bytes in the cypher stream is an encryption header
# used to strengthen the algorithm. The first 11 bytes are # used to strengthen the algorithm. The first 11 bytes are
# completely random, while the 12th contains the MSB of the CRC, # 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. # and is used to check the correctness of the password.
bytes = zef_file.read(12) bytes = zef_file.read(12)
h = map(zd, bytes[0:12]) h = map(zd, bytes[0:12])
if ord(h[11]) != ((zinfo.CRC>>24)&255): if zinfo.flag_bits & 0x8:
raise RuntimeError, "Bad password for file %s" % name # 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 # build and return a ZipExtFile
if zd is None: if zd is None:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment