Index: PC/os2emx/config.c =================================================================== --- PC/os2emx/config.c (revision 85810) +++ PC/os2emx/config.c (working copy) @@ -76,6 +76,7 @@ extern void inittiming(); extern void initxxsubtype(); extern void initzipimport(); +extern void init_zipdecrypt(); #if !HAVE_DYNAMIC_LOADING extern void init_curses(); extern void init_curses_panel(); @@ -142,6 +143,7 @@ {"timing", inittiming}, {"xxsubtype", initxxsubtype}, {"zipimport", initzipimport}, + {"_zipdecrypt", init_zipdecrypt}, #if !HAVE_DYNAMIC_LOADING {"_curses", init_curses}, {"_curses_panel", init_curses_panel}, Index: Lib/zipfile.py =================================================================== --- Lib/zipfile.py (revision 85810) +++ Lib/zipfile.py (working copy) @@ -13,6 +13,12 @@ zlib = None crc32 = binascii.crc32 +try: + import _zipdecrypt +except ImportError: + _zipdecrypt = None + + __all__ = ["BadZipfile", "error", "ZIP_STORED", "ZIP_DEFLATED", "is_zipfile", "ZipInfo", "ZipFile", "PyZipFile", "LargeZipFile" ] @@ -590,7 +596,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._readbuffer = self._readbuffer[self._offset:] + data @@ -877,14 +886,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 Index: Lib/test/test_zipfile.py =================================================================== --- Lib/test/test_zipfile.py (revision 85810) +++ Lib/test/test_zipfile.py (working copy) @@ -4,6 +4,11 @@ except ImportError: zlib = None +try: + import _zipdecrypt +except ImportError: + _zipdecrypt = None + import os import sys import time @@ -895,7 +900,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") @@ -909,6 +921,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 @@ -929,6 +945,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) @@ -1230,10 +1253,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() Index: Modules/_zipdecrypt.c =================================================================== --- Modules/_zipdecrypt.c (revision 0) +++ Modules/_zipdecrypt.c (revision 0) @@ -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\ +zipDecryprer(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(); + +} +