diff -r 466cbfb3c0c6 -r 747cb08ae623 Lib/test/test_zipfile.py --- a/Lib/test/test_zipfile.py Fri Nov 02 07:34:37 2012 +0100 +++ b/Lib/test/test_zipfile.py Sat Nov 03 22:17:21 2012 +0100 @@ -14,6 +14,12 @@ import unittest from StringIO import StringIO + +try: + import _zipdecrypt +except ImportError: + _zipdecrypt = None + from tempfile import TemporaryFile from random import randint, random from unittest import skipUnless @@ -1047,7 +1053,14 @@ plain = 'zipfile.py encryption test' plain2 = '\x00'*512 + isCDecryptionTest = False + savedZipDecryptRef = None + def setUp(self): + if not self.isCDecryptionTest: + self.savedZipDecryptRef = zipfile._zipdecrypt + zipfile._zipdecrypt = None + with open(TESTFN, "wb") as fp: fp.write(self.data) self.zip = zipfile.ZipFile(TESTFN, "r") @@ -1061,6 +1074,10 @@ self.zip2.close() os.unlink(TESTFN2) + if not self.isCDecryptionTest: + zipfile._zipdecrypt = self.savedZipDecryptRef + self.savedZipDecryptRef = None + def test_no_password(self): # Reading the encrypted file without password # must generate a RunTime exception @@ -1081,6 +1098,13 @@ self.assertEqual(self.zip2.read("zero"), self.plain2) +class PyDecryptionTests(DecryptionTests): + isCDecryptionTest = False + +class CDecryptionTests(DecryptionTests): + isCDecryptionTest = True + + class TestsWithRandomBinaryFiles(unittest.TestCase): def setUp(self): datacount = randint(16, 64)*1024 + randint(1, 1024) @@ -1397,10 +1421,14 @@ def test_main(): - run_unittest(TestsWithSourceFile, TestZip64InSmallFiles, OtherTests, - PyZipFileTests, DecryptionTests, TestsWithMultipleOpens, - TestWithDirectory, UniversalNewlineTests, - TestsWithRandomBinaryFiles) + tests = [TestsWithSourceFile, TestZip64InSmallFiles, OtherTests, + PyZipFileTests, PyDecryptionTests, TestsWithMultipleOpens, + TestWithDirectory, UniversalNewlineTests, TestsWithRandomBinaryFiles] + + if _zipdecrypt: + tests.append(CDecryptionTests) + + run_unittest(*tests) if __name__ == "__main__": test_main() diff -r 466cbfb3c0c6 -r 747cb08ae623 Lib/zipfile.py --- a/Lib/zipfile.py Fri Nov 02 07:34:37 2012 +0100 +++ b/Lib/zipfile.py Sat Nov 03 22:17:21 2012 +0100 @@ -15,7 +15,11 @@ __all__ = ["BadZipfile", "error", "ZIP_STORED", "ZIP_DEFLATED", "is_zipfile", "ZipInfo", "ZipFile", "PyZipFile", "LargeZipFile" ] - +try: + import _zipdecrypt +except ImportError: + _zipdecrypt = None + class BadZipfile(Exception): pass @@ -617,7 +621,10 @@ self._compress_left -= len(data) if data and self._decrypter is not None: - data = ''.join(map(self._decrypter, data)) + if _zipdecrypt: + data = self._decrypter(data) + else: + data = ''.join(map(self._decrypter, data)) if self._compress_type == ZIP_STORED: self._update_crc(data, eof=(self._compress_left==0)) @@ -934,14 +941,20 @@ raise RuntimeError, "File %s is encrypted, " \ "password required for extraction" % name - zd = _ZipDecrypter(pwd) + if _zipdecrypt: + zd = _zipdecrypt.ZipDecrypter(pwd) + else: + zd = _ZipDecrypter(pwd) # 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 _zipdecrypt: + h = zd(bytes[0:12]) + else: + h = map(zd, bytes[0:12]) if zinfo.flag_bits & 0x8: # compare against the file type from extended local headers check_byte = (zinfo._raw_time >> 8) & 0xff diff -r 466cbfb3c0c6 -r 747cb08ae623 Modules/_zipdecrypt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_zipdecrypt.c Sat Nov 03 22:17:21 2012 +0100 @@ -0,0 +1,226 @@ +/* + * Module for decryption of data encrypted using the ZIP 2.0 simple encryption + * algorithm. + * + * Details of encryption algo at: + * http://www.pkware.com/documents/casestudies/APPNOTE.TXT + * + * Author: Shashank(shashank.sunny.singh@gmail.com) + */ + +#define PY_SSIZE_T_CLEAN + +#include "Python.h" +#include "structmember.h" + +static const int _CRC_POLY = 0xedb88320; +static int _CRC_TABLE[256]; + +static void +_build_crc_table(void) +{ + int i, j, crc; + for (i = 0; i < 256; i++) { + crc = i; + for (j = 0; j < 8; j++) { + if (crc & 1) + crc = ((crc >> 1) & 0x7FFFFFFF) ^ _CRC_POLY; + else + crc = ((crc >> 1) & 0x7FFFFFFF); + } + _CRC_TABLE[i] = crc; + } +} + +static int +_crc32(char c, int crc) +{ + return ((crc >> 8) & 0xffffff) ^ _CRC_TABLE[(crc ^ c) & 0xff]; +} + +static void +_update_keys(char c, int* key0, int* key1, int* key2) +{ + *key0 = _crc32(c, *key0); + *key1 = (*key1 + (*key0 & 255)) & 0xFFFFFFFF ; + *key1 = (*key1 * 134775813 + 1) & 0xFFFFFFFF; + *key2 = _crc32((*key1 >> 24) & 255, *key2); +} + +static void +decrypt(char* cypher_text, const Py_ssize_t cypher_len, + int* key0, int* key1, int* key2) +{ + Py_ssize_t i; + + for (i = 0; i < cypher_len; i++) { + unsigned short k = *key2 | 2; + *cypher_text = *cypher_text ^ (((k * (k^1)) >> 8) & 255); + _update_keys(*cypher_text, key0, key1, key2); + cypher_text++; + } +} + +typedef struct +{ + PyObject_HEAD + int key0; + int key1; + int key2; +} ZipDecrypter; + +static void +zipdecrypter_dealloc(ZipDecrypter *self) +{ + self->ob_type->tp_free((PyObject *) self); +} + +static PyObject * +zipdecrypter_new(PyTypeObject *type, PyObject *args, PyObject* kwds) +{ + ZipDecrypter *self; + + self = (ZipDecrypter *) type->tp_alloc(type, 0); + return (PyObject *) self; +} + +static int +zipdecrypter_init(ZipDecrypter *self, PyObject *args, PyObject *kwds) +{ + Py_buffer pwd_buf; + char *pwd; + Py_ssize_t i; + + if (!PyArg_ParseTuple(args, "s*:ZipDecrypter.__init__", &pwd_buf)) + return -1; + + self->key0 = 305419896; + self->key1 = 591751049; + self->key2 = 878082192; + + pwd = (char*)pwd_buf.buf; + + for(i=0; ikey0, &self->key1, &self->key2); + pwd++; + } + + PyBuffer_Release(&pwd_buf); + + return 0; +} + +static PyObject * +zipdecrypter_call(ZipDecrypter *self, PyObject *args, PyObject *kwds) +{ + Py_buffer cipher_buf; + PyObject *plain_buf=NULL; + Py_ssize_t len; + char *temp_buf; + + if (!PyArg_ParseTuple(args, "s*:ZipDecrypter.__call__", &cipher_buf)) + return NULL; + + len = cipher_buf.len; + + temp_buf = PyMem_Malloc(len); + if(temp_buf == NULL) { + PyErr_NoMemory(); + PyBuffer_Release(&cipher_buf); + return NULL; + } + + memcpy(temp_buf, cipher_buf.buf, cipher_buf.len); + + PyBuffer_Release(&cipher_buf); + + decrypt(temp_buf, len, &self->key0, &self->key1, &self->key2); + + plain_buf = PyBytes_FromStringAndSize(temp_buf, len); + + PyMem_Free(temp_buf); + + return plain_buf; + +} + + +PyDoc_STRVAR(zipdecrypter_doc, + "Support for decryption of data encrypted using the ZIP 2.0 simple \n\ +encryption algorithm\n\ +zipDecrypter(cipher_byte_buffer) -> decrypted_byte_buffer.\n\ +\n\ +Decrypts the given encrypted data (cipher text)\n\ +as the input byte buffer and returns a new byte\n\ +buffer containing the decrypted data(plain text)."); + +static PyTypeObject ZipDecrypter_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "_zipdecrypt.ZipDecrypter", /* tp_name */ + sizeof (ZipDecrypter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) zipdecrypter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)zipdecrypter_call, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + zipdecrypter_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)zipdecrypter_init, /* tp_init */ + 0, /* tp_alloc */ + zipdecrypter_new, /* tp_new */ + 0, /* tp_free */ +}; + +PyDoc_STRVAR(module_doc, + "Provides support for decrypting ZIP archives encrypted.\n\ +using the ZIP 2.0 simple encryption algorithm\n\ +"); + +PyMODINIT_FUNC +init_zipdecrypt(void) +{ + PyObject *m; + + if (PyType_Ready(&ZipDecrypter_Type) < 0) + return; + + m = Py_InitModule3("_zipdecrypt", NULL, module_doc); + if (m == NULL) + return; + + Py_INCREF(&ZipDecrypter_Type); + if (PyModule_AddObject(m, "ZipDecrypter", + (PyObject *) & ZipDecrypter_Type) < 0) + return; + + _build_crc_table(); + +} + diff -r 466cbfb3c0c6 -r 747cb08ae623 PC/VC6/pythoncore.dsp --- a/PC/VC6/pythoncore.dsp Fri Nov 02 07:34:37 2012 +0100 +++ b/PC/VC6/pythoncore.dsp Sat Nov 03 22:17:21 2012 +0100 @@ -193,6 +193,10 @@ # End Source File # Begin Source File +SOURCE=..\..\Modules\_zipdecrypt.c +# End Source File +# Begin Source File + SOURCE=..\..\Objects\abstract.c # End Source File # Begin Source File diff -r 466cbfb3c0c6 -r 747cb08ae623 PC/VS7.1/pythoncore.vcproj --- a/PC/VS7.1/pythoncore.vcproj Fri Nov 02 07:34:37 2012 +0100 +++ b/PC/VS7.1/pythoncore.vcproj Sat Nov 03 22:17:21 2012 +0100 @@ -410,6 +410,9 @@ RelativePath="..\..\Pc\_winreg.c"> + + + + diff -r 466cbfb3c0c6 -r 747cb08ae623 setup.py --- a/setup.py Fri Nov 02 07:34:37 2012 +0100 +++ b/setup.py Sat Nov 03 22:17:21 2012 +0100 @@ -626,6 +626,9 @@ # http://mail.python.org/pipermail/python-dev/2006-January/060023.html #exts.append( Extension('timing', ['timingmodule.c']) ) + # zipfile decryption speedup + exts.append( Extension("_zipdecrypt", ['_zipdecrypt.c']) ) + # # Here ends the simple stuff. From here on, modules need certain # libraries, are platform-specific, or present other surprises.