View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.compress.archivers.zip;
20  
21  import java.util.Arrays;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.Map;
25  import java.util.zip.ZipException;
26  
27  /**
28   * Base class for all PKWare strong crypto extra headers.
29   *
30   * <p>
31   * This base class acts as a marker so you know you can ignore all extra fields that extend this class if you are not interested in the meta data of PKWare
32   * strong encryption.
33   * </p>
34   *
35   * <strong>Algorithm IDs</strong> - integer identifier of the encryption algorithm from the following range
36   *
37   * <ul>
38   * <li>0x6601 - DES</li>
39   * <li>0x6602 - RC2 (version needed to extract &lt; 5.2)</li>
40   * <li>0x6603 - 3DES 168</li>
41   * <li>0x6609 - 3DES 112</li>
42   * <li>0x660E - AES 128</li>
43   * <li>0x660F - AES 192</li>
44   * <li>0x6610 - AES 256</li>
45   * <li>0x6702 - RC2 (version needed to extract &gt;= 5.2)</li>
46   * <li>0x6720 - Blowfish</li>
47   * <li>0x6721 - Twofish</li>
48   * <li>0x6801 - RC4</li>
49   * <li>0xFFFF - Unknown algorithm</li>
50   * </ul>
51   *
52   * <strong>Hash Algorithms</strong> - integer identifier of the hash algorithm from the following range
53   *
54   * <ul>
55   * <li>0x0000 - none</li>
56   * <li>0x0001 - CRC32</li>
57   * <li>0x8003 - MD5</li>
58   * <li>0x8004 - SHA1</li>
59   * <li>0x8007 - RIPEMD160</li>
60   * <li>0x800C - SHA256</li>
61   * <li>0x800D - SHA384</li>
62   * <li>0x800E - SHA512</li>
63   * </ul>
64   *
65   * @since 1.11
66   */
67  public abstract class PKWareExtraHeader implements ZipExtraField {
68  
69      /**
70       * Enumerates encryption algorithm.
71       *
72       * @since 1.11
73       */
74      public enum EncryptionAlgorithm {
75  
76          /**
77           * DES with code 0x6601.
78           */
79          DES(0x6601),
80  
81          /**
82           * RC2pre52 with code 0x6602.
83           */
84          RC2pre52(0x6602),
85  
86          /**
87           * TripleDES168 with code 0x6603.
88           */
89          TripleDES168(0x6603),
90  
91          /**
92           * TripleDES192 with code 0x6609.
93           */
94          TripleDES192(0x6609),
95  
96          /**
97           * AES128 with code 0x660E.
98           */
99          AES128(0x660E),
100 
101         /**
102          * AES192 with code 0x660F.
103          */
104         AES192(0x660F),
105 
106         /**
107          * AES256 with code 0x6610.
108          */
109         AES256(0x6610),
110 
111         /**
112          * RC2 with code 0x6702.
113          */
114         RC2(0x6702),
115 
116         /**
117          * RC4 with code 0x6801.
118          */
119         RC4(0x6801),
120 
121         /**
122          * UNKNOWN with code 0xFFFF.
123          */
124         UNKNOWN(0xFFFF);
125 
126         private static final Map<Integer, EncryptionAlgorithm> codeToEnum;
127 
128         static {
129             final Map<Integer, EncryptionAlgorithm> cte = new HashMap<>();
130             for (final EncryptionAlgorithm method : values()) {
131                 cte.put(method.getCode(), method);
132             }
133             codeToEnum = Collections.unmodifiableMap(cte);
134         }
135 
136         /**
137          * Returns the EncryptionAlgorithm for the given code or null if the method is not known.
138          *
139          * @param code the code of the algorithm
140          * @return the EncryptionAlgorithm for the given code or null if the method is not known
141          */
142         public static EncryptionAlgorithm getAlgorithmByCode(final int code) {
143             return codeToEnum.get(code);
144         }
145 
146         private final int code;
147 
148         /**
149          * Constructs a new instance.
150          */
151         EncryptionAlgorithm(final int code) {
152             this.code = code;
153         }
154 
155         /**
156          * Gets the algorithm ID.
157          *
158          * @return the PKWare AlgorithmId
159          */
160         public int getCode() {
161             return code;
162         }
163     }
164 
165     /**
166      * Enumerates hash Algorithm
167      *
168      * @since 1.11
169      */
170     public enum HashAlgorithm {
171 
172         /**
173          * NONE with code 0.
174          */
175         NONE(0),
176 
177         /**
178          * CRC32 with code 1.
179          */
180         CRC32(1),
181 
182         /**
183          * MD5 with code 0x8003.
184          */
185         MD5(0x8003),
186 
187         /**
188          * SHA1 with code 0x8004.
189          */
190         SHA1(0x8004),
191 
192         /**
193          * RIPEND160 with code 0x8007.
194          */
195         RIPEND160(0x8007),
196 
197         /**
198          * SHA256 with code 0x800C.
199          */
200         SHA256(0x800C),
201 
202         /**
203          * SHA384 with code 0x800D.
204          */
205         SHA384(0x800D),
206 
207         /**
208          * SHA512 with code 0x800E.
209          */
210         SHA512(0x800E);
211 
212         private static final Map<Integer, HashAlgorithm> codeToEnum;
213 
214         static {
215             final Map<Integer, HashAlgorithm> cte = new HashMap<>();
216             for (final HashAlgorithm method : values()) {
217                 cte.put(method.getCode(), method);
218             }
219             codeToEnum = Collections.unmodifiableMap(cte);
220         }
221 
222         /**
223          * Returns the HashAlgorithm for the given code or null if the method is not known.
224          *
225          * @param code the code of the algorithm
226          * @return the HashAlgorithm for the given code or null if the method is not known
227          */
228         public static HashAlgorithm getAlgorithmByCode(final int code) {
229             return codeToEnum.get(code);
230         }
231 
232         private final int code;
233 
234         /**
235          * Constructs a new instance.
236          */
237         HashAlgorithm(final int code) {
238             this.code = code;
239         }
240 
241         /**
242          * Gets the hash algorithm ID.
243          *
244          * @return the PKWare hashAlg
245          */
246         public int getCode() {
247             return code;
248         }
249     }
250 
251     private final ZipShort headerId;
252 
253     /**
254      * Extra field data in local file data - without Header-ID or length specifier.
255      */
256     private byte[] localData;
257 
258     /**
259      * Extra field data in central directory - without Header-ID or length specifier.
260      */
261     private byte[] centralData;
262 
263     /**
264      * Constructs a new instance.
265      *
266      * @param headerId The header ID.
267      */
268     protected PKWareExtraHeader(final ZipShort headerId) {
269         this.headerId = headerId;
270     }
271 
272     /**
273      * Asserts the given length is greater or equal to the given minimum.
274      *
275      * @param minimum the minimum.
276      * @param length the length.
277      * @throws ZipException Thrown if the length is less than the minimum.
278      */
279     protected final void assertMinimalLength(final int minimum, final int length) throws ZipException {
280         if (length < minimum) {
281             throw new ZipException(getClass().getName() + " is too short, only " + length + " bytes, expected at least " + minimum);
282         }
283     }
284 
285     /**
286      * Gets the central data.
287      *
288      * @return the central data if present, else return the local file data
289      */
290     @Override
291     public byte[] getCentralDirectoryData() {
292         if (centralData != null) {
293             return ZipUtil.copy(centralData);
294         }
295         return getLocalFileDataData();
296     }
297 
298     /**
299      * Gets the central data length. If there is no central data, get the local file data length.
300      *
301      * @return the central data length
302      */
303     @Override
304     public ZipShort getCentralDirectoryLength() {
305         if (centralData != null) {
306             return new ZipShort(centralData.length);
307         }
308         return getLocalFileDataLength();
309     }
310 
311     /**
312      * Gets the header id.
313      *
314      * @return the header id
315      */
316     @Override
317     public ZipShort getHeaderId() {
318         return headerId;
319     }
320 
321     /**
322      * Gets the local data.
323      *
324      * @return the local data
325      */
326     @Override
327     public byte[] getLocalFileDataData() {
328         return ZipUtil.copy(localData);
329     }
330 
331     /**
332      * Gets the length of the local data.
333      *
334      * @return the length of the local data.
335      */
336     @Override
337     public ZipShort getLocalFileDataLength() {
338         return ZipShort.lengthOf(localData);
339     }
340 
341     /**
342      * @param data   the array of bytes.
343      * @param offset the source location in the data array.
344      * @param length the number of bytes to use in the data array.
345      * @see ZipExtraField#parseFromCentralDirectoryData(byte[], int, int)
346      */
347     @Override
348     public void parseFromCentralDirectoryData(final byte[] data, final int offset, final int length) throws ZipException {
349         final byte[] tmp = Arrays.copyOfRange(data, offset, offset + length);
350         setCentralDirectoryData(tmp);
351         if (localData == null) {
352             setLocalFileDataData(tmp);
353         }
354     }
355 
356     /**
357      * @param data   the array of bytes.
358      * @param offset the source location in the data array.
359      * @param length the number of bytes to use in the data array.
360      * @see ZipExtraField#parseFromLocalFileData(byte[], int, int)
361      */
362     @Override
363     public void parseFromLocalFileData(final byte[] data, final int offset, final int length) throws ZipException {
364         setLocalFileDataData(Arrays.copyOfRange(data, offset, offset + length));
365     }
366 
367     /**
368      * Sets the extra field data in central directory.
369      *
370      * @param data the data to use
371      */
372     public void setCentralDirectoryData(final byte[] data) {
373         centralData = ZipUtil.copy(data);
374     }
375 
376     /**
377      * Sets the extra field data in the local file data - without Header-ID or length specifier.
378      *
379      * @param data the field data to use
380      */
381     public void setLocalFileDataData(final byte[] data) {
382         localData = ZipUtil.copy(data);
383     }
384 }