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 * http://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 * <b>Format</b> - 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 * <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 */ 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 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 }