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