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