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.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 * <b>Format</b> - 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 * <b>Flags</b> - 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    public X0017_StrongEncryptionHeader() {
287        super(HEADER_ID);
288    }
289
290    private void assertDynamicLengthFits(final String what, final int dynamicLength, final int prefixLength, final int length) throws ZipException {
291        if (prefixLength + dynamicLength > length) {
292            throw new ZipException("Invalid X0017_StrongEncryptionHeader: " + what + " " + dynamicLength + " doesn't fit into " + length
293                    + " bytes of data at position " + prefixLength);
294        }
295    }
296
297    /**
298     * Gets encryption algorithm.
299     *
300     * @return the encryption algorithm
301     */
302    public EncryptionAlgorithm getEncryptionAlgorithm() {
303        return algId;
304    }
305
306    /**
307     * Gets hash algorithm.
308     *
309     * @return the hash algorithm
310     */
311    public HashAlgorithm getHashAlgorithm() {
312        return hashAlg;
313    }
314
315    /**
316     * Gets record count.
317     *
318     * @return the record count
319     */
320    public long getRecordCount() {
321        return rcount;
322    }
323
324    /**
325     * Parse central directory format.
326     *
327     * @param data   the buffer to read data from
328     * @param offset offset into buffer to read data
329     * @param length the length of data
330     * @throws ZipException if an error occurs
331     */
332    public void parseCentralDirectoryFormat(final byte[] data, final int offset, final int length) throws ZipException {
333        assertMinimalLength(12, length);
334        // TODO: double check we really do not want to call super here
335        this.format = ZipShort.getValue(data, offset);
336        this.algId = EncryptionAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + 2));
337        this.bitlen = ZipShort.getValue(data, offset + 4);
338        this.flags = ZipShort.getValue(data, offset + 6);
339        this.rcount = ZipLong.getValue(data, offset + 8);
340
341        if (rcount > 0) {
342            assertMinimalLength(16, length);
343            this.hashAlg = HashAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + 12));
344            this.hashSize = ZipShort.getValue(data, offset + 14);
345        }
346    }
347
348    /**
349     * Parse file header format.
350     *
351     * <p>
352     * (Password only?)
353     * </p>
354     *
355     * @param data   the buffer to read data from
356     * @param offset offset into buffer to read data
357     * @param length the length of data
358     * @throws ZipException if an error occurs
359     */
360    public void parseFileFormat(final byte[] data, final int offset, final int length) throws ZipException {
361        assertMinimalLength(4, length);
362        final int ivSize = ZipShort.getValue(data, offset);
363        assertDynamicLengthFits("ivSize", ivSize, 4, length);
364        assertMinimalLength(offset + 4, ivSize);
365        // TODO: what is at offset + 2?
366        this.ivData = Arrays.copyOfRange(data, offset + 4, ivSize);
367
368        assertMinimalLength(16 + ivSize, length); // up to and including erdSize
369        // TODO: what is at offset + 4 + ivSize?
370        this.format = ZipShort.getValue(data, offset + ivSize + 6);
371        this.algId = EncryptionAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + ivSize + 8));
372        this.bitlen = ZipShort.getValue(data, offset + ivSize + 10);
373        this.flags = ZipShort.getValue(data, offset + ivSize + 12);
374
375        final int erdSize = ZipShort.getValue(data, offset + ivSize + 14);
376        assertDynamicLengthFits("erdSize", erdSize, ivSize + 16, length);
377        assertMinimalLength(offset + ivSize + 16, erdSize);
378        this.erdData = Arrays.copyOfRange(data, offset + ivSize + 16, erdSize);
379
380        assertMinimalLength(16 + 4 + ivSize + erdSize, length);
381        this.rcount = ZipLong.getValue(data, offset + ivSize + 16 + erdSize);
382        if (rcount == 0) {
383            assertMinimalLength(ivSize + 20 + erdSize + 2, length);
384            final int vSize = ZipShort.getValue(data, offset + ivSize + 20 + erdSize);
385            assertDynamicLengthFits("vSize", vSize, ivSize + 22 + erdSize, length);
386            if (vSize < 4) {
387                throw new ZipException("Invalid X0017_StrongEncryptionHeader: vSize " + vSize + " is too small to hold CRC");
388            }
389            assertMinimalLength(offset + ivSize + 22 + erdSize, vSize - 4);
390            this.vData = Arrays.copyOfRange(data, offset + ivSize + 22 + erdSize, vSize - 4);
391            assertMinimalLength(offset + ivSize + 22 + erdSize + vSize - 4, 4);
392            this.vCRC32 = Arrays.copyOfRange(data, offset + ivSize + 22 + erdSize + vSize - 4, 4);
393        } else {
394            assertMinimalLength(ivSize + 20 + erdSize + 6, length); // up to and including resize
395            this.hashAlg = HashAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + ivSize + 20 + erdSize));
396            this.hashSize = ZipShort.getValue(data, offset + ivSize + 22 + erdSize);
397            final int resize = ZipShort.getValue(data, offset + ivSize + 24 + erdSize);
398
399            if (resize < this.hashSize) {
400                throw new ZipException("Invalid X0017_StrongEncryptionHeader: resize " + resize + " is too small to hold hashSize" + this.hashSize);
401            }
402            // TODO: this looks suspicious, 26 rather than 24 would be "after" resize
403            assertDynamicLengthFits("resize", resize, ivSize + 24 + erdSize, length);
404            //
405            this.recipientKeyHash = Arrays.copyOfRange(data, offset + ivSize + 24 + erdSize, this.hashSize);
406            this.keyBlob = Arrays.copyOfRange(data, offset + ivSize + 24 + erdSize + this.hashSize, resize - this.hashSize);
407
408            assertMinimalLength(ivSize + 26 + erdSize + resize + 2, length);
409            final int vSize = ZipShort.getValue(data, offset + ivSize + 26 + erdSize + resize);
410            if (vSize < 4) {
411                throw new ZipException("Invalid X0017_StrongEncryptionHeader: vSize " + vSize + " is too small to hold CRC");
412            }
413            // TODO: these offsets look even more suspicious, the constant should likely be 28 rather than 22
414            assertDynamicLengthFits("vSize", vSize, ivSize + 22 + erdSize + resize, length);
415            //
416            this.vData = Arrays.copyOfRange(data, offset + ivSize + 22 + erdSize + resize, vSize - 4);
417            this.vCRC32 = Arrays.copyOfRange(data, offset + ivSize + 22 + erdSize + resize + vSize - 4, 4);
418        }
419
420        // validate values?
421    }
422
423    @Override
424    public void parseFromCentralDirectoryData(final byte[] data, final int offset, final int length) throws ZipException {
425        super.parseFromCentralDirectoryData(data, offset, length);
426        parseCentralDirectoryFormat(data, offset, length);
427    }
428
429    @Override
430    public void parseFromLocalFileData(final byte[] data, final int offset, final int length) throws ZipException {
431        super.parseFromLocalFileData(data, offset, length);
432        parseFileFormat(data, offset, length);
433    }
434}