Skip to content

Commit 54c3817

Browse files
authored
Merge pull request #472 from 10bitFX/info-zip-password
Add support for Info-Zip password check spec for ZipCrypto.
2 parents 10242c3 + 1bfa3bb commit 54c3817

File tree

5 files changed

+62
-7
lines changed

5 files changed

+62
-7
lines changed

headers/entryHeader.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ module.exports = function () {
8383
set time(val) {
8484
setTime(val);
8585
},
86-
86+
get timeHighByte() {
87+
return (_time >>> 8) & 0xff;
88+
},
8789
get crc() {
8890
return _crc;
8991
},

methods/zipcrypto.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,12 @@ function decrypt(/*Buffer*/ data, /*Object*/ header, /*String, Buffer*/ pwd) {
118118
// 2. decrypt salt what is always 12 bytes and is a part of file content
119119
const salt = decrypter(data.slice(0, 12));
120120

121-
// 3. does password meet expectations
122-
if (salt[11] !== header.crc >>> 24) {
121+
// if bit 3 (0x08) of the general-purpose flags field is set, check salt[11] with the high byte of the header time
122+
// 2 byte data block (as per Info-Zip spec), otherwise check with the high byte of the header entry
123+
const verifyByte = ((header.flags & 0x8) === 0x8) ? header.timeHighByte : header.crc >>> 24;
124+
125+
//3. does password meet expectations
126+
if (salt[11] !== verifyByte) {
123127
throw "ADM-ZIP: Wrong Password";
124128
}
125129

331 Bytes
Binary file not shown.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"use strict";
2+
3+
// Tests for github issue 471: https://github.com/cthackers/adm-zip/issues/471
4+
5+
const assert = require("assert");
6+
const path = require("path");
7+
const Zip = require("../../adm-zip");
8+
9+
describe("decryption with info-zip spec password check", () => {
10+
11+
12+
// test decryption with both password types
13+
it("test decrypted data with password", () => {
14+
// the issue-471-infozip-encrypted.zip file has been generated with Info-Zip Zip 2.32, but the Info-Zip
15+
// standard is used by other zip generators as well.
16+
const infoZip = new Zip(path.join(__dirname, "../assets/issue-471-infozip-encrypted.zip"));
17+
const entries = infoZip.getEntries();
18+
assert(entries.length === 1, "Good: Test archive contains exactly 1 file");
19+
20+
const testFile = entries.filter(function (entry) {
21+
return entry.entryName === "dummy.txt";
22+
});
23+
assert(testFile.length === 1, "Good: dummy.txt file exists as archive entry");
24+
25+
const readData = entries[0].getData('secret');
26+
assert(readData.toString('utf8').startsWith('How much wood could a woodchuck chuck'), "Good: buffer matches expectations");
27+
28+
// assert that the following call throws an exception
29+
assert.throws(() => {
30+
const readDataBad = entries[0].getData('badpassword');
31+
}, "Good: error thrown for bad password");
32+
33+
});
34+
});
35+

test/methods/zipcrypto.test.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ describe("method - zipcrypto decrypt", () => {
1515
md5: "wYHjota6dQNazueWO9/uDg==",
1616
pwdok: "secret",
1717
pwdbad: "Secret",
18+
flagsencrypted: 0x01,
19+
flagsinfozipencrypted: 0x09,
20+
timeHighByte: 0xD8,
1821
// result
1922
result: Buffer.from("test", "ascii")
2023
};
@@ -40,22 +43,33 @@ describe("method - zipcrypto decrypt", () => {
4043
// is error thrown if invalid password was provided
4144
it("should throw if invalid password is provided", () => {
4245
expect(function badpassword() {
43-
decrypt(source.data, { crc: source.crc }, source.pwdbad);
46+
decrypt(source.data, { crc: source.crc, flags: source.flagsencrypted }, source.pwdbad);
4447
}).to.throw();
4548

4649
expect(function okpassword() {
47-
decrypt(source.data, { crc: source.crc }, source.pwdok);
50+
decrypt(source.data, { crc: source.crc, flags: source.flagsencrypted }, source.pwdok);
51+
}).to.not.throw();
52+
});
53+
54+
// is error thrown if invalid password was provided
55+
it("should throw if invalid password is provided for Info-Zip bit 3 flag", () => {
56+
expect(function badpassword() {
57+
decrypt(source.data, { crc: source.crc, flags: source.flagsinfozipencrypted, timeHighByte: source.timeHighByte }, source.pwdbad);
58+
}).to.throw();
59+
60+
expect(function okpassword() {
61+
decrypt(source.data, { crc: source.crc, flags: source.flagsinfozipencrypted, timeHighByte: source.timeHighByte }, source.pwdok);
4862
}).to.not.throw();
4963
});
5064

5165
// test decryption with both password types
5266
it("test decrypted data with password", () => {
5367
// test password, string
54-
const result1 = decrypt(source.data, { crc: source.crc }, source.pwdok);
68+
const result1 = decrypt(source.data, { crc: source.crc, flags: source.flagsencrypted }, source.pwdok);
5569
expect(result1.compare(source.result)).to.equal(0);
5670

5771
// test password, buffer
58-
const result2 = decrypt(source.data, { crc: source.crc }, Buffer.from(source.pwdok, "ascii"));
72+
const result2 = decrypt(source.data, { crc: source.crc, flags: source.flagsencrypted }, Buffer.from(source.pwdok, "ascii"));
5973
expect(result2.compare(source.result)).to.equal(0);
6074
});
6175
});

0 commit comments

Comments
 (0)