1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * https://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.commons.compress.archivers.zip;
20
21 import java.util.Arrays;
22 import java.util.zip.ZipException;
23
24 /**
25 * Strong Encryption Header (0x0017).
26 *
27 * <p>
28 * Certificate-based encryption:
29 * </p>
30 *
31 * <pre>
32 * Value Size Description
33 * ----- ---- -----------
34 * 0x0017 2 bytes Tag for this "extra" block type
35 * TSize 2 bytes Size of data that follows
36 * Format 2 bytes Format definition for this record
37 * AlgID 2 bytes Encryption algorithm identifier
38 * Bitlen 2 bytes Bit length of encryption key (32-448 bits)
39 * Flags 2 bytes Processing flags
40 * RCount 4 bytes Number of recipients.
41 * HashAlg 2 bytes Hash algorithm identifier
42 * HSize 2 bytes Hash size
43 * SRList (var) Simple list of recipients hashed public keys
44 *
45 * Flags - This defines the processing flags.
46 * </pre>
47 *
48 * <ul>
49 * <li>0x0007 - reserved for future use
50 * <li>0x000F - reserved for future use
51 * <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
52 * wrapping is not used when generating a Master Session Key using ErdData.
53 * <li>0x4000 - ErdData must be decrypted using 3DES-168, otherwise use the same algorithm used for encrypting the file contents.
54 * <li>0x8000 - reserved for future use
55 * </ul>
56 *
57 * <pre>
58 * RCount - This defines the number intended recipients whose
59 * public keys were used for encryption. This identifies
60 * the number of elements in the SRList.
61 *
62 * see also: reserved1
63 *
64 * HashAlg - This defines the hash algorithm used to calculate
65 * the public key hash of each public key used
66 * for encryption. This field currently supports
67 * only the following value for SHA-1
68 *
69 * 0x8004 - SHA1
70 *
71 * HSize - This defines the size of a hashed public key.
72 *
73 * SRList - This is a variable length list of the hashed
74 * public keys for each intended recipient. Each
75 * element in this list is HSize. The total size of
76 * SRList is determined using RCount * HSize.
77 * </pre>
78 *
79 * <p>
80 * Password-based Extra Field 0x0017 in central header only.
81 * </p>
82 *
83 * <pre>
84 * Value Size Description
85 * ----- ---- -----------
86 * 0x0017 2 bytes Tag for this "extra" block type
87 * TSize 2 bytes Size of data that follows
88 * Format 2 bytes Format definition for this record
89 * AlgID 2 bytes Encryption algorithm identifier
90 * Bitlen 2 bytes Bit length of encryption key (32-448 bits)
91 * Flags 2 bytes Processing flags
92 * (more?)
93 * </pre>
94 *
95 * <p>
96 * <strong>Format</strong> - the data format identifier for this record. The only value allowed at this time is the integer value 2.
97 * </p>
98 *
99 * <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 */
258 public 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 }