001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.compress.archivers.zip;
020
021import java.util.Arrays;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.Map;
025import java.util.zip.ZipException;
026
027/**
028 * Base class for all PKWare strong crypto extra headers.
029 *
030 * <p>
031 * 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
032 * strong encryption.
033 * </p>
034 *
035 * <strong>Algorithm IDs</strong> - integer identifier of the encryption algorithm from the following range
036 *
037 * <ul>
038 * <li>0x6601 - DES</li>
039 * <li>0x6602 - RC2 (version needed to extract &lt; 5.2)</li>
040 * <li>0x6603 - 3DES 168</li>
041 * <li>0x6609 - 3DES 112</li>
042 * <li>0x660E - AES 128</li>
043 * <li>0x660F - AES 192</li>
044 * <li>0x6610 - AES 256</li>
045 * <li>0x6702 - RC2 (version needed to extract &gt;= 5.2)</li>
046 * <li>0x6720 - Blowfish</li>
047 * <li>0x6721 - Twofish</li>
048 * <li>0x6801 - RC4</li>
049 * <li>0xFFFF - Unknown algorithm</li>
050 * </ul>
051 *
052 * <strong>Hash Algorithms</strong> - integer identifier of the hash algorithm from the following range
053 *
054 * <ul>
055 * <li>0x0000 - none</li>
056 * <li>0x0001 - CRC32</li>
057 * <li>0x8003 - MD5</li>
058 * <li>0x8004 - SHA1</li>
059 * <li>0x8007 - RIPEMD160</li>
060 * <li>0x800C - SHA256</li>
061 * <li>0x800D - SHA384</li>
062 * <li>0x800E - SHA512</li>
063 * </ul>
064 *
065 * @since 1.11
066 */
067public abstract class PKWareExtraHeader implements ZipExtraField {
068
069    /**
070     * Enumerates encryption algorithm.
071     *
072     * @since 1.11
073     */
074    public enum EncryptionAlgorithm {
075
076        /**
077         * DES with code 0x6601.
078         */
079        DES(0x6601),
080
081        /**
082         * RC2pre52 with code 0x6602.
083         */
084        RC2pre52(0x6602),
085
086        /**
087         * TripleDES168 with code 0x6603.
088         */
089        TripleDES168(0x6603),
090
091        /**
092         * TripleDES192 with code 0x6609.
093         */
094        TripleDES192(0x6609),
095
096        /**
097         * AES128 with code 0x660E.
098         */
099        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}