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