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