    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.zip.ZipException;
    023
    024/**
    025 * Strong Encryption Header (0x0017).
    026 *
    027 * <p>
    028 * Certificate-based encryption:
    029 * </p>
    030 *
    031 * <pre>
    032 * Value     Size     Description
    033 * -----     ----     -----------
    034 * 0x0017    2 bytes  Tag for this "extra" block type
    035 * TSize     2 bytes  Size of data that follows
    036 * Format    2 bytes  Format definition for this record
    037 * AlgID     2 bytes  Encryption algorithm identifier
    038 * Bitlen    2 bytes  Bit length of encryption key (32-448 bits)
    039 * Flags     2 bytes  Processing flags
    040 * RCount    4 bytes  Number of recipients.
    041 * HashAlg   2 bytes  Hash algorithm identifier
    042 * HSize     2 bytes  Hash size
    043 * SRList    (var)    Simple list of recipients hashed public keys
    044 *
    045 * Flags -   This defines the processing flags.
    046 * </pre>
    047 *
    048 * <ul>
    049 * <li>0x0007 - reserved for future use
    050 * <li>0x000F - reserved for future use
    051 * <li>0x0100 - Indicates non-OAEP key wrapping was used. If this this field is set, the version needed to extract must be at least 61. This means OAEP key
    052 * wrapping is not used when generating a Master Session Key using ErdData.
    053 * <li>0x4000 - ErdData must be decrypted using 3DES-168, otherwise use the same algorithm used for encrypting the file contents.
    054 * <li>0x8000 - reserved for future use
    055 * </ul>
    056 *
    057 * <pre>
    058 * RCount - This defines the number intended recipients whose
    059 *          public keys were used for encryption.  This identifies
    060 *          the number of elements in the SRList.
    061 *
    062 *          see also: reserved1
    063 *
    064 * HashAlg - This defines the hash algorithm used to calculate
    065 *           the public key hash of each public key used
    066 *           for encryption. This field currently supports
    067 *           only the following value for SHA-1
    068 *
    069 *           0x8004 - SHA1
    070 *
    071 * HSize -   This defines the size of a hashed public key.
    072 *
    073 * SRList -  This is a variable length list of the hashed
    074 *           public keys for each intended recipient.  Each
    075 *           element in this list is HSize.  The total size of
    076 *           SRList is determined using RCount * HSize.
    077 * </pre>
    078 *
    079 * <p>
    080 * Password-based Extra Field 0x0017 in central header only.
    081 * </p>
    082 *
    083 * <pre>
    084 * Value     Size     Description
    085 * -----     ----     -----------
    086 * 0x0017    2 bytes  Tag for this "extra" block type
    087 * TSize     2 bytes  Size of data that follows
    088 * Format    2 bytes  Format definition for this record
    089 * AlgID     2 bytes  Encryption algorithm identifier
    090 * Bitlen    2 bytes  Bit length of encryption key (32-448 bits)
    091 * Flags     2 bytes  Processing flags
    092 * (more?)
    093 * </pre>
    094 *
    095 * <p>
    096 * <strong>Format</strong> - the data format identifier for this record. The only value allowed at this time is the integer value 2.
    097 * </p>
    098 *
    099 * <p>
    100 * Password-based Extra Field 0x0017 preceding compressed file data.
    101 * </p>
    102 *
    103 * <pre>
    104 * Value     Size     Description
    105 * -----     ----     -----------
    106 * 0x0017    2 bytes  Tag for this "extra" block type
    107 * IVSize    2 bytes  Size of initialization vector (IV)
    108 * IVData    IVSize   Initialization vector for this file
    109 * Size      4 bytes  Size of remaining decryption header data
    110 * Format    2 bytes  Format definition for this record
    111 * AlgID     2 bytes  Encryption algorithm identifier
    112 * Bitlen    2 bytes  Bit length of encryption key (32-448 bits)
    113 * Flags     2 bytes  Processing flags
    114 * ErdSize   2 bytes  Size of Encrypted Random Data
    115 * ErdData   ErdSize  Encrypted Random Data
    116 * Reserved1 4 bytes  Reserved certificate processing data
    117 * Reserved2 (var)    Reserved for certificate processing data
    118 * VSize     2 bytes  Size of password validation data
    119 * VData     VSize-4  Password validation data
    120 * VCRC32    4 bytes  Standard ZIP CRC32 of password validation data
    121 *
    122 * IVData - The size of the IV should match the algorithm block size.
    123 *          The IVData can be completely random data.  If the size of
    124 *          the randomly generated data does not match the block size
    125 *          it should be complemented with zero's or truncated as
    126 *          necessary.  If IVSize is 0, then IV = CRC32 + Uncompressed
    127 *          File Size (as a 64 bit little-endian, unsigned integer value).
    128 *
    129 * Format -  the data format identifier for this record.  The only
    130 *           value allowed at this time is the integer value 2.
    131 *
    132 * ErdData - Encrypted random data is used to store random data that
    133 *           is used to generate a file session key for encrypting
    134 *           each file.  SHA1 is used to calculate hash data used to
    135 *           derive keys.  File session keys are derived from a master
    136 *           session key generated from the user-supplied password.
    137 *           If the Flags field in the decryption header contains
    138 *           the value 0x4000, then the ErdData field must be
    139 *           decrypted using 3DES. If the value 0x4000 is not set,
    140 *           then the ErdData field must be decrypted using AlgId.
    141 *
    142 * Reserved1 - Reserved for certificate processing, if value is
    143 *           zero, then Reserved2 data is absent.  See the explanation
    144 *           under the Certificate Processing Method for details on
    145 *           this data structure.
    146 *
    147 * Reserved2 - If present, the size of the Reserved2 data structure
    148 *           is located by skipping the first 4 bytes of this field
    149 *           and using the next 2 bytes as the remaining size.  See
    150 *           the explanation under the Certificate Processing Method
    151 *           for details on this data structure.
    152 *
    153 * VSize - This size value will always include the 4 bytes of the
    154 *         VCRC32 data and will be greater than 4 bytes.
    155 *
    156 * VData - Random data for password validation.  This data is VSize
    157 *         in length and VSize must be a multiple of the encryption
    158 *         block size.  VCRC32 is a checksum value of VData.
    159 *         VData and VCRC32 are stored encrypted and start the
    160 *         stream of encrypted data for a file.
    161 * </pre>
    162 *
    163 * <p>
    164 * Reserved1 - Certificate Decryption Header Reserved1 Data:
    165 * </p>
    166 *
    167 * <pre>
    168 * Value     Size     Description
    169 * -----     ----     -----------
    170 * RCount    4 bytes  Number of recipients.
    171 * </pre>
    172 *
    173 * <p>
    174 * RCount - This defines the number intended recipients whose public keys were used for encryption. This defines the number of elements in the REList field
    175 * defined below.
    176 * </p>
    177 *
    178 * <p>
    179 * Reserved2 - Certificate Decryption Header Reserved2 Data Structures:
    180 * </p>
    181 *
    182 * <pre>
    183 * Value     Size     Description
    184 * -----     ----     -----------
    185 * HashAlg   2 bytes  Hash algorithm identifier
    186 * HSize     2 bytes  Hash size
    187 * REList    (var)    List of recipient data elements
    188 *
    189 * HashAlg - This defines the hash algorithm used to calculate
    190 *           the public key hash of each public key used
    191 *           for encryption. This field currently supports
    192 *           only the following value for SHA-1
    193 *
    194 *               0x8004 - SHA1
    195 *
    196 * HSize -   This defines the size of a hashed public key
    197 *           defined in REHData.
    198 *
    199 * REList -  This is a variable length of list of recipient data.
    200 *           Each element in this list consists of a Recipient
    201 *           Element data structure as follows:
    202 * </pre>
    203 *
    204 * <p>
    205 * Recipient Element (REList) Data Structure:
    206 * </p>
    207 *
    208 * <pre>
    209 * Value     Size     Description
    210 * -----     ----     -----------
    211 * RESize    2 bytes  Size of REHData + REKData
    212 * REHData   HSize    Hash of recipients public key
    213 * REKData   (var)    Simple key blob
    214 *
    215 *
    216 * RESize -  This defines the size of an individual REList
    217 *           element.  This value is the combined size of the
    218 *           REHData field + REKData field.  REHData is defined by
    219 *           HSize.  REKData is variable and can be calculated
    220 *           for each REList element using RESize and HSize.
    221 *
    222 * REHData - Hashed public key for this recipient.
    223 *
    224 * REKData - Simple Key Blob.  The format of this data structure
    225 *           is identical to that defined in the Microsoft
    226 *           CryptoAPI and generated using the CryptExportKey()
    227 *           function.  The version of the Simple Key Blob
    228 *           supported at this time is 0x02 as defined by
    229 *           Microsoft.
    230 *
    231 *           For more details see https://msdn.microsoft.com/en-us/library/aa920051.aspx
    232 * </pre>
    233 *
    234 * <p>
    235 * <strong>Flags</strong> - Processing flags needed for decryption
    236 * </p>
    237 *
    238 * <ul>
    239 * <li>0x0001 - Password is required to decrypt</li>
    240 * <li>0x0002 - Certificates only</li>
    241 * <li>0x0003 - Password or certificate required to decrypt</li>
    242 * <li>0x0007 - reserved for future use
    243 * <li>0x000F - reserved for future use
    244 * <li>0x0100 - indicates non-OAEP key wrapping was used. If this field is set the version needed to extract must be at least 61. This means OAEP key wrapping
    245 * is not used when generating a Master Session Key using ErdData.
    246 * <li>0x4000 - ErdData must be decrypted using 3DES-168, otherwise use the same algorithm used for encrypting the file contents.
    247 * <li>0x8000 - reserved for future use.
    248 * </ul>
    249 *
    250 * <p>
    251 * <b>See the section describing the Strong Encryption Specification for details. Refer to the section in this document entitled "Incorporating PKWARE
    252 * Proprietary Technology into Your Product" for more information.</b>
    253 * </p>
    254 *
    255 * @NotThreadSafe
    256 * @since 1.11
    257 */
    258public class X0017_StrongEncryptionHeader extends PKWareExtraHeader {
    259
    260    static final ZipShort HEADER_ID = new ZipShort(0x0017);
    261
    262    private int format; // TODO written but not read
    263
    264    private EncryptionAlgorithm algId;
    265    private int bitlen; // TODO written but not read
    266    private int flags; // TODO written but not read
    267    private long rcount;
    268    private HashAlgorithm hashAlg;
    269    private int hashSize;
    270
    271    /** Encryption data/ */
    272    private byte[] ivData;
    273
    274    private byte[] erdData;
    275
    276    /** Encryption key. */
    277    private byte[] recipientKeyHash;
    278
    279    private byte[] keyBlob;
    280
    281    /** Password verification data. */
    282    private byte[] vData;
    283
    284    private byte[] vCRC32;
    285
    286    /**
    287     * Constructs a new instance.
    288     */
    289    public X0017_StrongEncryptionHeader() {
    290        super(HEADER_ID);
    291    }
    292
    293    private void assertDynamicLengthFits(final String what, final int dynamicLength, final int prefixLength, final int length) throws ZipException {
    294        if (prefixLength + dynamicLength > length) {
    295            throw new ZipException("Invalid X0017_StrongEncryptionHeader: " + what + " " + dynamicLength + " doesn't fit into " + length
    296                    + " bytes of data at position " + prefixLength);
    297        }
    298    }
    299
    300    /**
    301     * Gets encryption algorithm.
    302     *
    303     * @return the encryption algorithm
    304     */
    305    public EncryptionAlgorithm getEncryptionAlgorithm() {
    306        return algId;
    307    }
    308
    309    /**
    310     * Gets hash algorithm.
    311     *
    312     * @return the hash algorithm
    313     */
    314    public HashAlgorithm getHashAlgorithm() {
    315        return hashAlg;
    316    }
    317
    318    /**
    319     * Gets record count.
    320     *
    321     * @return the record count
    322     */
    323    public long getRecordCount() {
    324        return rcount;
    325    }
    326
    327    /**
    328     * Parse central directory format.
    329     *
    330     * @param data   the buffer to read data from
    331     * @param offset offset into buffer to read data
    332     * @param length the length of data
    333     * @throws ZipException if an error occurs
    334     */
    335    public void parseCentralDirectoryFormat(final byte[] data, final int offset, final int length) throws ZipException {
    336        assertMinimalLength(12, length);
    337        // TODO: double check we really do not want to call super here
    338        this.format = ZipShort.getValue(data, offset);
    339        this.algId = EncryptionAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + 2));
    340        this.bitlen = ZipShort.getValue(data, offset + 4);
    341        this.flags = ZipShort.getValue(data, offset + 6);
    342        this.rcount = ZipLong.getValue(data, offset + 8);
    343
    344        if (rcount > 0) {
    345            assertMinimalLength(16, length);
    346            this.hashAlg = HashAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + 12));
    347            this.hashSize = ZipShort.getValue(data, offset + 14);
    348        }
    349    }
    350
    351    /**
    352     * Parse file header format.
    353     *
    354     * <p>
    355     * (Password only?)
    356     * </p>
    357     *
    358     * @param data   the buffer to read data from
    359     * @param offset offset into buffer to read data
    360     * @param length the length of data
    361     * @throws ZipException if an error occurs
    362     */
    363    public void parseFileFormat(final byte[] data, final int offset, final int length) throws ZipException {
    364        assertMinimalLength(4, length);
    365        final int ivSize = ZipShort.getValue(data, offset);
    366        assertDynamicLengthFits("ivSize", ivSize, 4, length);
    367        assertMinimalLength(offset + 4, ivSize);
    368        // TODO: what is at offset + 2?
    369        this.ivData = Arrays.copyOfRange(data, offset + 4, ivSize);
    370
    371        assertMinimalLength(16 + ivSize, length); // up to and including erdSize
    372        // TODO: what is at offset + 4 + ivSize?
    373        this.format = ZipShort.getValue(data, offset + ivSize + 6);
    374        this.algId = EncryptionAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + ivSize + 8));
    375        this.bitlen = ZipShort.getValue(data, offset + ivSize + 10);
    376        this.flags = ZipShort.getValue(data, offset + ivSize + 12);
    377
    378        final int erdSize = ZipShort.getValue(data, offset + ivSize + 14);
    379        assertDynamicLengthFits("erdSize", erdSize, ivSize + 16, length);
    380        assertMinimalLength(offset + ivSize + 16, erdSize);
    381        this.erdData = Arrays.copyOfRange(data, offset + ivSize + 16, erdSize);
    382
    383        assertMinimalLength(16 + 4 + ivSize + erdSize, length);
    384        this.rcount = ZipLong.getValue(data, offset + ivSize + 16 + erdSize);
    385        if (rcount == 0) {
    386            assertMinimalLength(ivSize + 20 + erdSize + 2, length);
    387            final int vSize = ZipShort.getValue(data, offset + ivSize + 20 + erdSize);
    388            assertDynamicLengthFits("vSize", vSize, ivSize + 22 + erdSize, length);
    389            if (vSize < 4) {
    390                throw new ZipException("Invalid X0017_StrongEncryptionHeader: vSize " + vSize + " is too small to hold CRC");
    391            }
    392            assertMinimalLength(offset + ivSize + 22 + erdSize, vSize - 4);
    393            this.vData = Arrays.copyOfRange(data, offset + ivSize + 22 + erdSize, vSize - 4);
    394            assertMinimalLength(offset + ivSize + 22 + erdSize + vSize - 4, 4);
    395            this.vCRC32 = Arrays.copyOfRange(data, offset + ivSize + 22 + erdSize + vSize - 4, 4);
    396        } else {
    397            assertMinimalLength(ivSize + 20 + erdSize + 6, length); // up to and including resize
    398            this.hashAlg = HashAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + ivSize + 20 + erdSize));
    399            this.hashSize = ZipShort.getValue(data, offset + ivSize + 22 + erdSize);
    400            final int resize = ZipShort.getValue(data, offset + ivSize + 24 + erdSize);
    401
    402            if (resize < this.hashSize) {
    403                throw new ZipException("Invalid X0017_StrongEncryptionHeader: resize " + resize + " is too small to hold hashSize" + this.hashSize);
    404            }
    405            // TODO: this looks suspicious, 26 rather than 24 would be "after" resize
    406            assertDynamicLengthFits("resize", resize, ivSize + 24 + erdSize, length);
    407            //
    408            this.recipientKeyHash = Arrays.copyOfRange(data, offset + ivSize + 24 + erdSize, this.hashSize);
    409            this.keyBlob = Arrays.copyOfRange(data, offset + ivSize + 24 + erdSize + this.hashSize, resize - this.hashSize);
    410
    411            assertMinimalLength(ivSize + 26 + erdSize + resize + 2, length);
    412            final int vSize = ZipShort.getValue(data, offset + ivSize + 26 + erdSize + resize);
    413            if (vSize < 4) {
    414                throw new ZipException("Invalid X0017_StrongEncryptionHeader: vSize " + vSize + " is too small to hold CRC");
    415            }
    416            // TODO: these offsets look even more suspicious, the constant should likely be 28 rather than 22
    417            assertDynamicLengthFits("vSize", vSize, ivSize + 22 + erdSize + resize, length);
    418            //
    419            this.vData = Arrays.copyOfRange(data, offset + ivSize + 22 + erdSize + resize, vSize - 4);
    420            this.vCRC32 = Arrays.copyOfRange(data, offset + ivSize + 22 + erdSize + resize + vSize - 4, 4);
    421        }
    422
    423        // validate values?
    424    }
    425
    426    @Override
    427    public void parseFromCentralDirectoryData(final byte[] data, final int offset, final int length) throws ZipException {
    428        super.parseFromCentralDirectoryData(data, offset, length);
    429        parseCentralDirectoryFormat(data, offset, length);
    430    }
    431
    432    @Override
    433    public void parseFromLocalFileData(final byte[] data, final int offset, final int length) throws ZipException {
    434        super.parseFromLocalFileData(data, offset, length);
    435        parseFileFormat(data, offset, length);
    436    }
    437}