    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}