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 * http://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>This base class acts as a marker so you know you can ignore all
031 * extra fields that extend this class if you are not interested in
032 * the meta data of PKWare strong encryption.</p>
033 *
034 * <b>Algorithm IDs</b> - integer identifier of the encryption algorithm from
035 * 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 * <b>Hash Algorithms</b> - integer identifier of the hash algorithm from the
053 * following range
054 *
055 * <ul>
056 * <li>0x0000 - none</li>
057 * <li>0x0001 - CRC32</li>
058 * <li>0x8003 - MD5</li>
059 * <li>0x8004 - SHA1</li>
060 * <li>0x8007 - RIPEMD160</li>
061 * <li>0x800C - SHA256</li>
062 * <li>0x800D - SHA384</li>
063 * <li>0x800E - SHA512</li>
064 * </ul>
065 *
066 * @since 1.11
067 */
068public abstract class PKWareExtraHeader implements ZipExtraField {
069
070    /**
071     * Encryption algorithm.
072     *
073     * @since 1.11
074     */
075    public enum EncryptionAlgorithm {
076        DES(0x6601),
077        RC2pre52(0x6602),
078        TripleDES168(0x6603),
079        TripleDES192(0x6609),
080        AES128(0x660E),
081        AES192(0x660F),
082        AES256(0x6610),
083        RC2(0x6702),
084        RC4(0x6801),
085        UNKNOWN(0xFFFF);
086
087        private static final Map<Integer, EncryptionAlgorithm> codeToEnum;
088
089        static {
090            final Map<Integer, EncryptionAlgorithm> cte = new HashMap<>();
091            for (final EncryptionAlgorithm method : values()) {
092                cte.put(method.getCode(), method);
093            }
094            codeToEnum = Collections.unmodifiableMap(cte);
095        }
096
097        /**
098         * Returns the EncryptionAlgorithm for the given code or null if the
099         * method is not known.
100         * @param code the code of the algorithm
101         * @return the EncryptionAlgorithm for the given code or null
102         * if the method is not known
103         */
104        public static EncryptionAlgorithm getAlgorithmByCode(final int code) {
105            return codeToEnum.get(code);
106        }
107
108        private final int code;
109
110        /**
111         * private constructor for enum style class.
112         */
113        EncryptionAlgorithm(final int code) {
114            this.code = code;
115        }
116
117        /**
118         * the algorithm id.
119         *
120         * @return the PKWare AlgorithmId
121         */
122        public int getCode() {
123            return code;
124        }
125    }
126
127    /**
128     * Hash Algorithm
129     *
130     * @since 1.11
131     */
132    public enum HashAlgorithm {
133        NONE(0),
134        CRC32(1),
135        MD5(0x8003),
136        SHA1(0x8004),
137        RIPEND160(0x8007),
138        SHA256(0x800C),
139        SHA384(0x800D),
140        SHA512(0x800E);
141
142        private static final Map<Integer, HashAlgorithm> codeToEnum;
143
144        static {
145            final Map<Integer, HashAlgorithm> cte = new HashMap<>();
146            for (final HashAlgorithm method : values()) {
147                cte.put(method.getCode(), method);
148            }
149            codeToEnum = Collections.unmodifiableMap(cte);
150        }
151
152        /**
153         * Returns the HashAlgorithm for the given code or null if the method is
154         * not known.
155         * @param code the code of the algorithm
156         * @return the HashAlgorithm for the given code or null
157         * if the method is not known
158         */
159        public static HashAlgorithm getAlgorithmByCode(final int code) {
160            return codeToEnum.get(code);
161        }
162
163        private final int code;
164
165        /**
166         * private constructor for enum style class.
167         */
168        HashAlgorithm(final int code) {
169            this.code = code;
170        }
171
172        /**
173         * the hash algorithm ID.
174         *
175         * @return the PKWare hashAlg
176         */
177        public int getCode() {
178            return code;
179        }
180    }
181    private final ZipShort headerId;
182
183    /**
184     * Extra field data in local file data - without Header-ID or length
185     * specifier.
186     */
187    private byte[] localData;
188
189    /**
190     * Extra field data in central directory - without Header-ID or length
191     * specifier.
192     */
193    private byte[] centralData;
194
195    protected PKWareExtraHeader(final ZipShort headerId) {
196        this.headerId = headerId;
197    }
198
199    protected final void assertMinimalLength(final int minimum, final int length)
200        throws ZipException {
201        if (length < minimum) {
202            throw new ZipException(getClass().getName() + " is too short, only "
203                + length + " bytes, expected at least " + minimum);
204        }
205    }
206
207    /**
208     * Get the central data.
209     *
210     * @return the central data if present, else return the local file data
211     */
212    @Override
213    public byte[] getCentralDirectoryData() {
214        if (centralData != null) {
215            return ZipUtil.copy(centralData);
216        }
217        return getLocalFileDataData();
218    }
219
220    /**
221     * Get the central data length. If there is no central data, get the local
222     * file data length.
223     *
224     * @return the central data length
225     */
226    @Override
227    public ZipShort getCentralDirectoryLength() {
228        if (centralData != null) {
229            return new ZipShort(centralData.length);
230        }
231        return getLocalFileDataLength();
232    }
233
234    /**
235     * Get the header id.
236     *
237     * @return the header id
238     */
239    @Override
240    public ZipShort getHeaderId() {
241        return headerId;
242    }
243
244    /**
245     * Get the local data.
246     *
247     * @return the local data
248     */
249    @Override
250    public byte[] getLocalFileDataData() {
251        return ZipUtil.copy(localData);
252    }
253
254    /**
255     * Get the length of the local data.
256     *
257     * @return the length of the local data
258     */
259    @Override
260    public ZipShort getLocalFileDataLength() {
261        return new ZipShort(localData != null ? localData.length : 0);
262    }
263
264    /**
265     * @param data
266     *            the array of bytes.
267     * @param offset
268     *            the source location in the data array.
269     * @param length
270     *            the number of bytes to use in the data array.
271     * @see ZipExtraField#parseFromCentralDirectoryData(byte[], int, int)
272     */
273    @Override
274    public void parseFromCentralDirectoryData(final byte[] data, final int offset, final int length)
275        throws ZipException {
276        final byte[] tmp = Arrays.copyOfRange(data, offset, offset + length);
277        setCentralDirectoryData(tmp);
278        if (localData == null) {
279            setLocalFileDataData(tmp);
280        }
281    }
282
283    /**
284     * @param data
285     *            the array of bytes.
286     * @param offset
287     *            the source location in the data array.
288     * @param length
289     *            the number of bytes to use in the data array.
290     * @see ZipExtraField#parseFromLocalFileData(byte[], int, int)
291     */
292    @Override
293    public void parseFromLocalFileData(final byte[] data, final int offset, final int length)
294        throws ZipException {
295        setLocalFileDataData(Arrays.copyOfRange(data, offset, offset + length));
296    }
297
298    /**
299     * Set the extra field data in central directory.
300     *
301     * @param data
302     *            the data to use
303     */
304    public void setCentralDirectoryData(final byte[] data) {
305        centralData = ZipUtil.copy(data);
306    }
307
308    /**
309     * Set the extra field data in the local file data - without Header-ID or
310     * length specifier.
311     *
312     * @param data
313     *            the field data to use
314     */
315    public void setLocalFileDataData(final byte[] data) {
316        localData = ZipUtil.copy(data);
317    }
318}