This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-compress.git


The following commit(s) were added to refs/heads/master by this push:
     new 9d6d7c5ad Fix copyOfRange end index in x0017 strong encryption header 
(#786)
9d6d7c5ad is described below

commit 9d6d7c5ada1ef8f1e8f5f9964868013189facadf
Author: KALI 834X <[email protected]>
AuthorDate: Wed Jul 15 23:47:54 2026 +0530

    Fix copyOfRange end index in x0017 strong encryption header (#786)
---
 .../zip/X0017_StrongEncryptionHeader.java          | 20 ++++-----
 .../zip/X0017_StrongEncryptionHeaderTest.java      | 49 ++++++++++++++++++++++
 2 files changed, 57 insertions(+), 12 deletions(-)

diff --git 
a/src/main/java/org/apache/commons/compress/archivers/zip/X0017_StrongEncryptionHeader.java
 
b/src/main/java/org/apache/commons/compress/archivers/zip/X0017_StrongEncryptionHeader.java
index b0f20ae25..d934f74cb 100644
--- 
a/src/main/java/org/apache/commons/compress/archivers/zip/X0017_StrongEncryptionHeader.java
+++ 
b/src/main/java/org/apache/commons/compress/archivers/zip/X0017_StrongEncryptionHeader.java
@@ -364,9 +364,8 @@ public void parseFileFormat(final byte[] data, final int 
offset, final int lengt
         assertMinimalLength(4, length);
         final int ivSize = ZipShort.getValue(data, offset);
         assertDynamicLengthFits("ivSize", ivSize, 4, length);
-        assertMinimalLength(offset + 4, ivSize);
         // TODO: what is at offset + 2?
-        this.ivData = Arrays.copyOfRange(data, offset + 4, ivSize);
+        this.ivData = Arrays.copyOfRange(data, offset + 4, offset + 4 + 
ivSize);
 
         assertMinimalLength(16 + ivSize, length); // up to and including 
erdSize
         // TODO: what is at offset + 4 + ivSize?
@@ -377,8 +376,7 @@ public void parseFileFormat(final byte[] data, final int 
offset, final int lengt
 
         final int erdSize = ZipShort.getValue(data, offset + ivSize + 14);
         assertDynamicLengthFits("erdSize", erdSize, ivSize + 16, length);
-        assertMinimalLength(offset + ivSize + 16, erdSize);
-        this.erdData = Arrays.copyOfRange(data, offset + ivSize + 16, erdSize);
+        this.erdData = Arrays.copyOfRange(data, offset + ivSize + 16, offset + 
ivSize + 16 + erdSize);
 
         assertMinimalLength(16 + 4 + ivSize + erdSize, length);
         this.rcount = ZipLong.getValue(data, offset + ivSize + 16 + erdSize);
@@ -389,10 +387,8 @@ public void parseFileFormat(final byte[] data, final int 
offset, final int lengt
             if (vSize < 4) {
                 throw new ZipException("Invalid X0017_StrongEncryptionHeader: 
vSize " + vSize + " is too small to hold CRC");
             }
-            assertMinimalLength(offset + ivSize + 22 + erdSize, vSize - 4);
-            this.vData = Arrays.copyOfRange(data, offset + ivSize + 22 + 
erdSize, vSize - 4);
-            assertMinimalLength(offset + ivSize + 22 + erdSize + vSize - 4, 4);
-            this.vCRC32 = Arrays.copyOfRange(data, offset + ivSize + 22 + 
erdSize + vSize - 4, 4);
+            this.vData = Arrays.copyOfRange(data, offset + ivSize + 22 + 
erdSize, offset + ivSize + 22 + erdSize + vSize - 4);
+            this.vCRC32 = Arrays.copyOfRange(data, offset + ivSize + 22 + 
erdSize + vSize - 4, offset + ivSize + 22 + erdSize + vSize);
         } else {
             assertMinimalLength(ivSize + 20 + erdSize + 6, length); // up to 
and including resize
             this.hashAlg = 
HashAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + ivSize + 20 + 
erdSize));
@@ -405,8 +401,8 @@ public void parseFileFormat(final byte[] data, final int 
offset, final int lengt
             // TODO: this looks suspicious, 26 rather than 24 would be "after" 
resize
             assertDynamicLengthFits("resize", resize, ivSize + 24 + erdSize, 
length);
             //
-            this.recipientKeyHash = Arrays.copyOfRange(data, offset + ivSize + 
24 + erdSize, this.hashSize);
-            this.keyBlob = Arrays.copyOfRange(data, offset + ivSize + 24 + 
erdSize + this.hashSize, resize - this.hashSize);
+            this.recipientKeyHash = Arrays.copyOfRange(data, offset + ivSize + 
24 + erdSize, offset + ivSize + 24 + erdSize + this.hashSize);
+            this.keyBlob = Arrays.copyOfRange(data, offset + ivSize + 24 + 
erdSize + this.hashSize, offset + ivSize + 24 + erdSize + resize);
 
             assertMinimalLength(ivSize + 26 + erdSize + resize + 2, length);
             final int vSize = ZipShort.getValue(data, offset + ivSize + 26 + 
erdSize + resize);
@@ -416,8 +412,8 @@ public void parseFileFormat(final byte[] data, final int 
offset, final int lengt
             // TODO: these offsets look even more suspicious, the constant 
should likely be 28 rather than 22
             assertDynamicLengthFits("vSize", vSize, ivSize + 22 + erdSize + 
resize, length);
             //
-            this.vData = Arrays.copyOfRange(data, offset + ivSize + 22 + 
erdSize + resize, vSize - 4);
-            this.vCRC32 = Arrays.copyOfRange(data, offset + ivSize + 22 + 
erdSize + resize + vSize - 4, 4);
+            this.vData = Arrays.copyOfRange(data, offset + ivSize + 22 + 
erdSize + resize, offset + ivSize + 22 + erdSize + resize + vSize - 4);
+            this.vCRC32 = Arrays.copyOfRange(data, offset + ivSize + 22 + 
erdSize + resize + vSize - 4, offset + ivSize + 22 + erdSize + resize + vSize);
         }
 
         // validate values?
diff --git 
a/src/test/java/org/apache/commons/compress/archivers/zip/X0017_StrongEncryptionHeaderTest.java
 
b/src/test/java/org/apache/commons/compress/archivers/zip/X0017_StrongEncryptionHeaderTest.java
new file mode 100644
index 000000000..6eaf707db
--- /dev/null
+++ 
b/src/test/java/org/apache/commons/compress/archivers/zip/X0017_StrongEncryptionHeaderTest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.commons.compress.archivers.zip;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.zip.ZipException;
+
+import org.junit.jupiter.api.Test;
+
+class X0017_StrongEncryptionHeaderTest {
+
+    /**
+     * A crafted 0x0017 local extra field whose {@code hashSize} is smaller 
than the offset of the recipient key hash used to make
+     * {@link java.util.Arrays#copyOfRange} throw {@link 
IllegalArgumentException} ({@code fromIndex > toIndex}). That runtime exception 
is not caught by
+     * {@link ExtraFieldUtils#fillExtraField} (which only maps {@link 
ArrayIndexOutOfBoundsException}), so it used to escape parsing uncaught. It 
must now be
+     * reported as a {@link ZipException} like every other corrupt extra field.
+     */
+    @Test
+    void testParseFileFormatRejectsCorruptRecipientKeyHash() {
+        // [id 0x0017][len 
66][ivSize=8]...[erdSize=28]...[rcount=1]...[hashSize=0][resize=0][vSize=3]
+        final byte[] data = new byte[70];
+        data[0] = 0x17;
+        data[1] = 0x00;
+        data[2] = 66; // field length
+        data[4] = 8; // ivSize
+        data[26] = 28; // erdSize
+        data[56] = 1; // rcount
+        // hashSize @ 62..64 and resize @ 64..66 stay 0
+        data[66] = 3; // vSize, < 4 so parsing stops with a ZipException once 
the offsets are read correctly
+        assertThrows(ZipException.class, () -> ExtraFieldUtils.parse(data, 
true));
+    }
+}

Reply via email to