Skip to content

Commit 003d4cf

Browse files
committed
Added ZipCrypto decrypting ability
1 parent 63ed6e2 commit 003d4cf

File tree

4 files changed

+90
-10
lines changed

4 files changed

+90
-10
lines changed

adm-zip.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ module.exports = function (/**String*/input) {
6363
*
6464
* @return Buffer or Null in case of error
6565
*/
66-
readFile: function (/**Object*/entry) {
66+
readFile: function (/**Object*/entry, /*String, Buffer*/pass) {
6767
var item = getEntry(entry);
68-
return item && item.getData() || null;
68+
return item && item.getData(pass) || null;
6969
},
7070

7171
/**
@@ -482,7 +482,7 @@ module.exports = function (/**String*/input) {
482482
* Test the archive
483483
*
484484
*/
485-
test: function () {
485+
test: function (pass) {
486486
if (!_zip) {
487487
return false;
488488
}
@@ -492,7 +492,7 @@ module.exports = function (/**String*/input) {
492492
if (entry.isDirectory) {
493493
continue;
494494
}
495-
var content = _zip.entries[entry].getData();
495+
var content = _zip.entries[entry].getData(pass);
496496
if (!content) {
497497
return false;
498498
}
@@ -510,7 +510,7 @@ module.exports = function (/**String*/input) {
510510
* @param overwrite If the file already exists at the target path, the file will be overwriten if this is true.
511511
* Default is FALSE
512512
*/
513-
extractAllTo: function (/**String*/targetPath, /**Boolean*/overwrite) {
513+
extractAllTo: function (/**String*/targetPath, /**Boolean*/overwrite, /*String, Buffer*/pass) {
514514
overwrite = overwrite || false;
515515
if (!_zip) {
516516
throw new Error(Utils.Errors.NO_ZIP);
@@ -521,7 +521,7 @@ module.exports = function (/**String*/input) {
521521
Utils.makeDir(entryName);
522522
return;
523523
}
524-
var content = entry.getData();
524+
var content = entry.getData(pass);
525525
if (!content) {
526526
throw new Error(Utils.Errors.CANT_EXTRACT_FILE);
527527
}

methods/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
exports.Deflater = require("./deflater");
2-
exports.Inflater = require("./inflater");
2+
exports.Inflater = require("./inflater");
3+
exports.ZipCrypto = require("./zipcrypto");

methods/zipcrypto.js

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// generate CRC32 lookup table
2+
const crctable = (new Uint32Array(256)).map((t,crc)=>{
3+
for(let j=0;j<8;j++){
4+
if (0 !== (crc & 1)){
5+
crc = (crc >>> 1) ^ 0xEDB88320
6+
}else{
7+
crc >>>= 1
8+
}
9+
}
10+
return crc>>>0;
11+
});
12+
13+
function make_decrypter(/*Buffer*/pwd){
14+
// C-style uInt32 Multiply
15+
const uMul = (a,b) => Math.imul(a, b) >>> 0;
16+
// Initialize keys with default values
17+
const keys = new Uint32Array([0x12345678, 0x23456789, 0x34567890]);
18+
// crc32 byte update
19+
const crc32update = (pCrc32, bval) => {
20+
return crctable[(pCrc32 ^ bval) & 0xff] ^ (pCrc32 >>> 8);
21+
}
22+
// update keys with byteValues
23+
const updateKeys = (byteValue) => {
24+
keys[0] = crc32update(keys[0], byteValue);
25+
keys[1] += keys[0] & 0xff;
26+
keys[1] = uMul(keys[1], 134775813) + 1;
27+
keys[2] = crc32update(keys[2], keys[1] >>> 24);
28+
}
29+
30+
// 1. Stage initialize key
31+
const pass = (Buffer.isBuffer(pwd)) ? pwd : Buffer.from(pwd);
32+
for(let i=0; i< pass.length; i++){
33+
updateKeys(pass[i]);
34+
}
35+
36+
// return decrypter function
37+
return function (/*Buffer*/data){
38+
if (!Buffer.isBuffer(data)){
39+
throw 'decrypter needs Buffer'
40+
}
41+
// result - we create new Buffer for results
42+
const result = Buffer.alloc(data.length);
43+
let pos = 0;
44+
// process input data
45+
for(let c of data){
46+
const k = (keys[2] | 2) >>> 0; // key
47+
c ^= (uMul(k, k^1) >> 8) & 0xff; // decode
48+
result[pos++] = c; // Save Value
49+
updateKeys(c); // update keys with decoded byte
50+
}
51+
return result;
52+
}
53+
}
54+
55+
function decrypt(/*Buffer*/ data, /*Object*/header, /*String, Buffer*/ pwd){
56+
if (!data || !Buffer.isBuffer(data) || data.length < 12) {
57+
return Buffer.alloc(0);
58+
}
59+
60+
// We Initialize and generate decrypting function
61+
const decrypter = make_decrypter(pwd);
62+
63+
// check - for testing password
64+
const check = header.crc >>> 24;
65+
// decrypt salt what is always 12 bytes and is a part of file content
66+
const testbyte = decrypter(data.slice(0, 12))[11];
67+
68+
// does password meet expectations
69+
if (check !== testbyte){
70+
throw 'ADM-ZIP: Wrong Password';
71+
}
72+
73+
// decode content
74+
return decrypter(data.slice(12));
75+
}
76+
77+
module.exports = {decrypt};

zipEntry.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ module.exports = function (/*Buffer*/input) {
3434
return true;
3535
}
3636

37-
function decompress(/*Boolean*/async, /*Function*/callback, /*String*/pass) {
37+
function decompress(/*Boolean*/async, /*Function*/callback, /*String, Buffer*/pass) {
3838
if(typeof callback === 'undefined' && typeof async === 'string') {
3939
pass=async;
4040
async=void 0;
@@ -55,8 +55,10 @@ module.exports = function (/*Buffer*/input) {
5555
}
5656

5757
if (_entryHeader.encripted){
58-
if (async && callback) callback(Buffer.alloc(0), Utils.Errors.UNKNOWN_METHOD);
59-
throw new Error(Utils.Errors.UNKNOWN_METHOD);
58+
if ('string' !== typeof pass && !Buffer.isBuffer(pass)){
59+
throw new Error('ADM-ZIP: Incompatible password parameter');
60+
}
61+
compressedData = Methods.ZipCrypto.decrypt(compressedData, _entryHeader, pass);
6062
}
6163

6264
var data = Buffer.alloc(_entryHeader.size);

0 commit comments

Comments
 (0)