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