Index: Lib/zipfile.py =================================================================== --- Lib/zipfile.py (revision 85374) +++ Lib/zipfile.py (working copy) @@ -13,6 +13,11 @@ 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 +595,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.decrypt(data) + else: + data = ''.join(map(self._decrypter, data)) if self._compress_type == ZIP_STORED: self._readbuffer = self._readbuffer[self._offset:] + data @@ -876,15 +884,21 @@ if not pwd: 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.decrypt(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 85374) +++ 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/zipdecryptmodule.c =================================================================== --- Modules/zipdecryptmodule.c (revision 0) +++ Modules/zipdecryptmodule.c (revision 0) @@ -0,0 +1,204 @@ +#include "Python.h" +#include "structmember.h" + +static const int _CRC_POLY = 0xedb88320; +static int _CRC_TABLE[256]; + +static void +build_crc_table() +{ + 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 int cypher_len, + int* key0, int* key1, int* key2) +{ + int 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; +} ZipDecrypt; + +static void +zipdecrypt_dealloc(ZipDecrypt *self) +{ + self->ob_type->tp_free((PyObject *) self); +} + +static PyObject * +zipdecrypt_new(PyTypeObject *type, PyObject *args, PyObject* kwds) +{ + ZipDecrypt *self; + char *pwd; int pwd_len; + + if (!PyArg_ParseTuple(args, "s#", &pwd, &pwd_len)) + return NULL; + + self = (ZipDecrypt *) type->tp_alloc(type, 0); + return (PyObject *) self; +} + +static int +zipdecrypt_init(ZipDecrypt *self, PyObject *args, PyObject *kwds) +{ + char *pwd; + int pwd_len; + int i; + + if (!PyArg_ParseTuple(args, "s#", &pwd, &pwd_len)) + return -1; + + self->key0 = 305419896; + self->key1 = 591751049; + self->key2 = 878082192; + + for(i=0; ikey0, &self->key1, &self->key2); + pwd++; + } + + return 0; +} + +static PyObject * +zipdecrypt_decrypt(ZipDecrypt *self, PyObject *args) +{ + char *buffer; + int buffer_len; + PyObject *rv; + + if (!PyArg_ParseTuple(args, "s#", &buffer, &buffer_len)) + return NULL; + + decrypt(buffer, buffer_len, &self->key0, &self->key1, &self->key2); + + rv = Py_BuildValue("s#", buffer, buffer_len); + if (rv == NULL) + return NULL; + + return rv; + +} + +PyDoc_STRVAR(doc_decrypt, +"decrypt(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 PyMethodDef zipdecrypt_methods[] = { + {"decrypt",(PyCFunction)zipdecrypt_decrypt, + METH_VARARGS, doc_decrypt + }, + {NULL} +}; + + +PyDoc_STRVAR(zipdecrypt_doc, + "zipdecrypt provides support for decrypting ZIP archives encrypted.\n\ +using the ZIP 2.0 simple encryption algorithm\n\ +"); + +static PyTypeObject ZipDecrypt_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "zipdecrypt.ZipDecrypt", /* tp_name */ + sizeof (ZipDecrypt), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) zipdecrypt_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 */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + zipdecrypt_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + zipdecrypt_methods, /* 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) zipdecrypt_init, /* tp_init */ + 0, /* tp_alloc */ + zipdecrypt_new, /* tp_new */ + 0, /* tp_free */ +}; + +PyMODINIT_FUNC +initzipdecrypt(void) +{ + PyObject *m; + + if (PyType_Ready(&ZipDecrypt_Type) < 0) + return; + + m = Py_InitModule3("zipdecrypt", NULL, zipdecrypt_doc); + if (m == NULL) + return; + + Py_INCREF(&ZipDecrypt_Type); + if (PyModule_AddObject(m, "ZipDecrypter", + (PyObject *) & ZipDecrypt_Type) < 0) + return; + + build_crc_table(); + +} Index: Modules/Setup.dist =================================================================== --- Modules/Setup.dist (revision 85374) +++ Modules/Setup.dist (working copy) @@ -493,3 +493,5 @@ # Another example -- the 'xxsubtype' module shows C-level subtyping in action xxsubtype xxsubtype.c + +zipdecrypt zipdecryptmodule.c