[Git][java-team/zip4j][upstream] New upstream version 2.11.3
Andrius Merkys (@merkys)
gitlab at salsa.debian.org
Mon Jan 30 11:52:31 GMT 2023
Andrius Merkys pushed to branch upstream at Debian Java Maintainers / zip4j
Commits:
47338ff9 by Andrius Merkys at 2023-01-30T02:39:21-05:00
New upstream version 2.11.3
- - - - -
13 changed files:
- README.md
- pom.xml
- src/main/java/net/lingala/zip4j/crypto/AESDecrypter.java
- src/main/java/net/lingala/zip4j/crypto/PBKDF2/MacBasedPRF.java
- src/main/java/net/lingala/zip4j/io/inputstream/AesCipherInputStream.java
- src/main/java/net/lingala/zip4j/io/inputstream/CipherInputStream.java
- src/main/java/net/lingala/zip4j/io/inputstream/DecompressedInputStream.java
- src/main/java/net/lingala/zip4j/io/inputstream/InflaterInputStream.java
- src/main/java/net/lingala/zip4j/io/inputstream/ZipInputStream.java
- src/test/java/net/lingala/zip4j/MiscZipFileIT.java
- src/test/java/net/lingala/zip4j/io/inputstream/ZipInputStreamIT.java
- src/test/java/net/lingala/zip4j/io/outputstream/ZipOutputStreamIT.java
- + src/test/resources/test-archives/aes_with_extra_data_record_and_corrupt_mac.zip
Changes:
=====================================
README.md
=====================================
@@ -71,7 +71,7 @@ Zip4j supports JDK 7 as well. In cases where the feature/class from JDK 8 is mis
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
- <version>2.11.2</version>
+ <version>2.11.3</version>
</dependency>
```
=====================================
pom.xml
=====================================
@@ -6,7 +6,7 @@
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
- <version>2.11.2</version>
+ <version>2.11.4-SNAPSHOT</version>
<name>Zip4j</name>
<description>Zip4j - A Java library for zip files and streams</description>
=====================================
src/main/java/net/lingala/zip4j/crypto/AESDecrypter.java
=====================================
@@ -86,7 +86,7 @@ public class AESDecrypter implements Decrypter {
return len;
}
- public byte[] getCalculatedAuthenticationBytes() {
- return mac.doFinal();
+ public byte[] getCalculatedAuthenticationBytes(int numberOfBytesPushedBack) {
+ return mac.doFinal(numberOfBytesPushedBack);
}
}
=====================================
src/main/java/net/lingala/zip4j/crypto/PBKDF2/MacBasedPRF.java
=====================================
@@ -16,11 +16,16 @@
package net.lingala.zip4j.crypto.PBKDF2;
+import net.lingala.zip4j.util.InternalZipConstants;
+
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
+import static net.lingala.zip4j.util.InternalZipConstants.AES_BLOCK_SIZE;
+
/*
* Source referred from Matthias Gartner's PKCS#5 implementation -
* see http://rtner.de/software/PBKDF2.html
@@ -30,9 +35,11 @@ public class MacBasedPRF implements PRF {
private Mac mac;
private int hLen;
private String macAlgorithm;
+ private ByteArrayOutputStream macCache;
public MacBasedPRF(String macAlgorithm) {
this.macAlgorithm = macAlgorithm;
+ this.macCache = new ByteArrayOutputStream(InternalZipConstants.BUFF_SIZE);
try {
mac = Mac.getInstance(macAlgorithm);
hLen = mac.getMacLength();
@@ -42,10 +49,20 @@ public class MacBasedPRF implements PRF {
}
public byte[] doFinal(byte[] M) {
+ if (macCache.size() > 0) {
+ doMacUpdate(0);
+ }
return mac.doFinal(M);
}
public byte[] doFinal() {
+ return doFinal(0);
+ }
+
+ public byte[] doFinal(int numberOfBytesToPushbackForMac) {
+ if (macCache.size() > 0) {
+ doMacUpdate(numberOfBytesToPushbackForMac);
+ }
return mac.doFinal();
}
@@ -61,19 +78,29 @@ public class MacBasedPRF implements PRF {
}
}
- public void update(byte[] U) {
+ public void update(byte[] u) {
+ update(u, 0, u.length);
+ }
+
+ public void update(byte[] u, int start, int len) {
try {
- mac.update(U);
+ if (macCache.size() + len > InternalZipConstants.BUFF_SIZE) {
+ doMacUpdate(0);
+ }
+ macCache.write(u, start, len);
} catch (IllegalStateException e) {
throw new RuntimeException(e);
}
}
- public void update(byte[] U, int start, int len) {
- try {
- mac.update(U, start, len);
- } catch (IllegalStateException e) {
- throw new RuntimeException(e);
+ private void doMacUpdate(int numberOfBytesToPushBack) {
+ byte[] macBytes = macCache.toByteArray();
+ int numberOfBytesToRead = macBytes.length - numberOfBytesToPushBack;
+ int updateLength;
+ for (int i = 0; i < numberOfBytesToRead; i += InternalZipConstants.AES_BLOCK_SIZE) {
+ updateLength = (i + AES_BLOCK_SIZE) <= numberOfBytesToRead ? AES_BLOCK_SIZE : numberOfBytesToRead - i;
+ mac.update(macBytes, i, updateLength);
}
+ macCache.reset();
}
}
=====================================
src/main/java/net/lingala/zip4j/io/inputstream/AesCipherInputStream.java
=====================================
@@ -4,9 +4,7 @@ import net.lingala.zip4j.crypto.AESDecrypter;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.AESExtraDataRecord;
import net.lingala.zip4j.model.LocalFileHeader;
-import net.lingala.zip4j.model.enums.CompressionMethod;
import net.lingala.zip4j.util.InternalZipConstants;
-import net.lingala.zip4j.util.Zip4jUtil;
import java.io.IOException;
import java.io.InputStream;
@@ -119,22 +117,12 @@ class AesCipherInputStream extends CipherInputStream<AESDecrypter> {
}
@Override
- protected void endOfEntryReached(InputStream inputStream) throws IOException {
- verifyContent(readStoredMac(inputStream));
+ protected void endOfEntryReached(InputStream inputStream, int numberOfBytesPushedBack) throws IOException {
+ verifyContent(readStoredMac(inputStream), numberOfBytesPushedBack);
}
- private void verifyContent(byte[] storedMac) throws IOException {
- if (getLocalFileHeader().isDataDescriptorExists()
- && CompressionMethod.DEFLATE.equals(Zip4jUtil.getCompressionMethod(getLocalFileHeader()))) {
- // Skip content verification in case of Deflate compression and if data descriptor exists.
- // In this case, we do not know the exact size of compressed data before hand and it is possible that we read
- // and pass more than required data into inflater, thereby corrupting the aes mac bytes.
- // See usage of PushBackInputStream in the project for how this push back of data is done
- // Unfortunately, in this case we cannot perform a content verification and have to skip
- return;
- }
-
- byte[] calculatedMac = getDecrypter().getCalculatedAuthenticationBytes();
+ private void verifyContent(byte[] storedMac, int numberOfBytesPushedBack) throws IOException {
+ byte[] calculatedMac = getDecrypter().getCalculatedAuthenticationBytes(numberOfBytesPushedBack);
byte[] first10BytesOfCalculatedMac = new byte[AES_AUTH_LENGTH];
System.arraycopy(calculatedMac, 0, first10BytesOfCalculatedMac, 0, InternalZipConstants.AES_AUTH_LENGTH);
=====================================
src/main/java/net/lingala/zip4j/io/inputstream/CipherInputStream.java
=====================================
@@ -80,7 +80,7 @@ abstract class CipherInputStream<T extends Decrypter> extends InputStream {
return decrypter;
}
- protected void endOfEntryReached(InputStream inputStream) throws IOException {
+ protected void endOfEntryReached(InputStream inputStream, int numberOfBytesPushedBack) throws IOException {
// is optional but useful for AES
}
=====================================
src/main/java/net/lingala/zip4j/io/inputstream/DecompressedInputStream.java
=====================================
@@ -39,12 +39,13 @@ abstract class DecompressedInputStream extends InputStream {
cipherInputStream.close();
}
- public void endOfEntryReached(InputStream inputStream) throws IOException {
- cipherInputStream.endOfEntryReached(inputStream);
+ public void endOfEntryReached(InputStream inputStream, int numberOfBytesPushedBack) throws IOException {
+ cipherInputStream.endOfEntryReached(inputStream, numberOfBytesPushedBack);
}
- public void pushBackInputStreamIfNecessary(PushbackInputStream pushbackInputStream) throws IOException {
+ public int pushBackInputStreamIfNecessary(PushbackInputStream pushbackInputStream) throws IOException {
// Do nothing by default
+ return 0;
}
protected byte[] getLastReadRawDataCache() {
=====================================
src/main/java/net/lingala/zip4j/io/inputstream/InflaterInputStream.java
=====================================
@@ -55,21 +55,22 @@ public class InflaterInputStream extends DecompressedInputStream {
}
@Override
- public void endOfEntryReached(InputStream inputStream) throws IOException {
+ public void endOfEntryReached(InputStream inputStream, int numberOfBytesPushedBack) throws IOException {
if (inflater != null) {
inflater.end();
inflater = null;
}
- super.endOfEntryReached(inputStream);
+ super.endOfEntryReached(inputStream, numberOfBytesPushedBack);
}
@Override
- public void pushBackInputStreamIfNecessary(PushbackInputStream pushbackInputStream) throws IOException {
+ public int pushBackInputStreamIfNecessary(PushbackInputStream pushbackInputStream) throws IOException {
int n = inflater.getRemaining();
if (n > 0) {
byte[] rawDataCache = getLastReadRawDataCache();
pushbackInputStream.unread(rawDataCache, len - n, n);
}
+ return n;
}
@Override
=====================================
src/main/java/net/lingala/zip4j/io/inputstream/ZipInputStream.java
=====================================
@@ -231,10 +231,10 @@ public class ZipInputStream extends InputStream {
private void endOfCompressedDataReached() throws IOException {
//With inflater, without knowing the compressed or uncompressed size, we over read necessary data
//In such cases, we have to push back the inputstream to the end of data
- decompressedInputStream.pushBackInputStreamIfNecessary(inputStream);
+ int numberOfBytesPushedBack = decompressedInputStream.pushBackInputStreamIfNecessary(inputStream);
//First signal the end of data for this entry so that ciphers can read any header data if applicable
- decompressedInputStream.endOfEntryReached(inputStream);
+ decompressedInputStream.endOfEntryReached(inputStream, numberOfBytesPushedBack);
readExtendedLocalFileHeaderIfPresent();
verifyCrc();
=====================================
src/test/java/net/lingala/zip4j/MiscZipFileIT.java
=====================================
@@ -673,6 +673,16 @@ public class MiscZipFileIT extends AbstractIT {
verifyLastModifiedFileTime(zipFile, fileToTestWith, expectedLastModifiedTimeInMillis);
}
+ @Test
+ public void testExtractFileWithExtraDataRecordAndCorruptMac() throws ZipException {
+ ZipFile zipFile = new ZipFile(getTestArchiveFromResources("aes_with_extra_data_record_and_corrupt_mac.zip"), PASSWORD);
+
+ expectedException.expect(ZipException.class);
+ expectedException.expectMessage("java.io.IOException: Reached end of data for this entry, but aes verification failed");
+
+ zipFile.extractAll(outputFolder.getPath());
+ }
+
private void testAddAndExtractWithPasswordUtf8Encoding(boolean useUtf8ForPassword) throws IOException {
char[] password = "hun 焰".toCharArray();
ZipFile zipFile = new ZipFile(generatedZipFile, password);
=====================================
src/test/java/net/lingala/zip4j/io/inputstream/ZipInputStreamIT.java
=====================================
@@ -24,10 +24,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
+import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
-import java.security.SecureRandom;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -351,6 +351,15 @@ public class ZipInputStreamIT extends AbstractIT {
}
}
+ @Test
+ public void testExtractZipFileWithExtraDataRecordAndCorruptAesMacFails() throws IOException {
+ expectedException.expect(IOException.class);
+ expectedException.expectMessage("Reached end of data for this entry, but aes verification failed");
+
+ extractZipFileWithInputStreams(TestUtils.getTestArchiveFromResources("aes_with_extra_data_record_and_corrupt_mac.zip"),
+ PASSWORD, InternalZipConstants.BUFF_SIZE, false, 1);
+ }
+
private void extractZipFileWithInputStreams(File zipFile, char[] password) throws IOException {
extractZipFileWithInputStreams(zipFile, password, InternalZipConstants.BUFF_SIZE);
}
=====================================
src/test/java/net/lingala/zip4j/io/outputstream/ZipOutputStreamIT.java
=====================================
@@ -30,6 +30,8 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
@@ -310,6 +312,28 @@ public class ZipOutputStreamIT extends AbstractIT {
}
}
+ @Test
+ public void testZipInputStreamWithDeflateAndAesEncryption() throws IOException {
+ byte[] buffer = new byte[InternalZipConstants.BUFF_SIZE];
+ int readLen;
+ File fileToAdd = getTestFileFromResources("file_PDF_1MB.pdf");
+ try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(generatedZipFile.toPath()), PASSWORD)) {
+ ZipParameters zipParameters = new ZipParameters();
+ zipParameters.setFileNameInZip(fileToAdd.getName());
+ zipParameters.setEncryptFiles(true);
+ zipParameters.setEncryptionMethod(EncryptionMethod.AES);
+ zipOutputStream.putNextEntry(zipParameters);
+ try (InputStream inputStream = Files.newInputStream(fileToAdd.toPath())) {
+ while ((readLen = inputStream.read(buffer)) != -1) {
+ zipOutputStream.write(buffer, 0, readLen);
+ }
+ }
+ }
+
+ verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 1, true);
+ extractZipFileWithInputStream(generatedZipFile);
+ }
+
private void testZipOutputStream(CompressionMethod compressionMethod, boolean encrypt,
EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength,
AesVersion aesVersion)
@@ -513,4 +537,19 @@ public class ZipOutputStreamIT extends AbstractIT {
zipOutputStream.putNextEntry(zipParameters);
zipOutputStream.closeEntry();
}
+
+ private void extractZipFileWithInputStream(File zipFile) throws IOException {
+ byte[] buffer = new byte[InternalZipConstants.BUFF_SIZE];
+ int readLen;
+ LocalFileHeader lfh;
+ try (ZipInputStream zipInputStream = new ZipInputStream(Files.newInputStream(zipFile.toPath()), PASSWORD)) {
+ while ((lfh = zipInputStream.getNextEntry()) != null) {
+ while ((readLen = zipInputStream.read(buffer)) != -1) {
+ try (OutputStream outputStream = Files.newOutputStream(Paths.get(outputFolder.getPath(), lfh.getFileName()))) {
+ outputStream.write(buffer, 0, readLen);
+ }
+ }
+ }
+ }
+ }
}
=====================================
src/test/resources/test-archives/aes_with_extra_data_record_and_corrupt_mac.zip
=====================================
Binary files /dev/null and b/src/test/resources/test-archives/aes_with_extra_data_record_and_corrupt_mac.zip differ
View it on GitLab: https://salsa.debian.org/java-team/zip4j/-/commit/47338ff98a29b74fa3509f2e09c036683142fd73
--
View it on GitLab: https://salsa.debian.org/java-team/zip4j/-/commit/47338ff98a29b74fa3509f2e09c036683142fd73
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20230130/9d4905c9/attachment.htm>
More information about the pkg-java-commits
mailing list