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 < 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 >= 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 }