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