Skip to content

Commit

Permalink
Merge pull request #472 from 10bitFX/info-zip-password
Browse files Browse the repository at this point in the history
Add support for Info-Zip password check spec for ZipCrypto.
  • Loading branch information
cthackers authored Mar 13, 2024
2 parents 10242c3 + 1bfa3bb commit 54c3817
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 7 deletions.
4 changes: 3 additions & 1 deletion headers/entryHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ module.exports = function () {
set time(val) {
setTime(val);
},

get timeHighByte() {
return (_time >>> 8) & 0xff;
},
get crc() {
return _crc;
},
Expand Down
8 changes: 6 additions & 2 deletions methods/zipcrypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,12 @@ function decrypt(/*Buffer*/ data, /*Object*/ header, /*String, Buffer*/ pwd) {
// 2. decrypt salt what is always 12 bytes and is a part of file content
const salt = decrypter(data.slice(0, 12));

// 3. does password meet expectations
if (salt[11] !== header.crc >>> 24) {
// if bit 3 (0x08) of the general-purpose flags field is set, check salt[11] with the high byte of the header time
// 2 byte data block (as per Info-Zip spec), otherwise check with the high byte of the header entry
const verifyByte = ((header.flags & 0x8) === 0x8) ? header.timeHighByte : header.crc >>> 24;

//3. does password meet expectations
if (salt[11] !== verifyByte) {
throw "ADM-ZIP: Wrong Password";
}

Expand Down
Binary file added test/assets/issue-471-infozip-encrypted.zip
Binary file not shown.
35 changes: 35 additions & 0 deletions test/issue_471/infozip-password.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"use strict";

// Tests for github issue 471: https://github.com/cthackers/adm-zip/issues/471

const assert = require("assert");
const path = require("path");
const Zip = require("../../adm-zip");

describe("decryption with info-zip spec password check", () => {


// test decryption with both password types
it("test decrypted data with password", () => {
// the issue-471-infozip-encrypted.zip file has been generated with Info-Zip Zip 2.32, but the Info-Zip
// standard is used by other zip generators as well.
const infoZip = new Zip(path.join(__dirname, "../assets/issue-471-infozip-encrypted.zip"));
const entries = infoZip.getEntries();
assert(entries.length === 1, "Good: Test archive contains exactly 1 file");

const testFile = entries.filter(function (entry) {
return entry.entryName === "dummy.txt";
});
assert(testFile.length === 1, "Good: dummy.txt file exists as archive entry");

const readData = entries[0].getData('secret');
assert(readData.toString('utf8').startsWith('How much wood could a woodchuck chuck'), "Good: buffer matches expectations");

// assert that the following call throws an exception
assert.throws(() => {
const readDataBad = entries[0].getData('badpassword');
}, "Good: error thrown for bad password");

});
});

22 changes: 18 additions & 4 deletions test/methods/zipcrypto.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ describe("method - zipcrypto decrypt", () => {
md5: "wYHjota6dQNazueWO9/uDg==",
pwdok: "secret",
pwdbad: "Secret",
flagsencrypted: 0x01,
flagsinfozipencrypted: 0x09,
timeHighByte: 0xD8,
// result
result: Buffer.from("test", "ascii")
};
Expand All @@ -40,22 +43,33 @@ describe("method - zipcrypto decrypt", () => {
// is error thrown if invalid password was provided
it("should throw if invalid password is provided", () => {
expect(function badpassword() {
decrypt(source.data, { crc: source.crc }, source.pwdbad);
decrypt(source.data, { crc: source.crc, flags: source.flagsencrypted }, source.pwdbad);
}).to.throw();

expect(function okpassword() {
decrypt(source.data, { crc: source.crc }, source.pwdok);
decrypt(source.data, { crc: source.crc, flags: source.flagsencrypted }, source.pwdok);
}).to.not.throw();
});

// is error thrown if invalid password was provided
it("should throw if invalid password is provided for Info-Zip bit 3 flag", () => {
expect(function badpassword() {
decrypt(source.data, { crc: source.crc, flags: source.flagsinfozipencrypted, timeHighByte: source.timeHighByte }, source.pwdbad);
}).to.throw();

expect(function okpassword() {
decrypt(source.data, { crc: source.crc, flags: source.flagsinfozipencrypted, timeHighByte: source.timeHighByte }, source.pwdok);
}).to.not.throw();
});

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

// test password, buffer
const result2 = decrypt(source.data, { crc: source.crc }, Buffer.from(source.pwdok, "ascii"));
const result2 = decrypt(source.data, { crc: source.crc, flags: source.flagsencrypted }, Buffer.from(source.pwdok, "ascii"));
expect(result2.compare(source.result)).to.equal(0);
});
});
Expand Down

0 comments on commit 54c3817

Please sign in to comment.