Skip to content

Commit

Permalink
Add support for Info-Zip password check spec for ZipCrypto. (Uses hig…
Browse files Browse the repository at this point in the history
…h byte of header modified time, rather than crc). Updates current tests to handle.
  • Loading branch information
lukemalcolm committed Mar 5, 2024
1 parent 10242c3 commit e1cddb3
Show file tree
Hide file tree
Showing 3 changed files with 27 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
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 e1cddb3

Please sign in to comment.