    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}