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 (long 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}