From 3da125d1e8b6a393e47b142e3c7542017a92ab47 Mon Sep 17 00:00:00 2001 From: aurimasv Date: Fri, 2 May 2014 22:30:28 -0500 Subject: [PATCH 1/4] Move crc32 to utils.js so we can share the table with ZipCrypto Remove unused variable from generateZipParts --- lib/object.js | 94 ++------------------------------------------------- lib/utils.js | 92 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 92 deletions(-) diff --git a/lib/object.js b/lib/object.js index c22672a8..13ac2f72 100644 --- a/lib/object.js +++ b/lib/object.js @@ -347,8 +347,7 @@ var generateCompressedObjectFrom = function(file, compression) { * @return {object} the zip parts. */ var generateZipParts = function(name, file, compressedObject, offset) { - var data = compressedObject.compressedContent, - utfEncodedFileName = this.utf8encode(file.name), + var utfEncodedFileName = this.utf8encode(file.name), useUTF8 = utfEncodedFileName !== file.name, o = file.options, dosTime, @@ -751,96 +750,7 @@ var out = { * http://www.webtoolkit.info/ * */ - crc32: function crc32(input, crc) { - if (typeof input === "undefined" || !input.length) { - return 0; - } - - var isArray = utils.getTypeOf(input) !== "string"; - - var table = [ - 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, - 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, - 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, - 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, - 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, - 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, - 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, - 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, - 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, - 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, - 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, - 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, - 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, - 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, - 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, - 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, - 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, - 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, - 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, - 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, - 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, - 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, - 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, - 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, - 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, - 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, - 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, - 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, - 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, - 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, - 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, - 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, - 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, - 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, - 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, - 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, - 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, - 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, - 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, - 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, - 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, - 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, - 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, - 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, - 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, - 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, - 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, - 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, - 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, - 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, - 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, - 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, - 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, - 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, - 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, - 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, - 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, - 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, - 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, - 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, - 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, - 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, - 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, - 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D]; - - if (typeof(crc) == "undefined") { - crc = 0; - } - var x = 0; - var y = 0; - var b = 0; - - crc = crc ^ (-1); - for (var i = 0, iTop = input.length; i < iTop; i++) { - b = isArray ? input[i] : input.charCodeAt(i); - y = (crc ^ b) & 0xFF; - x = table[y]; - crc = (crc >>> 8) ^ x; - } - - return crc ^ (-1); - }, + crc32: utils.crc32, // Inspired by http://my.opera.com/GreyWyvern/blog/show.dml/1725165 diff --git a/lib/utils.js b/lib/utils.js index 219c53f9..73af6fe9 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -349,3 +349,95 @@ exports.isRegExp = function (object) { return Object.prototype.toString.call(object) === "[object RegExp]"; }; +exports.crcTable = [ + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D]; + +exports.crc32 = function crc32(input, crc) { + if (typeof input === "undefined" || !input.length) { + return 0; + } + + var isArray = exports.getTypeOf(input) !== "string"; + + var table = exports.crcTable; + + if (typeof(crc) == "undefined") { + crc = 0; + } + var x = 0; + var y = 0; + var b = 0; + + crc = crc ^ (-1); + for (var i = 0, iTop = input.length; i < iTop; i++) { + b = isArray ? input[i] : input.charCodeAt(i); + y = (crc ^ b) & 0xFF; + x = table[y]; + crc = (crc >>> 8) ^ x; + } + + return crc ^ (-1); +}; \ No newline at end of file From e83dffe633785a8b78f8e9e47bf796e5b33bad01 Mon Sep 17 00:00:00 2001 From: aurimasv Date: Fri, 2 May 2014 23:50:08 -0500 Subject: [PATCH 2/4] Add ZipCrypto library for decryption For ZipCrypto encrypted files, one of the following MUST be passed in the loadOptions object: * retrievePasswordCallback: a function that returns a password as a string, or * retrieveEncryptionKeysCallback: a function that returns an array of 3 32-bit integers to be used as pre-primed encryption keys If both are set, ZipCrypto will call only retrieveEncryptionKeysCallback. Additionally, invalidPasswordCallback MAY be set and will be called if the supplied password does not validate. invalidPasswordCallback should return `true` if password validation should be ignored. This patch does _not_ add support for PKWARE's Strong Encryption. --- lib/zipCrypto.js | 160 ++++++++++++++++++++++++++++++++++++++++++++++ lib/zipEntries.js | 9 +++ lib/zipEntry.js | 25 ++++++-- 3 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 lib/zipCrypto.js diff --git a/lib/zipCrypto.js b/lib/zipCrypto.js new file mode 100644 index 00000000..587e0d4a --- /dev/null +++ b/lib/zipCrypto.js @@ -0,0 +1,160 @@ +'use strict'; +var utils = require('./utils'); + +/** + * Based on the algorithm described in + * http://www.pkware.com/documents/casestudies/APPNOTE.TXT + */ + +var ZipCrypto = function(loadOptions) { + this.encryptionKeys = null; + this.retrievePasswordCallback = loadOptions.retrievePasswordCallback; + this.retrieveEncryptionKeysCallback = loadOptions.retrieveEncryptionKeysCallback; + this.invalidPasswordCallback = loadOptions.invalidPasswordCallback; +}; + +/** + * Requests password/encryption keys (once), primes the encryption keys, if necessary, + * and returns them to the caller. + * @return {number[]} An array of 3 32-bit numeric pre-primed encryption keys + * @throws {Error} if the password callbacks are not set or return incorrect data types + */ +ZipCrypto.prototype.getEncryptionKeys = function() { + if (!this.encryptionKeys) { + if (typeof this.retrievePasswordCallback != "function" && + typeof this.retrieveEncryptionKeysCallback != "function") { + throw new Error("retrievePasswordCallback or retrieveEncryptionKeysCallback must be set for encrypted ZIP files"); + } + + if (this.retrieveEncryptionKeysCallback) { + var keys = this.retrieveEncryptionKeysCallback(); + if(!(keys instanceof Array)) throw new Error("retrieveEncryptionKeysCallback must return an array of encryption keys"); + if(keys.length != 3) throw new Error("retrieveEncryptionKeysCallback must return an array of 3 encryption keys"); + for(var i=0; i>> 24) && + !this.invalidPassword(data.subarray(0,12), crc32) ) { + return; + } + } + } + return data.subarray(12); // First 12 bytes are encryption header +}; + +/** + * Updates a given CRC32 hash with an additional byte + * @param {number} crc CRC32 hash as a 32-bit number + * @param {number} b A single byte + * @return {number} Updated CRC32 hash + */ +ZipCrypto.crc32Byte = function(crc, b) { + var x = utils.crcTable[(crc ^ b) & 0xFF]; + return (crc >>> 8) ^ x; +}; + +/** + * Updates encyption keys + * @param {number[]} keys Encryption keys to be updated + * @param {number} b A single byte + */ +function updateKeys(keys, b) { + keys[0] = ZipCrypto.crc32Byte(keys[0], b); + keys[1] = u32Multiply(keys[1] + (keys[0] & 0xFF), 0x08088405) + 1; + keys[2] = ZipCrypto.crc32Byte(keys[2], keys[1] >> 24); +} + +/** + * Decrypts a single byte of data + * @param {number[]} keys Encryption keys + * @param {number} b A single byte + * @return {number} Decrypted byte + */ +function decryptByte(keys, b) { + var tmp = keys[2] | 2; + b = b ^ (u32Multiply(tmp,tmp ^ 1) >> 8); + updateKeys(keys, b); + return b; +} + +/** + * Performs 32-bit multiplication. + * Discards overflow bits and maintains accuracy for the low significance bits + * @param {number} a + * @param {number} b + * @return 32-bit product of a and b + */ +function u32Multiply(a, b) { + // We have a 52 bit mantissa, so we can safely multiply 32 bit and 16 bit + // numbers without losing accuracy (result cannot be more than 48 bits) + var a1 = a >>> 16; // MSB 16 bits + var a2 = a & 0xFFFF; // LSB 16 bits + // a1 and a2 are always positive here + b = b >>> 0; // Truncate MSBs past 32 bits + + return ( ( (b * a1) << 16 >>> 0) + b * a2 ) >>> 0; // Don't return negative numbers +} + +module.exports = ZipCrypto; \ No newline at end of file diff --git a/lib/zipEntries.js b/lib/zipEntries.js index b0f38e78..948b05d7 100644 --- a/lib/zipEntries.js +++ b/lib/zipEntries.js @@ -6,6 +6,7 @@ var utils = require('./utils'); var sig = require('./signature'); var ZipEntry = require('./zipEntry'); var support = require('./support'); +var ZipCrypto = require('./zipCrypto'); // class ZipEntries {{{ /** * All the entries in the zip file. @@ -16,6 +17,14 @@ var support = require('./support'); function ZipEntries(data, loadOptions) { this.files = []; this.loadOptions = loadOptions; + + if (ZipCrypto) { + var zc = new ZipCrypto(loadOptions); + this.loadOptions.decrypt = function() { + return zc.decryptData.apply(zc, arguments); + }; + } + if (data) { this.load(data); } diff --git a/lib/zipEntry.js b/lib/zipEntry.js index 8f56e809..77d43edd 100644 --- a/lib/zipEntry.js +++ b/lib/zipEntry.js @@ -20,9 +20,17 @@ ZipEntry.prototype = { * @return {boolean} true if the file is encrypted, false otherwise. */ isEncrypted: function() { - // bit 1 is set + // bit 0 is set return (this.bitFlag & 0x0001) === 0x0001; }, + /** + * say if the file uses PKWARE's strong encryption + * @return {boolean} true if the file is encryted using strong encryption, false otherwise + */ + isStrongEncryption: function() { + // bit 6 is set + return (this.bitFlag & 0x0040) === 0x0040; + }, /** * say if the file has utf-8 filename/comment. * @return {boolean} true if the filename/comment is in utf-8, false otherwise. @@ -61,6 +69,9 @@ ZipEntry.prototype = { return function() { var compressedFileData = utils.transformTo(compression.uncompressInputType, this.getCompressedContent()); + if (this.encrypted) { + compressedFileData = this.decrypt(compressedFileData, this.crc32); + } var uncompressedFileData = compression.uncompress(compressedFileData); if (uncompressedFileData.length !== uncompressedSize) { @@ -73,6 +84,7 @@ ZipEntry.prototype = { /** * Read the local part of a zip file and add the info in this object. * @param {DataReader} reader the reader to use. + * Pointer should be just past the local file header signature */ readLocalPart: function(reader) { var compression, localExtraFieldsLength; @@ -98,7 +110,8 @@ ZipEntry.prototype = { localExtraFieldsLength = reader.readInt(2); // can't be sure this will be the same as the central dir this.fileName = reader.readString(this.fileNameLength); reader.skip(localExtraFieldsLength); - + + this.encrypted = this.isEncrypted(); if (this.compressedSize == -1 || this.uncompressedSize == -1) { throw new Error("Bug or corrupted zip : didn't get enough informations from the central directory " + "(compressedSize == -1 || uncompressedSize == -1)"); } @@ -112,6 +125,10 @@ ZipEntry.prototype = { this.decompressed.uncompressedSize = this.uncompressedSize; this.decompressed.crc32 = this.crc32; this.decompressed.compressionMethod = this.compressionMethod; + this.decompressed.encrypted = this.encrypted; + if (this.encrypted) { + this.decompressed.decrypt = this.loadOptions.decrypt; + } this.decompressed.getCompressedContent = this.prepareCompressedContent(reader, reader.index, this.compressedSize, compression); this.decompressed.getContent = this.prepareContent(reader, reader.index, this.compressedSize, compression, this.uncompressedSize); @@ -145,8 +162,8 @@ ZipEntry.prototype = { this.externalFileAttributes = reader.readInt(4); this.localHeaderOffset = reader.readInt(4); - if (this.isEncrypted()) { - throw new Error("Encrypted zip are not supported"); + if (this.isEncrypted() && this.isStrongEncryption()) { + throw new Error("ZIP files using Strong Encryption are not supported"); } this.fileName = reader.readString(this.fileNameLength); From d35cee0f88627cf9f9c34d066fe6614aca0adf19 Mon Sep 17 00:00:00 2001 From: aurimasv Date: Sat, 3 May 2014 01:04:05 -0500 Subject: [PATCH 3/4] Minor fixes --- lib/zipCrypto.js | 15 +++++++-------- lib/zipEntry.js | 1 + 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/zipCrypto.js b/lib/zipCrypto.js index 587e0d4a..10c22fb1 100644 --- a/lib/zipCrypto.js +++ b/lib/zipCrypto.js @@ -72,23 +72,22 @@ ZipCrypto.prototype.invalidPassword = function(header, crc32) { /** * Decrypts data using ZipCrypto algorithm. Validates password if crc32 is supplied - * @param {Unit8Array} data Data to be decrypted. + * @param {Uint8Array} data Data to be decrypted. * @param {number} [crc32] CRC32 hash of the data. If supplied, the password is validated. - * @return {Unit8Array} Decrypted data in the same format as was passed in + * @return {Uint8Array} Decrypted data in the same format as was passed in */ ZipCrypto.prototype.decryptData = function(data, crc32) { - // This doesn't work, how can we check? - //if(data instanceof Unit8Array) { - return this.decryptDataAsUnit8Array(data, crc32); - //} + if(data instanceof Uint8Array) { + return this.decryptDataAsUint8Array(data, crc32); + } - //throw new Error("ZipCrypto decryption is only supported for Unit8Array data"); + throw new Error("ZipCrypto decryption is only supported for Uint8Array data"); }; /** * @see ZipCrypto.decryptData */ -ZipCrypto.prototype.decryptDataAsUnit8Array = function(data, crc32) { +ZipCrypto.prototype.decryptDataAsUint8Array = function(data, crc32) { var keys = this.getEncryptionKeys(); for (var i=0; i Date: Sat, 3 May 2014 01:04:19 -0500 Subject: [PATCH 4/4] Update tests --- test/ref/encrypted.zip | Bin 156 -> 156 bytes test/test.js | 39 ++++++++++++++++++++++++++------------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/test/ref/encrypted.zip b/test/ref/encrypted.zip index 632a988b756ee264af0859f8ad9959a7a454bb15..c5d5fd22ba42fa6294f8a04d32b8e3d66555f8e9 100644 GIT binary patch delta 58 zcmbQkIERrhz?+$ci-D7Yfx(e=vCBk0Nr`avTmkt)p1hs2BDRY)e8|qxZ54Z8bx3-m Ly$lOjO}s4ts=yH! delta 58 zcmbQkIERrhz?+$ci-D7YfniR`N2`f^k`gJ)Zl9a4`f<0x6|K`dwPx{!TAC~K@qG1} MXfMM8RugXv02y!;O8@`> diff --git a/test/test.js b/test/test.js index 0e076526..699b5f2c 100644 --- a/test/test.js +++ b/test/test.js @@ -911,20 +911,8 @@ test("unknown compression throws an exception", function () { ok(true, "an exception were thrown"); } }); -// }}} More advanced - -QUnit.module("Load file, not supported features"); // {{{ -// zip -0 -X -e encrypted.zip Hello.txt -testZipFile("basic encryption", "ref/encrypted.zip", function(file) { - try { - var zip = new JSZip(file); - ok(false, "Encryption is not supported, but no exception were thrown"); - } catch(e) { - equal(e.message, "Encrypted zip are not supported", "the error message is useful"); - } -}); -// }}} Load file, not supported features +// }}} More advanced QUnit.module("Load file, corrupted zip"); // {{{ @@ -1134,6 +1122,31 @@ test("A folder stays a folder", function () { ok(reloaded.files['folder/'].options.dir, "the folder is marked as a folder"); }); +// zip -0 -X -e encrypted.zip Hello.txt (password: test) +testZipFile("ZipCrypto encryption", "ref/encrypted.zip", function(file) { + var zip = new JSZip(file, { + retrievePasswordCallback: function() { + return 'test' + } + }); + equal(zip.file("Hello.txt").asText(), "Hello World\n", "ZipCrypto-encrypted file was correctly read."); +}); + +// zip -0 -X -e encrypted.zip Hello.txt (password: test) +testZipFile("ZipCrypto encryption: wrong password", "ref/encrypted.zip", function(file) { + var zip = new JSZip(file, { + retrievePasswordCallback: function() { + return 'wrong' + } + }); + try { + zip.file("Hello.txt").asText(); + ok(false, "no exception was thrown"); + } catch(e) { + ok(e.message.match("Supplied password is invalid"), "expected exception was thrown"); + } +}); + // }}} Load file QUnit.module("Load complex files"); // {{{