[Git][java-team/zip4j][master] 4 commits: New upstream version 2.11.3
Andrius Merkys (@merkys)
gitlab at salsa.debian.org
Mon Jan 30 11:52:16 GMT 2023
Andrius Merkys pushed to branch master at Debian Java Maintainers / zip4j
Commits:
47338ff9 by Andrius Merkys at 2023-01-30T02:39:21-05:00
New upstream version 2.11.3
- - - - -
f6dc5200 by Andrius Merkys at 2023-01-30T02:39:31-05:00
Update upstream source from tag 'upstream/2.11.3'
Update to upstream version '2.11.3'
with Debian dir 10338ce15b31bdda7bec9e2f4773a9dcf7a880ff
- - - - -
3e6e52c7 by Andrius Merkys at 2023-01-30T02:56:32-05:00
Drop CVE-2023-22899.patch: fix adopted upstream.
- - - - -
d3eba659 by Andrius Merkys at 2023-01-30T02:59:06-05:00
Update changelog for 2.11.3-1 release
- - - - -
16 changed files:
- README.md
- debian/changelog
- − debian/patches/CVE-2023-22899.patch
- − debian/patches/series
- 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>
```
=====================================
debian/changelog
=====================================
@@ -1,3 +1,10 @@
+zip4j (2.11.3-1) unstable; urgency=medium
+
+ * New upstream version 2.11.3
+ * Drop CVE-2023-22899.patch: fix adopted upstream.
+
+ -- Andrius Merkys <merkys at debian.org> Mon, 30 Jan 2023 02:59:02 -0500
+
zip4j (2.11.2-3) unstable; urgency=high
* Team upload.
=====================================
debian/patches/CVE-2023-22899.patch deleted
=====================================
@@ -1,43 +0,0 @@
-Description: Check for MAC even when DataDescritor exists
- Addresses vulnerability CVE-2023-22899
- Zip4j through 2.11.2, as used in Threema and other products, does not
- always check the MAC when decrypting a ZIP archive.
-Source: https://github.com/srikanth-lingala/zip4j/commit/597b31afb473a40e8252de5b5def1876bab198d3.patch
-From: Srikanth Reddy Lingala <srikanth.mailbox at gmail.com>
-Date: Sun, 15 Jan 2023 11:19:55 -0500
-Bug-Vendor: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1029038
-Forwarded: not-needed
-
----
- .../zip4j/io/inputstream/AesCipherInputStream.java | 12 ------------
- 1 file changed, 12 deletions(-)
-
---- a/src/main/java/net/lingala/zip4j/io/inputstream/AesCipherInputStream.java
-+++ b/src/main/java/net/lingala/zip4j/io/inputstream/AesCipherInputStream.java
-@@ -4,9 +4,7 @@
- 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;
-@@ -124,16 +122,6 @@
- }
-
- 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();
- byte[] first10BytesOfCalculatedMac = new byte[AES_AUTH_LENGTH];
- System.arraycopy(calculatedMac, 0, first10BytesOfCalculatedMac, 0, InternalZipConstants.AES_AUTH_LENGTH);
=====================================
debian/patches/series deleted
=====================================
@@ -1 +0,0 @@
-CVE-2023-22899.patch
=====================================
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/-/compare/2b9b7f84c3fcd9d012e1a11637fcf6c563d51baf...d3eba6591630c9af54c0731835072ee2bc1386bf
--
View it on GitLab: https://salsa.debian.org/java-team/zip4j/-/compare/2b9b7f84c3fcd9d012e1a11637fcf6c563d51baf...d3eba6591630c9af54c0731835072ee2bc1386bf
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/40bea833/attachment.htm>
More information about the pkg-java-commits
mailing list